action_policy 0.4.0 → 0.5.0

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.
Files changed (100) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +233 -171
  3. data/LICENSE.txt +1 -1
  4. data/README.md +7 -11
  5. data/lib/action_policy.rb +7 -1
  6. data/lib/action_policy/behaviour.rb +22 -16
  7. data/lib/action_policy/behaviours/policy_for.rb +10 -3
  8. data/lib/action_policy/behaviours/scoping.rb +2 -1
  9. data/lib/action_policy/behaviours/thread_memoized.rb +1 -3
  10. data/lib/action_policy/ext/module_namespace.rb +1 -6
  11. data/lib/action_policy/ext/policy_cache_key.rb +15 -33
  12. data/lib/action_policy/ext/{symbol_classify.rb → symbol_camelize.rb} +6 -6
  13. data/lib/action_policy/i18n.rb +1 -1
  14. data/lib/action_policy/lookup_chain.rb +41 -21
  15. data/lib/action_policy/policy/aliases.rb +7 -12
  16. data/lib/action_policy/policy/authorization.rb +14 -17
  17. data/lib/action_policy/policy/cache.rb +34 -18
  18. data/lib/action_policy/policy/core.rb +25 -12
  19. data/lib/action_policy/policy/defaults.rb +3 -9
  20. data/lib/action_policy/policy/execution_result.rb +3 -9
  21. data/lib/action_policy/policy/pre_check.rb +19 -58
  22. data/lib/action_policy/policy/reasons.rb +30 -20
  23. data/lib/action_policy/policy/scoping.rb +5 -6
  24. data/lib/action_policy/rails/controller.rb +6 -1
  25. data/lib/action_policy/rails/ext/active_record.rb +7 -0
  26. data/lib/action_policy/rails/policy/instrumentation.rb +1 -1
  27. data/lib/action_policy/rspec/be_authorized_to.rb +5 -9
  28. data/lib/action_policy/rspec/dsl.rb +3 -3
  29. data/lib/action_policy/rspec/have_authorized_scope.rb +5 -7
  30. data/lib/action_policy/testing.rb +1 -1
  31. data/lib/action_policy/utils/pretty_print.rb +21 -24
  32. data/lib/action_policy/utils/suggest_message.rb +1 -3
  33. data/lib/action_policy/version.rb +1 -1
  34. data/lib/generators/action_policy/install/templates/{application_policy.rb → application_policy.rb.tt} +1 -1
  35. data/lib/generators/action_policy/policy/policy_generator.rb +4 -1
  36. data/lib/generators/action_policy/policy/templates/{policy.rb → policy.rb.tt} +0 -0
  37. data/lib/generators/rspec/templates/{policy_spec.rb → policy_spec.rb.tt} +0 -0
  38. data/lib/generators/test_unit/templates/{policy_test.rb → policy_test.rb.tt} +0 -0
  39. metadata +30 -119
  40. data/.gitattributes +0 -2
  41. data/.github/FUNDING.yml +0 -1
  42. data/.github/ISSUE_TEMPLATE.md +0 -18
  43. data/.github/PULL_REQUEST_TEMPLATE.md +0 -29
  44. data/.gitignore +0 -15
  45. data/.rubocop.yml +0 -54
  46. data/.tidelift.yml +0 -6
  47. data/.travis.yml +0 -31
  48. data/Gemfile +0 -22
  49. data/Rakefile +0 -27
  50. data/action_policy.gemspec +0 -44
  51. data/benchmarks/namespaced_lookup_cache.rb +0 -71
  52. data/bin/console +0 -14
  53. data/bin/setup +0 -8
  54. data/docs/.nojekyll +0 -0
  55. data/docs/CNAME +0 -1
  56. data/docs/README.md +0 -77
  57. data/docs/_sidebar.md +0 -27
  58. data/docs/aliases.md +0 -122
  59. data/docs/assets/docsify-search.js +0 -364
  60. data/docs/assets/docsify.min.js +0 -3
  61. data/docs/assets/fonts/FiraCode-Medium.woff +0 -0
  62. data/docs/assets/fonts/FiraCode-Regular.woff +0 -0
  63. data/docs/assets/images/banner.png +0 -0
  64. data/docs/assets/images/cache.png +0 -0
  65. data/docs/assets/images/cache.svg +0 -70
  66. data/docs/assets/images/layer.png +0 -0
  67. data/docs/assets/images/layer.svg +0 -35
  68. data/docs/assets/prism-ruby.min.js +0 -1
  69. data/docs/assets/styles.css +0 -347
  70. data/docs/assets/vue.min.css +0 -1
  71. data/docs/authorization_context.md +0 -92
  72. data/docs/behaviour.md +0 -113
  73. data/docs/caching.md +0 -273
  74. data/docs/controller_action_aliases.md +0 -109
  75. data/docs/custom_lookup_chain.md +0 -48
  76. data/docs/custom_policy.md +0 -53
  77. data/docs/debugging.md +0 -55
  78. data/docs/decorators.md +0 -27
  79. data/docs/favicon.ico +0 -0
  80. data/docs/graphql.md +0 -302
  81. data/docs/i18n.md +0 -44
  82. data/docs/index.html +0 -43
  83. data/docs/instrumentation.md +0 -84
  84. data/docs/lookup_chain.md +0 -17
  85. data/docs/namespaces.md +0 -77
  86. data/docs/non_rails.md +0 -28
  87. data/docs/pre_checks.md +0 -57
  88. data/docs/pundit_migration.md +0 -80
  89. data/docs/quick_start.md +0 -118
  90. data/docs/rails.md +0 -120
  91. data/docs/reasons.md +0 -120
  92. data/docs/scoping.md +0 -255
  93. data/docs/testing.md +0 -333
  94. data/docs/writing_policies.md +0 -107
  95. data/gemfiles/jruby.gemfile +0 -8
  96. data/gemfiles/rails42.gemfile +0 -8
  97. data/gemfiles/rails6.gemfile +0 -8
  98. data/gemfiles/railsmaster.gemfile +0 -6
  99. data/lib/action_policy/ext/string_match.rb +0 -14
  100. data/lib/action_policy/ext/yield_self_then.rb +0 -25
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2018-2019 Vladimir Dementyev
3
+ Copyright (c) 2018-2020 Vladimir Dementyev
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -1,8 +1,9 @@
1
1
  [![Gem Version](https://badge.fury.io/rb/action_policy.svg)](https://badge.fury.io/rb/action_policy)
2
- [![Build Status](https://travis-ci.org/palkan/action_policy.svg?branch=master)](https://travis-ci.org/palkan/action_policy)
2
+ ![Build](https://github.com/palkan/action_policy/workflows/Build/badge.svg)
3
+ ![JRuby Build](https://github.com/palkan/action_policy/workflows/JRuby%20Build/badge.svg)
3
4
  [![Documentation](https://img.shields.io/badge/docs-link-brightgreen.svg)](https://actionpolicy.evilmartians.io)
4
5
 
5
- # ActionPolicy
6
+ # Action Policy
6
7
 
7
8
  Authorization framework for Ruby and Rails applications.
8
9
 
@@ -21,7 +22,6 @@ Composable. Extensible. Performant.
21
22
 
22
23
  - 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))
23
24
 
24
-
25
25
  ## Integrations
26
26
 
27
27
  - GraphQL Ruby ([`action_policy-graphql`](https://github.com/palkan/action_policy-graphql))
@@ -36,7 +36,9 @@ gem "action_policy", "~> 0.4.0"
36
36
 
37
37
  And then execute:
38
38
 
39
- $ bundle
39
+ ```sh
40
+ bundle install
41
+ ```
40
42
 
41
43
  ## Usage
42
44
 
@@ -89,7 +91,6 @@ end
89
91
 
90
92
  \* See [Non-Rails Usage](docs/non_rails.md) on how to add `authorize!` to any Ruby project.
91
93
 
92
-
93
94
  When authorization is successful (i.e., the corresponding rule returns `true`), nothing happens, but in case of authorization failure `ActionPolicy::Unauthorized` error is raised.
94
95
 
95
96
  There is also an `allowed_to?` method which returns `true` or `false`, and could be used, in views, for example:
@@ -98,7 +99,7 @@ There is also an `allowed_to?` method which returns `true` or `false`, and could
98
99
  <% @posts.each do |post| %>
99
100
  <li><%= post.title %>
100
101
  <% if allowed_to?(:edit?, post) %>
101
- = link_to post, "Edit"
102
+ <%= link_to post, "Edit">
102
103
  <% end %>
103
104
  </li>
104
105
  <% end %>
@@ -121,8 +122,3 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/palkan
121
122
  The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
122
123
 
123
124
  [Documentation]: http://actionpolicy.evilmartians.io
124
-
125
- ## Security Contact
126
-
127
- To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security). Tidelift will coordinate the fix and disclosure.
128
-
@@ -1,5 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "ruby-next"
4
+
5
+ require "ruby-next/language/setup"
6
+ RubyNext::Language.setup_gem_load_path(transpile: true)
7
+
3
8
  # ActionPolicy is an authorization framework for Ruby/Rails applications.
4
9
  #
5
10
  # It provides a way to write access policies and helpers to check these policies
@@ -30,8 +35,9 @@ module ActionPolicy
30
35
  attr_accessor :cache_store
31
36
 
32
37
  # Find a policy class for a target
33
- def lookup(target, allow_nil: false, **options)
38
+ def lookup(target, allow_nil: false, default: nil, **options)
34
39
  LookupChain.call(target, **options) ||
40
+ default ||
35
41
  (allow_nil ? nil : raise(NotFound, target))
36
42
  end
37
43
  end
@@ -35,12 +35,7 @@ module ActionPolicy
35
35
  #
36
36
  # Raises `ActionPolicy::Unauthorized` if check failed.
37
37
  def authorize!(record = :__undef__, to:, **options)
38
- record = implicit_authorization_target! if record == :__undef__
39
- raise ArgumentError, "Record must be specified" if record.nil?
40
-
41
- options[:context] && (options[:context] = authorization_context.merge(options[:context]))
42
-
43
- policy = policy_for(record: record, **options)
38
+ policy = lookup_authorization_policy(record, **options)
44
39
 
45
40
  Authorizer.call(policy, authorization_rule_for(policy, to))
46
41
  end
@@ -49,14 +44,17 @@ module ActionPolicy
49
44
  #
50
45
  # Returns true of false.
51
46
  def allowed_to?(rule, record = :__undef__, **options)
52
- record = implicit_authorization_target! if record == :__undef__
53
- raise ArgumentError, "Record must be specified" if record.nil?
47
+ policy = lookup_authorization_policy(record, **options)
54
48
 
55
- options[:context] && (options[:context] = authorization_context.merge(options[:context]))
49
+ policy.apply(authorization_rule_for(policy, rule))
50
+ end
56
51
 
57
- policy = policy_for(record: record, **options)
52
+ # Returns the authorization result object after applying a specified rule to a record.
53
+ def allowance_to(rule, record = :__undef__, **options)
54
+ policy = lookup_authorization_policy(record, **options)
58
55
 
59
56
  policy.apply(authorization_rule_for(policy, rule))
57
+ policy.result
60
58
  end
61
59
 
62
60
  def authorization_context
@@ -75,6 +73,15 @@ module ActionPolicy
75
73
  policy.resolve_rule(rule)
76
74
  end
77
75
 
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?
79
+
80
+ options[:context] && (options[:context] = authorization_context.merge(options[:context]))
81
+
82
+ policy_for(record: record, **options)
83
+ end
84
+
78
85
  module ClassMethods # :nodoc:
79
86
  # Configure authorization context.
80
87
  #
@@ -97,12 +104,11 @@ module ActionPolicy
97
104
  def authorization_targets
98
105
  return @authorization_targets if instance_variable_defined?(:@authorization_targets)
99
106
 
100
- @authorization_targets =
101
- if superclass.respond_to?(:authorization_targets)
102
- superclass.authorization_targets.dup
103
- else
104
- {}
105
- end
107
+ if superclass.respond_to?(:authorization_targets)
108
+ superclass.authorization_targets.dup
109
+ else
110
+ {}
111
+ end => @authorization_targets
106
112
  end
107
113
  end
108
114
  end
@@ -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
@@ -13,77 +13,59 @@ 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
32
28
 
33
29
  refine NilClass do
34
- def _policy_cache_key(*)
35
- ""
36
- end
30
+ def _policy_cache_key(*) = ""
37
31
  end
38
32
 
39
33
  refine TrueClass do
40
- def _policy_cache_key(*)
41
- "t"
42
- end
34
+ def _policy_cache_key(*) = "t"
43
35
  end
44
36
 
45
37
  refine FalseClass do
46
- def _policy_cache_key(*)
47
- "f"
48
- end
38
+ def _policy_cache_key(*) = "f"
49
39
  end
50
40
 
51
41
  refine String do
52
- def _policy_cache_key(*)
53
- self
54
- end
42
+ def _policy_cache_key(*) = self
55
43
  end
56
44
 
57
45
  refine Symbol do
58
- def _policy_cache_key(*)
59
- to_s
60
- end
46
+ def _policy_cache_key(*) = to_s
61
47
  end
62
48
 
63
49
  if RUBY_PLATFORM.match?(/java/i)
64
50
  refine Integer do
65
- def _policy_cache_key(*)
66
- to_s
67
- end
51
+ def _policy_cache_key(*) = to_s
68
52
  end
69
53
 
70
54
  refine Float do
71
- def _policy_cache_key(*)
72
- to_s
73
- end
55
+ def _policy_cache_key(*) = to_s
74
56
  end
75
57
  else
76
58
  refine Numeric do
77
- def _policy_cache_key(*)
78
- to_s
79
- end
59
+ def _policy_cache_key(*) = to_s
80
60
  end
81
61
  end
82
62
 
83
63
  refine Time do
84
- def _policy_cache_key(*)
85
- to_s
86
- end
64
+ def _policy_cache_key(*) = to_s
65
+ end
66
+
67
+ refine Module do
68
+ def _policy_cache_key(*) = name
87
69
  end
88
70
  end
89
71
  end
@@ -2,15 +2,15 @@
2
2
 
3
3
  module ActionPolicy
4
4
  module Ext
5
- # Add `classify` to Symbol
6
- module SymbolClassify
5
+ # Add `camelize` to Symbol
6
+ module SymbolCamelize
7
7
  refine Symbol do
8
- if "".respond_to?(:classify)
9
- def classify
10
- to_s.classify
8
+ if "".respond_to?(:camelize)
9
+ def camelize
10
+ to_s.camelize
11
11
  end
12
12
  else
13
- def classify
13
+ def camelize
14
14
  word = to_s.capitalize
15
15
  word.gsub!(/(?:_)([a-z\d]*)/) { $1.capitalize }
16
16
  word
@@ -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}",
@@ -12,8 +12,8 @@ module ActionPolicy
12
12
  using ActionPolicy::Ext::StringConstantize
13
13
  end
14
14
 
15
- require "action_policy/ext/symbol_classify"
16
- using ActionPolicy::Ext::SymbolClassify
15
+ require "action_policy/ext/symbol_camelize"
16
+ using ActionPolicy::Ext::SymbolCamelize
17
17
 
18
18
  require "action_policy/ext/module_namespace"
19
19
  using ActionPolicy::Ext::ModuleNamespace
@@ -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,22 +65,23 @@ module ActionPolicy
53
65
 
54
66
  private
55
67
 
56
- def lookup_within_namespace(policy_name, namespace)
57
- NamespaceCache.fetch(namespace.name, policy_name) do
68
+ def lookup_within_namespace(policy_name, namespace, strict: false)
69
+ return unless namespace
70
+ NamespaceCache.fetch(namespace.name, policy_name, strict: strict) do
58
71
  mod = namespace
59
-
60
72
  loop do
61
73
  policy = "#{mod.name}::#{policy_name}".safe_constantize
62
-
63
74
  break policy unless policy.nil?
64
-
65
75
  mod = mod.namespace
66
-
67
- break if mod.nil?
76
+ break if mod.nil? || strict
68
77
  end
69
78
  end
70
79
  end
71
80
 
81
+ def objectify_policy(policy_name, strict: false)
82
+ policy_name.safe_constantize unless strict
83
+ end
84
+
72
85
  def policy_class_name_for(record)
73
86
  return record.policy_name.to_s if record.respond_to?(:policy_name)
74
87
 
@@ -111,23 +124,30 @@ module ActionPolicy
111
124
  }
112
125
 
113
126
  # Infer from passed symbol
114
- SYMBOL_LOOKUP = ->(record, namespace: nil, **) {
127
+ SYMBOL_LOOKUP = ->(record, namespace: nil, strict_namespace: false, **) {
115
128
  next unless record.is_a?(Symbol)
116
129
 
117
- policy_name = "#{record.classify}Policy"
118
- if namespace.nil?
119
- policy_name.safe_constantize
120
- else
121
- lookup_within_namespace(policy_name, namespace)
122
- end
130
+ policy_name = "#{record.camelize}Policy"
131
+ lookup_within_namespace(policy_name, namespace, strict: strict_namespace) ||
132
+ objectify_policy(policy_name, strict: strict_namespace)
133
+ }
134
+
135
+ # (Optional) Infer using String#classify if available
136
+ CLASSIFY_SYMBOL_LOOKUP = ->(record, namespace: nil, strict_namespace: false, **) {
137
+ next unless record.is_a?(Symbol)
138
+
139
+ policy_name = "#{record.to_s.classify}Policy"
140
+ lookup_within_namespace(policy_name, namespace, strict: strict_namespace) ||
141
+ objectify_policy(policy_name, strict: strict_namespace)
123
142
  }
124
143
 
125
144
  self.chain = [
126
145
  SYMBOL_LOOKUP,
146
+ (CLASSIFY_SYMBOL_LOOKUP if String.method_defined?(:classify)),
127
147
  INSTANCE_POLICY_CLASS,
128
148
  CLASS_POLICY_CLASS,
129
149
  NAMESPACE_LOOKUP,
130
150
  INFER_FROM_CLASS
131
- ]
151
+ ].compact
132
152
  end
133
153
  end