accessly 1.0.1 → 1.0.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
  SHA1:
3
- metadata.gz: 815f41738a2e4a9ff10656b7328cfcbce68ceb41
4
- data.tar.gz: 9920d928acb62e3a3f823f28a2322cd0a8dba174
3
+ metadata.gz: 96505b522e737820634c4c02ab4c861880008a70
4
+ data.tar.gz: eee707f0a8667c4e9f383e827197f64db141412f
5
5
  SHA512:
6
- metadata.gz: 120ca23c06d76c839e22860b01483c182e32ecb6ce1f67e8377fddeb46871d70ecb6acc85a474acb3061ecc6fc7e2bc9411b0ecba71fc20811d20429c126862b
7
- data.tar.gz: ed13d76b6ee3b35bed3f3ba0b10c330511b1f0e602e25651b4b530ce153f229a1cb81d4dfe7cdaca6cecf2fadd332390fbfda99dabf304093bf8027dc57d209e
6
+ metadata.gz: 504dd75a1ce9ae60c89dd3e335de18cb7ef46e45bc2a39d67e07407d94eec0daffbeedcf70ac8a045cdb26c5be2c584c3e756cbbca46757ac9826281b3ad00dd
7
+ data.tar.gz: 511e3129de53be6bb92c8519db2dd9fc29b392851337dfb38d0104116eb7d56cd0d85f4957465bfcb15b6817e1a602225b5a42e88df56d06aecaafc65ba81ae5
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- accessly (1.0.0)
4
+ accessly (1.0.2)
5
5
  activerecord (~> 5.0)
6
6
 
7
7
  GEM
data/README.md CHANGED
@@ -64,6 +64,18 @@ class ApplicationFeaturePolicy < Accessly::Policy::Base
64
64
  end
65
65
  ```
66
66
 
67
+ #### ACTIONS
68
+
69
+ Accessly policies rely on a definition of `actions` and/or `actions_on_objects` for effective use. This example uses `actions` which represent a "permission" that can be granted to an actor for later validation within your app's business logic.
70
+
71
+ - `actions` map a symbol to an integer value.
72
+ - An `action` value should be a unique integer within each policy.
73
+ - removing/editing `actions` and values can have negative consequences if the underlying data is not migrated
74
+
75
+ Defined policy `actions` become part of the policy API. (see examples below)
76
+
77
+ #### Policy API Example
78
+
67
79
  With this policy we can `grant` permissions to a user
68
80
 
69
81
  ```ruby
@@ -107,6 +119,18 @@ class UserPolicy < Accessly::Policy::Base
107
119
  end
