Using class methods in named_scope and rspec - ruby-on-rails

I may have painted myself into a corner.
In some of my rails (2.3.18) named_scopes I've used class methods to retrieve known rows from the database - for example status values.
However, when I try to use these with rspec, I think I've got a problem because the fixtures (I'm using FactoryGirl) haven't loaded before the app gets loaded - so I get an error when its parsing the named_scopes (I think).
For example:
named_scope :active_users, :conditions => [ 'status_id = ?', UserStatus.Active.id ]
When the user model is loading it gives an error to effect
app/models/user.rb:34: Called id for nil, which would mistakenly be 4
which is the named_scope line.
user_status.rb
def self.Active
UserStatus.find_by_name('active')
end
So I think I've got two questions:
Is this an abuse of named_scope and if so what would be a better way of writing it?
Is it possible to get rspec to load some key data into the database before it loads the application?
Thanks

Your named scope is written fine. You need to check output of UserStatus.Active.id. It should return array of ids. As per the naming convention if you have written method named 'Active' in UserStatus then it is wrong. It should be in lowercase.
Also I do not understand the use of id in => UserStatus.Active.id. Can you put this method here?

UserStatus.Active must be giving you nil so
UserStatus.Active.id giving you this error. Because id of nil is 4. Make sure you are getting a record in Active method

Managed to answer my own question and am putting it here in case anyone else has the same issue.
To ensure that an attempt to access the database when the named_scope is parsed is avoided, I needed to wrap the :condition in a lamdba / proc as below
named_scope :active_users, lambda {{ :conditions => [ 'status_id = ?', UserStatus.Active.id ] }}
This now allows the application to be loaded, and then the data required for the tests to be loaded into the database ahead of the test as usual.

Related

Weird and inconsistent Model issues

I feel like I'm missing something rather important in both circumstances, but I can't seem to figure either out:
1) I have a model named TestCase -
class TestCase < ActiveRecord::Base
belongs_to :test_suite
scope :queued, lambda { where("test_cases.suite_id IS NOT NULL") }
scope :assigned_to, lambda { |sid| where(:suite_id => sid) }
end
The controller can interact with it perfectly fine. When trying to display information from it in either the view or via the view helper such as TestCase.all, I get a NoMethodError (undefined method 'all') If I call it with ::TestCase.all, that works. I had a theory that it has something to do with the fact that it's associated to another model (belongs_to ...), I just can't find anything to confirm that or tell me why that happens.
2) On another project I have yet another model named Artwork. Again, it has associations (belongs_to). In this case, I can access it just fine in the view, and all the methods within it work fine for the controller except if I try to do dynamic method calls. In this case I have a simple toggle for -
#artwork = Artwork.find(params[:id])
value = params[:value].to_sym
#artwork.update_attributes(value => !#artwork.method(value).call)
That gives me a NoMethodError. However, if I add - if #artwork.respond_to?(value) - then it works as expected. Again, I can't figure out why.
Both items I get working using the mentioned methods, but again, I feel like I'm really missing something important here.
Re: problem 1 -- Don't call your model "TestCase". That conflicts with the Rails TestCase class.
Re: problem 2 -- That's an odd way of doing things. You might get it working by using
#artwork.send(value)
but keep in mind that a rogue user could pass in any method name through the form and wreak havoc.

Empty Scope with Ruby on Rails

