bullet_train-super_load_and_authorize_resource 1.3.20 → 1.3.21

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: 3c1f74640f9b22c14a96e442cd220b5ff94fa04fa7a2eddf05679ebb00bead63
4
- data.tar.gz: cd7d9bf7680e40cc617267229144c14ecb7c3da3e21746ad412fd5c18157d96b
3
+ metadata.gz: 74d2505967e4685338d190a363efe41ab12213b60ff86c614149ed1b6d1f5177
4
+ data.tar.gz: 6397d26bffb654078abb9e9d5adde60a318c51d0edcbc782c0a4fe831340be8f
5
5
  SHA512:
6
- metadata.gz: eae3fd324a9c2ae90ab34494ad8d2ced46d6defc6f6f7f0eecc11a89c572e7ed0994e39e8afba14484daa537b66e06ed18f817c51240e9c4375516acec1058ad
7
- data.tar.gz: c615535e8f45bbf2c2ae56d0a85c8398f0eb9a7818b2b5ee4d489935a55dd68679d1220c6e7f8c4e90b170dfe0c07f7e0035c19f21b113b5e4247adb8d92814c
6
+ metadata.gz: a211809ddaed548a8f6c0c2859d18062824cbfe3ee4c8914f889e1731fdfaa07deb03160b042c063ce5d9b193fc6c4b248c74300f04c91af084a30a8a1df0040
7
+ data.tar.gz: 226e486e4a3c01068869f8680ec5438d63559cd0394bbadd79adb4f14d2331d039e554aed8174dcbfaf67eea43ab258719be3c6eb1dfa2f8ecd15d8608e0d454
@@ -0,0 +1,229 @@
1
+ module BulletTrain::LoadsAndAuthorizesResource
2
+ extend ActiveSupport::Concern
3
+
4
+ class_methods do
5
+ def model_namespace_from_controller_namespace
6
+ controller_class_name =
7
+ if regex_to_remove_controller_namespace
8
+ name.gsub(regex_to_remove_controller_namespace, "")
9
+ else
10
+ name
11
+ end
12
+ namespace = controller_class_name.split("::")
13
+ # Remove "::ThingsController"
14
+ namespace.pop
15
+ namespace
16
+ end
17
+
18
+ # this is one of the few pieces of 'magical' functionality that bullet train implements
19
+ # for you in your controllers beyond that is provided by the underlying gems that we've
20
+ # tied together. we've taken the liberty of doing this because it's heavily based on
21
+ # cancancan's `load_and_authorize_resource` method, which is awesome, but it also
22
+ # implements a lot of the options required to make that method work very well for our
23
+ # controllers in the account namespace, including our shallow nested routes.
24
+ #
25
+ # there are also some complications that were introduced into this method by our support
26
+ # for namespaced models and controllers. (we introduced this complexity in support of
27
+ # namespacing our `Oauth::` models and controllers.)
28
+ #
29
+ # to help you understand the code below, usually `through` is `team`
30
+ # and `model` is something like `project`.
31
+ def account_load_and_authorize_resource(model, options, old_options = {})
32
+ # options are now required, because you have to have at least a 'through' setting.
33
+
34
+ # we used to support calling this method with a signature like this:
35
+ #
36
+ # `account_load_and_authorize_resource [:oauth, :twitter_account], :team`
37
+ #
38
+ # however this abstraction was too short-sighted so we've updated this method to accept the exact same method
39
+ # signature as cancancan's original `load_and_authorize_resource` method.
40
+ if model.is_a?(Array)
41
+ raise "Bullet Train has depreciated this method of calling `account_load_and_authorize_resource`. Read the comments on this line of source for more details."
42
+ end
43
+
44
+ # this is providing backward compatibility for folks who are calling this method like this:
45
+ # account_load_and_authorize_resource :thing, through: :team, through_association: :scaffolding_things
46
+ # i'm going to deprecate this at some point.
47
+ if options.is_a?(Hash)
48
+ through = options[:through]
49
+ options.delete(:through)
50
+ else
51
+ through = options
52
+ options = old_options
53
+ end
54
+
55
+ # fetch the namespace of the controller. this should generally match the namespace of the model, except for the
56
+ # `account` part.
57
+ namespace = model_namespace_from_controller_namespace
58
+
59
+ tried = []
60
+ begin
61
+ # check whether the parent exists in the model namespace.
62
+ model_class_name = (namespace + [model.to_s.classify]).join("::")
63
+ model_class_name.constantize
64
+ rescue NameError
65
+ tried << model_class_name
66
+ if namespace.any?
67
+ namespace.pop
68
+ retry
69
+ else
70
+ raise "Oh no, it looks like your call to 'account_load_and_authorize_resource' is broken. We tried #{tried.join(" and ")}, but didn't find a valid class name."
71
+ end
72
+ end
73
+
74
+ # treat through as an array even if the user only specified one parent type.
75
+ through_as_symbols = through.is_a?(Array) ? through : [through]
76
+
77
+ through = []
78
+ through_class_names = []
79
+
80
+ through_as_symbols.each do |through_as_symbol|
81
+ # reflect on the belongs_to association of the child model to figure out the class names of the parents.
82
+ unless (
83
+ association =
84
+ model_class_name.constantize.reflect_on_association(
85
+ through_as_symbol
86
+ )
87
+ )
88
+ raise "Oh no, it looks like your call to 'account_load_and_authorize_resource' is broken. Tried to reflect on the `#{through_as_symbol}` association of #{model_class_name}, but didn't find one."
89
+ end
90
+
91
+ through_class_name = association.klass.name
92
+
93
+ begin
94
+ through << through_class_name.constantize
95
+ through_class_names << through_class_name
96
+ rescue NameError
97
+ raise "Oh no, it looks like your call to 'account_load_and_authorize_resource' is broken. We tried to load `#{through_class_name}}` (the class name defined for the `#{through_as_symbol}` association), but couldn't find it."
98
+ end
99
+ end
100
+
101
+ if through_as_symbols.count > 1 && !options[:polymorphic]
102
+ raise "When a resource can be loaded through multiple parents, please specify the 'polymorphic' option to tell us what that controller calls the parent, e.g. `polymorphic: :imageable`."
103
+ end
104
+
105
+ # this provides the support we need for shallow nested resources, which
106
+ # helps keep our routes tidy even after many levels of nesting. most people
107
+ # i talk to don't actually know about this feature in rails, but it's
108
+ # actually the recommended approach in the rails routing documentation.
109
+ #
110
+ # also, similar to `load_and_authorize_resource`, people can pass in additional
111
+ # actions for which the resource should be loaded, but because we're making
112
+ # separate calls to `load_and_authorize_resource` for member and collection
113
+ # actions, we ask controllers to specify these actions separately, e.g.:
114
+ # `account_load_and_authorize_resource :invitation, :team, member_actions: [:accept, :promote]`
115
+ collection_actions = options[:collection_actions] || []
116
+ member_actions = options[:member_actions] || []
117
+
118
+ # this option is native to cancancan and allows you to skip account_load_and_authorize_resource
119
+ # for a specific action that would otherwise run it (e.g. see invitations#show.)
120
+ except_actions = options[:except] || []
121
+
122
+ collection_actions =
123
+ (%i[index new create reorder] + collection_actions) - except_actions
124
+ member_actions =
125
+ (%i[show edit update destroy] + member_actions) - except_actions
126
+
127
+ options.delete(:collection_actions)
128
+ options.delete(:member_actions)
129
+
130
+ # NOTE: because we're using prepend for all of these, these are written in backwards order
131
+ # of how they'll be executed during a request!
132
+
133
+ # 4. finally, load the team and parent resource if we can.
134
+ prepend_before_action :load_team
135
+
136
+ # x. this and the thing below it are only here to make a sortable concern possible.
137
+ prepend_before_action only: member_actions do
138
+ instance_variable_name = options[:polymorphic] || through_as_symbols[0]
139
+ eval "@child_object = @#{model}"
140
+ eval "@parent_object = @#{instance_variable_name}"
141
+ end
142
+
143
+ prepend_before_action only: collection_actions do
144
+ instance_variable_name = options[:polymorphic] || through_as_symbols[0]
145
+ eval "@parent_object = @#{instance_variable_name}"
146
+ if options[:through_association].present?
147
+ eval "@child_collection = :#{options[:through_association]}"
148
+ else
149
+ eval "@child_collection = :#{model.to_s.pluralize}"
150
+ end
151
+ end
152
+
153
+ prepend_before_action only: member_actions do
154
+ instance_variable_name = options[:polymorphic] || through_as_symbols[0]
155
+ possible_sources_of_parent =
156
+ through_as_symbols.map { |tas| "@#{model}.#{tas}" }.join(" || ")
157
+ eval_string =
158
+ "@#{instance_variable_name} ||= " + possible_sources_of_parent
159
+ eval eval_string
160
+ end
161
+
162
+ if options[:polymorphic]
163
+ prepend_before_action only: collection_actions do
164
+ possible_sources_of_parent =
165
+ through_as_symbols.map { |tas| "@#{tas}" }.join(" || ")
166
+ eval "@#{options[:polymorphic]} ||= #{possible_sources_of_parent}"
167
+ end
168
+ end
169
+
170
+ # 3. on action resource, we have a specific id for the child resource, so load it directly.
171
+ load_and_authorize_resource model,
172
+ options.merge(
173
+ class: model_class_name,
174
+ only: member_actions,
175
+ prepend: true,
176
+ shallow: true
177
+ )
178
+
179
+ # 2. only load the child resource through the parent resource for collection actions.
180
+ load_and_authorize_resource model,
181
+ options.merge(
182
+ class: model_class_name,
183
+ through: through_as_symbols,
184
+ only: collection_actions,
185
+ prepend: true,
186
+ shallow: true
187
+ )
188
+
189
+ # 1. load the parent resource for collection actions only. (we're using shallow routes.)
190
+ # since a controller can have multiple potential parents, we have to run this as a loop on every possible
191
+ # parent. (the vast majority of controllers only have one parent.)
192
+
193
+ through_class_names.each_with_index do |through_class_name, index|
194
+ load_and_authorize_resource through_as_symbols[index],
195
+ options.merge(
196
+ class: through_class_name,
197
+ only: collection_actions,
198
+ prepend: true,
199
+ shallow: true
200
+ )
201
+ end
202
+ end
203
+ end
204
+
205
+ def regex_to_remove_controller_namespace
206
+ raise "This is a template method that needs to be implemented by controllers including LoadsAndAuthorizesResource."
207
+ end
208
+
209
+ def load_team
210
+ # Not all objects that need to be authorized belong to a team,
211
+ # so we give @team a nil value if no association is found.
212
+ begin
213
+ # Sometimes `@team` has already been populated by earlier `before_action` steps.
214
+ @team ||= @child_object&.team || @parent_object&.team
215
+ rescue NoMethodError
216
+ @team = nil
217
+ end
218
+
219
+ # Update current attributes.
220
+ Current.team = @team
221
+
222
+ # If the currently loaded team is saved to the database, make that the user's new current team.
223
+ if @team.try(:persisted?)
224
+ if can? :show, @team
225
+ current_user.update_column(:current_team_id, @team.id)
226
+ end
227
+ end
228
+ end
229
+ end
@@ -1,195 +1,5 @@
1
1
  module LoadsAndAuthorizesResource
