wicked-pipeline 0.1.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 +7 -0
- data/.rspec +1 -0
- data/.standard.yml +6 -0
- data/.yardopts +1 -0
- data/CHANGELOG.md +7 -0
- data/Gemfile +16 -0
- data/LICENSE.txt +21 -0
- data/README.md +449 -0
- data/Rakefile +14 -0
- data/app/controllers/wicked/pipeline/base_steps_controller.rb +191 -0
- data/lib/wicked/pipeline/base_pipeline.rb +126 -0
- data/lib/wicked/pipeline/base_step.rb +304 -0
- data/lib/wicked/pipeline/engine.rb +27 -0
- data/lib/wicked/pipeline/readonly_step.rb +23 -0
- data/lib/wicked/pipeline/version.rb +7 -0
- data/lib/wicked/pipeline.rb +37 -0
- data/sig/wicked/pipeline.rbs +5 -0
- data/wicked-pipeline.gemspec +35 -0
- metadata +157 -0
@@ -0,0 +1,191 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Wicked
|
4
|
+
module Pipeline
|
5
|
+
class BaseStepsController < parent_controller.constantize
|
6
|
+
include Wicked::Wizard
|
7
|
+
|
8
|
+
before_action :_set_steps
|
9
|
+
before_action :setup_wizard
|
10
|
+
|
11
|
+
rescue_from Wicked::Wizard::InvalidStepError, with: :handle_invalid_step!
|
12
|
+
|
13
|
+
attr_reader :step_processor
|
14
|
+
helper_method :step_processor
|
15
|
+
|
16
|
+
def show
|
17
|
+
yield if block_given?
|
18
|
+
|
19
|
+
_set_processor_required!
|
20
|
+
|
21
|
+
if step == Wicked::FINISH_STEP && steps_metadata[steps.last][:valid] && steps_metadata[steps.last][:accessible]
|
22
|
+
jump_to(steps.last) if steps_metadata[steps.last][:blocking]
|
23
|
+
render_wizard(nil, {}, resource_url_params)
|
24
|
+
elsif _accessible_steps.include?(step_processor.step_name)
|
25
|
+
render_step(step_processor.step_name)
|
26
|
+
else
|
27
|
+
jump_to(_accessible_steps.last)
|
28
|
+
render_wizard(nil, {}, resource_url_params)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def update
|
33
|
+
params.require(:id)
|
34
|
+
|
35
|
+
yield if block_given?
|
36
|
+
|
37
|
+
_set_processor_required!
|
38
|
+
|
39
|
+
unless step_processor.resource.new_record?
|
40
|
+
render_wizard(step_processor, {}, resource_url_params)
|
41
|
+
return
|
42
|
+
end
|
43
|
+
|
44
|
+
process_resource!(step_processor)
|
45
|
+
|
46
|
+
if step_processor.saved?
|
47
|
+
render_wizard(nil, {}, resource_url_params)
|
48
|
+
else
|
49
|
+
render_step(step_processor.step_name)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
protected
|
54
|
+
|
55
|
+
def resource_param_name
|
56
|
+
raise NotImplementedError, "resource_param_name must be implemented in subclasses of #{self.class.name}"
|
57
|
+
end
|
58
|
+
|
59
|
+
def resource_url_params
|
60
|
+
_set_processor_required!
|
61
|
+
{resource_param_name => step_processor.id}
|
62
|
+
end
|
63
|
+
|
64
|
+
def find_resource
|
65
|
+
raise NotImplementedError, "find_resource must be implemented in subclasses of #{self.class.name}"
|
66
|
+
end
|
67
|
+
|
68
|
+
def steps_pipeline
|
69
|
+
raise NotImplementedError, "steps_pipeline must be implemented in subclasses of #{self.class.name}"
|
70
|
+
end
|
71
|
+
helper_method :steps_pipeline
|
72
|
+
|
73
|
+
def step_classes
|
74
|
+
Array(steps_pipeline)
|
75
|
+
end
|
76
|
+
|
77
|
+
def step_class
|
78
|
+
return @step_class if @step_class
|
79
|
+
|
80
|
+
the_step =
|
81
|
+
case step
|
82
|
+
when Wicked::FIRST_STEP
|
83
|
+
steps.first
|
84
|
+
when Wicked::LAST_STEP
|
85
|
+
steps.last
|
86
|
+
when Wicked::FINISH_STEP
|
87
|
+
steps.last
|
88
|
+
else
|
89
|
+
step || steps.first
|
90
|
+
end
|
91
|
+
|
92
|
+
@step_class = _step_classes.fetch(the_step.to_s)
|
93
|
+
end
|
94
|
+
|
95
|
+
def step_path(step_name: nil, **options)
|
96
|
+
wizard_path(step_name.presence || step_class.step_name, options.reverse_merge(resource_url_params))
|
97
|
+
end
|
98
|
+
helper_method :step_path
|
99
|
+
|
100
|
+
def steps_metadata
|
101
|
+
return @steps_metadata if @steps_metadata
|
102
|
+
|
103
|
+
@steps_metadata = step_classes.each_with_index.with_object(ActiveSupport::HashWithIndifferentAccess.new) do |(klass, idx), obj|
|
104
|
+
processor = klass.new(_shallow_resource)
|
105
|
+
|
106
|
+
obj[processor.step_name] = {
|
107
|
+
url: step_path(step_name: processor.step_name),
|
108
|
+
active: (step.present? && processor.step_name.to_s == step.to_s) || (step.blank? && idx == 0)
|
109
|
+
}.merge(_steps_metadata[processor.step_name])
|
110
|
+
end
|
111
|
+
end
|
112
|
+
helper_method :steps_metadata
|
113
|
+
|
114
|
+
def current_step
|
115
|
+
return @current_step if @current_step
|
116
|
+
|
117
|
+
_set_processor_required!
|
118
|
+
|
119
|
+
@current_step = step_processor.step_name
|
120
|
+
end
|
121
|
+
helper_method :current_step
|
122
|
+
|
123
|
+
def step_i18n_scope
|
124
|
+
return @step_i18n_scope if @step_i18n_scope
|
125
|
+
|
126
|
+
_set_processor_required!
|
127
|
+
|
128
|
+
@step_i18n_scope = "#{controller_path}.#{step_processor.step_name}".tr("/", ".")
|
129
|
+
end
|
130
|
+
helper_method :step_i18n_scope
|
131
|
+
|
132
|
+
def handle_invalid_step!(exception)
|
133
|
+
if block_given?
|
134
|
+
yield
|
135
|
+
else
|
136
|
+
error_details = {
|
137
|
+
:step => params[:id],
|
138
|
+
resource_param_name => params[resource_param_name],
|
139
|
+
:controller => params[:controller],
|
140
|
+
:action => params[:action]
|
141
|
+
}
|
142
|
+
|
143
|
+
logger = ActiveSupport::TaggedLogging.new(Rails.logger)
|
144
|
+
logger.tagged("[#{self.class.name}]") do
|
145
|
+
logger.error("#{exception.class.name}: #{exception.message} -- details: #{error_details.to_json}")
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
redirect_to url_for({
|
150
|
+
:controller => params[:controller],
|
151
|
+
:action => params[:action],
|
152
|
+
resource_param_name => params[resource_param_name]
|
153
|
+
})
|
154
|
+
end
|
155
|
+
|
156
|
+
private
|
157
|
+
|
158
|
+
def _shallow_resource
|
159
|
+
@_shallow_resource ||= find_resource
|
160
|
+
end
|
161
|
+
|
162
|
+
def _set_processor_required!
|
163
|
+
raise "@step_processor must not be nil" if step_processor.nil?
|
164
|
+
end
|
165
|
+
|
166
|
+
def _step_classes
|
167
|
+
@_step_classes ||= step_classes.each_with_object(ActiveSupport::HashWithIndifferentAccess.new) do |klass, obj|
|
168
|
+
obj[klass.step_name] = klass
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
def _steps_metadata
|
173
|
+
return @_steps_metadata if @_steps_metadata
|
174
|
+
|
175
|
+
@_steps_metadata = steps_pipeline.metadata(_shallow_resource)
|
176
|
+
end
|
177
|
+
|
178
|
+
def _accessible_steps
|
179
|
+
@_accessible_steps ||= _steps_metadata.each_with_object([]) do |(step_name, step_metadata), obj|
|
180
|
+
break obj unless step_metadata[:accessible]
|
181
|
+
|
182
|
+
obj << step_name
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
def _set_steps
|
187
|
+
self.steps = _step_classes.keys
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Wicked
|
4
|
+
module Pipeline
|
5
|
+
class BasePipeline
|
6
|
+
def steps
|
7
|
+
[]
|
8
|
+
end
|
9
|
+
|
10
|
+
def valid?(resource)
|
11
|
+
steps.all? { |step| step.new(resource).valid? }
|
12
|
+
end
|
13
|
+
|
14
|
+
def blocked?(resource)
|
15
|
+
steps.any? { |step| step.new(resource).blocking? }
|
16
|
+
end
|
17
|
+
|
18
|
+
def first_step?(step)
|
19
|
+
steps.first == step
|
20
|
+
end
|
21
|
+
|
22
|
+
def last_step?(step)
|
23
|
+
steps.last == step
|
24
|
+
end
|
25
|
+
|
26
|
+
def find_step(the_step)
|
27
|
+
steps.find { |step| step == the_step || step.step_name == the_step }
|
28
|
+
end
|
29
|
+
|
30
|
+
def next_step(current_step)
|
31
|
+
step, step_index = steps.each_with_index.find { |step, _| step == current_step || step.step_name == current_step }
|
32
|
+
|
33
|
+
last_step?(step) || step_index.nil? ? nil : steps[step_index + 1]
|
34
|
+
end
|
35
|
+
|
36
|
+
def previous_step(current_step)
|
37
|
+
step, step_index = steps.each_with_index.find { |step, _| step == current_step || step.step_name == current_step }
|
38
|
+
|
39
|
+
first_step?(step) || step_index.nil? ? nil : steps[step_index - 1]
|
40
|
+
end
|
41
|
+
|
42
|
+
def next_step?(current_step, step)
|
43
|
+
!current_step.nil? && !step.nil? &&
|
44
|
+
next_step(current_step) == step
|
45
|
+
end
|
46
|
+
|
47
|
+
def previous_step?(current_step, step)
|
48
|
+
!current_step.nil? && !step.nil? &&
|
49
|
+
previous_step(current_step) == step
|
50
|
+
end
|
51
|
+
|
52
|
+
def metadata(resource)
|
53
|
+
steps.each_with_index.with_object(ActiveSupport::HashWithIndifferentAccess.new) do |(step, idx), obj|
|
54
|
+
processor = step.new(resource)
|
55
|
+
previous_step_metadata = obj.fetch(obj.keys[idx - 1], {})
|
56
|
+
|
57
|
+
obj[processor.step_name] = {
|
58
|
+
valid: processor.valid?,
|
59
|
+
blocking: processor.blocking?,
|
60
|
+
accessible: idx == 0 || (
|
61
|
+
!previous_step_metadata[:blocking] &&
|
62
|
+
previous_step_metadata[:accessible] &&
|
63
|
+
previous_step_metadata[:valid]
|
64
|
+
)
|
65
|
+
}
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def to_ary
|
70
|
+
steps
|
71
|
+
end
|
72
|
+
alias_method :to_a, :to_ary
|
73
|
+
|
74
|
+
class << self
|
75
|
+
def steps
|
76
|
+
new.steps
|
77
|
+
end
|
78
|
+
|
79
|
+
def valid?(resource)
|
80
|
+
new.valid?(resource)
|
81
|
+
end
|
82
|
+
|
83
|
+
def blocked?(resource)
|
84
|
+
new.blocked?(resource)
|
85
|
+
end
|
86
|
+
|
87
|
+
def first_step?(step)
|
88
|
+
new.first_step?(step)
|
89
|
+
end
|
90
|
+
|
91
|
+
def last_step?(step)
|
92
|
+
new.last_step?(step)
|
93
|
+
end
|
94
|
+
|
95
|
+
def find_step(step)
|
96
|
+
new.find_step(step)
|
97
|
+
end
|
98
|
+
|
99
|
+
def next_step(current_step)
|
100
|
+
new.next_step(current_step)
|
101
|
+
end
|
102
|
+
|
103
|
+
def previous_step(current_step)
|
104
|
+
new.previous_step(current_step)
|
105
|
+
end
|
106
|
+
|
107
|
+
def next_step?(current_step, step)
|
108
|
+
new.next_step(current_step, step)
|
109
|
+
end
|
110
|
+
|
111
|
+
def previous_step?(current_step, step)
|
112
|
+
new.previous_step(current_step, step)
|
113
|
+
end
|
114
|
+
|
115
|
+
def metadata(resource)
|
116
|
+
new.metadata(resource)
|
117
|
+
end
|
118
|
+
|
119
|
+
def to_ary
|
120
|
+
new.to_ary
|
121
|
+
end
|
122
|
+
alias_method :to_a, :to_ary
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,304 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_model"
|
4
|
+
|
5
|
+
module Wicked
|
6
|
+
module Pipeline
|
7
|
+
class BaseStep
|
8
|
+
include ActiveModel::Attributes
|
9
|
+
include ActiveModel::AttributeAssignment
|
10
|
+
include ActiveModel::Validations
|
11
|
+
|
12
|
+
# @return [ActiveRecord::Base] The record associated with the step
|
13
|
+
attr_reader :resource
|
14
|
+
|
15
|
+
# @param resource [ActiveRecord::Base] The record associated with the step
|
16
|
+
# @param params [Hash, ActionController::Parameters] Attribute changes to be applied to the resource
|
17
|
+
def initialize(resource, params = nil)
|
18
|
+
super()
|
19
|
+
|
20
|
+
@resource = resource
|
21
|
+
|
22
|
+
attributes.each_key do |key|
|
23
|
+
public_send("#{key}=", resource.public_send(key)) if resource.respond_to?(key)
|
24
|
+
end
|
25
|
+
|
26
|
+
unless params.nil?
|
27
|
+
parameters = _permit_params!(params)
|
28
|
+
self.attributes = parameters.select { |key, _| respond_to?("#{key}=") }
|
29
|
+
resource.attributes = parameters.select { |key, _| resource.respond_to?("#{key}=") }
|
30
|
+
end
|
31
|
+
|
32
|
+
regenerate_blocking_reasons!
|
33
|
+
end
|
34
|
+
|
35
|
+
# Saves the resource
|
36
|
+
#
|
37
|
+
# If the step is readonly the record will not
|
38
|
+
# be saved and the method will return +true+
|
39
|
+
#
|
40
|
+
# @return [Boolean]
|
41
|
+
def save(**options, &block)
|
42
|
+
return true if readonly?
|
43
|
+
|
44
|
+
cleanup_stale_data
|
45
|
+
|
46
|
+
valid? && resource.save(**options, &block)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Check if both the step and the resource are valid
|
50
|
+
#
|
51
|
+
# Always returns +true+ if the step is readonly
|
52
|
+
#
|
53
|
+
# @return [Boolean]
|
54
|
+
def valid?
|
55
|
+
return true if readonly?
|
56
|
+
|
57
|
+
is_valid = super
|
58
|
+
resource.validate
|
59
|
+
resource.errors.merge!(errors)
|
60
|
+
regenerate_blocking_reasons!
|
61
|
+
is_valid && resource.errors.none?
|
62
|
+
end
|
63
|
+
|
64
|
+
# Checks if the resource exists in the database
|
65
|
+
#
|
66
|
+
# Always returns +true+ if the step is readonly
|
67
|
+
#
|
68
|
+
# @return [Boolean]
|
69
|
+
def persisted?
|
70
|
+
readonly? || resource.persisted?
|
71
|
+
end
|
72
|
+
|
73
|
+
# Checks that the step has been successfully saved and there are no unsaved changes
|
74
|
+
#
|
75
|
+
# Always returns +true+ if the step is readonly
|
76
|
+
#
|
77
|
+
# @return [Boolean]
|
78
|
+
def saved?
|
79
|
+
readonly? || (persisted? && resource.saved_changes? && !resource.has_changes_to_save?)
|
80
|
+
end
|
81
|
+
|
82
|
+
# Checks if the step is blocking subsequent steps in a pipeline. In other words,
|
83
|
+
# if a step is "blocking", all the subsequent steps in the pipeline will be marked
|
84
|
+
# as inaccessible.
|
85
|
+
#
|
86
|
+
# This can be used to short-circuit a pipeline.
|
87
|
+
#
|
88
|
+
# @return [Boolean]
|
89
|
+
def blocking?
|
90
|
+
false
|
91
|
+
end
|
92
|
+
|
93
|
+
# Check if the step is readonly
|
94
|
+
#
|
95
|
+
# @return [Boolean]
|
96
|
+
def readonly?
|
97
|
+
false
|
98
|
+
end
|
99
|
+
|
100
|
+
# Returns the reason why a step is blocking, or +nil+ if it is not blocking.
|
101
|
+
#
|
102
|
+
# This should be used to set an overall blocking reason.
|
103
|
+
# To set per-attribute blocking reasons use {#blocking_reasons}.
|
104
|
+
#
|
105
|
+
# @return [String, nil]
|
106
|
+
# @see #blocking?
|
107
|
+
# @see #blocking_reasons
|
108
|
+
def blocking_reason
|
109
|
+
nil
|
110
|
+
end
|
111
|
+
|
112
|
+
# Returns reasons for each attribute why a step is blocking.
|
113
|
+
#
|
114
|
+
# @note This method should not be overridden, it should just be used to access
|
115
|
+
# blocking reasons. To add a new reason, just add it to the hash. To reset all
|
116
|
+
# the reasons, override the {#regenerate_blocking_reasons!} method in subsclasses.
|
117
|
+
#
|
118
|
+
# @note Warning: Every time {#valid?} is called the blocking reasons will be regenereted.
|
119
|
+
#
|
120
|
+
# @return [Hash{String,Symbol => Array<String>}] hash of reasons for each attribute.
|
121
|
+
# @see #blocking?
|
122
|
+
def blocking_reasons
|
123
|
+
@blocking_reasons ||= ActiveSupport::HashWithIndifferentAccess.new { |h, k| h[k] = [] }
|
124
|
+
end
|
125
|
+
|
126
|
+
# Returns the String representation of the step object
|
127
|
+
#
|
128
|
+
# @example Step object without a namespace
|
129
|
+
# FinancialSituationnStep.to_s #=> "financial_situation"
|
130
|
+
#
|
131
|
+
# @example Nested step object
|
132
|
+
# Users::FinancialSituationnStep.to_s #=> "users/financial_situation"
|
133
|
+
#
|
134
|
+
# @return [String]
|
135
|
+
def self.to_s
|
136
|
+
@_to_s ||= name.to_s.underscore.sub(/_step\z/, "")
|
137
|
+
end
|
138
|
+
|
139
|
+
# Returns the step name of the step object
|
140
|
+
#
|
141
|
+
# @example Step object without a namespace
|
142
|
+
# FinancialSituationnStep.step_name #=> "financial_situation"
|
143
|
+
#
|
144
|
+
# @example Nested step object
|
145
|
+
# Users::FinancialSituationnStep.step_name #=> "financial_situation"
|
146
|
+
#
|
147
|
+
# @return [String]
|
148
|
+
def self.step_name
|
149
|
+
@_step_name ||= to_s.gsub(%r{(?:\w+/)*}, "")
|
150
|
+
end
|
151
|
+
|
152
|
+
# Returns the String representation of the step object
|
153
|
+
#
|
154
|
+
# @example Step object without a namespace
|
155
|
+
# FinancialSituationnStep.new(resource).to_s #=> "financial_situation"
|
156
|
+
#
|
157
|
+
# @example Nested step object
|
158
|
+
# Users::FinancialSituationnStep.new(resource).to_s #=> "users/financial_situation"
|
159
|
+
#
|
160
|
+
# @see .to_s
|
161
|
+
#
|
162
|
+
# @return [String]
|
163
|
+
def to_s
|
164
|
+
self.class.to_s
|
165
|
+
end
|
166
|
+
|
167
|
+
# Returns the step name of the step object
|
168
|
+
#
|
169
|
+
# @example Step object without a namespace
|
170
|
+
# FinancialSituationnStep.new(resource).step_name #=> "financial_situation"
|
171
|
+
#
|
172
|
+
# @example Nested step object
|
173
|
+
# Users::FinancialSituationnStep.new(resource).step_name #=> "financial_situation"
|
174
|
+
#
|
175
|
+
# @see .step_name
|
176
|
+
#
|
177
|
+
# @return [String]
|
178
|
+
def step_name
|
179
|
+
self.class.step_name
|
180
|
+
end
|
181
|
+
|
182
|
+
# Check whether two steps are the same
|
183
|
+
#
|
184
|
+
# It returns +true+ only if +other+ is the same step class then +self+ or if +self.to_s+ is equal to +other+.
|
185
|
+
# +other.step_name+.
|
186
|
+
#
|
187
|
+
# @param other [BaseStep, String, Symbol] the other step tested for equality
|
188
|
+
# @return [Boolean]
|
189
|
+
#
|
190
|
+
# @see .to_s
|
191
|
+
def self.==(other)
|
192
|
+
super || to_s == other.to_s
|
193
|
+
end
|
194
|
+
|
195
|
+
# (see .==)
|
196
|
+
def self.eql?(other)
|
197
|
+
self == other
|
198
|
+
end
|
199
|
+
|
200
|
+
# (see .==)
|
201
|
+
# @see .==
|
202
|
+
def self.equal?(other)
|
203
|
+
self == other
|
204
|
+
end
|
205
|
+
|
206
|
+
# (see .equal?)
|
207
|
+
def self.===(other)
|
208
|
+
equal?(other)
|
209
|
+
end
|
210
|
+
|
211
|
+
# (see .equal?)
|
212
|
+
def equal?(other)
|
213
|
+
self.class.equal?(other)
|
214
|
+
end
|
215
|
+
|
216
|
+
# (see .equal?)
|
217
|
+
def ===(other)
|
218
|
+
equal?(other)
|
219
|
+
end
|
220
|
+
|
221
|
+
# @return [Number, Symbol] The ID of the resource or :new if the the resource is a new record
|
222
|
+
def id
|
223
|
+
resource.id || :new
|
224
|
+
end
|
225
|
+
|
226
|
+
# Validate the associations of of the resource
|
227
|
+
#
|
228
|
+
# @param associations [Symbol]
|
229
|
+
# @param options [void] <b>Do not use!</b> It is here for compatibility reasons.
|
230
|
+
# @note Do not use the +options+ param, it is here for compatibility with
|
231
|
+
# the +ActiveRecord::Validations::AssociatedValidator+.
|
232
|
+
#
|
233
|
+
# @example
|
234
|
+
# class OwnershipStep < BaseStep
|
235
|
+
# validates_associated :beneficiaries
|
236
|
+
# end
|
237
|
+
def self.validates_associated(*associations, **options)
|
238
|
+
if options.present?
|
239
|
+
warn "WARN: calling .validates_associated with options from step objects is not supported."
|
240
|
+
end
|
241
|
+
|
242
|
+
associations.each do |association|
|
243
|
+
unless respond_to?(association)
|
244
|
+
delegate association, to: :resource
|
245
|
+
end
|
246
|
+
|
247
|
+
validate do
|
248
|
+
Array(public_send(association)).each do |associated_object|
|
249
|
+
unless associated_object.valid?
|
250
|
+
errors.add(association, :invalid)
|
251
|
+
break
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
end
|
257
|
+
private_class_method :validates_associated
|
258
|
+
|
259
|
+
def self.i18n_scope
|
260
|
+
@_i18n_scope ||= name.underscore.tr("/", ".").prepend("steps.")
|
261
|
+
end
|
262
|
+
|
263
|
+
delegate :i18n_scope, to: :class
|
264
|
+
|
265
|
+
protected
|
266
|
+
|
267
|
+
def permitted_params
|
268
|
+
raise NotImplementedError, "permitted_params must be implemented in subclasses of BaseStep"
|
269
|
+
end
|
270
|
+
|
271
|
+
# This method is meant to be overridden.
|
272
|
+
# Make sure to call #super before any custom code!
|
273
|
+
def regenerate_blocking_reasons!
|
274
|
+
blocking_reasons.clear
|
275
|
+
end
|
276
|
+
|
277
|
+
def stale_attributes
|
278
|
+
[]
|
279
|
+
end
|
280
|
+
|
281
|
+
def cleanup_stale_data
|
282
|
+
unless stale_attributes.blank?
|
283
|
+
stale_attributes_params = Array(stale_attributes).zip([]).to_h
|
284
|
+
self.attributes = stale_attributes_params
|
285
|
+
resource.attributes = stale_attributes_params
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
private
|
290
|
+
|
291
|
+
def _permit_params!(params)
|
292
|
+
case params
|
293
|
+
when ActionController::Parameters
|
294
|
+
params.permit(*permitted_params)
|
295
|
+
when Hash
|
296
|
+
normalized_permitted_params = permitted_params.flat_map { |param| param.is_a?(Hash) ? param.keys : param }
|
297
|
+
params.slice(*normalized_permitted_params)
|
298
|
+
else
|
299
|
+
raise ArgumentError, "expected one of ActionController::Parameters, Hash but received #{params.class}"
|
300
|
+
end
|
301
|
+
end
|
302
|
+
end
|
303
|
+
end
|
304
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "action_controller"
|
4
|
+
|
5
|
+
module Wicked
|
6
|
+
module Pipeline
|
7
|
+
class Engine < ::Rails::Engine
|
8
|
+
isolate_namespace Wicked::Pipeline
|
9
|
+
|
10
|
+
def self.i18n_scope
|
11
|
+
@i18n_scope ||= name.split("::").tap(&:pop).map(&:underscore).join(".")
|
12
|
+
end
|
13
|
+
|
14
|
+
config.generators do |g|
|
15
|
+
g.helper false
|
16
|
+
end
|
17
|
+
|
18
|
+
initializer :i18n_load_path do |app|
|
19
|
+
ActiveSupport.on_load(:i18n) do
|
20
|
+
# Prepend the engine i18n load path so that translations can be overriden in the main app
|
21
|
+
engine_i18n_load_path = Dir[Engine.root.join("config", "locales", "**", "wicked", "pipeline", "**", "*.yml").to_s]
|
22
|
+
app.config.i18n.load_path = engine_i18n_load_path.concat(app.config.i18n.load_path)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Wicked
|
4
|
+
module Pipeline
|
5
|
+
class ReadonlyStep < BaseStep
|
6
|
+
# @return true
|
7
|
+
def readonly?
|
8
|
+
true
|
9
|
+
end
|
10
|
+
|
11
|
+
# @return false
|
12
|
+
def blocking?
|
13
|
+
false
|
14
|
+
end
|
15
|
+
|
16
|
+
protected
|
17
|
+
|
18
|
+
def permitted_params
|
19
|
+
[]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|