trailblazer-activity-dsl-linear 1.0.0 → 1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 91482f9f8f3191ea94fccb525e24e55b95edb1f089ee24dd7748364af140476b
4
- data.tar.gz: f85598f3e6796e6df4aa6a3927654dc882dae26e2e8c39eff4ea2b4a86db98f7
3
+ metadata.gz: 87302630d03df0e0e40ad7e7b9902744a1b9d7586e157018eab7652c6ad7276c
4
+ data.tar.gz: 10a821885b6a9e4228a0e04545d28689d28f4d3470f74b5f597f6d9bc1b4dd50
5
5
  SHA512:
6
- metadata.gz: 88644ff777e10df4e7e6337742f4684e487bdac3e2899d5168f9f4769563171c68638608b2d3de495b66467d9046c2d3f2c12c94ebd4d046c9ec66bbb364cce6
7
- data.tar.gz: 53279e4799c2e08909e4a60e55a139c8455faf1d12b8f2fa78a5c80eef6446981e5468e0f962569be1538ce87a4567aef3ef271923be56e8cb17a83ae11610c9
6
+ metadata.gz: '08a071547063505433e4400c7f5e23c8203c72f125397bf71af8402050d728e1e5995fc71f5cd23a8da362c277bedb16a71e9d4f0b5f1c439d96eed82c4589fc'
7
+ data.tar.gz: 11e54d0175baa4fa338605faf1ac44ef1ef891a763890730c44119a239f11ea2f37497c80156bc2895a11b7b0a6ce58e9349e4a44e49f90c3dc48535ff179b57
@@ -1,3 +1,6 @@
1
+ ## This file is managed by Terraform.
2
+ ## Do not modify this file directly, as it may be overwritten.
3
+ ## Please open an issue instead.
1
4
  name: CI
2
5
  on: [push, pull_request]
3
6
  jobs:
@@ -5,13 +8,12 @@ jobs:
5
8
  strategy:
6
9
  fail-fast: false
7
10
  matrix:
8
- # Due to https://github.com/actions/runner/issues/849, we have to use quotes for '3.0'
9
- ruby: [2.5, 2.6, 2.7, '3.0', head, jruby]
11
+ ruby: [2.7, '3.0', '3.1']
10
12
  runs-on: ubuntu-latest
11
13
  steps:
12
- - uses: actions/checkout@v2
14
+ - uses: actions/checkout@v3
13
15
  - uses: ruby/setup-ruby@v1
14
16
  with:
15
17
  ruby-version: ${{ matrix.ruby }}
16
- bundler-cache: true # runs 'bundle install' and caches installed gems automatically
18
+ bundler-cache: true
17
19
  - run: bundle exec rake
