can not find the user has a role of admin - ruby

Now I am try to find whether the user has permissions to do something as admin.
this is the user model code:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable,
# :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation, :remember_me
# attr_accessible :title, :body
has_many :user_roles
has_many :roles, :through => :user_roles
def has_role?(role)
case role
when :admin then admin?
when :member then true
else false
end
end
def admin?
roles.each do |role|
return true if role.name == 'admin'
end
return false
end
end
Now there is a user which has a role name = admin and the test code is here:
command:rails c
user = User.find(1)
user.has_role?('admin')
result is :
=> false
why is it not true?
what is more I think the admin? method needs some refactoring.Now it is augly but I don`t know how to refactor ):

It's because of you use string in method argument and symbol in case statement.
It may be better to refactor has_role? method to something like this:
def has_role?(role)
case role.to_s
when 'admin' then admin?
when 'member' then true
else false
end
end
.to_s is used to convert non-strings (such as symbols) to strings, so you may call has_role? :admin and has_role? 'admin' with equal result.
Also, your admin? method looks very ugly.
You may rewrite it equally to:
def admin?
roles.any? { |r| r.name == 'admin' }
end
Or write more generic has_role? as:
def has_role?(role)
roles.any? { |r| r.name == role.to_s }
end

Related

Extracting common ActiveRecord code for similar clases

I have the following classes defined that have a lot of common code with minor variations.
class ThirdPartyComponent < ActiveRecord::Base
belongs_to :prev_version, :class_name => 'ThirdPartyComponent', :foreign_key => 'prev_version_id'
has_one :next_version, :class_name => 'ThirdPartyComponent', :foreign_key => 'prev_version_id'
attr_accessible :name, :version, :installer, :install_script
mount_uploader :installer, ComponentFileUploader
mount_uploader :install_script, ComponentFileUploader
validates :name, :presence => true
validates :version, :presence => true, :format => { :with => /\A\d{1,3}\.\d{1,2}\z/ }
validates :installer, :presence => true
validates :install_script, :presence => true
validate :increased_version
def increased_version
# Check to ensure that version number is greater than the previous version number for the same component set
unless prev_version.nil?
version > prev_version.version
end
end
def all_previous_versions
prev_versions = all_versions
prev_versions.shift
prev_versions
end
def all_versions
current_version = self
all_versions = [current_version]
while !current_version.prev_version.nil?
all_versions << current_version.prev_version
current_version = current_version.prev_version
end
all_versions
end
end
class RegistryComponent < ActiveRecord::Base
belongs_to :prev_version, :class_name => 'RegistryComponent', :foreign_key => 'prev_version_id'
has_one :next_version, :class_name => 'RegistryComponent', :foreign_key => 'prev_version_id'
attr_accessible :name, :version, :registry_file
mount_uploader :registry_file, ComponentFileUploader
validates :name, :presence => true
validates :version, :presence => true, :format => { :with => /\A\d{1,3}\.\d{1,2}\z/ }
validates :registry_file, :presence => true
validate :increased_version
def increased_version
# Check to ensure that version number is greater than the previous version number for the same component set
unless prev_version.nil?
version > prev_version.version
end
end
def all_previous_versions
prev_versions = all_versions
prev_versions.shift
prev_versions
end
def all_versions
current_version = self
all_versions = [current_version]
while !current_version.prev_version.nil?
all_versions << current_version.prev_version
current_version = current_version.prev_version
end
all_versions
end
end
I'm also looking at adding some other components in the future, again with very similar functionality.
I want to extract the common code from these classes into a single file (including the ActiveRecord method calls such as validates, etc.) and then just reference them in the concrete classes.
So far I've tried,
inheritance - I created a base class that inherited from ActiveRecord and then each class inherited from the base class. Result: Rails complained that it couldn't find a database table whose name matched the base class.
inheritance - I considered creating the base class as tableless model instead (see http://railscasts.com/episodes/219-active-model) but then I realised that the concrete classes would also be lacking the full ActiveRecord functionality
composition - I tried defining the common code in a module and then using include or extend in the concrete classes to access it as shown below.
module ComponentBase
belongs_to :prev_version, :class_name => self.class.name, :foreign_key => 'prev_version_id'
has_one :next_version, :class_name => self.class.name, :foreign_key => 'prev_version_id'
attr_accessible :name, :version
validates :name, :presence => true
validates :version, :presence => true, :format => { :with => /\A\d{1,3}\.\d{1,2}\z/ }
validate :increased_version
def increased_version
# Check to ensure that version number is greater than the previous version number for the same component set
unless prev_version.nil?
version > prev_version.version
end
end
def all_previous_versions
prev_versions = all_versions
prev_versions.shift
prev_versions
end
def all_versions
current_version = self
all_versions = [current_version]
while !current_version.prev_version.nil?
all_versions << current_version.prev_version
current_version = current_version.prev_version
end
all_versions
end
end
class RegistryComponent < ActiveRecord::Base
include ComponentBase
attr_accessible :registry_file
mount_uploader :registry_file, ComponentFileUploader
validates :registry_file, :presence => true
end
This resulted in an error that the belongs_to method is not defined for ComponentBase. This looks the most promising solution but is there any way to execute the ActiveRecord class methods within the context of the class that includes them? Alternatively, is there a better way for me to achieve the same aims?
Your first option was actually the best option. Rails uses Single Table Inheritance, which means the data for all of your subclasses is kept in the same table which is why you got the error you did.
What you should do is create a new model called Component and add to it all the fields that are common across all of your components as well as one extra field called type that should be a string field.
Your Component model will then have all the common fields, logic, and validations.
class Component < ActiveRecord::Base
...
end
Then have have each of your compontent classes subclass Component.
class ThirdPartyComponent < Component
...
end
I stumbled across the following answer at Extending a Ruby class with a standalone piece of code and with a bit of experimentation got it to work. So the final code I ended up with was,
module ComponentBase
def self.included(base)
base.class_eval do
belongs_to :prev_version, :class_name => base, :foreign_key => 'prev_version_id'
has_one :next_version, :class_name => base, :foreign_key => 'prev_version_id'
attr_accessible :name, :version
validates :name, :presence => true
validates :version, :presence => true, :format => { :with => /\A\d{1,3}\.\d{1,2}\z/ }
validate :increased_version
end
end
def increased_version
# Check to ensure that version number is greater than the previous version number for the same component set
unless prev_version.nil?
version > prev_version.version
end
end
def all_previous_versions
prev_versions = all_versions
prev_versions.shift
prev_versions
end
def all_versions
current_version = self
all_versions = [current_version]
while !current_version.prev_version.nil?
all_versions << current_version.prev_version
current_version = current_version.prev_version
end
all_versions
end
end
class RegistryComponent < ActiveRecord::Base
include ComponentBase
attr_accessible :registry_file
mount_uploader :registry_file, ComponentFileUploader
validates :registry_file, :presence => true
end
The solution was to use the included callback which is called each time the module is included somewhere else. Then, call class_eval on the base module to run the methods in the class context (i.e. as class methods). The trickiest part was getting the class name in this context but it simply turned out that I could use base (not entirely sure why this is the case but it works).
The issue is that you need the belongs_to method to run on the class that is including the module, not the module itself.
Check out module#included http://www.ruby-doc.org/core-2.0/Module.html#method-i-included, which will allow you to run code on the module you are including your module into. Remmeber that Module is an ancestor of Class so this works for classes and modules.
In this case you will want to run belongs_to on the class that the module is being included into, so something like the following should act as an example you can work off of:
module ComponentBase
def self.included(mod)
mod.class_eval do
belongs_to :prev_version, :class_name => self.class.name, :foreign_key => 'prev_version_id'
end
end
end

Uninitialized constant (NameError) when using FactoryGirl in module

Here's the error I'm getting when I try to run my tests with RSpec:
C:/Ruby193/lib/ruby/gems/1.9.1/gems/activesupport-3.2.11/lib/active_support/infl
ector/methods.rb:230:in `block in constantize': uninitialized constant User (Nam
eError)
I'm trying to run FactoryGirl with RSpec but without Rails. Here are the files that take part in the testing:
user_spec.rb
require 'spec_helper'
module Bluereader
describe User do
describe 'login' do
user = FactoryGirl.build(:user)
end
describe 'logout' do
end
describe 'create_account' do
end
describe 'delete_account' do
end
end
end
spec/spec_helper
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..'))
$LOAD_PATH.unshift(File.dirname(__FILE__))
require 'rspec'
require 'lib/bluereader'
require 'factory_girl'
FactoryGirl.find_definitions
spec/factories.rb
require 'digest/sha1'
FactoryGirl.define do
sequence(:username) { |n| "user-#{n}" }
factory :user do
username
encrypted_password Digest::SHA1.hexdigest('password')
full_name 'John Doe'
logged_in_at Time.now
logged_out_at 0
end
end
At this point I know that the factories.rb file is being loaded (I tried with the moronic print-debugging). When I remove the user = FactoryGirl.build(:user) line from user_spec.rb I get no errors (and the normal RSpec feedback telling me there are no tests, but no errors). If you are interested, here's my model:
require 'digest/sha1'
module Bluereader
class User < ActiveRecord::Base
has_many :categories, :foreign_key => :user_id
has_many :news, :foreign_key => :user_id
has_many :settings, :foreign_key => :user_id
attr_reader :full_name
class << self
def login(username, password)
encrypted_password = Digest::SHA1.hexdigest(password)
if not User.exists?(:username => username, :encrypted_password => encrypted_password)
user_id = User.id_from_username(username)
update(user_id, :logged_in_at => Time.now, :logged_out_at => 0)
end
end
def logout
update(current_user.id, :logged_out_at => Time.now)
end
def validate_account(username, password, full_name)
if username.empty? or password.empty or full_name.empty?
return 'Please fill in all the fields.'
end
if User.exists?(:username => username)
return 'That username is already in use.'
end
unless username =~ /^\w+$/
return 'Username field should contain only letters, numbers and underscores.'
end
''
end
def create_account(username, password, full_name)
encrypted_password = Digest::SHA1.hexdigest(password)
User.create(:username => username,
:encrypted_password => encrypted_password,
:full_name => full_name,
:logged_in_at => Time.now,
:logged_out_at => 0)
end
def delete_account
current_user.destroy
end
private
def id_from_username(username)
user = where(:username => username).first
user.nil? ? 0 : user.id
end
def current_user
where(:logged_out_at => 0).first
end
end
end
end
SOLUTION
The problem was that the class User was in a module, here's the solution:
factory :user, class: Bluereader::User do
You need to require the rails environment in your spec helper file. Add the following to spec/spec_helper.rb:
require File.expand_path("../../config/environment", __FILE__)
Update
Even if you're not using Rails, you'll still need to require the models in your spec helper.
Taken from the bottom of the question
The problem was that the class User was in a module, here's the solution:
factory :user, class: Bluereader::User do
For anyone clumsy like me, you may have FactoryGirl in your code where you meant to have FactoryBot

