I want to create a default scope to filter all queries depending on the current user. Is it possible to pass the current user as an argument to the default_scope? (I know this can be done with regular scopes) If not, what would be another solution?
-
1possible duplicate of [Pass arguments in scope](http://stackoverflow.com/questions/6788203/pass-arguments-in-scope) – ABMagil Mar 11 '15 at 22:18
-
1not the same question @ABMagil – alberto911 Mar 11 '15 at 22:23
2 Answers
Instead of using default_scope
which has a few pitfalls, you should consider using a named scope with a lambda. For example scope :by_user, -> (user) { where('user_id = ?', user.id) }
You can then use a before_filter
in your controllers to easily use this scope in all the actions you need.
This is also the proper way to do it since you won't have access to helper methods in your model. Your models should never have to worry about session data, either.
Edit: how to use the scope in before_filter
inside a controller:
before_filter :set_object, only: [:show, :edit, :update, :destroy]
[the rest of your controller code here]
private
def set_object
@object = Object.by_user(current_user)
end
obviously you'd change this depending on your requirements. Here we're assuming you only need a valid @object depending on current_user inside your show, edit, update, and destroy actions
You cannot pass an argument to a default scope, but you can have a default scope's conditions referencing a proxy hash that executes its procs every time a value is retrieved:
module Proxies
# ProxyHash can be used when there is a need to have procs inside hashes, as it executes all procs before returning the hash
# ==== Example
# default_scope(:conditions => Proxies::ProxyHash.new(:user_id => lambda{Thread.current[:current_user].try(:id)}))
class ProxyHash < ::Hash
instance_methods.each{|m| undef_method m unless m =~ /(^__|^nil\?$|^method_missing$|^object_id$|proxy_|^respond_to\?$|^send$)/}
def [](_key)
call_hash_procs(@target, @original_hash)
ProxyHash.new(@original_hash[_key])
end
# Returns the \target of the proxy, same as +target+.
def proxy_target
@target
end
# Does the proxy or its \target respond to +symbol+?
def respond_to?(*args)
super(*args) || @target.respond_to?(*args)
end
# Returns the target of this proxy, same as +proxy_target+.
def target
@target
end
# Sets the target of this proxy to <tt>\target</tt>.
def target=(target)
@target = target
end
def send(method, *args)
if respond_to?(method)
super
else
@target.send(method, *args)
end
end
def initialize(*_find_args)
@original_hash = _find_args.extract_options!
@target = @original_hash.deep_dup
end
private
# Forwards any missing method call to the \target.
def method_missing(method, *args, &block)
if @target.respond_to?(method)
call_hash_procs(@target, @original_hash)
@target.send(method, *args, &block)
else
super
end
end
def call_hash_procs(_hash, _original_hash)
_hash.each do |_key, _value|
if _value.is_a?(Hash)
call_hash_procs(_value, _original_hash[_key]) if _original_hash.has_key?(_key)
else
_hash[_key] = _original_hash[_key].call if _original_hash[_key].is_a?(Proc)
end
end
end
end
end
Then in ApplicationController
you can use an around_filter
to set/unset Thread.current[:current_user]
at the begin/end of each request:
class ApplicationController < ActionController::Base
around_filter :set_unset_current_user
protected
def set_unset_current_user
Thread.current[:current_user] = current_user if logged_in?
yield
ensure
Thread.current[:current_user] = nil
end
end

- 191
- 1
- 6