bullet_train-super_load_and_authorize_resource 1.3.20 → 1.3.21

Sign up to get free protection for your applications and to get access to all the features.
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