action_policy 0.5.7 → 0.6.2

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: 021e6da5ccd46f76732cb52c0c04ae1a45ce3a74a2b64643e179ee16c40417e3
4
- data.tar.gz: 96a978308fde0160f5b739d47abdd833005ba7109e97837ac0dd5721f1ec52a8
3
+ metadata.gz: 491af624707f597ded52f7d1cacdfc07435937250370804c0ac374f436b494ce
4
+ data.tar.gz: 33ac6abf6907738c6fce67193659e4fdb6615a2dd78a375dba4b57060c9d8f6e
5
5
  SHA512:
6
- metadata.gz: 69953fedcbaa8d007c3c922e17dd26618eda2ed840d59bac476111f62bb7aae7c20f2920606925eb4b3acab7e90f994a6e1a9a55b7de0a0740f3747bb4e85aa3
7
- data.tar.gz: 23de3c82949db85f3a41f079187493087add27f88e0cd9315ccb492e925f4490390d5ae17fb493eb3ae290f5469f216ab258e21e7c155be9853de5728c8b3311
6
+ metadata.gz: 54e1dffffae9777c07d47f5718143cfbc546d6d100e873aadc3a51a1e8821f6495a7d869a477ac7d9d65be1ef92e1fef8bb9adde6c5177186c63eebb8dc88a94
7
+ data.tar.gz: 2b866340538957aa93c5db05a9c8bc7181b9cc4b9b178a137b58a13ad0d7a5158904b911834ab5dd2cf5af4d29bca29d7003095144c7786583375ec8d273c0d5
data/CHANGELOG.md CHANGED
@@ -2,6 +2,24 @@
2
2
 
3
3
  ## master
4
4
 
