I'm working through RailTutorial.org, and in section 8, it has you create helper methods and use them in your controller. I am not able to access any of them
Controller line: if current_student.admin?
Helper method:
module SessionsHelper
[...]
def current_student
if session[:student_id]
#current_student ||= Student.find_by(id: session[:student_id])
elsif cookies.signed[:student_id]
student = Student.find_by(id: cookies.signed[:student_id])
if student && student.authenticated?(cookies[:remember_token])
log_in student
#current_student = student
end
end
end
[...]
end
Thanks!
Helper methods are only available within views. If you need it within views and controllers, you must define it within a controller, then declare it a helper method as well:
class ApplicationController
def current_student
# ...
end
helper_method :current_student
end
Related
I'm learning rails.
I'm build a simple test application, with a simple authentication scheme.
I'm using a user.role field to group the users.
In my Application Helper i have:
module ApplicationHelper
def current_user
if session[:user_id]
#current_user ||= User.find(session[:user_id])
else
#current_user = nil
end
end
def user_identity
current_user.role if current_user
end
end
Now, in my app, i can use current_user in all controllers as expected, but instead user_identity is not visible.
why?
The application_helper is used mainly to access methods in views - I don't believe it's available in a controller.
The reason your 'current_user' method appears to work is that I'm assuming you're using Devise - when you call 'current_user' it is using the Engine's method rather than your own.
To solve this, write out a new module:
module MyHelper
def current_user
if session[:user_id]
#current_user ||= User.find(session[:user_id])
else
#current_user = nil
end
end
def user_identity
current_user.role if current_user
end
end
And in the controller you're using:
class MyController < ApplicationController
include MyHelper
bla bla bla...
end
Any methods defined in MyHelper will now be available in MyController, as we've included the module in the controller
Helper modules are mixed into the view context (the implicit self in your views) - not controllers.
So you can call it from the controller with:
class ThingsController < ApplicationController
# ...
def index
view_context.user_identity
end
end
Or you can include the helper with the helper method:
class ThingsController < ApplicationController
helper :my_helper
def index
user_identity
end
end
But if you're writing a set of authentication methods I wouldn't use a helper in the first place. Helpers are supposed to be aids for the view.
Instead I would create a concern (also a module) and include it in ApplicationController:
# app/controllers/concerns/authenticable.rb
module Authenticable
extend ActiveSupport::Concern
def current_user
#current_user ||= session[:user_id] ? User.find(session[:user_id]) : nil
end
def user_identity
current_user.try(:role)
end
end
class ApplicationController < ActionController::Base
include Authenticable
end
Since the view can access any of the controllers methods this adds the methods to both contexts.
I am trying to access Devise's current_user variable inside a new instance of another controller. Here is my definition of GetsInterfaceController
class GetsInterfaceController < ApplicationController
def select_current_signed_in_user
#signed_in_user_here = current_user
end
end
Then I instantiate a new instance of GetsInterfaceController in ClientsController
class ClientsController < ApplicationController
def get_current_user
#gets_interface_controller = GetsInterfaceController.new
find_signed_in_user = #gets_interface_controller.select_current_signed_in_user
end
end
But I get null error on the #signed_in_user_here = current_user line in GetsInterfaceController when I try this. Anyway to get to the current_user attribute from inside GetsInterfaceController ?
I solved this by moving my code into a Module in lib directory. Works like a charm
current_user is not a variable - it is a helper method. Thus it is already available in all your helpers and views.
Additionally you never instantiate controllers in Rails. The router does that for you.
The only public methods in your controllers should be the actions which respond to HTTP requests.
If you want to reuse a method in several controllers you should be using inheritance, modules (concerns) or helpers. Never by calling a method on another controller.
To call an external service you want to create an API client class:
# adapted from https://github.com/jnunemaker/httparty
require 'httparty'
class StackExchangeClient
include HTTParty
base_uri 'api.stackexchange.com'
def initialize(service, page, user = nil)
#user = user
#options = { query: {site: service, page: page} }
end
def questions
self.class.get("/2.2/questions", #options)
end
def users
self.class.get("/2.2/users", #options)
end
end
Or if you need to call an external service and for example create several models with the data a Service Object:
class SomeService
def initialize(user, client: SomeClient)
#user = user
#client = client # for mocking
end
def call
response = #client.get('/foo')
response.each do |d|
#user.baz << d[:woo]
end
end
end
SomeService.new(current_user).call
I want to create a method like current_user for devise's current resources.
Suppose I have two resources like User and Admin and devise is associated with both. So as usual it dynamically creates it's default methods like current_user and current_admin.
It creates it by defining like this in file lib/devise/controllers/helpers.rb:
def current_#{mapping}
How can I add a new method like this to it's dynamic methods.
I want to implement it with devise methods, so that when devise initializes then my method is also initialize with same mapping name.
Copy this code into your application controller. and your problem will solved.
Devise.mappings.each do |mapping, obj|
define_method "current_#{mapping}_email" do
eval("current_#{mapping}.email if current_#{mapping}")
end
helper_method "current_#{mapping}_email"
On view page/controller used according to your resource name
like if you have resource_name as user then its 'current_user_email'
or if have admin then 'current_admin_email'
This works!
I added a custom method current_user_email as follows!
Add a file devise_ext.rb in config/initializers:
And in that file, add your custom methods in this way:
module Devise
module Controllers
# Those helpers are convenience methods added to ApplicationController.
module Helpers
def self.define_helpers(mapping) #:nodoc:
mapping = mapping.name
class_eval <<-METHODS, __FILE__, __LINE__ + 1
def authenticate_#{mapping}!(opts={})
opts[:scope] = :#{mapping}
warden.authenticate!(opts) if !devise_controller? || opts.delete(:force)
end
def #{mapping}_signed_in?
!!current_#{mapping}
end
def current_#{mapping}
#current_#{mapping} ||= warden.authenticate(scope: :#{mapping})
end
def #{mapping}_session
current_#{mapping} && warden.session(:#{mapping})
end
def current_#{mapping}_email
#current_#{mapping}.email if #current_#{mapping}
end
METHODS
ActiveSupport.on_load(:action_controller) do
helper_method "current_#{mapping}", "#{mapping}_signed_in?", "#{mapping}_session", "current_#{mapping}_email"
end
end
end
end
end
P.S. I don't think this is the best way to do this. But it works. I tried this in one of my applications! You can use this code till I find a better way to do this :)
Here's the code:
class SyncController < ApplicationController
def get_sync
#passed_ids = params[:ids].split(',')
#users = #passed_ids.collect{|id| User.find(id)}
#add the current user to the list
#users << current_user
#recommendations = get_recommendations(#users)
end
end
module SyncHelper
def get_recommendations(users)
users
end
end
I'm getting a can't find get_recommendations method error...
Your SyncHelper module needs to be included into your SyncController class. You can either add the line
include SyncHelper
in your class definition, or, if SyncHelper lives in the expected app/helpers file, you can use the rails helper method.
I defined a helper class as below
module SessionsHelper
def current_user
#current_user= User.find_by_fbid(session[:fbid])
end
def sign_in(user)
session[:fbid] = user.fbid
#current_user = user
end
def signed_in?
!current_user.nil?
end
end
I included the Helper Class in my Application Controller
class ApplicationController < ActionController::Base
protect_from_forgery
include SessionsHelper
end
The sign in method gets called from Session Controller
class SessionsController < ApplicationController
def create
user = User.find_or_create_by_fbid(params[:user][:fbid])
user.update_attributes(params[:user])
sign_in(user)
redirect_to user_path(user)
end
end
However I am not able to access 'current_user' variable from users#show view.
<% if signed_in? %>
<p>
<b>Current User:</b>
<%= current_user.name %>
</p>
<% end %>
It says : undefined method `name' for nil:NilClass
Can anyone please advise ?
The method current_user does not get called at all from index.
Putting include SessionsHelper in your controller includes those module methods in the controller, so they are accessible in your controller methods. You want the helper methods available in your views, so you need to use helper SessionsHelper in your application controller.
That being said, I do agree with Jits that the methods you have in SessionsHelper really do belong in the controller instead of in a helper.
Generally you should have methods like current_user defined in your application_controller and then make them available as helpers in the views. This way the controllers have access to them (and trust me, you will most likely need access to things like that). Example:
def current_user
..
end
helper :current_user
What helped me:
Define methods to use in the controller in helper files
Define methods to use in the view in the relevant model file
Example
Suppose you had this in user_helper.rb
def something
2 + 2
end
simply move that code into
models/user.rb
and it will be accessible in the view without any further effort.