effective_resources 1.6.2 → 1.7.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
  SHA256:
3
- metadata.gz: 6aedd8615011c317ab13eb87dd39e859f2c96b2566e968eaa395ef6851bed01c
4
- data.tar.gz: 86428262c78668050c311979f0fad5002f5d28e73fe1b890053ad6fc70412ed4
3
+ metadata.gz: 9c2c2b66908d01a2f641dc34444882413040d6d9b4c403d2a3ec4041abed1822
4
+ data.tar.gz: 20497b49e1408e7c7128ad8f9a4ed601f76842d74f0fd165aa24645f9310a39d
5
5
  SHA512:
6
- metadata.gz: b07ef7750880fdc92771cd51faae6d1a41d0165fc18490889663fde1d82cf2c9de09d03d6b98e50e351bfe8977571bd61fb47ae3a4c183ab81811ae5f05d25fc
7
- data.tar.gz: f552fb107e746829a69c589f11d9eced9b8e1e33db07d25d9ea38557f4c6032372d01ff7877518f8d64a207312603c2a095a0995a1e044b672466dbdb81ab3c0
6
+ metadata.gz: 2c39301ac8c30eedab982905acfdc7bd0077fef8463d07f7bcf68f0861f3962b40c43d7fd7a85cd72b1da6a326d5d9ed3eeaa3de083a5153e289cf52c6b2723a
7
+ data.tar.gz: b3102a3d1e0d809350a41d60f9c517c2f48357838d58dc75e2c32a98686fe5bb5aec401a4e0f63e7cb40b542d9ffa255ccdd72687641d54450d2449e4481e963
data/README.md CHANGED
@@ -256,6 +256,102 @@ end
256
256
 
257
257
  and include Effective::CrudController in your resource controller.
258
258
 