5
+ ## 0.6.2 (2022-08-12)
6
+
7
+ - Allow omitting authorization record if `with` is provided. ([@palkan][])
8
+
9
+ ## 0.6.1 (2022-05-23)
10
+
11
+ - Fix policy lookup when a namespaced record is passed and the strict mode is used. ([@palkan][])
12
+ - Expose `#authorized_scope` as helper. ([@palkan][])
13
+ - [Fixes [#207](https://github.com/palkan/action_policy/issues/207)] refinement#include deprecation warning on Ruby 3.1
14
+
15
+ ## 0.6.0 (2021-09-02)
16
+
17
+ - Drop Ruby 2.5 support.
18
+ - [Closes [#186](https://github.com/palkan/action_policy/issues/186)] Add `inline_reasons: true` option to `allowed_to?` to avoid wrapping reasons. ([@palkan][])
19
+ - [Fixes [#173](https://github.com/palkan/action_policy/issues/173)] Explicit context were not merged with implicit one within policy classes. ([@palkan][])
20
+ - Add `strict_namespace:` option to policy_for behaviour ([@kevynlebouille][])
21
+ - Prevent possible side effects in policy lookup ([@tomdalling][])
22
+
5
23
  ## 0.5.7 (2021-03-03)
6
24
 
7
25
  The previous release had incorrect dependencies (due to the missing transpiled files).
@@ -449,3 +467,4 @@ This value is now stored in a cache (if any) instead of just the call result (`t
449
467
  [@Be-ngt-oH]: https://github.com/Be-ngt-oH
450
468
  [@pirj]: https://github.com/pirj
451
469
  [@skojin]: https://github.com/skojin
470
+ [@tomdalling]: https://github.com/tomdalling
@@ -8,16 +8,18 @@ module ActionPolicy
8
8
  using ActionPolicy::Ext::PolicyCacheKey
9
9
 
10
10
  # Returns policy instance for the record.
11
- def policy_for(record:, with: nil, namespace: authorization_namespace, context: authorization_context, allow_nil: false, default: default_authorization_policy_class)
11
+ def policy_for(record:, with: nil, namespace: authorization_namespace, context: nil, allow_nil: false, default: default_authorization_policy_class, strict_namespace: authorization_strict_namespace)
12
+ context = context ? authorization_context.merge(context) : authorization_context
13
+
12
14
  policy_class = with || ::ActionPolicy.lookup(
13
15
  record,
14
- namespace: namespace, context: context, allow_nil: allow_nil, default: default
16
+ namespace: namespace, context: context, allow_nil: allow_nil, default: default, strict_namespace: strict_namespace
15
17
  )
16
18
  policy_class&.new(record, **context)
17
19
  end
18
20
 
19
21
  def authorization_context
20
- raise NotImplementedError, "Please, define `authorization_context` method!"
22
+ Kernel.raise NotImplementedError, "Please, define `authorization_context` method!"
21
23
  end
22
24
 
23
25
  def authorization_namespace
@@ -28,6 +30,10 @@ module ActionPolicy
28
30
  # override to provide a policy class use when no policy found
29
31
  end
30
32
 
33
+ def authorization_strict_namespace
34
+ # override to provide strict namespace lookup option
35
+ end
36
+
31
37
  # Override this method to provide implicit authorization target
32
38
  # that would be used in case `record` is not specified in
33
39
  # `authorize!` and `allowed_to?` call.
@@ -39,7 +45,7 @@ module ActionPolicy
39
45
 
40
46
  # Return implicit authorization target or raises an exception if it's nil
41
47
  def implicit_authorization_target!
42
- implicit_authorization_target || raise(
48
+ implicit_authorization_target || Kernel.raise(
43
49
  NotFound,
44
50
  [
45
51
  self,
@@ -23,7 +23,7 @@ module ActionPolicy
23
23
  end
24
24
 
25
25
  refine Object do
26
- include ObjectExt
26
+ ::RubyNext::Core.import_methods(ObjectExt, binding)
27
27
  end
28
28
 
29
29
  refine NilClass do
@@ -86,7 +86,7 @@ module ActionPolicy
86
86
  @result = self.class.result_class.new(self.class, rule)
87
87
 
88
88
  catch :policy_fulfilled do
89
- result.load __apply__(rule)
89
+ result.load __apply__(resolve_rule(rule))
90
90
  end
91
91
 
92
92
  result.value
@@ -133,13 +133,13 @@ module ActionPolicy
133
133
  end
134
134
 
135
135
  # An alias for readability purposes
136
- def check?(*args) ; allowed_to?(*args); end
136
+ def check?(*args, **hargs) ; allowed_to?(*args, **hargs); end
137
137
 
138
138
  # Returns a rule name (policy method name) for activity.
139
139
  #
140
140
  # By default, rule name is equal to activity name.
141
141
  #
142
- # Raises ActionPolicy::UknownRule when rule is not found in policy.
142
+ # Raises ActionPolicy::UnknownRule when rule is not found in policy.
143
143
  def resolve_rule(activity)
144
144
  raise UnknownRule.new(self, activity) unless
145
145
  respond_to?(activity)
@@ -31,6 +31,20 @@ module ActionPolicy
31
31
 
32
32
  def present?() ; !empty?; end
33
33
 
34
+ def merge(other)
35
+ other.reasons.each do |policy_class, rules|
36
+ reasons[policy_class] ||= []
37
+
38
+ rules.each do |rule|
39
+ if rule.is_a?(::Hash)
40
+ add_detailed_reason(reasons[policy_class], rule)
41
+ else
42
+ add_non_detailed_reason(reasons[policy_class], rule)
43
+ end
44
+ end
45
+ end
46
+ end
47
+
34
48
  private
35
49
 
36
50
  def add_non_detailed_reason(store, rule)
@@ -182,7 +196,7 @@ module ActionPolicy
182
196
  result.details ||= {}
183
197
  end
184
198
 
185
- def allowed_to?(rule, record = :__undef__, **options)
199
+ def allowed_to?(rule, record = :__undef__, inline_reasons: false, **options)
186
200
  res =
187
201
  if (record == :__undef__ || record == self.record) && options.empty?
188
202
  rule = resolve_rule(rule)
@@ -196,7 +210,9 @@ module ActionPolicy
196
210
  policy.result
197
211
  end
198
212
 
199
- result&.reasons&.add(policy, rule, res.details) if res.fail?
213
+ if res.fail? && result&.reasons
214
+ inline_reasons ? result.reasons.merge(res.reasons) : result.reasons.add(policy, rule, res.details)
215
+ end
200
216
 
201
217
  res.clear_details
202
218
 
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "action_policy/testing"
4
+
5
+ module ActionPolicy
6
+ module RSpec
7
+ # Policy rule alias matcher `be_an_alias_of`.
8
+ #
9
+ # Verifies that for given policy a policy rule has an alias.
10
+ #
11
+ # Example:
12
+ #
13
+ # # in policy specs
14
+ # subject(:policy) { described_class.new(record, user: user) }
15
+ #
16
+ # let(:user) { build_stubbed(:user) }
17
+ # let(:record) { build_stubbed(:post) }
18
+ #
19
+ # describe "#show?" do
20
+ # it "is an alias of :index? policy rule" do
21
+ # expect(:show?).to be_an_alias_of(policy, :index?)
22
+ # end
23
+ # end
24
+ #
25
+ # # negated version
26
+ # describe "#show?" do
27
+ # it "is not an alias of :index? policy rule" do
28
+ # expect(:show?).to_not be_an_alias_of(policy, :index?)
29
+ # end
30
+ # end
31
+ #
32
+ class BeAnAliasOf < ::RSpec::Matchers::BuiltIn::BaseMatcher
33
+ attr_reader :policy, :rule, :actual
34
+
35
+ def initialize(policy, rule)
36
+ @policy = policy
37
+ @rule = rule
38
+ end
39
+
40
+ def match(_expected, actual)
41
+ policy.resolve_rule(actual) == rule
42
+ end
43
+
44
+ def does_not_match?(actual)
45
+ @actual = actual
46
+ policy.resolve_rule(actual) != rule
47
+ end
48
+
49
+ def supports_block_expectations?() ; false; end
50
+
51
+ def failure_message
52
+ "expected #{policy}##{actual} " \
53
+ "to be an alias of #{policy}##{rule}"
54
+ end
55
+
56
+ def failure_message_when_negated
57
+ "expected #{policy}##{actual} " \
58
+ "to not be an alias of #{policy}##{rule}"
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ RSpec.configure do |config|
65
+ config.include(Module.new do
66
+ def be_an_alias_of(policy, rule)
67
+ ActionPolicy::RSpec::BeAnAliasOf.new(policy, rule)
68
+ end
69
+ end)
70
+ end
@@ -8,16 +8,18 @@ module ActionPolicy
8
8
  using ActionPolicy::Ext::PolicyCacheKey
9
9
 
10
10
  # Returns policy instance for the record.
11
- def policy_for(record:, with: nil, namespace: authorization_namespace, context: authorization_context, allow_nil: false, default: default_authorization_policy_class)
11
+ def policy_for(record:, with: nil, namespace: authorization_namespace, context: nil, allow_nil: false, default: default_authorization_policy_class, strict_namespace: authorization_strict_namespace)
12
+ context = context ? authorization_context.merge(context) : authorization_context
13
+
12
14
  policy_class = with || ::ActionPolicy.lookup(
13
15
  record,
14
- namespace: namespace, context: context, allow_nil: allow_nil, default: default
16
+ namespace: namespace, context: context, allow_nil: allow_nil, default: default, strict_namespace: strict_namespace
15
17
  )
16
18
  policy_class&.new(record, **context)
17
19
  end
18
20
 
19
21
  def authorization_context
20
- raise NotImplementedError, "Please, define `authorization_context` method!"
22
+ Kernel.raise NotImplementedError, "Please, define `authorization_context` method!"
21
23
  end
22
24
 
23
25
  def authorization_namespace
@@ -28,6 +30,10 @@ module ActionPolicy
28
30
  # override to provide a policy class use when no policy found
29
31
  end
30
32
 
33
+ def authorization_strict_namespace
34
+ # override to provide strict namespace lookup option
35
+ end
36
+
31
37
  # Override this method to provide implicit authorization target
32
38
  # that would be used in case `record` is not specified in
33
39
  # `authorize!` and `allowed_to?` call.
@@ -39,7 +45,7 @@ module ActionPolicy
39
45
 
40
46
  # Return implicit authorization target or raises an exception if it's nil
41
47
  def implicit_authorization_target!
42
- implicit_authorization_target || raise(
48
+ implicit_authorization_target || Kernel.raise(
43
49
  NotFound,
44
50
  [
45
51
  self,
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionPolicy
4
+ module Ext
5
+ # Add Module#namespace method
6
+ module ModuleNamespace # :nodoc: all
7
+ unless "".respond_to?(:safe_constantize)
8
+ require "action_policy/ext/string_constantize"
9
+ using ActionPolicy::Ext::StringConstantize
10
+ end
11
+
12
+ module Ext
13
+ def namespace
14
+ return unless name&.match?(/[^^]::/)
15
+
16
+ name.sub(/::[^:]+$/, "").safe_constantize
17
+ end
18
+ end
19
+
20
+ # See https://github.com/jruby/jruby/issues/5220
21
+ ::Module.include(Ext) if RUBY_PLATFORM.match?(/java/i)
22
+
23
+ refine Module do
24
+ if RUBY_VERSION <= "2.7.0"
25
+ include Ext
26
+ else
27
+ ::RubyNext::Core.import_methods(Ext, binding)
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionPolicy
4
+ module Ext
5
+ # Adds #_policy_cache_key method to Object,
6
+ # which just call #policy_cache_key or #cache_key
7
+ # or #object_id (if `use_object_id` parameter is set to true).
8
+ #
9
+ # For other core classes returns string representation.
10
+ #
11
+ # Raises ArgumentError otherwise.
12
+ module PolicyCacheKey # :nodoc: all
13
+ module ObjectExt
14
+ def _policy_cache_key(use_object_id: false)
15
+ return policy_cache_key if respond_to?(:policy_cache_key)
16
+ return cache_key_with_version if respond_to?(:cache_key_with_version)
17
+ return cache_key if respond_to?(:cache_key)
18
+
19
+ return object_id.to_s if use_object_id == true
20
+
21
+ raise ArgumentError, "object is not cacheable"
22
+ end
23
+ end
24
+
25
+ refine Object do
26
+ ::RubyNext::Core.import_methods(ObjectExt, binding)
27
+ end
28
+
29
+ refine NilClass do
30
+ def _policy_cache_key(*) = ""
31
+ end
32
+
33
+ refine TrueClass do
34
+ def _policy_cache_key(*) = "t"
35
+ end
36
+
37
+ refine FalseClass do
38
+ def _policy_cache_key(*) = "f"
39
+ end
40
+
41
+ refine String do
42
+ def _policy_cache_key(*) = self
43
+ end
44
+
45
+ refine Symbol do
46
+ def _policy_cache_key(*) = to_s
47
+ end
48
+
49
+ if RUBY_PLATFORM.match?(/java/i)
50
+ refine Integer do
51
+ def _policy_cache_key(*) = to_s
52
+ end
53
+
54
+ refine Float do
55
+ def _policy_cache_key(*) = to_s
56
+ end
57
+ else
58
+ refine Numeric do
59
+ def _policy_cache_key(*) = to_s
60
+ end
61
+ end
62
+
63
+ refine Time do
64
+ def _policy_cache_key(*) = to_s
65
+ end
66
+
67
+ refine Module do
68
+ def _policy_cache_key(*) = name
69
+ end
70
+ end
71
+ end
72
+ end
@@ -58,10 +58,11 @@ module ActionPolicy
58
58
  end
59
59
 
60
60
  def authorization_context
61
- return @__authorization_context if
62
- instance_variable_defined?(:@__authorization_context)
61
+ @_authorization_context ||= build_authorization_context
62
+ end
63
63
 
64
- @__authorization_context = self.class.authorization_targets
64
+ def build_authorization_context
65
+ self.class.authorization_targets
65
66
  .each_with_object({}) do |(key, meth), obj|
66
67
  obj[key] = send(meth)
67
68
  end
@@ -73,13 +74,19 @@ module ActionPolicy
73
74
  policy.resolve_rule(rule)
74
75
  end
75
76
 
76
- def lookup_authorization_policy(record, **options) # :nodoc:
77
- record = implicit_authorization_target! if record == :__undef__
78
- raise ArgumentError, "Record must be specified" if record.nil?
77
+ def lookup_authorization_policy(record, with: nil, **options) # :nodoc:
78
+ if record == :__undef__
79
+ record =
80
+ if with
81
+ implicit_authorization_target
82
+ else
83
+ implicit_authorization_target!
84
+ end
85
+ end
79
86
 
80
- options[:context] && (options[:context] = authorization_context.merge(options[:context]))
87
+ Kernel.raise ArgumentError, "Record or policy must be specified" if record.nil? && with.nil?
81
88
 
82
- policy_for(record: record, **options)
89
+ policy_for(record: record, with: with, **options)
83
90
  end
84
91
 
85
92
  module ClassMethods # :nodoc:
@@ -8,16 +8,18 @@ module ActionPolicy
8
8
  using ActionPolicy::Ext::PolicyCacheKey
9
9
 
10
10
  # Returns policy instance for the record.
11
- def policy_for(record:, with: nil, namespace: authorization_namespace, context: authorization_context, allow_nil: false, default: default_authorization_policy_class)
11
+ def policy_for(record:, with: nil, namespace: authorization_namespace, context: nil, allow_nil: false, default: default_authorization_policy_class, strict_namespace: authorization_strict_namespace)
12
+ context = context ? authorization_context.merge(context) : authorization_context
13
+
12
14
  policy_class = with || ::ActionPolicy.lookup(
13
15
  record,
14
- namespace:, context:, allow_nil:, default:
16
+ namespace:, context:, allow_nil:, default:, strict_namespace:
15
17
  )
16
18
  policy_class&.new(record, **context)
17
19
  end
18
20
 
19
21
  def authorization_context
20
- raise NotImplementedError, "Please, define `authorization_context` method!"
22
+ Kernel.raise NotImplementedError, "Please, define `authorization_context` method!"
21
23
  end
22
24
 
23
25
  def authorization_namespace
@@ -28,6 +30,10 @@ module ActionPolicy
28
30
  # override to provide a policy class use when no policy found
29
31
  end
30
32
 
33
+ def authorization_strict_namespace
34
+ # override to provide strict namespace lookup option
35
+ end
36
+
31
37
  # Override this method to provide implicit authorization target
32
38
  # that would be used in case `record` is not specified in
33
39
  # `authorize!` and `allowed_to?` call.
@@ -39,7 +45,7 @@ module ActionPolicy
39
45
 
40
46
  # Return implicit authorization target or raises an exception if it's nil
41
47
  def implicit_authorization_target!
42
- implicit_authorization_target || raise(
48
+ implicit_authorization_target || Kernel.raise(
43
49
  NotFound,
44
50
  [
45
51
  self,
@@ -21,7 +21,11 @@ module ActionPolicy
21
21
  ::Module.include(Ext) if RUBY_PLATFORM.match?(/java/i)
22
22
 
23
23
  refine Module do
24
- include Ext
24
+ if RUBY_VERSION <= "2.7.0"
25
+ include Ext
26
+ else
27
+ import_methods Ext
28
+ end
25
29
  end
26
30
  end
27
31
  end
@@ -23,7 +23,7 @@ module ActionPolicy
23
23
  end
24
24
 
25
25
  refine Object do
26
- include ObjectExt
26
+ import_methods ObjectExt
27
27
  end
28
28
 
29
29
  refine NilClass do
@@ -67,12 +67,20 @@ module ActionPolicy
67
67
  def lookup_within_namespace(policy_name, namespace, strict: false)
68
68
  NamespaceCache.fetch(namespace&.name || "Kernel", policy_name, strict: strict) do
69
69
  mod = namespace
70
+ policy_class = nil
71
+
70
72
  loop do
71
- policy = [mod&.name, policy_name].compact.join("::").safe_constantize
72
- break policy if policy || mod.nil? || strict
73
+ policy_class = [mod&.name, policy_name].compact.join("::").safe_constantize
74
+ break policy_class if policy_class || mod.nil?
73
75
 
74
76
  mod = mod.namespace
75
77
  end
78
+
79
+ next policy_class if !strict || namespace.nil? || policy_class.nil?
80
+
81
+ # If we're in the strict mode and the namespace boundary is provided,
82
+ # we must check that the found policy satisfies it
83
+ policy_class if policy_class.name.start_with?("#{namespace.name}::")
76
84
  end
77
85
  end
78
86
 
@@ -66,7 +66,7 @@ module ActionPolicy
66
66
  allow_nil ||= optional
67
67
 
68
68
  ids.each do |id|
69
- authorization_targets[id] = {allow_nil, optional}
69
+ authorization_targets[id] = {allow_nil:, optional:}
70
70
  end
71
71
 
72
72
  attr_reader(*ids)
@@ -86,7 +86,7 @@ module ActionPolicy
86
86
  @result = self.class.result_class.new(self.class, rule)
87
87
 
88
88
  catch :policy_fulfilled do
89
- result.load __apply__(rule)
89
+ result.load __apply__(resolve_rule(rule))
90
90
  end
91
91
 
92
92
  result.value
@@ -133,13 +133,13 @@ module ActionPolicy
133
133
  end
134
134
 
135
135
  # An alias for readability purposes
136
- def check?(*args) = allowed_to?(*args)
136
+ def check?(*args, **hargs) = allowed_to?(*args, **hargs)
137
137
 
138
138
  # Returns a rule name (policy method name) for activity.
139
139
  #
140
140
  # By default, rule name is equal to activity name.
141
141
  #
142
- # Raises ActionPolicy::UknownRule when rule is not found in policy.
142
+ # Raises ActionPolicy::UnknownRule when rule is not found in policy.
143
143
  def resolve_rule(activity)
144
144
  raise UnknownRule.new(self, activity) unless
145
145
  respond_to?(activity)
@@ -31,6 +31,20 @@ module ActionPolicy
31
31
 
32
32
  def present?() = !empty?
33
33
 
34
+ def merge(other)
35
+ other.reasons.each do |policy_class, rules|
36
+ reasons[policy_class] ||= []
37
+
38
+ rules.each do |rule|
39
+ if rule.is_a?(::Hash)
40
+ add_detailed_reason(reasons[policy_class], rule)
41
+ else
42
+ add_non_detailed_reason(reasons[policy_class], rule)
43
+ end
44
+ end
45
+ end
46
+ end
47
+
34
48
  private
35
49
 
36
50
  def add_non_detailed_reason(store, rule)
@@ -182,7 +196,7 @@ module ActionPolicy
182
196
  result.details ||= {}
183
197
  end
184
198
 
185
- def allowed_to?(rule, record = :__undef__, **options)
199
+ def allowed_to?(rule, record = :__undef__, inline_reasons: false, **options)
186
200
  res =
187
201
  if (record == :__undef__ || record == self.record) && options.empty?
188
202
  rule = resolve_rule(rule)
@@ -196,7 +210,9 @@ module ActionPolicy
196
210
  policy.result
197
211
  end
198
212
 
199
- result&.reasons&.add(policy, rule, res.details) if res.fail?
213
+ if res.fail? && result&.reasons
214
+ inline_reasons ? result.reasons.merge(res.reasons) : result.reasons.add(policy, rule, res.details)
215
+ end
200
216
 
201
217
  res.clear_details
202
218
 
@@ -23,7 +23,10 @@ module ActionPolicy
23
23
  include ActionPolicy::Behaviours::Namespaced
24
24
 
25
25
  included do
26
- helper_method :allowed_to? if respond_to?(:helper_method)
26
+ if respond_to?(:helper_method)
27
+ helper_method :allowed_to?
28
+ helper_method :authorized_scope
29
+ end
27
30
 
28
31
  attr_writer :authorize_count
29
32
  attr_reader :verify_authorized_skipped
@@ -57,7 +60,7 @@ module ActionPolicy
57
60
  end
58
61
 
59
62
  def verify_authorized
60
- raise UnauthorizedAction.new(controller_path, action_name) if
63
+ Kernel.raise UnauthorizedAction.new(controller_path, action_name) if
61
64
  authorize_count.zero? && !verify_authorized_skipped
62
65
  end
63
66
 
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "action_policy/testing"
4
+
5
+ module ActionPolicy
6
+ module RSpec
7
+ # Policy rule alias matcher `be_an_alias_of`.
8
+ #
9
+ # Verifies that for given policy a policy rule has an alias.
10
+ #
11
+ # Example:
12
+ #
13
+ # # in policy specs
14
+ # subject(:policy) { described_class.new(record, user: user) }
15
+ #
16
+ # let(:user) { build_stubbed(:user) }
17
+ # let(:record) { build_stubbed(:post) }
18
+ #
19
+ # describe "#show?" do
20
+ # it "is an alias of :index? policy rule" do
21
+ # expect(:show?).to be_an_alias_of(policy, :index?)
22
+ # end
23
+ # end
24
+ #
25
+ # # negated version
26
+ # describe "#show?" do
27
+ # it "is not an alias of :index? policy rule" do
28
+ # expect(:show?).to_not be_an_alias_of(policy, :index?)
29
+ # end
30
+ # end
31
+ #
32
+ class BeAnAliasOf < ::RSpec::Matchers::BuiltIn::BaseMatcher
33
+ attr_reader :policy, :rule, :actual
34
+
35
+ def initialize(policy, rule)
36
+ @policy = policy
37
+ @rule = rule
38
+ end
39
+
40
+ def match(_expected, actual)
41
+ policy.resolve_rule(actual) == rule
42
+ end
43
+
44
+ def does_not_match?(actual)
45
+ @actual = actual
46
+ policy.resolve_rule(actual) != rule
47
+ end
48
+
49
+ def supports_block_expectations?() = false
50
+
51
+ def failure_message
52
+ "expected #{policy}##{actual} " \
53
+ "to be an alias of #{policy}##{rule}"
54
+ end
55
+
56
+ def failure_message_when_negated
57
+ "expected #{policy}##{actual} " \
58
+ "to not be an alias of #{policy}##{rule}"
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ RSpec.configure do |config|
65
+ config.include(Module.new do
66
+ def be_an_alias_of(policy, rule)
67
+ ActionPolicy::RSpec::BeAnAliasOf.new(policy, rule)
68
+ end
69
+ end)
70
+ end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "action_policy/rspec/be_an_alias_of"
3
4
  require "action_policy/rspec/be_authorized_to"
4
5
  require "action_policy/rspec/have_authorized_scope"
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActionPolicy
4
- VERSION = "0.5.7"
4
+ VERSION = "0.6.2"
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.5.7
4
+ version: 0.6.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vladimir Dementyev
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-03-04 00:00:00.000000000 Z
11
+ date: 2022-08-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ruby-next-core
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 0.11.0
19
+ version: 0.14.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: 0.11.0
26
+ version: 0.14.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: ammeter
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -133,9 +133,6 @@ files:
133
133
  - LICENSE.txt
134
134
  - README.md
135
135
  - config/rubocop-rspec.yml
136
- - lib/.rbnext/1995.next/action_policy/behaviours/policy_for.rb
137
- - lib/.rbnext/1995.next/action_policy/behaviours/scoping.rb
138
- - lib/.rbnext/1995.next/action_policy/policy/authorization.rb
139
136
  - lib/.rbnext/1995.next/action_policy/utils/pretty_print.rb
140
137
  - lib/.rbnext/2.7/action_policy/behaviours/policy_for.rb
141
138
  - lib/.rbnext/2.7/action_policy/i18n.rb
@@ -153,10 +150,16 @@ files:
153
150
  - lib/.rbnext/3.0/action_policy/policy/execution_result.rb
154
151
  - lib/.rbnext/3.0/action_policy/policy/pre_check.rb
155
152
  - lib/.rbnext/3.0/action_policy/policy/reasons.rb
153
+ - lib/.rbnext/3.0/action_policy/rspec/be_an_alias_of.rb
156
154
  - lib/.rbnext/3.0/action_policy/rspec/be_authorized_to.rb
157
155
  - lib/.rbnext/3.0/action_policy/rspec/have_authorized_scope.rb
158
156
  - lib/.rbnext/3.0/action_policy/utils/pretty_print.rb
159
157
  - lib/.rbnext/3.0/action_policy/utils/suggest_message.rb
158
+ - lib/.rbnext/3.1/action_policy/behaviours/policy_for.rb
159
+ - lib/.rbnext/3.1/action_policy/behaviours/scoping.rb
160
+ - lib/.rbnext/3.1/action_policy/ext/module_namespace.rb
161
+ - lib/.rbnext/3.1/action_policy/ext/policy_cache_key.rb
162
+ - lib/.rbnext/3.1/action_policy/policy/authorization.rb
160
163
  - lib/action_policy.rb
161
164
  - lib/action_policy/authorizer.rb
162
165
  - lib/action_policy/base.rb
@@ -194,6 +197,7 @@ files:
194
197
  - lib/action_policy/rails/scope_matchers/active_record.rb
195
198
  - lib/action_policy/railtie.rb
196
199
  - lib/action_policy/rspec.rb
200
+ - lib/action_policy/rspec/be_an_alias_of.rb
197
201
  - lib/action_policy/rspec/be_authorized_to.rb
198
202
  - lib/action_policy/rspec/dsl.rb
199
203
  - lib/action_policy/rspec/have_authorized_scope.rb
@@ -222,7 +226,7 @@ metadata:
222
226
  documentation_uri: https://actionpolicy.evilmartians.io/
223
227
  homepage_uri: https://actionpolicy.evilmartians.io/
224
228
  source_code_uri: http://github.com/palkan/action_policy
225
- post_install_message:
229
+ post_install_message:
226
230
  rdoc_options: []
227
231
  require_paths:
228
232
  - lib
@@ -230,15 +234,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
230
234
  requirements:
231
235
  - - ">="
232
236
  - !ruby/object:Gem::Version
233
- version: 2.5.0
237
+ version: 2.6.0
234
238
  required_rubygems_version: !ruby/object:Gem::Requirement
235
239
  requirements:
236
240
  - - ">="
237
241
  - !ruby/object:Gem::Version
238
242
  version: '0'
239
243
  requirements: []
240
- rubygems_version: 3.0.6
241
- signing_key:
244
+ rubygems_version: 3.3.11
245
+ signing_key:
242
246
  specification_version: 4
243
247
  summary: Authorization framework for Ruby/Rails application
244
248
  test_files: []