I'm upgrading an app to Rails 3. We have an Application Controller like the following:
class ApplicationController < ActionController::Base
helper_method :current_user
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
Because we define "helper_method", "current_user" is available to all the views. It's available to the controllers because they all inherit from class ApplicationController.
However, when we were on 2.3.8, access to "current_user" was available through a model, but now it's not. Is there a way to get this exposed to the models?
I could suggest moving current_user into a helper like UserHelper, then you can include it in your model with include UserHelper.
You'll also have to include UserHelper in your ApplicationController, but helper :user will include that like normal into your views too.
Related
I have this code:
class ApplicationController < ActionController::Base
def authenticate_user!(*_args)
super
end
end
In authenticate_user! I call super, which should call authenticate_user! from ActionController::Base, which is an abstract class.
I couldn't find authenticate_user! inside ActionController::Base, so I suppose there is some meta programming involved(?).
How is that authenticate_user! from Devise ends up in ActionController::Base? Or any other method that is called by using super in ApplicationController?
authenticate_user! is a method that id dynamically built by devise, so you have to monkey patch devise. Helpers are available in both controllers and views, so that's probably why you're not hitting ActionController::Base
If you need to override the functionality you'll need to monkey patch this class: https://github.com/plataformatec/devise/blob/88724e10adaf9ffd1d8dbfbaadda2b9d40de756a/lib/devise/controllers/helpers.rb#L148
You can override the authenticate_user! method by defining your own. First, you'll have to create the directory lib/devise/controllers and add a file called helpers.rb, to which you can add:
module Devise
module Controllers
module Helpers
module ClassMethods
def authenticate_user!
# if something
# do something
# else
super
# end
end
end
If you aren't using devise, post a comment and I'll delete this answer
I have been working with Ruby on Rails for a short time. Recently I implemented an authentication system for my application. I made a method available on 'application_helper.rb' to retrieve the current logged user (method called current_user).
The method just retrieves my User object if the session[:user_id] variable is present.
However I face the following problem.
If I place the current_user method in 'application_helper.rb', my controllers can't make use of it
If I place the current_user method in 'application_controller.rb', my views can't make use of it
What's the best approach to solve this problem? The easy way would be duplicate my code in both controller and helper, but I know there is a better and more correct way.
Thanks in advance
This is a common and well-solved problem.
Rails doesn't allow controllers to access helper methods. If you want to share a method between your views and controllers, you need to define the method in your controller, and then make it available to your views with helper_method:
class ApplicationController < ActionController::Bbase
# Let views access current_user
helper_method :current_user
def current_user
# ...
end
end
You can pass more than one method name to helper_method to make additional methods in your controller available to your views:
helper_method :current_user, :logged_in?
def current_user
# ...
end
def logged_in?
!current_user.nil?
end
I have six distinct sections of my Rails application, all of which have their own models, views, and controllers.
I'm trying to create a "dashboard" page that accesses variables from each of the sections. For instance, in one of my controllers, I have this condition:
if #retirementsavingsdiff < 0
#retiregrade = "pass"
end
I can't seem to access this variable from a different view/controller though.
Do I put my dashboard logic in application_controller.rb?
A good option for making code reusable is separating it out into modules. Rails 4 includes something called Concerns that make this really easy. Here's a blog post with a good illustration of using Concerns for Controllers, and here's a sample of what your code might look like:
# /app/controllers/concerns/retirement_grade_checker.rb
module RetirementGradeChecker
extend ActiveSupport::Concern
def check_retire_grade
#retirementsavingsdiff = params[:retirementsavingsdiff]
if #retirementsavingsdiff < 0
#retiregrade = "pass"
end
end
end
# /app/controllers/retirement_controller.rb
class RetirementController < ApplicationController
include RetirementGradeChecker
def index
check_retire_grade
#... other stuff
end
end
# /app/controllers/dashboard_controller.rb
class DashboardController < ApplicationController
include RetirementGradeChecker
def index
check_retire_grade
#... other stuff
end
end
I would avoid using view helpers and instead create a new class or module with all of your logic inside. By doing that you can reuse that logic whenever you need it.
Why do this instead of helpers? You can easily test it.
methods defined inside helpers are automatically available across all views.
if you want to convert a method defined inside the controller to a helper method, you can do that too:
def my_method
# code
end
helper_method :my_method
UPDATE:
here is an example from API
class ApplicationController < ActionController::Base
helper_method :current_user, :logged_in?
def current_user
#current_user ||= User.find_by(id: session[:user])
end
def logged_in?
current_user != nil
end
end
I would like to define a helper method, my_method, that will be available inside BuyersController methods (like index, create, e.t.c.).
I tried to define it in app/helpers/application_helper.rb but it didn't work:
undefined method `my_method' for #<BuyersController:0x26df468>
It should be in some shared place because I want to use it in other controllers also. This is why I tried app/helpers/application_helper.rb.
What is the right place to define it ?
It should be in app/controllers/application_controller.rb
The app/helpers/application_helper.rb is for shared view helpers.
You should include the application helper module in your application controller so that its methods will be available everywhere (all controllers and views) during a request.
class ApplicationController < ActionController::Base
helper ApplicationHelper
…
end
See the API docs for the helper method
Starting from Rails 3 you could also call view_context.my_method inside your controller
Expanding on the accepted answer, if you did want to share a controller method with views, helper_method seems well suited for this. Declaring a helper_method in the controller makes it accessible to the views.
class ApplicationController < ActionController::Base
helper_method :current_user, :logged_in?
def current_user
#current_user ||= User.find_by(id: session[:user])
end
def logged_in?
current_user != nil
end
end
Before rails 4, helpers with the same controller names will be only available in their controllers and views. Starting from rails 4, all helpers are available for all controllers (if you included them) and views. With that said, all helpers will be shared across your views.
Note: if you don't want this behavior for specific controllers, you can use clear_helpers, it will only include the helper with the same controller name.
For more information about using helpers see this
Strange thing – I have Authentication module in lib/ like this:
module Authentication
protected
def current_user
User.find(1)
end
end
and in ApplicationController I'm including this module and all helpers, but method current_user is available in controllers, but not from views :( How can I make this work?
If the method were defined directly in the controller, you'd have to make it available to views by calling helper_method :method_name.
class ApplicationController < ActionController::Base
def current_user
# ...
end
helper_method :current_user
end
With a module, you can do the same, but it's a bit more tricky.
module Authentication
def current_user
# ...
end
def self.included m
return unless m < ActionController::Base
m.helper_method :current_user # , :any_other_helper_methods
end
end
class ApplicationController < ActionController::Base
include Authentication
end
Ah, yes, if your module is meant to be strictly a helper module, you can do as Lichtamberg said. But then again, you could just name it AuthenticationHelper and put it in the app/helpers folder.
Although, by my own experience with authentication code, you will want to have it be available to both the controller and views. Because generally you'll handle authorization in the controller. Helpers are exclusively available to the view. (I believe them to be originally intended as shorthands for complex html constructs.)
Did you declare it with
helper :foo # => requires 'foo_helper' and includes FooHelper
helper 'resources/foo' # => requires 'resources/foo_helper' and includes Resources::FooHelper
in your ApplicationController?
http://railsapi.com/doc/rails-v2.3.3.1/classes/ActionController/Helpers/ClassMethods.html#M001904