action_policy 0.4.4 → 0.5.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (122) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +203 -174
  3. data/README.md +5 -4
  4. data/lib/.rbnext/2.7/action_policy/behaviours/policy_for.rb +62 -0
  5. data/lib/.rbnext/2.7/action_policy/i18n.rb +56 -0
  6. data/lib/.rbnext/2.7/action_policy/policy/cache.rb +101 -0
  7. data/lib/.rbnext/2.7/action_policy/policy/pre_check.rb +162 -0
  8. data/lib/.rbnext/2.7/action_policy/rspec/be_authorized_to.rb +89 -0
  9. data/lib/.rbnext/2.7/action_policy/rspec/have_authorized_scope.rb +124 -0
  10. data/lib/.rbnext/2.7/action_policy/utils/pretty_print.rb +159 -0
  11. data/lib/.rbnext/3.0/action_policy/behaviour.rb +115 -0
  12. data/lib/.rbnext/3.0/action_policy/behaviours/policy_for.rb +62 -0
  13. data/lib/.rbnext/3.0/action_policy/behaviours/scoping.rb +35 -0
  14. data/lib/.rbnext/3.0/action_policy/behaviours/thread_memoized.rb +59 -0
  15. data/lib/.rbnext/3.0/action_policy/ext/policy_cache_key.rb +72 -0
  16. data/lib/.rbnext/3.0/action_policy/policy/aliases.rb +69 -0
  17. data/lib/.rbnext/3.0/action_policy/policy/authorization.rb +87 -0
  18. data/lib/.rbnext/3.0/action_policy/policy/cache.rb +101 -0
  19. data/lib/.rbnext/3.0/action_policy/policy/core.rb +161 -0
  20. data/lib/.rbnext/3.0/action_policy/policy/defaults.rb +31 -0
  21. data/lib/.rbnext/3.0/action_policy/policy/execution_result.rb +37 -0
  22. data/lib/.rbnext/3.0/action_policy/policy/pre_check.rb +162 -0
  23. data/lib/.rbnext/3.0/action_policy/policy/reasons.rb +212 -0
  24. data/lib/.rbnext/3.0/action_policy/policy/scoping.rb +160 -0
  25. data/lib/.rbnext/3.0/action_policy/rspec/be_authorized_to.rb +89 -0
  26. data/lib/.rbnext/3.0/action_policy/rspec/have_authorized_scope.rb +124 -0
  27. data/lib/.rbnext/3.0/action_policy/utils/pretty_print.rb +159 -0
  28. data/lib/.rbnext/3.0/action_policy/utils/suggest_message.rb +19 -0
  29. data/lib/action_policy.rb +7 -1
  30. data/lib/action_policy/behaviour.rb +22 -16
  31. data/lib/action_policy/behaviours/policy_for.rb +10 -3
  32. data/lib/action_policy/behaviours/scoping.rb +2 -1
  33. data/lib/action_policy/behaviours/thread_memoized.rb +1 -3
  34. data/lib/action_policy/ext/module_namespace.rb +1 -6
  35. data/lib/action_policy/ext/policy_cache_key.rb +10 -30
  36. data/lib/action_policy/i18n.rb +1 -1
  37. data/lib/action_policy/lookup_chain.rb +29 -15
  38. data/lib/action_policy/policy/aliases.rb +7 -12
  39. data/lib/action_policy/policy/authorization.rb +8 -7
  40. data/lib/action_policy/policy/cache.rb +11 -17
  41. data/lib/action_policy/policy/core.rb +25 -12
  42. data/lib/action_policy/policy/defaults.rb +3 -9
  43. data/lib/action_policy/policy/execution_result.rb +3 -9
  44. data/lib/action_policy/policy/pre_check.rb +19 -58
  45. data/lib/action_policy/policy/reasons.rb +31 -19
  46. data/lib/action_policy/policy/scoping.rb +5 -6
  47. data/lib/action_policy/rails/controller.rb +6 -1
  48. data/lib/action_policy/rails/policy/instrumentation.rb +1 -1
  49. data/lib/action_policy/rspec/be_authorized_to.rb +5 -9
  50. data/lib/action_policy/rspec/dsl.rb +1 -1
  51. data/lib/action_policy/rspec/have_authorized_scope.rb +5 -7
  52. data/lib/action_policy/utils/pretty_print.rb +21 -24
  53. data/lib/action_policy/utils/suggest_message.rb +1 -3
  54. data/lib/action_policy/version.rb +1 -1
  55. data/lib/generators/action_policy/install/templates/{application_policy.rb → application_policy.rb.tt} +0 -0
  56. data/lib/generators/action_policy/policy/policy_generator.rb +4 -1
  57. data/lib/generators/action_policy/policy/templates/{policy.rb → policy.rb.tt} +0 -0
  58. data/lib/generators/rspec/templates/{policy_spec.rb → policy_spec.rb.tt} +0 -0
  59. data/lib/generators/test_unit/templates/{policy_test.rb → policy_test.rb.tt} +0 -0
  60. metadata +54 -119
  61. data/.gitattributes +0 -2
  62. data/.github/ISSUE_TEMPLATE.md +0 -21
  63. data/.github/PULL_REQUEST_TEMPLATE.md +0 -29
  64. data/.github/bug_report_template.rb +0 -175
  65. data/.gitignore +0 -15
  66. data/.rubocop.yml +0 -54
  67. data/.tidelift.yml +0 -6
  68. data/.travis.yml +0 -31
  69. data/Gemfile +0 -22
  70. data/Rakefile +0 -27
  71. data/action_policy.gemspec +0 -44
  72. data/benchmarks/namespaced_lookup_cache.rb +0 -74
  73. data/benchmarks/pre_checks.rb +0 -73
  74. data/bin/console +0 -14
  75. data/bin/setup +0 -8
  76. data/docs/.nojekyll +0 -0
  77. data/docs/CNAME +0 -1
  78. data/docs/README.md +0 -79
  79. data/docs/_sidebar.md +0 -27
  80. data/docs/aliases.md +0 -122
  81. data/docs/assets/docsify-search.js +0 -364
  82. data/docs/assets/docsify.min.js +0 -3
  83. data/docs/assets/fonts/FiraCode-Medium.woff +0 -0
  84. data/docs/assets/fonts/FiraCode-Regular.woff +0 -0
  85. data/docs/assets/images/banner.png +0 -0
  86. data/docs/assets/images/cache.png +0 -0
  87. data/docs/assets/images/cache.svg +0 -70
  88. data/docs/assets/images/layer.png +0 -0
  89. data/docs/assets/images/layer.svg +0 -35
  90. data/docs/assets/prism-ruby.min.js +0 -1
  91. data/docs/assets/styles.css +0 -347
  92. data/docs/assets/vue.min.css +0 -1
  93. data/docs/authorization_context.md +0 -92
  94. data/docs/behaviour.md +0 -113
  95. data/docs/caching.md +0 -291
  96. data/docs/controller_action_aliases.md +0 -109
  97. data/docs/custom_lookup_chain.md +0 -48
  98. data/docs/custom_policy.md +0 -53
  99. data/docs/debugging.md +0 -55
  100. data/docs/decorators.md +0 -27
  101. data/docs/favicon.ico +0 -0
  102. data/docs/graphql.md +0 -302
  103. data/docs/i18n.md +0 -44
  104. data/docs/index.html +0 -43
  105. data/docs/instrumentation.md +0 -84
  106. data/docs/lookup_chain.md +0 -22
  107. data/docs/namespaces.md +0 -77
  108. data/docs/non_rails.md +0 -28
  109. data/docs/pre_checks.md +0 -57
  110. data/docs/pundit_migration.md +0 -80
  111. data/docs/quick_start.md +0 -118
  112. data/docs/rails.md +0 -120
  113. data/docs/reasons.md +0 -120
  114. data/docs/scoping.md +0 -255
  115. data/docs/testing.md +0 -390
  116. data/docs/writing_policies.md +0 -107
  117. data/gemfiles/jruby.gemfile +0 -8
  118. data/gemfiles/rails42.gemfile +0 -9
  119. data/gemfiles/rails6.gemfile +0 -8
  120. data/gemfiles/railsmaster.gemfile +0 -6
  121. data/lib/action_policy/ext/string_match.rb +0 -14
  122. data/lib/action_policy/ext/yield_self_then.rb +0 -25
