trailblazer-activity-dsl-linear 1.0.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (29) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +2 -3
  3. data/CHANGES.md +100 -0
  4. data/Gemfile +7 -4
  5. data/Rakefile +1 -1
  6. data/lib/trailblazer/activity/dsl/linear/feature/merge.rb +2 -2
  7. data/lib/trailblazer/activity/dsl/linear/feature/patch.rb +9 -5
  8. data/lib/trailblazer/activity/dsl/linear/feature/variable_mapping/dsl.rb +241 -156
  9. data/lib/trailblazer/activity/dsl/linear/feature/variable_mapping/runtime.rb +276 -0
  10. data/lib/trailblazer/activity/dsl/linear/feature/variable_mapping.rb +70 -226
  11. data/lib/trailblazer/activity/dsl/linear/helper/path.rb +37 -18
  12. data/lib/trailblazer/activity/dsl/linear/helper.rb +38 -17
  13. data/lib/trailblazer/activity/dsl/linear/normalizer/extensions.rb +63 -0
  14. data/lib/trailblazer/activity/dsl/linear/normalizer/inherit.rb +90 -0
  15. data/lib/trailblazer/activity/dsl/linear/normalizer/output_tuples.rb +160 -0
  16. data/lib/trailblazer/activity/dsl/linear/normalizer/terminus.rb +26 -29
  17. data/lib/trailblazer/activity/dsl/linear/normalizer.rb +99 -160
  18. data/lib/trailblazer/activity/dsl/linear/sequence/builder.rb +3 -2
  19. data/lib/trailblazer/activity/dsl/linear/sequence/compiler.rb +21 -17
  20. data/lib/trailblazer/activity/dsl/linear/sequence/search.rb +2 -8
  21. data/lib/trailblazer/activity/dsl/linear/strategy.rb +56 -17
  22. data/lib/trailblazer/activity/dsl/linear/version.rb +1 -1
  23. data/lib/trailblazer/activity/dsl/linear.rb +13 -1
  24. data/lib/trailblazer/activity/fast_track.rb +96 -67
  25. data/lib/trailblazer/activity/path.rb +35 -53
  26. data/lib/trailblazer/activity/railway.rb +63 -65
  27. data/trailblazer-activity-dsl-linear.gemspec +8 -8
  28. metadata +27 -18
  29. data/lib/trailblazer/activity/dsl/linear/feature/variable_mapping/inherit.rb +0 -38
@@ -10,44 +10,19 @@ module Trailblazer
10
10
  module DSL
11
11
  module_function
12
12
 
13
- # Compute pipeline for {:input} option.
14
- def pipe_for_mono_input(input: [], inject: [], in_filters: [], output: [], **)
15
- has_input = Array(input).any?
16
- has_mono_options = has_input || Array(inject).any? || Array(output).any? # :input, :inject and :output are "mono options".
17
- has_composable_options = in_filters.any? # DISCUSS: why are we not testing Inject()?
18
-
19
- if has_mono_options && has_composable_options
20
- warn "[Trailblazer] You are mixing `:input` and `In() => ...`. `In()` and Inject () options are ignored and `:input` wins: #{input} #{inject} #{output} <> #{in_filters} / "
21
- end
22
-
23
- pipeline = initial_input_pipeline(add_default_ctx: !has_input)
24
- pipeline = add_steps_for_input_option(pipeline, input: input)
25
- pipeline = add_steps_for_inject_option(pipeline, inject: inject)
26
-
27
- return pipeline, has_mono_options, has_composable_options
28
- end
29
-
30
13
  # Compute pipeline for In() and Inject().
31
- # We allow to inject {:initial_input_pipeline} here in order to skip creating a new input pipeline and instead
32
- # use the inherit one.
33
- def pipe_for_composable_input(in_filters: [], inject_filters: [], initial_input_pipeline: initial_input_pipeline_for(in_filters), **)
34
- inject_filters = DSL::Inject.filters_for_injects(inject_filters) # {Inject() => ...} the pure user input gets translated into AddVariable aggregate steps.
35
- in_filters = DSL::Tuple.filters_from_options(in_filters)
36
-
37
- # With only injections defined, we do not filter out anything, we use the original ctx
38
- # and _add_ defaulting for injected variables.
39
- pipeline = add_filter_steps(initial_input_pipeline, in_filters)
40
- pipeline = add_filter_steps(pipeline, inject_filters, path_prefix: "inject")
14
+ def pipe_for_composable_input(in_filters: [], initial_input_pipeline: initial_input_pipeline_for(in_filters), **)
15
+ in_filters = DSL::Tuple.filters_from_options(in_filters)
16
+ _pipeline = add_filter_steps(initial_input_pipeline, in_filters)
41
17
  end
