Fix bug with MongoidAdditions throwing a NameError when Mongoid is not defined by always checking if Mongoid is defined before referencing Mongoid-related constants

Also add spec for this bug
This commit is contained in:
Mani Tadayon 2010-12-26 02:17:50 -08:00
parent ebb8e1bf8b
commit e14e1edec2
2 changed files with 145 additions and 122 deletions

View File

@ -1,10 +1,9 @@
module CanCan module CanCan
module Ability module Ability
# could use alias_method_chain, but it's not worth adding activesupport as a gem dependency # could use alias_method_chain, but it's not worth adding activesupport as a gem dependency
alias_method :query_without_mongoid_support, :query alias_method :query_without_mongoid_support, :query
def query(action, subject) def query(action, subject)
if Object.const_defined?(:Mongoid) && subject <= CanCan::MongoidAdditions if defined?(::Mongoid) && subject <= CanCan::MongoidAdditions
query_with_mongoid_support(action, subject) query_with_mongoid_support(action, subject)
else else
query_without_mongoid_support(action, subject) query_without_mongoid_support(action, subject)
@ -47,7 +46,7 @@ module CanCan
# => true # => true
class Rule class Rule
def matches_conditions_hash_with_mongoid_subject?(subject, conditions = @conditions) def matches_conditions_hash_with_mongoid_subject?(subject, conditions = @conditions)
if subject.class.include?(Mongoid::Document) && conditions.any?{|k,v| !k.kind_of?(Symbol)} if defined?(::Mongoid) && subject.class.include?(::Mongoid::Document) && conditions.any?{|k,v| !k.kind_of?(Symbol)}
if conditions.empty? if conditions.empty?
true true
else else
@ -98,7 +97,7 @@ end
# Info on monkeypatching Mongoid : # Info on monkeypatching Mongoid :
# http://log.mazniak.org/post/719062325/monkey-patching-activesupport-concern-and-you#footer # http://log.mazniak.org/post/719062325/monkey-patching-activesupport-concern-and-you#footer
if defined? Mongoid if defined?(::Mongoid)
module Mongoid module Mongoid
module Components module Components
old_block = @_included_block old_block = @_included_block

View File

