effective_resources 0.7.13 → 0.8.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 287350db2bd99d6ee44fb82d7a31ff7a138b13d6
4
- data.tar.gz: b624b6db3fdb2ae627fc201a36c53ec615f10bf3
3
+ metadata.gz: 548398bd2a4c2d0b765dbe366a13792a40e2738b
4
+ data.tar.gz: 0c86f9d641ce9453397ae1f85b9601f0995f45d2
5
5
  SHA512:
6
- metadata.gz: 8083ed649eee3b427b455487e21485ec01c8e46f242404eb6e551b470a165ed9d2793ec9f07ad1fa4b6eefbabf15d26583ea8559adcf60e2512ad78c08c0032c
7
- data.tar.gz: fe26139eb7170c782064ab0c82d0dd0ad56114657f40972ef2e0bb33816a8b0111196983a1050483b01fb0da7638179d4345b6719c91ec08449aee712dc61f78
6
+ metadata.gz: 0d8512be42bc14efb7cbd3683ed59e5a0b8a9f5c8735c9b54c0b2864f6ea4a7932e150f861eac0371cb06e7a3c0b785b46bac565fc69349b23eb6ee11f38fded
7
+ data.tar.gz: 972f967e8097e08411b32f544bda79e8fdb35508a1cb48112066769a92a5084235c5d859ac2d3113b98c60820048d11c978b7abb34d3c44191227ad8b532d887
@@ -4,28 +4,29 @@ module Effective
4
4
 
5
5
  included do
6
6
  class << self
7
- def member_actions
8
- @_effective_member_actions ||= {}
7
+
8
+ def effective_resource
9
+ @_effective_resource ||= Effective::Resource.new(controller_path)
9
10
  end
10
11
 
11
- def _default_member_actions
12
- if defined?(EffectiveBootstrap)
13
- { 'Save': { action: :save }, 'Continue': { action: :save }, 'Add New': { action: :save } }
14
- else
15
- {
16
- 'Save' => { action: :save, data: { disable_with: 'Saving...' }},
17
- 'Continue' => { action: :save, data: { disable_with: 'Saving...' }},
18
- 'Add New' => { action: :save, data: { disable_with: 'Saving...' }}
19
- }
20
- end
12
+ def submits
13
+ effective_resource.submits
21
14
  end
22
15
  end
23
16
 
17
+ define_actions_from_routes
24
18
  define_callbacks :resource_render, :resource_save, :resource_error
25
19
  end
26
20
 
27
21
  module ClassMethods
28
22
 
23
+ # Automatically respond to any action defined via the routes file
24
+ def define_actions_from_routes
25
+ resource = Effective::Resource.new(controller_path)
26
+ resource.member_actions.each { |action| member_action(action) }
27
+ resource.collection_actions.each { |action| collection_action(action) }
28
+ end
29
+
29
30
  # https://github.com/rails/rails/blob/v5.1.4/actionpack/lib/abstract_controller/callbacks.rb
30
31
  def before_render(*names, &blk)
31
32
  _insert_callbacks(names, blk) { |name, options| set_callback(:resource_render, :before, name, options) }
@@ -39,73 +40,44 @@ module Effective
39
40
  _insert_callbacks(names, blk) { |name, options| set_callback(:resource_error, :after, name, options) }
40
41
  end
41
42
 
42
- # Add the following to your controller for a simple member action
43
- # member_action :print
43
+ # This controls the form submit options of effective_submit
44
+ # It also controls the redirect path for any actions
44
45
  #
45
- # Add to your permissions: can :print, Thing
46
+ # Effective::Resource will populate this with all member_post_actions
47
+ # And you can control the details with this DSL:
46
48
  #
