effective_resources 0.9.5 → 0.10.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 (38) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/concerns/effective/crud_controller.rb +23 -546
  3. data/app/controllers/concerns/effective/crud_controller/actions.rb +289 -0
  4. data/app/controllers/concerns/effective/crud_controller/dsl.rb +81 -0
  5. data/app/controllers/concerns/effective/crud_controller/paths.rb +87 -0
  6. data/app/controllers/concerns/effective/crud_controller/permitted_params.rb +66 -0
  7. data/app/controllers/concerns/effective/crud_controller/save.rb +77 -0
  8. data/app/controllers/concerns/effective/crud_controller/submits.rb +122 -0
  9. data/app/helpers/effective_resources_helper.rb +51 -52
  10. data/app/helpers/effective_resources_private_helper.rb +69 -0
  11. data/app/models/concerns/effective_resource.rb +24 -0
  12. data/app/models/effective/model_reader.rb +29 -0
  13. data/app/models/effective/resource.rb +6 -2
  14. data/app/models/effective/resources/actions.rb +10 -10
  15. data/app/models/effective/resources/associations.rb +5 -0
  16. data/app/models/effective/resources/attributes.rb +40 -27
  17. data/app/models/effective/resources/controller.rb +81 -0
  18. data/app/models/effective/resources/forms.rb +0 -51
  19. data/app/models/effective/resources/init.rb +19 -17
  20. data/app/models/effective/resources/klass.rb +6 -8
  21. data/app/models/effective/resources/model.rb +23 -0
  22. data/app/models/effective/resources/naming.rb +7 -3
  23. data/app/models/effective/resources/paths.rb +4 -63
  24. data/app/models/effective/resources/relation.rb +4 -1
  25. data/app/models/effective/resources/sql.rb +1 -1
  26. data/app/views/application/create.js.erb +1 -1
  27. data/app/views/application/edit.html.haml +2 -2
  28. data/app/views/application/index.html.haml +1 -1
  29. data/app/views/application/member_action.js.erb +1 -1
  30. data/app/views/application/new.html.haml +3 -1
  31. data/app/views/application/show.html.haml +1 -1
  32. data/app/views/application/update.js.erb +1 -1
  33. data/app/views/effective/resource/_actions.html.haml +3 -32
  34. data/app/views/effective/resource/_actions_dropleft.html.haml +4 -26
  35. data/app/views/effective/resource/_actions_glyphicons.html.haml +4 -10
  36. data/lib/effective_resources/engine.rb +1 -0
  37. data/lib/effective_resources/version.rb +1 -1
  38. metadata +14 -3
