Keeping named_scope Extensions DRY - ruby-on-rails

In Rails, you can add a block after a named_scope for additional, context-sensitive methods like so:
class User < ActiveRecord::Base
named_scope :inactive, :conditions => {:active => false} do
def activate
each { |i| i.update_attribute(:active, true) }
end
end
end
In this example, the activate method is being defined not on the User class, but on the ActiveRecord::NamedScope::Scope object.
I have a series of three scopes that need to have identical method definitions. In the interests of not repeating code, how would I abstract that block such that I could define it once and pass it to each named_scope?

Firstly, great question--I didn't know about that feature of named scopes! The following works for me:
class User < ActiveRecord::Base
add_activate = lambda do
define_method "activate" do
each { |i| i.update_attribute(:active, true) }
end
end
named_scope :inactive, :conditions => {:active => false}, &add_activate
end
You can pass the add_activate block as the last argument to any named scopes that need the activate method.

Much better:
http://tuxicity.se/rails/dry/2009/01/04/share-named-scopes-in-rails.html
module NamedScope
def self.included(base)
base.class_eval do
named_scope :inactive, :conditions => {:active => false} do
def activate
each { |i| i.update_attribute(:active, true) }
end
end
end
end
end
Save in your /lib directory (put a require in an initializers in rails 3) and include NamedScope in your User class

Related

How to display scopes based on the role of the user in active admin?

