Rails: any way to refactor this ActiveRecord code? - ruby-on-rails

I have a piece of code that checks that a survey response picked by user to a survey question is in fact one of valid choices:
Question.find_by_id(question_id).question_choices.all(:select => 'id').map {|x| x.id}.include?(user_choice_id)
Is there an easier way?
Thanks!

At the very least the question_choices.all(:select => 'id').map {|x| x.id} component can be rewritten, as ActiveRecord provides a method for this question_choice_ids.
You can also just do find instead of find_by_id. I know that find will raise an exception if nothing is found, but so will calling question_choices on nil in your example, anyway:
Question.find(question_id).question_choice_ids.include?(user_choice_id)
# or
# Rails 2 (will run 2 queries unless you use :include)
Question.find(question_id).question_choices.first(:conditions => {:id => user_choice_id})
# Rails 3 (will only run 1 query)
Question.find(question_id).question_choices.where(:id => user_choice_id).first

Related

Rails Active Record method to ensure query returns single record?

Is there something in Active Record that ensures that your query does NOT return more than one record?
This is what the basic functionality would be (apologies--this isn't real code but just enough to give the idea of what I'm looking for):
Foo.where(:thing => 'this_should_be_uniq').single
def single(records)
if records.length > 1
raise # or maybe return nil or something like that
else
return records.first
end
end
Essentially, this would be a safeguard against accidentally assuming (incorrectly) that your query will always return a single record.
Thanks!
If i'm understanding your question correctly, you can use limit
Foo.where(:thing => 'this_should_be_uniq').limit(1)
you can do Foo.where(:thing => 'this_should_be_uniq').single or Foo.where(:thing => 'this_should_be_uniq').single or .limit(1)
Foo.where(:thing => 'this_should_be_uniq').first
Foo.where(:thing => 'this_should_be_uniq').last
Foo.where(:thing => 'this_should_be_uniq').limit(1)
I cannot find such method in ActiveRecord::FinderMethods.
As alternative solution, you can write it shorter using tap method in the case of raising the exception if more than two records exists:
Foo.where(:thing => 'this_should_be_uniq').tap { |r| raise "ERROR" if r.count > 1 }.first
Considering isolation from other operations, the following code is proper:
Foo.where(:thing => 'this_should_be_uniq').to_a.tap { |r| raise "ERROR" if r.size > 1 }[0]
You can also use ActiveRecord find
Foo.find_by_thing('this_should_be_uniq')
Foo.find(:first, :conditions => {:thing => 'this_should_be_uniq'})
You can also find with multiple attributes
Foo.find_by_attr1_and_attr2(attr1_value, attr2_value)

How to update a column without loading the object in ActiveRecord

Foo.where(:some_id => 1).update_all(:some_columnn => "1")
Is this the right way to update Foo? I don't want to do a find and update the object.
As of Rails 4, the conditions are no longer supplied on the update_all method, but are instead specified on the preceding collection. For example,
# updates everything, as usual
Foo.update_all(some_column: '1')
# update only the specified rows
Foo.where(some_id: 1).update_all(some_column: '1')
Yes it is the right way, but remember, no callbacks or validations will be executed.
BTW, update_all accepts conditions also. Like this
Foo.update_all({:some_columnn => "1"}, {:some_id => 1})
It is the right approach if you don't want to instantiate an object, but keep in mind that this also means it won't perform any of your models validations or callbacks - it goes straight to a SQL update command.
Further information
You can use conditions,according to the api of update_all
update_all(updates, conditions = nil, options = {})
So you can do:
Foo.update_all(:some_column => '1', :some_id => 1)

CanCan and Mongoid "or" do not play nice

I want to get all activities that a user owns or has created, so I join it using or:
Activity.or(owner_id: 123).or(creator_id: 123).selector
# => {"$or"=>[{"owner_id"=>123}, {"creator_id"=>123}]}
Now I try to use CanCan on that.
Activity.accessible_by(current_ability)
# => {"$or"=>[{"privacy"=>"public"}, {"user_id"=>"51091cc977bb1eb27a000003"}]}
CanCan creates an or selector by default, as there are more than one rule in the ability.
It would be intuitive now to do just this:
Activity.or(owner_id: 123).or(creator_id: 123).accessible_by(current_ability)
# => {"$or"=>[{"owner_id"=>123}, {"creator_id"=>123}, {"privacy"=>"public"}, {"user_id"=>"51092b9777bb1ec385000003"}]}
But this joins the both or Arrays into one which is not what I want, so I did the following:
Activity.or(criteria).and(Activity.accessible_by(current_ability).selector).desc(:created_at)
# => {"$or"=>[{"owner_id"=>123}, {"creator_id"=>123}], "$and"=>[{"$or"=>[{"privacy"=>"public"}, {"user_id"=>"51092b9777bb1ec385000003"}]}]}
But this seems a bit unclean. Any idea on how to beautify this? Thank you.
PS: An afterthought: Should accessible_by not always return a {'$and' => {'$or' => [...]}} instead of only a {'$or' => [...]}?

ArgumentError recursive array join