@@ -0,0 +1,289 @@
1
+ module Effective
2
+ module CrudController
3
+ module Actions
4
+
5
+ def index
6
+ Rails.logger.info 'Processed by Effective::CrudController#index'
7
+
8
+ EffectiveResources.authorize!(self, :index, resource_klass)
9
+ @page_title ||= resource_plural_name.titleize
10
+
11
+ self.resources ||= resource_scope.all
12
+
13
+ if (datatable = resource_datatable_class).present?
14
+ @datatable ||= datatable.new(resource_datatable_attributes)
15
+ @datatable.view = view_context
16
+ end
17
+
18
+ run_callbacks(:resource_render)
19
+ end
20
+
21
+ def new
22
+ Rails.logger.info 'Processed by Effective::CrudController#new'
23
+
24
+ self.resource ||= resource_scope.new
25
+
26
+ self.resource.assign_attributes(
27
+ params.to_unsafe_h.except(:controller, :action, :id).select { |k, v| resource.respond_to?("#{k}=") }
28
+ )
29
+
30
+ if params[:duplicate_id]
31
+ duplicate = resource_scope.find(params[:duplicate_id])
32
+ EffectiveResources.authorize!(self, :show, duplicate)
33
+
34
+ self.resource = duplicate_resource(duplicate)
35
+ raise "expected duplicate_resource to return an unsaved new #{resource_klass} resource" unless resource.kind_of?(resource_klass) && resource.new_record?
36
+
37
+ if (message = flash[:success].to_s).present?
38
+ flash.delete(:success)
39
+ flash.now[:success] = "#{message.chomp('.')}. Adding another #{resource_name.titleize} based on previous."
40
+ end
41
+ end
42
+
43
+ EffectiveResources.authorize!(self, :new, resource)
44
+ @page_title ||= "New #{resource_name.titleize}"
45
+
46
+ run_callbacks(:resource_render)
47
+ end
48
+
49
+ def create
50
+ Rails.logger.info 'Processed by Effective::CrudController#create'
51
+
52
+ self.resource ||= resource_scope.new
53
+ action = commit_action[:action]
54
+
55
+ resource.assign_attributes(send(resource_params_method_name))
56
+ resource.created_by = current_user if resource.respond_to?(:created_by=)
57
+
58
+ EffectiveResources.authorize!(self, (action == :save ? :create : action), resource)
59
+ @page_title ||= "New #{resource_name.titleize}"
60
+
61
+ respond_to do |format|
62
+ if save_resource(resource, action)
63
+ request.format = :html if specific_redirect_path?
64
+
65
+ format.html do
66
+ flash[:success] ||= resource_flash(:success, resource, action)
67
+ redirect_to(resource_redirect_path)
68
+ end
69
+
70
+ format.js do
71
+ flash.now[:success] ||= resource_flash(:success, resource, action)
72
+ reload_resource # create.js.erb
73
+ end
74
+ else
75
+ flash.delete(:success)
76
+ flash.now[:danger] ||= resource_flash(:danger, resource, action)
77
+
78
+ run_callbacks(:resource_render)
79
+
80
+ format.html { render :new }
81
+ format.js {} # create.js.erb
82
+ end
83
+ end
84
+ end
85
+
86
+ def show
87
+ Rails.logger.info 'Processed by Effective::CrudController#show'
88
+
89
+ self.resource ||= resource_scope.find(params[:id])
90
+
91
+ EffectiveResources.authorize!(self, :show, resource)
92
+ @page_title ||= resource.to_s
93
+
94
+ run_callbacks(:resource_render)
95
+ end
96
+
97
+ def edit
98
+ Rails.logger.info 'Processed by Effective::CrudController#edit'
99
+
100
+ self.resource ||= resource_scope.find(params[:id])
101
+
102
+ EffectiveResources.authorize!(self, :edit, resource)
103
+ @page_title ||= "Edit #{resource}"
104
+
105
+ run_callbacks(:resource_render)
106
+ end
107
+
108
+ def update
109
+ Rails.logger.info 'Processed by Effective::CrudController#update'
110
+
111
+ self.resource ||= resource_scope.find(params[:id])
112
+ action = commit_action[:action]
113
+
114
+ EffectiveResources.authorize!(self, (action == :save ? :update : action), resource)
115
+ @page_title ||= "Edit #{resource}"
116
+
117
+ resource.assign_attributes(send(resource_params_method_name))
118
+ resource.current_user = current_user if resource.respond_to?(:current_user=)
119
+
120
+ respond_to do |format|
121
+ if save_resource(resource, action)
122
+ request.format = :html if specific_redirect_path?
123
+
124
+ format.html do
125
+ flash[:success] ||= resource_flash(:success, resource, action)
126
+ redirect_to(resource_redirect_path)
127
+ end
128
+
129
+ format.js do
130
+ flash.now[:success] ||= resource_flash(:success, resource, action)
131
+ reload_resource # update.js.erb
132
+ end
133
+ else
134
+ flash.delete(:success)
135
+ flash.now[:danger] ||= resource_flash(:danger, resource, action)
136
+
137
+ run_callbacks(:resource_render)
138
+
139
+ format.html { render :edit }
140
+ format.js { } # update.js.erb
141
+ end
142
+ end
143
+ end
144
+
145
+ def destroy
146
+ Rails.logger.info 'Processed by Effective::CrudController#destroy'
147
+
148
+ self.resource = resource_scope.find(params[:id])
149
+ action = :destroy
150
+
151
+ EffectiveResources.authorize!(self, action, resource)
152
+ @page_title ||= "Destroy #{resource}"
153
+
154
+ respond_to do |format|
155
+ if save_resource(resource, action)
156
+ request.format = :html if specific_redirect_path?(action)
157
+
158
+ format.html do
159
+ flash[:success] ||= resource_flash(:success, resource, action)
160
+ redirect_to(resource_redirect_path(action))
161
+ end
162
+
163
+ format.js do
164
+ flash.now[:success] ||= resource_flash(:success, resource, action)
165
+ # destroy.js.erb
166
+ end
167
+ else
168
+ flash.delete(:success)
169
+ request.format = :html # Don't run destroy.js.erb
170
+
171
+ format.html do
172
+ flash[:danger] = (flash.now[:danger].presence || resource_flash(:danger, resource, action))
173
+ redirect_to(resource_redirect_path(action))
174
+ end
175
+ end
176
+ end
177
+ end
178
+
179
+ def member_action(action)
180
+ Rails.logger.info "Processed by Effective::CrudController#member_action"
181
+
182
+ self.resource ||= resource_scope.find(params[:id])
183
+
184
+ EffectiveResources.authorize!(self, action, resource)
185
+ @page_title ||= "#{action.to_s.titleize} #{resource}"
186
+
187
+ if request.get?
188
+ run_callbacks(:resource_render); return
189
+ end
190
+
191
+ to_assign = (send(resource_params_method_name) rescue {})
192
+ resource.assign_attributes(to_assign) if to_assign.present? && to_assign.permitted?
193
+
194
+ resource.current_user = current_user if resource.respond_to?(:current_user=)
195
+
196
+ respond_to do |format|
197
+ if save_resource(resource, action)
198
+ request.format = :html if specific_redirect_path?(action)
199
+
200
+ format.html do
201
+ flash[:success] ||= resource_flash(:success, resource, action)
202
+ redirect_to(resource_redirect_path(action))
203
+ end
204
+
205
+ format.js do
206
+ flash.now[:success] ||= resource_flash(:success, resource, action)
207
+ reload_resource
208
+ render_member_action(action)
209
+ end
210
+ else
211
+ flash.delete(:success)
212
+ flash.now[:danger] ||= resource_flash(:danger, resource, action)
213
+
214
+ run_callbacks(:resource_render)
215
+
216
+ format.html do
217
+ if resource_edit_path && (referer_redirect_path || '').end_with?(resource_edit_path)
218
+ @page_title ||= "Edit #{resource}"
219
+ render :edit
220
+ elsif resource_new_path && (referer_redirect_path || '').end_with?(resource_new_path)
221
+ @page_title ||= "New #{resource_name.titleize}"
222
+ render :new
223
+ elsif resource_show_path && (referer_redirect_path || '').end_with?(resource_show_path)
224
+ @page_title ||= resource_name.titleize
225
+ render :show
226
+ else
227
+ @page_title ||= resource.to_s
228
+ flash[:danger] = flash.now[:danger]
229
+ redirect_to(referer_redirect_path || resource_redirect_path(action))
230
+ end
231
+ end
232
+
233
+ format.js { render_member_action(action) }
234
+ end
235
+ end
236
+ end
237
+
238
+ def collection_action(action)
239
+ Rails.logger.info 'Processed by Effective::CrudController#collection_action'
240
+
241
+ action = action.to_s.gsub('bulk_', '').to_sym
242
+
243
+ if params[:ids].present?
244
+ self.resources ||= resource_scope.where(id: params[:ids])
245
+ end
246
+
247
+ if effective_resource.scope?(action)
248
+ self.resources ||= resource_scope.public_send(action)
249
+ end
250
+
251
+ self.resources ||= resource_scope.all
252
+
253
+ EffectiveResources.authorize!(self, action, resource_klass)
254
+ @page_title ||= "#{action.to_s.titleize} #{resource_plural_name.titleize}"
255
+
256
+ if request.get?
257
+ run_callbacks(:resource_render); return
258
+ end
259
+
260
+ raise "expected all #{resource_name} objects to respond to #{action}!" if resources.to_a.present? && !resources.all? { |resource| resource.respond_to?("#{action}!") }
261
+
262
+ successes = 0
263
+
264
+ # No attributes are assigned or saved. We purely call action! on the resource
265
+
266
+ ActiveRecord::Base.transaction do
267
+ successes = resources.select do |resource|
268
+ begin
269
+ resource.public_send("#{action}!") if EffectiveResources.authorized?(self, action, resource)
270
+ rescue => e
271
+ false
272
+ end
273
+ end.length
274
+ end
275
+
276
+ render json: { status: 200, message: "Successfully #{action_verb(action)} #{successes} / #{resources.length} selected #{resource_plural_name}" }
277
+ end
278
+ end
279
+
280
+ private
281
+
282
+ # Which member javascript view to render: #{action}.js or effective_resources member_action.js
283
+ def render_member_action(action)
284
+ view = lookup_context.template_exists?(action, _prefixes) ? action : :member_action
285
+ render(view, locals: { action: action })
286
+ end
287
+
288
+ end
289
+ end
@@ -0,0 +1,81 @@
1
+ module Effective
2
+ module CrudController
3
+ module Dsl
4
+
5
+ # https://github.com/rails/rails/blob/v5.1.4/actionpack/lib/abstract_controller/callbacks.rb
6
+ def before_render(*names, &blk)
7
+ _insert_callbacks(names, blk) { |name, options| set_callback(:resource_render, :before, name, options) }
8
+ end
9
+
10
+ def after_save(*names, &blk)
11
+ _insert_callbacks(names, blk) { |name, options| set_callback(:resource_save, :after, name, options) }
12
+ end
13
+
14
+ def after_error(*names, &blk)
15
+ _insert_callbacks(names, blk) { |name, options| set_callback(:resource_error, :after, name, options) }
16
+ end
17
+
18
+ # This controls the form submit options of effective_submit
19
+ # Takes precidence over any 'on' dsl commands
20
+ #
21
+ # Effective::Resource will populate this with all crud actions
22
+ # And you can control the details with this DSL:
23
+ #
24
+ # submit :approve, 'Save and Approve', unless: -> { resource.approved? }, redirect: :show
25
+ #
26
+ # submit :toggle, 'Blacklist', if: -> { sync? }, class: 'btn btn-primary'
27
+ # submit :toggle, 'Whitelist', if: -> { !sync? }, class: 'btn btn-primary'
28
+ # submit :save, 'Save', success: -> { "#{resource} was saved okay!" }
29
+ def submit(action, label = nil, args = {})
30
+ _insert_submit(action, label, args)
31
+ end
32
+
33
+ # This controls the resource buttons section of the page
34
+ # Takes precidence over any on commands
35
+ #
36
+ # Effective::Resource will populate this with all member_actions
37
+ #
38
+ # button :approve, 'Approve', unless: -> { resource.approved? }, redirect: :show
39
+ # button :decline, false
40
+ def button(action, label = nil, args = {})
41
+ _insert_button(action, label, args)
42
+ end
43
+
44
+ # This is a way of defining the redirect, flash etc of any action without tweaking defaults
45
+ # submit and buttons options will be merged ontop of these
46
+ def on(action, args = {})
47
+ _insert_on(action, args)
48
+ end
49
+
50
+ # page_title 'My Title', only: [:new]
51
+ def page_title(label = nil, opts = {}, &block)
52
+ opts = label if label.kind_of?(Hash)
53
+ raise 'expected a label or block' unless (label || block_given?)
54
+
55
+ instance_exec do
56
+ before_action(opts) { @page_title ||= (block_given? ? instance_exec(&block) : label).to_s }
57
+ end
58
+ end
59
+
60
+ # resource_scope -> { current_user.things }
61
+ # resource_scope -> { Thing.active.where(user: current_user) }
62
+ # resource_scope do
63
+ # { user_id: current_user.id }
64
+ # end
65
+ # Nested controllers? sure
66
+ # resource_scope -> { User.find(params[:user_id]).things }
67
+
68
+ # Return value should be:
69
+ # a Relation: Thing.where(user: current_user)
70
+ # a Hash: { user_id: current_user.id }
71
+ def resource_scope(obj = nil, opts = {}, &block)
72
+ raise 'expected a proc or block' unless (obj.respond_to?(:call) || block_given?)
73
+
74
+ instance_exec do
75
+ before_action(opts) { @_effective_resource_scope ||= instance_exec(&(block_given? ? block : obj)) }
76
+ end
77
+ end
78
+
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,87 @@
1
+ module Effective
2
+ module CrudController
3
+ module Paths
4
+
5
+ def resource_redirect_path(action = nil)
6
+ submit = commit_action(action)
7
+ redirect = submit[:redirect].respond_to?(:call) ? instance_exec(&submit[:redirect]) : submit[:redirect]
8
+
9
+ commit_action_redirect = case redirect
10
+ when :index ; resource_index_path
11
+ when :edit ; resource_edit_path
12
+ when :show ; resource_show_path
13
+ when :new ; resource_new_path
14
+ when :duplicate ; resource_duplicate_path
15
+ when :back ; referer_redirect_path
16
+ when :save ; [resource_edit_path, resource_show_path].compact.first
17
+ when Symbol ; resource_action_path(submit[:action])
18
+ when String ; redirect
19
+ else ; nil
20
+ end
21
+
22
+ return commit_action_redirect if commit_action_redirect.present?
23
+
24
+ if action == :destroy
25
+ return [referer_redirect_path, resource_index_path, root_path].compact.first
26
+ end
27
+
28
+ case params[:commit].to_s
29
+ when 'Save'
30
+ [resource_edit_path, resource_show_path, resource_index_path]
31
+ when 'Save and Add New', 'Add New'
32
+ [resource_new_path, resource_index_path]
33
+ when 'Duplicate'
34
+ [resource_duplicate_path, resource_index_path]
35
+ when 'Continue', 'Save and Continue'
36
+ [resource_index_path]
37
+ else
38
+ [referer_redirect_path, resource_edit_path, resource_show_path, resource_index_path]
39
+ end.compact.first.presence || root_path
40
+ end
41
+
42
+ def referer_redirect_path
43
+ url = request.referer.to_s
44
+
45
+ return if (resource && resource.respond_to?(:destroyed?) && resource.destroyed? && url.include?("/#{resource.to_param}"))
46
+ return if url.include?('duplicate_id=')
47
+ return unless (Rails.application.routes.recognize_path(URI(url).path) rescue false)
48
+
49
+ url
50
+ end
51
+
52
+ def specific_redirect_path?(action = nil)
53
+ submit = commit_action(action)
54
+ (submit[:redirect].respond_to?(:call) ? instance_exec(&submit[:redirect]) : submit[:redirect]).present?
55
+ end
56
+
57
+ def resource_index_path
58
+ effective_resource.action_path(:index)
59
+ end
60
+
61
+ def resource_new_path
62
+ effective_resource.action_path(:new)
63
+ end
64
+
65
+ def resource_duplicate_path
66
+ effective_resource.action_path(:new, duplicate_id: resource.id)
67
+ end
68
+
69
+ def resource_edit_path
70
+ effective_resource.action_path(:edit, resource)
71
+ end
72
+
73
+ def resource_show_path
74
+ effective_resource.action_path(:show, resource)
75
+ end
76
+
77
+ def resource_destroy_path
78
+ effective_resource.action_path(:destroy, resource)
79
+ end
80
+
81
+ def resource_action_path(action)
82
+ effective_resource.action_path(action.to_sym, resource)
83
+ end
84
+
85
+ end
86
+ end
87
+ end