Marko I. Marko I. - 5 months ago 12
Ruby Question

Order_items not expiring after setting background checks in order_cleaner.rb

I'm building a store in rails. A store has orders, order_items, users..

Right now when customer adds order_item to a cart, order with unique id is created automatically, and order_items are saved under order_items model. There is also validation saying that only 3 order_items are allowed in one order.

If possible, I would like to remove those order_items that are saved to a model after 5 minutes. 5 minutes counter should start after saving all 3 order_items.

What I did:

I added order_cleaner. to config/initializers/order_cleaner.rb in order to do background process and periodically check the database and remove those saved order_items..

Here is how it looks:

Thread.new do
while true
# Get all orders with at least 3 order_items
orders = Orders.joins(:order_items).group('orders.id').having('count(order_id) >= 3')
orders.each do |o|
# Delete associated order_item if it's older than 5 minutes
o.order_items.each {|oi| oi.destroy! if oi.updated_at < 5.minutes.ago }
end
sleep 1.minute
end
end


I check back after 5 minutes after adding 3 order_items and all order_items are still present. They haven't been removed. What could be the issue and do you have any other solution to achieve this?

Thanks

Relevant code:

class Order < ActiveRecord::Base
belongs_to :order_status
belongs_to :user
has_many :order_items
validates_length_of :order_items, maximum: 3 #only 3 order_items are allowed within an order for each user
before_create :set_order_status
before_save :update_subtotal



def subtotal
order_items.collect { |oi| oi.valid? ? (oi.quantity * oi.unit_price) : 0 }.sum
end
private
def set_order_status
self.order_status_id = 1
end

def update_subtotal
self[:subtotal] = subtotal
end



end


order_item.rb

class OrderItem < ActiveRecord::Base
belongs_to :product
belongs_to :order
validates_associated :order
validates :quantity, presence: true, numericality: { only_integer: true, greater_than: 0 }
validate :product_present
validate :order_present


before_save :finalize

def unit_price
if persisted?
self[:unit_price]
else
product.price
end
end

def total_price
unit_price * quantity
end

private
def product_present
if product.nil?
errors.add(:product, "is not valid or is not active.")
end
end

def order_present
if order.nil?
errors.add(:order, "is not a valid order.")
end
end

def finalize
self[:unit_price] = unit_price
self[:total_price] = quantity * self[:unit_price]
end


end


order_items_controller.rb

class OrderItemsController < ApplicationController
def create
@order = current_order
@order_item = @order.order_items.new(order_item_params)
@order.user_id = current_user.id
@order.save
session[:order_id] = @order.id


respond_to do |format|
format.js { flash[:notice] = "ORDER HAS BEEN CREATED." }
end
end



def update
@order = current_order
@order_item = @order.order_items.find(params[:id])
@order_item.update_attributes(order_item_params)
@order_items = @order.order_items
end



def destroy
@order = current_order
@order_item = @order.order_items.find(params[:id])
@order_item.destroy
@order_items = @order.order_items
end


private

def order_item_params
params.require(:order_item).permit(:quantity, :product_id, :user_id)

end
end


schema.rb

create_table "order_items", force: :cascade do |t|
t.integer "product_id"
t.integer "order_id"
t.decimal "unit_price", precision: 12, scale: 3
t.integer "quantity"
t.decimal "total_price", precision: 12, scale: 3
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.boolean "has_ordered"
end

create_table "orders", force: :cascade do |t|
t.decimal "subtotal", precision: 12, scale: 3
t.decimal "tax", precision: 12, scale: 3
t.decimal "shipping", precision: 12, scale: 3
t.decimal "total", precision: 12, scale: 3
t.integer "order_status_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "user_id"
t.boolean "ordered"
t.date "first_item_added_at"
t.date "first_order_added_at"
end

Answer

There was a bug in Orders.joins should be Order.joins

Also you could use a gem like https://github.com/plashchynski/crono to schedule jobs, in the way of cron for unix.