Helper methods are not being seen (Rails 4 engine) - ruby-on-rails

I have defined a helper method as such (for my rails engine):
module Xaaron
class ApplicationController < ActionController::Base
protect_from_forgery with: :null_session
rescue_from ActiveRecord::RecordNotFound, :with => :record_not_found
helper_method :current_user
helper_method :authenticate_user!
def current_user
#current_user ||= Xaaron::User.find_by_auth_token(cookies[:auth_token]) if cookies[:auth_token]
end
def authenticate_user!
if current_user
true
else
redirect_to xaaron.login_path
false
end
end
protected
def record_not_found
flash[:error] = 'Could not find specified role'
redirect_to xaaron.record_not_found_path
true
end
end
end
As far as I know everything above is correct in terms of creating helper methods. So now I need to use this helper method:
module Xaaron
class ApiKeysController < ActionController::Base
before_action :authenticate_user!
def index
#api_key = Xaaron::ApiKey.where(:user_id => current_user.id)
end
def create
#api_key = Xaaron::ApiKey.new(:user_id => current_user.id, :api_key => SecureRandom.hex(16))
create_api_key(#api_key)
end
def destroy
Xaaron::ApiKey.find(params[:id]).destroy
flash[:notice] = 'Api Key has been deleted.'
redirect_to xarron.api_keys_path
end
end
end
As you can see, before every action the user must be authenticated. So the authenticat_user!
method is then called.
Lets write a test for this
it "should not create an api key for those not logged in" do
post :create
expect(response).to redirect_to xaaron.login_path
end
This, we expect, to send us back to the login path because we are not signed in, and as you will recall we are using authenticate before every action in the API Controller. What do we get instead:
1) Xaaron::ApiKeysController#create should not create an api key for those not logged in
Failure/Error: post :create
NoMethodError:
undefined method `authenticate_user!' for #<Xaaron::ApiKeysController:0x007f898e908a98>
# ./spec/controllers/api_keys_controller_spec.rb:9:in `block (3 levels) in <top (required)>'
Last I checked the way I defined a helper method is how rails casts has done it, how other stack questions have done it and how rails docs states to do it - unless I missed some majour step - why isn't this working?

Maybe I haven't seen a helper method set up like this before (I'm new to rails) but the helper methods I've seen are defined without controllers.
Usually I see a file like this in the helpers folder
module SessionsHelper
def sign_in(user)
remember_token = User.new_remember_token
cookies.permanent[:remember_token] = remember_token
user.update_attribute(:remember_token, User.encrypt(remember_token))
self.current_user = user
end
def current_user=(user)
#current_user = user
end
...
and then
include SessionsHelper
In the application controller.
To me it looks like you're calling the controller a helper method, I'm not sure what the benefits of that would be - but I suppose I wouldn't.
Sorry if this wasn't helpful

Related

Implementation of survey using surveyor gem in rails

I am trying to implement a survey using the surveyor gem in rails. I want to make use of the user id to keep track of which user creates the survey and which user gave what response on which survey.
The problem is that I did not use the Devise gem for my user signin and signup. I built it manually. The surveyor gem uses a helper method current_user of Devise which returns details about the current user.
Since, I did not use devise, I am not sure where to add the helper method current_user.
I am not really sure as to what code to post, so please comment the required details. I will edit my post as needed.
Thanks!
application_controller.rb
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
before_filter :authorize
helper_method :current_user
protected
def authorize
return true if ((self.class == SessionsController)|| (self.class == UsersController && (self.action_name == "new" || self.action_name == "create")))
unless (User.find_by_id(session[:user_id]))
redirect_to url_for(:controller => :sessions , :action => :new), alert: "You need to be logged in."
end
end
def current_user
#current_user = User.find(session[:user_id])
end
end
Here is the link of the surveyor gem controller which uses the current_user method: https://github.com/kjayma/surveyor_gui/blob/master/app/controllers/surveyor_gui/survey_controller.rb
Here is one possible solution to implement a current_user method.
helper_method would make the current_user method available in every controller, which inherits from ApplicationController.
class ApplicationController
helper_method :current_user
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
end

Rails Engine creating a before_filter like method for all parent controllers

