Upgrading Rails 3.2. to Rails 4. I have the following scope:
# Rails 3.2
scope :by_post_status, lambda { |post_status| where("post_status = ?", post_status) }
scope :published, by_post_status("public")
scope :draft, by_post_status("draft")
# Rails 4.1.0
scope :by_post_status, -> (post_status) { where('post_status = ?', post_status) }
But I couldn't find out how to do the 2nd and 3rd lines. How can I create another scope from the first scope?
Very simple, just same lambda without arguments:
scope :by_post_status, -> (post_status) { where('post_status = ?', post_status) }
scope :published, -> { by_post_status("public") }
scope :draft, -> { by_post_status("draft") }
or more shorted:
%i[published draft].each do |type|
scope type, -> { by_post_status(type.to_s) }
end
From the Rails edge docs
"Rails 4.0 requires that scopes use a callable object such as a Proc or lambda:"
scope :active, where(active: true)
# becomes
scope :active, -> { where active: true }
With this in mind, you can easily rewrite you code as such:
scope :by_post_status, lambda { |post_status| where('post_status = ?', post_status) }
scope :published, lambda { by_post_status("public") }
scope :draft, lambda { by_post_status("draft") }
In the event that you have many different statuses that you wish to support and find this to be cumbersome, the following may suit you:
post_statuses = %I[public draft private published ...]
scope :by_post_status, -> (post_status) { where('post_status = ?', post_status) }
post_statuses.each {|s| scope s, -> {by_post_status(s.to_s)} }
Related
I've got a scope which does the fetching
scope :between, -> (start, endd) {
where(:start_time => start..endd}
}
and a custom method
def time_of_day
Helper.time_of_day(start_time || est_start_time )
end
I want to be able to group the results by a custom method
Class.between
Class.between.time_of_start
So far, I've tried
def self.custom_sort
self.group_by { |a| a.time_of_start }
end
But I get
NoMethodError: undefined method `by_time' for Instance::ActiveRecord_Relation:0x00000005537e60>
I know I can do things like
Class.between.group_by { |a| a.time_of_start}
But I want to define a custom 'scope'
Try adding .all like:
self.all.group_by { |a| a.time_of_start }
Reference
Try this:
scope :between, -> {
where(...)
}
scope :grouped, -> {
group_by { |a| a.custom_method }
}
private
def custom_method
#some manipulation
end
The you can call:
Class.between.grouped
If I want to make the following scopes available to multiple models how do I do so without having to add them directly into each model?
scope :today, -> { where("DATE(created_at) = DATE(?)", Date.today ) }
scope :yesterday, -> { where("DATE(created_at) = DATE(?)", 1.day.ago) }
scope :last_week, -> { where("DATE(created_at) = DATE(?)", 1.week.ago) }
One of the prescribed ways is by using concerns.
You should be able to create a file like this at app/models/concerns/dateable.rb:
module Dateable
extend ActiveSupport::Concern
included do
scope :today, -> { where("DATE(created_at) = DATE(?)", Date.today ) }
scope :yesterday, -> { where("DATE(created_at) = DATE(?)", 1.day.ago) }
scope :last_week, -> { where("DATE(created_at) = DATE(?)", 1.week.ago) }
end
end
Then include it into the models that need it.
class Employee < ApplicationRecord
include Dateable
end
class Customer < ApplicationRecord
include Dateable
end
I have the following method that I'd like to move to named scope
def self.running_promotion
Supplier.all.select { |s| s.has_running_promotion? == true }
end
Not sure how to use lambda with rails 4 scope or if this is possible. I tried
scope :on_sale, -> { where({ |s| s.has_running_promotion? == true }) }
if has_running_promotion is field in your table you could write:
scope :on_sale, -> { where(has_running_promotion: true) }
i wrote this scope for my User model but it isn't working because of the 'or'.
How can i do ?
scope :offline, ->{ where((online: false).or(name: 'Undefined')) }
Thanks
Try this
scope :offline, lambda { where( "online = ? OR name = ?", false, 'undefined') }
I've seen a lot of posts regarding this, but none seem to solve my problem. I have a default_scope on a model like so:
default_scope where(:is_active => true).order('LOWER(table.name)');
I have other (normal) scopes, and I want to create an inactive scope using unscoped. I would like to define it as a scope, but it only works when defined as a class method:
# works
def self.inactive
unscoped { where(:is_active => false) }
end
# none of these work
scope :inactive, unscoped { where(:is_active => false) }
scope :inactive, with_exclusive_scope { where(:is_active => true) }
scope :inactive, unscoped.where(:is_active => false)
scope :inactive, lambda { unscoped { where(:is_active => false) } }
scope :inactive, unscoped { lambda { where(:is_active => false) } }
unscoped do
scope :inactive, where(:is_active => false)
end
Is there a way that I missed, or do I have to use a class method to define this scope?
There does not seem to be a way to do this. I opened an issue on the rails repo on github...
Try this
scope :inactive, lambda { unscoped.where(:is_active => false) }