47
- # If you want to POST and do an action based on this:
48
- # Make sure your model responds to approve!
49
- # member_action :approve
50
- # If you want it to automatically show up in your forms
51
- # member_action :approve, 'Save and Approve', if: -> { approved? }
52
- # member_action :approve, 'Save and Approve', unless: -> { !approved? }, redirect: :show
53
-
54
- def member_action(action, commit = nil, args = {})
55
- if commit.present?
56
- raise 'expected args to be a Hash or false' unless args.kind_of?(Hash) || args == false
57
-
58
- if args == false
59
- member_actions.delete(commit); return
60
- end
61
-
62
- if args.key?(:if) && args[:if].respond_to?(:call) == false
63
- raise "expected if: to be callable. Try member_action :approve, 'Save and Approve', if: -> { submitted? }"
64
- end
65
-
66
- if args.key?(:unless) && args[:unless].respond_to?(:call) == false
67
- raise "expected unless: to be callable. Try member_action :approve, 'Save and Approve', unless: -> { declined? }"
68
- end
49
+ # submit :approve, 'Save and Approve', unless: -> { approved? }, redirect: :show
50
+ #
51
+ # submit :toggle, 'Blacklist', if: -> { sync? }, class: 'btn btn-primary'
52
+ # submit :toggle, 'Whitelist', if: -> { !sync? }, class: 'btn btn-primary'
69
53
 
70
- redirect = args.delete(:redirect_to) || args.delete(:redirect) # Remove redirect_to keyword. use redirect.
54
+ def submit(action, commit = nil, args = {})
55
+ raise 'expected args to be a Hash or false' unless args.kind_of?(Hash) || args == false
71
56
 
72
- member_actions[commit] = (args || {}).merge(action: action, redirect: redirect)
57
+ if commit == false
58
+ submits.delete_if { |commit, args| args[:action] == action }; return
73
59
  end
74
60
 
75
- define_method(action) do
76
- self.resource ||= resource_scope.find(params[:id])
77
-
78
- EffectiveResources.authorize!(self, action, resource)
79
-
80
- @page_title ||= "#{action.to_s.titleize} #{resource}"
81
-
82
- member_post_action(action) unless request.get?
61
+ if args == false
62
+ submits.delete(commit); return
83
63
  end
84
- end
85
64
 
86
- def collection_action(action)
87
- define_method(action) do
88
- if params[:ids].present?
89
- self.resources ||= resource_scope.where(id: params[:ids])
90
- end
91
-
92
- if effective_resource.scope?(action)
93
- self.resources ||= resource_scope.public_send(action)
94
- end
95
-
96
- self.resources ||= resource_scope.all
97
-
98
- EffectiveResources.authorize!(self, action, resource_klass)
65
+ if commit # Overwrite the default member action when given a custom commit
66
+ submits.delete_if { |commit, args| args[:default] && args[:action] == action }
67
+ end
99
68
 
100
- @page_title ||= "#{action.to_s.titleize} #{resource_plural_name.titleize}"
69
+ if args.key?(:if) && args[:if].respond_to?(:call) == false
70
+ raise "expected if: to be callable. Try submit :approve, 'Save and Approve', if: -> { finished? }"
71
+ end
101
72
 
102
- collection_post_action(action) unless request.get?
73
+ if args.key?(:unless) && args[:unless].respond_to?(:call) == false
74
+ raise "expected unless: to be callable. Try submit :approve, 'Save and Approve', unless: -> { declined? }"
103
75
  end
104
- end
105
76
 
106
- # Applies the default actions
107
- def default_actions(args = {})
108
- default_member_actions.each { |k, v| member_actions[k] = (args || {}).merge(v) }
77
+ redirect = args.delete(:redirect_to) || args.delete(:redirect) # Remove redirect_to keyword. use redirect.
78
+ args.merge!(action: action, redirect: redirect)
79
+
80
+ (submits[commit] ||= {}).merge!(args)
109
81
  end
110
82
 
111
83
  # page_title 'My Title', only: [:new]
@@ -138,6 +110,43 @@ module Effective
138
110
  end
139
111
  end
140
112
 
113
+ # Defines a function to handle a GET and POST request on this URL
114
+ # Just add a member action to your routes, you shouldn't need to call this directly
115
+ def member_action(action)
116
+ define_method(action) do
117
+ self.resource ||= resource_scope.find(params[:id])
118
+
119
+ EffectiveResources.authorize!(self, action, resource)
120
+
121
+ @page_title ||= "#{action.to_s.titleize} #{resource}"
122
+
123
+ member_post_action(action) unless request.get?
124
+ end
125
+ end
126
+
127
+ # Defines a function to handle a GET and POST request on this URL
128
+ # Handles bulk_ actions
129
+ # Just add a member action to your routes, you shouldn't need to call this directly
130
+ # You shouldn't need to call this directly
131
+ def collection_action(action)
132
+ define_method(action) do
133
+ if params[:ids].present?
134
+ self.resources ||= resource_scope.where(id: params[:ids])
135
+ end
136
+
137
+ if effective_resource.scope?(action)
138
+ self.resources ||= resource_scope.public_send(action)
139
+ end
140
+
141
+ self.resources ||= resource_scope.all
142
+
143
+ EffectiveResources.authorize!(self, action, resource_klass)
144
+
145
+ @page_title ||= "#{action.to_s.titleize} #{resource_plural_name.titleize}"
146
+
147
+ collection_post_action(action) unless request.get?
148
+ end
149
+ end
141
150
  end
