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 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
@@ -1,4 +1,5 @@
1
1
  require 'memoizer'
2
+ require 'edge_rider'
2
3
  require 'consul/util'
3
4
  require 'consul/power'
4
5
  require 'consul/errors'
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
- scope = scope.scoped(:select => "`#{scope.table_name}`.`id`")
168
- query = Util.scope_to_sql(scope)
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 = scope_to_sql(scope)
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
@@ -1,3 +1,3 @@
1
1
  module Consul
2
- VERSION = '0.6.1'
2
+ VERSION = '0.7.0'
3
3
  end
@@ -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
- ActiveRecord::Base.connection.should_receive(:select_values).once.and_return([]) #.and_return(double('connection').as_null_object)
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
- ActiveRecord::Base.connection.should_receive(:select_values).and_return([])
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
- ActiveRecord::Base.connection.should_not_receive(:select_values)
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
- ActiveRecord::Base.connection.should_receive(:select_values).and_return([])
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
- ActiveRecord::Base.connection.should_receive(:select_values).and_return([])
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: 5
4
+ hash: 3
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
- - 6
9
- - 1
10
- version: 0.6.1
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-01-16 00:00:00 +01:00
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: []