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).
Related
CanCanCan makes two assumptions about the application:
1*An Ability class which defines the permissions.
2*A current_user method in the controller which returns the current user model.
In my case, i have two models object and subject (I can create, update, destroy) both of them using CRUD operations.
Now i want to restrict access to this object using CanCanCan library authorization. I know that CanCanCan expects a current_user method to exist in the controller and set up some authentication (like Devise). How can i override the default method that expects a current_user and pass it a current_subject, since that one shoud have access to object.
Use an alias to the new helper method:
Simply alias the current_user helper method in the controller to the actual helper method that you are using:
class ApplicationController < ActionController::Base
alias_method :current_user, :current_subject # or whatever your helper method is called - this is untested btw
end
# Source: https://github.com/CanCanCommunity/cancancan/wiki/changing-defaults
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.
I am using Rails 4.0.2 and Devise 3.2.2 to handle user registration / authentication.
I would like to write a custom method for Devise's current_user , this method is for checking how many times does the current_user sign in. I will be using sign_in_count
Do I write the method in the User model or should I define the method in Users Controller ?
Is it possilbe to write something like the below
def count
user = current_user
user.sign_in_count
end
and call current_user.count ?
Thanks
----edited----
What if I need to add other methods, am I able to add something like the below
#app/controllers/post_controller.rb
before_action :check_time
def check_time
time = User.last_sign_in_at(current_user)
if # something
# do bla bla
end
end
Do I write the method in the User model or should I define the method in Users Controller ?
It depends where (& when) you want to use the method
If you're going to use it as part of the "controller-level" interactivity, you'll want to put it into the UsersController, but if it's going to be used on "model-level" (by multiple controllers / models), you may wish to put it into the model
Something you need to be aware of is that current_user is a helper, and is not available at model level:
#app/controllers/products_controller.rb
def lookup
sign_ins = User.sign_in_count(current_user)
if sign_ins > 10
#do something
end
end
#app/models/user.rb
Class User < ActiveRecord::Base
def self.sign_in_count(user)
user = find(user.id)
user.sign_in_count
end
end
But as stated by #apneadiving, a far more efficient way to do this is to reference the current_user.sign_in_count attribute directly
Update
In reference to your update, you'll be best reading up about class & instance methods
You could perform the method like this:
#app/controllers/post_controller.rb
before_action :check_time
private
def check_time
time = current_user.last_sign_in_at
if # something
# do bla bla
end
end
In my references to model / controller methods - you'd use model methods if you wanted to give standard functionality on an app-wide level (such as User.weight_gain?). If you're using controller-centric data, you're best to keep it all in the controller
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.