142
151
 
143
152
  def index
@@ -299,31 +308,6 @@ module Effective
299
308
  render json: { status: 200, message: "Successfully #{action_verb(action)} #{successes} / #{resources.length} selected #{resource_plural_name}" }
300
309
  end
301
310
 
302
- # Here we look at all available (class level) member actions, see which ones apply to the current resource
303
- # This feeds into the helper simple_form_submit(f)
304
- # Returns a Hash of {'Save': {data-disable-with: 'Saving...'}, 'Approve': {data-disable-with: 'Approve'}}
305
- def member_actions_for(obj)
306
- actions = (self.class.member_actions.presence || self.class._default_member_actions)
307
-
308
- actions.select do |commit, args|
309
- args[:class] = args[:class].to_s
310
-
311
- (args.key?(:if) ? obj.instance_exec(&args[:if]) : true) &&
312
- (args.key?(:unless) ? !obj.instance_exec(&args[:unless]) : true)
313
- end.sort do |(commit_x, x), (commit_y, y)|
314
- # Sort to front
315
- primary = (y[:class].include?('primary') ? 1 : 0) - (x[:class].include?('primary') ? 1 : 0)
316
- primary = nil if primary == 0
317
-
318
- # Sort to back
319
- danger = (x[:class].include?('danger') ? 1 : 0) - (y[:class].include?('danger') ? 1 : 0)
320
- danger = nil if danger == 0
321
-
322
- primary || danger || actions.keys.index(commit_x) <=> actions.keys.index(commit_y)
323
- end.inject({}) do |h, (commit, args)|
324
- h[commit] = args.except(:action, :if, :unless, :redirect); h
325
- end
326
- end
327
311
 
328
312
  protected
329
313
 
@@ -357,6 +341,7 @@ module Effective
357
341
  when :index ; resource_index_path
358
342
  when :edit ; resource_edit_path
359
343
  when :show ; resource_show_path
344
+ when :new ; resource_new_path
360
345
  when :back ; referer_redirect_path
361
346
  when nil ; nil
362
347
  else ; resource_member_action_path(commit_action[:action])
@@ -466,7 +451,7 @@ module Effective
466
451
  end
467
452
 
468
453
  def commit_action
469
- self.class.member_actions[params[:commit].to_s] || { action: :save }
454
+ self.class.submits[params[:commit].to_s] || { action: :save }
470
455
  end
471
456
 
472
457
  # Returns an ActiveRecord relation based on the computed value of `resource_scope` dsl method
@@ -1,77 +1,23 @@
1
1
  module EffectiveResourcesHelper
2
2
 
3
3
  def effective_submit(form, options = {}, &block) # effective_bootstrap
4
- resource = (@_effective_resource || Effective::Resource.new(controller_path))
5
-
6
- # Apply btn-primary to the first item, only if the class isn't already present
7
- actions = if controller.respond_to?(:member_actions_for)
8
- controller.member_actions_for(form.object).transform_values.with_index do |opts, index|
9
- opts[:class] = "btn #{index == 0 ? 'btn-primary' : 'btn-secondary'}" if opts[:class].blank?
10
- opts
11
- end
12
- else
13
- {}.tap do |actions|
14
- actions['Save'] = { class: 'btn btn-primary' }
15
-
16
- if resource.action_path(:index) && EffectiveResources.authorized?(controller, :index, resource.klass)
17
- actions['Continue'] = { class: 'btn btn-secondary' }
18
- end
19
-
20
- if resource.action_path(:new) && EffectiveResources.authorized?(controller, :new, resource.klass)
21
- actions['Add New'] = { class: 'btn btn-secondary' }
22
- end
23
- end
24
- end
4
+ resource = (controller.class.respond_to?(:effective_resource) ? controller.class.effective_resource : Effective::Resource.new(controller_path))
5
+ actions = resource.submits_for(form.object, controller: controller)
25
6
 
