effective_resources 0.7.13 → 0.8.0

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
  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