marnen-cancan 2.0.0.alpha.pre.f1cebde51a87be149b4970a3287826bb63c0ac0b
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +15 -0
- data/CHANGELOG.rdoc +381 -0
- data/Gemfile +3 -0
- data/LICENSE +20 -0
- data/README.rdoc +108 -0
- data/Rakefile +18 -0
- data/init.rb +1 -0
- data/lib/cancan.rb +13 -0
- data/lib/cancan/ability.rb +348 -0
- data/lib/cancan/controller_additions.rb +392 -0
- data/lib/cancan/controller_resource.rb +265 -0
- data/lib/cancan/exceptions.rb +53 -0
- data/lib/cancan/inherited_resource.rb +20 -0
- data/lib/cancan/matchers.rb +14 -0
- data/lib/cancan/model_adapters/abstract_adapter.rb +56 -0
- data/lib/cancan/model_adapters/active_record_adapter.rb +172 -0
- data/lib/cancan/model_adapters/data_mapper_adapter.rb +34 -0
- data/lib/cancan/model_adapters/default_adapter.rb +7 -0
- data/lib/cancan/model_adapters/mongoid_adapter.rb +54 -0
- data/lib/cancan/model_additions.rb +29 -0
- data/lib/cancan/rule.rb +178 -0
- data/lib/generators/cancan/ability/USAGE +5 -0
- data/lib/generators/cancan/ability/ability_generator.rb +16 -0
- data/lib/generators/cancan/ability/templates/ability.rb +24 -0
- data/lib/generators/cancan/ability/templates/ability_spec.rb +16 -0
- data/lib/generators/cancan/ability/templates/ability_test.rb +10 -0
- data/spec/README.rdoc +28 -0
- data/spec/cancan/ability_spec.rb +541 -0
- data/spec/cancan/controller_additions_spec.rb +118 -0
- data/spec/cancan/controller_resource_spec.rb +535 -0
- data/spec/cancan/exceptions_spec.rb +58 -0
- data/spec/cancan/inherited_resource_spec.rb +58 -0
- data/spec/cancan/matchers_spec.rb +33 -0
- data/spec/cancan/model_adapters/active_record_adapter_spec.rb +278 -0
- data/spec/cancan/model_adapters/data_mapper_adapter_spec.rb +120 -0
- data/spec/cancan/model_adapters/default_adapter_spec.rb +7 -0
- data/spec/cancan/model_adapters/mongoid_adapter_spec.rb +227 -0
- data/spec/cancan/rule_spec.rb +55 -0
- data/spec/matchers.rb +13 -0
- data/spec/spec_helper.rb +49 -0
- metadata +197 -0
@@ -0,0 +1,58 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe CanCan::Unauthorized do
|
4
|
+
describe "with action and subject" do
|
5
|
+
before(:each) do
|
6
|
+
@exception = CanCan::Unauthorized.new(nil, :some_action, :some_subject)
|
7
|
+
end
|
8
|
+
|
9
|
+
it "has action and subject accessors" do
|
10
|
+
@exception.action.should == :some_action
|
11
|
+
@exception.subject.should == :some_subject
|
12
|
+
end
|
13
|
+
|
14
|
+
it "has a changable default message" do
|
15
|
+
@exception.message.should == "You are not authorized to access this page."
|
16
|
+
@exception.default_message = "Unauthorized!"
|
17
|
+
@exception.message.should == "Unauthorized!"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "with only a message" do
|
22
|
+
before(:each) do
|
23
|
+
@exception = CanCan::Unauthorized.new("Access denied!")
|
24
|
+
end
|
25
|
+
|
26
|
+
it "has nil action and subject" do
|
27
|
+
@exception.action.should be_nil
|
28
|
+
@exception.subject.should be_nil
|
29
|
+
end
|
30
|
+
|
31
|
+
it "has passed message" do
|
32
|
+
@exception.message.should == "Access denied!"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "i18n in the default message" do
|
37
|
+
after(:each) do
|
38
|
+
I18n.backend = nil
|
39
|
+
end
|
40
|
+
|
41
|
+
it "uses i18n for the default message" do
|
42
|
+
I18n.backend.store_translations :en, :unauthorized => {:default => "This is a different message"}
|
43
|
+
@exception = CanCan::Unauthorized.new
|
44
|
+
@exception.message.should == "This is a different message"
|
45
|
+
end
|
46
|
+
|
47
|
+
it "defaults to a nice message" do
|
48
|
+
@exception = CanCan::Unauthorized.new
|
49
|
+
@exception.message.should == "You are not authorized to access this page."
|
50
|
+
end
|
51
|
+
|
52
|
+
it "does not use translation if a message is given" do
|
53
|
+
@exception = CanCan::Unauthorized.new("Hey! You're not welcome here")
|
54
|
+
@exception.message.should == "Hey! You're not welcome here"
|
55
|
+
@exception.message.should_not == "You are not authorized to access this page."
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe CanCan::InheritedResource do
|
4
|
+
before(:each) do
|
5
|
+
@params = HashWithIndifferentAccess.new(:controller => "projects")
|
6
|
+
@controller_class = Class.new
|
7
|
+
@controller = @controller_class.new
|
8
|
+
@ability = Ability.new(nil)
|
9
|
+
@controller.stub(:params) { @params }
|
10
|
+
@controller.stub(:current_ability) { @ability }
|
11
|
+
# @controller_class.stub(:cancan_skipper) { {:authorize => {}, :load => {}} }
|
12
|
+
end
|
13
|
+
|
14
|
+
it "show should load resource through @controller.resource" do
|
15
|
+
@params.merge!(:action => "show", :id => 123)
|
16
|
+
@controller.stub(:resource) { :project_resource }
|
17
|
+
CanCan::InheritedResource.new(@controller, :load => true).process
|
18
|
+
@controller.instance_variable_get(:@project).should == :project_resource
|
19
|
+
end
|
20
|
+
|
21
|
+
it "new should load through @controller.build_resource" do
|
22
|
+
@params[:action] = "new"
|
23
|
+
@controller.stub(:build_resource) { :project_resource }
|
24
|
+
CanCan::InheritedResource.new(@controller, :load => true).process
|
25
|
+
@controller.instance_variable_get(:@project).should == :project_resource
|
26
|
+
end
|
27
|
+
|
28
|
+
it "index should load through @controller.association_chain when parent" do
|
29
|
+
@params[:action] = "index"
|
30
|
+
@controller.stub(:association_chain) { @controller.instance_variable_set(:@project, :project_resource) }
|
31
|
+
CanCan::InheritedResource.new(@controller, :load => true, :parent => true).process
|
32
|
+
@controller.instance_variable_get(:@project).should == :project_resource
|
33
|
+
end
|
34
|
+
|
35
|
+
it "index should load through @controller.end_of_association_chain" do
|
36
|
+
@params[:action] = "index"
|
37
|
+
Project.stub(:accessible_by).with(@ability, :index) { :projects }
|
38
|
+
@controller.stub(:end_of_association_chain) { Project }
|
39
|
+
CanCan::InheritedResource.new(@controller, :load => true).process
|
40
|
+
@controller.instance_variable_get(:@projects).should == :projects
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should build a new resource with attributes from current ability" do
|
44
|
+
@params[:action] = "new"
|
45
|
+
@ability.can(:create, :projects, :name => "from conditions")
|
46
|
+
@controller.stub(:build_resource) { Struct.new(:name).new }
|
47
|
+
CanCan::InheritedResource.new(@controller, :load => true).process
|
48
|
+
@controller.instance_variable_get(:@project).name.should == "from conditions"
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should override initial attributes with params" do
|
52
|
+
@params.merge!(:action => "new", :project => {:name => "from params"})
|
53
|
+
@ability.can(:create, :projects, :name => "from conditions")
|
54
|
+
@controller.stub(:build_resource) { Struct.new(:name).new }
|
55
|
+
CanCan::ControllerResource.new(@controller, :load => true).process
|
56
|
+
@controller.instance_variable_get(:@project).name.should == "from params"
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe "be_able_to" do
|
4
|
+
it "delegates to can?" do
|
5
|
+
object = Object.new
|
6
|
+
object.should_receive(:can?).with(:read, 123) { true }
|
7
|
+
object.should be_able_to(:read, 123)
|
8
|
+
end
|
9
|
+
|
10
|
+
it "reports a nice failure message for should" do
|
11
|
+
object = Object.new
|
12
|
+
object.should_receive(:can?).with(:read, 123) { false }
|
13
|
+
expect do
|
14
|
+
object.should be_able_to(:read, 123)
|
15
|
+
end.should raise_error('expected to be able to :read 123')
|
16
|
+
end
|
17
|
+
|
18
|
+
it "reports a nice failure message for should not" do
|
19
|
+
object = Object.new
|
20
|
+
object.should_receive(:can?).with(:read, 123) { true }
|
21
|
+
expect do
|
22
|
+
object.should_not be_able_to(:read, 123)
|
23
|
+
end.should raise_error('expected not to be able to :read 123')
|
24
|
+
end
|
25
|
+
|
26
|
+
it "delegates additional arguments to can? and reports in failure message" do
|
27
|
+
object = Object.new
|
28
|
+
object.should_receive(:can?).with(:read, 123, 456) { false }
|
29
|
+
expect do
|
30
|
+
object.should be_able_to(:read, 123, 456)
|
31
|
+
end.should raise_error('expected to be able to :read 123 456')
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,278 @@
|
|
1
|
+
if ENV["MODEL_ADAPTER"].nil? || ENV["MODEL_ADAPTER"] == "active_record"
|
2
|
+
require "spec_helper"
|
3
|
+
|
4
|
+
class Category
|
5
|
+
has_many :articles
|
6
|
+
end
|
7
|
+
|
8
|
+
class Article < ActiveRecord::Base
|
9
|
+
connection.create_table(table_name) do |t|
|
10
|
+
t.integer :category_id
|
11
|
+
t.string :name
|
12
|
+
t.boolean :published
|
13
|
+
t.boolean :secret
|
14
|
+
t.integer :priority
|
15
|
+
end
|
16
|
+
belongs_to :category
|
17
|
+
has_many :comments
|
18
|
+
end
|
19
|
+
|
20
|
+
class Comment < ActiveRecord::Base
|
21
|
+
connection.create_table(table_name) do |t|
|
22
|
+
t.integer :article_id
|
23
|
+
t.boolean :spam
|
24
|
+
end
|
25
|
+
belongs_to :article
|
26
|
+
end
|
27
|
+
|
28
|
+
describe CanCan::ModelAdapters::ActiveRecordAdapter do
|
29
|
+
before(:each) do
|
30
|
+
Article.delete_all
|
31
|
+
Comment.delete_all
|
32
|
+
@ability = Object.new
|
33
|
+
@ability.extend(CanCan::Ability)
|
34
|
+
@article_table = Article.table_name
|
35
|
+
@comment_table = Comment.table_name
|
36
|
+
end
|
37
|
+
|
38
|
+
it "is for only active record classes" do
|
39
|
+
CanCan::ModelAdapters::ActiveRecordAdapter.should_not be_for_class(Object)
|
40
|
+
CanCan::ModelAdapters::ActiveRecordAdapter.should be_for_class(Article)
|
41
|
+
CanCan::ModelAdapters::AbstractAdapter.adapter_class(Article).should == CanCan::ModelAdapters::ActiveRecordAdapter
|
42
|
+
end
|
43
|
+
|
44
|
+
it "finds record" do
|
45
|
+
article = Article.create!
|
46
|
+
CanCan::ModelAdapters::ActiveRecordAdapter.find(Article, article.id).should == article
|
47
|
+
end
|
48
|
+
|
49
|
+
it "does not fetch any records when no abilities are defined" do
|
50
|
+
Article.create!
|
51
|
+
Article.accessible_by(@ability).should be_empty
|
52
|
+
end
|
53
|
+
|
54
|
+
it "fetches all articles when one can read all" do
|
55
|
+
@ability.can :read, :articles
|
56
|
+
article = Article.create!
|
57
|
+
Article.accessible_by(@ability).should == [article]
|
58
|
+
end
|
59
|
+
|
60
|
+
it "fetches only the articles that are published" do
|
61
|
+
@ability.can :read, :articles, :published => true
|
62
|
+
article1 = Article.create!(:published => true)
|
63
|
+
article2 = Article.create!(:published => false)
|
64
|
+
Article.accessible_by(@ability).should == [article1]
|
65
|
+
end
|
66
|
+
|
67
|
+
it "fetches any articles which are published or secret" do
|
68
|
+
@ability.can :read, :articles, :published => true
|
69
|
+
@ability.can :read, :articles, :secret => true
|
70
|
+
article1 = Article.create!(:published => true, :secret => false)
|
71
|
+
article2 = Article.create!(:published => true, :secret => true)
|
72
|
+
article3 = Article.create!(:published => false, :secret => true)
|
73
|
+
article4 = Article.create!(:published => false, :secret => false)
|
74
|
+
Article.accessible_by(@ability).should == [article1, article2, article3]
|
75
|
+
end
|
76
|
+
|
77
|
+
it "fetches only the articles that are published and not secret" do
|
78
|
+
@ability.can :read, :articles, :published => true
|
79
|
+
@ability.cannot :read, :articles, :secret => true
|
80
|
+
article1 = Article.create!(:published => true, :secret => false)
|
81
|
+
article2 = Article.create!(:published => true, :secret => true)
|
82
|
+
article3 = Article.create!(:published => false, :secret => true)
|
83
|
+
article4 = Article.create!(:published => false, :secret => false)
|
84
|
+
Article.accessible_by(@ability).should == [article1]
|
85
|
+
end
|
86
|
+
|
87
|
+
it "only reads comments for articles which are published" do
|
88
|
+
@ability.can :read, :comments, :article => { :published => true }
|
89
|
+
comment1 = Comment.create!(:article => Article.create!(:published => true))
|
90
|
+
comment2 = Comment.create!(:article => Article.create!(:published => false))
|
91
|
+
Comment.accessible_by(@ability).should == [comment1]
|
92
|
+
end
|
93
|
+
|
94
|
+
it "only reads comments for visible categories through articles" do
|
95
|
+
pending "does ActiveRecord no longer support a deep nested hash of conditions?"
|
96
|
+
@ability.can :read, :comments, :article => { :category => { :visible => true } }
|
97
|
+
comment1 = Comment.create!(:article => Article.create!(:category => Category.create!(:visible => true)))
|
98
|
+
comment2 = Comment.create!(:article => Article.create!(:category => Category.create!(:visible => false)))
|
99
|
+
Comment.accessible_by(@ability).should == [comment1]
|
100
|
+
end
|
101
|
+
|
102
|
+
it "allows conditions in SQL and merge with hash conditions" do
|
103
|
+
@ability.can :read, :articles, :published => true
|
104
|
+
@ability.can :read, :articles, ["secret=?", true]
|
105
|
+
article1 = Article.create!(:published => true, :secret => false)
|
106
|
+
article2 = Article.create!(:published => true, :secret => true)
|
107
|
+
article3 = Article.create!(:published => false, :secret => true)
|
108
|
+
article4 = Article.create!(:published => false, :secret => false)
|
109
|
+
Article.accessible_by(@ability).should == [article1, article2, article3]
|
110
|
+
end
|
111
|
+
|
112
|
+
it "allows a scope for conditions" do
|
113
|
+
@ability.can :read, :articles, Article.where(:secret => true)
|
114
|
+
article1 = Article.create!(:secret => true)
|
115
|
+
article2 = Article.create!(:secret => false)
|
116
|
+
Article.accessible_by(@ability).should == [article1]
|
117
|
+
end
|
118
|
+
|
119
|
+
it "fetches only associated records when using with a scope for conditions" do
|
120
|
+
@ability.can :read, :articles, Article.where(:secret => true)
|
121
|
+
category1 = Category.create!(:visible => false)
|
122
|
+
category2 = Category.create!(:visible => true)
|
123
|
+
article1 = Article.create!(:secret => true, :category => category1)
|
124
|
+
article2 = Article.create!(:secret => true, :category => category2)
|
125
|
+
# for some reason the objects aren't comparing equally here so it's necessary to compare by id
|
126
|
+
category1.articles.accessible_by(@ability).map(&:id).should == [article1.id]
|
127
|
+
end
|
128
|
+
|
129
|
+
it "raises an exception when trying to merge scope with other conditions" do
|
130
|
+
@ability.can :read, :articles, :published => true
|
131
|
+
@ability.can :read, :articles, Article.where(:secret => true)
|
132
|
+
lambda { Article.accessible_by(@ability) }.should raise_error(CanCan::Error, "Unable to merge an Active Record scope with other conditions. Instead use a hash or SQL for read articles ability.")
|
133
|
+
end
|
134
|
+
|
135
|
+
it "does not allow to fetch records when ability with just block present" do
|
136
|
+
@ability.can :read, :articles do
|
137
|
+
false
|
138
|
+
end
|
139
|
+
lambda { Article.accessible_by(@ability) }.should raise_error(CanCan::Error)
|
140
|
+
end
|
141
|
+
|
142
|
+
it "does not allow to check ability on object against SQL conditions without block" do
|
143
|
+
@ability.can :read, :articles, ["secret=?", true]
|
144
|
+
lambda { @ability.can? :read, Article.new }.should raise_error(CanCan::Error)
|
145
|
+
end
|
146
|
+
|
147
|
+
it "has false conditions if no abilities match" do
|
148
|
+
@ability.model_adapter(Article, :read).conditions.should == "'t'='f'"
|
149
|
+
end
|
150
|
+
|
151
|
+
it "returns false conditions for cannot clause" do
|
152
|
+
@ability.cannot :read, :articles
|
153
|
+
@ability.model_adapter(Article, :read).conditions.should == "'t'='f'"
|
154
|
+
end
|
155
|
+
|
156
|
+
it "returns SQL for single `can` definition in front of default `cannot` condition" do
|
157
|
+
@ability.cannot :read, :articles
|
158
|
+
@ability.can :read, :articles, :published => false, :secret => true
|
159
|
+
@ability.model_adapter(Article, :read).conditions.should orderlessly_match(%Q["#{@article_table}"."published" = 'f' AND "#{@article_table}"."secret" = 't'])
|
160
|
+
end
|
161
|
+
|
162
|
+
it "returns true condition for single `can` definition in front of default `can` condition" do
|
163
|
+
@ability.can :read, :articles
|
164
|
+
@ability.can :read, :articles, :published => false, :secret => true
|
165
|
+
@ability.model_adapter(Article, :read).conditions.should eq(:secret => true, :published => false)
|
166
|
+
end
|
167
|
+
|
168
|
+
it "returns `false condition` for single `cannot` definition in front of default `cannot` condition" do
|
169
|
+
@ability.cannot :read, :articles
|
170
|
+
@ability.cannot :read, :articles, :published => false, :secret => true
|
171
|
+
@ability.model_adapter(Article, :read).conditions.should == "'t'='f'"
|
172
|
+
end
|
173
|
+
|
174
|
+
it "returns `not (sql)` for single `cannot` definition in front of default `can` condition" do
|
175
|
+
@ability.can :read, :articles
|
176
|
+
@ability.cannot :read, :articles, :published => false, :secret => true
|
177
|
+
@ability.model_adapter(Article, :read).conditions.should orderlessly_match(%Q["not (#{@article_table}"."published" = 'f' AND "#{@article_table}"."secret" = 't')])
|
178
|
+
end
|
179
|
+
|
180
|
+
it "returns appropriate sql conditions in complex case" do
|
181
|
+
@ability.can :read, :articles
|
182
|
+
@ability.can :access, :articles, :id => 1
|
183
|
+
@ability.can :update, :articles, :published => true
|
184
|
+
@ability.cannot :update, :articles, :secret => true
|
185
|
+
@ability.model_adapter(Article, :update).conditions.should == %Q[not ("#{@article_table}"."secret" = 't') AND (("#{@article_table}"."published" = 't') OR ("#{@article_table}"."id" = 1))]
|
186
|
+
@ability.model_adapter(Article, :access).conditions.should == {:id => 1}
|
187
|
+
@ability.model_adapter(Article, :read).conditions.should == {:id => 1} # used to be "t=t" but changed with new specificity rule (issue #321)
|
188
|
+
end
|
189
|
+
|
190
|
+
it "does not forget conditions when calling with SQL string" do
|
191
|
+
@ability.can :read, :articles, :published => true
|
192
|
+
@ability.can :read, :articles, ['secret=?', false]
|
193
|
+
adapter = @ability.model_adapter(Article, :read)
|
194
|
+
2.times do
|
195
|
+
adapter.conditions.should == %Q[(secret='f') OR ("#{@article_table}"."published" = 't')]
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
it "has nil joins if no rules" do
|
200
|
+
@ability.model_adapter(Article, :read).joins.should be_nil
|
201
|
+
end
|
202
|
+
|
203
|
+
it "has nil joins if no nested hashes specified in conditions" do
|
204
|
+
@ability.can :read, :articles, :published => false
|
205
|
+
@ability.can :read, :articles, :secret => true
|
206
|
+
@ability.model_adapter(Article, :read).joins.should be_nil
|
207
|
+
end
|
208
|
+
|
209
|
+
it "merges separate joins into a single array" do
|
210
|
+
@ability.can :read, :articles, :project => { :blocked => false }
|
211
|
+
@ability.can :read, :articles, :company => { :admin => true }
|
212
|
+
@ability.model_adapter(Article, :read).joins.inspect.should orderlessly_match([:company, :project].inspect)
|
213
|
+
end
|
214
|
+
|
215
|
+
it "merges same joins into a single array" do
|
216
|
+
@ability.can :read, :articles, :project => { :blocked => false }
|
217
|
+
@ability.can :read, :articles, :project => { :admin => true }
|
218
|
+
@ability.model_adapter(Article, :read).joins.should == [:project]
|
219
|
+
end
|
220
|
+
|
221
|
+
it "restricts articles given a MetaWhere condition" do
|
222
|
+
pending
|
223
|
+
@ability.can :read, :articles, :priority.lt => 2
|
224
|
+
article1 = Article.create!(:priority => 1)
|
225
|
+
article2 = Article.create!(:priority => 3)
|
226
|
+
Article.accessible_by(@ability).should == [article1]
|
227
|
+
@ability.should be_able_to(:read, article1)
|
228
|
+
@ability.should_not be_able_to(:read, article2)
|
229
|
+
end
|
230
|
+
|
231
|
+
it "should merge MetaWhere and non-MetaWhere conditions" do
|
232
|
+
pending
|
233
|
+
@ability.can :read, Article, :priority.lt => 2
|
234
|
+
@ability.can :read, Article, :priority => 1
|
235
|
+
article1 = Article.create!(:priority => 1)
|
236
|
+
article2 = Article.create!(:priority => 3)
|
237
|
+
Article.accessible_by(@ability).should == [article1]
|
238
|
+
@ability.should be_able_to(:read, article1)
|
239
|
+
@ability.should_not be_able_to(:read, article2)
|
240
|
+
end
|
241
|
+
|
242
|
+
it "matches any MetaWhere condition" do
|
243
|
+
pending
|
244
|
+
adapter = CanCan::ModelAdapters::ActiveRecordAdapter
|
245
|
+
article1 = Article.new(:priority => 1, :name => "Hello World")
|
246
|
+
adapter.matches_condition?(article1, :priority.eq, 1).should be_true
|
247
|
+
adapter.matches_condition?(article1, :priority.eq, 2).should be_false
|
248
|
+
adapter.matches_condition?(article1, :priority.eq_any, [1, 2]).should be_true
|
249
|
+
adapter.matches_condition?(article1, :priority.eq_any, [2, 3]).should be_false
|
250
|
+
adapter.matches_condition?(article1, :priority.eq_all, [1, 1]).should be_true
|
251
|
+
adapter.matches_condition?(article1, :priority.eq_all, [1, 2]).should be_false
|
252
|
+
adapter.matches_condition?(article1, :priority.ne, 2).should be_true
|
253
|
+
adapter.matches_condition?(article1, :priority.ne, 1).should be_false
|
254
|
+
adapter.matches_condition?(article1, :priority.in, [1, 2]).should be_true
|
255
|
+
adapter.matches_condition?(article1, :priority.in, [2, 3]).should be_false
|
256
|
+
adapter.matches_condition?(article1, :priority.nin, [2, 3]).should be_true
|
257
|
+
adapter.matches_condition?(article1, :priority.nin, [1, 2]).should be_false
|
258
|
+
adapter.matches_condition?(article1, :priority.lt, 2).should be_true
|
259
|
+
adapter.matches_condition?(article1, :priority.lt, 1).should be_false
|
260
|
+
adapter.matches_condition?(article1, :priority.lteq, 1).should be_true
|
261
|
+
adapter.matches_condition?(article1, :priority.lteq, 0).should be_false
|
262
|
+
adapter.matches_condition?(article1, :priority.gt, 0).should be_true
|
263
|
+
adapter.matches_condition?(article1, :priority.gt, 1).should be_false
|
264
|
+
adapter.matches_condition?(article1, :priority.gteq, 1).should be_true
|
265
|
+
adapter.matches_condition?(article1, :priority.gteq, 2).should be_false
|
266
|
+
adapter.matches_condition?(article1, :name.like, "%ello worl%").should be_true
|
267
|
+
adapter.matches_condition?(article1, :name.like, "hello world").should be_true
|
268
|
+
adapter.matches_condition?(article1, :name.like, "hello%").should be_true
|
269
|
+
adapter.matches_condition?(article1, :name.like, "h%d").should be_true
|
270
|
+
adapter.matches_condition?(article1, :name.like, "%helo%").should be_false
|
271
|
+
adapter.matches_condition?(article1, :name.like, "hello").should be_false
|
272
|
+
adapter.matches_condition?(article1, :name.like, "hello.world").should be_false
|
273
|
+
# For some reason this is reporting "The not_matches MetaWhere condition is not supported."
|
274
|
+
# adapter.matches_condition?(article1, :name.nlike, "%helo%").should be_true
|
275
|
+
# adapter.matches_condition?(article1, :name.nlike, "%ello worl%").should be_false
|
276
|
+
end
|
277
|
+
end
|
278
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
if ENV["MODEL_ADAPTER"] == "data_mapper"
|
2
|
+
require "spec_helper"
|
3
|
+
|
4
|
+
DataMapper.setup(:default, 'sqlite::memory:')
|
5
|
+
|
6
|
+
class DataMapperArticle
|
7
|
+
include DataMapper::Resource
|
8
|
+
property :id, Serial
|
9
|
+
property :published, Boolean, :default => false
|
10
|
+
property :secret, Boolean, :default => false
|
11
|
+
property :priority, Integer
|
12
|
+
has n, :data_mapper_comments
|
13
|
+
end
|
14
|
+
|
15
|
+
class DataMapperComment
|
16
|
+
include DataMapper::Resource
|
17
|
+
property :id, Serial
|
18
|
+
property :spam, Boolean, :default => false
|
19
|
+
belongs_to :data_mapper_article
|
20
|
+
end
|
21
|
+
|
22
|
+
DataMapper.finalize
|
23
|
+
DataMapper.auto_migrate!
|
24
|
+
|
25
|
+
describe CanCan::ModelAdapters::DataMapperAdapter do
|
26
|
+
before(:each) do
|
27
|
+
DataMapperArticle.destroy
|
28
|
+
DataMapperComment.destroy
|
29
|
+
@ability = Object.new
|
30
|
+
@ability.extend(CanCan::Ability)
|
31
|
+
end
|
32
|
+
|
33
|
+
it "is for only data mapper classes" do
|
34
|
+
CanCan::ModelAdapters::DataMapperAdapter.should_not be_for_class(Object)
|
35
|
+
CanCan::ModelAdapters::DataMapperAdapter.should be_for_class(DataMapperArticle)
|
36
|
+
CanCan::ModelAdapters::AbstractAdapter.adapter_class(DataMapperArticle).should == CanCan::ModelAdapters::DataMapperAdapter
|
37
|
+
end
|
38
|
+
|
39
|
+
it "finds record" do
|
40
|
+
article = DataMapperArticle.create
|
41
|
+
CanCan::ModelAdapters::DataMapperAdapter.find(DataMapperArticle, article.id).should == article
|
42
|
+
end
|
43
|
+
|
44
|
+
it "does not fetch any records when no abilities are defined" do
|
45
|
+
DataMapperArticle.create
|
46
|
+
DataMapperArticle.accessible_by(@ability).should be_empty
|
47
|
+
end
|
48
|
+
|
49
|
+
it "fetches all articles when one can read all" do
|
50
|
+
@ability.can :read, :data_mapper_articles
|
51
|
+
article = DataMapperArticle.create
|
52
|
+
DataMapperArticle.accessible_by(@ability).should == [article]
|
53
|
+
end
|
54
|
+
|
55
|
+
it "fetches only the articles that are published" do
|
56
|
+
@ability.can :read, :data_mapper_articles, :published => true
|
57
|
+
article1 = DataMapperArticle.create(:published => true)
|
58
|
+
article2 = DataMapperArticle.create(:published => false)
|
59
|
+
DataMapperArticle.accessible_by(@ability).should == [article1]
|
60
|
+
end
|
61
|
+
|
62
|
+
it "fetches any articles which are published or secret" do
|
63
|
+
@ability.can :read, :data_mapper_articles, :published => true
|
64
|
+
@ability.can :read, :data_mapper_articles, :secret => true
|
65
|
+
article1 = DataMapperArticle.create(:published => true, :secret => false)
|
66
|
+
article2 = DataMapperArticle.create(:published => true, :secret => true)
|
67
|
+
article3 = DataMapperArticle.create(:published => false, :secret => true)
|
68
|
+
article4 = DataMapperArticle.create(:published => false, :secret => false)
|
69
|
+
DataMapperArticle.accessible_by(@ability).should == [article1, article2, article3]
|
70
|
+
end
|
71
|
+
|
72
|
+
it "fetches only the articles that are published and not secret" do
|
73
|
+
pending "the `cannot` may require some custom SQL, maybe abstract out from Active Record adapter"
|
74
|
+
@ability.can :read, :data_mapper_articles, :published => true
|
75
|
+
@ability.cannot :read, :data_mapper_articles, :secret => true
|
76
|
+
article1 = DataMapperArticle.create(:published => true, :secret => false)
|
77
|
+
article2 = DataMapperArticle.create(:published => true, :secret => true)
|
78
|
+
article3 = DataMapperArticle.create(:published => false, :secret => true)
|
79
|
+
article4 = DataMapperArticle.create(:published => false, :secret => false)
|
80
|
+
DataMapperArticle.accessible_by(@ability).should == [article1]
|
81
|
+
end
|
82
|
+
|
83
|
+
it "only reads comments for articles which are published" do
|
84
|
+
@ability.can :read, :data_mapper_comments, :data_mapper_article => { :published => true }
|
85
|
+
comment1 = DataMapperComment.create(:data_mapper_article => DataMapperArticle.create!(:published => true))
|
86
|
+
comment2 = DataMapperComment.create(:data_mapper_article => DataMapperArticle.create!(:published => false))
|
87
|
+
DataMapperComment.accessible_by(@ability).should == [comment1]
|
88
|
+
end
|
89
|
+
|
90
|
+
it "allows conditions in SQL and merge with hash conditions" do
|
91
|
+
@ability.can :read, :data_mapper_articles, :published => true
|
92
|
+
@ability.can :read, :data_mapper_articles, ["secret=?", true]
|
93
|
+
article1 = DataMapperArticle.create(:published => true, :secret => false)
|
94
|
+
article4 = DataMapperArticle.create(:published => false, :secret => false)
|
95
|
+
DataMapperArticle.accessible_by(@ability).should == [article1]
|
96
|
+
end
|
97
|
+
|
98
|
+
it "matches gt comparison" do
|
99
|
+
@ability.can :read, :data_mapper_articles, :priority.gt => 3
|
100
|
+
article1 = DataMapperArticle.create(:priority => 4)
|
101
|
+
article2 = DataMapperArticle.create(:priority => 3)
|
102
|
+
DataMapperArticle.accessible_by(@ability).should == [article1]
|
103
|
+
@ability.should be_able_to(:read, article1)
|
104
|
+
@ability.should_not be_able_to(:read, article2)
|
105
|
+
end
|
106
|
+
|
107
|
+
it "matches gte comparison" do
|
108
|
+
@ability.can :read, :data_mapper_articles, :priority.gte => 3
|
109
|
+
article1 = DataMapperArticle.create(:priority => 4)
|
110
|
+
article2 = DataMapperArticle.create(:priority => 3)
|
111
|
+
article3 = DataMapperArticle.create(:priority => 2)
|
112
|
+
DataMapperArticle.accessible_by(@ability).should == [article1, article2]
|
113
|
+
@ability.should be_able_to(:read, article1)
|
114
|
+
@ability.should be_able_to(:read, article2)
|
115
|
+
@ability.should_not be_able_to(:read, article3)
|
116
|
+
end
|
117
|
+
|
118
|
+
# TODO: add more comparison specs
|
119
|
+
end
|
120
|
+
end
|