Use CanCan Authorization along with Custom Authentication in Rails 3

I am new to Rails and have been developing an app in rails 3 after following a Lynda.com tutorial where Kevin Skoglund showed us a way to authenticate a user using SHA1 Digest. I used that in my app and there is a need now to put in some Authorization. When I searched around, I found CanCan to be one of the better ones for authorization in rails. However, CanCan seems to be mostly implemented using Devise or Authlogic authentication and not custom authentication.
I wanted to know if it is at all possible to use CanCan if we use custom authentication, like I did. Is so, how to go about getting CanCan to work ?
It looks like CanCan needs some 'create_user' to be present but I am not sure how/where to create it.
Another alternative that I thought would be to put in my custom check on every page to check the user role and redirect them to an error page if they are unauthorized but that seems like a bad way to approach this problem...Your views on this please.
Please let me know if you need any additional information. I am using Ruby 1.9.3 and Rails 3.2.1.
Below is the way I have my current authentication set up. Any help would be greatly appreciated.
access_controller.rb
class AccessController < ApplicationController
before_filter :confirm_logged_in, :except => [:login, :attempt_login, :logout]
def attempt_login
authorized_user = User.authenticate(params[:username], params[:password])
if authorized_user
session[:user_id] = authorized_user.id
flash[:notice] = "You are logged in"
redirect_to(:controller => 'orders', :action => 'list')
else
flash[:notice] = "Invalid Username/password combination"
redirect_to(:action => 'login')
end
end
def logout
session[:user_id] = nil
flash[:notice] = "You have been logged out"
redirect_to(:action => 'login')
end
end
user.rb (User Model)
require 'digest/sha1'
class User < ActiveRecord::Base
has_one :profile
has_many :user_roles
has_many :roles, :through => :user_roles
attr_accessor :password
attr_protected :hashed_password, :salt
def self.authenticate(username="", password="")
user = User.find_by_username(username)
if user && user.password_match(password)
return user
else
return false
end
end
def password_match(password="")
hashed_password == User.hash_with_salt(password, salt)
end
validates_length_of :password, :within => 4..25, :on => :create
before_save :create_hashed_password
after_save :clear_password
def self.make_salt(username="")
Digest::SHA1.hexdigest("Use #{username} with #{Time.now} to make salt")
end
def self.hash_with_salt(password="", salt="")
Digest::SHA1.hexdigest("Put #{salt} on the #{password}" )
end
private
def create_hashed_password
unless password.blank?
self.salt = User.make_salt(username) if salt.blank?
self.hashed_password = User.hash_with_salt(password, salt)
end
end
def clear_password
self.password = nil
end
end
ApplicationController.rb
class ApplicationController < ActionController::Base
protect_from_forgery
private
def confirm_logged_in
unless session[:user_id]
flash[:notice] = "Please Log In"
redirect_to(:controller => 'access', :action => 'login')
return false
else
return true
end
end
end
I recommend first reading or watching the Railscast about CanCan. It is produced by the author of this gem and therefore very informative:
http://railscasts.com/episodes/192-authorization-with-cancan
You can also get help on the Github page:
https://github.com/ryanb/cancan
Somehow, you need to fetch the currently logged in user. This is what the current_user method does, and it needs to be defined on the users controller. Try something like this:
class UsersController < ApplicationController
# your other actions here
def current_user
User.find(session[:user_id])
end
end
Then, you should be able to use CanCan as described in the resources above.

