Is it possible for me to use string interpolation to build up an ActiveRecord query like so:
query = '.owner.name'
widget = Widget.first
name = "#{Widget" + query + "}"
This obviously doesn't work, but I'm looking for an acceptable alternative.
Is it possible for me to use string interpolation to build up an ActiveRecord query like so:
query = '.owner.name'
widget = Widget.first
name = "#{Widget" + query + "}"
This obviously doesn't work, but I'm looking for an acceptable alternative.
query = '.owner.name'
widget = Widget.first
name = eval "widget#{query}"
Not that this is a good idea, but it does work.
Is 'eval' supposed to be nasty?
With great power comes great responsibility (and risk).
Since Rails uses hashes to map columns to methods, and those hashes are "hashes with indifferent access" you can use either a string or a symbol. Thus:
widget = Widget.first
widget[:owner] # => returns the same as widget.owner
widget['owner'] # => also returns widget.owner.
The problem with the above code, is that owner IS PROBABLY a foreign key to another column, so the method name is actually:
widget['owner_id']
which returns an integer that Rails uses to look up the id in the Owners table. So if you are ONLY stringing together columns that return columns in the Widget table, you can use the above code. But when you are stringing together query methods it is usually because you are looking up some relationship in the DB and calling a column method on that.
Thus you would need to do some sort of recursive loop to take:
query = 'owner.name'
q = query.split'.'
And work up something that results in:
q0 = "#{q[0]}_id"
q1 = "#{q[1]}"
Owner.find(widget[q0])[q1]
#Owner Load (0.6ms) SELECT "owners".* FROM "owners" WHERE "owners"."id" = $1 LIMIT 1 [["id", 9]]
#=> "Joe"
So in theory you split your query string on the dots, add "_id" to everyone except the last one, then chain those together using (potentially nested) .find
commands. Can it be done? Probably. Should it be done the way I described? Probably not.
If you give a more descriptive use case scenario people might be able to offer a much better answer. What are you really trying to do in the broader picture? There might be some other approach that completely negates the need for doing what you are asking.
Here is a less stringly typed version.
widget = Widget.first
query = 'owner.name'
name = query.split('.').inject(widget){|sum, meth| sum.send(meth)}
This will apply the methods separated by the dots subsequently to the object passed to inject.
You could even define the inject call as method in you models.
But beware that if someone injects a 'class.delete_all' into you properties, all your widgets would be deleted (propably applies to other solutions as well)
while you can use aval
and get what you want , its not the best way to go around with it.
So. it you are looking to append active record queries conditionally you should use merge
as an overview in your case (not the best example, but you get the idea)
widget = Widget.first
owner = Widget.first.owner
widget_owner = if <some condition>
widget.merge(owner)
end
widget_owner.try(:name)