@@ -8,8 +8,11 @@ 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)
12
- policy_class = with || ::ActionPolicy.lookup(record, namespace: namespace, context: context, allow_nil: allow_nil)
11
+ def policy_for(record:, with: nil, namespace: authorization_namespace, context: authorization_context, allow_nil: false, default: default_authorization_policy_class)
12
+ policy_class = with || ::ActionPolicy.lookup(
13
+ record,
14
+ **{namespace, context, allow_nil, default}
15
+ )
13
16
  policy_class&.new(record, **context)
14
17
  end
15
18
 
@@ -21,6 +24,10 @@ module ActionPolicy
21
24
  # override to provide specific authorization namespace
22
25
  end
23
26
 
27
+ def default_authorization_policy_class
28
+ # override to provide a policy class use when no policy found
29
+ end
30
+
24
31
  # Override this method to provide implicit authorization target
25
32
  # that would be used in case `record` is not specified in
26
33
  # `authorize!` and `allowed_to?` call.
@@ -46,7 +53,7 @@ module ActionPolicy
46
53
 
47
54
  def policy_for_cache_key(record:, with: nil, namespace: nil, context: authorization_context, **)
48
55
  record_key = record._policy_cache_key(use_object_id: true)
49
- context_key = context.values.map { |v| v._policy_cache_key(use_object_id: true) }.join(".")
56
+ context_key = context.values.map { _1._policy_cache_key(use_object_id: true) }.join(".")
50
57
 