I'm attempting to build a Rails Gem somewhere between Devise and CanCan. Not nearly as complex as Devise, but having views and a controller.
I've created a method to be added to the top of any controller of the parent app that needs it almost exactly like Devise's before_action :authenticate_user! and CanCan's load_and_authorize_resource
I need the method to redirect_to a path in my mounted routes if the requirements are not met.
module MyEngine
module ControllerAdditions
extend ActiveSupport::Concern
module ClassMethods
def pin_verified
current_user ||= nil
#pinned = current_user.nil? ? nil : current_user
redirect_to setup_mobiles_path unless #pinned && #pinned.verified?
end
end
end
end
and in my spec/dummy/app/controllers/users_controller.rb
class UsersController < ApplicationController
pin_verified
def index
#users = User.all
end
end
pin_verified is getting called as it's supposed to but I get the following error:
undefined local variable or method `setup_mobiles_path' for UsersController:Class
Any thoughts on how I should be doing this?
==== edit ====
I altered this now to raise a custom exception, but now I need to rescue that exception some how and redirect to the needed path.
def pin_verified
current_user ||= nil
#pinned = current_user.nil? ? nil : current_user
unless #pinned && #pinned.verified?
raise ValidatedPinExpired
end
end
I tried adding this to the ApplicationController of my gem, but it doesn't seem to be hitting Controller at all.
module MyEngine
class ApplicationController < ActionController::Base
rescue_from Exception do |exception|
Rails.logger.info "==== exception: #{exception} ===="
redirect_to setup_mobiles_path
end
end
end

How to create `authenticate_user' method without devise in ror

I am new in Ruby on Rails and i am using Ruby version 1.9.3 and Rails version 4.0.2.
My Query is:-
How to create `authenticate_user' method without devise in Ruby on Rails.
Below my routes
get "admin/users/sign_in" => "admin/users#sign_in"
Below My Application Controller:-
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
rescue_from CanCan::AccessDenied do |exception|
flash[:alert] = "Access denied. You are not authorized to access the requested page."
redirect_to root_path and return
end
helper_method :current_user
before_filter :authenticate_user, :current_user
def current_user
# Note: we want to use "find_by_id" because it's OK to return a nil.
# If we were to use User.find, it would throw an exception if the user can't be found.
#current_user ||= User.find_by_id(session[:user_id]) if session[:user_id]
#current_user ||= User.find_by_authentication_token(cookies[:auth_token]) if cookies[:auth_token] && #current_user.nil?
#current_user
end
def authenticate_user
if #current_user.nil?
flash[:error] = 'You must be signed in to view that page.'
redirect_to :admin_users_sign_in
end
end
protected
#derive the model name from the controller. egs UsersController will return User
def self.permission
return name = self.name.gsub('Controller','').singularize.split('::').last.constantize.name rescue nil
end
def current_ability
#current_ability ||= Ability.new(current_user)
end
#load the permissions for the current user so that UI can be manipulated
def load_permissions
#current_permissions = current_user.role.permissions.collect{|i| [i.subject_class, i.action]}
end
end
Below code using my controller
before_filter :authenticate_user!
My authenticate_user method not redirect properly
redirect_to :admin_users_sign_in
admin_users_sign_in path define in routes see on top
Above the code every time say on browser "The page isn't redirecting properly"
Please help
I suspect the problem is due to this line:
redirect_to :admin_users_sign_in
You need to pass either an action & controller or a friendly name of the path to redirect_to.
Change your routes to be something like
get "admin/users/sign_in" => "admin/users#sign_in", :as => :admin_user_signin
Then you can do something like
redirect_to admin_user_signin_path
This looks an infinite loop.
You defined authenticate_user at ApplicationController level. So, when a visitor visited page 'foo', he is denied by this method because current_user is nil. Then he got redirected to admin sign in page, but that page has this before_filter as well, so he got redirected again, to the same page and never end.
To fix, move such filter to specific controllers which need protection. And do not set it in sign in/sign up page.
Side notes:
You've already used CanCan which has authorization on "read" as well. There is no point to use authenticate_user again for same functionality.

session hash does not persist on rspec tests

In my people_controller_spec.rb I have
before(:each) do
#office = FactoryGirl.create(:office)
#organization = FactoryGirl.create(:organization)
#user = FactoryGirl.create(:user, organization: #organization)
#request.session['user_id'] = #user.id
#request.session['current_organization_id'] = #user.organization.id
end
and I have this application_controller.rb
class ApplicationController < ActionController::Base
protect_from_forgery
private
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
def current_organization
if session[:current_organization_id]
Organization.find(session[:current_organization_id])
else
#current_organization ||= Organization.find(current_user.organization_id)
end
end
helper_method :current_user
helper_method :current_organization
end
The session hash does not seem to persist inside the application_controller.rb that is why I get these kinds of test errors where #current_user in the application_controller.rb is nil
6) PeopleController index sorts all people alphabetically by first_name
Failure/Error: get :index, {search: {meta_sort: "first_name.asc"}}, valid_session
NoMethodError:
undefined method `organization_id' for nil:NilClass
# ./app/controllers/application_controller.rb:15:in `current_organization'
# ./app/controllers/people_controller.rb:107:in `get_orgs'
# ./spec/controllers/people_controller_spec.rb:71:in `block (3 levels) in <top (required)>'
I have already done everything but failed.
I use rails (3.2.9) and rspec-rails 2.12.2
I SOLVED THE PROBLEM AFTER SEEING THIS Devise Test Helper - sign_in does not work
I just removed all the "valid_session" method call.
In your before :each block set the session with:
session[:user_id] = #user.id
session[:current_organization_id] = #user.organization.id
This uses the session helper provided by the rspec controller macros. Also I'm not sure if the session is a HashWithIndifferentAccess like params but either way its good to keep to using the same key type.