how to autobuild an associated polymorphic activerecord object in rails 3

class ItemSource < ActiveRecord::Base
belongs_to :product, :polymorphic => true
end
class RandomProduct < ActiveRecord::Base
has_one :item_source, :as => :product, :autosave => true, :dependent => :destroy
end
What I'd like to do is is call:
a = RandomProduct.find(1)
a.item_source
and if item_source doesn't already exist (= nil), then build it automatically (build_item_source).
previously, I did this with alias_chain_method, but that's not supported in Rails 3.
oh, and I also tried this to no avail:
class RandomProduct < ActiveRecord::Base
has_one :item_source, :as => :product, :autosave => true, :dependent => :destroy
module AutoBuildItemSource
def item_source
super || build_item_source
end
end
include AutoBuildItemSource
end
In Rails 3, alias_method_chain (and alias_method, and alias) work fine:
class User < ActiveRecord::Base
has_one :profile, :inverse_of => :user
# This works:
#
# def profile_with_build
# profile_without_build || build_profile
# end
# alias_method_chain :profile, :build
#
# But so does this:
alias profile_without_build profile
def profile
profile_without_build || build_profile
end
end
But there's always accept_nested_attributes_for as an alternative, which calls build when profile_attributes are set. Combine it with delegate (optional) and you won't have to worry if the record exists or not:
class User < ActiveRecord::Base
has_one :profile, :inverse_of => :user
delegate :website, :to => :profile, :allow_nil => true
accepts_nested_attributes_for :profile
end
User.new.profile # => nil
User.new.website # => nil
u = User.new :profile_attributes => { :website => "http://example.com" }
u.profile # => #<Profile id: nil, user_id: nil, website: "http://example.com"...>
If the association is always created, delegation isn't necessary (but may be helpful, anyhow).
(Note: I set :inverse_of to make Profile.validates_presence_of :user work and to generally save queries.)
(Rails 4, FYI)
I personally prefer setting it up with after_initialize
after_initialize :after_initialize
def after_initialize
build_item_source if item_source.nil?
end
This also works well because you can automatically use forms with what would otherwise be an empty association (HAML because it's nicer):
= form_for #product do |f|
= f.fields_for :item_source do |isf|
= isf.label :prop1
= isf.text_field :prop1
If you didn't have the item_source built already, the label and text field wouldn't render at all.
How about creating the item_source when the RandomProduct is created:
class RandomProduct << ActiveRecord::Base
after_create :create_item_source
end
Of course, if you need to pass specific arguments to the item source, you could do something like this, instead:
class RandomProduct << ActiveRecord::Base
after_create :set_up_item_source
protected
def set_up_item_source
create_item_source(
:my => "options",
:go => "here"
)
end
end
Not that you really need a gem for this since it's so simple to do yourself, but here's a gem that makes it even easier to declare an auto-build:
https://github.com/TylerRick/active_record_auto_build_associations

Polymorphic has_many self-referential

I have A number of models (Article, Video, Photo)
Now I am trying to create a related_to association, such that
An article can have many other articles, videos and photos related to it. As can videos and photos.
Heres what I have tried:
module ActsAsRelatable
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def acts_as_relatable
has_many :related_items, :as => :related
has_many :source_items, :as => :source, :class_name => 'RelatedItem'
end
end
end
class RelatedItem < ActiveRecord::Base
belongs_to :source, :polymorphic => true
belongs_to :related, :polymorphic => true
end
Then I have added acts_as_relatable to my three models (Article, Video, Photo) and included the module in ActiveRecord::Base
When trying in ./script/console I get it to add the related items and the ids work correctly however the source_type and related_type are always the same (the object that related_items was called from) I want the related_item to be the other model name.
Any ideas anyone?
I would use the has many polymorphs plugin since it supports double sided polymorphism you can do something like this:
class Relating < ActiveRecord::Base
belongs_to :owner, :polymorphic => true
belongs_to :relative, :polymorphic => true
acts_as_double_polymorphic_join(
:owners => [:articles, :videos, :photos],
:relatives => [:articles, :videos, :photos]
)
end
and don't forget the db migration:
class CreateRelatings < ActiveRecord::Migration
def self.up
create_table :relating do |t|
t.references :owner, :polymorphic => true
t.references :relative, :polymorphic => true
end
end
def self.down
drop_table :relatings
end
end
I don't know if "Relating" is a good name, but you get the idea. Now an article, video and photo can be related to another article, video or photo.

Resources