action_policy 0.4.2 → 0.4.3

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 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