tng-rails-code-snippets-4#
SCOPE#
1#
class Person
scope :grouped_counts, group(:name).count
end
It does however work as a class method#
def self.grouped_counts
group(:name).count
end
In Rails 3, everything returns an ActiveRecord::Relation until you need the actual results, so scopes can be chained against class methods and vice versa (as long as the class methods return ActiveRecord::Relation objects, not some other object type (like a count)).
As Dylan alluded to in his answer, one difference between scope and class method is that scopes are evaluated when the class is loaded. This may lead to unexpected result.#
For example,
class Post < ActiveRecord::Base
scope :published_earlier, where('published_at < ?', Date.today)
end
is prone to error. The correct way is to use a lambda
class Post < ActiveRecord::Base
scope :published_earlier, -> { where('published_at < ?', Date.today) }
end
Lambda block is lazily evaluated. So Date.today is run when you call the scope, not when the class is evaluated.
If you use a class method, then you don't need to use lambda.
class Post < ActiveRecord::Base
def self.published_earlier
where('published_at < ?', Date.today)
end
end
Because with class method, the code is run at the time of method call.
Defining a scope#
First of all, lets get a better understanding about how scopes are used. In Rails 3 you can define a scope in two ways:
class Post < ActiveRecord::Base
scope :published, where(status: 'published')
scope :draft, -> { where(status: 'draft') }
end
in Rails 4 the first way is going to be deprecated which means you will always need to declare scopes with a callable object as argument. This is to avoid issues when trying to declare a scope with some sort of Time argument:#
class Post < ActiveRecord::Base
scope :published_last_week, where('published_at >= ?', 1.week.ago)
end
Because this won’t work as expected: 1.week.ago will be evaluated when the class is loaded, not every time the scope is called.
How to json a computed field#
You can overwrite the to_json method in your model by passing :methods options and call super. This will call as_json version of the super class with :methods options so that it will serialize your model with full_name attributes.
class Person < ActiveRecord::Base
def as_json(options={})
options[:methods] = [:full_name]
super
end
def full_name
"#{first_name} #{last_name}"
end
end
Check this document out if you want to know more options that can pass to as_json method.
Inside your controller, you can simply
render :json => @person
Specifically the :methods hash:
respond_with({
:cars => @cars.as_json(:only => [:make, :model], :methods => [:full_name]),
:vans => @vans
})
Alternatively#
What you can do is have a setter method in your User class like so
def full_name=(string)
names = string.split # the default delimiter is a space
self.update_attributes(:first_name => names[0], :last_name => names[1])
end
The form should then refer to :full_name as a field, and even though it's not an actual column, Rails will automatically access this method above and execute it, thereby updating the columns you actually want.