why am i seeing User::ActiveRecord_Relation instead of ActiveRecord::Relation? [duplicate] - ruby-on-rails

Suppose I have a Rails model: class Project < ActiveRecord::Base
In the Rails console:
> Project.all
=> #<ActiveRecord::Relation []>
That seems reasonable. However,
> Project.all.class
=> Project::ActiveRecord_Relation
What is Project::ActiveRecord_Relation? Specifically,
How did it get "added" (namespaced into) to my model class?
How does it respond to is_a? correctly? Project.all.is_a?(ActiveRecord::Relation) returns true (which is expected), but is Project::ActiveRecord_Relation actually an instance of ActiveRecord::Relation, or is it something else?
Why do this? Why doesn't Project.all return an ActiveRecord::Relation, rather than Project::ActiveRecord_Relation?
(This is in the context of Rails 5.1, in case it's changed in older or newer versions.)
(I'm open to title edits if someone else can come up with a better title for this question)

Check this line of code from ActiveRecord.
https://github.com/rails/rails/blob/f40860800c231ecd1daef6cf6b5a8a8eda76478d/activerecord/lib/active_record/relation/delegation.rb#L23
mangled_name = klass.name.gsub("::", "_")
So, for your questions:
it get's added on activerecord's base when it extendes the delegation module https://github.com/rails/rails/blob/f40860800c231ecd1daef6cf6b5a8a8eda76478d/activerecord/lib/active_record/base.rb#L290
it's actually the same class, just something like an alias (not actually an alias, it's a constant with the class as value)
the class is actually an ActiveRecord::Relation, it's just that the name was changed

There are actually two questions you are asking:
How does it work?
Why is it like that? (What for?)
#arieljuod has already given you some explanations and a link.
However the second question is still unanswered.
There is another similar question exists which I hope will help you find all the answers:
How can an ActiveRecord::Relation object call class methods
It looks like the two questions (by the link and yours one) answer each other )
Take a look at #nikita-shilnikov's answer. Good luck in your investigation!

Related

Rails join query

I am pretty new in rails and honestly, I am struggling with queries even after multiple researchs.
Here is my simple schema:
So basically, a question has many options, an option belongs to a question and has many answer, and an answer belongs to an option and has many users.
I don't think it s necessary to post the models code since it is just like i mentioned above.
What i would like to do is given a question option, see if a particular user already checked it (so look in the answer table if there is a row matching a given id_option, user_id and user_type). So in my haml loop, when displaying the different question option, i'm calling a method of my question_option model just like this :
- question.question_option.all.each do |option|
#{option.title}
.check
- if option.selected_by(current_actor)
= check_box_tag(option.id, "checked",true, class: 'styled-checkbox')
- else
= check_box_tag(option.id, "checked",false, class: 'styled-checkbox')
and the method called :
def selected_by(answerer)
answer_match =
::Vacancies::QuestionOption
.joins(:answers)
.where(answerer_id: answerer.id, answerer_type:answerer.type )
response = answer_match.find(self.id)
return response
end
This method is located in my QuestionOption model and leads to no errors but it s not working ever.
Can you help me transform this query to make it work with ActiveRecord ? Thanks
Try the below code. It checks if there are any answers by the user passed in the params on the question. I think this is what you intended to do as well-
def selected_by(answerer)
answers =
Answer.where(answerer_id: answerer.id, answerer_type:answerer.type, id_option: self.id)
answers.exists?
end

Independent ActiveRecord query inside ActiveRecord::Relation context

There is some ruby on rails code
class User < ActiveRecord::Base
def self.all_users_count
User.all
end
end
User.all_users_count
returns, for example, 100
User.limit(5).all_users_count
Now it return 5 because of ActiveRecord::Relation context, in despite of i wroute name of class User.all instead simple all
(.to_sql show that query always contains limit or where id or other things in other cases)
So, how can i make context-independent AR queries inside model methods? like User.all and others?
Thank you!
Ps. Or maybe my code has an error or something like this, and in fact User.all inside any methods and context always must returns correct rows count of this model table
This is very weird and unexpected (unfortunately I can't confirm that, because my computer crashed, and have no rails projects at hand).
I would expect
User.all
to create a new scope (or as you call it - context)
Try working around this with
User.unscoped.all
Edit:
I tried it out on my project and on clean rails repo, and the results are consistent.
And after thinking a bit - this is maybe not even an issue - I think your approach could be faulty.
In what scenario would you chain User.limit(2).all_users_count ?? I can't think of any. Because either you need all users count, and you call User.all_usert_count (or just User.count)
... or you need something else and you call User.limit(2).where(...) - there's no point in calling all_users_count in that chain, is it?
And, when you think of it, it makes sense. Imagine you had some different method like count_retired, what would you expect from such call:
User.limit(2).count_retired ?
The number of retired users not bigger than 2, or the number of all retired users in the system? I would expect the former.
So I think one of two possibilities here:
either you implemented it wrong and should do it in a different way (as described above in the edit section)
or you have some more complex issue, but you boiled your examples down to a point where they don't make much sense anymore (please follow up with another question if you please, and please, ping me in the comment with a link if you do, because it sounds interesting)

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

Override ActiveRecord::Base find method (to accept non-default id field as a search parameter)

I have followed this tutorial on how to accept not-only-numeric primary key id when creating instance of my ModelName in my Ruby on Rails application.
Everything is okay, but there is a paragraph:
Be aware that Product.find won’t work anymore, and other Rails helper that relies on id will stop functioning. If you really want that, you need to override more methods and this seems too much of a pain for me. So I’d highly recommend you to leave #id as is.
The question is: when I am trying to get instance of my model by using .find() method in my ModelNameController it doesn't work (I think that's because of .find() method's search parameters - it does find something by id field which is numeric)
I have this piece of code:
def set_model_name
#model_name = ModelName.find(params[:hashid])
end
Where :hashid is a parameter that is a string (I'd like to use a string instead of a number)
How could I solve my problem?
One of the solutions would be overriding ActiveRecord::Base's .find() method.
Thanks in advance!
You do not need to override the default behaviour of find. Instead, you can use the find_by method:
def set_model_name
#model_name = ModelName.find_by(hashid: params[:hashid])
end

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