Return Object After Validation Failure - ruby-on-rails

Is there a method to save an object but return the object if it fails the validation_uniqueness_of for a given field? For example, I have the following:
class User
has_many :words
...
end
class Word
belongs_to :user
validates_uniqueness_of :title
...
end
And I have a situation where I want to either return the Word object if the user has already saved this word, or return the newly saved word object. I am finding myself checking if the word object exists through my own search and then performing a save if not:
existing_word = find_by_word_and_user_id(word, user)
!existing_word.nil? ? existing_word : user.words.create({:word => word})
But I feel like if the user does not have the word saved, rails is going to perform a redundant search for the title uniqueness validation after my check and I was wondering if I could just do the save and return the already existing object that rails finds for the validation.

You might be interested in #find_or_create_by_word_and_user, another of ActiveRecord's magic methods:
obj = Word.find_or_create_by_word_and_user(word, user)
# Preferred way
obj = user.words.find_or_create_by_word(word)
This will do 2 database requests anyway: there's no way around that. Also, if your Word instance has more validation that you've shown us, the create will fail, because #find_or_create_by does not allow passing more attributes.
For all the details on the dynamic finder methods, search for "Dynamic attribute-based finders" on http://api.rubyonrails.org/classes/ActiveRecord/Base.html.

You can DRY up that statement a little by just saying:
find_by_word_and_user(word, user) || user.words.create(:word => word)
you could eliminate a db hit by reversing these, where it tries to create before trying to find. But that wouldn't be a good idea, because there are other reasons the create might fail, so you can't be guaranteed it's the uniqueness issue.
You're not going to see a noticeable performance hit by just doing it the first way, I'd stick with that. Your only other option is to remove the validation itself, and use the statement above. But then you have to make sure that everywhere you create words, you're checking for uniqueness.

Related

access ActiveRecord Has Many associations

I have a model which has a lot of associations. What I need to do is to check whether one of those associations is not present or all of them are set correctly.
To do so, I created an array which includes all of those needs-to-be-checked fields. And created a loop through each element, but the case is that I can not access has_many related attributes with read_attribute method. It basically returns nil whenever I try to access has many associated fields.
What I am trying to do, I can access all related objects via car.drivers but I can not access the same object with car.read_attribute(:drivers) (because some of them are attributes and some are relations)
I think it's the behavior of read_attribute, so what should I use to access any (attribute or relation) on ActiveRecord object?
Regarding to the comments, it looks like no one understand what I am trying to do. I want to access the relations of one ActiveRecord object such like;
RELATIONS.each do |relation|
puts "#{relation} exists" if #object.relation.present?
end
What I do not know about this, is there any method that I can access the related objects with their string typed name. Similar to, #object.read_attribute(:attribute_name) In that way I can create a for loop, but for relations not for the attributes
To do so, I used dynamical method call. Below is an example of showing it
RELATIONS.each do |relation|
puts "#{relation} exists" unless #object.send('relation').nil?
end

Can I list all dependent objects of an object (say, user)?

