Rails 3 devise, current_user is not accessible in a Model ?
in my project.rb model, I'm trying to create a scope with a dynamic variable:
scope :instanceprojects, lambda {
where("projects.instance_id = ?", current_user.instance_id)
}
I get the following error:
undefined local variable or method `current_user' for #<Class:0x102fe3af0>
Where in the controller I can access current_user.instance_id
... Is there a reason the model can't access it and a way to get access? Also, is this the right place to create a scope like the above, or does that belong in the controller?
Solution 1:
This doesn't make much sense, as you already pointed. The current_user doesn't belong to model logic at all, it should be handled on the controller level.
But you can still create scope like that, just pass the parameter to it from the controller:
scope :instanceprojects, lambda { |user|
where("projects.instance_id = ?", user.instance_id)
}
Now you can call it in the controller:
Model.instanceprojects(current_user)
Solution 2:
The already accepted answer provides a really correct way to achieve this.
But here's the thread-safe version of User.current_user
trick.
class User
class << self
def current_user=(user)
Thread.current[:current_user] = user
end
def current_user
Thread.current[:current_user]
end
end
end
class ApplicationController
before_filter :set_current_user
def set_current_user
User.current_user = current_user
end
end
This works as expected, however it can be considered dirty, because we basically define a global variable here.
Solution 3:
Ryan Bates lays out a pretty safe way to implement this kind of strategy in this railscast
This a paid episode (don't down vote me!) but you can browse the source code for free
Here he creates a current_tenant
method, but you could easily substitute current_user
instead.
Here are the key bits of code...
#application_controller.rb
around_filter :scope_current_tenant
private
def current_tenant
Tenant.find_by_subdomain! request.subdomain
end
helper_method :current_tenant
def scope_current_tenant
Tenant.current_id = current_tenant.id
yield
ensure
Tenant.current_id = nil
end
#models/tenant.rb
def self.current_id=(id)
Thread.current[:tenant_id] = id
end
def self.current_id
Thread.current[:tenant_id]
end
Then in the model you can do something like...
default_scope { where(tenant_id: Tenant.current_id) }