Add support for Mongoid documents along with basic specs.
This commit is contained in:
		
							parent
							
								
									f901c367fc
								
							
						
					
					
						commit
						be74df0548
					
				@ -15,6 +15,9 @@ Gem::Specification.new do |s|
 | 
				
			|||||||
  s.add_development_dependency 'rr', '~> 0.10.11' # 1.0.0 has respond_to? issues: http://github.com/btakita/rr/issues/issue/43
 | 
					  s.add_development_dependency 'rr', '~> 0.10.11' # 1.0.0 has respond_to? issues: http://github.com/btakita/rr/issues/issue/43
 | 
				
			||||||
  s.add_development_dependency 'supermodel', '~> 0.1.4'
 | 
					  s.add_development_dependency 'supermodel', '~> 0.1.4'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  s.add_development_dependency 'mongoid', '~> 2.0.0.beta.19'
 | 
				
			||||||
 | 
					  s.add_development_dependency 'bson_ext', '~> 1.1'
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
  s.rubyforge_project = s.name
 | 
					  s.rubyforge_project = s.name
 | 
				
			||||||
  s.required_rubygems_version = ">= 1.3.4"
 | 
					  s.required_rubygems_version = ">= 1.3.4"
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
				
			|||||||
@ -3,6 +3,7 @@ require 'cancan/can_definition'
 | 
				
			|||||||
require 'cancan/controller_resource'
 | 
					require 'cancan/controller_resource'
 | 
				
			||||||
require 'cancan/controller_additions'
 | 
					require 'cancan/controller_additions'
 | 
				
			||||||
require 'cancan/active_record_additions'
 | 
					require 'cancan/active_record_additions'
 | 
				
			||||||
 | 
					require 'cancan/mongoid_additions'
 | 
				
			||||||
require 'cancan/exceptions'
 | 
					require 'cancan/exceptions'
 | 
				
			||||||
require 'cancan/query'
 | 
					require 'cancan/query'
 | 
				
			||||||
