Class: Foobara::Value::Processor

Inherits:
Object
  • Object
show all
Includes:
IsManifestable, Manifestable
Defined in:
foobara-0.0.110/projects/value/src/processor.rb,
foobara-0.0.110/projects/value/src/processor/multi.rb,
foobara-0.0.110/projects/value/src/processor/runner.rb,
foobara-0.0.110/projects/value/src/processor/casting.rb,
foobara-0.0.110/projects/value/src/processor/pipeline.rb,
foobara-0.0.110/projects/value/src/processor/selection.rb

Defined Under Namespace

Modules: Priority Classes: Casting, Multi, Pipeline, Runner, Selection

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from IsManifestable

#foobara_domain, #foobara_organization, #scoped_clear_caches

Methods included from Concern

foobara_class_methods_module_for, foobara_concern?, included

Constructor Details

#initialize(*args) ⇒ Processor

Returns a new instance of Processor.



147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
# File 'foobara-0.0.110/projects/value/src/processor.rb', line 147

def initialize(*args)
  # TODO: get this out of here somehow?
  unless Foobara::Namespace.current == Foobara
    self.created_in_namespace = Foobara::Namespace.current
  end

  expected_arg_count = requires_declaration_data? ? 1 : 0
  expected_arg_count += 1 if requires_parent_declaration_data?

  unless expected_arg_count == args.count
    # :nocov:
    raise ArgumentError, "#{name} expected #{expected_arg_count} received #{args.count}"
    # :nocov:
  end

  if requires_declaration_data?
    self.declaration_data = args.shift
  end

  if requires_parent_declaration_data?
    self.parent_declaration_data = args.first
  end
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method, *args, **opts) ⇒ Object



351
352
353
354
355
356
357
358
359
# File 'foobara-0.0.110/projects/value/src/processor.rb', line 351

def method_missing(method, *args, **opts)
  if method == symbol
    declaration_data
  else
    # :nocov:
    super
    # :nocov:
  end
end

Instance Attribute Details

#created_in_namespaceObject



171
172
173
174
# File 'foobara-0.0.110/projects/value/src/processor.rb', line 171

def created_in_namespace
  # TODO: can we find a way to not depend on domains in this project?
  @created_in_namespace ||= GlobalDomain
end

#declaration_dataObject

Returns the value of attribute declaration_data.



144
145
146
# File 'foobara-0.0.110/projects/value/src/processor.rb', line 144

def declaration_data
  @declaration_data
end

#parent_declaration_dataObject

Returns the value of attribute parent_declaration_data.



144
145
146
# File 'foobara-0.0.110/projects/value/src/processor.rb', line 144

def parent_declaration_data
  @parent_declaration_data
end

Class Method Details

.default_declaration_dataObject



24
25
26
# File 'foobara-0.0.110/projects/value/src/processor.rb', line 24

def default_declaration_data
  true
end

.error_classObject



119
120
121
122
123
124
125
126
127
128
129
# File 'foobara-0.0.110/projects/value/src/processor.rb', line 119

def error_class
  return @error_class if defined?(@error_class)

  unless error_classes.size == 1
    # :nocov:
    raise "Expected exactly one error class to be defined for #{name} but has #{error_classes.size}"
    # :nocov:
  end

  @error_class = error_classes.first
end

.error_classesObject



95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'foobara-0.0.110/projects/value/src/processor.rb', line 95

def error_classes
  @error_classes ||= begin
    error_klasses = Util.constant_values(self, extends: Foobara::Error)

    # TODO: we shouldn´t have wo ways to do this. But keeping this check for now until the old
    # namespace implementation is removed.
    error_klasses2 = if is_a?(Foobara::Namespace::IsNamespace)
                       foobara_all_error(mode: Namespace::LookupMode::DIRECT)
                     end

    if error_klasses.sort_by(&:name) != error_klasses2.sort_by(&:name)
      # :nocov:
      raise "Expected #{error_klasses} to equal #{error_klasses2} for #{name}"
      # :nocov:
    end

    if superclass < Processor
      error_klasses2 += superclass.error_classes
    end

    error_klasses2
  end
end

.foobara_manifestObject



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'foobara-0.0.110/projects/value/src/processor.rb', line 28