26
7
  # Group by class and render
27
8
  buttons = actions.group_by { |_, opts| opts[:class] }.flat_map do |_, btns|
28
9
  btns.map { |name, opts| form.save(name, opts) }
29
10
  end.join.html_safe
30
11
 
31
- effective_save(form) do
12
+ form.submit('', options) do
32
13
  (block_given? ? capture(&block) : ''.html_safe) + buttons
33
14
  end
34
-
35
- end
36
-
37
- def effective_save(form, label = 'Save', &block) # effective_bootstrap
38
- wrapper = (form.layout == :horizontal) ? { class: 'form-group form-actions row' } : { class: 'form-group form-actions' }
39
-
40
- content_tag(:div, wrapper) do
41
- icon('spinner') + (block_given? ? capture(&block) : form.save(label, class: 'btn btn-primary'))
42
- end
43
- end
44
-
45
- def effective_save_button(form, label = 'Save', &block)
46
- content_tag(:div, class: 'form-actions') do
47
- icon('spinner') + (block_given? ? capture(&block) : form.save(label, class: 'btn btn-primary'))
48
- end
49
15
  end
50
16
 
51
17
  # effective_form_inputs
52
18
  def simple_form_submit(form, options = {}, &block)
53
- resource = (@_effective_resource || Effective::Resource.new(controller_path))
54
-
55
- # Apply btn-primary to the first item, only if the class isn't already present
56
- actions = if controller.respond_to?(:member_actions_for)
57
- controller.member_actions_for(form.object).transform_values.with_index do |opts, index|
58
- opts[:class] = "btn #{index == 0 ? 'btn-primary' : 'btn-default'}" if opts[:class].blank?
59
- opts
60
- end
61
- else
62
- {}.tap do |actions|
63
- actions['Save'] = { class: 'btn btn-primary', data: { disable_with: 'Saving...' }}
64
-
65
- if resource.action_path(:index) && EffectiveResources.authorized?(controller, :index, resource.klass)
66
- actions['Continue'] = { class: 'btn btn-default', data: { disable_with: 'Saving...' }}
67
- end
68
-
69
- if resource.action_path(:new) && EffectiveResources.authorized?(controller, :new, resource.klass)
70
- actions['Add New'] = { class: 'btn btn-default', data: { disable_with: 'Saving...' }}
71
- end
72
-
73
- end
74
- end
19
+ resource = (controller.class.respond_to?(:effective_resource) ? controller.class.effective_resource : Effective::Resource.new(controller_path))
20
+ actions = resource.submits_for(form.object, controller: controller)
75
21
 
76
22
  wrapper_options = { class: 'form-actions' }.merge(options.delete(:wrapper_html) || {})
77
23
 
@@ -54,17 +54,30 @@ module Effective
54
54
 
55
55
  # GET actions
56
56
  def collection_actions
57
- routes.values.map { |route| route.defaults[:action].to_sym if is_get_route?(route) && !is_member_route?(route) }.compact - crud_actions
57
+ routes.values.map { |route| route.defaults[:action].to_sym if is_collection_route?(route) }.compact - crud_actions
58
58
  end
59
59
 
60
- # GET actions
60
+ def collection_get_actions
61
+ routes.values.map { |route| route.defaults[:action].to_sym if is_collection_route?(route) && is_get_route?(route) }.compact - crud_actions
62
+ end
63
+
64
+ def collection_post_actions
65
+ routes.values.map { |route| route.defaults[:action].to_sym if is_collection_route?(route) && is_post_route?(route) }.compact - crud_actions
66
+ end
67
+
68
+ # All actions
61
69
  def member_actions
62
- routes.values.map { |route| route.defaults[:action].to_sym if is_get_route?(route) && is_member_route?(route) }.compact - crud_actions
70
+ routes.values.map { |route| route.defaults[:action].to_sym if is_member_route?(route) }.compact - crud_actions
71
+ end
72
+
73
+ # GET actions
74
+ def member_get_actions
75
+ routes.values.map { |route| route.defaults[:action].to_sym if is_member_route?(route) && is_get_route?(route) }.compact - crud_actions
63
76
  end