51
58
  "#{namespace}/#{with}/#{context_key}/#{record_key}"
52
59
  end
@@ -17,8 +17,9 @@ module ActionPolicy
17
17
  policy ||= policy_for(record: implicit_authorization_target!, **options)
18
18
 
19
19
  type ||= authorization_scope_type_for(policy, target)
20
+ name = as
20
21
 
21
- Authorizer.scopify(target, policy, type: type, name: as, scope_options: scope_options)
22
+ Authorizer.scopify(target, policy, **{type, name, scope_options})
22
23
  end
23
24
 
24
25
  # For backward compatibility
@@ -7,9 +7,7 @@ module ActionPolicy
7
7
  class << self
8
8
  attr_writer :enabled
9
9
 
10
- def enabled?
11
- @enabled == true
12
- end
10
+ def enabled?() = @enabled == true
13
11
 
14
12
  def fetch(key)
15
13
  return yield unless enabled?
@@ -9,11 +9,6 @@ module ActionPolicy
9
9
  using ActionPolicy::Ext::StringConstantize
10
10
  end
11
11
 
12
- unless "".respond_to?(:match?)
13
- require "action_policy/ext/string_match"
14
- using ActionPolicy::Ext::StringMatch
15
- end
16
-
17
12
  module Ext
18
13
  def namespace
19
14
  return unless name&.match?(/[^^]::/)
@@ -23,7 +18,7 @@ module ActionPolicy
23
18
  end
24
19
 
25
20
  # See https://github.com/jruby/jruby/issues/5220
26
- ::Module.include(Ext) if RUBY_PLATFORM =~ /java/i
21
+ ::Module.include(Ext) if RUBY_PLATFORM.match?(/java/i)
27
22
 
28
23
  refine Module do
29
24
  include Ext
@@ -27,65 +27,45 @@ module ActionPolicy
27
27
  end
28
28
 
29
29
  refine NilClass do
30
- def _policy_cache_key(*)
31
- ""
32
- end
30
+ def _policy_cache_key(*) = ""
33
31
  end
34
32
 
35
33
  refine TrueClass do
36
- def _policy_cache_key(*)
37
- "t"
38
- end
34
+ def _policy_cache_key(*) = "t"
39
35
  end
40
36
 
41
37
  refine FalseClass do
42
- def _policy_cache_key(*)
43
- "f"
44
- end
38
+ def _policy_cache_key(*) = "f"
45
39
  end
46
40
 
47
41
  refine String do
48
- def _policy_cache_key(*)
49
- self
50
- end
42
+ def _policy_cache_key(*) = self
51
43
  end
52
44
 
53
45
  refine Symbol do
54
- def _policy_cache_key(*)
55
- to_s
56
- end
46
+ def _policy_cache_key(*) = to_s
57
47
  end
58
48
 
59
49
  if RUBY_PLATFORM.match?(/java/i)
60
50
  refine Integer do