require 'cancan/inherited_resource'
 | 
					require 'cancan/inherited_resource'
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										78
									
								
								lib/cancan/mongoid_additions.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								lib/cancan/mongoid_additions.rb
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,78 @@
 | 
				
			|||||||
 | 
					module CanCan
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  module Ability
 | 
				
			||||||
 | 
					    alias_method :query_without_mongoid_support, :query
 | 
				
			||||||
 | 
					    def query(action, subject)
 | 
				
			||||||
 | 
					      if Object.const_defined?(:Mongoid) && subject <= CanCan::MongoidAdditions
 | 
				
			||||||
 | 
					        query_with_mongoid_support(action, subject)
 | 
				
			||||||
 | 
					      else
 | 
				
			||||||
 | 
					        query_without_mongoid_support(action, subject)
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    def query_with_mongoid_support(action, subject)
 | 
				
			||||||
 | 
					      MongoidQuery.new(subject, relevant_can_definitions_for_query(action, subject))
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  class MongoidQuery
 | 
				
			||||||
 | 
					    def initialize(sanitizer, can_definitions)
 | 
				
			||||||
 | 
					      @sanitizer = sanitizer
 | 
				
			||||||
 | 
					      @can_definitions = can_definitions
 | 
				
			||||||
 | 
					    end    
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    def conditions
 | 
				
			||||||
 | 
					      @can_definitions.first.try(:tableized_conditions)
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  module MongoidAdditions
 | 
				
			||||||
 | 
					    module ClassMethods
 | 
				
			||||||
 | 
					      # Returns a scope which fetches only the records that the passed ability
 | 
				
			||||||
 | 
					      # can perform a given action on. The action defaults to :read. This
 | 
				
			||||||
 | 
					      # is usually called from a controller and passed the +current_ability+.
 | 
				
			||||||
 | 
					      #
 | 
				
			||||||
 | 
					      #   @articles = Article.accessible_by(current_ability)
 | 
				
			||||||
 | 
					      # 
 | 
				
			||||||
 | 
					      # Here only the articles which the user is able to read will be returned.
 | 
				
			||||||
 | 
					      # If the user does not have permission to read any articles then an empty
 | 
				
			||||||
 | 
					      # result is returned. Since this is a scope it can be combined with any
 | 
				
			||||||
 | 
					      # other scopes or pagination.
 | 
				
			||||||
 | 
					      # 
 | 
				
			||||||
 | 
					      # An alternative action can optionally be passed as a second argument.
 | 
				
			||||||
 | 
					      # 
 | 
				
			||||||
 | 
					      #   @articles = Article.accessible_by(current_ability, :update)
 | 
				
			||||||
 | 
					      # 
 | 
				
			||||||
 | 
					      # Here only the articles which the user can update are returned. This
 | 
				
			||||||
 | 
					      # internally uses Ability#conditions method, see that for more information.
 | 
				
			||||||
 | 
					      def accessible_by(ability, action = :read)
 | 
				
			||||||
 | 
					        query = ability.query(action, self)        
 | 
				
			||||||
 | 
					        if query.conditions.blank?
 | 
				
			||||||
 | 
					          # this query is sure to return no results
 | 
				
			||||||
 | 
					          # we need this so there is a Mongoid::Criteria object to return, since an empty array would cause problems
 | 
				
			||||||
 | 
					          where({:_id => {'$exists' => true, '$type' => 2}})  
 | 
				
			||||||
 | 
					        else   
 | 
				
			||||||
 | 
					          where(query.conditions)
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    def self.included(base)
 | 
				
			||||||
 | 
					      base.extend ClassMethods
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Info on monkeypatching Mongoid : 
 | 
				
			||||||
 | 
					# http://log.mazniak.org/post/719062325/monkey-patching-activesupport-concern-and-you#footer
 | 
				
			||||||
 | 
					if defined? Mongoid
 | 
				
			||||||
 | 
					  module Mongoid
 | 
				
			||||||
 | 
					    module Components
 | 
				
			||||||
 | 
					      old_block = @_included_block
 | 
				
			||||||
 | 
					      @_included_block = Proc.new do 
 | 
				
			||||||
 | 
					        class_eval(&old_block) if old_block
 | 
				
			||||||
 | 
					        include CanCan::MongoidAdditions
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					  end  
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
							
								
								
									
										67
									
								
								spec/cancan/mongoid_additions_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								spec/cancan/mongoid_additions_spec.rb
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,67 @@
 | 
				
			|||||||
 | 
					require "spec_helper"
 | 
				
			||||||
 | 
					require 'mongoid'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class MongoidCategory
 | 
				
			||||||
 | 
					  include Mongoid::Document
 | 
				
			||||||
 | 
					  references_many :mongoid_projects
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class MongoidProject
 | 
				
			||||||
 | 
					  include Mongoid::Document
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  referenced_in :mongoid_category
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  class << self
 | 
				
			||||||
 | 
					    protected
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def sanitize_sql(hash_cond)
 | 
				
			||||||
 | 
					      hash_cond
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def sanitize_hash(hash)
 | 
				
			||||||
 | 
					      hash.map do |name, value|
 | 
				
			||||||
 | 
					        if Hash === value
 | 
				
			||||||
 | 
					          sanitize_hash(value).map{|cond| "#{name}.#{cond}"}
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					          "#{name}=#{value}"
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					      end.flatten
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Mongoid.configure do |config|
 | 
				
			||||||
 | 
					  config.master = Mongo::Connection.new('127.0.0.1', 27017).db("workflow_on_mongoid")
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					describe CanCan::MongoidAdditions do
 | 
				
			||||||
 | 
					  before(:each) do
 | 
				
			||||||
 | 
					    @model_class = Class.new(MongoidProject)
 | 
				
			||||||
 | 
					    stub(@model_class).scoped { :scoped_stub }
 | 
				
			||||||
 | 
					    @model_class.send(:include, CanCan::MongoidAdditions)
 | 
				
			||||||
 | 
					    @ability = Object.new
 | 
				
			||||||
 | 
					    @ability.extend(CanCan::Ability)
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  after(:each) do
 | 
				
			||||||
 | 
					    Mongoid.master.collections.select do |collection|
 | 
				
			||||||
 | 
					      collection.name !~ /system/
 | 
				
			||||||
 | 
					    end.each(&:drop)    
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  it "should return [] when no ability is defined so no records are found" do
 | 
				
			||||||
 | 
					    @model_class.accessible_by(@ability, :read).should == []
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  it "should call where with matching ability conditions" do
 | 
				
			||||||
 | 
					    @ability.can :read, @model_class, :foo => {:bar => 1}
 | 
				
			||||||
 | 
					    @model_class.accessible_by(@ability, :read).should == @model_class.where(:foos => { :bar => 1 })
 | 
				
			||||||
 | 
					  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
 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user