Calling method "polymorphic_url" in controller or template with array as argument, like:
polymorphic_url([#agency, #agency.divisions.first])
causing ArgumentError exception named "recursive array join". Any suggestions?
I can reproduce this exception with any of models:
#e = Estate.where(:booklets => {'$exists' => true}).first
#b = #e.booklets.first
polymorphic_url [#e,#b]
rails 3.2.3, 3.2.4, 3.2.5
ruby 1.9.2, 1.9.3
You can create your Error with an Array which contains a reference to itself:
a = []
a<<a
a.join #ArgumentError: recursive array join
I'm guessing here, but if divisions points to the same array as #agencie( for instance an agency being it's own division) I can imagine something like above happening. May be it does not have anything to do with updates but with the data.
I believe you are misusing it. According to APIDock, here are some examples of polymorphic_url use:
# calls post_url(post)
polymorphic_url(post) # => "http://example.com/posts/1"
polymorphic_url([blog, post]) # => "http://example.com/blogs/1/posts/1"
polymorphic_url([:admin, blog, post]) # => "http://example.com/admin/blogs/1/posts/1"
polymorphic_url([user, :blog, post]) # => "http://example.com/users/1/blog/posts/1"
polymorphic_url(Comment) # => "http://example.com/comments"
So maybe you should use:
polymorphic_url([#agency, #division])
Im solve this issue by forcing application to use bson '1.6.2'
https://github.com/mongoid/mongoid/issues/2069

How to test a scope in Rails 3

What's the best way to test scopes in Rails 3. In rails 2, I would do something like:
Rspec:
it 'should have a top_level scope' do
Category.top_level.proxy_options.should == {:conditions => {:parent_id => nil}}
end
This fails in rails 3 with a "undefined method `proxy_options' for []:ActiveRecord::Relation" error.
How are people testing that a scope is specified with the correct options? I see you could examine the arel object and might be able to make some expectations on that, but I'm not sure what the best way to do it would be.
Leaving the question of 'how-to-test' aside... here's how to achieve similar stuff in Rails3...
In Rails3 named scopes are different in that they just generate Arel relational operators.
But, investigate!
If you go to your console and type:
# All the guts of arel!
Category.top_level.arel.inspect
You'll see internal parts of Arel. It's used to build up the relation, but can also be introspected for current state. You'll notice public methods like #where_clauses and such.
However, the scope itself has a lot of helpful introspection public methods that make it easier than directly accessing #arel:
# Basic stuff:
=> [:table, :primary_key, :to_sql]
# and these to check-out all parts of your relation:
=> [:includes_values, :eager_load_values, :preload_values,
:select_values, :group_values, :order_values, :reorder_flag,
:joins_values, :where_values, :having_values, :limit_value,
:offset_value, :readonly_value, :create_with_value, :from_value]
# With 'where_values' you can see the whole tree of conditions:
Category.top_level.where_values.first.methods - Object.new.methods
=> [:operator, :operand1, :operand2, :left, :left=,
:right, :right=, :not, :or, :and, :to_sql, :each]
# You can see each condition to_sql
Category.top_level.where_values.map(&:to_sql)
=> ["`categories`.`parent_id` IS NULL"]
# More to the point, use #where_values_hash to see rails2-like :conditions hash:
Category.top_level.where_values_hash
=> {"parent_id"=>nil}
Use this last one: #where_values_hash to test scopes in a similar way to #proxy_options in Rails2....
Ideally your unit tests should treat models (classes) and instances thereof as black boxes. After all, it's not really the implementation you care about but the behavior of the interface.
So instead of testing that the scope is implemented in a particular way (i.e. with a particular set of conditions), try testing that it behaves correctly—that it returns instances it should and doesn't return instances it shouldn't.
describe Category do
describe ".top_level" do
it "should return root categories" do
frameworks = Category.create(:name => "Frameworks")
Category.top_level.should include(frameworks)
end
it "should not return child categories" do
frameworks = Category.create(:name => "Frameworks")
rails = Category.create(:name => "Ruby on Rails", :parent => frameworks)
Category.top_level.should_not include(rails)
end
end
end
If you write your tests in this way, you'll be free to re-factor your implementations as you please without needing to modify your tests or, more importantly, without needing to worry about unknowingly breaking your application.
This is how i check them. Think of this scope :
scope :item_type, lambda { |item_type|
where("game_items.item_type = ?", item_type )
}
that gets all the game_items where item_type equals to a value(like 'Weapon') :
it "should get a list of all possible game weapons if called like GameItem.item_type('Weapon'), with no arguments" do
Factory(:game_item, :item_type => 'Weapon')
Factory(:game_item, :item_type => 'Gloves')
weapons = GameItem.item_type('Weapon')
weapons.each { |weapon| weapon.item_type.should == 'Weapon' }
end
I test that the weapons array holds only Weapon item_types and not something else like Gloves that are specified in the spec.
Don't know if this helps or not, but I'm looking for a solution and ran across this question.
I just did this and it works for me
it { User.nickname('hello').should == User.where(:nickname => 'hello') }
FWIW, I agree with your original method (Rails 2). Creating models just for testing them makes your tests way too slow to run in continuous testing, so another approach is needed.
Loving Rails 3, but definitely missing the convenience of proxy_options!
Quickly Check the Clauses of a Scope
I agree with others here that testing the actual results you get back and ensuring they are what you expect is by far the best way to go, but a simple check to ensure that a scope is adding the correct clause can also be useful for faster tests that don't hit the database.
You can use the where_values_hash to test where conditions. Here's an example using Rspec:
it 'should have a top_level scope' do
Category.top_level.where_values_hash.should eq {"parent_id" => nil}
end
Although the documentation is very slim and sometimes non-existent, there are similar methods for other condition-types, such as:
order_values
Category.order(:id).order_values
# => [:id]
select_values
Category.select(:id).select_values
# => [:id]
group_values
Category.group(:id).group_values
# => [:id]
having_values
Category.having(:id).having_values
# => [:id]
etc.
Default Scope
For default scopes, you have to handle them a little differently. Check this answer out for a better explanation.

Resources