61
- def _policy_cache_key(*)
62
- to_s
63
- end
51
+ def _policy_cache_key(*) = to_s
64
52
  end
65
53
 
66
54
  refine Float do
67
- def _policy_cache_key(*)
68
- to_s
69
- end
55
+ def _policy_cache_key(*) = to_s
70
56
  end
71
57
  else
72
58
  refine Numeric do
73
- def _policy_cache_key(*)
74
- to_s
75
- end
59
+ def _policy_cache_key(*) = to_s
76
60
  end
77
61
  end
78
62
 
79
63
  refine Time do
80
- def _policy_cache_key(*)
81
- to_s
82
- end
64
+ def _policy_cache_key(*) = to_s
83
65
  end
84
66
 
85
67
  refine Module do
86
- def _policy_cache_key(*)
87
- name
88
- end
68
+ def _policy_cache_key(*) = name
89
69
  end
90
70
  end
91
71
  end
@@ -21,7 +21,7 @@ module ActionPolicy
21
21
  private
22
22
 
23
23
  def candidates_for(policy_class, rule)
24
- policy_hierarchy = policy_class.ancestors.select { |klass| klass.respond_to?(:identifier) }
24
+ policy_hierarchy = policy_class.ancestors.select { _1.respond_to?(:identifier) }
25
25
  [
26
26
  *policy_hierarchy.map { |klass| :"policy.#{klass.identifier}.#{rule}" },
27
27
  :"policy.#{rule}",
@@ -24,14 +24,26 @@ module ActionPolicy
24
24
  class << self
25
25
  attr_reader :store
26
26
 
27
- def fetch(namespace, policy)
27
+ def put_if_absent(scope, namespace, policy)
28
+ local_store = store[scope][namespace]
29
+ return local_store[policy] if local_store[policy]
30
+ local_store[policy] ||= yield
31
+ end
32
+
33
+ def fetch(namespace, policy, strict:, &block)
28
34
  return yield unless LookupChain.namespace_cache_enabled?
29
- return store[namespace][policy] if store[namespace].key?(policy)
30
- store[namespace][policy] ||= yield
35
+
36
+ if strict
37
+ put_if_absent(:strict, namespace, policy, &block)
38
+ else
39
+ cached_policy = put_if_absent(:strict, namespace, policy, &block)
40
+ put_if_absent(:flexible, namespace, policy) { cached_policy }
41
+ end
31
42
  end
32
43
 
33
44
  def clear
34
- @store = Hash.new { |h, k| h[k] = {} }
45
+ hash = Hash.new { |h, k| h[k] = {} }
46
+ @store = {strict: hash, flexible: hash.clone}
35
47
  end
36
48
  end
37
49
 
@@ -53,23 +65,23 @@ module ActionPolicy
53
65
 
54
66
  private
55
67
 
56
- def lookup_within_namespace(policy_name, namespace)
68
+ def lookup_within_namespace(policy_name, namespace, strict: false)
57
69
  return unless namespace
58
- NamespaceCache.fetch(namespace.name, policy_name) do
70
+ NamespaceCache.fetch(namespace.name, policy_name, strict: strict) do
59
71
  mod = namespace
60
-
61
72
  loop do
62
73
  policy = "#{mod.name}::#{policy_name}".safe_constantize
63
-
64
74
  break policy unless policy.nil?
65
-
66
75
  mod = mod.namespace
67
-
68
- break if mod.nil?
76
+ break if mod.nil? || strict
69
77
  end
70
78
  end
71
79
  end
72
80
 
81
+ def objectify_policy(policy_name, strict: false)
82
+ policy_name.safe_constantize unless strict
83
+ end
84
+
73
85
  def policy_class_name_for(record)
74
86
  return record.policy_name.to_s if record.respond_to?(:policy_name)
75
87
 
@@ -112,19 +124,21 @@ module ActionPolicy
112
124
  }
113
125
 
114
126
  # Infer from passed symbol
