undefined method for #<Array - ruby-on-rails

I have this inside User model
def self.home_opinions (user)
home_opinions = user.opinions
user.follows.find_each do |follow|
home_opinions+=(follow.opinions)
end
home_opinions.order_by_most_recent
end
I have this scope inside Opinion model
scope :order_by_most_recent, -> { includes(:author).order(created_at: :desc) }
It shows this error
undefined method `order_by_most_recent' for #<Array:0x00007eff64d076f8>
But when I try User.home_opinions(User.find(9)) inside rails console
It works
I have two questions
why It shows the error
What are the best practices for this code maybe using includes?

.order_by_most_recent will only work on an ActiveRecord::Relation.
When you call home_opinions = user.opinions you get a relation object back.
The problem comes when you call
home_opinions += follow.opinions
That action converts the relation into an array, and then .order_by_most_recent is no longer available.
If you can, you should try and get all relevant opinions in a single ActiveRecord call. That way, you'll have an ActiveRecord::Relation object which you can chain with other scopes – plus, you can do everything in a fixed number of database calls, rather than an extra call for every member of the follows association.
Try something like:
opinion_owner_ids = [user.id] + user.follow_ids
home_opinions = Opinion.where(user_id: opinion_owner_ids)
home_opinions.order_by_most_recent

Related

Rails custom model method in where query

In my rails app I have defined in the Kid model a calculation based on the fields from the Kids DB. the method is as follows:
def flip_date
self.dob.advance(months: 10)
end
I want to use this in my controller as I have a method where I am defining something as follows:
new_kids = Kid.where(discharge_date: nil).where('flip_date > ?', Date.current.advance(year: 1).beginning_of_year)
However I keep getting the following error:
SQLite3::SQLException: no such column: flip_date: SELECT "kids".* FROM "kids" WHERE "kids"."discharge_date" IS NULL AND (flip_date < '2017-01-01')
Any ideas on how can I make this work? All help is appreciated!
If you really want to use model methods take a look at http://apidock.com/rails/v4.0.2/ActiveRecord/QueryMethods/select
For your case:
new_kids = Kid.where(discharge_date: nil).select{|k| k.flip_date > Date.current.advance(year: 1).beginning_of_year}
But select method takes every object in memory before returning final result. Hence I will advise to use normal where clause and instead of flip_date take dob (which is a column in database) in consideration.
Like this
new_kids = Kid.where(discharge_date: nil).where('dob > ?', <date criteria>)
The select method (http://apidock.com/rails/v4.0.2/ActiveRecord/QueryMethods/select) works great if you are okay with the return being an Array.
I am still looking for a way to do this with an ActiveRecord_Relation return.
If others know how to do this, it would be much appreciated if you can share.
This example doesn't respond to your specific code, but to the extent it helps someone else with a similar question, here's a very simple example of how .select can be really handy:
#expired_memberships = User.select{|u| u.membership_expired_yesterday?}
In that example you've looped through all your Users and filtered them based on a custom method you defined on the User model (membership_expired_yesterday?). Now you can easily do stuff with that collection like this example in a mailer:
#expirations.each do |user|
MembershipExpirationMailer.with(user: user).first_reminder.deliver_now
end

Rails: Can you impose multiple sort orders, in the same line?

For instance:
#examples = #user.examples.mostrecent.paginate(page: params[:page])
Where "mostrecent" is defined as:
def self.mostrecent
self.order('created_at DESC')
end
So basically the first call to the database is pull every User's example, and then on top of that, order them by most recent first. It seems like this should be doable, but for some reason I can't get it to work.
There is no defined order scope in the model I'm working with, and other calls to order work just fine. By checking the development.log I can see only the first database pulling example by users is respected. The mostrecent order is never called.
Is there a Rails way of doing this all in one line?
You could use a scope, as in:
scope :by_recent, lambda
{ |since_when| order("created_at") }

Chaining ActiveRecord scopes leads to undefined method for []:Array

I got this error only in test environment with RSpec. The code works in all other environments including Rails console.
Given this code
class User < ActiveRecord::Base
scope :flag, -> {where(flag: nil) }
end
When I call User.where(expired_at: nil).flag
Then it results in undefined methodflagfor []:Array
I'm using Rails 4.1.1 and rspec-rails 2.14.0.
With that stub, your "User.where(expired_at: nil)" will return [], which your code will take and then try to call "flag" on it. Since arrays in Ruby do not have a method called "flag", the exception you're seeing gets thrown. There are a few ways you can handle this.
1) Don't stub that call. Just let the framework handle it (and yes it will attempt to hit the db which may be what you're trying to avoid here but still, it's an option).
2) Return a test double and not an empty array, as in
results = double(:expired_users, flag: [])
User.stub(:where).and_return(expired_users)
See how flag is stubbed out there on ":expired_users" to return an empty array?
3) Add a method to your user model which basically runs what you want and stub that out, for example:
class User
def self.flagged_expired_users
where(expired_at: nil).flag
end
end
and in the spec
User.stub(:flagged_expired_users).and_return([])
I prefer #3 myself since that style of testing/mocking tends to be less brittle in my experience, but ymmv.
In my RSpec example I call User.stub(:where).and_return([]).
I replaced it by User.stub(:where).and_return(User.all)

Rails4: Get an ActiveRecord::Relation from a model or an already chained relation

I'm writing a module for ActiveRecord models. In short it's a method that can call a series of where, join and order statements. The statements are not known at the time of writing so it is not possible to use scopes. So far it works well but there is one point I'd like to improve.
Here is my method:
def filter
rel = respond_to?(:filter_scope) ? filter_scope : where(1)
# Do other stuffs with `rel`
# ...
rel
end
It first call filter_scope if it is defined, or it obtain an ActiveRecord::Relation from the target model. To do so I use where(1) to force the model to return a relation object. This works well whenever I call filter directly on the model (User.filter) or on a relation (User.order(:name).filter, User.my_scope.filter.order(:age) etc...)
But using where(1) fells a bit dirty. In Rails 3 I would be using all instead but it's depreciated in Rails 4. Any idea on how to improve this?
Thanks in advance
Note: I cannot substitute where(1) by self because there is a possibility that self would be returned from filter and User.filter would be a class, therefore not usable as a query object.
In Rails 3 I would be using all instead but it's depreciated in Rails 4.
I don't think all is depreciated, it used to return an Array (rails 3) and now returns an ActiveRecord::Relation so you should be able to use it for chaining queries.
see http://api.rubyonrails.org/classes/ActiveRecord/Scoping/Named/ClassMethods.html#method-i-all
Returns an ActiveRecord::Relation scope object.

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.

Resources