42
18
 
43
19
  # initial pipleline depending on whether or not we got any In() filters.
44
20
  def initial_input_pipeline_for(in_filters)
45
- is_inject_only = Array(in_filters).empty?
21
+ is_inject_only = in_filters.find { |k, v| k.is_a?(VariableMapping::DSL::In) }.nil?
46
22
 
47
23
  initial_input_pipeline(add_default_ctx: is_inject_only)
48
24
  end
49
25
 
50
-
51
26
  # Adds the default_ctx step as per option {:add_default_ctx}
52
27
  def initial_input_pipeline(add_default_ctx: false)
53
28
  # No In() or {:input}. Use default ctx, which is the original ctxx.
@@ -55,11 +30,10 @@ module Trailblazer
55
30
  default_ctx_row =
56
31
  add_default_ctx ? Activity::TaskWrap::Pipeline.Row(*default_input_ctx_config) : nil
57
32
 
58
- pipe = Activity::TaskWrap::Pipeline.new(
33
+ Activity::TaskWrap::Pipeline.new(
59
34
  [
60
- Activity::TaskWrap::Pipeline.Row("input.init_hash", VariableMapping.method(:initial_aggregate)), # very first step
61
35
  default_ctx_row,
62
- Activity::TaskWrap::Pipeline.Row("input.scope", VariableMapping.method(:scope)), # last step
36
+ Activity::TaskWrap::Pipeline.Row("input.scope", VariableMapping.method(:scope)), # last step
63
37
  ].compact
64
38
  )
65
39
  end
@@ -68,38 +42,14 @@ module Trailblazer
68
42
  ["input.default_input", VariableMapping.method(:default_input_ctx)]
69
43
  end
70
44
 
71
- # Handle {:input} and {:inject} option, the "old" interface.
45
+ # Handle {:input} and {:inject} option, the "old" interface.
72
46
  def add_steps_for_input_option(pipeline, input:)
73
- tuple = DSL.In(name: ":input") # simulate {In() => input}
47
+ tuple = DSL.In() # simulate {In() => input}
74
48
  input_filter = DSL::Tuple.filters_from_options([[tuple, input]])
75
49
 
76
50
  add_filter_steps(pipeline, input_filter)
77
51
  end
78
52
 
79
-
80
- def pipe_for_mono_output(output_with_outer_ctx: false, output: [], out_filters: [], **)
81
- # No Out(), no {:output} will result in a default_output_ctx step.
82
- has_output = Array(output).any?
83
- has_mono_options = has_output
84
- has_composable_options = Array(out_filters).any?
85
-
86
- if has_mono_options && has_composable_options
87
- warn "[Trailblazer] You are mixing `:output` and `Out() => ...`. `Out()` options are ignored and `:output` wins."
88
- end
89
-
90
- pipeline = initial_output_pipeline(add_default_ctx: !has_output)
91
- pipeline = add_steps_for_output_option(pipeline, output: output, output_with_outer_ctx: output_with_outer_ctx)
92
-
93
- return pipeline, has_mono_options, has_composable_options
94
- end
95
-
96
- def add_steps_for_output_option(pipeline, output:, output_with_outer_ctx:)
97
- tuple = DSL.Out(name: ":output", with_outer_ctx: output_with_outer_ctx) # simulate {Out() => output}
98
- output_filter = DSL::Tuple.filters_from_options([[tuple, output]])
99
-
100
- add_filter_steps(pipeline, output_filter, prepend_to: "output.merge_with_original", path_prefix: "output")
101
- end
102
-
103
53
  def pipe_for_composable_output(out_filters: [], initial_output_pipeline: initial_output_pipeline(add_default_ctx: Array(out_filters).empty?), **)
104
54
  out_filters = DSL::Tuple.filters_from_options(out_filters)
105
55
 
@@ -112,7 +62,6 @@ module Trailblazer
112
62
 
113
63
  Activity::TaskWrap::Pipeline.new(
114
64
  [
115
- Activity::TaskWrap::Pipeline.Row("output.init_hash", VariableMapping.method(:initial_aggregate)), # very first step
116
65
  default_ctx_row,
117
66
  Activity::TaskWrap::Pipeline.Row("output.merge_with_original", VariableMapping.method(:merge_with_original)), # last step
118
67
  ].compact
@@ -123,14 +72,6 @@ module Trailblazer
123
72
  ["output.default_output", VariableMapping.method(:default_output_ctx)]
124
73
  end
125
74
 
126
- def add_steps_for_inject_option(pipeline, inject:)
127
- injects = inject.collect { |name| name.is_a?(Symbol) ? [DSL.Inject(), [name]] : [DSL.Inject(), name] }
128
-
129
- tuples = DSL::Inject.filters_for_injects(injects) # DISCUSS: should we add passthrough/defaulting here at Inject()-time?
130
-
131
- add_filter_steps(pipeline, tuples, path_prefix: "inject")
132
- end
133
-
134
75
  def add_filter_steps(pipeline, rows, prepend_to: "input.scope", path_prefix: "input")
135
76
  rows = add_variables_steps_for_filters(rows, path_prefix: path_prefix)
136
77
 
@@ -141,53 +82,14 @@ module Trailblazer
141
82
  Activity::Adds.apply_adds(pipeline, adds)
142
83
  end
143
84
 
144
- # Returns array of step rows ("sequence").
85
+ # Returns array of step rows ("sequence").
145
86
  # @param filters [Array] List of {Filter} objects
146
87
  def add_variables_steps_for_filters(filters, path_prefix:)
147
88
  filters.collect do |filter|
148
- ["#{path_prefix}.add_variables.#{filter.name}", filter.aggregate_step] # FIXME: config name sucks, of course, if we want to allow inserting etc.
149
- end
150
- end
151
-
152
-
153
- # Filter code
154
- # Converting user options to callable filters.
155
-
156
- # @param [Array, Hash, Proc] User option coming from the DSL, like {[:model]}
157
- #
158
- # Returns a "filter interface" callable that's invoked in {AddVariables}:
159
- # filter.(new_ctx, ..., keyword_arguments: new_ctx.to_hash, **circuit_options)
160
- def self.build_filter(user_filter)
161
- Trailblazer::Option(filter_for(user_filter))
162
- end
163
-
164
- # Convert a user option such as {[:model]} to a filter.
165
- #
166
- # Returns a filter proc to be called in an Option.
167
- # @private
168
- def self.filter_for(filter)
169
- if filter.is_a?(::Array) || filter.is_a?(::Hash)
170
- filter_from_dsl(filter)
171
- else
172
- filter
89
+ ["#{path_prefix}.add_variables.#{filter.name}", filter] # FIXME: config name sucks, of course, if we want to allow inserting etc.
173
90
  end
174
91
  end
175
92
 
176
- # The returned filter compiles a new hash for Scoped/Unscoped that only contains
177
- # the desired i/o variables.
178
- #
179
- # Filter expects a "filter interface" {(ctx, **)}.
180
- def self.filter_from_dsl(map)
181
- hsh = DSL.hash_for(map)
182
-
183
- ->(incoming_ctx, **kwargs) { Hash[hsh.collect { |from_name, to_name| [to_name, incoming_ctx[from_name]] }] }
184
- end
185
-
186
- def self.hash_for(ary)
187
- return ary if ary.instance_of?(::Hash)
188
- Hash[ary.collect { |name| [name, name] }]
189
- end
190
-
191
93
  # Keeps user's DSL configuration for a particular io-pipe step.
192
94
  # Implements the interface for the actual I/O code and is DSL code happening in the normalizer.
193
95
  # The actual I/O code expects {DSL::In} and {DSL::Out} objects to generate the two io-pipes.
@@ -195,84 +97,267 @@ module Trailblazer
195
97
  # If a user needs to inject their own private iop step they can create this data structure with desired values here.
196
98
  # This is also the reason why a lot of options computation such as {:with_outer_ctx} happens here and not in the IO code.
197
99
 
198
- class Tuple < Struct.new(:name, :add_variables_class, :filter_builder, :insert_args)
199
- def self.filters_from_options(tuples_to_user_filters)
200
- tuples_to_user_filters.collect { |tuple, user_filter| tuple.(user_filter) }
100
+ class Tuple
101
+ def initialize(variable_name, add_variables_class, filters_builder, add_variables_class_for_callable = nil, insert_args: nil, **options)
102
+ @options =
103
+ {
104
+ variable_name: variable_name,
105
+ add_variables_class: add_variables_class,
106
+ filters_builder: filters_builder,
107
+ insert_args: insert_args,
108
+
109
+ add_variables_class_for_callable: add_variables_class_for_callable,
110
+
111
+ **options
112
+ }
201
113
  end
202
114
 
115
+ def to_h
116
+ @options
117
+ end
118
+
119
+ def self.filters_from_options(tuples_to_user_filters)
120
+ tuples_to_user_filters.flat_map { |tuple, user_filter| tuple.(user_filter) }
121
+ end
203
122
 
204
123
  # @return [Filter] Filter instance that keeps {name} and {aggregate_step}.
205
124
  def call(user_filter)
206
- filter = filter_builder.(user_filter)
207
- aggregate_step = add_variables_class.new(filter, user_filter)
208
-
209
- VariableMapping::Filter.new(aggregate_step, filter, name, add_variables_class)
125
+ @options[:filters_builder].(user_filter, **to_h)
210
126
  end
211
127
  end # TODO: implement {:insert_args}
212
128
 
213
129
  # In, Out and Inject are objects instantiated when using the DSL, for instance {In() => [:model]}.
214
- class In < Tuple; end
215
- class Out < Tuple; end
130
+ class In < Tuple
131
+ class FiltersBuilder
132
+ def self.call(user_filter, add_variables_class:, add_variables_class_for_callable:, type: :In, **options)
133
+ # In()/Out() => {:user => :current_user}
134
+ if user_filter.is_a?(Hash)
135
+ # For In(): build {SetVariable} filters.
136
+ # For Out(): build {SetVariable::Output} filters.
137
+ return Filter.build_filters_for_hash(user_filter, add_variables_class: add_variables_class) do |options, from_name, to_name|
138
+ options.merge(
139
+ name: Filter.name_for(type, "#{from_name.inspect}>#{to_name.inspect}"),
140
+ read_name: from_name,
141
+ write_name: to_name,
142
+ )
143
+ end
144
+ end
145
+
146
+ # In()/Out() => [:current_user]
147
+ if user_filter.is_a?(Array)
148
+ user_filter = Filter.hash_for(user_filter)
149
+
150
+ return Filter.build_filters_for_hash(user_filter, add_variables_class: add_variables_class) do |options, from_name, _|
151
+ options.merge(
152
+ name: Filter.name_for(type, from_name.inspect),
153
+ write_name: from_name,
154
+ read_name: from_name,
155
+ )
156
+ end
157
+ end
158
+
159
+ # callable, producing a hash!
160
+ build_for_option(
161
+ user_filter,
162
+ name: Filter.name_for(type, user_filter.object_id, :add_variables),
163
+ write_name: nil,
164
+ read_name: nil,
165
+ add_variables_class: add_variables_class_for_callable, # for example, {AddVariables::Output}
166
+ **options
167
+ )
168
+ # TODO: remove {add_variables_class_for_callable} and make everything SetVariable.
169
+ end # call
170
+
171
+ # Simply invoke user's filter.
172
+ # Use this for filters without condition and default.
173
+ def self.build_for_option(user_filter, **options)
174
+ filter = Activity::Circuit.Step(user_filter, option: true)
175
+
176
+ [
177
+ Filter.build(
178
+ filter: filter,
179
+ user_filter: user_filter,
180
+ **options
181
+ )
182
+ ]
183
+ end
184
+ end
185
+ end # In
186
+
187
+ class Out < Tuple
188
+ class FiltersBuilder
189
+ def self.call(user_filter, with_outer_ctx:, **options)
190
+ if with_outer_ctx
191
+ callable = user_filter # FIXME: :instance_method
192
+ call_method = callable.respond_to?(:arity) ? callable : callable.method(:call)
193
+
194
+ options =
195
+ # TODO: remove {if} and only leave {else}.
196
+ if call_method.arity == 3
197
+ Activity::Deprecate.warn Linear::Deprecate.dsl_caller_location,
198
+ "The positional argument `outer_ctx` is deprecated, please use the `:outer_ctx` keyword argument.\n#{VariableMapping.deprecation_link}"
199
+
200
+ options.merge(
201
+ filter: Trailblazer::Option(user_filter),
202
+ add_variables_class_for_callable: AddVariables::Output::WithOuterContext_Deprecated, # old positional arg
203
+ )
204
+ else
205
+ options.merge(
206
+ add_variables_class_for_callable: AddVariables::Output::WithOuterContext,
207
+ )
208
+ end
209
+ end
210
+
211
+ In::FiltersBuilder.(user_filter, type: :Out, **options)
212
+ end
213
+ end
214
+ end # Out
216
215
 
217
- def self.In(name: rand, add_variables_class: AddVariables, filter_builder: method(:build_filter))
218
- In.new(name, add_variables_class, filter_builder)
216
+ def self.In(variable_name = nil, add_variables_class: SetVariable, filter_builder: In::FiltersBuilder, add_variables_class_for_callable: AddVariables)
217
+ In.new(variable_name, add_variables_class, filter_builder, add_variables_class_for_callable)
219
218
  end
220
219
 
221
220
  # Builder for a DSL Output() object.
222
- def self.Out(name: rand, add_variables_class: AddVariables::Output, with_outer_ctx: false, delete: false, filter_builder: method(:build_filter), read_from_aggregate: false)
223
- add_variables_class = AddVariables::Output::WithOuterContext if with_outer_ctx
224
- add_variables_class = AddVariables::Output::Delete if delete
225
- filter_builder = ->(user_filter) { user_filter } if delete
226
- add_variables_class = AddVariables::ReadFromAggregate if read_from_aggregate
227
-
228
- Out.new(name, add_variables_class, filter_builder)
221
+ def self.Out(variable_name = nil, add_variables_class: SetVariable::Output, with_outer_ctx: false, delete: false, filter_builder: Out::FiltersBuilder, read_from_aggregate: false, add_variables_class_for_callable: AddVariables::Output)
222
+ add_variables_class = SetVariable::Output::Delete if delete
223
+ add_variables_class = SetVariable::ReadFromAggregate if read_from_aggregate
224
+
225
+ Out.new(
226
+ variable_name,
227
+ add_variables_class,
228
+ filter_builder,
229
+ add_variables_class_for_callable,
230
+ with_outer_ctx: with_outer_ctx,
231
+ )
229
232
  end
230
233
 
231
- def self.Inject()
232
- Inject.new
234
+ # Used in the DSL by you.
235
+ def self.Inject(variable_name = nil, override: false, **)
236
+ Inject.new(
237
+ variable_name,
238
+ nil, # add_variables_class # DISCUSS: do we really want that here?
239
+ Inject::FiltersBuilder,
240
+ nil,
241
+ override: override,
242
+ )
233
243
  end
234
244
 
235
245
  # This class is supposed to hold configuration options for Inject().
236
- class Inject
237
- # Translate the raw input of the user to {In} tuples
238
- # @return Array of VariableMapping::Filter
239
- def self.filters_for_injects(injects)
240
- injects.collect do |inject, user_filter| # iterate all {Inject() => user_filter} calls
241
- DSL::Inject.compute_filters_for_inject(inject, user_filter)
242
- end.flatten(1)
243
- end
246
+ class Inject < Tuple
247
+ class FiltersBuilder
248
+ # Called via {Tuple#call}
249
+ def self.call(user_filter, add_variables_class:, variable_name:, **options)
250
+ # Build {SetVariable::Default}
251
+ if user_filter.is_a?(Hash) # TODO: deprecate in favor if {Inject(:variable_name)}!
252
+ return Filter.build_filters_for_hash(user_filter, add_variables_class: SetVariable::Default) do |options, from_name, user_proc|
253
+ options_with_condition_for_defaulted(
254
+ **options,
255
+ user_filter: user_proc,
256
+ write_name: from_name,
257
+ read_name: from_name,
258
+ )
259
+ end
260
+ end
261
+
262
+ # Build {SetVariable::Conditioned}
263
+ if user_filter.is_a?(Array)
264
+ user_filter = Filter.hash_for(user_filter)
265
+
266
+ return Filter.build_filters_for_hash(user_filter, add_variables_class: SetVariable::Conditioned) do |options, from_name, _|
267
+ options_with_condition(
268
+ **options,
269
+ write_name: from_name,
270
+ read_name: from_name,
271
+ user_filter: user_filter, # FIXME: this is not really helpful, it's something like [:field, :injects]
272
+ )
273
+ end
274
+ end
275
+
276
+ if options[:override]
277
+ return In::FiltersBuilder.build_for_option(
278
+ user_filter,
279
+ name: Filter.name_for(:Inject, variable_name, :add_variables),
280
+ write_name: variable_name,
281
+ read_name: nil,
282
+ add_variables_class: SetVariable,
283
+ **options
284
+ )
285
+ end
286
+
287
+ # Build {SetVariable::Default}
288
+ # {user_filter} is one of the following
289
+ # :instance_method
290
+ options = options_with_condition_for_defaulted(
291
+ **options,
292
+ write_name: variable_name,
293
+ read_name: variable_name,
294
+ user_filter: user_filter,
295
+ )
296
+
297
+ [
298
+ Filter.build_for_reading(add_variables_class: SetVariable::Default, **options)
299
+ ]
300
+ end # call
301
+
302
+ def self.options_with_condition(user_filter:, write_name:, name_specifier: nil, **options)
303
+ {
304
+ name: Filter.name_for(:Inject, write_name.inspect, name_specifier),
305
+ **options,
306
+ condition: VariablePresent.new(variable_name: write_name),
307
+ write_name: write_name,
308
+ user_filter: user_filter,
309
+ }
310
+ end
244
311
 
245
- # Compute {In} tuples from the user's DSL input.
246
- # We simply use AddVariables but use our own {inject_filter} which checks if the particular
247
- # variable is already present in the incoming ctx.
248
- def self.compute_filters_for_inject(inject, user_filter) # {user_filter} either [:current_user, :model] or {model: ->{}}
249
- return filters_for_array(inject, user_filter) if user_filter.is_a?(Array)
250
- filters_for_hash_of_callables(inject, user_filter)
312
+ def self.options_with_condition_for_defaulted(user_filter:, **options)
313
+ default_filter = Activity::Circuit.Step(user_filter, option: true) # this is passed into {SetVariable.new}.
314
+
315
+ options_with_condition(
316
+ **options,
317
+ user_filter: user_filter,
318
+ name_specifier: :default,
319
+ default_filter: default_filter,
320
+ )
321
+ end
322
+ end # FiltersBuilder
323
+ end # Inject
324
+
325
+ # DISCUSS: generic, again
326
+ module Filter
327
+ def self.build(add_variables_class:, **options)
328
+ add_variables_class.new(
329
+ **options,
330
+ )
251
331
  end
252
332
 
253
- # [:model, :current_user]
254
- def self.filters_for_array(inject, user_filter)
255
- user_filter.collect do |name|
256
- inject_filter = ->(original_ctx, **) { original_ctx.key?(name) ? {name => original_ctx[name]} : {} } # FIXME: make me an {Inject::} method.
333
+ def self.build_for_reading(read_name:, **options)
334
+ circuit_step_filter = VariableFromCtx.new(variable_name: read_name) # Activity::Circuit.Step(filter, option: true) # this is passed into {SetVariable.new}.
257
335
 
258
- filter_for(inject, inject_filter, name, "passthrough")
259
- end
336
+ build(
337
+ filter: circuit_step_filter,
338
+ **options
339
+ )
260
340
  end
261
341
 
262
- # {model: ->(*) { snippet }}
263
- def self.filters_for_hash_of_callables(inject, user_filter)
264
- user_filter.collect do |name, defaulting_filter|
265
- inject_filter = ->(original_ctx, **kws) { original_ctx.key?(name) ? {name => original_ctx[name]} : {name => defaulting_filter.(original_ctx, **kws)} }
342
+ def self.build_filters_for_hash(user_filter, **options)
343
+ user_filter.collect do |from_name, to_name|
344
+ options = yield(options, from_name, to_name)
266
345
 
267
- filter_for(inject, inject_filter, name, "defaulting_callable")
346
+ Filter.build_for_reading(
347
+ user_filter: user_filter,
348
+ **options,
349
+ )
268
350
  end
269
351
  end
270
352
 
271
- def self.filter_for(inject, inject_filter, name, type)
272
- DSL.In(name: "#{type}.#{name.inspect}", add_variables_class: AddVariables).(inject_filter)
353
+ def self.hash_for(ary)
354
+ ary.collect { |name| [name, name] }.to_h
273
355
  end
274
- end
275
356
 
357
+ def self.name_for(type, name, specifier = nil)
358
+ [type, specifier].compact.join(".") + "{#{name}}"
359
+ end
360
+ end # Filter
276
361
  end # DSL
277
362
  end
278
363
  end