def foobara_manifest
  to_include = TypeDeclarations.foobara_manifest_context_to_include

  errors = error_classes.map do |error_class|
    if to_include
      to_include << error_class
    end
    error_class.foobara_manifest_reference
  end

  manifest = super.merge(
    name: processor_name,
    processor_type: :processor
  )

  unless errors.empty?
    manifest[:error_classes] = errors.sort
  end

  manifest
end

.instanceObject



83
84
85
86
87
88
89
90
91
92
93
# File 'foobara-0.0.110/projects/value/src/processor.rb', line 83

def instance
  @instance ||= begin
    if requires_parent_declaration_data?
      # :nocov:
      raise "Cannot treat processors dependent on parent declaration data as singletons"
      # :nocov:
    end

    requires_declaration_data? ? new(default_declaration_data) : new
  end
end

.new_with_agnostic_args(**rest) ⇒ Object



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'foobara-0.0.110/projects/value/src/processor.rb', line 50

def new_with_agnostic_args(**rest)
  allowed_keys = %i[declaration_data parent_declaration_data]

  invalid_keys = rest.keys - allowed_keys

  unless invalid_keys.empty?
    # :nocov:
    raise ArgumentError, "Invalid keys: #{invalid_keys.join(", ")} expected one of #{allowed_keys.join(", ")}"
    # :nocov:
  end

  args = []

  if requires_declaration_data?
    args << if rest.key?(:declaration_data)
              rest[:declaration_data]
            else
              default_declaration_data
            end
  end

  if requires_parent_declaration_data?
    args << rest[:parent_declaration_data] if requires_parent_declaration_data?
  end

  if args.empty? || args == [default_declaration_data]
    instance
  else
    # TODO: This feels goofy that the positional types of this interface is dependent on these flags...
    new(*args)
  end
end

.processor_nameObject



20
21
22
# File 'foobara-0.0.110/projects/value/src/processor.rb', line 20

def processor_name
  name || "Anonymous"
end

.requires_declaration_data?Boolean

Returns:

  • (Boolean)


135
136
137
# File 'foobara-0.0.110/projects/value/src/processor.rb', line 135

def requires_declaration_data?
  true
end

.requires_parent_declaration_data?Boolean

Returns:

  • (Boolean)


139
140
141
# File 'foobara-0.0.110/projects/value/src/processor.rb', line 139

def requires_parent_declaration_data?
  false
end

.symbolObject



131
132
133
# File 'foobara-0.0.110/projects/value/src/processor.rb', line 131

def symbol
  @symbol ||= Util.non_full_name_underscore(self)&.gsub(/_(processor|transformer|validator)$/, "")&.to_sym
end

Instance Method Details

#always_applicable?Boolean

This means it’s applicable regardless of value to transform. Override if different behavior is needed.

Returns:

  • (Boolean)


216
217
218
# File 'foobara-0.0.110/projects/value/src/processor.rb', line 216

def always_applicable?
  !!declaration_data
end

#applicable?(_value) ⇒ Boolean

A transformer with no declaration data or with declaration data of false is considered to be not applicable. Override this wherever different behavior is needed.

Returns:

  • (Boolean)


211
212
213
# File 'foobara-0.0.110/projects/value/src/processor.rb', line 211

def applicable?(_value)
  always_applicable?
end

#attribute_nameObject

TODO: this is a bit problematic. Maybe eliminate this instead of assuming it’s generally useful



276
277
278
# File 'foobara-0.0.110/projects/value/src/processor.rb', line 276

def attribute_name
  nil
end

#build_error(value = nil, error_class: self.error_class, symbol: error_class.symbol, message: error_message(value), context: error_context(value), path: error_path) ⇒ Object



246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
# File 'foobara-0.0.110/projects/value/src/processor.rb', line 246

def build_error(
  value = nil,
  error_class: self.error_class,
  symbol: error_class.symbol,
  message: error_message(value),
  context: error_context(value),
  path: error_path,
  **
)
  unless error_classes.include?(error_class)
    # :nocov:
    raise "invalid error"
    # :nocov:
  end

  error_class.new(
    path:,
    message:,
    context:,
    symbol:,
    **
  )
end

#dup_processor(**opts) ⇒ Object



285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
# File 'foobara-0.0.110/projects/value/src/processor.rb', line 285

