Skip to content
This repository has been archived by the owner on Dec 12, 2021. It is now read-only.

Commit

Permalink
improving DataMapper adapter and specs
Browse files Browse the repository at this point in the history
  • Loading branch information
ryanb committed Jan 5, 2011
1 parent cef6c21 commit 15ca8ad
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 40 deletions.
2 changes: 2 additions & 0 deletions Gemfile
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ when nil, "active_record"
gem "with_model" gem "with_model"
when "data_mapper" when "data_mapper"
gem "dm-core", "~> 1.0.2" gem "dm-core", "~> 1.0.2"
gem "dm-sqlite-adapter", "~> 1.0.2"
gem "dm-migrations", "~> 1.0.2"
when "mongoid" when "mongoid"
gem "bson_ext", "~> 1.1" gem "bson_ext", "~> 1.1"
gem "mongoid", "~> 2.0.0.beta.19" gem "mongoid", "~> 2.0.0.beta.19"
Expand Down
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Original file line Diff line number Diff line change
@@ -1,4 +1,4 @@
Copyright (c) 2010 Ryan Bates Copyright (c) 2011 Ryan Bates


Permission is hereby granted, free of charge, to any person obtaining Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the a copy of this software and associated documentation files (the
Expand Down
10 changes: 9 additions & 1 deletion lib/cancan/model_adapters/data_mapper_adapter.rb
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -5,8 +5,16 @@ def self.for_class?(model_class)
model_class <= DataMapper::Resource model_class <= DataMapper::Resource
end end


def self.override_conditions_hash_matching?(subject, conditions)
conditions.any? { |k,v| !k.kind_of?(Symbol) }
end

def self.matches_conditions_hash?(subject, conditions)
subject.class.all(:conditions => conditions).include?(subject) # TODO don't use a database query here for performance and other instances
end

def database_records def database_records
scope = @model_class.all(:conditions => ['true=false']) scope = @model_class.all(:conditions => ["0=1"])
conditions.each do |condition| conditions.each do |condition|
scope += @model_class.all(:conditions => condition) scope += @model_class.all(:conditions => condition)
end end
Expand Down
2 changes: 1 addition & 1 deletion lib/cancan/model_adapters/mongoid_adapter.rb
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ def self.override_conditions_hash_matching?(subject, conditions)
end end


def self.matches_conditions_hash?(subject, conditions) def self.matches_conditions_hash?(subject, conditions)
subject.class.where(conditions).include?(subject) # just use Mongoid's where function subject.class.where(conditions).include?(subject) # TODO don't use a database query here for performance and other instances
end end


def database_records def database_records
Expand Down
123 changes: 86 additions & 37 deletions spec/cancan/model_adapters/data_mapper_adapter_spec.rb
Original file line number Original file line Diff line number Diff line change
@@ -1,66 +1,115 @@
if ENV["MODEL_ADAPTER"] == "data_mapper" if ENV["MODEL_ADAPTER"] == "data_mapper"
require "spec_helper" require "spec_helper"


DataMapper.setup(:default, 'sqlite::memory:')

class Article
include DataMapper::Resource
property :id, Serial
property :published, Boolean, :default => false
property :secret, Boolean, :default => false
property :priority, Integer
has n, :comments
end

class Comment
include DataMapper::Resource
property :id, Serial
property :spam, Boolean, :default => false
belongs_to :article
end

DataMapper.finalize
DataMapper.auto_migrate!

describe CanCan::ModelAdapters::DataMapperAdapter do describe CanCan::ModelAdapters::DataMapperAdapter do
before(:each) do before(:each) do
@model_class = Class.new Article.destroy
@model_class.class_eval do Comment.destroy
include DataMapper::Resource
end
stub(@model_class).all(:conditions => ['true=false']) { 'no-match:' }

@ability = Object.new @ability = Object.new
@ability.extend(CanCan::Ability) @ability.extend(CanCan::Ability)
end end


it "should be for only data mapper classes" do it "should be for only data mapper classes" do
CanCan::ModelAdapters::DataMapperAdapter.should_not be_for_class(Object) CanCan::ModelAdapters::DataMapperAdapter.should_not be_for_class(Object)
CanCan::ModelAdapters::DataMapperAdapter.should be_for_class(@model_class) CanCan::ModelAdapters::DataMapperAdapter.should be_for_class(Article)
CanCan::ModelAdapters::AbstractAdapter.adapter_class(@model_class).should == CanCan::ModelAdapters::DataMapperAdapter CanCan::ModelAdapters::AbstractAdapter.adapter_class(Article).should == CanCan::ModelAdapters::DataMapperAdapter
end end


it "should return no records when no ability is defined so no records are found" do it "should not fetch any records when no abilities are defined" do
@model_class.accessible_by(@ability, :read).should == 'no-match:' Article.create
Article.accessible_by(@ability).should be_empty
end end


it "should call all with matching ability conditions" do it "should fetch all articles when one can read all" do
@ability.can :read, @model_class, :foo => {:bar => 1} @ability.can :read, Article
stub(@model_class).all(:conditions => {:foo => {:bar => 1}}) { 'found-records:' } article = Article.create
@model_class.accessible_by(@ability, :read).should == 'no-match:found-records:' Article.accessible_by(@ability).should == [article]
end end


it "should merge association joins and sanitize conditions" do it "should fetch only the articles that are published" do
@ability.can :read, @model_class, :foo => {:bar => 1} @ability.can :read, Article, :published => true
@ability.can :read, @model_class, :too => {:car => 1, :far => {:bar => 1}} article1 = Article.create(:published => true)

article2 = Article.create(:published => false)
stub(@model_class).all(:conditions => {:foo => {:bar => 1}}) { 'foo:' } Article.accessible_by(@ability).should == [article1]
stub(@model_class).all(:conditions => {:too => {:car => 1, :far => {:bar => 1}}}) { 'too:' } end


@model_class.accessible_by(@ability).should == 'no-match:too:foo:' it "should fetch any articles which are published or secret" do
@ability.can :read, Article, :published => true
@ability.can :read, Article, :secret => true
article1 = Article.create(:published => true, :secret => false)
article2 = Article.create(:published => true, :secret => true)
article3 = Article.create(:published => false, :secret => true)
article4 = Article.create(:published => false, :secret => false)
Article.accessible_by(@ability).should == [article1, article2, article3]
end end


it "should allow to define sql conditions by not hash" do it "should fetch only the articles that are published and not secret" do
@ability.can :read, @model_class, :foo => 1 pending "the `cannot` may require some custom SQL, maybe abstract out from Active Record adapter"
@ability.can :read, @model_class, ['bar = ?', 1] @ability.can :read, Article, :published => true
@ability.cannot :read, Article, :secret => true
article1 = Article.create(:published => true, :secret => false)
article2 = Article.create(:published => true, :secret => true)
article3 = Article.create(:published => false, :secret => true)
article4 = Article.create(:published => false, :secret => false)
Article.accessible_by(@ability).should == [article1]
end


stub(@model_class).all(:conditions => {:foo => 1}) { 'foo:' } it "should only read comments for articles which are published" do
stub(@model_class).all(:conditions => ['bar = ?', 1]) { 'bar:' } @ability.can :read, Comment, :article => { :published => true }
comment1 = Comment.create(:article => Article.create!(:published => true))
comment2 = Comment.create(:article => Article.create!(:published => false))
Comment.accessible_by(@ability).should == [comment1]
end


@model_class.accessible_by(@ability).should == 'no-match:bar:foo:' it "should allow conditions in SQL and merge with hash conditions" do
@ability.can :read, Article, :published => true
@ability.can :read, Article, ["secret=?", true]
article1 = Article.create(:published => true, :secret => false)
article4 = Article.create(:published => false, :secret => false)
Article.accessible_by(@ability).should == [article1]
end end


it "should not allow to fetch records when ability with just block present" do it "should match gt comparison" do
@ability.can :read, @model_class do false end @ability.can :read, Article, :priority.gt => 3
lambda { article1 = Article.create(:priority => 4)
@model_class.accessible_by(@ability) article2 = Article.create(:priority => 3)
}.should raise_error(CanCan::Error) Article.accessible_by(@ability).should == [article1]
@ability.should be_able_to(:read, article1)
@ability.should_not be_able_to(:read, article2)
end end


it "should not allow to check ability on object when nonhash sql ability definition without block present" do it "should match gte comparison" do
@ability.can :read, @model_class, ['bar = ?', 1] @ability.can :read, Article, :priority.gte => 3
lambda { article1 = Article.create(:priority => 4)
@ability.can? :read, @model_class.new article2 = Article.create(:priority => 3)
}.should raise_error(CanCan::Error) article3 = Article.create(:priority => 2)
Article.accessible_by(@ability).should == [article1, article2]
@ability.should be_able_to(:read, article1)
@ability.should be_able_to(:read, article2)
@ability.should_not be_able_to(:read, article3)
end end

# TODO: add more comparison specs
end end
end end

0 comments on commit 15ca8ad

Please sign in to comment.