2
2
  extend ActiveSupport::Concern
3
3
 
4
- included do
5
- def regex_to_remove_controller_namespace
6
- raise "This is a template method that needs to be implemented by controllers including LoadsAndAuthorizesResource."
7
- end
8
-
9
- def load_team
10
- # Not all objects that need to be authorized belong to a team,
11
- # so we give @team a nil value if no association is found.
12
- begin
13
- # Sometimes `@team` has already been populated by earlier `before_action` steps.
14
- @team ||= @child_object&.team || @parent_object&.team
15
- rescue NoMethodError
16
- @team = nil
17
- end
18
-
19
- # Update current attributes.
20
- Current.team = @team
21
-
22
- # If the currently loaded team is saved to the database, make that the user's new current team.
23
- if @team.try(:persisted?)
24
- if can? :show, @team
25
- current_user.update_column(:current_team_id, @team.id)
26
- end
27
- end
28
- end
29
-
30
- def self.model_namespace_from_controller_namespace
31
- controller_class_name = regex_to_remove_controller_namespace ? name.gsub(regex_to_remove_controller_namespace, "") : name
32
- namespace = controller_class_name.split("::")
33
- # Remove "::ThingsController"
34
- namespace.pop
35
- namespace
36
- end
37
-
38
- # this is one of the few pieces of 'magical' functionality that bullet train implements
39
- # for you in your controllers beyond that is provided by the underlying gems that we've
40
- # tied together. we've taken the liberty of doing this because it's heavily based on
41
- # cancancan's `load_and_authorize_resource` method, which is awesome, but it also
42
- # implements a lot of the options required to make that method work very well for our
43
- # controllers in the account namespace, including our shallow nested routes.
44
- #
45
- # there are also some complications that were introduced into this method by our support
46
- # for namespaced models and controllers. (we introduced this complexity in support of
47
- # namespacing our `Oauth::` models and controllers.)
48
- #
49
- # to help you understand the code below, usually `through` is `team`
50
- # and `model` is something like `project`.
51
- def self.account_load_and_authorize_resource(model, options, old_options = {})
52
- # options are now required, because you have to have at least a 'through' setting.
53
-
54
- # we used to support calling this method with a signature like this:
55
- #
56
- # `account_load_and_authorize_resource [:oauth, :twitter_account], :team`
57
- #
58
- # however this abstraction was too short-sighted so we've updated this method to accept the exact same method
59
- # signature as cancancan's original `load_and_authorize_resource` method.
60
- if model.is_a?(Array)
61
- raise "Bullet Train has depreciated this method of calling `account_load_and_authorize_resource`. Read the comments on this line of source for more details."
62
- end
63
-
64
- # this is providing backward compatibility for folks who are calling this method like this:
65
- # account_load_and_authorize_resource :thing, through: :team, through_association: :scaffolding_things
66
- # i'm going to deprecate this at some point.
67
- if options.is_a?(Hash)
68
- through = options[:through]
69
- options.delete(:through)
70
- else
71
- through = options
72
- options = old_options
73
- end
74
-
75
- # fetch the namespace of the controller. this should generally match the namespace of the model, except for the
76
- # `account` part.
77
- namespace = model_namespace_from_controller_namespace
78
-
79
- tried = []
80
- begin
81
- # check whether the parent exists in the model namespace.
82
- model_class_name = (namespace + [model.to_s.classify]).join("::")
83
- model_class_name.constantize
84
- rescue NameError
85
- tried << model_class_name
86
- if namespace.any?
87
- namespace.pop
88
- retry
89
- else
90
- raise "Oh no, it looks like your call to 'account_load_and_authorize_resource' is broken. We tried #{tried.join(" and ")}, but didn't find a valid class name."
91
- end
92
- end
93
-
94
- # treat through as an array even if the user only specified one parent type.
95
- through_as_symbols = through.is_a?(Array) ? through : [through]
96
-
97
- through = []
98
- through_class_names = []
99
-
100
- through_as_symbols.each do |through_as_symbol|
101
- # reflect on the belongs_to association of the child model to figure out the class names of the parents.
102
- unless (association = model_class_name.constantize.reflect_on_association(through_as_symbol))
103
- raise "Oh no, it looks like your call to 'account_load_and_authorize_resource' is broken. Tried to reflect on the `#{through_as_symbol}` association of #{model_class_name}, but didn't find one."
104
- end
105
-
106
- through_class_name = association.klass.name
107
-
108
- begin
109
- through << through_class_name.constantize
110
- through_class_names << through_class_name
111
- rescue NameError
112
- raise "Oh no, it looks like your call to 'account_load_and_authorize_resource' is broken. We tried to load `#{through_class_name}}` (the class name defined for the `#{through_as_symbol}` association), but couldn't find it."
113
- end
114
- end
115
-
116
- if through_as_symbols.count > 1 && !options[:polymorphic]
117
- raise "When a resource can be loaded through multiple parents, please specify the 'polymorphic' option to tell us what that controller calls the parent, e.g. `polymorphic: :imageable`."
118
- end
119
-
120
- # this provides the support we need for shallow nested resources, which
121
- # helps keep our routes tidy even after many levels of nesting. most people
122
- # i talk to don't actually know about this feature in rails, but it's
123
- # actually the recommended approach in the rails routing documentation.
124
- #
125
- # also, similar to `load_and_authorize_resource`, people can pass in additional
126
- # actions for which the resource should be loaded, but because we're making
127
- # separate calls to `load_and_authorize_resource` for member and collection
128
- # actions, we ask controllers to specify these actions separately, e.g.:
129
- # `account_load_and_authorize_resource :invitation, :team, member_actions: [:accept, :promote]`
130
- collection_actions = options[:collection_actions] || []
131
- member_actions = options[:member_actions] || []
132
-
133
- # this option is native to cancancan and allows you to skip account_load_and_authorize_resource
134
- # for a specific action that would otherwise run it (e.g. see invitations#show.)
135
- except_actions = options[:except] || []
136
-
137
- collection_actions = ([:index, :new, :create, :reorder] + collection_actions) - except_actions
138
- member_actions = ([:show, :edit, :update, :destroy] + member_actions) - except_actions
139
-
140
- options.delete(:collection_actions)
141
- options.delete(:member_actions)
142
-
143
- # NOTE: because we're using prepend for all of these, these are written in backwards order
144
- # of how they'll be executed during a request!
145
-
146
- # 4. finally, load the team and parent resource if we can.
147
- prepend_before_action :load_team
148
-
149
- # x. this and the thing below it are only here to make a sortable concern possible.
150
- prepend_before_action only: member_actions do
151
- instance_variable_name = options[:polymorphic] || through_as_symbols[0]
152
- eval "@child_object = @#{model}"
153
- eval "@parent_object = @#{instance_variable_name}"
154
- end
155
-
156
- prepend_before_action only: collection_actions do
157
- instance_variable_name = options[:polymorphic] || through_as_symbols[0]
158
- eval "@parent_object = @#{instance_variable_name}"
159
- if options[:through_association].present?
160
- eval "@child_collection = :#{options[:through_association]}"
161
- else
162
- eval "@child_collection = :#{model.to_s.pluralize}"
163
- end
164
- end
165
-
166
- prepend_before_action only: member_actions do
167
- instance_variable_name = options[:polymorphic] || through_as_symbols[0]
168
- possible_sources_of_parent = through_as_symbols.map { |tas| "@#{model}.#{tas}" }.join(" || ")
169
- eval_string = "@#{instance_variable_name} ||= " + possible_sources_of_parent
170
- eval eval_string
171
- end
172
-
173
- if options[:polymorphic]
174
- prepend_before_action only: collection_actions do
175
- possible_sources_of_parent = through_as_symbols.map { |tas| "@#{tas}" }.join(" || ")
176
- eval "@#{options[:polymorphic]} ||= #{possible_sources_of_parent}"
177
- end
178
- end
179
-
180
- # 3. on action resource, we have a specific id for the child resource, so load it directly.
181
- load_and_authorize_resource model, options.merge(class: model_class_name, only: member_actions, prepend: true, shallow: true)
182
-
183
- # 2. only load the child resource through the parent resource for collection actions.
184
- load_and_authorize_resource model, options.merge(class: model_class_name, through: through_as_symbols, only: collection_actions, prepend: true, shallow: true)
185
-
186
- # 1. load the parent resource for collection actions only. (we're using shallow routes.)
187
- # since a controller can have multiple potential parents, we have to run this as a loop on every possible
188
- # parent. (the vast majority of controllers only have one parent.)
189
-
190
- through_class_names.each_with_index do |through_class_name, index|
191
- load_and_authorize_resource through_as_symbols[index], options.merge(class: through_class_name, only: collection_actions, prepend: true, shallow: true)
192
- end
193
- end
194
- end
4
+ include BulletTrain::LoadsAndAuthorizesResource
195
5
  end
@@ -1,5 +1,5 @@
1
1
  module BulletTrain
2
2
  module SuperLoadAndAuthorizeResource
3
- VERSION = "1.3.20"
3
+ VERSION = "1.3.21"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bullet_train-super_load_and_authorize_resource
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.20
4
+ version: 1.3.21
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Culver
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: 6.0.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: pry
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
27
41
  description: Bullet Train Super Load And Authorize Resource
28
42
  email:
29
43
  - andrew.culver@gmail.com
@@ -35,6 +49,7 @@ files:
35
49
  - README.md
36
50
  - Rakefile
37
51
  - app/assets/config/bullet_train_super_load_and_authorize_resource_manifest.js
52
+ - app/controllers/concerns/bullet_train/loads_and_authorizes_resource.rb
38
53
  - app/controllers/concerns/loads_and_authorizes_resource.rb
39
54
  - config/routes.rb
40
55
  - lib/bullet_train/super_load_and_authorize_resource.rb