action_policy 0.4.2 → 0.4.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 946e026afb064f557ce87761fb4ddb981635d0b95ca5f8a6415ae7572932f758
4
- data.tar.gz: c97a78ac4be2b1482280c70345caa881d4b6d126e1d92c7b8e852592ccca09ed
3
+ metadata.gz: 6530513fc2b087beae97b3b33e662f421a0f12911a1e21b2cce9778dc7f693c9
4
+ data.tar.gz: a915127b22da7b43cb0fc1aa1048c8a4d9c0b5a51f458039594eac17b29e92bc
5
5
  SHA512:
6
- metadata.gz: 5585386d303ef8af41651a3951fadd6970d4a127226a88b7c220852235d8f7cd34f7b608ef1390ed02b921fe64f93abc9d0ddc2d2f0780293e7b925818830d17
7
- data.tar.gz: '0954283e4e9576ced888c80e1e02bec3fa2c128f69f79962ba748b69aa6fbab0c46814746a9ac6fe36dd2609dc9a626fb4ee51848b84a5ebe3a584838e2eeb9e'
6
+ metadata.gz: 2a34ac6a7ab8289521e2ccb977ed6c6b6e655a185deb053676aad2d78674b7367d04c75abd552dafd59b1ae46465cfa117ae97239b9fd2e3cc6947d020d80775
7
+ data.tar.gz: 991f5b553314cf11dedaab9964d5d4b4ee384877474ed6dc814fa0c4d4bdf383b8288df46bf987a437b82a33f859ac5314a1c4bd75b1df28d8a2438886f37c40
@@ -18,11 +18,11 @@ matrix:
18
18
  gemfile: gemfiles/railsmaster.gemfile
19
19
  - rvm: jruby-9.2.8.0
20
20
  gemfile: gemfiles/jruby.gemfile
21
- - rvm: 2.6.0
21
+ - rvm: 2.6.5
22
22
  gemfile: gemfiles/rails6.gemfile
23
23
  - rvm: 2.5.3
24
24
  gemfile: Gemfile
25
- - rvm: 2.4.3
25
+ - rvm: 2.5.3
26
26
  gemfile: gemfiles/rails42.gemfile
27
27
  allow_failures:
28
28
  - rvm: ruby-head
@@ -2,6 +2,17 @@
2
2
 
3
3
  ## master
4
4
 
5
+ ## 0.4.3 (2019-12-14)
6
+
7
+ - Add `#cache(*parts, **options) { ... }` method. ([@palkan][])
8
+
9
+ Allows you to cache anything in policy classes using the Action Policy
10
+ cache key generation mechanism.
11
+
12
+ - Handle versioned Rails cache keys. ([@palkan][])
13
+
14
+ Use `#cache_with_version` as a cache key if defined.
15
+
5
16
  ## 0.4.2 (2019-12-13)
6
17
 
7
18
  - Fix regression introduced in 0.4.0 which broke testing Class targets. ([@palkan][])
@@ -65,6 +65,8 @@ Learn more about the motivation behind the Action Policy and its features by wat
65
65
 
66
66
  ## Resources
67
67
 
