is_admin? function in rails? - undefined method Error - ruby-on-rails

At the moment, I am creating some kind of admin panel/backend for my site.
I want to do the following:
Only admins (a user has a user_role(integer) --> 1 = admin, 2 = moderator, 3 = user) can see and access a link for the admin panel.
So I created an admin_controller. In my admin controller I created a new function called is_admin?:
class AdminController < ApplicationController
def admin_panel
end
def is_admin?
current_user.user_role == 1
end
end
my route looks like.
map.admin_panel '/admin-panel', :controller => 'admin', :action => 'admin_panel'
and in my _sidebar.html.erb (partial in applicaton.html.erb) I created the link:
<%= link_to "Admin Panel", :admin_panel unless is_admin? %>
Now I get an error called:
undefined method `is_admin?'
Where is the problem? Please help me solving this problem!
Okay, sorry for this, but it still wont work. Here are my controllers:
application_controller.rb:
class ApplicationController < ActionController::Base
include AuthenticatedSystem
helper :all
protect_from_forgery
helper_method :current_user
def current_user
#current_user ||= User.find_by_id(session[:user])
end
end
users_controller.rb:
class UsersController < ApplicationController
layout 'application'
include AuthenticatedSystem
helper_method :is_admin? #only added this line
def new
end
...
end
user.rb
require 'digest/sha1'
class User < ActiveRecord::Base
# Virtual attribute for the unencrypted password
attr_accessor :password
... #more stuff but nothing for is_admin?
def active?
# the existence of an activation code means they have not activated yet
activation_code.nil?
end
#here is my is_admin? code
def is_admin?
self.user_role == 1
end
...
end
and now my view (_sidebar.html.erb):
<div>
<%= link_to "Admin Panel", :admin_panel unless current_user.is_admin? %>
</div>
That's it. Any ideas?
Btw: now the error changed a bit. Now it is:
undefined method `is_admin?' for nil:NilClass
My Session Create (in sessions_controller.rb):
def create
self.current_user = User.authenticate(params[:login], params[:password])
if logged_in?
if params[:remember_me] == "1"
current_user.remember_me unless current_user.remember_token?
cookies[:auth_token] = { :value => self.current_user.remember_token , :expires => self.current_user.remember_token_expires_at }
end
redirect_back_or_default('/')
flash[:notice] = "Logged in successfully"
else
render :action => 'new'
end
end

The problem is that methods defined in your controllers are not available in your views, unless you do this in the controller:
helper_method :is_admin?
However, in your case I would suggest that you move this method into the user model, as it seems to be more or less part of the business logic of the application.
So, in your user model,
class User < ActiveRecord::Base
def is_admin?
self.user_role == 1
end
end
And then in the view,
<%= link_to "Admin Panel", :admin_panel unless current_user.is_admin? %>
Oh, and btw, make sure that your users cannot change their roles arbitrarily via mass attributes assignment. And also it would be better to define constants for those role integer values. Sorry if this is too obvious :)

use helper_method if you want to use your controller's method in your views
class AdminController < ApplicationController
helper_method :is_admin? #you should include this line so that you can access it in your view.
def admin_panel
end
def is_admin?
current_user.user_role == 1
end
end

Related

Should static pages have access to cookies in rails controller and routes.rb?