@@ -0,0 +1,19 @@
1
+ ## This file is managed by Terraform.
2
+ ## Do not modify this file directly, as it may be overwritten.
3
+ ## Please open an issue instead.
4
+ name: CI JRuby
5
+ on: [push, pull_request]
6
+ jobs:
7
+ test:
8
+ strategy:
9
+ fail-fast: false
10
+ matrix:
11
+ ruby: [jruby, jruby-head]
12
+ runs-on: ubuntu-latest
13
+ steps:
14
+ - uses: actions/checkout@v3
15
+ - uses: ruby/setup-ruby@v1
16
+ with:
17
+ ruby-version: ${{ matrix.ruby }}
18
+ bundler-cache: true
19
+ - run: bundle exec rake
@@ -0,0 +1,19 @@
1
+ ## This file is managed by Terraform.
2
+ ## Do not modify this file directly, as it may be overwritten.
3
+ ## Please open an issue instead.
4
+ name: CI with EOL ruby versions
5
+ on: [push, pull_request]
6
+ jobs:
7
+ test:
8
+ strategy:
9
+ fail-fast: false
10
+ matrix:
11
+ ruby: [2.5, 2.6]
12
+ runs-on: ubuntu-latest
13
+ steps:
14
+ - uses: actions/checkout@v3
15
+ - uses: ruby/setup-ruby@v1
16
+ with:
17
+ ruby-version: ${{ matrix.ruby }}
18
+ bundler-cache: true
19
+ - run: bundle exec rake
@@ -0,0 +1,19 @@
1
+ ## This file is managed by Terraform.
2
+ ## Do not modify this file directly, as it may be overwritten.
3
+ ## Please open an issue instead.
4
+ name: CI TruffleRuby
5
+ on: [push, pull_request]
6
+ jobs:
7
+ test:
8
+ strategy:
9
+ fail-fast: false
10
+ matrix:
11
+ ruby: [truffleruby, truffleruby-head]
12
+ runs-on: ubuntu-latest
13
+ steps:
14
+ - uses: actions/checkout@v3
15
+ - uses: ruby/setup-ruby@v1
16
+ with:
17
+ ruby-version: ${{ matrix.ruby }}
18
+ bundler-cache: true
19
+ - run: bundle exec rake
data/CHANGES.md CHANGED
@@ -1,3 +1,31 @@
1
+ # 1.1.0
2
+
3
+ * Use `trailblazer-activity` 0.15.0.
4
+ * Remove `Path::DSL.OptionsForSequenceBuilder` and move concrete code to `Path::DSL.options_for_sequence_build`
5
+ which returns a set:
6
+
7
+ 1. default termini instructions for the concrete strategy
8
+ 2. options specific for this strategy subclass.
9
+
10
+ Everything else, such as merging user options, computing and adding termini, etc, now happens in
11
+ `Strategy::DSL.OptionsForSequenceBuilder`.
12
+ * Adding `Subprocess(Create, strict: true)` to wire all outputs of `Create` automatically.
13
+ Each output will be wired to its same named Track(semantic).
14
+ * Adding `Strategy(termini: )`
15
+ * For `output:` in combination with `:output_with_outer_ctx`, deprecate the second positional argument and make it
16
+ the `:outer_ctx` keyword argument instead.
17
+ * Introduce `Linear.Patch` as the public entry point for patching activities.
18
+ * Remove `Runtime.initial_aggregate` step for the input and output pipelines which results in slightly better runtime performance and less code.
19
+
20
+ ## Variable Mapping
21
+
22
+ * Simplify the architecture in `VariableMapping`, filters are now added directly into the `Pipeline`.
23
+ Performance increase from 17k to 25k from 1.0.0 to this version.
24
+ * Introduce `Inject(:variable)` to supersede the version receiving a big mapping hash.
25
+ * Add `Inject(:variable, override: true)` to always write a variable to ctx, regardless of its presence.
26
+ * Fix a bug where `Inject()` would override `In()` filters even though the latter was added latest. This
27
+ is fixed by treating both filter types equally and in the order they were added by the user (and the macro).
28
+
1
29
  # 1.0.0
2
30
 
3
31
  ## Additions
data/Gemfile CHANGED
@@ -5,11 +5,11 @@ gemspec
5
5
 
6
6
  gem "minitest-line"
7
7
 
8
- gem "rubocop", require: false
9
-
10
- gem "trailblazer-developer", path: "../trailblazer-developer"
8
+ # gem "trailblazer-developer", path: "../trailblazer-developer"
11
9
  # gem "trailblazer-developer", github: "trailblazer/trailblazer-developer"
12
10
  # gem "trailblazer-declarative", path: "../trailblazer-declarative"
13
11
  # gem "trailblazer-activity", path: "../trailblazer-activity"
14
12
  # gem "trailblazer-activity", github: "trailblazer/trailblazer-activity"
15
13
  # gem "trailblazer-activity", path: "../circuit"
14
+ gem "benchmark-ips"
15
+ # gem "trailblazer-core-utils", path: "../trailblazer-core-utils"
data/Rakefile CHANGED
@@ -4,7 +4,7 @@ require "rake/testtask"
4
4
  Rake::TestTask.new(:test) do |t|
5
5
  t.libs << "test"