68
+ - RubyRussia, 2019 "Welcome, or access denied?" talk ([video](https://www.youtube.com/watch?v=y15a2g7v8i0) [RU], [slides](https://speakerdeck.com/palkan/rubyrussia-2019-welcome-or-access-denied))
69
+
68
70
  - Seattle.rb, 2019 "A Denial!" talk [[slides](https://speakerdeck.com/palkan/seattle-dot-rb-2019-a-denial)]
69
71
 
70
72
  - RailsConf, 2018 "Access Denied" talk [[video](https://www.youtube.com/watch?v=NVwx0DARDis), [slides](https://speakerdeck.com/palkan/railsconf-2018-access-denied-the-missing-guide-to-authorization-in-rails)]
@@ -166,7 +166,7 @@ Cache store must provide at least a `#read(key)` and `write(key, value, **option
166
166
 
167
167
  **NOTE:** cache store also should take care of serialiation/deserialization since the `value` is `ExecutionResult` instance (which contains also some additional information, e.g. failure reasons). Rails cache store supports serialization/deserialization out-of-the-box.
168
168
 
169
- By default, Action Policy builds a cache key using the following scheme:
169
+ By default, Action Policy builds a cache key using the following scheme (defined in `#rule_cache_key(rule)` method):
170
170
 
171
171
  ```ruby
172
172
  "#{cache_namespace}/#{context_cache_key}" \
@@ -175,11 +175,29 @@ By default, Action Policy builds a cache key using the following scheme:
175
175
 
176
176
  Where `cache_namespace` is equal to `"acp:#{MAJOR_GEM_VERSION}.#{MINOR_GEM_VERSION}"`, and `context_cache_key` is a concatenation of all authorization contexts cache keys (in the same order as they are defined in the policy class).
177
177
 
178
- If any object does not respond to `#policy_cache_key`, we fallback to `#cache_key`. If `#cache_key` is not defined, an `ArgumentError` is raised.
178
+ If any object does not respond to `#policy_cache_key`, we fallback to `#cache_key` (or `#cache_key_with_version` for modern Rails versions). If `#cache_key` is not defined, an `ArgumentError` is raised.
179
179
 
180
180
  **NOTE:** if your `#cache_key` method is performance-heavy (e.g. like the `ActiveRecord::Relation`'s one), we recommend to explicitly define the `#policy_cache_key` method on the corresponding class to avoid unnecessary load. See also [action_policy#55](https://github.com/palkan/action_policy/issues/55).
181
181
 
182
- You can define your own `cache_key` / `cache_namespace` / `context_cache_key` methods for policy class to override this logic.
182
+ You can define your own `rule_cache_key` / `cache_namespace` / `context_cache_key` methods for policy class to override this logic.
183
+
184
+ You can also use the `#cache` instance method to cache arbitrary values in you policies:
185
+
186
+ ```ruby
187
+ class ApplicationPolicy < ActionPolicy::Base
188
+ # Suppose that a user has many roles each having an array of permissions
189
+ def permissions
190
+ cache(user) { user.roles.pluck(:permissions).flatten.uniq }
191
+ end
192
+
193
+ # You can pass multiple cache key "parts"
194
+ def account_permissions(account)
195
+ cache(user, account) { user.account_roles.where(account: account).pluck(:permissions).flatten.uniq }
196
+ end
197
+ end
198
+ ```
199
+
200
+ **NOTE:** `#cache` method uses the same cache key generation logic as rules caching (described above).
183
201
 
184
202
  #### Invalidation
185
203
 
@@ -2,6 +2,7 @@ source "https://rubygems.org"
2
2
 
3
3
  gem "sqlite3", "~> 1.3.0"
4
4
  gem "rails", "~> 4.2"
5
+ gem "thor", "< 1.0"
5
6
  gem "method_source"
6
7
  gem "unparser"
7
8
 
@@ -13,19 +13,15 @@ module ActionPolicy
13
13
  module ObjectExt
14
14
  def _policy_cache_key(use_object_id: false)
15
15
  return policy_cache_key if respond_to?(:policy_cache_key)
16
+ return cache_key_with_version if respond_to?(:cache_key_with_version)
16
17
  return cache_key if respond_to?(:cache_key)
17
18
 
18
- return object_id if use_object_id == true
19
+ return object_id.to_s if use_object_id == true
19
20
 
20
21
  raise ArgumentError, "object is not cacheable"
21
22
  end
22
23
  end
23
24
 
24
- # JRuby doesn't support _global_ modules refinements (see https://github.com/jruby/jruby/issues/5220)
25
- # Fallback to monkey-patching.
26
- # TODO: remove after 9.2.7.0 (See https://github.com/jruby/jruby/pull/5627)
27
- ::Object.include(ObjectExt) if RUBY_PLATFORM =~ /java/i
28
-
29
25
  refine Object do
30
26
  include ObjectExt
31
27
  end
@@ -85,6 +81,12 @@ module ActionPolicy
85
81
  to_s
86
82
  end
87
83
  end
84
+
85
+ refine Module do
86
+ def _policy_cache_key(*)
87
+ name
88
+ end
89
+ end
88
90
  end
89
91
  end
90
92
  end
@@ -43,21 +43,17 @@ module ActionPolicy
43
43
 
44
44
  attr_reader :authorization_context
45
45
 
46
- def initialize(*args, **params)
47
- super(*args)
46
+ def initialize(record = nil, **params)
47
+ super(record)
48
48
 
49
49
  @authorization_context = {}
50
50
 
51
51
  self.class.authorization_targets.each do |id, opts|
52
- if opts[:optional] == true
53
- val = params.fetch(id, nil)
54
- else
55
- raise AuthorizationContextMissing, id unless params.key?(id)
52
+ raise AuthorizationContextMissing, id unless params.key?(id) || opts[:optional]
56
53
 
57
- val = params.fetch(id)
54
+ val = params.fetch(id, nil)
58
55
 
59
- raise AuthorizationContextMissing, id if val.nil? && opts[:allow_nil] != true
60
- end
56
+ raise AuthorizationContextMissing, id if val.nil? && opts[:allow_nil] != true
61
57
 
62
58
  authorization_context[id] = instance_variable_set("@#{id}", val)
63
59
  end
@@ -66,9 +62,9 @@ module ActionPolicy
66
62
  end
67
63
 
68
64
  module ClassMethods # :nodoc:
69
- def authorize(*ids, **opts)
65
+ def authorize(*ids, allow_nil: false, optional: false)
70
66
  ids.each do |id|
71
- authorization_targets[id] = opts
67
+ authorization_targets[id] = {allow_nil: allow_nil || optional, optional: optional}
72
68
  end
73
69
 
74
70
  attr_reader(*ids)
@@ -30,9 +30,20 @@ module ActionPolicy # :nodoc:
30
30
  ActionPolicy::CACHE_NAMESPACE
31
31
  end
32
32
 
33
- def cache_key(rule)
34
- "#{cache_namespace}/#{context_cache_key}/" \
35
- "#{record._policy_cache_key}/#{self.class.name}/#{rule}"
33
+ def cache_key(*parts)
34
+ [
35
+ cache_namespace,
36
+ *parts
37
+ ].map { |part| part._policy_cache_key }.join("/")
38
+ end
39
+
40
+ def rule_cache_key(rule)
41
+ cache_key(
42
+ context_cache_key,
43
+ record,
44
+ self.class,
45
+ rule
46
+ )
36
47
  end
37
48
 
38
49
  def context_cache_key
@@ -41,7 +52,7 @@ module ActionPolicy # :nodoc:
41
52
 
42
53
  def apply_with_cache(rule)
43
54
  options = self.class.cached_rules.fetch(rule)
44
- key = cache_key(rule)
55
+ key = rule_cache_key(rule)
45
56
 
46
57
  ActionPolicy.cache_store.then do |store|
47
58
  @result = store.read(key)
@@ -62,6 +73,17 @@ module ActionPolicy # :nodoc:
62
73
  apply_with_cache(rule) { super }
63
74
  end
64
75
 
76
+ def cache(*parts, **options)
77
+ key = cache_key(*parts)
78
+ ActionPolicy.cache_store.then do |store|
79
+ res = store.read(key)
80
+ next res unless res.nil?
81
+ res = yield
82
+ store.write(key, res, **options)
83
+ res
84
+ end
85
+ end
86
+
65
87
  module ClassMethods # :nodoc:
66
88
  def cache(*rules, **options)
67
89
  rules.each do |rule|
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActionPolicy
4
- VERSION = "0.4.2"
4
+ VERSION = "0.4.3"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: action_policy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.2
4
+ version: 0.4.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vladimir Dementyev
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-12-13 00:00:00.000000000 Z
11
+ date: 2019-12-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ammeter