@ -1,5 +1,5 @@
require 'mongoid' # require mongoid first so MongoidAdditions are loaded
require "spec_helper" require "spec_helper"
require 'mongoid'
class MongoidCategory class MongoidCategory
include Mongoid::Document include Mongoid::Document
@ -38,129 +38,153 @@ Mongoid.configure do |config|
end end
describe CanCan::MongoidAdditions do describe CanCan::MongoidAdditions do
before(:each) do context "Mongoid not defined" do
@model_class = MongoidProject before(:all) do
@ability = Object.new @mongoid_class = Object.send(:remove_const, :Mongoid)
@ability.extend(CanCan::Ability)
end
after(:each) do
Mongoid.master.collections.select do |collection|
collection.name !~ /system/
end.each(&:drop)
end
it "should compare properties on mongoid documents with the conditions hash" do
model = @model_class.new
@ability.can :read, @model_class, :id => model.id
@ability.should be_able_to :read, model
end
it "should return [] when no ability is defined so no records are found" do
@model_class.create :title => 'Sir'
@model_class.create :title => 'Lord'
@model_class.create :title => 'Dude'
@model_class.accessible_by(@ability, :read).entries.should == []
end
it "should return the correct records based on the defined ability" do
@ability.can :read, @model_class, :title => "Sir"
sir = @model_class.create :title => 'Sir'
lord = @model_class.create :title => 'Lord'
dude = @model_class.create :title => 'Dude'
@model_class.accessible_by(@ability, :read).should == [sir]
end
it "should return everything when the defined ability is manage all" do
@ability.can :manage, :all
sir = @model_class.create :title => 'Sir'
lord = @model_class.create :title => 'Lord'
dude = @model_class.create :title => 'Dude'
@model_class.accessible_by(@ability, :read).entries.should == [sir, lord, dude]
end
describe "Mongoid::Criteria where clause Symbol extensions using MongoDB expressions" do
it "should handle :field.in" do
obj = @model_class.create :title => 'Sir'
@ability.can :read, @model_class, :title.in => ["Sir", "Madam"]
@ability.can?(:read, obj).should == true
@model_class.accessible_by(@ability, :read).should == [obj]
obj2 = @model_class.create :title => 'Lord'
@ability.can?(:read, obj2).should == false
end end
describe "activates only when there are Criteria in the hash" do after(:all) do
it "Calls where on the model class when there are criteria" do Object.const_set(:Mongoid, @mongoid_class)
obj = @model_class.create :title => 'Bird'
@conditions = {:title.nin => ["Fork", "Spoon"]}
mock(@model_class).where(@conditions) {[obj]}
@ability.can :read, @model_class, @conditions
@ability.should be_able_to(:read, obj)
end
it "Calls the base version if there are no mongoid criteria" do
obj = @model_class.new :title => 'Bird'
@conditions = {:id => obj.id}
@ability.can :read, @model_class, @conditions
@ability.should be_able_to(:read, obj)
end
end end
it "should not raise an error for ActiveRecord models" do
it "should handle :field.nin" do @model_class = Class.new(Project)
obj = @model_class.create :title => 'Sir' stub(@model_class).scoped { :scoped_stub }
@ability.can :read, @model_class, :title.nin => ["Lord", "Madam"] @model_class.send(:include, CanCan::ActiveRecordAdditions)
@ability.can?(:read, obj).should == true @ability = Object.new
@model_class.accessible_by(@ability, :read).should == [obj] @ability.extend(CanCan::Ability)
obj2 = @model_class.create :title => 'Lord' @ability.can :read, @model_class
@ability.can?(:read, obj2).should == false lambda {
end @ability.can? :read, @model_class.new
}.should_not raise_error
it "should handle :field.size" do
obj = @model_class.create :titles => ['Palatin', 'Margrave']
@ability.can :read, @model_class, :titles.size => 2
@ability.can?(:read, obj).should == true
@model_class.accessible_by(@ability, :read).should == [obj]
obj2 = @model_class.create :titles => ['Palatin', 'Margrave', 'Marquis']
@ability.can?(:read, obj2).should == false
end
it "should handle :field.exists" do
obj = @model_class.create :titles => ['Palatin', 'Margrave']
@ability.can :read, @model_class, :titles.exists => true
@ability.can?(:read, obj).should == true
@model_class.accessible_by(@ability, :read).should == [obj]
obj2 = @model_class.create
@ability.can?(:read, obj2).should == false
end
it "should handle :field.gt" do
obj = @model_class.create :age => 50
@ability.can :read, @model_class, :age.gt => 45
@ability.can?(:read, obj).should == true
@model_class.accessible_by(@ability, :read).should == [obj]
obj2 = @model_class.create :age => 40
@ability.can?(:read, obj2).should == false
end end
end end
context "Mongoid defined" do
before(:each) do
@model_class = MongoidProject
@ability = Object.new
@ability.extend(CanCan::Ability)
end
it "should call where with matching ability conditions" do after(:each) do
obj = @model_class.create :foo => {:bar => 1} Mongoid.master.collections.select do |collection|
@ability.can :read, @model_class, :foo => {:bar => 1} collection.name !~ /system/
@model_class.accessible_by(@ability, :read).entries.first.should == obj end.each(&:drop)
end end
it "should not allow to fetch records when ability with just block present" do it "should compare properties on mongoid documents with the conditions hash" do
@ability.can :read, @model_class do false end model = @model_class.new
lambda { @ability.can :read, @model_class, :id => model.id
@model_class.accessible_by(@ability) @ability.should be_able_to :read, model
}.should raise_error(CanCan::Error) end
it "should return [] when no ability is defined so no records are found" do
@model_class.create :title => 'Sir'
@model_class.create :title => 'Lord'
@model_class.create :title => 'Dude'
@model_class.accessible_by(@ability, :read).entries.should == []
end
it "should return the correct records based on the defined ability" do
@ability.can :read, @model_class, :title => "Sir"
sir = @model_class.create :title => 'Sir'
lord = @model_class.create :title => 'Lord'
dude = @model_class.create :title => 'Dude'
@model_class.accessible_by(@ability, :read).should == [sir]
end
it "should return everything when the defined ability is manage all" do
@ability.can :manage, :all
sir = @model_class.create :title => 'Sir'
lord = @model_class.create :title => 'Lord'
dude = @model_class.create :title => 'Dude'
@model_class.accessible_by(@ability, :read).entries.should == [sir, lord, dude]
end
describe "Mongoid::Criteria where clause Symbol extensions using MongoDB expressions" do
it "should handle :field.in" do
obj = @model_class.create :title => 'Sir'
@ability.can :read, @model_class, :title.in => ["Sir", "Madam"]
@ability.can?(:read, obj).should == true
@model_class.accessible_by(@ability, :read).should == [obj]
obj2 = @model_class.create :title => 'Lord'
@ability.can?(:read, obj2).should == false
end
describe "activates only when there are Criteria in the hash" do
it "Calls where on the model class when there are criteria" do
obj = @model_class.create :title => 'Bird'
@conditions = {:title.nin => ["Fork", "Spoon"]}
mock(@model_class).where(@conditions) {[obj]}
@ability.can :read, @model_class, @conditions
@ability.should be_able_to(:read, obj)
end
it "Calls the base version if there are no mongoid criteria" do
obj = @model_class.new :title => 'Bird'
@conditions = {:id => obj.id}
@ability.can :read, @model_class, @conditions
@ability.should be_able_to(:read, obj)
end
end
it "should handle :field.nin" do
obj = @model_class.create :title => 'Sir'
@ability.can :read, @model_class, :title.nin => ["Lord", "Madam"]
@ability.can?(:read, obj).should == true
@model_class.accessible_by(@ability, :read).should == [obj]
obj2 = @model_class.create :title => 'Lord'
@ability.can?(:read, obj2).should == false
end
it "should handle :field.size" do
obj = @model_class.create :titles => ['Palatin', 'Margrave']
@ability.can :read, @model_class, :titles.size => 2
@ability.can?(:read, obj).should == true
@model_class.accessible_by(@ability, :read).should == [obj]
obj2 = @model_class.create :titles => ['Palatin', 'Margrave', 'Marquis']
@ability.can?(:read, obj2).should == false
end
it "should handle :field.exists" do
obj = @model_class.create :titles => ['Palatin', 'Margrave']
@ability.can :read, @model_class, :titles.exists => true
@ability.can?(:read, obj).should == true
@model_class.accessible_by(@ability, :read).should == [obj]
obj2 = @model_class.create
@ability.can?(:read, obj2).should == false
end
it "should handle :field.gt" do
obj = @model_class.create :age => 50
@ability.can :read, @model_class, :age.gt => 45
@ability.can?(:read, obj).should == true
@model_class.accessible_by(@ability, :read).should == [obj]
obj2 = @model_class.create :age => 40
@ability.can?(:read, obj2).should == false
end
end
it "should call where with matching ability conditions" do
obj = @model_class.create :foo => {:bar => 1}
@ability.can :read, @model_class, :foo => {:bar => 1}
@model_class.accessible_by(@ability, :read).entries.first.should == obj
end
it "should not allow to fetch records when ability with just block present" do
@ability.can :read, @model_class do false end
lambda {
@model_class.accessible_by(@ability)
}.should raise_error(CanCan::Error)
end
end end
end end