108
120
  ```
109
121
 
122
+ #### ACTIONS ON OBJECTS
123
+
124
+ Accessly policies rely on a definition of `actions` and/or `actions_on_objects` for effective use. This example uses `actions_on_objects` which associate a "permission" with an object in your system. The "object" is typically an ActiveRecord object.
125
+
126
+ - `actions_on_objects` map a symbol to an integer value.
127
+ - An `actions_on_objects` value should be a unique integer within each policy.
128
+ - removing/editing `actions_on_objects` and values can have negative consequences if the underlying data is not migrated
129
+
130
+ Defined policy `actions_on_objects` become part of the policy API. (see examples below)
131
+
132
+ #### Policy API Example
133
+
110
134
  We differentiate permissions by a `namespace` which by default is the name of your policy class. However,
111
135
  it may be necessary to override the default behavior represented in the above example.
112
136
 
@@ -1,6 +1,10 @@
1
1
  module Accessly
2
2
  module Policy
3
3
  class Base
4
+ # Module that will hold our meta-programmed action methods just above
5
+ # the policy class in the inheritance hierarchy; allowing for them to
6
+ # be overridden in the policy.
7
+ ACTIONS_MODULE = :Actions
4
8
 
5
9
  attr_reader :actor
6
10
 
@@ -8,17 +12,82 @@ module Accessly
8
12
  @actor = actor
9
13
  end
10
14
 
15
+ # Meta-programs action methods from actions supplied.
16
+ # Used in policies as a DSL to declare actions.
17
+ #
18
+ # This defines the actions on the `actions_module` so that they are
19
+ # positioned higher in the inheritance tree than methods defined on the
20
+ # class itself. This will allow us to define methods that override these
21
+ # base methods and call `super`.
22
+ #
23
+ # @param actions [Hash] the actions to define on the policy
24
+ #
25
+ # @example Define Actions
26
+ #
27
+ # # This example causes the following methods to be defined:
28
+ # # some_action? : Returns true if the actor has the some_action
29
+ # # permission, false otherwise
30
+ # # flip_the_flop? : Returns true if the actor has the flip_the_flop
31
+ # # permission, false otherwise
32
+ # # create? : Returns true if the actor has the create permission, false
33
+ # # otherwise
34
+ # actions(
35
+ # some_action: 1,
36
+ # flip_the_flop: 2,
37
+ # create: 3
38
+ # )
39
+ #
40
+ # @return [Hash] actions
11
41
  def self.actions(actions)
12
42
  _actions.merge!(actions)
13
43
  actions.each do |action, action_id|
14
- _define_action_methods(action, action_id)
44
+ actions_module.module_eval do
45
+ define_method(:"#{action}?") do |*args|
46
+ _can_do_action?(action, action_id, args.first)
47
+ end
48
+ end
15
49
  end
16
50
  end
17
51
 
52
+ # Meta-programs action_on_objects methods from the actions supplied.
53
+ # Used in policies as a DSL to declare actions on objects.
54
+ # It is different from actions in that it will also define a method
55
+ # for listing all objects authorized with this action for the given
56
+ # actor and that these actions will always be associated not only with
57
+ # an actor, but with an object of the action.
58
+ #
59
+ # @param actions_on_objects [Hash] the actions on objects to define
60
+ # on the policy
61
+ #
62
+ # @example Define Actions On Objects
63
+ #
64
+ # # This example causes the following methods to be defined:
65
+ # # edit : Returns an ActiveRecord::Relation of the objects on which
66
+ # # the actor has the edit permission
67
+ # # edit?(object) : Returns true if the actor has the edit permission
68
+ # # on the given object, false otherwise
69
+ # # show : Returns an ActiveRecord::Relation of the objects on which
70
+ # # the actor has the show permission
71
+ # # show?(object) : Returns true if the actor has the show permission
72
+ # # on the given object, false otherwise
73
+ # actions_on_objects(
74
+ # edit: 1,
75
+ # show: 2
76
+ # )
77
+ #
78
+ # @return [Hash] actions_on_objects
18
79
  def self.actions_on_objects(actions_on_objects)
19
80
  _actions_on_objects.merge!(actions_on_objects)
20
81
  actions_on_objects.each do |action, action_id|
21
- _define_action_methods(action, action_id)
82
+ actions_module.module_eval do
83
+ define_method(:"#{action}?") do |*args|
84
+ _can_do_action?(action, action_id, args.first)
85
+ end
86
+
87
+ define_method(action) do |*args|
88
+ _list_for_action(action, action_id)
89
+ end
90
+ end
22
91
  end
23
92
  end
24
93
 
@@ -102,85 +171,31 @@ module Accessly
102
171
 
103
172
  private
104
173
 
105
- def _get_action_id(action, object_id = nil)
106
- if object_id.nil?
107
- _get_general_action_id!(action)
174
+ # Accessor for the actions module that will hold our meta-programmed
175
+ # methods.
176
+ # We put these methods in this module so that they are positioned
177
+ # above the policy in the inheritance chain. We can then override
178
+ # the methods in our policy as needed and call super to access the
179
+ # previous definition.
180
+ #
181
+ # @return [ACTIONS_MODULE] the module for holding actions currently
182
+ # defined on this class.
183
+ def self.actions_module
184
+ if const_defined?(ACTIONS_MODULE, _search_ancestors = false)
185
+ mod = const_get(ACTIONS_MODULE)
108
186
  else
109
- _get_action_on_object_id!(action)
187
+ mod = const_set(ACTIONS_MODULE, Module.new)
188
+ include mod
110
189
  end
111
- end
112
190
 
113
- # Determines whether the caller is trying to call an action method
114
- # in the format `action_name?`. If so, this calls that method with
115
- # the given arguments.
116
- def method_missing(method_name, *args)
117
- action_method_name = _resolve_action_method_name(method_name)
118
- if action_method_name.nil?
119
- super
120
- else
121
- send(action_method_name, *args)
122
- end
191
+ mod
123
192
  end
124
193
 
125
- # Parses an action name from a given method name of the format
126
- # `action_name?` or `action_name and returns the action method
127
- # or the list method name. If the method name does not follow
128
- # one of those formats, this assumes the caller is not calling
129
- # an action or list method and returns nil.
130
- def _resolve_action_method_name(method_name)
131
- action_method_match = /\A(\w+)(\??)\z/.match(method_name)
132
-
133
- return nil if action_method_match.nil? || action_method_match[1].nil?
134
-
135
- action_name = action_method_match[1].to_sym
136
- is_predicate = action_method_match[2] == "?"
137
-
138
- if !_action_defined?(action_name)
139
- nil
140
- elsif is_predicate
141
- _action_method_name(action_name)
194
+ def _get_action_id(action, object_id = nil)
195
+ if object_id.nil?
196
+ _get_general_action_id!(action)
142
197
  else
143
- _action_list_method_name(action_name)
144
- end
145
- end
146
-
147
- # The implementation for action methods follow the naming format
148
- # `_resolve_action_name`. This is to allow child Policies to override
149
- # the action method and still be able to call `super` when they
150
- # need to call the base implementation of the action method.
151
- def self._action_method_name(action_name)
152
- "_resolve_#{action_name}"
153
- end
154
-
155
- def _action_method_name(action_name)
156
- self.class._action_method_name(action_name)
157
- end
158
-
159
- # The implementation for list methods follow the naming format
160
- # `_list_action_name`. This is to allow child Policies to override
161
- # the list method and still be able to call `super` when they
162
- # need to call the base implementation of the lsit method.
163
- def self._action_list_method_name(action_name)
164
- "_list_#{action_name}"
165
- end
166
-
167
- def _action_list_method_name(action_name)
168
- self.class._action_list_method_name(action_name)
169
- end
170
-
171
- # Defines the action method on the Policy class for the given
172
- # action name.
173
- def self._define_action_methods(action, action_id)
174
- unless method_defined?(_action_method_name(action))
175
- define_method(_action_method_name(action)) do |*args|
176
- _can_do_action?(action, action_id, args.first)
177
- end
178
- end
179
-
180
- unless method_defined?(_action_list_method_name(action))
181
- define_method(_action_list_method_name(action)) do |*args|
182
- _list_for_action(action, action_id)
183
- end
198
+ _get_action_on_object_id!(action)
184
199
  end
185
200
  end
186
201
 
@@ -1,3 +1,3 @@
1
1
  module Accessly
2
- VERSION = "1.0.1"
2
+ VERSION = "1.0.2"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: accessly
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aaron Milam
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: exe
12
12
  cert_chain: []
13
- date: 2018-06-15 00:00:00.000000000 Z
13
+ date: 2018-10-12 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activerecord