Why MyModel.all works in Rails? - ruby-on-rails

i don't understand this little thing:
Suppose, we have "Condition" model
class Condition < ActiveRecord::Base
end
Why Condition.all works ?
Condition.all.each { |p| do_something }
This syntax tells us, that we have "Condition" class-object instanciated somewhere ?
Or is it some convention over configuration case ?
I asking this, because i want to override Condition.all method to return Conditions, sorted by "created_at" field value ?
I don't need to use sort method in place, i want to insert Conditions to, because in the entire project i need only one sorting
Thanks

Person.all is just an alias for Person.find(:all) (see the documentation here).
all, like find, is a class method on ActiveRecord::Base so doesn't require an instance in order to be called.
Update
To override a class method you need to remember the self. prefix. e.g. you can override all like this:
class Condition < ActiveRecord::Base
def self.all(*args)
# overridden implementation here
end
end
If you aren't clear on instance methods vs. class methods read this blog post which is a good summary,
However, if you just want to specify a default ordering you don't need to do this. You can just use default_scope:
class Condition < ActiveRecord::Base
default_scope :order => 'created_at'
end

Related

Rails; where to put query narrowing interface

I've been build web API with Rails.
For this case,
I need to have define some resources for API.
So it's hard to figure out where to put something to narrows query results.
We could do specify things in AR scope;
class Post < ActiveRecord::Base
scope :published, -> { where(published: true) }
end
or
with class method;
class Post < ActiveRecord::Base
def self.published
where(published: true)
end
end
and serverside resources (I use jsonapi-resource);
class ContactResource < JSONAPI::Resource
attributes :name_first, :name_last, :full_name
def full_name
"#{#model.name_first}, #{#model.name_last}"
end
def self.updatable_fields(context)
super - [:full_name]
end
def self.creatable_fields(context)
super - [:full_name]
end
end
What makes you decide to where to put those type of query narrowing interface.
And what's the difference among those. (Especially class method vs scope is very confusing.)
Any idea?
I suggest you read the scope documentation:
Unlike Shirt.find(...), however, the object returned by Shirt.red is
not an Array but an ActiveRecord::Relation, which is composable with
other scopes; it resembles the association object constructed by a
has_many declaration. For instance, you can invoke Shirt.red.first,
Shirt.red.count, Shirt.red.where(size: 'small'). Also, just as with
the association objects, named scopes act like an Array, implementing
Enumerable; Shirt.red.each(&block), Shirt.red.first, and
Shirt.red.inject(memo, &block) all behave as if Shirt.red really was
an array.
Basically, using a scope is more flexible. Personally, I'd start by using a scope, and fall back to using a class method if using scope causes an edge case problem.

Rails sorting gem

I'd like to create a gem that modifies ActiveRecord::Base select methods. For instance, when I include my gem and type:
Somemodel.all
it should return an array ordered by id as normally but in descending order instead of ascending. I have no clue how it should look. I would not like to create additional methods like:
Somemodel.where(name: "John").revert_it
but simply do:
Somemodel.where(name: "John")
I was wondering about modifying ActiveRecord::Base methods, but it does not make any sense. IMO the best way is to callback after every ActiveRecord::Base method that will do it.
You can use the ActiveRecord method default_scope to achieve this:
class MyModel < ActiveRecord::Base
default_scope { order("id DESC") }
end
MyModel.all
# => SELECT * FROM my_models ORDER BY id DESC
It's not advisable to modify any core ActiveRecord methods (unless you have a really good reason), because that will make lot of confusion in future.
Even if you are thinking of modifying the select , you have make sure that you always return an ActiveRecord relation , so that it can be chained with as the standard way,
For your example, AR already has a method
User.all.reverse
I got fully valid answer:
def self.included(base)
base.class_eval do
default_scope { order(id: 'desc') }
end
end
Above method should be inside some module and then module included within some model class

Access to query in concern module method

I have model user.rb and concern query_filter.rb.
module QueryFilter
extend ActiveSupport::Concern
def apply(attr)
end
end
class User < ActiveRecord::Base
extend QueryFilter
end
I would like to apply filters for whole model or for query.
For example:
> User.apply(attributes)
=> #query
> User.where(sex: 'male').apply(attributes)
=> #query
I have two problems.
First of all I don't know how can I access to query on which I have called my method in module method?
Secondly User.apply(attributes) won't work, I can use User.all.apply(attributes) but that's not the case. Is there any possibility to call method right after class name nor query ?
You have to include QueryFilter instead of extend
see http://api.rubyonrails.org/classes/ActiveSupport/Concern.html
But I think for the desired effect you should look into scopes more than concerns.

How to make default_scope in Rails model dynamic?

In one of my Rails models I have this:
class Project < ActiveRecord::Base
belongs_to :user
default_scope order("number ASC")
end
Now the problem is that I want each user to be able to set his or her default_scope individually. For example, a user A might want default_scope order("date ASC"), another one might want default_scope order("number DESC").
In my User table I even have columns to store these values: order_column and order_direction.
But how can I make the default_scope in the model dynamic?
Thanks for any help.
As #screenmutt said, default scopes are not meant to be data-driven, they are meant to be model driven. Since this scope is going to change according to each user's data I'd use a regular scope for this.
#fmendez answer is pretty good but it uses default scope which I just explained why it is not recommended using this method.
This is what I'd do in your case:
class Post < ActiveRecord::Base
scope :user_order, lambda { order("#{current_user.order_column} #{current_user.order_direction}")}
end
Also a very important thing to notice here is SQL injection: Since you are embedding current_user.order_column and current_user.order_direction inside your query, you MUST ensure that the user can only feed these columns into the database with valid data. Otherwise, users will be able to craft unwanted SQL queries.
You won't want to use default_scope. What you do what is regular scope.
class Post < ActiveRecord::Base
scope :created_before, ->(time) { where("created_at < ?", time) }
end
Scope | Ruby on Rails
You could do something like this:
def self.default_scope
order("#{current_user.order_column} #{current_user.order_direction}")
end
This should dynamically pick the values stored in the current_user's order_column and order_direction columns.
You can define a class method with whatever logic you require and set your default scope to that. A class method is identical to a named scope when it returns a relation,eg by returning the result of a method like order.
For example:
def self.user_ordering
# user ording logic here
end
default_scope :user_ordering
You may want to add a current_user and current_user= class methods to your User model which maintains the request user in a thread local variable. You would typically set the current user on your User model from your application controller. This makes current_user available to all your models for logic such as your sorting order and does it in a thread safe manner.

ActiveRecord Always Run Some Code After Retrieval

I'm trying to always run some code after .find or .where or whatever is used to retrieve objects.
For example, the following describes what I want, but does not work
Class Person < ActiveRecord::BA
#mortality=true
end
I want #mortality=true to run whenever a Person object is created
And based on my current understanding of ORM/ActiveRecord, a new object is created whenever retrieval is done. Hopefully that is correct.
You want to do this in the after_initialize method:
class Person < ActiveRecord::Base
def after_initialize
#mortality = true
end
end
Note that this is something you should avoid doing if possible because it happens on every object, even when you retrieve enormous result sets.
In this (albeit simple) case, you can do the assignment lazily by overriding the getter:
class Person < ActiveRecord::Base
def mortality
#mortality.nil? ? true : #mortality
end
end
(you can't use the nil gate ||= here because it filters false values as well)
http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html
Look for after_find and after_initialize callbacks.

Resources