259
+ ### acts_as_wizard
260
+
261
+ Build up an object through a wizard.
262
+
263
+ Works with the [wicked](https://github.com/zombocom/wicked) gem to create wizard quickly.
264
+
265
+ Create a model and define `acts_as_wizard`:
266
+
267
+ ```ruby
268
+ class Thing < ApplicationRecord
269
+ acts_as_wizard(
270
+ start: 'Start',
271
+ select: 'Select',
272
+ finish: 'Finish'
273
+ )
274
+
275
+ effective_resource do
276
+ title :string
277
+ wizard_steps :text, permitted: false
278
+ end
279
+
280
+ validates :title, presence: true
281
+
282
+ def to_s
283
+ title.presence || 'New Thing'
284
+ end
285
+
286
+ # If you define a bang method matching the name of a step
287
+ # it will be called when that step is submitted.
288
+ # Otherwise save! is called.
289
+ def select!
290
+ ApplicationMailer.selected(self).deliver_later
291
+ save!
292
+ end
293
+
294
+ # An array of steps that the controller will use
295
+ # Default value is just all of them. But you can customize here
296
+ # def required_steps
297
+ # steps = WIZARD_STEPS.keys
298
+ # selectable? ? steps : steps - [:select]
299
+ # end
300
+
301
+ # Control whether the user has permission to visit this step
302
+ #
303
+ # This is the default, can go forward or back:
304
+ #
305
+ # def can_visit_step?(step)
306
+ # can_revisit_completed_steps(step)
307
+ # end
308
+ #
309
+ # Easy change if you only want to go forward:
310
+ #
311
+ # def can_visit_step?(step)
312
+ # cannot_revisit_completed_steps(step)
313
+ # end
314
+ #
315
+ # or custom algorithm:
316
+ #
317
+ # def can_visit_step?(step)
318
+ # return false unless has_completed_previous_step?(step)
319
+ # return false if has_completed_step?(:finish) && step != :finish
320
+ # end
321
+ end
322
+ ```
323
+
324
+ In your routes:
325
+
326
+ ```ruby
327
+ resources :things, only: [:index, :show, :new, :destroy] do
328
+ resources :build, controller: :things, only: [:show, :update]
329
+ end
330
+ ```
331
+
332
+ Make a controller:
333
+
334
+ ```ruby
335
+ class ThingsController < ApplicationController
336
+ include Effective::WizardController
337
+ end
338
+ ```
339
+
340
+ And then create one view per step.
341
+
342
+ Here's `views/things/start.html.haml`:
343
+
344
+ ```haml
345
+ = render_wizard_sidebar(resource) do
346
+ %h1= @page_title
347
+
348
+ = effective_form_with(model: resource, url: wizard_path(step), method: :put) do |f|
349
+ = f.text_field :title
350
+ = f.submit 'Save and Continue'
351
+ ```
352
+
353
+ You can also call `render_wizard_sidebar(resource)` without the block syntax.
354
+
259
355
  ## Testing
260
356
 
261
357
  Run tests by:
@@ -11,13 +11,14 @@ module Effective
11
11
 
12
12
  included do
13
13
  define_actions_from_routes
14
- define_permitted_params_from_model
15
14
  define_callbacks :resource_render, :resource_before_save, :resource_after_save, :resource_after_commit, :resource_error
16
15
  end
17
16
 
18
17
  module ClassMethods
19
18
  include Effective::CrudController::Dsl
20
19
 
20
+ # This is used to define_actions_from_routes and for the buttons/submits/ons
21
+ # It doesn't really work with the resource_scope correctly but the routes are important here
21
22
  def effective_resource
22
23
  @_effective_resource ||= Effective::Resource.new(controller_path)
23
24
  end
@@ -32,17 +33,6 @@ module Effective
32
33
  define_method(action) { collection_action(action) }
33
34
  end
34
35
  end
35
-
36
- def define_permitted_params_from_model
37
- if effective_resource.model.present?
38
- define_method(:effective_resource_permitted_params) { resource_permitted_params } # save.rb
39
- end
40
-
41
- if effective_resource.active_model?
42
- define_method(:effective_resource_permitted_params) { resource_active_model_permitted_params } # save.rb
43
- end
44
- end
45
-
46
36
  end
47
37
 
48
38
  def resource # @thing
@@ -62,15 +52,37 @@ module Effective
62
52
  end
63
53
 
64
54
  def effective_resource
65
- self.class.effective_resource
55
+ @_effective_resource ||= begin
56
+ relation = instance_exec(&resource_scope_relation) if respond_to?(:resource_scope_relation)
57
+
58
+ if respond_to?(:resource_scope_relation) && !relation.kind_of?(ActiveRecord::Relation)
59
+ raise('resource_scope must return an ActiveRecord::Relation')
60
+ end
61
+
62
+ resource = Effective::Resource.new(controller_path, relation: relation)
63
+
64
+ unless resource.relation.kind_of?(ActiveRecord::Relation) || effective_resource.active_model?
65
+ raise("unable to build resource_scope for #{resource.klass || 'unknown klass'}. Please name your controller to match an existing model, or manually define a resource_scope.")
66
+ end
67
+
68
+ resource
69
+ end
66
70
  end
67
71
 
68
72
  private
69
73
 
74
+ def resource_scope
75
+ effective_resource.relation
76
+ end
77
+
70
78
  def resource_name # 'thing'
71
79
  effective_resource.name
72
80
  end
73
81
 
82
+ def resource_name_id
83
+ (effective_resource.name + '_id').to_sym
84
+ end
85
+
74
86
  def resource_klass # Thing
75
87
  effective_resource.klass
76
88
  end
@@ -79,30 +91,6 @@ module Effective
79
91
  effective_resource.plural_name
80
92
  end
81
93
 
82
- # Returns an ActiveRecord relation based on the computed value of `resource_scope` dsl method
83
- def resource_scope # Thing
84
- @_effective_resource_relation ||= (
85
- relation = case @_effective_resource_scope # If this was initialized by the resource_scope before_action
86
- when ActiveRecord::Relation
87
- @_effective_resource_scope
88
- when Hash
89
- effective_resource.klass.where(@_effective_resource_scope)
90
- when Symbol
91
- effective_resource.klass.send(@_effective_resource_scope)
92
- when nil
93
- effective_resource.klass.respond_to?(:all) ? effective_resource.klass.all : effective_resource.klass
94
- else
95
- raise "expected resource_scope method to return an ActiveRecord::Relation or Hash"
96
- end
97
-
98
- unless relation.kind_of?(ActiveRecord::Relation) || effective_resource.active_model?
99
- raise("unable to build resource_scope for #{effective_resource.klass || 'unknown klass'}. Please name your controller to match an existing model, or manually define a resource_scope.")
100
- end
101
-
102
- relation
103
- )
104
- end
105
-
106
94
  def resource_datatable_attributes
107
95
  resource_scope.where_values_hash.symbolize_keys
108
96
  end
@@ -124,7 +112,16 @@ module Effective
124
112
  end
125
113
 
126
114
  def resource_params_method_name
127
- ["#{resource_name}_params", "#{resource_plural_name}_params", 'permitted_params', 'effective_resource_permitted_params', ('resource_permitted_params' if effective_resource.model.present?)].compact.find { |name| respond_to?(name, true) } || 'params'
115
+ ['permitted_params', "#{resource_name}_params", "#{resource_plural_name}_params"].each do |name|
116
+ return name if respond_to?(name, true)
117
+ end
118
+
119
+ # Built in ones
120
+ return 'resource_permitted_params' if effective_resource.model.present?
121
+ return 'resource_active_model_permitted_params' if effective_resource.active_model?
122
+
123
+ # Fallback
124
+ 'params'
128
125
  end
129
126
 
130
127
  end
@@ -63,8 +63,13 @@ module Effective
63
63
  self.resource ||= resource_scope.new
64
64
  action = (commit_action[:action] == :save ? :create : commit_action[:action])
65
65
 
66
- resource.current_user ||= current_user if resource.respond_to?(:current_user=)
67
- resource.created_by ||= current_user if resource.respond_to?(:created_by=)
66
+ if respond_to?(:current_user) && resource.respond_to?(:current_user=)
67
+ resource.current_user ||= current_user
68
+ end
69
+
70
+ if respond_to?(:current_user) && resource.respond_to?(:created_by=)
71
+ resource.created_by ||= current_user
72
+ end
68
73
 
69
74
  resource.assign_attributes(send(resource_params_method_name))
70
75
 
@@ -121,8 +126,13 @@ module Effective
121
126
  EffectiveResources.authorize!(self, action, resource)
122
127
  @page_title ||= "Edit #{resource}"
123
128
 
124
- resource.current_user ||= current_user if resource.respond_to?(:current_user=)
125
- resource.updated_by ||= current_user if resource.respond_to?(:updated_by=)
129
+ if respond_to?(:current_user) && resource.respond_to?(:current_user=)
130
+ resource.current_user ||= current_user
131
+ end
132
+
133
+ if respond_to?(:current_user) && resource.respond_to?(:updated_by=)
134
+ resource.updated_by ||= current_user
135
+ end
126
136
 
127
137
  resource.assign_attributes(send(resource_params_method_name))
128
138
 
@@ -79,9 +79,12 @@ module Effective
79
79
  def resource_scope(obj = nil, opts = {}, &block)
80
80
  raise 'expected a proc or block' unless (obj.respond_to?(:call) || block_given?)
81
81
 
82
- instance_exec do
83
- before_action(opts) { @_effective_resource_scope ||= instance_exec(&(block_given? ? block : obj)) }
82
+ if block_given?
83
+ define_method(:resource_scope_relation) { return block }
84
+ else
85
+ define_method(:resource_scope_relation) { return obj }
84
86
  end
87
+
85
88
  end
86
89
 
87
90
  end
@@ -96,7 +96,7 @@ module Effective
96
96
 
97
97
  if (nested_params = permitted_params_for(nested.klass, namespaces)).present?
98
98
  nested_params.insert(nested_params.rindex { |obj| !obj.kind_of?(Hash)} + 1, :_destroy)
99
- permitted_params << { "#{nested.resource_name}_attributes".to_sym => nested_params }
99
+ permitted_params << { "#{nested.name}_attributes".to_sym => nested_params }
100
100
  end
101
101
  end
102
102
 
@@ -24,7 +24,9 @@ module Effective
24
24
  save_action = ([:create, :update].include?(action) ? :save : action)
25
25
  raise "expected @#{resource_name} to respond to #{save_action}!" unless resource.respond_to?("#{save_action}!")
26
26
 
27
- resource.current_user ||= current_user if resource.respond_to?(:current_user=)
27
+ if respond_to?(:current_user) && resource.respond_to?(:current_user=)
28
+ resource.current_user ||= current_user
29
+ end
28
30
 
29
31
  success = false
30
32
 
@@ -0,0 +1,66 @@
1
+ module Effective
2
+ module WizardController
3
+ extend ActiveSupport::Concern
4
+
5
+ include Wicked::Wizard if defined?(Wicked)
6
+ include Effective::CrudController
7
+
8
+ include Effective::WizardController::Actions
9
+ include Effective::WizardController::BeforeActions
10
+ include Effective::WizardController::Save
11
+ include Effective::WizardController::WickedOverrides
12
+
13
+ included do
14
+ raise("please install gem 'wicked' to use Effective::WizardController") unless defined?(Wicked)
15
+
16
+ with_options(only: [:show, :update]) do
17
+ before_action :redirect_if_blank_step
18
+
19
+ before_action :assign_resource
20
+ before_action :authorize_resource
21
+ before_action :assign_required_steps
22
+ before_action :setup_wizard # Wicked
23
+
24
+ before_action :enforce_can_visit_step
25
+
26
+ before_action :assign_current_step
27
+ before_action :assign_page_title
28
+ end
29
+
30
+ helper_method :resource
31
+ helper_method :resource_wizard_step_title
32
+
33
+ helper EffectiveResourcesWizardHelper
34
+
35
+ rescue_from Wicked::Wizard::InvalidStepError do |exception|
36
+ flash[:danger] = "Unknown step. You have been moved to the #{resource_wizard_steps.first} step."
37
+ redirect_to wizard_path(resource_wizard_steps.first)
38
+ end
39
+ end
40
+
41
+ def find_wizard_resource
42
+ if params[resource_name_id] && params[resource_name_id] != 'new'
43
+ resource_scope.find(params[resource_name_id])
44
+ else
45
+ resource_scope.new
46
+ end
47
+ end
48
+
49
+ def resource_wizard_step_title(step)
50
+ return if step == 'wicked_finish'
51
+ effective_resource.klass.const_get(:WIZARD_STEPS).fetch(step)
52
+ end
53
+
54
+ def resource_wizard_steps
55
+ effective_resource.klass.const_get(:WIZARD_STEPS).keys
56
+ end
57
+
58
+ # It could be :new, :start
59
+ # Or resource, step
60
+ def resource_wizard_path(resource, step)
61
+ param = (resource.respond_to?(:to_param) ? resource.to_param : resource)
62
+ wizard_path(step, resource_name_id => param)
63
+ end
64
+
65
+ end
66
+ end
@@ -0,0 +1,30 @@
1
+ module Effective
2
+ module WizardController
3
+ module Actions
4
+
5
+ def new
6
+ Rails.logger.info 'Processed by Effective::WizardController#new'
7
+
8
+ self.resource ||= resource_scope.new
9
+ EffectiveResources.authorize!(self, :new, resource)
10
+
11
+ redirect_to resource_wizard_path(:new, resource_wizard_steps.first)
12
+ end
13
+
14
+ def show
15
+ Rails.logger.info 'Processed by Effective::WizardController#show'
16
+
17
+ render_wizard
18
+ end
19
+
20
+ def update
21
+ Rails.logger.info 'Processed by Effective::WizardController#update'
22
+
23
+ resource.assign_attributes(send(resource_params_method_name))
24
+
25
+ save_wizard_resource(resource)
26
+ end
27
+
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,74 @@
1
+ module Effective
2
+ module WizardController
3
+ module BeforeActions
4
+
5
+ # before_action :redirect_if_blank_step, only: [:show]
6
+ # When I visit /resources/1, redirect to /resources/1/build/step
7
+ def redirect_if_blank_step
8
+ if params[:id].present? && params[resource_name_id].blank?
9
+ params[resource_name_id] = params[:id]
10
+
11
+ assign_resource()
12
+
13
+ step = (resource.first_uncompleted_step || resource_wizard_steps.last)
14
+ redirect_to resource_wizard_path(resource, step)
15
+ end
16
+ end
17
+
18
+ # before_action :assign_resource, only: [:show, :update]
19
+ # Assigns the resource
20
+ def assign_resource
21
+ self.resource ||= find_wizard_resource
22
+ end
23
+
24
+ # before_action :authorize_resource, only: [:show, :update]
25
+ # Authorize the resource
26
+ def authorize_resource
27
+ EffectiveResources.authorize!(self, action_name.to_sym, resource)
28
+ end
29
+
30
+ # before_action :assign_required_steps, only: [:show, :update]
31
+ # Assign the required steps to Wickeds (dynamic steps)
32
+ def assign_required_steps
33
+ self.steps = resource.required_steps
34
+ end
35
+
36
+ # setup_wizard from Wicked called now
37
+
38
+ # before_action :enforce_can_visit_step, only: [:show, :update]
39
+ # Make sure I have permission for this step
40
+ def enforce_can_visit_step
41
+ return if step == 'wicked_finish'
42
+ return if resource.can_visit_step?(step)
43
+
44
+ next_step = wizard_steps.reverse.find { |step| resource.can_visit_step?(step) }
45
+ raise('There is no wizard step to visit. Make sure can_visit_step?(step) returns true for at least one step') unless next_step
46
+
47
+ if Rails.env.development?
48
+ Rails.logger.info " \e[31m\e[1mFAILED\e[0m\e[22m" # bold red
49
+ Rails.logger.info " Unable to visit step :#{step}. Last can_visit_step? is :#{next_step}. Change the acts_as_wizard model's can_visit_step?(step) function to change this."
50
+ end
51
+
52
+ flash[:success] = "You have been redirected to the #{resource_wizard_step_title(next_step)} step."
53
+ redirect_to wizard_path(next_step)
54
+ end
55
+
56
+ # before_action :assign_current_step, only: [:show, :update]
57
+ # Assign the urrent step to resource
58
+ def assign_current_step
59
+ if respond_to?(:current_user) && resource.respond_to?(:current_user=)
60
+ resource.current_user = current_user
61
+ end
62
+
63
+ resource.current_step = step.to_sym
64
+ end
65
+
66
+ # before_action :assign_page_title, only: [:show, :update]
67
+ # Assign page title
68
+ def assign_page_title
69
+ @page_title ||= resource_wizard_step_title(step)
70
+ end
71
+
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,24 @@
1
+ module Effective
2
+ module WizardController
3
+ module Save
4
+
5
+ def save_wizard_resource(resource, action = nil, options = {})
6
+ was_new_record = resource.new_record?
7
+ action ||= resource.respond_to?("#{step}!") ? step : :save
8
+
9
+ if save_resource(resource, action)
10
+ flash[:success] = options.delete(:success) || resource_flash(:success, resource, action)
11
+
12
+ @skip_to ||= next_step
13
+ @redirect_to ||= resource_wizard_path(resource, @skip_to) if was_new_record
14
+
15
+ redirect_to(@redirect_to || wizard_path(@skip_to))
16
+ else
17
+ flash.now[:danger] = options.delete(:error) || resource_flash(:danger, resource, action)
18
+ render_step(wizard_value(step), options)
19
+ end
20
+ end
21
+
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,23 @@
1
+ module Effective
2
+ module WizardController
3
+ module WickedOverrides
4
+
5
+ # Changes made here to work inside an effective rails engine
6
+ #
7
+ # https://github.com/zombocom/wicked/blob/main/lib/wicked/controller/concerns/path.rb
8
+ # https://github.com/rails/rails/blob/master/actionpack/lib/action_dispatch/routing/url_for.rb#L180
9
+ def wizard_path(goto_step = nil, options = {})
10
+ options = options.respond_to?(:to_h) ? options.to_h : options
11
+ options = { :controller => wicked_controller,
12
+ :action => 'show',
13
+ :id => goto_step || params[:id],
14
+ :only_path => true
15
+ }.merge options
16
+
17
+ merged_url_options = options.reverse_merge!(url_options)
18
+ effective_resource.url_helpers.url_for(merged_url_options)
19
+ end
20
+
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module EffectiveResourcesWizardHelper
4
+
5
+ def render_wizard_sidebar(resource, numbers: true, &block)
6
+ sidebar = content_tag(:div, class: 'nav list-group wizard-sidebar') do
7
+ resource.required_steps.map.with_index do |nav_step, index|
8
+ render_wizard_sidebar_item(resource, nav_step, (index + 1 if numbers))
9
+ end.join.html_safe
10
+ end
11
+
12
+ return sidebar unless block_given?
13
+
14
+ content_tag(:div, class: 'row') do
15
+ content_tag(:div, class: 'col-3') { sidebar } +
16
+ content_tag(:div, class: 'col-9') { yield }
17
+ end
18
+ end
19
+
20
+ def render_wizard_sidebar_item(resource, nav_step, index = nil)
21
+ # From Controller
22
+ current = (nav_step == step)
23
+ title = resource_wizard_step_title(nav_step)
24
+
25
+ # From Model
26
+ disabled = !resource.can_visit_step?(nav_step)
27
+
28
+ label = [index, title].compact.join('. ')
29
+ klass = ['list-group-item', ('active' if current), ('disabled' if disabled && !current)].compact.join(' ')
30
+
31
+ if (current || disabled)
32
+ content_tag(:li, label, class: klass)
33
+ else
34
+ link_to(label, wizard_path(nav_step), class: klass)
35
+ end
36
+ end
37
+
38
+ end
@@ -0,0 +1,103 @@
1
+ # ActsAsWizard
2
+ # Works alongside wicked gem to build a wizard
3
+ # https://github.com/zombocom/wicked
4
+
5
+ # acts_as_wizard(start: 'Start Step', select: 'Select Step', finish: 'Finished')
6
+
7
+ module ActsAsWizard
8
+ extend ActiveSupport::Concern
9
+
10
+ module Base
11
+ def acts_as_wizard(steps)
12
+ raise 'acts_as_wizard expected a Hash of steps' unless steps.kind_of?(Hash)
13
+
14
+ unless steps.all? { |k, v| k.kind_of?(Symbol) && v.kind_of?(String) }
15
+ raise 'acts_as_wizard expected a Hash of symbol => String steps'
16
+ end
17
+
18
+ @acts_as_wizard_options = {steps: steps}
19
+
20
+ include ::ActsAsWizard
21
+ end
22
+ end
23
+
24
+ included do
25
+ acts_as_wizard_options = @acts_as_wizard_options
26
+
27
+ attr_accessor :current_step
28
+ attr_accessor :current_user
29
+
30
+ if Rails.env.test? # So our tests can override the required_steps method
31
+ cattr_accessor :test_required_steps
32
+ end
33
+
34
+ const_set(:WIZARD_STEPS, acts_as_wizard_options[:steps])
35
+
36
+ effective_resource do
37
+ wizard_steps :text, permitted: false
38
+ end
39
+
40
+ serialize :wizard_steps, Hash
41
+
42
+ before_save(if: -> { current_step.present? }) do
43
+ wizard_steps[current_step.to_sym] ||= Time.zone.now
44
+ end
45
+
46
+ def can_visit_step?(step)
47
+ can_revisit_completed_steps(step)
48
+ end
49
+
50
+ def required_steps
51
+ return self.class.test_required_steps if Rails.env.test? && self.class.test_required_steps.present?
52
+ self.class.const_get(:WIZARD_STEPS).keys
53
+ end
54
+
55
+ def first_completed_step
56
+ required_steps.find { |step| has_completed_step?(step) }
57
+ end
58
+
59
+ def last_completed_step
60
+ required_steps.reverse.find { |step| has_completed_step?(step) }
61
+ end
62
+
63
+ def first_uncompleted_step
64
+ required_steps.find { |step| has_completed_step?(step) == false }
65
+ end
66
+
67
+ def has_completed_step?(step)
68
+ wizard_steps[step].present?
69
+ end
70
+
71
+ def previous_step(step)
72
+ index = required_steps.index(step)
73
+ required_steps[index-1] unless index == 0 || index.nil?
74
+ end
75
+
76
+ def has_completed_previous_step?(step)
77
+ previous = previous_step(step)
78
+ previous.blank? || has_completed_step?(previous)
79
+ end
80
+
81
+ def has_completed_last_step?
82
+ has_completed_step?(required_steps.last)
83
+ end
84
+
85
+ private
86
+
87
+ def can_revisit_completed_steps(step)
88
+ return (step == required_steps.last) if has_completed_last_step?
89
+ has_completed_previous_step?(step)
90
+ end
91
+
92
+ def cannot_revisit_completed_steps(step)
93
+ return (step == required_steps.last) if has_completed_last_step?
94
+ has_completed_previous_step?(step) && !has_completed_step?(step)
95
+ end
96
+
97
+ end
98
+
99
+ module ClassMethods
100
+ def acts_as_wizard?; true; end
101
+ end
102
+
103
+ end
@@ -15,8 +15,8 @@ module Effective
15
15
  include Effective::Resources::Sql
16
16
 
17
17
  # post, Post, Admin::Post, admin::Post, admin/posts, admin/post, admin/effective::post
18
- def initialize(input, namespace: nil, &block)
19
- _initialize_input(input, namespace: namespace)
18
+ def initialize(input, namespace: nil, relation: nil, &block)
19
+ _initialize_input(input, namespace: namespace, relation: relation)
20
20
  _initialize_model(&block) if block_given?
21
21
  self
22
22
  end
@@ -12,8 +12,8 @@ module Effective
12
12
  def routes
13
13
  @routes ||= (
14
14
  matches = [
15
- [namespace, plural_name].compact.join('/'),
16
- [namespace, name].compact.join('/')
15
+ [namespace, route_name.pluralize].compact.join('/'),
16
+ [namespace, route_name].compact.join('/'),
17
17
  ]
18
18
 
19
19
  # Check main Rails app
@@ -21,9 +21,25 @@ module Effective
21
21
  (matches & [route.defaults[:controller]]).present? && !route.name.to_s.end_with?('root')
22
22
  end
23
23
 
24
+ if routes.blank?
25
+ matches = [
26
+ [namespace, plural_name].compact.join('/'),
27
+ [namespace, name].compact.join('/')
28
+ ]
29
+
30
+ # Check main Rails app
31
+ routes = Rails.application.routes.routes.select do |route|
32
+ (matches & [route.defaults[:controller]]).present? && !route.name.to_s.end_with?('root')
33
+ end
34
+ end
35
+
24
36
  # Check engine routes
25
37
  if routes.blank?
26
38
  matches = [
39
+ [namespace, route_name.pluralize].compact.join('/'),
40
+ [namespace, route_name].compact.join('/'),
41
+ ['effective', namespace, route_name.pluralize].compact.join('/'),
42
+ ['effective', namespace, route_name].compact.join('/'),
27
43
  [namespace, plural_name].compact.join('/'),
28
44
  [namespace, name].compact.join('/'),
29
45
  ['effective', namespace, plural_name].compact.join('/'),
@@ -35,7 +51,11 @@ module Effective
35
51
  (matches & [route.defaults[:controller]]).present? && !route.name.to_s.end_with?('root')
36
52
  end
37
53
 
38
- break if routes.present?
54
+ if routes.present?
55
+ @routes_app = engine
56
+ break
57
+ end
58
+
39
59
  end
40
60
  end
41
61
 
@@ -43,6 +63,14 @@ module Effective
43
63
  )
44
64
  end
45
65
 
66
+ def routes_app
67
+ @routes_app if routes.present?
68
+ end
69
+
70
+ def url_helpers
71
+ (routes_app || Rails.application).routes.url_helpers
72
+ end
73
+
46
74
  # Effective::Resource.new('admin/posts').action_path_helper(:edit) => 'edit_admin_posts_path'
47
75
  # This will return empty for create, update and destroy
48
76
  def action_path_helper(action)
@@ -4,9 +4,9 @@ module Effective
4
4
 
5
5
  private
6
6
 
7
- def _initialize_input(input, namespace: nil)
7
+ def _initialize_input(input, namespace: nil, relation: nil)
8
8
  @initialized_name = input
9
- @model_klass = _klass_by_input(input)
9
+ @model_klass = (relation ? _klass_by_input(relation) : _klass_by_input(input))
10
10
 
11
11
  # Consider namespaces
12
12
  if namespace
@@ -18,20 +18,24 @@ module Effective
18
18
  end
19
19
 
20
20
  # Consider relation
21
+ if relation.kind_of?(ActiveRecord::Relation)
22
+ @relation ||= relation
23
+ end
24
+
21
25
  if input.kind_of?(ActiveRecord::Relation)
22
26
  @relation ||= input
23
27
  end
24
28
 
25
29
  if input.kind_of?(ActiveRecord::Reflection::MacroReflection) && input.scope
26
- @relation ||= klass.where(nil).merge(input.scope)
30
+ @relation ||= @model_klass.where(nil).merge(input.scope)
27
31
  end
28
32
 
29
33
  # Consider instance
30
- if klass && input.instance_of?(klass)
34
+ if @model_klass && input.instance_of?(@model_klass)
31
35
  @instance ||= input
32
36
  end
33
37
 
34
- if klass && input.kind_of?(Array) && input.last.instance_of?(klass)
38
+ if @model_klass && input.kind_of?(Array) && input.last.instance_of?(@model_klass)
35
39
  @instance ||= input.last
36
40
  end
37
41
  end
@@ -19,6 +19,10 @@ module Effective
19
19
  @initialized_name
20
20
  end
21
21
 
22
+ def route_name # 'post' initialized from the controller_path/initialized_name and not the class
23
+ @route_name ||= (initialized_name.to_s.split(SPLIT).last || '').singularize.underscore
24
+ end
25
+
22
26
  def class_name # 'Effective::Post'
23
27
  @model_klass ? @model_klass.name : name.classify
24
28
  end
@@ -44,11 +48,11 @@ module Effective
44
48
  end
45
49
 
46
50
  def human_name
47
- class_name.gsub('::', ' ').underscore.gsub('_', ' ')
51
+ name.gsub('::', ' ').underscore.gsub('_', ' ')
48
52
  end
49
53
 
50
54
  def human_plural_name
51
- class_name.pluralize.gsub('::', ' ').underscore.gsub('_', ' ')
55
+ name.pluralize.gsub('::', ' ').underscore.gsub('_', ' ')
52
56
  end
53
57
 
54
58
  end
@@ -4,8 +4,10 @@ module Effective
4
4
  TARGET_LIST_LIMIT = 1500
5
5
  TARGET_KEYS_LIMIT = 30000
6
6
 
7
+ # This could be active_model? in which we just return the klass itself here
8
+ # This value ends up being crud_controller resource_scope()
7
9
  def relation
8
- @relation ||= klass.where(nil)
10
+ @relation ||= (klass.respond_to?(:where) ? klass.where(nil) : klass)
9
11
  end
10
12
 
11
13
  # When Effective::Resource is initialized with an ActiveRecord relation, the following
@@ -26,6 +26,7 @@ module EffectiveResources
26
26
  ActiveRecord::Base.extend(ActsAsTokened::Base)
27
27
  ActiveRecord::Base.extend(ActsAsSlugged::Base)
28
28
  ActiveRecord::Base.extend(ActsAsStatused::Base)
29
+ ActiveRecord::Base.extend(ActsAsWizard::Base)
29
30
  ActiveRecord::Base.extend(EffectiveResource::Base)
30
31
  end
31
32
  end
@@ -51,6 +52,10 @@ module EffectiveResources
51
52
  # resources :things, concerns: :acts_as_archived
52
53
  initializer 'effective_resources.routes_concern' do |app|
53
54
  ActionDispatch::Routing::Mapper.include(ActsAsArchived::RoutesConcern)
55
+
56
+ # Doesn't seem to work with the on_load in rails 6.0
57
+ #ActiveSupport.on_load :action_controller_base do
58
+ #end
54
59
  end
55
60
 
56
61
  # Register the flash_messages concern so that it can be called in ActionController
@@ -1,3 +1,3 @@
1
1
  module EffectiveResources
2
- VERSION = '1.6.2'.freeze
2
+ VERSION = '1.7.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: 1.6.2
4
+ version: 1.7.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: 2020-11-23 00:00:00.000000000 Z
11
+ date: 2021-01-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -94,6 +94,20 @@ dependencies:
94
94
  - - ">="
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: wicked
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
97
111
  description: Make any controller an effective resource controller.
98
112
  email:
99
113
  - info@codeandeffect.com
@@ -114,12 +128,19 @@ files:
114
128
  - app/controllers/concerns/effective/crud_controller/save.rb
115
129
  - app/controllers/concerns/effective/crud_controller/submits.rb
116
130
  - app/controllers/concerns/effective/flash_messages.rb
131
+ - app/controllers/concerns/effective/wizard_controller.rb
132
+ - app/controllers/concerns/effective/wizard_controller/actions.rb
133
+ - app/controllers/concerns/effective/wizard_controller/before_actions.rb
134
+ - app/controllers/concerns/effective/wizard_controller/save.rb
135
+ - app/controllers/concerns/effective/wizard_controller/wicked_overrides.rb
117
136
  - app/helpers/effective_resources_helper.rb
118
137
  - app/helpers/effective_resources_private_helper.rb
138
+ - app/helpers/effective_resources_wizard_helper.rb
119
139
  - app/models/concerns/acts_as_archived.rb
120
140
  - app/models/concerns/acts_as_slugged.rb
121
141
  - app/models/concerns/acts_as_statused.rb
122
142
  - app/models/concerns/acts_as_tokened.rb
143
+ - app/models/concerns/acts_as_wizard.rb
123
144
  - app/models/concerns/effective_resource.rb
124
145
  - app/models/effective/access_denied.rb
125
146
  - app/models/effective/action_failed.rb