Hacking ActiveRecord: add global named scope
Rails 5, ApplicationRecord (Hope it helps others)
# app/models/concerns/not_older_than.rb
module NotOlderThan
extend ActiveSupport::Concern
included do
scope :not_older_than, -> (time, table = self.table_name){
where("#{table}.created_at >= ?", time.ago)
}
end
end
# app/models/application_record.rb
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
include NotOlderThan
end
# app/models/user.rb
class User < ApplicationRecord
# Code
end
# Usage
User.not_older_than(1.week)
In Rails 5, all models are inherited from ApplicationRecord by default. If you wan to apply this scope for only particular set of models, add include statements only to those model classes. This works for join queries and chained scopes as well.
Access named scope dynamically
You would call Foo.public_send(variable)
.
ActiveRecord list custom scopes
The ActiveRecord::Scoping::Named::ClassMethods#scope
DSL helper just creates a new method, it does not store the scope name anywhere, so no, it’s not possible out of the box.
OTOH, one might easily provide such a functionality:
ActiveRecord::Scoping::Named::ClassMethods.prepend(Module.new do
def scope(name, body, &block)
(@__scopes__ ||= []) << name
super
end
end)
and then the instance variable on your class would be defined:
MyRecord.instance_variable_get(:@__scopes__)
#⇒ [:custom_scope_one, :custom_scope_two, :custom_scope_three]
You might also declare an accessor for this instance variable or whatever.
NB the code above is not tested, I only proved it looks ok.
Rails 4 named scope with record always at end
This is a little bit tricky. In SQL you can add CASE
statements to your ORDER BY
. In your case the SQL would be something similar to.
SELECT *
FROM categories
ORDER BY
(
CASE
WHEN name = 'Other' THEN 1
ELSE 0
END
)
Here's a live example.
As far as I know, the ActiveRecord order
method accepts arbitrary string, so you could (not tested) be able to pass the case to the method
Category.order("CASE WHEN name = 'Other' ... ")
This approach seems complicated, but if you can get it to work is by far the most efficient.
The second alternative is to play a little bit with ActiveRecord.
class Category < ActiveRecord::Base
def self.ordered(condition = nil)
# Get the ID of the record to exclude
excluded = self.where(name: 'Other').pluck(:id).first
# order the records and collect the ids
ids = where('id <> ?', excluded).order(condition).pluck(:id)
# append the excluded at the end
ids << excluded
# recreate the scope and return it.
where(id: ids)
end
end
Category.where(...).ordered
Generally speaking, I encourage you to avoid default_scopes
in ActiveRecord. It's so easy to add them, but very hard to remove them when you need.
Rails3: combine scope with OR
From Arel documentation
The
OR
operator is not yet supported. It will work like this:
users.where(users[:name].eq('bob').or(users[:age].lt(25)))
This RailsCast shows you how to use the .or
operator. However, it works with Arel objects while you have instances of ActiveRecord::Relation
.
You can convert a relation to Arel using Product.name_a.arel
, but now you have to figure out how to merge the conditions.
How can I use multiple scopes to construct a query?
Found it!
Looks like merge
can be used:
http://api.rubyonrails.org/classes/ActiveRecord/SpawnMethods.html#method-i-merge
Equivalent to += (plus equal) to add scopes with ActiveRecord in rails
Chain them like this
@list = Product.all
@list = @list.scope1 if condition1
@list = @list.scope2 if condition2
@list
Then in the end @list
holds the elements you want.
Programmatically apply an ActiveRecord scope that accepts arguments
Object#send
accepts the name of the method and its arguments:
name = :age_range
low = 45
high = 55
Employee.send(name, low, high)
I would recommend to use Object#public_send
, to make sure you only use publicly accessible API:
Employee.public_send(name, low, high)
Related Topics
Xpath Expression for Regex-Like Matching
Trying to Install Ruby-Filemagic on Snow Leopard Using Brew Rather Than Ports
Sort a Collection of Objects by Number (Highest First) Then by Letter (Alphabetical)
How to Check If a Ruby Array Includes One of Several Values
Putting the Results of Pp (Or Anything Outputted to Console) into a String
Use of Caret Symbol (^) in Ruby
Missing Host to Link To! Please Provide the :Host Parameter, for Rails 4
Rails Date Format in Form Field
Ruby Forgets Local Variables During a While Loop
Disable Sprockets Asset Caching in Development
How to Insert a String into a Textfile
How to Fire Raw Mongodb Queries Directly in Ruby
How to Install Redcloth on Windows
Has Anyone Successfully Deployed a Rails Project with Ruby 1.9.1
Activerecord::Connectiontimeouterror