I'm sure this is an obvious question but I just don't understand why this isn't working.
I'm finding that my static pages defined in the routes.rb file don't seem to have access to cookies? Is that correct? I'm trying to read the value of a cookie but the pages below seem to return a null object.
My routes.rb contains the following:
scope controller: :static do
get :about
get :terms
get :privacy
get :cookies
get :returns
get :delivery
end
For completeness here is the Static Controller:
# frozen_string_literal: true
class StaticController < ApplicationController
before_action :find_order
def index; end
def about; end
def pricing
redirect_to root_path, alert: t('.no_plans') unless Plan.without_free.exists?
end
def terms; end
def privacy; end
def cookies; end
def returns; end
def delivery; end
end
And this is how the cookie is set:
class OrdersController < ApplicationController
before_action :find_order
def add_hamper
#order ||= Order.create!(
ordernum: find_next_order_number,
user: current_user,
basket: true
)
#order.hampers << Hamper.friendly.find(params[:id])
update_order_total(#order)
if current_user&.custaddress
#order.update(custaddress: current_user.custaddress)
else
cookies[:ordernum] = #order.ordernum
end
redirect_to order_path(#order.ordernum)
# Update basket in Navbar
# Save the information as a cookie reference if they are not signed in
end
At the start of each controller I have a before_action to find an order if it exists in the DB or Cookie. For all other controllers, the find_order method is working. For the StaticController, there seems to be no access to the cookies.
Here is my find_order as defined in ApplicationController:
def find_order
#order = if current_user
Order.where(
user: current_user,
basket: true
).first
else
if cookies.dig[:ordernum]
Order.where(
ordernum: cookies[:ordernum],
basket: true
).first
end
end
end
helper_method :find_order
I've had to add the check for cookies and then if cookies[:ordernum] to stop it from failing on the static pages.
Thanks for any help with this.
PS. If anyone feels the above code could be better ... please let me know! There must be a nicer way to achieve this. It feels a little clunky.

Using draper gem with devise registration controller

I have declared user_decorator.rb instead of user_helper.rb in the following way
class UserDecorator < Draper::Decorator
delegate_all
def contract_type
contract_types.keys.collect {|k| [k.humanize, k]}
end
def employee_type
employee_types.keys.collect {|k| [k.humanize, k]}
end
def department_role
department_roles.keys.collect {|k| [k.humanize, k]}
end
end
and here are my enums that are declared on the user.rb
enum contract_type: [:probationary, :apprenticeship, :six_months_to_one_year,
:three_years]
enum department_role: [:ceo, :cto, :coo, :manager, :tl, :gl, :developer]
enum employee_type: [:full_time, :part_time, :internship]
I want to call the helper method from the view that is related to registrations controller. It is like as
class RegistrationsController < Devise::RegistrationsController
def new
super
end
def create
super
end
end
But if I call the helper method, like as following from the views/devise/registrations/new.html.erb
<%= f.select :contract_type, contract_type, {prompt: t(".contract_type",
default: "Select contract type")} %>
It don't find the contract_type. Need help about how I can access the helper methods from the view that is declared on the user_decorator.rb
In order to get a decorated user object from Devise's current_user method, you can override it:
class ApplicationController < ActionController::Base
def current_user
UserDecorator.decorate(super) unless super.nil?
end
end
However, since your issue is on the devise/registrations/new.html.erb page, at this point a user will not have logged in and hence won't have a any kind of decorated current_user object.
So, what it would seem you want is to get the set of decorated contract types from your User model into that view, which you could do by creating an instance variable on the controller:
class RegistrationsController < Devise::RegistrationsController
def new
#contract_types = User.contract_types.keys.collect {|k| [k.humanize, k]}
super
end
end
and then use it in the view:
<%= f.select :contract_type,
#contract_types,
{ prompt: t(".contract_type", default: "Select contract type") } %>
If you wanted to do further refactorings, you could perhaps create a scope-like class method on your User model that does the decoration of the contract types:
class User < ActiveRecord::Base
def self.humanized_contract_types
contract_types.keys.collect {|k| [k.humanize, k]}
end
end
So, then your controller code could be shortened to:
class RegistrationsController < Devise::RegistrationsController
def new
#contract_types = User.humanized_contract_types
super
end
end
Or, if you're intent on using the UserDecorator class, you could look into proxying class method calls to it.
You can override your new method to decorate your resource like so :
# registrations_controller.rb
def new
build_resource({})
yield resource if block_given?
respond_with resource
end
protected
def build_resource(hash = nil)
self.resource = resource_class.new_with_session(hash || {}, session).decorate
end

Rails 3.2: session variable gets lost in view helper method [duplicate]

how to get session in helper file?
UserHelper.rb
module UsersHelper
def self.auth login, password
user = Users.where("firstname = :firstname AND password = :password", {:firstname => login, :password => password})
if user != []
return true
else
return false
end
end
def self.is_auth? level
puts #session
user = Users.where("firstname = :firstname AND password = :password", {:firstname => #session[:firstname], :password => #session[:password]})
if user != []
return true
else
return false
end
end
end
Admin_controller.rb
class AdminController < ApplicationController
include Rails.application.routes.url_helpers
def initialization
#session = session
end
def index
#session = session
if UsersHelper.is_auth?(2)
render :text => "ssssss"
end
end
def auth
if params[:send] != nil
if UsersHelper.auth params[:firstname], params[:password]
session[:firstname] = params[:firstname]
session[:password] = params[:password]
redirect_to :action => "index"
else
#error = 1
end
end
end
def exit
session.delete(:firstname)
session.delete(:password)
render :json => session
end
end
Error
undefined method `[]' for nil:NilClass
app/helpers/users_helper.rb:13:in `is_auth?'
app/controllers/admin_controller.rb:8:in `index'
Only Controller can access session.
So, in a nutshell, if you are going to use this method in Controllers only like what is you case, you can define it as ApplicationController's method. Or define it a module and include it in AppplicationController.
class ApplicationController < ActionController::Base
def auth
end
def is_auth?
end
end
If you want to use the method in both controller and view, just declare them as helper_method
class ApplicationController < ActionController::Base
helper_method :auth, :is_auth?
def auth
end
def is_auth?
end
end
Ref: http://apidock.com/rails/ActionController/Helpers/ClassMethods/helper_method
Another note: In my opinion it's really not worth the time to build auth system from scratch by yourself. The functionalities are not easy but quite general. There are well baked gems such as Devise, Authlogic. Better to use them.

I defined a method but still getting an error 'rails undefined method'

I'm doing an authentication application. I have this code
class UsersController < ApplicationController
def new
#user = User.new
#title = "User Sign Up"
end
def create
#user = User.new(params[:user])
sign_in_check #user
if #user.save
#flash[:status] = true
#flash[:alert] = "You have successfully signed up!!"
#sign_in_check #user
redirect_to root_path, :flash => { :success => "Welcome to the Bakeshop"}
else
#title = "User Sign Up"
render 'new'
end
end
end
This is a simple sign-up code, and whenever I try and sign up, rails returns an error:
undefined method `sign_in_check' for #<UsersController:0x68c0a90>
but I defined a method sign_in_check in my Users_helper.rb:
module UsersHelper
def sign_in_check(user)
#some stuff to enable session
end
end
Does anyone have an idea why this is happening, and how to fix it?
The reason is your method is a helper. Helpers will be available in views with matching name by default, but not open to controllers without setting.
Two ways to fix:
Allow this helper in UsersController
class UsersController < ApplicationController
helper :user #This will expose UsersHelper module to UsersController
Instead, put this method into ApplicationController. I would prefer this due to the method's nature.
Include your UserHelper in your UserController as follows and you should be able to use any methods defined within the helper.
class UsersController < ApplicationController
include UsersHelper
...
end
This is usually put in the application controller
class ApplicationController < ActionController::Base
def sign_in_check(user)
#some stuff to enable session
end
end
Helpers are used for views. If you want to use it in both - you can do that, but that doesn't sound like what you're looking for here.
just include your helper module in your controller
class UsersController < ApplicationController
helper :user
...
end
Thanks

Access current_user in model

I have 3 tables
items (columns are: name , type)
history(columns are: date, username, item_id)
user(username, password)
When a user say "ABC" logs in and creates a new item, a history record gets created with the following after_create filter.
How to assign this username ‘ABC’ to the username field in history table through this filter.
class Item < ActiveRecord::Base
has_many :histories
after_create :update_history
def update_history
histories.create(:date=>Time.now, username=> ?)
end
end
My login method in session_controller
def login
if request.post?
user=User.authenticate(params[:username])
if user
session[:user_id] =user.id
redirect_to( :action=>'home')
flash[:message] = "Successfully logged in "
else
flash[:notice] = "Incorrect user/password combination"
redirect_to(:action=>"login")
end
end
end
I am not using any authentication plugin. I would appreciate if someone could tell me how to achieve this without using plugin(like userstamp etc.) if possible.
Rails 5
Declare a module
module Current
thread_mattr_accessor :user
end
Assign the current user
class ApplicationController < ActionController::Base
around_action :set_current_user
def set_current_user
Current.user = current_user
yield
ensure
# to address the thread variable leak issues in Puma/Thin webserver
Current.user = nil
end
end
Now you can refer to the current user as Current.user
Documentation about thread_mattr_accessor
Rails 3,4
It is not a common practice to access the current_user within a model. That being said, here is a solution:
class User < ActiveRecord::Base
def self.current
Thread.current[:current_user]
end
def self.current=(usr)
Thread.current[:current_user] = usr
end
end
Set the current_user attribute in a around_filter of ApplicationController.
class ApplicationController < ActionController::Base
around_filter :set_current_user
def set_current_user
User.current = User.find_by_id(session[:user_id])
yield
ensure
# to address the thread variable leak issues in Puma/Thin webserver
User.current = nil
end
end
Set the current_user after successful authentication:
def login
if User.current=User.authenticate(params[:username], params[:password])
session[:user_id] = User.current.id
flash[:message] = "Successfully logged in "
redirect_to( :action=>'home')
else
flash[:notice] = "Incorrect user/password combination"
redirect_to(:action=>"login")
end
end
Finally, refer to the current_user in update_history of Item.
class Item < ActiveRecord::Base
has_many :histories
after_create :update_history
def update_history
histories.create(:date=>Time.now, :username=> User.current.username)
end
end
The Controller should tell the model instance
Working with the database is the model's job. Handling web requests, including knowing the user for the current request, is the controller's job.
Therefore, if a model instance needs to know the current user, a controller should tell it.
def create
#item = Item.new
#item.current_user = current_user # or whatever your controller method is
...
end
This assumes that Item has an attr_accessor for current_user.
The Rails 5.2 approach for having global access to the user and other attributes is CurrentAttributes.
If the user creates an item, shouldn't the item have a belongs_to :user clause? This would allow you in your after_update to do
History.create :username => self.user.username
You could write an around_filter in ApplicationController
around_filter :apply_scope
def apply_scope
Document.where(:user_id => current_user.id).scoping do
yield
end
This can be done easily in few steps by implementing Thread.
Step 1:
class User < ApplicationRecord
def self.current
Thread.current[:user]
end
def self.current=(user)
Thread.current[:user] = user
end
end
Step 2:
class ApplicationController < ActionController::Base
before_filter :set_current_user
def set_current_user
User.current = current_user
end
end
Now you can easily get current user as User.current
The Thread trick isn't threadsafe, ironically.
My solution was to walk the stack backwards looking for a frame that responds to current_user. If none is found it returns nil. Example:
def find_current_user
(1..Kernel.caller.length).each do |n|
RubyVM::DebugInspector.open do |i|
current_user = eval "current_user rescue nil", i.frame_binding(n)
return current_user unless current_user.nil?
end
end
return nil
end
It could be made more robust by confirming the expected return type, and possibly by confirming owner of the frame is a type of controller...

Resources