Preventing duplicate requests in rails - ruby-on-rails

We have a Rails API with the following API call
POST /book
user_id (integer): The user id to book the class for
class_id (integer): The id of the class to book
In this call, we send out a request to a third party API to book the class. The problem is we want to guarantee that duplicate requests for a (user_id, class_id) pair aren't sent. We could prevent users from clicking on the book button multiple times but we want that extra level of protection. We don't want duplicate charges on our hands.
Any ideas how we can enforce something like this? Thanks!

You could create an new model called Booking with an associated database table and then create a new booking whenever this post request occurs. To make sure that no previous bookings exist, you can validate uniqueness in your model like so:
validates_uniqueness_of :user_id, :scope => :class_id
If creating a booking fails in the controller, then you know that a booking already exists and can return a message to that effect.

Related

Recurring database entries

Quick question about recurring DB entries.
Say I have users posting their Trips on a platform. Now, the user can say these Trips are recurring - she/he makes that trip every Tuesday and Thursday.
To make things even more interesting, say that every Trip has Requests attached to it, that other users can make. And they can make these Requests for every recurring trip.
Any ideas on how would I handle a case like this one on the back-end? Using Rails and Postgres.
Thanks in advance!
User
has_many :trips
Trip
belongs_to :user
has_many :requests
Request
belongs_to :user
belongs_to :trip
Add recurring_start and recurring_end attributes on Trip, and perhaps a recur attribute on Request. I don't know that you need to create any additional records for each Trip then, do you?
If so, you want your business logic handling that. Something like Sidekiq with a Query object that fetches Trips that are due for recurrence and creates the new trip with (for example) updated start and end dates…
class Trip < ApplicationModel
scope :recurring, -> { where(recur: true) }
scope :due_for_recurrence, -> { recurring.where(Trip.arel_table[:end_date].lt(Time.now)) }
end
You can use something like DeepCloneable if you want to automatically clone/dup associated records as well.
Since every trip can have different requests, they all need their own individual ID. It would make sense to treat them exactly the same as non-recurring trips, except for one situation: when the creator of the trips wants to edit or delete all the instances of a recurring trip. For that, you may want to make an additional table for recurring trips, with a one-to-many relation from recurring trip IDs to trip IDs, and allow users to own recurring trip IDs as well as trip IDs. That way the user interface can display the two kinds logically and they won't get lost.
Just make sure that whenever a trip is edited or deleted, the recurring trip table is updated. That could either be accomplished by simply disallowing edits or deletion to trips that are part of a recurring trip, or make the trips table store an optional recurring trip ID.

Rails: Designing relations between models