I am getting an error:
undefined local variable or method `current_admin_user' for #<ActiveAdmin::ResourceDSL
How can I access current_admin_user object here?
ActiveAdmin.register Job do
def quick_filters
if current_admin_user.super_admin?
[:all, :draft, :scheduling, :rejected]
else
[:all, :scheduling, :rejected]
end
end
quick_filters.each do |a|
scope a
end
end
This is working:
controller do
def scoped_collection
#jobs = if current_admin_user.super_admin?
super.includes :trip, :car, :concierge, :creator, :job_template
else
super.where(concierge_id: current_admin_user.id).includes :trip, :car, :concierge, :creator, :job_template
end
end
end
I think this should work:
ActiveAdmin.register Job do
scope :all
scope :draft, :if => proc { current_admin_user.super_admin? }
scope :scheduling
scope :rejected
end
The DSL for setting up scopes is evaluated at server startup time and does not have access to request cycle based data (like the logged in user). To get around this ActiveAdmin has the concept of wrapping code code with proc so that the enclosed code evaluation is deferred to request-cycle-time.
Good luck!

RailsAdmin - pass parameters to scopes

I defined several scopes on my model and use them in rails_admin.
scope :foo , ->() { where(status: 'active')}
...
list do
scopes [nil, 'foo']
I want to create scopes where I can pass parameters and then load those from rails_admin. Something like this:
scope :bar , ->(query) { where(status: query)}
But because RA does not pass parameter I keep getting
wrong number of arguments (0 for 1)
Does anyone have any suggestions? I am using Rails 4.1.14 with rails_admin 0.8.1 and mongoid 5.0
I solved this via creating custom action and then specify it to use index template. It shows up next to List, Add New and other actions instead of below filters.
class Article
include Mongoid::Document
field :title, type: String
...
belongs_to :user, counter_cache: true
scope :by_user, ->(user) { where(user: user) }
...
end
# in rails_admin.rb
class Myarticles < RailsAdmin::Config::Actions::Base
RailsAdmin::Config::Actions.register(self)
register_instance_option :collection do
true
end
register_instance_option :only do
Article
end
register_instance_option :controller do
proc do
#objects = Article.by_user(current_user)
render :index
end
end
end
Related question Rails_admin custom action specify template name
Another way is using cancan. You can do this in the ability file:
def initialize(user)
can [:read], Profile, user_id: user.id
end

Define Custom Object Lifecycle Hooks In Ruby

So I have a object and I want to define a lifecycle hook such as before_create, after_create, etc.
I want to call this after_retire and have it setup so I can do the following:
class User < ActiveRecord::Base
include Active
after_retire :method
def method
#do stuff
end
end
So far I have a module setup like so but I keep getting a method undefined after_retire error on my User model.
module Active
extend ActiveSupport::Concern
included do
define_callbacks :retire
set_callback :retire, :after, :after_retire
default_scope { where(:retired => false) }
scope :retired, where(:retired => true)
end
def retire!
run_callbacks :retire do
update_attribute :retired, true
update_attribute :retired_at, Time.now.to_datetime
end
end
end
How should I be setting this up?
The before/after_callback syntax is handled in ActiveModel::Callbacks through #define_model_callbacks. The raw ActiveSupport::Callbacks will require you to use #set_callback without any syntactic sugar:
module Active
extend ActiveSupport::Concern
included do
define_callbacks :retire
default_scope { where(:retired => false) }
scope :retired, where(:retired => true)
end
def retire!
run_callbacks :retire do
update_attribute :retired, true
update_attribute :retired_at, Time.now.to_datetime
end
end
end
class User < ActiveRecord::Base
include Active
set_callback :retire, :after, :method
def method
#do stuff
end
end
If you want to have the after/before syntax, since you are working with an ActiveRecord (and thus ActiveModel) class, you can use:
module Active
extend ActiveSupport::Concern
included do
define_model_callbacks :retire
default_scope { where(:retired => false) }
scope :retired, where(:retired => true)
end
def retire!
run_callbacks :retire do
update_attribute :retired, true
update_attribute :retired_at, Time.now.to_datetime
end
end
end
class User < ActiveRecord::Base
include Active
after_retire :method
def method
#do stuff
end
end

Activity history with Rails audited gem

I have rails 3 application with some models, like Product and User. I'm using "audited" gem to track changes for products, it's simple and nice working.
But I want to make special page where I want to put daily activity history. I need something like Audits.all.order("created_at") for first step, but there is no such model.
Question: How can I get all audits for today for all models?
I think you should query like Audited::Adapters::ActiveRecord::Audit.where("created_at >= ?", Date.today) according to the gem structure
To be able to access today's audits with:
#audits = Audit.today
Create an audit.rb file in app/models/ like:
Audit = Audited.audit_class
class Audit
scope :today, -> do
where("created_at >= ?", Time.zone.today.midnight).reorder(:created_at)
end
end
Audited also provides a few named scopes of its own that may prove useful:
scope :descending, ->{ reorder("version DESC") }
scope :creates, ->{ where({:action => 'create'}) }
scope :updates, ->{ where({:action => 'update'}) }
scope :destroys, ->{ where({:action => 'destroy'}) }
scope :up_until, ->(date_or_time){ where("created_at <= ?", date_or_time) }
scope :from_version, ->(version){ where(['version >= ?', version]) }
scope :to_version, ->(version){ where(['version <= ?', version]) }
scope :auditable_finder, ->(auditable_id, auditable_type){ where(auditable_id: auditable_id, auditable_type: auditable_type) }
my solution is simply to extend the audit object, e.g.
cat lib/audit_extensions.rb
# The audit class is part of audited plugin
# we reopen here to add search functionality
require 'audited'
module AuditExtentions
def self.included(base)
base.send :include, InstanceMethods
base.class_eval do
belongs_to :search_users, :class_name => 'User', :foreign_key => :user_id
scoped_search :on => :username, :complete_value => true
scoped_search :on => :audited_changes, :rename => 'changes'
scoped_search :on => :created_at, :complete_value => true, :rename => :time, :default_order => :desc
scoped_search :on => :action, :complete_value => { :create => 'create', :update => 'update', :delete => 'destroy' }
before_save :ensure_username
end
end
module InstanceMethods
private
def ensure_username
self.username ||= User.current.to_s rescue ""
end
end
end
Audit = Audited.audit_class
Audit.send(:include, AuditExtentions)

multiple scope rails

class Anketum < ActiveRecord::Base
has_one :user
class << self
def search(params)
self.scope :h, :conditions => {:height => params[:height]}
#scope :w, :conditions => {:width => params[:width]}
self.h if params[:height]
end
end
end
I need to create multiple scope depend on params[:xxx] present
Judging by your code sample, you're way over-engineering this:
# app/models/anketum.rb
class Anketum < ActiveRecord::Base
end
# app/controller/some_controller.rb
def search
#results = Anketum.scoped
[:width, :height, :any, :other, :searchable, :attribute].each do |key|
#results.where(key => params[key]) if params[key].present?
end
end
Your models should never access the params hash, by the way.

Resources