6
6
  t.libs << "lib"
7
- t.test_files = FileList["test/**/*_test.rb"]
7
+ t.test_files = FileList["test/**/*_test.rb"] - FileList["test/docs/autogenerated/*"]
8
8
  end
9
9
 
10
10
  task :default => :test
@@ -1,6 +1,10 @@
1
1
  class Trailblazer::Activity
2
2
  module DSL
3
3
  module Linear
4
+ def self.Patch(activity, instructions)
5
+ Patch.customize(activity, options: instructions)
6
+ end
7
+
4
8
  module Patch
5
9
  # DISCUSS: we could make this a generic DSL option, not just for Subprocess().
6
10
  # Currently, this is called from the Subprocess() helper.
@@ -21,7 +25,7 @@ class Trailblazer::Activity
21
25
 
22
26
  patch =
23
27
  if task_id
24
- segment_activity = Introspect::Graph(activity).find(task_id).task
28
+ segment_activity = Introspect::TaskMap(activity).find_by_id(task_id).task
25
29
  patched_segment_activity = call(segment_activity, path, customization)
26
30
 
27
31
  # Replace the patched subprocess.
@@ -10,39 +10,17 @@ 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
14
  # We allow to inject {:initial_input_pipeline} here in order to skip creating a new input pipeline and instead
32
15
  # 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")
16
+ def pipe_for_composable_input(in_filters: [], initial_input_pipeline: initial_input_pipeline_for(in_filters), **)
17
+ in_filters = DSL::Tuple.filters_from_options(in_filters)
18
+ pipeline = add_filter_steps(initial_input_pipeline, in_filters)
41
19
  end
42
20
 
43
21
  # initial pipleline depending on whether or not we got any In() filters.
44
22
  def initial_input_pipeline_for(in_filters)
45
- is_inject_only = Array(in_filters).empty?
23
+ is_inject_only = in_filters.find { |k, v| k.is_a?(VariableMapping::DSL::In) }.nil?
46
24
 
47
25
  initial_input_pipeline(add_default_ctx: is_inject_only)
48
26
  end
@@ -57,7 +35,6 @@ module Trailblazer
57
35
 
58
36
  pipe = Activity::TaskWrap::Pipeline.new(
59
37
  [
60
- Activity::TaskWrap::Pipeline.Row("input.init_hash", VariableMapping.method(:initial_aggregate)), # very first step
61
38
  default_ctx_row,
62
39
  Activity::TaskWrap::Pipeline.Row("input.scope", VariableMapping.method(:scope)), # last step
63
40
  ].compact
@@ -70,36 +47,12 @@ module Trailblazer
70
47
 
71
48
  # Handle {:input} and {:inject} option, the "old" interface.
72
49
  def add_steps_for_input_option(pipeline, input:)
73
- tuple = DSL.In(name: ":input") # simulate {In() => input}
50
+ tuple = DSL.In() # simulate {In() => input}
74
51
  input_filter = DSL::Tuple.filters_from_options([[tuple, input]])
75
52
 
76
53
  add_filter_steps(pipeline, input_filter)
77
54
  end
78
55
 
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
56
  def pipe_for_composable_output(out_filters: [], initial_output_pipeline: initial_output_pipeline(add_default_ctx: Array(out_filters).empty?), **)
104
57
  out_filters = DSL::Tuple.filters_from_options(out_filters)
105
58
 
@@ -112,7 +65,6 @@ module Trailblazer
112
65
 
113
66
  Activity::TaskWrap::Pipeline.new(
114
67
  [
115
- Activity::TaskWrap::Pipeline.Row("output.init_hash", VariableMapping.method(:initial_aggregate)), # very first step
116
68
  default_ctx_row,
117
69
  Activity::TaskWrap::Pipeline.Row("output.merge_with_original", VariableMapping.method(:merge_with_original)), # last step
118
70
  ].compact
@@ -123,14 +75,6 @@ module Trailblazer
123
75
  ["output.default_output", VariableMapping.method(:default_output_ctx)]
124
76
  end
125
77
 
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
78
  def add_filter_steps(pipeline, rows, prepend_to: "input.scope", path_prefix: "input")
135
79
  rows = add_variables_steps_for_filters(rows, path_prefix: path_prefix)
136
80
 
@@ -145,48 +89,10 @@ module Trailblazer
145
89
  # @param filters [Array] List of {Filter} objects
146
90
  def add_variables_steps_for_filters(filters, path_prefix:)
147
91
  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
92
+ ["#{path_prefix}.add_variables.#{filter.name}", filter] # FIXME: config name sucks, of course, if we want to allow inserting etc.
173
93
  end
174
94
  end
175
95
 
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
96
 
191
97
  # Keeps user's DSL configuration for a particular io-pipe step.
192
98
  # Implements the interface for the actual I/O code and is DSL code happening in the normalizer.
@@ -195,83 +101,272 @@ module Trailblazer
195
101
  # If a user needs to inject their own private iop step they can create this data structure with desired values here.
196
102
  # 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
103
 
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) }
104
+ class Tuple
105
+ def initialize(variable_name, add_variables_class, filters_builder, add_variables_class_for_callable=nil, insert_args: nil, **options)
106
+ @options =
107
+ {
108
+ variable_name: variable_name,
109
+ add_variables_class: add_variables_class,
110
+ filters_builder: filters_builder,
111
+ insert_args: insert_args,
112
+
113
+ add_variables_class_for_callable: add_variables_class_for_callable,
114
+
115
+ **options
116
+ }
201
117
  end
