effective_resources 0.9.5 → 0.10.0

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