From 4b6f5386631761307e976ba74a3f39ce9d2a250d Mon Sep 17 00:00:00 2001 From: Ryan Bates Date: Mon, 16 Nov 2009 19:59:40 -0800 Subject: [PATCH] moving can definition into ability instance instead of class, this removes ugly instance_exec command --- lib/cancan.rb | 1 - lib/cancan/ability.rb | 54 +++++++++++------------- lib/cancan/controller_additions.rb | 4 +- lib/cancan/instance_exec.rb | 15 ------- spec/cancan/ability_spec.rb | 31 ++++++-------- spec/cancan/controller_additions_spec.rb | 1 - 6 files changed, 41 insertions(+), 65 deletions(-) delete mode 100644 lib/cancan/instance_exec.rb diff --git a/lib/cancan.rb b/lib/cancan.rb index 227e421..aefc301 100644 --- a/lib/cancan.rb +++ b/lib/cancan.rb @@ -2,6 +2,5 @@ module CanCan class AccessDenied < StandardError; end end -require File.dirname(__FILE__) + '/cancan/instance_exec' require File.dirname(__FILE__) + '/cancan/ability' require File.dirname(__FILE__) + '/cancan/controller_additions' diff --git a/lib/cancan/ability.rb b/lib/cancan/ability.rb index f25878b..1e6a76c 100644 --- a/lib/cancan/ability.rb +++ b/lib/cancan/ability.rb @@ -2,15 +2,8 @@ module CanCan module Ability attr_accessor :user - def self.included(base) - base.extend ClassMethods - base.alias_action :index, :show, :to => :read - base.alias_action :new, :to => :create - base.alias_action :edit, :to => :update - end - def can?(original_action, target) # TODO this could use some refactoring - (self.class.can_history || []).reverse.each do |can_action, can_target, can_block| + (@can_history || []).reverse.each do |can_action, can_target, can_block| possible_actions_for(original_action).each do |action| if (can_action == :manage || can_action == action) && (can_target == :all || can_target == target || target.kind_of?(can_target)) if can_block.nil? @@ -20,7 +13,7 @@ module CanCan block_args << action if can_action == :manage block_args << (target.class == Class ? target : target.class) if can_target == :all block_args << (target.class == Class ? nil : target) - return instance_exec(*block_args, &can_block) + return can_block.call(*block_args) end end end @@ -30,32 +23,33 @@ module CanCan def possible_actions_for(initial_action) actions = [initial_action] - (self.class.aliased_actions || []).each do |target, aliases| + (@aliased_actions || default_alias_actions).each do |target, aliases| actions += possible_actions_for(target) if aliases.include? initial_action end actions end + + def can(action, target, &block) + @can_history ||= [] + @can_history << [action, target, block] + end - module ClassMethods - attr_reader :can_history - attr_reader :aliased_actions - - def can(action, target, &block) - @can_history ||= [] - @can_history << [action, target, block] - end - - def alias_action(*args) - @aliased_actions ||= {} - target = args.pop[:to] - @aliased_actions[target] = args - end - - def for_user(user) - ability = new - ability.user = user - ability - end + def alias_action(*args) + @aliased_actions ||= default_alias_actions + target = args.pop[:to] + @aliased_actions[target] = args + end + + def default_alias_actions + { + :read => [:index, :show], + :create => [:new], + :update => [:edit], + } + end + + def prepare(user) + # to be overriden by included class end end end diff --git a/lib/cancan/controller_additions.rb b/lib/cancan/controller_additions.rb index 6c8caa4..b1a6f99 100644 --- a/lib/cancan/controller_additions.rb +++ b/lib/cancan/controller_additions.rb @@ -9,7 +9,9 @@ module CanCan end def current_ability - ::Ability.for_user(current_user) + ability = ::Ability.new + ability.prepare(current_user) + ability end def can?(*args) diff --git a/lib/cancan/instance_exec.rb b/lib/cancan/instance_exec.rb deleted file mode 100644 index fa28ad3..0000000 --- a/lib/cancan/instance_exec.rb +++ /dev/null @@ -1,15 +0,0 @@ -# see http://eigenclass.org/hiki.rb?instance_exec -class Object - module InstanceExecHelper; end - include InstanceExecHelper - def instance_exec(*args, &block) # !> method redefined; discarding old instance_exec - mname = "__instance_exec_#{Thread.current.object_id.abs}_#{object_id.abs}" - InstanceExecHelper.module_eval{ define_method(mname, &block) } - begin - ret = send(mname, *args) - ensure - InstanceExecHelper.module_eval{ undef_method(mname) } rescue nil - end - ret - end -end diff --git a/spec/cancan/ability_spec.rb b/spec/cancan/ability_spec.rb index abefab9..614d16d 100644 --- a/spec/cancan/ability_spec.rb +++ b/spec/cancan/ability_spec.rb @@ -8,7 +8,7 @@ describe CanCan::Ability do end it "should be able to :read anything" do - @ability_class.can :read, :all + @ability.can :read, :all @ability.can?(:read, String).should be_true @ability.can?(:read, 123).should be_true end @@ -18,8 +18,8 @@ describe CanCan::Ability do end it "should return what block returns on a can call" do - @ability_class.can :read, :all - @ability_class.can :read, Symbol do |sym| + @ability.can :read, :all + @ability.can :read, Symbol do |sym| sym end @ability.can?(:read, Symbol).should be_nil @@ -27,21 +27,21 @@ describe CanCan::Ability do end it "should pass class with object if :all objects are accepted" do - @ability_class.can :preview, :all do |object_class, object| + @ability.can :preview, :all do |object_class, object| [object_class, object] end @ability.can?(:preview, 123).should == [Fixnum, 123] end it "should pass class with no object if :all objects are accepted and class is passed directly" do - @ability_class.can :preview, :all do |object_class, object| + @ability.can :preview, :all do |object_class, object| [object_class, object] end @ability.can?(:preview, Hash).should == [Hash, nil] end it "should pass action and object for global manage actions" do - @ability_class.can :manage, Array do |action, object| + @ability.can :manage, Array do |action, object| [action, object] end @ability.can?(:stuff, [1, 2]).should == [:stuff, [1, 2]] @@ -49,8 +49,8 @@ describe CanCan::Ability do end it "should alias update or destroy actions to modify action" do - @ability_class.alias_action :update, :destroy, :to => :modify - @ability_class.can :modify, :all do |object_class, object| + @ability.alias_action :update, :destroy, :to => :modify + @ability.can :modify, :all do |object_class, object| :modify_called end @ability.can?(:update, 123).should == :modify_called @@ -58,7 +58,7 @@ describe CanCan::Ability do end it "should return block result for action, object_class, and object for any action" do - @ability_class.can :manage, :all do |action, object_class, object| + @ability.can :manage, :all do |action, object_class, object| [action, object_class, object] end @ability.can?(:foo, 123).should == [:foo, Fixnum, 123] @@ -66,22 +66,19 @@ describe CanCan::Ability do end it "should automatically alias index and show into read calls" do - @ability_class.can :read, :all + @ability.can :read, :all @ability.can?(:index, 123).should be_true @ability.can?(:show, 123).should be_true end it "should automatically alias new and edit into create and update respectively" do - @ability_class.can(:create, :all) { :create_called } - @ability_class.can(:update, :all) { :update_called } + @ability.can(:create, :all) { :create_called } + @ability.can(:update, :all) { :update_called } @ability.can?(:new, 123).should == :create_called @ability.can?(:edit, 123).should == :update_called end - it "should be able to access given user" do - @ability_class.can(:preview, :all) { user } - ability = @ability_class.for_user(:some_user) - ability.user.should == :some_user - ability.can?(:preview, 123).should == :some_user + it "should respond to prepare" do + @ability.should respond_to(:prepare) end end diff --git a/spec/cancan/controller_additions_spec.rb b/spec/cancan/controller_additions_spec.rb index 38bb582..0b5d06e 100644 --- a/spec/cancan/controller_additions_spec.rb +++ b/spec/cancan/controller_additions_spec.rb @@ -21,7 +21,6 @@ describe CanCan::ControllerAdditions do it "should have a current_ability method which generates an ability for the current user" do stub(@controller).current_user { :current_user } @controller.current_ability.should be_kind_of(Ability) - @controller.current_ability.user.should == :current_user end it "should provide a can? method which goes through the current ability" do