def dup_processor(**opts)
  valid_opts = %i[declaration_data parent_declaration_data]

  invalid_opts = opts.keys - valid_opts

  unless invalid_opts.empty?
    # :nocov:
    raise ArgumentError, "Invalid opts #{invalid_opts.inspect} expected only #{valid_opts.inspect}"
    # :nocov:
  end

  declaration_data = if opts.key?(:declaration_data)
                       opts[:declaration_data]
                     else
                       self.declaration_data
                     end
  parent_declaration_data = if opts.key?(:parent_declaration_data)
                              opts[:parent_declaration_data]
                            else
                              self.parent_declaration_data
                            end

  self.class.new_with_agnostic_args(declaration_data:, parent_declaration_data:)
end

#error_context(_value) ⇒ Object



197
198
199
# File 'foobara-0.0.110/projects/value/src/processor.rb', line 197

def error_context(_value)
  error_class.context
end

#error_message(_value) ⇒ Object

TODO: probably actually better to pass it through to the error class method. Bring that back.



193
194
195
# File 'foobara-0.0.110/projects/value/src/processor.rb', line 193

def error_message(_value)
  error_class.message
end

#error_pathObject

TODO: does this make sense to have something called attribute_name here??



271
272
273
# File 'foobara-0.0.110/projects/value/src/processor.rb', line 271

def error_path
  Util.array(attribute_name)
end

#foobara_manifestObject

TODO: is this in the wrong place? Should this be an extension?



324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
# File 'foobara-0.0.110/projects/value/src/processor.rb', line 324

def foobara_manifest
  to_include = TypeDeclarations.foobara_manifest_context_to_include

  possible_errors = self.possible_errors.map do |possible_error|
    [possible_error.key.to_s, possible_error.foobara_manifest]
  end

  manifest = super.dup

  if scoped_category == :processor
    if to_include
      to_include << self.class
    end
    manifest[:processor_class] = self.class.foobara_manifest_reference
  end

  if requires_declaration_data?
    manifest[:declaration_data] = declaration_data
  end

  unless possible_errors.empty?
    manifest[:possible_errors] = possible_errors.sort.to_h
  end

  manifest
end

#inspectObject



310
311
312
313
314
315
316
317
318
319
320
# File 'foobara-0.0.110/projects/value/src/processor.rb', line 310

def inspect
  s = super

  if s.size > 400
    # :nocov:
    s = "#{s[0..400]}..."
    # :nocov:
  end

  s
end

#nameObject



176
177
178
# File 'foobara-0.0.110/projects/value/src/processor.rb', line 176

def name
  self.class.processor_name
end

#possible_errorsObject



201
202
203
204
205
206
207
# File 'foobara-0.0.110/projects/value/src/processor.rb', line 201

def possible_errors
  Foobara::Namespace.use created_in_namespace do
    error_classes.map do |error_class|
      PossibleError.new(error_class, processor: self)
    end
  end
end

#priorityObject

Helps control when it runs in a pipeline



281
282
283
# File 'foobara-0.0.110/projects/value/src/processor.rb', line 281

def priority
  Priority::MEDIUM
end

#process_outcome(old_outcome) ⇒ Object



230
231
232
233
234
235
236
237
238
239
240
# File 'foobara-0.0.110/projects/value/src/processor.rb', line 230

def process_outcome(old_outcome)
  return old_outcome if old_outcome.fatal?

  value = old_outcome.result

  return old_outcome unless applicable?(value)

  process_value(value).tap do |outcome|
    outcome.add_errors(old_outcome.errors)
  end
end

#process_outcome!(old_outcome) ⇒ Object



242
243
244
# File 'foobara-0.0.110/projects/value/src/processor.rb', line 242

def process_outcome!(old_outcome)
  process_outcome(old_outcome).result!
end

#process_value(_value) ⇒ Object



220
221
222
223
224
# File 'foobara-0.0.110/projects/value/src/processor.rb', line 220

def process_value(_value)
  # :nocov:
  raise "subclass responsibility"
  # :nocov:
end

#process_value!(value) ⇒ Object



226
227
228
# File 'foobara-0.0.110/projects/value/src/processor.rb', line 226

def process_value!(value)
  process_value(value).result!
end

#respond_to_missing?(method, private = false) ⇒ Boolean

Returns:

  • (Boolean)


361
362
363
# File 'foobara-0.0.110/projects/value/src/processor.rb', line 361

def respond_to_missing?(method, private = false)
  method == symbol || super
end

#runner(value) ⇒ Object

Whoa, forgot this existed. Shouldn’t we use this more?



188
189
190
# File 'foobara-0.0.110/projects/value/src/processor.rb', line 188

def runner(value)
  self.class::Runner.new(self, value)
end