action_policy 0.2.0 → 0.2.2

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: a261b126090b29222039294b84b4575fa796bf115033c2514fe301e0ec3d34af
4
- data.tar.gz: 05f9d1cd84b2f4ce2b951267e0122fb54a436a5e51f0dd2f9a7d79e06f30d6ec
3
+ metadata.gz: 5aa63f362aa8606be7d0ba77e1c78be9ed8c811eb57b06c38835ec4df24c9bf1
4
+ data.tar.gz: 9c704fda90d4735827eddfb78be38a6f2ea2a293f71165df1955cdd78a3e803d
5
5
  SHA512:
6
- metadata.gz: 80bbbe6ec61bf7241fe0e823844120292e6b8efd98a3c06834d98ac797cf92ebdffa21332e40305770622d5120d391443d0570193001a5e358f1ee6056c09bbb
7
- data.tar.gz: 7f6990f7bcd5998eef536640a451591801afd073e29b25a764c500b1d0f535e3cfaf1a176f0adf930f498386ca9f206aa377b4c5adf1b2d119be642681fa7e81
6
+ metadata.gz: a0cd9a62e04d0dbf1263d5c30ba0e860930877e63dd5c3c93f0be327e204b5e66265b8d9ce264278e9b16bef1bff7924331c66f11f88012c0a26dca445ad51bd
7
+ data.tar.gz: eb7b6b92a10869ab50f71656b446d26199d339891f5336ca28f87918cfd8dad467498e0262011703662944f82c49927e0c26dc24c871c456813ca87397cc4786
@@ -1,5 +1,16 @@
1
1
  ## master
2
2
 