202
118
 
119
+ def to_h
120
+ @options
121
+ end
122
+
123
+ def self.filters_from_options(tuples_to_user_filters)
124
+ tuples_to_user_filters.collect { |tuple, user_filter| tuple.(user_filter) }.flatten(1)
125
+ end
203
126
 
204
127
  # @return [Filter] Filter instance that keeps {name} and {aggregate_step}.
205
128
  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)
129
+ @options[:filters_builder].(user_filter, **to_h)
210
130
  end
211
131
  end # TODO: implement {:insert_args}
212
132
 
213
133
  # 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
134
+ class In < Tuple
135
+ class FiltersBuilder
136
+ def self.call(user_filter, add_variables_class:, add_variables_class_for_callable:, type: :In, **options)
137
+ # In()/Out() => {:user => :current_user}
138
+ if user_filter.is_a?(Hash)
139
+ # For In(): build {SetVariable} filters.
140
+ # For Out(): build {SetVariable::Output} filters.
141
+ return Filter.build_filters_for_hash(user_filter, add_variables_class: add_variables_class) do |options, from_name, to_name|
142
+ options.merge(
143
+ name: Filter.name_for(type, "#{from_name.inspect}>#{to_name.inspect}"),
144
+ read_name: from_name,
145
+ write_name: to_name,
146
+ )
147
+ end
148
+ end
149
+
150
+ # In()/Out() => [:current_user]
151
+ if user_filter.is_a?(Array)
152
+ user_filter = Filter.hash_for(user_filter)
153
+
154
+ return Filter.build_filters_for_hash(user_filter, add_variables_class: add_variables_class) do |options, from_name, _|
155
+ options.merge(
156
+ name: Filter.name_for(type, from_name.inspect),
157
+ write_name: from_name,
158
+ read_name: from_name,
159
+ )
160
+ end
161
+ end
162
+
163
+ # callable, producing a hash!
164
+
165
+ return build_for_option(user_filter,
166
+ name: Filter.name_for(type, user_filter.object_id, :add_variables),
167
+ write_name: nil,
168
+ read_name: nil,
169
+ add_variables_class: add_variables_class_for_callable, # for example, {AddVariables::Output}
170
+ **options
171
+ )
172
+ # TODO: remove {add_variables_class_for_callable} and make everything SetVariable.
173
+ end # call
174
+
175
+ # Simply invoke user's filter.
176
+ # Use this for filters without condition and default.
177
+ def self.build_for_option(user_filter, **options)
178
+ filter = Activity::Circuit.Step(user_filter, option: true)
179
+
180
+ [
181
+ Filter.build(
182
+ filter: filter,
183
+ user_filter: user_filter,
184
+ **options
185
+ )
186
+ ]
187
+ end
188
+ end
189
+ end # In
190
+
191
+ class Out < Tuple
192
+ class FiltersBuilder
193
+ def self.call(user_filter, with_outer_ctx:, **options)
194
+ if with_outer_ctx
195
+ callable = user_filter # FIXME: :instance_method, for fuck's sake.
196
+ call_method = callable.respond_to?(:arity) ? callable : callable.method(:call)
197
+
198
+ options =
199
+ # TODO: remove {if} and only leave {else}.
200
+ if call_method.arity == 3
201
+ index = caller_locations.find_index { |location| location.to_s =~ /recompile_activity_for/ }
202
+ caller_location = caller_locations[index+2]
203
+
204
+ Activity::Deprecate.warn caller_location,
205
+ "The positional argument `outer_ctx` is deprecated, please use the `:outer_ctx` keyword argument.\n#{VariableMapping.deprecation_link}"
206
+
207
+ options.merge(
208
+ filter: Trailblazer::Option(user_filter),
209
+ add_variables_class_for_callable: AddVariables::Output::WithOuterContext_Deprecated, # old positional arg
210
+ )
211
+ else
212
+ options.merge(
213
+ add_variables_class_for_callable: AddVariables::Output::WithOuterContext,
214
+ )
215
+ end
216
+ end
217
+
218
+ In::FiltersBuilder.(user_filter, type: :Out, **options)
219
+ end
220
+ end
221
+ end # Out
216
222
 