115
- SYMBOL_LOOKUP = ->(record, namespace: nil, **) {
127
+ SYMBOL_LOOKUP = ->(record, namespace: nil, strict_namespace: false, **) {
116
128
  next unless record.is_a?(Symbol)
117
129
 
118
130
  policy_name = "#{record.camelize}Policy"
119
- lookup_within_namespace(policy_name, namespace) || policy_name.safe_constantize
131
+ lookup_within_namespace(policy_name, namespace, strict: strict_namespace) ||
132
+ objectify_policy(policy_name, strict: strict_namespace)
120
133
  }
121
134
 
122
135
  # (Optional) Infer using String#classify if available
123
- CLASSIFY_SYMBOL_LOOKUP = ->(record, namespace: nil, **) {
136
+ CLASSIFY_SYMBOL_LOOKUP = ->(record, namespace: nil, strict_namespace: false, **) {
124
137
  next unless record.is_a?(Symbol)
125
138
 
126
139
  policy_name = "#{record.to_s.classify}Policy"
127
- lookup_within_namespace(policy_name, namespace) || policy_name.safe_constantize
140
+ lookup_within_namespace(policy_name, namespace, strict: strict_namespace) ||
141
+ objectify_policy(policy_name, strict: strict_namespace)
128
142
  }
129
143
 
130
144
  self.chain = [
@@ -46,23 +46,18 @@ module ActionPolicy
46
46
  end
47
47
  end
48
48
 
49
- def lookup_alias(rule)
50
- rules_aliases[rule]
51
- end
49
+ def lookup_alias(rule) = rules_aliases[rule]
52
50
 
53
- def lookup_default_rule
54
- rules_aliases[DEFAULT]
55
- end
51
+ def lookup_default_rule() = rules_aliases[DEFAULT]
56
52
 
57
53
  def rules_aliases
58
54
  return @rules_aliases if instance_variable_defined?(:@rules_aliases)
59
55
 
60
- @rules_aliases =
61
- if superclass.respond_to?(:rules_aliases)
62
- superclass.rules_aliases.dup
63
- else
64
- {}
65
- end
56
+ if superclass.respond_to?(:rules_aliases)
57
+ superclass.rules_aliases.dup
58
+ else
59
+ {}
60
+ end => @rules_aliases
66
61
  end
67
62
 
68
63
  def method_added(name)
@@ -63,8 +63,10 @@ module ActionPolicy
63
63
 
64
64
  module ClassMethods # :nodoc:
65
65
  def authorize(*ids, allow_nil: false, optional: false)
66
+ allow_nil ||= optional
67
+
66
68
  ids.each do |id|
67
- authorization_targets[id] = {allow_nil: allow_nil || optional, optional: optional}
69
+ authorization_targets[id] = {allow_nil, optional}
68
70
  end
69
71
 
70
72
  attr_reader(*ids)
@@ -73,12 +75,11 @@ module ActionPolicy
73
75
  def authorization_targets
74
76
  return @authorization_targets if instance_variable_defined?(:@authorization_targets)
75
77
 
76
- @authorization_targets =
77
- if superclass.respond_to?(:authorization_targets)
78
- superclass.authorization_targets.dup
79
- else
80
- {}
81
- end
78
+ if superclass.respond_to?(:authorization_targets)
79
+ superclass.authorization_targets.dup
80
+ else
81
+ {}
82
+ end => @authorization_targets
82
83
  end
83
84
  end
84
85
  end
@@ -3,16 +3,13 @@
3
3
  require "action_policy/version"
4
4
 
5
5
  module ActionPolicy # :nodoc:
6
+ using RubyNext
7
+
6
8
  # By default cache namespace (or prefix) contains major and minor version of the gem
7
9
  CACHE_NAMESPACE = "acp:#{ActionPolicy::VERSION.split(".").take(2).join(".")}"
8
10
 
9
- require "action_policy/ext/yield_self_then"
10
11
  require "action_policy/ext/policy_cache_key"
11
12
 
12
- unless "".respond_to?(:then)
13
- require "action_policy/ext/yield_self_then"
14
- using ActionPolicy::Ext::YieldSelfThen
15
- end
16
13
  using ActionPolicy::Ext::PolicyCacheKey
17
14
 
18
15
  module Policy
@@ -26,15 +23,13 @@ module ActionPolicy # :nodoc:
26
23
  end
27
24
  end
28
25
 
29
- def cache_namespace
30
- ActionPolicy::CACHE_NAMESPACE
31
- end
26
+ def cache_namespace() = ActionPolicy::CACHE_NAMESPACE
32
27
 
33
28
  def cache_key(*parts)
34
29
  [
35
30
  cache_namespace,
36
31
  *parts
37
- ].map { |part| part._policy_cache_key }.join("/")
32
+ ].map { _1._policy_cache_key }.join("/")
38
33
  end
39
34
 
40
35
  def rule_cache_key(rule)
@@ -47,7 +42,7 @@ module ActionPolicy # :nodoc:
47
42
  end
48
43
 
49
44
  def context_cache_key
50
- authorization_context.map { |_k, v| v._policy_cache_key.to_s }.join("/")
45
+ authorization_context.map { _2._policy_cache_key.to_s }.join("/")
51
46
  end
52
47
 
53
48
  def apply_with_cache(rule)
@@ -79,7 +74,7 @@ module ActionPolicy # :nodoc:
79
74
  res = store.read(key)
80
75
  next res unless res.nil?
81
76
  res = yield
82
- store.write(key, res, **options)
77
+ store.write(key, res, options)
83
78
  res
84
79
  end
85
80
  end
@@ -94,12 +89,11 @@ module ActionPolicy # :nodoc:
94
89
  def cached_rules
95
90
  return @cached_rules if instance_variable_defined?(:@cached_rules)
96
91
 
97
- @cached_rules =
98
- if superclass.respond_to?(:cached_rules)
99
- superclass.cached_rules.dup
100
- else
101
- {}
102
- end
92
+ if superclass.respond_to?(:cached_rules)
93
+ superclass.cached_rules.dup
94
+ else
95
+ {}
96
+ end => @cached_rules
103
97
  end
104
98
  end
105
99
  end
@@ -11,6 +11,8 @@ unless "".respond_to?(:underscore)
11
11
  end
12
12
 
13
13
  module ActionPolicy
14
+ using RubyNext
15
+
14
16
  # Raised when `resolve_rule` failed to find an approriate
15
17
  # policy rule method for the activity
16
18
  class UnknownRule < Error
@@ -75,15 +77,28 @@ module ActionPolicy
75
77
  # `apply` also calls pre-checks.
76
78
  def apply(rule)
77
79
  @result = self.class.result_class.new(self.class, rule)
78
- @result.load __apply__(rule)
80
+
81
+ catch :policy_fulfilled do
82
+ result.load __apply__(rule)
83
+ end
84
+
85
+ result.value
86
+ end
87
+
88
+ def deny!
89
+ result&.load false
90
+ throw :policy_fulfilled
91
+ end
92
+
93
+ def allow!
94
+ result&.load true
95
+ throw :policy_fulfilled
79
96
  end
80
97
 
81
98
  # This method performs the rule call.
82
99
  # Override or extend it to provide custom functionality
83
100
  # (such as caching, pre checks, etc.)
84
- def __apply__(rule)
85
- public_send(rule)
86
- end
101
+ def __apply__(rule) = public_send(rule)
87
102
 
88
103
  # Wrap code that could modify result
89
104
  # to prevent the current result modification
@@ -102,16 +117,16 @@ module ActionPolicy
102
117
  # If record is `nil` then we uses the current policy.
103
118
  def allowed_to?(rule, record = :__undef__, **options)
104
119
  if (record == :__undef__ || record == self.record) && options.empty?
105
- __apply__(rule)
120
+ __apply__(resolve_rule(rule))
106
121
  else
107
- policy_for(record: record, **options).apply(rule)
122
+ policy_for(record: record, **options).then do |policy|
123
+ policy.apply(policy.resolve_rule(rule))
124
+ end
108
125
  end
109
126
  end
110
127
 
111
128
  # An alias for readability purposes
112
- def check?(*args)
113
- allowed_to?(*args)
114
- end
129
+ def check?(*args) = allowed_to?(*args)
115
130
 
116
131
  # Returns a rule name (policy method name) for activity.
117
132
  #
@@ -127,9 +142,7 @@ module ActionPolicy
127
142
  # Return annotated source code for the rule
128
143
  # NOTE: require "method_source" and "unparser" gems to be installed.
129
144
  # Otherwise returns empty string.
130
- def inspect_rule(rule)
131
- PrettyPrint.print_method(self, rule)
132
- end
145
+ def inspect_rule(rule) = PrettyPrint.print_method(self, rule)
133
146
 
134
147
  # Helper for printing the annotated rule source.
135
148
  # Useful for debugging: type `pp :show?` within the context of the policy