Cookie related rspec test problem while working through tutorial

I've been working through Michael Hartl's Rails tutorial (which is unbelievably awesome by the way).
Anyway, everything has been going pretty well and I've nearly reached the end of chapter 10. The problem is that my rspec tests have started to generate some failures and I can't figure out what's wrong.
The first failure occurred when I was working through the section on destroying users. The test
before :each do
#user = Factory :user
end
describe "as a non-signed-in user" do
it "should deny access" do
delete :destroy, :id => #user
response.should redirect_to(signin_path)
end
end
gives the error:
UsersController DELETE 'destroy' as a non-signed-in user should deny access
Failure/Error: delete :destroy, :id => #user
NoMethodError:
undefined method `admin?' for nil:NilClass
# ./app/controllers/users_controller.rb:76:in `admin_user'
# ./spec/controllers/users_controller_spec.rb:308:in `block (4 levels) in <top (required)>'
Here's the code the message references in users_controller:
def admin_user
# the error tels me that current_user = NilClass
redirect_to(root_path) unless current_user.admin?
end
So I guess this would suggest that current_user isn't working correctly and is being set to nil. Now current_user involves a lot of methods of the SessionsHelper which (afaik) deal with setting the users ID in secure cookies and referencing the cookie as they move around the site. So this suggests that there is something wrong with the cookies.
I've checked the browser and the cookie is being set, I've also gone over every part of the code and it all replicates the tutorial exactly as far as I can tell.
Is there something else I should be looking at?
Appendix
Here is the contents of the SessionsHelper module:
module SessionsHelper
def sign_in user
# rails represents cookies as a hash and deals with the conversion for us
# making the cookie "signed" makes it impervious to attack
cookies.permanent.signed[:remember_token] = [user.id, user.salt]
# this line calls the assignment operator below
self.current_user = user
end
def current_user=(user)
#current_user = user
end
# this is a getter method
def current_user
# this sets #current_user = to the user corresponding to the remember token
# but only if #current user is undefined. ie it only works once
#current_user ||= user_from_remember_token
end
def signed_in?
# return true if current_user is not nil
!current_user.nil?
end
def sign_out
cookies.delete(:remember_token)
self.current_user = nil
end
def current_user? user
# returns true if the user object == the current_user object
user == current_user
end
def authenticate
deny_access unless signed_in?
end
def deny_access
store_location
# this is a shortcut for flash notices: flash[:notice] = "Please sign in to access this page."
redirect_to signin_path, :notice => "Please sign in to access this page."
end
def redirect_back_or(default)
redirect_to(session[:return_to] || default)
clear_return_to
end
private
def user_from_remember_token
# the * allows us to give a 2 element array to a method expecting 2 seperate arguments
User.authenticate_with_salt(*remember_token)
end
def remember_token
# return [nil, nil] if the :remember_token cookie is nil
cookies.signed[:remember_token] || [nil, nil]
end
def store_location
# stores the url the browser was requesting
session[:return_to] = request.fullpath
end
def clear_return_to
session[:return_to] = nil
end
end
In your spec ypu are trying to delete user, while you are not logged in, so current_user is nil. You should prevent access to this action non singed in user.
class UsersController < ApplicationController
before_filter :authenticate, :only => [:index, :edit, :update, :destroy]
....
Just as it is described in example.
I made the same omission and this fixed worked. Thanks. I agree with ducky, Michael Hartl's Rails tutorial is tremendously awesome. It's almost a shame to +just+ call it a "Rails Tutorial." While the tutorial does indeed seem to be pointing me towards good Rails development habits, the text concisely contains much more. Very well done.

Resources