217
- def self.In(name: rand, add_variables_class: AddVariables, filter_builder: method(:build_filter))
218
- In.new(name, add_variables_class, filter_builder)
223
+ def self.In(variable_name = nil, add_variables_class: SetVariable, filter_builder: In::FiltersBuilder, add_variables_class_for_callable: AddVariables)
224
+ In.new(variable_name, add_variables_class, filter_builder, add_variables_class_for_callable)
219
225
  end
220
226
 
221
227
  # 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
228
+ 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)
229
+ add_variables_class = SetVariable::Output::Delete if delete
230
+ add_variables_class = SetVariable::ReadFromAggregate if read_from_aggregate
231
+
232
+ Out.new(
233
+ variable_name,
234
+ add_variables_class,
235
+ filter_builder,
236
+ add_variables_class_for_callable,
227
237
 
228
- Out.new(name, add_variables_class, filter_builder)
238
+ with_outer_ctx: with_outer_ctx,
239
+ )
229
240
  end
230
241
 
231
- def self.Inject()
232
- Inject.new
242
+ # Used in the DSL by you.
243
+ def self.Inject(variable_name = nil, override: false, **)
244
+ Inject.new(
245
+ variable_name,
246
+ nil, # add_variables_class # DISCUSS: do we really want that here?
247
+ Inject::FiltersBuilder,
248
+ nil,
249
+ override: override,
250
+ )
233
251
  end
234
252
 
235
253
  # 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