Following Problem:
I need something like an empty scope. Which means that this scope is emtpy, but responds to all methods a scope usually responds to.
I'm currently using a little dirty hack. I simply supply "1=0" as conditions. I find this realy ugly, since it hits the database. Simply returning an empty array won't work, since the result must respond to the scoped methods.
Is there a better existing solution for this or will I need to code this myself?
Maybe some example code could help explain what i need:
class User < ActiveRecord::Base
named_scope :admins, :conditions => {:admin => true }
named_scope :none_dirty, :conditions => "1=0" # this scope is always empty
def none_broken
[]
end
def self.sum_score # okay, a bit simple, but a method like this should work!
total = 0
self.all.each do |user|
total += user.score
end
return total
end
end
User.admin.sum_score # the score i want to know
User.none_drity.sum_score # works, but hits the db
User.none_broken.sum_score # ...error, since it doesn't respond to sum_score
Rails 4 introduces the none scope.
It is to be used in instances where you have a method which returns a relation, but there is a condition in which you do not want the database to be queried.
If you want a scope to return an unaltered scope use all:
No longer will a call to Model.all execute a query immediately and return an array of records. In Rails 4, calls to Model.all is equivalent to now deprecated Model.scoped. This means that more relations can be chained to Model.all and the result will be lazily evaluated.
User.where('false')
returns an ActiveRecord::Relation with zero elements, that is a chain-able scope that won't hit the database until you actually try to access one of its elements. This is similar to PhilT's solution with ('1=0') but a little more elegant.
Sorry User.scoped is not what you want. As commented this returns everything. Should have paid more attention to the question.
I've seen where('1 = 0') suggested before and Rails should probably cache it as well.
Also, where('1 = 0') won't hit the database until you do .all, .each, or one of the calculations methods.
I thing you need User.scoped({})
How about User.where(id: nil) ?
Or User.where(_id: nil) for mongoid.
The thing you are looking for does not exist. You could implement something like this by monky patching the find method. Yet, this would be an overkill, so I recomend keeping this unless it's performance critical.
Looking at your example code indicates you may not know about aggregated queries in SQL which are exposed as calculations methods in Rails:
User.sum(:score) will give you the sum of all users' scores
Take a look at Rails Guides for more info:
http://guides.rubyonrails.org/active_record_querying.html#sum

rails/activerecord search eager loaded associations

I have a simple find statement as such:
m = MyModel.find(1, :include => :my_children)
With m.mychildren being an Array; is there anyway to find a particular record from within the array without having to iterate over the entire thing. If I do mychildren.find(1), a new DB query is issues, which doesn't make sense, since they are all loaded already
It looks like there's a little Rails magic going on here. Where Enumerable#find is being overridden by ActiveRecord::Base#find on methods created for associations.
On the upside Enumerable#find is aliased to Enumerable#detect.
Unfortunately Enumerable#find/Enumerable#detect have significantly different syntax from ActiveRecord::Base#find.
So you can't just do mychildren.find(1), instead you've got to do mychildren.detect{|c| c.id == 1} if you want to avoid hitting the database again. You may also want to consider extending Array for a more DRY way of doing this.
class Array
def id_find id
self.detect{|element| element.id == id}
end
end
I'm not quite sure what your asking, but have you tried select:
m.mychildren.select{ |child| child == <<some_statement>> }
This won't hit the database assuming you've used the :include option as you stated in your question.
Alternatively, if you know the number of the child you want, you should be able to just use
m.mychildren[1]

Get the SQL that would be executed from a certain method or named_scope

Given an ActiveRecord method or named_scope chain, is there a way to return the SQL that will get executed, without actually running it?
e.g.
Vote.positive.count.sql #=> "SELECT COUNT(*) FROM votes WHERE value > 0"
Is there a built-in way to do this or a plug-in that offers this functionality? If not, any clues to where I can start to build my own plug-in or at least a solution for this current project.
Cheers
You can get information out of a named scope like so:
Vote.positive.proxy_options
As for getting the full SQL statement, ActiveRecord puts this together using a protected method called construct_finder_sql.
So you could try this:
Vote.positive.send(:construct_finder_sql, {})
The method used is different for calculation queries:
Vote.positive.send(:construct_calculation_sql, :count, :id, {})
in both cases the empty hash argument can have keys like :conditions, :order, :limit or some additional options you may discover by inspecting the bodies of these methods.
It's been so long but I thought it'd be useful to point out that Rails3 will have support for this on any scope/finder/model method that ends up hitting the DB.

How to get array of many_to_many assocation from array in Rails

I have the following code in Rails:
#possibleMatchingOffers = SmsOffer.valid.find(:all, :conditions => {:hub_phone_no => unhndledMsg.hub_phone_no})
#matchingContact = #possibleMatchingOffers.biz_sms_reply_queues.valid.find(:all)
The Error I'm getting:
You have a nil object when you didn't expect it!
You might have expected an instance of Array.
The error occurred while evaluating nil.each
#possibleMatchingOffers is an array so it's not accepting to use the association (.biz_sms_reply_queues).
I can figure out manual way to do it but I was wondering if there is better easy way to do this.
Thanks,
Tam
Without knowing what you're intention is here, it looks like you need to turn the first "find" into an named scope. So it would look something like:
SmsOffer.valid.by_hub_phone_no(unhndledMsg.hub_phone_no).biz_sms_reply_queues
Named scopes return AR Proxy objects, and thus, you can use an association on them.
What is "valid"? This isn't a rails method. Is it also a named scope? You should probably dry that up also.

Resources