I have a model and can manually check every (:has_many, :has_one) dependency, but I want some magic like current_user.attributes for records. So when I update model, I don't need to update method.
I tried Reflections, but it returns all theoretical dependencies/connections of model, isn't it? And I need dependent records from DB.
Something like X.where(user_id: #user.id) or #user.dependents
Is it possible?
You can assign required object to model and then
model.class.reflect_on_all_associations.map { |table| model.method(table.name).call }.select(&:any?)
For example:
user = User.first
user.class.reflect_on_all_associations.map { |table| user.method(table.name).call }.select(&:any?)
# returns all associated objects of first user
You can specify result using :has_many, :has_one or :belongs_to as argument of reflect_on_all_associations.
Possibly there is more elegant way, but it works.
TL;DR Don't do this :)
You can do something quite similar using reflections. For example
#user.class.reflections.keys.flat_map { |reflection| me.send(reflection) }
will give you an array with all the objects associated with the user. But what's next?
For almost any real-world logic around this list's members (except the basics that come from AR::Base) you will have to check either a class of an object or use bug-prone try magic - both options are reasonable trade-off sometimes, but in most practical cases I'd prefer less smelly solutions (even if they are a bit more verbose).

Handling associations w/ null objects in Rails

I'm using the Null Object pattern in my Rails application to implement the concept of a guest user account.
Like many apps, I have a method on ApplicationController called current_user.
In the case of a non-logged in user, I want to use my guest user null object.
It works in many cases, but then there run into something like the following -
params.merge({ user: current_user })
MyModel.new(params)
Of course this fails, with the following exception.
ActiveRecord::AssociationTypeMismatch: User expected, got GuestUser
My question is, what is a way to elegantly handle this kind of case. The idea for the Null Object pattern is that you can transparently swap in this null object and have it essentially be a duck type of the real object.
It's obvious how to do that for methods being called on the object, but in this case, I want to be able to pass this in and basically have it set the association column to null, rather than needing a whole bunch of custom logic (avoiding that is the whole point of the null object pattern anyway).
A polymorphic relation isn't quite it.
Quick answer: No such thing as an elegant way to handle that (I'm not sure how elegance is quantified).
You'll have to create a concern that mimics the persistence methods of the model from which your null object is based on (User). You'll also have to write methods to appease ActiveRecord to make the associated column be nil.
Fortunately for you, this use-case has been solved
if your MyModel accepts null for user_id, then you can do
params.merge(user: current_user) unless current_user.is_a?(GuestUser)
MyModel.new(params)
Using the null object pattern here is definatly not a good idea since you need database generated ids to build associations and maintain referential integrity if you intend the user to have any kind of persistence before "registering".
Allowing a MyModel to be created without a user_id would essentially create an orphaned record and just gives you another problem of linking it to user behind the screen. Thats why your schema should not allow it in the first place.
Rather you want to create the guest user record when needed (like when a guest user adds the first item to a cart) and use a recurring task (like a Cron tab) to periodicaly clean out junk records.
I would also consider if you really want to setup guest users as a seperate class since STI and polymorphism tends to get really messy when joining. Just use a timestamp column (records when the account was activated) or an enum instead.
One option would be to override the user= method, so that it's aware of the existence of GuestUser (and can handle appropriately):
def user=(value)
if value.is_a?(GuestUser)
super(nil)
else
super
end
end
All mass-assignment methods in Rails (create, update, etc.) will use the appropriate setter to set the value. This can be easily be put into a concern if this is a common pattern in your application.
If you don't allow nil in the user_id column you have the flexibility to do something like assign a sentinel value, which you could then use in your accessor as well:
def user
if user_id == GUEST_USER_ID
GuestUser.new
else
super
end
end
I had a similar problem. I just went from assigning the object to assigning the object.id which I set to nil on the Null Object. It is kind of a hack I think though.

How to filter a relation in place

This is a pretty fundamental question, I guess, but I'm having a hard time finding an answer. It may be that this just isn't a thing people do, and if that's the case I'd like to know why.
In a show action, usually I have some code like this:
#structure = Structure.where(id: params[:id])
This gets me a relation containing the unique structure with the given id. Structure has_many plannings. What I'd like is a way for my "Structure" relation to contain structures that have plannings relations that have been filtered. So, I don't want the Structures scoped to their plannings, I want the plannings on those structures to themselves have been filtered. I want to get back a relation containing a structure that has only a subset of its plannings. Does this even make sense?
edit:
I'm starting to see why this doesn't make sense, but also having a hard time articulating it. This,
#structure = Structure.where(id: params[:id])
#structure.plannings = #structure.plannings.where(audience_ids: "1")
throws an error along the lines of Cannot modify association 'Structure#plannings' because the source reflection class 'Planning' is associated to 'Course' via :has_many. The message I'm getting here is: what you are trying to do is more complicated than you think.
I guess I should just return json that looks like what I want it to look like, rather than trying to modify the relation in place.
If your criteria are constantly the same for all plannings that are fetched as a structure's relation, you could use a default scope in the relation (available on Rails 4)
class Structure < ActiveRecord::Base
has_many :plannings -> { where(attribute_a: true) }
end
that way
Structure.where( ... ).first.plannings
will apply the desired scope to plannings.

Force reload another model's methods in rails?

I have a model that defines methods based off of the entries in another model's table: eg Article and Type. An article habtm types and vice versa.
I define in Article.rb:
Type.all.each do |type|
define_method "#{type.name}?" do
is?(:"#{type.name}")
end
end
This works great! it allows me to ensure that any types in the type db result in the methods associated being created, such as:
article.type?
However, these methods only run when you load the Article model. This introduces certain caveats: for example, in Rails Console, if I create a new Type, its method article.type_name? won't be defined until I reload! everything.
Additionally, the same problem exists in test/rspec: if I create a certain number of types, their associated methods won't exist yet. And in rspec, I don't know how to reload the User model.
Does anyone know a solution here? Perhaps, is there some way to, on creation of a new Type, to reload the Article model's methods? This sounds unlikely.. Any advice or guidance would be great!
I think you'll be better off avoiding reloading the model and changing your api a bit. In Article, are you really opposed to a single point of access through a more generic method?
def type?(type)
return is? type if type.is_a? String # for when type is the Type name already
is? type.name # for when an instance of Type is passed
end
If you're set on having separate methods for each type, perhaps something like this would work in your Type class
after_insert do
block = eval <<-END.gsub(/^ {6}/, '')
Proc.new { is? :#{self.name} }
END
Article.send(:define_method, "#{self.name}?", block)
end

Resources