254
+ class Inject < Tuple
255
+ class FiltersBuilder
256
+ # Called via {Tuple#call}
257
+ def self.call(user_filter, add_variables_class:, variable_name:, **options)
258
+ # Build {SetVariable::Default}
259
+ if user_filter.is_a?(Hash) # TODO: deprecate in favor if {Inject(:variable_name)}!
260
+ return Filter.build_filters_for_hash(user_filter, add_variables_class: SetVariable::Default) do |options, from_name, user_proc|
261
+ options_with_condition_for_defaulted(
262
+ **options,
263
+ user_filter: user_proc,
264
+ write_name: from_name,
265
+ read_name: from_name,
266
+ )
267
+ end
268
+ end
269
+
270
+ # Build {SetVariable::Conditioned}
271
+ if user_filter.is_a?(Array)
272
+ user_filter = Filter.hash_for(user_filter)
273
+
274
+ return Filter.build_filters_for_hash(user_filter, add_variables_class: SetVariable::Conditioned) do |options, from_name, _|
275
+ options_with_condition(
276
+ **options,
277
+ write_name: from_name,
278
+ read_name: from_name,
279
+ user_filter: user_filter, # FIXME: this is not really helpful, it's something like [:field, :injects]
280
+ )
281
+ end
282
+ end
283
+
284
+ if options[:override]
285
+ return In::FiltersBuilder.build_for_option(
286
+ user_filter,
287
+ name: Filter.name_for(:Inject, variable_name, :add_variables),
288
+ write_name: variable_name,
289
+ read_name: nil,
290
+ add_variables_class: SetVariable,
291
+ **options
292
+ )
293
+ end
294
+
295
+
296
+ # Build {SetVariable::Default}
297
+ # {user_filter} is one of the following
298
+ # :instance_method
299
+ options = options_with_condition_for_defaulted(
300
+ **options,
301
+ write_name: variable_name,
302
+ read_name: variable_name,
303
+ user_filter: user_filter,
304
+ )
305
+
306
+ [
307
+ Filter.build_for_reading(add_variables_class: SetVariable::Default, **options)
308
+ ]
309
+ end # call
310
+
311
+ def self.options_with_condition(user_filter:, write_name:, name_specifier: nil, **options)
312
+ {
313
+ name: Filter.name_for(:Inject, write_name.inspect, name_specifier),
314
+ **options,
315
+ condition: VariablePresent.new(variable_name: write_name),
316
+ write_name: write_name,
317
+ user_filter: user_filter,
318
+ }
319
+ end
320
+
321
+ def self.options_with_condition_for_defaulted(user_filter:, **options)
322
+ default_filter = Activity::Circuit.Step(user_filter, option: true) # this is passed into {SetVariable.new}.
244
323
 
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)
324
+ options_with_condition(
325
+ **options,
326
+ user_filter: user_filter,
327
+ name_specifier: :default,
328
+ default_filter: default_filter,
329
+ )
330
+ end
331
+ end # FiltersBuilder
332
+ end # Inject
333
+
334
+ # DISCUSS: generic, again
335
+ module Filter
336
+ def self.build(add_variables_class:, **options)
337
+ add_variables_class.new(
338
+ **options,
339
+ )
251
340
  end
252
341
 
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.
342
+ def self.build_for_reading(read_name:, **options)
343
+ circuit_step_filter = VariableFromCtx.new(variable_name: read_name) # Activity::Circuit.Step(filter, option: true) # this is passed into {SetVariable.new}.
257
344
 
258
- filter_for(inject, inject_filter, name, "passthrough")
259
- end
345
+ build(
346
+ filter: circuit_step_filter,
347
+ **options
348
+ )
260
349
  end
261
350
 
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)} }
351
+ def self.build_filters_for_hash(user_filter, **options)
352
+ return user_filter.collect do |from_name, to_name|
353
+ options = yield(options, from_name, to_name)
266
354
 
267
- filter_for(inject, inject_filter, name, "defaulting_callable")
355
+ Filter.build_for_reading(
356
+ user_filter: user_filter,
357
+ **options,
358
+ )
268
359
  end
269
360
  end
270
361
 
271
- def self.filter_for(inject, inject_filter, name, type)
272
- DSL.In(name: "#{type}.#{name.inspect}", add_variables_class: AddVariables).(inject_filter)
362
+ def self.hash_for(ary)
363
+ ary.collect { |name| [name, name] }.to_h
273
364
  end
274
- end
365
+
366
+ def self.name_for(type, name, specifier=nil)
367
+ [type, specifier].compact.join(".") + "{#{name}}"
368
+ end
369
+ end # Filter
275
370
 
276
371
  end # DSL
277
372
  end