My application needs to implement a functionality for performing competitions of photographers. Process looks like this: Administrator creates a competition, then user can apply a request for participating. Admin can approve request or it can deny it. Admin does it by creating a response with response status field assigned to ACCEPTED (0) or BANNED (100).
class Competition
end
class User
end
class CompetitionRequest
belongs_to :user
belongs_to: competition
end
class CompetitionResponse
ACCEPTED = 0
BANNED = 100
belongs_to :competition_request
end
class Photo
belongs_to :competition
end
But later i need to figure out how to filter banned photos from showing them to users and jury.
And Photo has no direct connection to CompetitionResponse. Photo and CompetitionResponse have relations to Competition but i can not figure out how to make filtration possible. I tried doing it via plain sql (find_by_sql) inside Photo model (it returns records only if appropriate request was approved) but it does not seems to be a good design, because i reference value defined in CompetitionResponse class in Photo model.
Later i changed design and make a before_save filter inside CompetitionResponse model, it checks if status == BANNED and if true, marks all the photos posted to competition by user (who's request was banned) as banned (i added a status field to Photo model).
But now i feel it looks not good. CompetitionRequest model takes too much responsibility.
What is the best way to design it ? May be there must some kind of Observer or something like this or another design pattern ?
First step I would take is assign each Photo to the user it "belongs_to". This will allow you to, at any time, check whether the "owning" user was indeed accepted or banned for the competition that photo refers to...
It seems to me that you should have a many-to-many relation between Competition and User. When the admin accepts a user into the competition, you associate the records. This keeps things simple in your data model and also allows for cases where Users get into a Competition without requesting it (perhaps as part of a tournament?).
Also consider getting rid of CompetitionResponse - it seems like this should just be an attribute of CompetitionRequest, as there would only be one response to a request, right?

Remote data model in Rails

i have model with validations, some methods, filters and so. Unfortunately the data are from API, so i need to overload the method which is pull the records from DB. Which method is that?
So far i have creation. Method create in active record's model is presisting new record. I just add method create to my model and it's creating records over the API. Now i want it for selecting the data.
Following code is example of what i already have (creation of records):
def create
EmployeesApi.create(self.attributes.reject{|k,v| %w(id created_at updated_at).include? k })
end
I need it as low level as possible, because it has some relations and app specific validations. Moving also the relations and surrounding logic would mean integrate two existing systems and that's not ok in this case.
Another explanation:
I want to bypass the database for data of the model, but for association let everything as it was. The model's data are stored in another app/database/system.
I'll load model's own data by API and ActiveRecord will pair/load it's associations from local DB
If EmployeeApi would be modeled after ActiveResource you could possibly enhance it with ActiveModel. Associations might work only in one way (from ActiveRecord to ActiveResource). It is also good to consider exceptions such as API is down.
I think what you are trying to achieve is to pull some data from the DB before creating this record? or validate some DB field, maybe ensure a unique constrain or something.. Please look into the active record hooks/callbacks
http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html
You can do something like this:
class Subscription < ActiveRecord::Base
before_create :record_signup
private
def record_signup
self.signed_up_on = Date.today
end
end

Rails Braintree gem subscription class conflict

In my Rails app, I'm using the Braintree gem to create subscriptions. Without realizing, I had also created a Subscription model and controller to manage the subscription info I wanted to store locally. In my model, a subscription can belong_to a user. However, some of the normal stuff you can do wasn't working such as
current_user.subscriptions.build()
But for some reason when someone was helping me they were able to use
current_user.create_subscription
Where is this create_subscription method defined? Is it somehow overriding the Rails convention?
I noticed that there is a subscription.rb file in the Braintree gem. Is there some conflict with the class defined by Braintree and my Subscription model? I know that I can probably just rename my Subscription model, but I'm curious as to what the conflict is.
Your issue is that the subscription relation is has_one or belongs_to, rather than has_many. User would not have a subscriptions method in this case as the attached subscription would be singular. Review the API docs for how to manipulate these sorts of relations in AR.
From the manual on has_one:
The following methods for retrieval and query of a single associated object will be added:
association(force_reload = false)
Returns the associated object. nil is returned if none is found.
association=(associate)
Assigns the associate object, extracts the primary key, sets it as the foreign key, and saves the associate object.
build_association(attributes = {})
Returns a new object of the associated type that has been instantiated with attributes and linked to this object through a foreign key, but has not yet been saved. Note: This ONLY works if an association already exists. It will NOT work if the association is nil.
create_association(attributes = {})
Returns a new object of the associated type that has been instantiated with attributes, linked to this object through a foreign key, and that has already been saved (if it passed the validation).
Braintree does have a Subscription class, but this is namespaced to Braintree:Subscription so it is not the issue.

Rails - after_save - How to determine if a record was Created/Update or Deleted

In my rails app I have the following models:
Messages (id, title, content, project_id)
MessageViews (id, message_id, view_status)
Permissions (id, user_id, project_id)
Projects (id, name)
Whenever a user is added to a project which existing messages, I want to create a MessageView record for every message in the project.
Likewise, if a user's project permission is deleted, I want to delete all the user's messageViews associated to that project's messages.
In my permissions model, so far I have:
before_destroy :check_message_views
private
def check_message_views
.....
end
How in check_message_views can I determine if a record is being created/update or deleted.
When created/updated, I want to loop through and check
When deleted, I want to loop through all the project's messages and delete any messageView for that user.
Thanks
Shouldn't your MessageView model belong to a Permission? Then you could set :dependent=>:destroy and everything should work automatically.
Otherwise you can use the destroyed? method.
I don't think there's any sort of equivalent created? method but you could put your code in an after_create callback in your Permissions model. There's an after_destroy callback for that matter as well if you want to be consistent.
Another sort of hacky way around it is to check if the model's created_at time is the same as it's updated_at time. If so then it's been created but hasn't been updated yet.

Resources