64
77
 
65
78
  # POST/PUT/PATCH actions
66
79
  def member_post_actions
67
- routes.values.map { |route| route.defaults[:action].to_sym if is_post_route?(route) && is_member_route?(route) }.compact - crud_actions
80
+ routes.values.map { |route| route.defaults[:action].to_sym if is_member_route?(route) && is_post_route?(route) }.compact - crud_actions
68
81
  end
69
82
 
70
83
  # Same as controller_path in the view
@@ -82,6 +95,10 @@ module Effective
82
95
  (route.path.required_names || []).include?('id')
83
96
  end
84
97
 
98
+ def is_collection_route?(route)
99
+ (route.path.required_names || []).include?('id') == false
100
+ end
101
+
85
102
  def is_get_route?(route)
86
103
  route.verb.to_s.include?('GET')
87
104
  end
@@ -2,6 +2,60 @@ module Effective
2
2
  module Resources
3
3
  module Forms
4
4
 
5
+ # Used by effective_form_submit
6
+ # The actions we would use to commit. For link_to
7
+ # { 'Save': { action: :save }, 'Continue': { action: :save }, 'Add New': { action: :save }, 'Approve': { action: :approve } }
8
+ # Saves a list of commit actions...
9
+ def submits
10
+ @submits ||= {}.tap do |submits|
11
+ if (actions.find { |a| a == :create } || actions.find { |a| a == :update })
12
+ submits['Save'] = { action: :save, class: 'btn btn-primary' }
13
+ end
14
+
15
+ member_post_actions.each do |action| # default true means it will be overwritten by dsl methods
16
+ submits[action.to_s.titleize] = { action: action, default: true, class: 'btn btn-primary' }
17
+ end
18
+
19
+ if actions.find { |a| a == :index }
20
+ submits['Continue'] = { action: :save, redirect: :index }
21
+ end
22
+
23
+ if actions.find { |a| a == :new }
24
+ submits['Add New'] = { action: :save, redirect: :new }
25
+ end
26
+ end
27
+ end
28
+
29
+ # Here we look at all available (class level) member actions, see which ones apply to the current resource
30
+ # This feeds into the helper simple_form_submit(f)
31
+ # Returns a Hash of {'Save': {data-disable-with: 'Saving...'}, 'Approve': {data-disable-with: 'Approve'}}
32
+ def submits_for(obj, controller:)
33
+ submits.select do |commit, args|
34
+ args[:class] = args[:class].to_s
35
+
36
+ action = (args[:action] == :save ? (obj.new_record? ? :create : :update) : args[:action])
37
+
38
+ (args.key?(:if) ? obj.instance_exec(&args[:if]) : true) &&
39
+ (args.key?(:unless) ? !obj.instance_exec(&args[:unless]) : true) &&
40
+ EffectiveResources.authorized?(controller, action, obj)
41
+ end.sort do |(commit_x, x), (commit_y, y)|
42
+ # Sort to front
43
+ primary = (y[:class].include?('primary') ? 1 : 0) - (x[:class].include?('primary') ? 1 : 0)
44
+ primary = nil if primary == 0
45
+
46
+ # Sort to back
47
+ danger = (x[:class].include?('danger') ? 1 : 0) - (y[:class].include?('danger') ? 1 : 0)
48
+ danger = nil if danger == 0
49
+
50
+ primary || danger || submits.keys.index(commit_x) <=> submits.keys.index(commit_y)
51
+ end.inject({}) do |h, (commit, args)|
52
+ h[commit] = args.except(:action, :default, :if, :unless, :redirect); h
53
+ end.transform_values.with_index do |opts, index|
54
+ opts[:class] = "btn #{index == 0 ? 'btn-primary' : 'btn-secondary'}" if opts[:class].blank?
55
+ opts
56
+ end
57
+ end
58
+
5
59
  # Used by datatables
6
60
  def search_form_field(name, type = nil)
7
61
  case (type || sql_type(name))
@@ -1,3 +1,3 @@
1
1
  module EffectiveResources
2
- VERSION = '0.7.13'.freeze
2
+ VERSION = '0.8.0'.freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: effective_resources
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.13
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Code and Effect
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-03-20 00:00:00.000000000 Z
11
+ date: 2018-03-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails