I am using devise and have created a service object - everything works as expected and perfectly. However I want to know how I would include the current_user? as the moment I have to always pass in the current_user or user_signed_in? variables which is annoying when I am using the same methods in different classes and views how would I go about doing this?
Currently I have this for testing:
class User
class GetStart
attr_reader :current_user
def initialize(user)
#current_user = user
p current_user
end
end
end
current_user is usually a helper method of ApplicationController which means it will only be accessible within your controllers and views. This is a good thing -- your service object has no business accessing current_user directly since current_user is created from information passed in the request/session which is a concern of the controller.
In my opinion, your approach is correct, it's preferable to pass your user object in as an external object (i.e. dependency injection) than directly couple it to method within a separate object. After all, does your service object need to know or care whether user is from the session?
Also, as was mentioned, keep in mind that attr_reader creates an instance variable for the class, so you should have #current_user = user.
Related
I have devise wich authenticates a user. User is split up into subclasses such as User::AsProfile < User or User::AsRegistrant< User. These are not Single Table Inheritance classes, just plain old ruby-objects that inherit from User.
I'd like devises' current_user helper to return an instance of User::AsProfile.
Unfortunately, Device has a lot of metaprogamming hidden away in class_eval so it is hard to understand what is going on.
Can I configure Devise to return another class (User::AsProfile) when running current_user in a controller?
Or must I override current_user in my controller to do so, and if so, how should I call warden to authenticate properly?
I ended up re-implementing current_user in my ApplicationController.
Since I don't need the ability to have one user use multiple sessions, I could simplify the current_user a lot:
class ApplicationController < ActionController::Base
def current_user
return #current_user if #current_user
current = warden.authenticate(scope: :user)
#current_user = current.becomes(User::AsProfile) if current
end
end
By using ActiveRecords becomes, the user is turned into a User::AsProfile. I'd rather not have this conversion and use User::AsProfile#find directly, but that seems only possible whith monkeypatching Warden.
As a sidenote: This problem shows well what happens when a library implements a poorly designed class-inheritance; or, in this case, no inheritance at all. The Devise code simply monkeypatches your ApplicationController and adds methods there. Never a good idea. With hindsight, Devise should simply be some modules (Concerns) that a developer can pick-and-choose and add to his controllers.
In my application controller I have the following code:
helper_method :current_user
def current_user
#current_user ||= User.find(session[:user]) if session[:user]
end
I would like to use the "current_user" method in a model. According to the rails API the helper method can be accessed at "AbstractController::Helpers::ClassMethods".
See link:
http://api.rubyonrails.org/classes/AbstractController/Helpers/ClassMethods.html
When I add this to my model I get a method not found error:
include AbstractController::Helpers::ClassMethods
Am I missing something in how to include these helper methods?
Thanks!
Short answer: you can't and you shouldn't.
The model doesn't have (and should not have) any visibility of the view and the controller. The clear separation is one of the key principle of the MVC pattern.
If you want a method in your model to have access to the current user, then pass the user when invoking the method.
For instance, assuming you want to pass the user on the Post creation, define a custom method
class Post
def do_something_with_user(user)
# ...
end
end
and call it from the controller
def action
Post.find(...).do_something_with_user(current_user)
end
There are possible workarounds, such as storing the current user into the current thread or in a global variable, but this is gonna break the rules (and you should not break the rules).
I have some code in my application layout view that requires an object to exist in order to work. The object exists on some controllers, but not all.
At first I thought I could use the after_filter on the application_controller to ensure that the object exists. But this didn't work because the after_filter is only applied after the view is rendered and it is apparently an anti-pattern as well.
What is the best way to ensure that the object always exists, without unnecessarily creating the object on controllers that already create the object.
Say your variable is #foo.
application_helper.rb
def foo
#foo ||= generate_foo()
end
then from any view, you just call your object by foo and not #foo.
This might have it's own pitfalls as well but personally, I will create a method in my application_controller to return the instance. To make it available in the views, just pass the method to helper_method. helper_method is used to share methods between views, helpers and controllers.
I mostly do this with authentication. So you I will have something like;
def current_user
#current_user ||= User.find_by_id(session[:user_id])
end
helper_method :current_user # make current_user available in the views
I have a
class CommentsController < ApplicationController
def foo
session[:comments] ||= {}
comment = Comment.new(params[:comment])
# Validation and such
session[:comments][comment.post_id] = comment
#several redirections and remote authentications. User returns to another method,
# But for the sake of the example, we continue here.
CommentsController.publish_from_session
end
def self.publish_from_session
session[:comments].each do |comment|
comment.save!
end
end
end
This gives me a can't convert Symbol into Integer error. When diving into this, apparently session is simply not available, or not a hash. It might be that calling CommentsController.some_method is plain wrong.
What would be the correct solution?
Also: As mentioned in the commented code, the real deal is a bit more complex. The user returns either on another controller (sessions controller via oauth) or on yet another method on the CommentsController. See controllers calling eachother and Comments created after Oauth for how I came to this.
session is an instance method. You can't access it in a class method, but you can pass it (or just session[:comments] to the class method.
I am trying to access an instance variable which is set in the controller in the model. The controller is the products controller and the model is the products model. The instance variable is a instance of another model called account.
The instance variable is #current_account
When I run the code nothing happens, I do not get an error. Does anyone know where I can find something read about access instance variables set in the controller from the model?
Thanks
Eef
You shouldn't generally try to access the controller from the model for high-minded issues I won't go into.
I solved a similar problem like so:
class Account < ActiveRecord::Base
cattr_accessor :current
end
class ApplicationController < ActionController::Base
before_filter :set_current_account
def set_current_account
# set #current_account from session data here
Account.current = #current_account
end
end
Then just access the current account with Account.current
DISCLAIMER: The following code breaks MVC conventions, that said...
Using class attributes can probably lead to thread safety issues. I would use Thread.current + around_filter to store controller related data at thread level, and ensure it gets cleared
just before the request finishes:
class ApplicationController < ActionController::Base
around_filter :wrap_with_hack
def wrap_with_hack
# We could do this (greener solution):
# http://coderrr.wordpress.com/2008/04/10/lets-stop-polluting-the-threadcurrent-hash/
# ... but for simplicity sake:
Thread.current[:controller] = self
begin
yield
ensure
# Prevent cross request access if thread is reused later
Thread.current[:controller] = nil
end
end
end
Now the current controller instance will be avaliable globaly during the request processing through Thread.current[:controller]
If you need to access a controller variable from a model it generally means your design is wrong because a controller serves as bridge between view and model (at least in Rails), controller gets info from models, models shouldn't know anything about controllers, but if you want to do it anyway you can do it just as jeem said, but I'd rather do:
class << self
attr_accessor :current
end
instead of cattr_accessor :current
you can see why here => cattr_accessor doesn't work as it should
I can't comment directly so I'll post here: the accepted answer does not seem to be right. As #vise notes, class variables are shared across requests. So unless there's just one current account for the entire app, this won't behave as expected.
For more, see the accepted answer by #molf here: Is Rails shared-nothing or can separate requests access the same runtime variables?
I'm not sure if I understand the question exactly, but I'll take a stab.
I think if you need to access a controller instance variable from the model then you either need to make it an attribute in the model, or move your logic to the other class controller, not model.