I am working on a marketplace in rails that allows buyers to make an offer to the seller as a bid for a listing. The seller can cancel or accept the bid but only has a 24 hr window to do this or the bid expires and the transaction is cancelled. A user can be both a buyer or a seller.
I have an Order model that represents each transaction.
Class Order < ActiveRecord::Base
belongs_to :seller, class_name: "User"
belongs_to :buyer, class_name: "User"
belongs_to :listing
end
On my reservation controller, Class OrdersController < Application Controller before_action :authenticate_user!
def create
@order = Order.new(order_params)
@seller = @order.listing.user
@order.buyer_id = current_user.id
@order.seller_id = @seller.id
@bidding_fee = @order.bidding_fee
end
I want to differentiate between a buyer cancel and seller cancel because if a seller cancels a bid for a listing the buyer pays no bidding fee but if the buyer cancels the order he made he is charged a bidding fee. How can the transactions for bids be implemented. The hardest part is figuring out creating a timer in rails that will show the seller how much time is left eg 4 hrs 12 min before the bid expires and cancelling the bid after 24 hrs if there's no response especially keeping in mind different client side time zones that may be hours apart from the created_at time of the order. I also need to show order status as pending, canceled or accepted which rules out boolean functionality because that creates three possible statuses. Any kind of help in any of these problems would be greatly appreciated.
You might have noticed that in bids
table (I am assuming the table name) there is a field called created_at
. You can use this as the start-time
indicator.
In bids
page where you show all the bids to seller
. Either you can
bids.where('bids.created_at > ?', 24.hours.ago)
) bids
but disable the bids created before 24 Hours
Better way would be keeping track of the status of the bid with status
attribute. To invalidate a bid
after 24Hours you can use background processors like sidekiq
, resque
.
For example: With Sidekiq
BidInvalidator.perform_in(24.hours, 'mike', 1)
BidInvalidator.perform_at(24.hours.from_now, 'mike', 1)
Update
Do you know how to check the time on the client side based on the timezone of the created_at timestamp on the database if you take into account some users may make an offer before the created_at time if they live 6hrs behind in Alaska?
Well, ActiveRecord stores all date-time in UTC time so that developers can converted it to any timezone you like.
You need to set TimeXone
according to user's timezone. You can store timezone offset
in browser cookie and get the offset
to calculate the timezone
in Server side.
$(document).ready(function(){
if(!($.cookie('time_zone'))) {
current_time = new Date();
expired_after_days = 365;
$.cookie('time_zone', current_time.getTimezoneOffset(), { path: '/', expires: expired_after_days } );
// Here expired_after_days represents the number of days the cookies will store the info
}
})
in application_controller
you can
# setting the time zone with the current time zone offset
before_filter :set_time_zone
def set_time_zone
time_zone_offset = cookies[:time_zone].to_i
Time.zone = ActiveSupport::TimeZone[-time_zone_offset.minutes]
end
Note : Time.zone
is thread safe.
Now you need to use DateTime.current
in place of DateTime.now
Does it provide functionality to create a worker that checks the status attribute a little more frequently like every ten minutes.If a bid is cancelled Immediately I don't want to have to wait 24hrs before informing a buyer the bid is cancelled
Well to execute tasks in specific point of time you can create cronjobs using Whenever gem
You do not really need a timer. You just need the time until the bid is valid. I guess you will have some kind of Bit
model. When a bit is created by a buyer store when the bid expires in a expires_at
column:
class Bid < ActiveRecord::Base
before_create :set_expired_at
private
def set_expired_at
self.expired_at = Time.current + 24.hour
end
end
This allows you to add an expired?
method to the same class which you could use in the view to decide if you want to still list an offer:
def expired?
expired_at < Time.current
end
Or you could add a scope to only load unexpired bits:
scope :unexpired, -> { where('expired_at < ?', Time.current) }
If an offer is pending or was excepted could be implemented as a simple accepted
boolean in the database. Which lead to the following status
method:
def status
if accepted?
'accepted'
else
expired? ? 'expired' : 'pending'
end
end
Since all this methods a depending on the current time, there is no need for an external timer, a cron job or a background job. The model it self is always able to return the current state.
You could use delayed_job
, sidekiq
or resque
or similar tools to schedule your function to be called as soon as the bid is created. And when 24 hour is up, when the function get's called you can check there if it was accepted or not.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.