3
+ ## 0.2.2 (2018-07-01)
4
+
5
+ - [Fix [#29](https://github.com/palkan/action_policy/issues/29)] Fix loading cache middleware. ([@palkan][])
6
+
7
+ - Use `send` instead of `public_send` to get the `authorization_context` so that contexts such as
8
+ `current_user` can be `private` in the controller. ([@brendon][])
9
+
10
+ - Fix railtie initialisation for Rails < 5. ([@brendon][])
11
+
12
+ ## 0.2.1 (yanked)
13
+
3
14
  ## 0.2.0 (2018-06-17)
4
15
 
5
16
  - Make `action_policy` JRuby-compatible. ([@palkan][])
@@ -45,3 +56,4 @@
45
56
 
46
57
  [@palkan]: https://github.com/palkan
47
58
  [@ilyasgaraev]: https://github.com/ilyasgaraev
59
+ [@brendon]: https://github.com/brendon
@@ -29,4 +29,5 @@ Gem::Specification.new do |spec|
29
29
  spec.add_development_dependency "rspec", "~> 3.3"
30
30
  spec.add_development_dependency "rubocop", "~> 0.56.0"
31
31
  spec.add_development_dependency "rubocop-md", "~> 0.2"
32
+ spec.add_development_dependency "benchmark-ips", "~> 2.7.0"
32
33
  end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ =begin
4
+
5
+ This benchmark measures the efficiency of NamespaceCache.
6
+
7
+ Run it multiple times with cache on/off to see the results:
8
+
9
+ $ bundle exec ruby namespaced_lookup_cache.rb
10
+ $ bundle exec ruby namespaced_lookup_cache.rb
11
+ $ NO_CACHE=1 bundle exec ruby namespaced_lookup_cache.rb
12
+ $ NO_CACHE=1 bundle exec ruby namespaced_lookup_cache.rb
13
+
14
+ =end
15
+
16
+ $LOAD_PATH.unshift File.expand_path("../lib", __dir__)
17
+
18
+ require "action_policy"
19
+ require "benchmark/ips"
20
+
21
+ GC.disable
22
+
23
+ class A; end
24
+
25
+ class B; end
26
+
27
+ module X
28
+ class BPolicy < ActionPolicy::Base; end
29
+
30
+ module Y
31
+ module Z
32
+ class APolicy < ActionPolicy::Base; end
33
+ end
34
+ end
35
+ end
36
+
37
+ a = A.new
38
+ b = B.new
39
+
40
+ if ENV['NO_CACHE']
41
+ ActionPolicy::LookupChain.namespace_cache_enabled = false
42
+ end
43
+
44
+ Benchmark.ips do |x|
45
+ x.warmup = 0
46
+
47
+ x.report("cache A") do
48
+ ActionPolicy.lookup(a, namespace: X::Y::Z)
49
+ end
50
+
51
+ x.report("cache B") do
52
+ ActionPolicy.lookup(b, namespace: X::Y::Z)
53
+ end
54
+
55
+ x.report("no cache A") do
56
+ ActionPolicy.lookup(a, namespace: X::Y::Z)
57
+ end
58
+
59
+ x.report("no cache B") do
60
+ ActionPolicy.lookup(b, namespace: X::Y::Z)
61
+ end
62
+
63
+ x.hold! 'temp_results'
64
+
65
+ x.compare!
66
+ end
67
+
68
+ =begin
69
+
70
+ Comparison:
71
+ cache B: 178577.4 i/s
72
+ cache A: 173061.4 i/s - same-ish: difference falls within error
73
+ no cache A: 97991.7 i/s - same-ish: difference falls within error
74
+ no cache B: 42505.4 i/s - 4.20x slower
75
+ =end
@@ -14,6 +14,8 @@
14
14
  * [Failure Reasons](reasons.md)
15
15
  * [Instrumentation](instrumentation.md)
16
16
  * [I18n Support](i18n.md)
17
+ * Tips & Tricks
18
+ * [Controller Action Aliases](controller_action_aliases.md)
17
19
  * Customize
18
20
  * [Base Policy](custom_policy.md)
19
21
  * [Lookup Chain](custom_lookup_chain.md)
@@ -0,0 +1,109 @@
1
+ # Controller Action Aliases
2
+
3
+ **This is a feature proposed here: https://github.com/palkan/action_policy/issues/25**
4
+
5
+ If you'd like to see this feature implemented, please comment on the issue to show your support.
6
+
7
+ ## Outline
8
+
9
+ Say you have abstracted your `authorize!` call to a controller superclass because your policy can
10
+ be executed without regard to the record in any of the subclass controllers:
11
+
12
+ ```ruby
13
+ class AbstractController < ApplicationController
14
+ authorize :context
15
+ before_action :authorize_context
16
+
17
+ def context
18
+ # Some code to get your policy context
19
+ end
20
+
21
+ private
22
+
23
+ def authorize_context
24
+ authorize! Context
25
+ end
26
+ end
27
+ ```
28
+
29
+ Your policy might look like this:
30
+
31
+ ```ruby
32
+ class ContextPolicy < ApplicationPolicy
33
+ authorize :context
34
+
35
+ alias_rule :index?, :show?, to: :view?
36
+ alias_rule :new?, :create?, :update?, :destroy?, to: :edit?
37
+
38
+ def view?
39
+ context.has_permission_to(:view, user)
40
+ end
41
+
42
+ def edit?
43
+ context.has_permission_to(:edit, user)
44
+ end
45
+ end
46
+ ```
47
+
48
+ We can safely add aliases for the common REST actions in the policy.
49
+
50
+ You may then want to include a concern in your subclass controller(s) that add extra actions to the controller.
51
+
52
+
53
+ ```ruby
54
+ class ConcreteController < AbstractController
55
+ include AdditionalFunctionalityConcern
56
+
57
+ def index
58
+ # Index Action
59
+ end
60
+
61
+ def new
62
+ # New Action
63
+ end
64
+
65
+ # etc...
66
+ end
67
+ ```
68
+
69
+ At this point you may be wondering how to tell your abstracted policy that these new methods map to either
70
+ the `view?` or `edit?` rule. You can currently provide the rule to execute to the `authorize!` method with
71
+ the `to:` parameter but since our call to `authorize!` is in a superclass it has no idea about our concern.
72
+ I propose the following controller method:
73
+
74
+ ```ruby
75
+ alias_action(*actions, to_rule: rule)
76
+ ```
77
+
78
+ Here's an example:
79
+
80
+ ```ruby
81
+ module AdditionalFunctionalityConcern
82
+ extend ActiveSupport::Concern
83
+
84
+ included do
85
+ alias_action [:first_action, :second_action], to_rule: :view?
86
+ alias_action [:third_action], to_rule: :edit?
87
+ end
88
+
89
+ def first_action
90
+ # First Action
91
+ end
92
+
93
+ def second_action
94
+ # Second Action
95
+ end
96
+
97
+ def third_action
98
+ # Third Action
99
+ end
100
+ end
101
+ ```
102
+
103
+ When `authorize!` is called in a controller, it will first check the action aliases for a corresponding
104
+ rule. If one is found, it will execute that rule instead of a rule matching the name of the current action.
105
+ The rule may point at a concrete rule in the policy, or a rule alias in the policy, it doens't matter, the
106
+ alias in the policy will be resolved like normal.
107
+
108
+ If you'd like to see this feature implemented, please show your support on the
109
+ [Github Issue](https://github.com/palkan/action_policy/issues/25).
@@ -29,7 +29,8 @@
29
29
  repo: 'https://github.com/palkan/action_policy',
30
30
  loadSidebar: true,
31
31
  subMaxLevel: 3,
32
- ga: 'UA-104346673-3'
32
+ ga: 'UA-104346673-3',
33
+ auto2top:true
33
34
  }
34
35
  </script>
35
36
  <script src="assets/docsify.min.js"></script>
@@ -4,7 +4,7 @@ Action Policy tries to automatically infer policy class from the target using th
4
4
 
5
5
  1. If the target responds to `policy_class`, then use it;
6
6
  2. If the target's class responds to `policy_class`, then use it;
7
- 3. If the target's class responds to `policy_name`, then use `#{target.class.policy_name}Policy`;
7
+ 3. If the target's class responds to `policy_name`, then use it (the `policy_name` should end with `Policy` as it's not appended automatically);
8
8
  4. Otherwise, use `#{target.class.name}Policy`.
9
9
 
10
10
  > \* [Namespaces](namespaces.md) could be also be considered when `namespace` option is set.
@@ -12,7 +12,7 @@ You can turn off this behaviour by setting `config.action_policy.controller_auth
12
12
 
13
13
  ```ruby
14
14
  class ApplicationController < ActionController::Base
15
- authorize :my_current_user, as: :user
15
+ authorize :user, through: :my_current_user
16
16
  end
17
17
  ```
18
18
 
@@ -71,7 +71,7 @@ class ApiController < ApplicationController::API
71
71
  include ActionPolicy::Controller
72
72
 
73
73
  # NOTE: you have to provide authorization context manually as well
74
- authorize :current_user, as: :user
74
+ authorize :user, through: :current_user
75
75
  end
76
76
  ```
77
77
 
@@ -50,7 +50,7 @@ module ActionPolicy
50
50
 
51
51
  @__authorization_context = self.class.authorization_targets
52
52
  .each_with_object({}) do |(key, meth), obj|
53
- obj[key] = public_send(meth)
53
+ obj[key] = send(meth)
54
54
  end
55
55
  end
56
56
 
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionPolicy # :nodoc:
4
+ class CacheMiddleware # :nodoc:
5
+ def initialize(app)
6
+ @app = app
7
+ end
8
+
9
+ def call(env)
10
+ ActionPolicy::PerThreadCache.clear_all
11
+ result = @app.call(env)
12
+ ActionPolicy::PerThreadCache.clear_all
13
+
14
+ result
15
+ end
16
+ end
17
+ end
@@ -15,12 +15,14 @@ module ActionPolicy
15
15
  require "action_policy/ext/module_namespace"
16
16
  using ActionPolicy::Ext::ModuleNamespace
17
17
 
18
- # Cache namespace resolving result for policies
18
+ # Cache namespace resolving result for policies.
19
+ # @see benchmarks/namespaced_lookup_cache.rb
19
20
  class NamespaceCache
20
21
  class << self
21
22
  attr_reader :store
22
23
 
23
24
  def fetch(namespace, policy)
25
+ return yield unless LookupChain.namespace_cache_enabled
24
26
  return store[namespace][policy] if store[namespace].key?(policy)
25
27
  store[namespace][policy] ||= yield
26
28
  end
@@ -34,7 +36,7 @@ module ActionPolicy
34
36
  end
35
37
 
36
38
  class << self
37
- attr_accessor :chain
39
+ attr_accessor :chain, :namespace_cache_enabled
38
40
 
39
41
  def call(record, **opts)
40
42
  chain.each do |probe|
@@ -74,6 +76,9 @@ module ActionPolicy
74
76
  end
75
77
  end
76
78
 
79
+ # Enable namespace cache by default
80
+ self.namespace_cache_enabled = true
81
+
77
82
  # By self `policy_class` method
78
83
  INSTANCE_POLICY_CLASS = ->(record, _) {
79
84
  record.policy_class if record.respond_to?(:policy_class)
@@ -45,8 +45,13 @@ module ActionPolicy # :nodoc:
45
45
  config.action_policy = Config
46
46
 
47
47
  initializer "action_policy.clear_per_thread_cache" do |app|
48
- app.executor.to_run { ActionPolicy::PerThreadCache.clear_all }
49
- app.executor.to_complete { ActionPolicy::PerThreadCache.clear_all }
48
+ if Rails::VERSION::MAJOR >= 5
49
+ app.executor.to_run { ActionPolicy::PerThreadCache.clear_all }
50
+ app.executor.to_complete { ActionPolicy::PerThreadCache.clear_all }
51
+ else
52
+ require "action_policy/cache_middleware"
53
+ app_middleware.use ActionPolicy::CacheMiddleware
54
+ end
50
55
  end
51
56
 
52
57
  config.to_prepare do |_app|
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActionPolicy
4
- VERSION = "0.2.0"
4
+ VERSION = "0.2.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.2.0
4
+ version: 0.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vladimir Dementyev
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-06-17 00:00:00.000000000 Z
11
+ date: 2018-07-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -94,6 +94,20 @@ dependencies:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0.2'
97
+ - !ruby/object:Gem::Dependency
98
+ name: benchmark-ips
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: 2.7.0
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: 2.7.0
97
111
  description: Authorization framework for Ruby/Rails application
98
112
  email:
99
113
  - dementiev.vm@gmail.com
@@ -113,6 +127,7 @@ files:
113
127
  - README.md
114
128
  - Rakefile
115
129
  - action_policy.gemspec
130
+ - benchmarks/namespaced_lookup_cache.rb
116
131
  - bin/console
117
132
  - bin/setup
118
133
  - docs/.nojekyll
@@ -133,6 +148,7 @@ files:
133
148
  - docs/assets/vue.min.css
134
149
  - docs/authorization_context.md
135
150
  - docs/caching.md
151
+ - docs/controller_action_aliases.md
136
152
  - docs/custom_lookup_chain.md
137
153
  - docs/custom_policy.md
138
154
  - docs/favicon.ico
@@ -159,6 +175,7 @@ files:
159
175
  - lib/action_policy/behaviours/namespaced.rb
160
176
  - lib/action_policy/behaviours/policy_for.rb
161
177
  - lib/action_policy/behaviours/thread_memoized.rb
178
+ - lib/action_policy/cache_middleware.rb
162
179
  - lib/action_policy/ext/module_namespace.rb
163
180
  - lib/action_policy/ext/policy_cache_key.rb
164
181
  - lib/action_policy/ext/string_constantize.rb