consul 0.6.1 → 0.7.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 +111 -7
- data/consul.gemspec +1 -8
- data/lib/consul.rb +1 -0
- data/lib/consul/power.rb +6 -3
- data/lib/consul/util.rb +1 -9
- data/lib/consul/version.rb +1 -1
- data/spec/shared/consul/power_spec.rb +6 -6
- metadata +19 -5
data/README.md
CHANGED
@@ -59,13 +59,6 @@ Here is how to do all of that:
|
|
59
59
|
power.note?(Note.last) # => returns whether the given Note is in the Power#notes scope. Caches the result for subsequent queries.
|
60
60
|
power.note!(Note.last) # => raises Consul::Powerless unless the given Note is in the Power#notes scope
|
61
61
|
|
62
|
-
You can also write power checks like this:
|
63
|
-
|
64
|
-
power.include?(:notes)
|
65
|
-
power.include!(:notes)
|
66
|
-
power.include?(:notes, Note.last)
|
67
|
-
power.include!(:notes, Note.last)
|
68
|
-
|
69
62
|
|
70
63
|
Boolean powers
|
71
64
|
--------------
|
@@ -342,6 +335,113 @@ The `authorize_values_for` macro comes with many useful options and details best
|
|
342
335
|
assignable_values_for :field, :through => lambda { Power.current }
|
343
336
|
|
344
337
|
|
338
|
+
Dynamic power access
|
339
|
+
--------------------
|
340
|
+
|
341
|
+
Consul gives you a way to dynamically access and query powers for a given name, model class or record.
|
342
|
+
A common use case for this are generic helper methods, e.g. a method to display an "edit" link for any given record
|
343
|
+
if the user is authorized to change that record:
|
344
|
+
|
345
|
+
module CrudHelper
|
346
|
+
|
347
|
+
def edit_record_action(record)
|
348
|
+
if current_power.include_record?(:updatable, record)
|
349
|
+
link_to 'Edit', [:edit, record]
|
350
|
+
end
|
351
|
+
end
|
352
|
+
|
353
|
+
end
|
354
|
+
|
355
|
+
You can find a full list of available dynamic calls below:
|
356
|
+
|
357
|
+
| Dynamic call | Equivalent |
|
358
|
+
|---------------------------------------------------------|--------------------------------------------|
|
359
|
+
| `Power.current.send(:notes)` | `Power.current.notes` |
|
360
|
+
| `Power.current.include?(:notes)` | `Power.current.notes?` |
|
361
|
+
| `Power.current.include!(:notes)` | `Power.current.notes!` |
|
362
|
+
| `Power.current.include?(:notes, Note.last)` | `Power.current.note?(Note.last)` |
|
363
|
+
| `Power.current.include!(:notes, Note.last)` | `Power.current.note!(Note.last)` |
|
364
|
+
| `Power.current.for_record(Note.last)` | `Power.current.notes` |
|
365
|
+
| `Power.current.for_record(:updatable, Note.last)` | `Power.current.updatable_notes` |
|
366
|
+
| `Power.current.for_model(Note)` | `Power.current.notes` |
|
367
|
+
| `Power.current.for_model(:updatable, Note)` | `Power.current.updatable_notes` |
|
368
|
+
| `Power.current.include_model?(Note)` | `Power.current.notes?` |
|
369
|
+
| `Power.current.include_model?(:updatable, Note)` | `Power.current.updatable_notes?` |
|
370
|
+
| `Power.current.include_model!(Note)` | `Power.current.notes!` |
|
371
|
+
| `Power.current.include_model!(:updatable, Note)` | `Power.current.updatable_notes!` |
|
372
|
+
| `Power.current.include_record?(Note.last)` | `Power.current.note?(Note.last)` |
|
373
|
+
| `Power.current.include_record?(:updatable, Note.last)` | `Power.current.updatable_note?(Note.last)` |
|
374
|
+
| `Power.current.include_record!(Note.last)` | `Power.current.note!(Note.last)` |
|
375
|
+
| `Power.current.include_record!(:updatable, Note.last)` | `Power.current.updatable_note!(Note.last)` |
|
376
|
+
| `Power.current.name_for_model(Note)` | `:notes` |
|
377
|
+
| `Power.current.name_for_model(:updatable, Note)` | `:updatable_notes` |
|
378
|
+
| `Power.current.name_for_record(Note.last)` | `:notes` |
|
379
|
+
| `Power.current.name_for_record(:updatable, Note.last)` | `:updatable_notes` |
|
380
|
+
|
381
|
+
|
382
|
+
|
383
|
+
Querying a power that might be nil
|
384
|
+
----------------------------------
|
385
|
+
|
386
|
+
You will often want to access `Power.current` from another model, to e.g. iterate through the list of accessible users:
|
387
|
+
|
388
|
+
class UserReport
|
389
|
+
|
390
|
+
def data
|
391
|
+
Power.current.users.collect do |user|
|
392
|
+
[user.name, user.email, user.income]
|
393
|
+
end
|
394
|
+
end
|
395
|
+
|
396
|
+
end
|
397
|
+
|
398
|
+
Good practice is for your model to not crash when `Power.current` is `nil`. This is the case when your model isn't
|
399
|
+
called as part of processing a browser request, e.g. on the console, during tests and during batch processes.
|
400
|
+
In such cases your model should simply skip authorization and assume that all users are accessible:
|
401
|
+
|
402
|
+
class UserReport
|
403
|
+
|
404
|
+
def data
|
405
|
+
accessible_users = Power.current.present? Power.current.users || User
|
406
|
+
accessible_users.collect do |user|
|
407
|
+
[user.name, user.email, user.income]
|
408
|
+
end
|
409
|
+
end
|
410
|
+
|
411
|
+
end
|
412
|
+
|
413
|
+
Because this pattern is so common, the `Power` class comes with a number of class methods you can use to either query
|
414
|
+
`Power.current` or, if it is not set, just assume that everything is accessible:
|
415
|
+
|
416
|
+
class UserReport
|
417
|
+
|
418
|
+
def data
|
419
|
+
Power.for_model(user).collect do |user|
|
420
|
+
[user.name, user.email, user.income]
|
421
|
+
end
|
422
|
+
end
|
423
|
+
|
424
|
+
end
|
425
|
+
|
426
|
+
There is a long selection of class methods that behave neutrally in case `Power.current` is `nil`:
|
427
|
+
|
428
|
+
| Call | Equivalent |
|
429
|
+
|----------------------------------------------------------|---------------------------------------------------------------------|
|
430
|
+
| `Power.for_model(Note)` | `Power.current.present? ? Power.current.notes : Note` |
|
431
|
+
| `Power.for_model(:updatable, Note)` | `Power.current.present? ? Power.current.updatable_notes : Note` |
|
432
|
+
| `Power.include_model?(Note)` | `Power.current.present? ? Power.notes? : true` |
|
433
|
+
| `Power.include_model?(:updatable, Note)` | `Power.current.present? ? Power.updatable_notes? : true` |
|
434
|
+
| `Power.include_model!(Note)` | `Power.notes! if Power.current.present?` |
|
435
|
+
| `Power.include_model!(:updatable, Note)` | `Power.updatable_notes! if Power.current.present?` |
|
436
|
+
| `Power.for_record(Note.last)` | `Power.current.present? ? Power.current.notes : Note` |
|
437
|
+
| `Power.for_record(:updatable, Note.last)` | `Power.current.present? ? Power.current.updatable_notes : Note` |
|
438
|
+
| `Power.include_record?(Note.last)` | `Power.current.present? ? Power.note?(Note.last) : true` |
|
439
|
+
| `Power.include_record?(:updatable, Note.last)` | `Power.current.present? ? Power.updatable_note?(Note.last?) : true` |
|
440
|
+
| `Power.include_record!(Note.last)` | `Power.note!(Note.last) if Power.current.present?` |
|
441
|
+
| `Power.include_record!(:updatable, Note.last)` | `Power.updatable_note!(Note.last) if Power.current.present?` |
|
442
|
+
|
443
|
+
|
444
|
+
|
345
445
|
Testing
|
346
446
|
-------
|
347
447
|
|
@@ -389,6 +489,10 @@ A nice shortcut is that when you call `with_power` with an argument that is not
|
|
389
489
|
end
|
390
490
|
|
391
491
|
|
492
|
+
|
493
|
+
|
494
|
+
|
495
|
+
|
392
496
|
Installation
|
393
497
|
------------
|
394
498
|
|
data/consul.gemspec
CHANGED
@@ -17,12 +17,5 @@ Gem::Specification.new do |s|
|
|
17
17
|
|
18
18
|
s.add_dependency('memoizer')
|
19
19
|
s.add_dependency('rails')
|
20
|
-
|
21
|
-
## s.add_development_dependency('assignable_values')
|
22
|
-
#s.add_development_dependency('assignable_values')
|
23
|
-
#s.add_development_dependency('rails', '~>2.3')
|
24
|
-
#s.add_development_dependency('rspec', '~>1.3')
|
25
|
-
#s.add_development_dependency('rspec-rails', '~>1.3')
|
26
|
-
#s.add_development_dependency('shoulda-matchers')
|
27
|
-
#s.add_development_dependency('sqlite3')
|
20
|
+
s.add_dependency('edge_rider')
|
28
21
|
end
|
data/lib/consul.rb
CHANGED
data/lib/consul/power.rb
CHANGED
@@ -78,6 +78,10 @@ module Consul
|
|
78
78
|
[TrueClass, FalseClass, NilClass].include?(value.class)
|
79
79
|
end
|
80
80
|
|
81
|
+
def database_touched
|
82
|
+
# spy for tests
|
83
|
+
end
|
84
|
+
|
81
85
|
module ClassMethods
|
82
86
|
|
83
87
|
def power(*names, &block)
|
@@ -164,9 +168,8 @@ module Consul
|
|
164
168
|
ids_method = power_ids_name(name)
|
165
169
|
define_method(ids_method) do |*args|
|
166
170
|
scope = send(name, *args)
|
167
|
-
|
168
|
-
|
169
|
-
::ActiveRecord::Base.connection.select_values(query).collect(&:to_i)
|
171
|
+
database_touched
|
172
|
+
scope.collect_ids
|
170
173
|
end
|
171
174
|
memoize ids_method
|
172
175
|
name
|
data/lib/consul/util.rb
CHANGED
@@ -2,17 +2,9 @@ module Consul
|
|
2
2
|
module Util
|
3
3
|
extend self
|
4
4
|
|
5
|
-
def scope_to_sql(scope)
|
6
|
-
if scope.respond_to?(:to_sql)
|
7
|
-
scope.to_sql
|
8
|
-
else
|
9
|
-
scope.send(:construct_finder_sql, {})
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
5
|
def scope_selects_all_records?(scope)
|
14
6
|
scope = scope.scoped({})
|
15
|
-
scope_sql =
|
7
|
+
scope_sql = scope.to_sql
|
16
8
|
quoted_table_name = Regexp.quote(scope.connection.quote_table_name(scope.table_name))
|
17
9
|
all_sql_pattern = /\ASELECT (#{quoted_table_name}\.)?\* FROM #{quoted_table_name}\z/
|
18
10
|
scope_sql.squish =~ all_sql_pattern
|
data/lib/consul/version.rb
CHANGED
@@ -107,28 +107,28 @@ describe Consul::Power do
|
|
107
107
|
end
|
108
108
|
|
109
109
|
it 'should only trigger a single query for multiple checks on the same scope' do
|
110
|
-
|
110
|
+
@user.power.should_receive(:database_touched).once
|
111
111
|
@user.power.client?(@client1)
|
112
112
|
@user.power.client?(@deleted_client)
|
113
113
|
end
|
114
114
|
|
115
|
-
it 'should trigger a query if the scope defines a condition' do
|
116
|
-
|
115
|
+
it 'should trigger a query only if the scope defines a condition' do
|
116
|
+
@user.power.should_receive(:database_touched).once
|
117
117
|
@user.power.client?(@client1)
|
118
118
|
end
|
119
119
|
|
120
120
|
it 'should not trigger a query if the scope defines no conditions' do
|
121
|
-
|
121
|
+
@user.power.should_not_receive(:database_touched)
|
122
122
|
@user.power.all_client?(@client1)
|
123
123
|
end
|
124
124
|
|
125
125
|
it 'should always trigger a query if a returned model has a default scope, even if it defines no additional conditions' do
|
126
|
-
|
126
|
+
@user.power.should_receive(:database_touched).once
|
127
127
|
@user.power.song?(Song.new)
|
128
128
|
end
|
129
129
|
|
130
130
|
it 'should trigger query if a returned model has a default scope and defines additional conditions' do
|
131
|
-
|
131
|
+
@user.power.should_receive(:database_touched).once
|
132
132
|
@user.power.recent_song?(Song.new)
|
133
133
|
end
|
134
134
|
|
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: 3
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
-
|
10
|
-
version: 0.
|
8
|
+
- 7
|
9
|
+
- 0
|
10
|
+
version: 0.7.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-02-11 00:00:00 +01:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -46,6 +46,20 @@ dependencies:
|
|
46
46
|
version: "0"
|
47
47
|
type: :runtime
|
48
48
|
version_requirements: *id002
|
49
|
+
- !ruby/object:Gem::Dependency
|
50
|
+
name: edge_rider
|
51
|
+
prerelease: false
|
52
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
hash: 3
|
58
|
+
segments:
|
59
|
+
- 0
|
60
|
+
version: "0"
|
61
|
+
type: :runtime
|
62
|
+
version_requirements: *id003
|
49
63
|
description: A scope-based authorization solution for Ruby on Rails.
|
50
64
|
email: henning.koch@makandra.de
|
51
65
|
executables: []
|