What happens if two people submit edits at once - ruby-on-rails

This might be a stupid questions but I wanted to know what happens if two users edit some data at once and then both click submit at the same time, I assumed Rails handled requests one after the other and so someone would get an error message but is this correct?
Thanks
Once one person has edited data I dont want it to be accessible or editable anymore, which is handled by validations
Ive got this validation in my model as well
def account_active
if self.active == true
return true
else
return false
end
end
Where active is a Boolean set within the controller if the validations pass

As has been mentioned in other answers, the latest write wins.
You might not think this is a problem but as there's no pessimistic lock preventing two users from having the same edit form open at once, both users may think the change they're making will be saved.
There is a way around this by using a version number or timestamp on your models that the system can use to tell "the user was editing version 1, but now there's version 2" and prevent the second user from overriding the first user's write.
Ryan Bates' awesome Railscasts series has covered the basics on how to set this up in Railscast 59: Optimistic Locking.

Your web server daemon would handle the requests one after the other; whichever request gets handled last becomes the newest update. Nobody would receive an error message unless you write some logic to handle such cases.

As with all race conditions involving blind writes, last one wins unless you take steps to change that.

Your original question was answered, but I'll add this:
For the validation, you can simply do the following
def account_active
self.active?
end
Ruby implicitly returns the last line of the method.

Related

Is there a validation I can use for a specific attribute on all associated records?

I have a Question that has_many :answers (just like SO). I also want each question to have only 1 accepted_answer, so I just added an :accepted attribute to the Answer model that is simply a boolean.
So now, to get the accepted answer for my question, I have written a method on my model that just does this:
def accepted_answer
answers.where(accepted: true)
end
That allows me to do question.accepted_answer and it returns an ActiveRelation object like you would expect.
Nothing fancy. Simple and effective.
However, what I want to ensure though is that there can only be one answer on each question that is accepted: true at any moment in time.
What's the best way to approach this?
I thought about using a validator, but I couldn't find one that handled associated objects in this way. There are some that have bits & pieces that are interesting, but I can't quite fit all the pieces together. For instance, presence is interesting as is absence and validates_with (but this last one feels too heavy).
Suggestions?
Most likely the best way would be to use after_add callback (an example here), which would set to false all your existing accepted records via update_all and the latest answer with accepted set to true. It all depends on your logic.
You can also employ some other callbacks such as before_save, before_update and such with the similar functionality depending on your application specifics.
It is not quit a validation, but it will effectively maintain the required state of your model. Besides, the purpose of the validations to warn you when something is not valid, but I guess you want to save your object without such failures, and just enforce a single accepted answer.
Let me know in case you want to stop adding answers after the first one was accepted. In this case it would require a different functionality.

How do I update a param in Rails before it submits, based on a selection

