consul 0.8.0 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of consul might be problematic. Click here for more details.
- data/README.md +170 -45
- data/Rakefile +1 -1
- data/lib/consul/controller.rb +1 -1
- data/lib/consul/power.rb +45 -49
- data/lib/consul/power/dynamic_access.rb +40 -4
- data/lib/consul/version.rb +1 -1
- data/spec/rails-2.3/Gemfile.lock +1 -1
- data/spec/rails-3.0/Gemfile.lock +1 -1
- data/spec/rails-3.2/Gemfile.lock +1 -1
- data/spec/shared/app_root/app/models/power.rb +20 -0
- data/spec/shared/consul/power_spec.rb +63 -26
- metadata +4 -4
data/README.md
CHANGED
@@ -1,19 +1,25 @@
|
|
1
|
-
Consul - A
|
2
|
-
|
1
|
+
Consul - A next gen authorization solution
|
2
|
+
==========================================
|
3
3
|
|
4
4
|
[![Build Status](https://secure.travis-ci.org/makandra/consul.png?branch=master)](https://travis-ci.org/makandra/consul)
|
5
5
|
|
6
|
-
Consul is a authorization solution for Ruby on Rails
|
6
|
+
Consul is a authorization solution for Ruby on Rails where you describe *sets of accessible things* to control what a user can see or edit.
|
7
7
|
|
8
8
|
We have used Consul in combination with [assignable_values](https://github.com/makandra/assignable_values) to solve a variety of authorization requirements ranging from boring to bizarre.
|
9
|
-
|
10
9
|
Also see our crash course video: [Solving bizare authorization requirements with Rails](http://bizarre-authorization.talks.makandra.com/).
|
11
10
|
|
12
11
|
|
13
|
-
Describing
|
14
|
-
|
12
|
+
Describing access to your application
|
13
|
+
-------------------------------------
|
14
|
+
|
15
|
+
You describe access to your application by putting a `Power` model into `app/models/power.rb`.
|
16
|
+
Inside your `Power` you can talk about what is accessible for the current user, e.g.
|
17
|
+
|
18
|
+
- [A scope of records a user may see](#scope-powers-relations)
|
19
|
+
- [Whether the user is allowed to use a particular screen](#boolean-powers)
|
20
|
+
- [A list of values a user may assign to a particular attribute](#validating-assignable-values)
|
15
21
|
|
16
|
-
|
22
|
+
A `Power` might look like this:
|
17
23
|
|
18
24
|
class Power
|
19
25
|
include Consul::Power
|
@@ -22,46 +28,101 @@ You describe access to your application by putting a `Power` model into `app/mod
|
|
22
28
|
@user = user
|
23
29
|
end
|
24
30
|
|
25
|
-
power :notes do
|
26
|
-
Note.by_author(@user)
|
27
|
-
end
|
28
|
-
|
29
31
|
power :users do
|
30
32
|
User if @user.admin?
|
31
33
|
end
|
32
34
|
|
35
|
+
power :notes do
|
36
|
+
Note.by_author(@user)
|
37
|
+
end
|
38
|
+
|
33
39
|
power :dashboard do
|
34
40
|
true # not a scope, but a boolean power. This is useful to control access to stuff that doesn't live in the database.
|
35
41
|
end
|
36
42
|
|
37
43
|
end
|
38
44
|
|
39
|
-
There are no restrictions on the name or constructor arguments of your
|
45
|
+
There are no restrictions on the name or constructor arguments of your this class.
|
46
|
+
|
47
|
+
You can deposit all kinds of objects in your power. See the sections below for details.
|
40
48
|
|
41
49
|
|
42
|
-
|
43
|
-
----------------
|
50
|
+
### Scope powers (relations)
|
44
51
|
|
45
|
-
Common things you might want from a power:
|
46
52
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
53
|
+
A typical use case in a Rails application is to restrict access to your ActiveRecord models. For example:
|
54
|
+
|
55
|
+
- Anonymous visitors may only see public posts
|
56
|
+
- Users may only see their own notes
|
57
|
+
- Only admins may edit users
|
58
|
+
|
59
|
+
You do this by making your powers return an ActiveRecord scope (or "relation"):
|
60
|
+
|
61
|
+
class Power
|
62
|
+
...
|
63
|
+
|
64
|
+
power :notes do
|
65
|
+
Note.by_author(@user)
|
66
|
+
end
|
67
|
+
|
68
|
+
power :users do
|
69
|
+
User if @user.admin?
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
52
73
|
|
53
|
-
|
74
|
+
You can now query these powers in order to retrieve the scope:
|
54
75
|
|
55
76
|
power = Power.new(user)
|
56
|
-
power.notes
|
57
|
-
|
58
|
-
|
77
|
+
power.notes # => returns an ActiveRecord::Scope
|
78
|
+
|
79
|
+
Or you can ask if the power is given (meaning it's not `nil`):
|
80
|
+
|
81
|
+
power.notes? # => returns true if Power#notes returns a scope and not nil
|
82
|
+
|
83
|
+
Or you can raise an error unless a power its given, e.g. to guard access into a controller action:
|
84
|
+
|
85
|
+
power.notes? # => returns true if Power#notes returns a scope, even if it's empty
|
86
|
+
|
87
|
+
Or you ask whether a given record is included in its scope (can be [optimized](#optimizing-record-checks-for-scope-powers)):
|
88
|
+
|
59
89
|
power.note?(Note.last) # => returns whether the given Note is in the Power#notes scope. Caches the result for subsequent queries.
|
90
|
+
|
91
|
+
Or you can raise an error unless a given record is included in its scope:
|
92
|
+
|
60
93
|
power.note!(Note.last) # => raises Consul::Powerless unless the given Note is in the Power#notes scope
|
61
94
|
|
95
|
+
See our crash course video [Solving bizare authorization requirements with Rails](http://bizarre-authorization.talks.makandra.com/) for many different use cases you can cover with this pattern.
|
96
|
+
|
62
97
|
|
63
|
-
|
64
|
-
|
98
|
+
|
99
|
+
### Defining different powers for different actions
|
100
|
+
|
101
|
+
If you have different access rights for e.g. viewing or updating posts, simply use different powers:
|
102
|
+
|
103
|
+
|
104
|
+
class Power
|
105
|
+
...
|
106
|
+
|
107
|
+
power :notes do
|
108
|
+
Note.published
|
109
|
+
end
|
110
|
+
|
111
|
+
power :updatable_notes do
|
112
|
+
Note.by_author(@user)
|
113
|
+
end
|
114
|
+
|
115
|
+
power :destroyable_notes do
|
116
|
+
Note if @user.admin?
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
|
121
|
+
There is also a [shortcut to map different powers to RESTful controller actions](#protect-entry-into-controller-actions).
|
122
|
+
|
123
|
+
|
124
|
+
|
125
|
+
### Boolean powers
|
65
126
|
|
66
127
|
Boolean powers are useful to control access to stuff that doesn't live in the database:
|
67
128
|
|
@@ -76,12 +137,12 @@ Boolean powers are useful to control access to stuff that doesn't live in the da
|
|
76
137
|
|
77
138
|
You can query it like the other powers:
|
78
139
|
|
140
|
+
power = Power.new(@user)
|
79
141
|
power.dashboard? # => true
|
80
142
|
power.dashboard! # => raises Consul::Powerless unless Power#dashboard? returns true
|
81
143
|
|
82
144
|
|
83
|
-
Powers that give no access at all
|
84
|
-
---------------------------------
|
145
|
+
### Powers that give no access at all
|
85
146
|
|
86
147
|
Note that there is a difference between having access to an empty list of records, and having no access at all.
|
87
148
|
If you want to express that a user has no access at all, make the respective power return `nil`.
|
@@ -99,15 +160,40 @@ Note how the power in the example below returns `nil` unless the user is an admi
|
|
99
160
|
|
100
161
|
When a non-admin queries the `:users` power, she will get the following behavior:
|
101
162
|
|
102
|
-
power
|
103
|
-
power.
|
104
|
-
power.
|
105
|
-
power.
|
106
|
-
power.
|
163
|
+
power = Power.new(@user)
|
164
|
+
power.users # => returns nil
|
165
|
+
power.users? # => returns false
|
166
|
+
power.users! # => raises Consul::Powerless
|
167
|
+
power.user?(User.last) # => returns false
|
168
|
+
power.user!(User.last) # => raises Consul::Powerless
|
169
|
+
|
170
|
+
|
171
|
+
|
172
|
+
### Powers that only check a given object
|
173
|
+
|
174
|
+
Sometimes it is not convenient to define powers as a collection. Sometimes you only want to store a method that
|
175
|
+
checks whether a given object is accessible.
|
107
176
|
|
177
|
+
To do so, simply define a power that ends in a question mark:
|
108
178
|
|
109
|
-
|
110
|
-
|
179
|
+
|
180
|
+
class Power
|
181
|
+
...
|
182
|
+
|
183
|
+
power :updatable_post? do |post|
|
184
|
+
post.author == @user
|
185
|
+
end
|
186
|
+
|
187
|
+
end
|
188
|
+
|
189
|
+
You can query such an power as always:
|
190
|
+
|
191
|
+
power = Power.new(@user)
|
192
|
+
power.updatable_post?(Post.last) # return true if the author of the post is @user
|
193
|
+
power.updatable_post!(Post.last) # raises Consul::Powerless unless the author of the post is @user
|
194
|
+
|
195
|
+
|
196
|
+
### Other types of powers
|
111
197
|
|
112
198
|
A power can return any type of object. For instance, you often want to return an array:
|
113
199
|
|
@@ -134,23 +220,21 @@ You can query it like any other power. E.g. if a non-admin queries this power sh
|
|
134
220
|
power.assignable_note_state!('published') # => raises Consul::Powerless
|
135
221
|
|
136
222
|
|
137
|
-
Defining multiple powers at once
|
138
|
-
--------------------------------
|
223
|
+
### Defining multiple powers at once
|
139
224
|
|
140
225
|
You can define multiple powers at once by giving multiple power names:
|
141
226
|
|
142
227
|
class Power
|
143
228
|
...
|
144
229
|
|
145
|
-
power :destroyable_users, updatable_users do
|
230
|
+
power :destroyable_users, :updatable_users do
|
146
231
|
User if admin?
|
147
232
|
end
|
148
233
|
|
149
234
|
end
|
150
235
|
|
151
236
|
|
152
|
-
Powers that require context (arguments)
|
153
|
-
---------------------------------------
|
237
|
+
### Powers that require context (arguments)
|
154
238
|
|
155
239
|
Sometimes it can be useful to define powers that require context. To do so, just take an argument in your `power` block:
|
156
240
|
|
@@ -164,11 +248,52 @@ Sometimes it can be useful to define powers that require context. To do so, just
|
|
164
248
|
%w[committed started finished]
|
165
249
|
end
|
166
250
|
end
|
251
|
+
|
252
|
+
end
|
167
253
|
|
168
254
|
When querying such a power, you always need to provide the context, e.g.:
|
169
255
|
|
170
256
|
story = ...
|
171
|
-
Power.
|
257
|
+
Power.current.assignable_story_state?(story, 'finished')
|
258
|
+
|
259
|
+
|
260
|
+
### Optimizing record checks for scope powers
|
261
|
+
|
262
|
+
You can query a scope power for a given record, e.g.
|
263
|
+
|
264
|
+
class Power
|
265
|
+
...
|
266
|
+
|
267
|
+
power :posts do |post|
|
268
|
+
Post.where(:author_id => @user.id)
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
power = Power.new(@user)
|
273
|
+
power.post?(Post.last)
|
274
|
+
|
275
|
+
What Consul does internally is fetch **all** the IDs of the `power.posts` scope and test if the given
|
276
|
+
record's ID is among them. This list of IDs is cached for subsequent calls, so you will only touch the database once.
|
277
|
+
|
278
|
+
As scary as it might sound, fetching all IDs of a scope scales quiet nicely for many thousand records. There will
|
279
|
+
however be the point where you want to optimize this.
|
280
|
+
|
281
|
+
What you can do in Consul is to define a second power that checks a given record in plain Ruby:
|
282
|
+
|
283
|
+
class Power
|
284
|
+
...
|
285
|
+
|
286
|
+
power :posts do |post|
|
287
|
+
Post.where(:author_id => @user.id)
|
288
|
+
end
|
289
|
+
|
290
|
+
power :post? do |post|
|
291
|
+
post.author_id == @user.id
|
292
|
+
end
|
293
|
+
|
294
|
+
end
|
295
|
+
|
296
|
+
This way you do not need to touch the database at all.
|
172
297
|
|
173
298
|
|
174
299
|
Role-based permissions
|
@@ -379,10 +504,10 @@ You can find a full list of available dynamic calls below:
|
|
379
504
|
| Dynamic call | Equivalent |
|
380
505
|
|---------------------------------------------------------|--------------------------------------------|
|
381
506
|
| `Power.current.send(:notes)` | `Power.current.notes` |
|
382
|
-
| `Power.current.
|
383
|
-
| `Power.current.
|
384
|
-
| `Power.current.
|
385
|
-
| `Power.current.
|
507
|
+
| `Power.current.include_power?(:notes)` | `Power.current.notes?` |
|
508
|
+
| `Power.current.include_power!(:notes)` | `Power.current.notes!` |
|
509
|
+
| `Power.current.include_object?(:notes, Note.last)` | `Power.current.note?(Note.last)` |
|
510
|
+
| `Power.current.include_object!(:notes, Note.last)` | `Power.current.note!(Note.last)` |
|
386
511
|
| `Power.current.for_record(Note.last)` | `Power.current.notes` |
|
387
512
|
| `Power.current.for_record(:updatable, Note.last)` | `Power.current.updatable_notes` |
|
388
513
|
| `Power.current.for_model(Note)` | `Power.current.notes` |
|
data/Rakefile
CHANGED
data/lib/consul/controller.rb
CHANGED
data/lib/consul/power.rb
CHANGED
@@ -7,48 +7,41 @@ module Consul
|
|
7
7
|
base.send :include, Memoizer
|
8
8
|
end
|
9
9
|
|
10
|
-
|
11
|
-
args = args.dup
|
10
|
+
private
|
12
11
|
|
13
|
-
|
12
|
+
def default_include_power?(power_name, *context)
|
13
|
+
# Everything that is not nil is considered as included
|
14
|
+
!!send(power_name, *context)
|
15
|
+
end
|
14
16
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
true
|
25
|
-
else
|
26
|
-
power_ids_name = self.class.power_ids_name(name)
|
27
|
-
send(power_ids_name, *context).include?(record.id)
|
28
|
-
end
|
29
|
-
elsif Util.collection?(power_value)
|
30
|
-
power_value.include?(record)
|
17
|
+
def default_include_object?(power_name, *args)
|
18
|
+
object = args.pop
|
19
|
+
context = args
|
20
|
+
power_value = send(power_name, *context)
|
21
|
+
if power_value.nil?
|
22
|
+
false
|
23
|
+
elsif Util.scope?(power_value)
|
24
|
+
if Util.scope_selects_all_records?(power_value)
|
25
|
+
true
|
31
26
|
else
|
32
|
-
|
27
|
+
power_ids_name = self.class.power_ids_name(power_name)
|
28
|
+
send(power_ids_name, *context).include?(object.id)
|
33
29
|
end
|
30
|
+
elsif Util.collection?(power_value)
|
31
|
+
power_value.include?(object)
|
32
|
+
else
|
33
|
+
raise Consul::NoCollection, "can only call #include_object? on a collection, but power was of type #{power_value.class.name}"
|
34
34
|
end
|
35
35
|
end
|
36
36
|
|
37
|
-
def
|
38
|
-
|
37
|
+
def default_power_ids(power_name, *args)
|
38
|
+
scope = send(power_name, *args)
|
39
|
+
database_touched
|
40
|
+
scope.collect_ids
|
39
41
|
end
|
40
42
|
|
41
|
-
|
42
|
-
|
43
|
-
def context_and_record_from_args(args, name)
|
44
|
-
context_count = send(self.class.context_count_name(name))
|
45
|
-
context = []
|
46
|
-
context_count.times do
|
47
|
-
arg = args.shift or raise Consul::InsufficientContext, "Insufficient context for parametrized power: #{name}"
|
48
|
-
context << arg
|
49
|
-
end
|
50
|
-
record = args.shift
|
51
|
-
[context, record]
|
43
|
+
def powerless!(*args)
|
44
|
+
raise Consul::Powerless.new("No power to #{[*args].inspect}")
|
52
45
|
end
|
53
46
|
|
54
47
|
def boolean_or_nil?(value)
|
@@ -89,28 +82,31 @@ module Consul
|
|
89
82
|
self.current = old_power
|
90
83
|
end
|
91
84
|
|
92
|
-
|
85
|
+
def define_query_and_bang_methods(name, &query)
|
86
|
+
query_method = "#{name}?"
|
87
|
+
bang_method = "#{name}!"
|
88
|
+
define_method(query_method, &query)
|
89
|
+
define_method(bang_method) { |*args| send(query_method, *args) or powerless!(name, *args) }
|
90
|
+
end
|
93
91
|
|
94
92
|
def define_power(name, &block)
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
93
|
+
name = name.to_s
|
94
|
+
if name.ends_with?('?')
|
95
|
+
name_without_suffix = name.chop
|
96
|
+
define_query_and_bang_methods(name_without_suffix, &block)
|
97
|
+
else
|
98
|
+
define_method(name, &block)
|
99
|
+
define_query_and_bang_methods(name) { |*args| default_include_power?(name, *args) }
|
100
|
+
if name.singularize != name
|
101
|
+
define_query_and_bang_methods(name.singularize) { |*args| default_include_object?(name, *args) }
|
102
|
+
end
|
103
|
+
ids_method = power_ids_name(name)
|
104
|
+
define_method(ids_method) { |*args| default_power_ids(name, *args) }
|
105
|
+
memoize ids_method
|
108
106
|
end
|
109
|
-
memoize ids_method
|
110
107
|
name
|
111
108
|
end
|
112
109
|
|
113
110
|
end
|
114
|
-
|
115
111
|
end
|
116
112
|
end
|
@@ -4,18 +4,54 @@ module Consul
|
|
4
4
|
|
5
5
|
module InstanceMethods
|
6
6
|
|
7
|
+
def include?(power_name, *args)
|
8
|
+
warn "#include? ist deprececated. Use #include_power? and #include_object? instead."
|
9
|
+
if args.size == 0
|
10
|
+
include_power?(power_name, *args)
|
11
|
+
else
|
12
|
+
include_object?(power_name, *args)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def include!(power_name, *args)
|
17
|
+
warn "#include! ist deprececated. Use #include_power! and #include_object! instead."
|
18
|
+
if args.size == 0
|
19
|
+
include_power!(power_name, *args)
|
20
|
+
else
|
21
|
+
include_object!(power_name, *args)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def include_power?(power_name, *context)
|
26
|
+
send("#{power_name}?", *context)
|
27
|
+
end
|
28
|
+
|
29
|
+
def include_power!(power_name, *context)
|
30
|
+
send("#{power_name}!", *context)
|
31
|
+
end
|
32
|
+
|
33
|
+
def include_object?(power_name, *context_and_object)
|
34
|
+
power_name = power_name.to_s
|
35
|
+
send("#{power_name.singularize}?", *context_and_object)
|
36
|
+
end
|
37
|
+
|
38
|
+
def include_object!(power_name, *context_and_object)
|
39
|
+
power_name = power_name.to_s
|
40
|
+
send("#{power_name.singularize}!", *context_and_object)
|
41
|
+
end
|
42
|
+
|
7
43
|
def for_record(*args)
|
8
44
|
send(name_for_record(*args))
|
9
45
|
end
|
10
46
|
|
11
47
|
def include_record?(*args)
|
12
48
|
adjective, record = Util.adjective_and_argument(*args)
|
13
|
-
|
49
|
+
include_object?(name_for_record(*args), record)
|
14
50
|
end
|
15
51
|
|
16
52
|
def include_record!(*args)
|
17
53
|
adjective, record = Util.adjective_and_argument(*args)
|
18
|
-
|
54
|
+
include_object!(name_for_record(*args), record)
|
19
55
|
end
|
20
56
|
|
21
57
|
def name_for_model(*args)
|
@@ -29,11 +65,11 @@ module Consul
|
|
29
65
|
end
|
30
66
|
|
31
67
|
def include_model?(*args)
|
32
|
-
|
68
|
+
include_power?(name_for_model(*args))
|
33
69
|
end
|
34
70
|
|
35
71
|
def include_model!(*args)
|
36
|
-
|
72
|
+
include_power!(name_for_model(*args))
|
37
73
|
end
|
38
74
|
|
39
75
|
def name_for_record(*args)
|
data/lib/consul/version.rb
CHANGED
data/spec/rails-2.3/Gemfile.lock
CHANGED
data/spec/rails-3.0/Gemfile.lock
CHANGED
data/spec/rails-3.2/Gemfile.lock
CHANGED
@@ -13,10 +13,30 @@ class Power
|
|
13
13
|
Client
|
14
14
|
end
|
15
15
|
|
16
|
+
power :fast_clients do
|
17
|
+
Client.active
|
18
|
+
end
|
19
|
+
|
20
|
+
power :fast_client? do |client|
|
21
|
+
!client.deleted?
|
22
|
+
end
|
23
|
+
|
16
24
|
power :client_notes do |client|
|
17
25
|
client.notes
|
18
26
|
end
|
19
27
|
|
28
|
+
power :fast_client_notes do |client|
|
29
|
+
client.notes
|
30
|
+
end
|
31
|
+
|
32
|
+
power :fast_client_note? do |client, note|
|
33
|
+
note.client_id == client.id
|
34
|
+
end
|
35
|
+
|
36
|
+
power :fast_client_note_without_collection? do |client, note|
|
37
|
+
note.client_id == client.id
|
38
|
+
end
|
39
|
+
|
20
40
|
power :notes do
|
21
41
|
Note.scoped(:joins => :client)
|
22
42
|
end
|
@@ -24,7 +24,7 @@ describe Consul::Power do
|
|
24
24
|
|
25
25
|
context 'nil powers' do
|
26
26
|
|
27
|
-
describe '
|
27
|
+
describe 'query methods' do
|
28
28
|
|
29
29
|
context 'when no record is given' do
|
30
30
|
|
@@ -49,7 +49,7 @@ describe Consul::Power do
|
|
49
49
|
|
50
50
|
end
|
51
51
|
|
52
|
-
describe '
|
52
|
+
describe 'bang methods' do
|
53
53
|
|
54
54
|
context 'when no record is given' do
|
55
55
|
|
@@ -86,7 +86,7 @@ describe Consul::Power do
|
|
86
86
|
@user.power.client_notes(@client1).should == [@client1_note1, @client1_note2]
|
87
87
|
end
|
88
88
|
|
89
|
-
describe '
|
89
|
+
describe 'query methods' do
|
90
90
|
|
91
91
|
context 'when no record is given' do
|
92
92
|
|
@@ -137,19 +137,55 @@ describe Consul::Power do
|
|
137
137
|
@user.power.client_note?(@client1, @client2_note1).should be_false
|
138
138
|
end
|
139
139
|
|
140
|
-
|
140
|
+
context 'optimization through additional definition of a Ruby method' do
|
141
|
+
|
142
|
+
it 'should not affect collection access' do
|
143
|
+
@user.power.fast_clients.to_a.should =~ [@client1, @client2]
|
144
|
+
@user.power.fast_client_ids.to_a.should =~ [@client1.id, @client2.id]
|
145
|
+
@user.power.fast_clients?.should be_true
|
146
|
+
end
|
147
|
+
|
148
|
+
it 'should no longer access the database when checking inclusion of a single record' do
|
149
|
+
@user.power.should_not_receive(:database_touched)
|
150
|
+
@user.power.fast_client?(@client1).should be_true
|
151
|
+
@user.power.fast_client?(@deleted_client).should be_false
|
152
|
+
end
|
141
153
|
|
142
|
-
|
154
|
+
it 'is used by and bang methods' do
|
155
|
+
@user.power.should_not_receive(:database_touched)
|
156
|
+
expect { @user.power.fast_client!(@client1) }.to_not raise_error
|
157
|
+
expect { @user.power.fast_client!(@deleted_client) }.to raise_error(Consul::Powerless)
|
158
|
+
end
|
159
|
+
|
160
|
+
it 'works with parametrized powers' do
|
161
|
+
@user.power.should_not_receive(:database_touched)
|
162
|
+
@user.power.fast_client_note?(@client1, @client1_note1).should be_true
|
163
|
+
@user.power.fast_client_note?(@client1, @client2_note1).should be_false
|
164
|
+
expect { @user.power.fast_client_note!(@client1, @client1_note1) }.to_not raise_error
|
165
|
+
expect { @user.power.fast_client_note!(@client1, @client2_note1) }.to raise_error(Consul::Powerless)
|
166
|
+
end
|
167
|
+
|
168
|
+
it 'works when only the optimized power is defined, without a collection power' do
|
169
|
+
@user.power.should_not_receive(:database_touched)
|
170
|
+
@user.power.fast_client_note_without_collection?(@client1, @client1_note1).should be_true
|
171
|
+
@user.power.fast_client_note_without_collection?(@client1, @client2_note1).should be_false
|
172
|
+
end
|
143
173
|
|
144
|
-
it 'should raise an error' do
|
145
|
-
expect { @user.power.client_notes? }.to raise_error(Consul::InsufficientContext)
|
146
174
|
end
|
147
175
|
|
148
176
|
end
|
149
177
|
|
178
|
+
#context 'when a power with arguments is given insufficient context' do
|
179
|
+
#
|
180
|
+
# it 'should raise an error' do
|
181
|
+
# expect { @user.power.client_notes? }.to raise_error(ArgumentError)
|
182
|
+
# end
|
183
|
+
#
|
184
|
+
#end
|
185
|
+
|
150
186
|
end
|
151
187
|
|
152
|
-
describe '
|
188
|
+
describe 'bang methods' do
|
153
189
|
|
154
190
|
context 'when no record is given' do
|
155
191
|
|
@@ -207,7 +243,7 @@ describe Consul::Power do
|
|
207
243
|
@user.power.key_figures.should == %w[amount working_costs]
|
208
244
|
end
|
209
245
|
|
210
|
-
describe '
|
246
|
+
describe 'query methods' do
|
211
247
|
|
212
248
|
context 'when no record is given' do
|
213
249
|
|
@@ -237,7 +273,7 @@ describe Consul::Power do
|
|
237
273
|
|
238
274
|
end
|
239
275
|
|
240
|
-
describe '
|
276
|
+
describe 'bang methods' do
|
241
277
|
|
242
278
|
context 'when no record is given' do
|
243
279
|
|
@@ -275,7 +311,7 @@ describe Consul::Power do
|
|
275
311
|
@user.power.always_false.should == false
|
276
312
|
end
|
277
313
|
|
278
|
-
describe '
|
314
|
+
describe 'query methods' do
|
279
315
|
|
280
316
|
context 'when no record is given' do
|
281
317
|
|
@@ -295,15 +331,15 @@ describe Consul::Power do
|
|
295
331
|
|
296
332
|
context 'with a given record' do
|
297
333
|
|
298
|
-
it 'should raise Consul::NoCollection' do
|
299
|
-
|
300
|
-
end
|
334
|
+
#it 'should raise Consul::NoCollection' do
|
335
|
+
# expect { @user.power.always_true?('foo') }.to raise_error(Consul::NoCollection)
|
336
|
+
#end
|
301
337
|
|
302
338
|
end
|
303
339
|
|
304
340
|
end
|
305
341
|
|
306
|
-
describe '
|
342
|
+
describe 'bang methods' do
|
307
343
|
|
308
344
|
context 'when no record is given' do
|
309
345
|
|
@@ -323,9 +359,10 @@ describe Consul::Power do
|
|
323
359
|
|
324
360
|
context 'with a given record' do
|
325
361
|
|
326
|
-
it 'should raise Consul::NoCollection' do
|
327
|
-
|
328
|
-
end
|
362
|
+
#it 'should raise Consul::NoCollection' do
|
363
|
+
# expect { @user.power.always_true!('foo') }.to raise_error(Consul::NoCollection)
|
364
|
+
#end
|
365
|
+
|
329
366
|
end
|
330
367
|
|
331
368
|
end
|
@@ -339,7 +376,7 @@ describe Consul::Power do
|
|
339
376
|
@user.power.api_key.should == 'secret-api-key'
|
340
377
|
end
|
341
378
|
|
342
|
-
describe '
|
379
|
+
describe 'query methods' do
|
343
380
|
|
344
381
|
context 'when no record is given' do
|
345
382
|
|
@@ -357,15 +394,15 @@ describe Consul::Power do
|
|
357
394
|
|
358
395
|
context 'with a given record' do
|
359
396
|
|
360
|
-
it 'should raise Consul::NoCollection' do
|
361
|
-
|
362
|
-
end
|
397
|
+
#it 'should raise Consul::NoCollection' do
|
398
|
+
# expect { @user.power.api_key?('foo') }.to raise_error(Consul::NoCollection)
|
399
|
+
#end
|
363
400
|
|
364
401
|
end
|
365
402
|
|
366
403
|
end
|
367
404
|
|
368
|
-
describe '
|
405
|
+
describe 'bang methods' do
|
369
406
|
|
370
407
|
context 'when no record is given' do
|
371
408
|
|
@@ -383,9 +420,9 @@ describe Consul::Power do
|
|
383
420
|
|
384
421
|
context 'with a given record' do
|
385
422
|
|
386
|
-
it 'should raise Consul::NoCollection' do
|
387
|
-
|
388
|
-
end
|
423
|
+
#it 'should raise Consul::NoCollection' do
|
424
|
+
# expect { @user.power.api_key!('foo') }.to raise_error(Consul::NoCollection)
|
425
|
+
#end
|
389
426
|
|
390
427
|
end
|
391
428
|
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: consul
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 59
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
8
|
+
- 9
|
9
9
|
- 0
|
10
|
-
version: 0.
|
10
|
+
version: 0.9.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Henning Koch
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2013-
|
18
|
+
date: 2013-07-19 00:00:00 +02:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|