Sorry if this is a super nooby question, but I have been trying to figure out this solution for a while.
Basically, I have a field in a form which requests a length of time. Currently it accepts numbers as months in a float, so if you want to put in 15 days, you would have to enter "0.5". I added a radio field which allows the user to select a type (months or days).
I tried adding
if params[:length_type] == "days"
params[:length] = params[:length]/30
end
to the controller but I realized that even though the parameter was changing (as seen in debugger), it was being submitted first, which meant that if the user chose 15 days it would still be submitted as 15 months.
Next I tried adding this code to the model in a before_create where I was informed that:
undefined local variable or method `params' for ...
I looked it up and apparently params are not accessible in the model, and more so if they were it would break MCV practice.
I am pretty confused as to where to go from here, so any help would be appreciated.
Thank you for taking the time to read my question.
(BTW I don't want to do this in JS because I am worried about the possibility of it being off on a users computer, unless there is a way around that?)
You need to perform the action (change params) in the update method in the controller before the thing.update method is called in it. You do that if the record exists. If it is a new record you will need to do the same thing in the controllers create method before thing.save is called.

How to properly enforce a conditional read-only record on Rails?

So a situation came up at work and I wanted to discuss it here because we could not get to an agreement between us:
We have two models, Order and Passport, which are related in a way that an Order has_one passport and a passport has_many orders. Whenever an order is completed, its associated passport must be 'locked', that is, turned into read-only (that information was already used to clear customs, so it can't be changed afterwards). We want to enforce that rule in the Passport model and we've thought of the following options:
Creating a validation. CONS: There will be records yielding valid? => false when technically the record is fine (although it can't be saved). For example, if other records have a validates_associated :passport on them, that could be a problem.
Overriding the readonly? method. CONS: This will raise an exception when trying to update that record, although you would expect that calling a save method won't ever raise one.
Creating a before_save callback. This has two flavors: either raise an exception (which is pretty much like the readonly? option) or add an #error and return false to stop the callback chain. CONS: Adding validation errors from outside a proper validation can be considered a bad practice. Also, you might find yourself calling valid? and getting true and then call save and get false.
This situation made us think a lot about the relationship between validations and Rails. What exactly does it mean for a record to be valid?? Does it imply that the save will work?
I would like to listen to your opinions to learn about this scenario. Maybe the best approach is neither one of the three! Thanks!
What about marking this record as read-only by using readonly! instance method? See the API
You could do it in a constructor, like:
class Passport < ActiveRecord::Base
def initialize(*args)
super(*args)
readonly! if orders.count>0 # or similar
end
end
I think there is an extra alternative. What you describe dictates that the Passport model can have some different states. I would consider using a state machine to describe the relevant orders status for the passport.
eg:
open
pending
locked
other_update_actions ...
With that in mind, all relevant order actions will trigger an event to the passport model and its state.
If it is possible to integrate the update actions to certain events then you could handle the readonly part in a more elegant way (incompatible state transition).
As an extra check you can always keep an ugly validator as a last resort to prevent the model from being updated without the state machine.
you can check the aasm gem for this

rails associations & object identity: maintaining object identity through a series of changes

`First of all, the fact that I am even asking this question implies that I am consciously choosing not to (strictly) obey the law of Demeter.
Since sometime (probably rails 3?) referring to model.association.first results in a new object each time, unless you use .to_a on the association:
campaign.campaign_shirts.first.to_s
=> "#<CampaignShirt:0x007fdd02c7fd58>"
campaign.campaign_shirts.first.to_s
=> "#<CampaignShirt:0x007fdd02ca6318>"
c.campaign_shirts.to_a.first.to_s
=> "#<CampaignShirt:0x007fdd02d13170>"
c.campaign_shirts.to_a.first.to_s
=> "#<CampaignShirt:0x007fdd02d13170>"
I've worked on several Rails 3/4 applications without even noticing this, probably because I do try to respect Demeter as much as is practical.
In this case I want Campaign to be in control, because it is a big state machine where many of its state changes involve transactionally coordinating changes in itself and various child objects.
Is there a way to freeze the association arrays at create and/or fetch time?
EDIT: I noticed almost immediately that they are frozen when you use Campaign.includes(...).find, which I am doing in my app. However I still have a problem in specs where the objects are factories created by FactoryGirl. Is there a way to say "freeze all the associations on this object" or do I have to call .to_a on each of them?
EDIT 2: I still have a problem when I refer to campaign through a belongs_to on user. (this seemed like a separate question, so I asked it here).
EDIT 3: the problem with the belongs_to includes extension was just syntax, so I'm removing the details of that.
So, my remaining problem is to get User.selected_campaign to act like it does in my app when it is built up by FactoryGirl. I'm going to try just doing a .reload at the start of each spec, which should trigger the includes extensions, at the cost of some spec performance.
I don't know the (gory) details of your setup, but what if you just memoize first on the Campaign object?
def Campaign
def first_campaign_shirt
#first_campaign_shirt ||= campaign_shirts.first
end
end
I think this way you obey the Law of Demeter again? But it might get annoying if you need more getters than just first_shirt. So consider this just a suggestion that won't fit in a comment box. :)
All of my in-app use cases were solved by carefully choosing where to add includes(..) to associations and scopes.
I was only able to solve the factory girl problem by calling reload on a factory object after creating it.

Rails - Rate Limiting a Model's Create

I'm looking to see if there is a smart way to do something like the following...
In my app I have projects. I want to prevent a user from adding more than 10 projects. My understand after only having used rails for a few weeks is, that I should make a helper in my model for this, does that sound right?
Also should I do this at the model/helper level or is this something that should be done for all models with some type of setting file?
So the idea is, when the user goes to create a new project, before_create, it checks, if the user has 10+ projects already, is says, sorry not at this time? ALso, interested in how to output the error msg, but 1 step at a time for a newbie.
thanks
Doing this as a validation method is pretty straightforward. In Rails 3 you just declare a method to be run during validation and it has an opportunity to add errors if the situation arises:
class Project
validate :user_can_create_projects
protected
def user_can_create_projects
if (user and user.projects.count >= 10)
errors.add_to_base("You have created too many projects.")
end
end
end
This is not an entirely bullet-proof method as there is a very small chance that someone might be able to create a project between the interval when you check the count and when you actually create the project. That sort of thing has a much greater chance of happening when someone double-clicks a form submit button, for instance, but in practice is relatively rare.

Resources