burner 1.0.0.pre.alpha.4 → 1.0.0.pre.alpha.9

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.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +2 -0
  3. data/README.md +52 -47
  4. data/burner.gemspec +1 -1
  5. data/exe/burner +2 -3
  6. data/lib/burner.rb +2 -0
  7. data/lib/burner/cli.rb +2 -0
  8. data/lib/burner/job.rb +27 -9
  9. data/lib/burner/job_with_register.rb +24 -0
  10. data/lib/burner/jobs.rb +28 -39
  11. data/lib/burner/library.rb +32 -0
  12. data/lib/burner/library/collection/arrays_to_objects.rb +75 -0
  13. data/lib/burner/{jobs → library}/collection/graph.rb +7 -8
  14. data/lib/burner/library/collection/objects_to_arrays.rb +88 -0
  15. data/lib/burner/{jobs → library}/collection/shift.rb +8 -9
  16. data/lib/burner/{jobs → library}/collection/transform.rb +17 -15
  17. data/lib/burner/{jobs → library}/collection/unpivot.rb +17 -9
  18. data/lib/burner/library/collection/validate.rb +89 -0
  19. data/lib/burner/library/collection/values.rb +49 -0
  20. data/lib/burner/{jobs → library}/deserialize/csv.rb +4 -5
  21. data/lib/burner/{jobs → library}/deserialize/json.rb +6 -5
  22. data/lib/burner/{jobs → library}/deserialize/yaml.rb +13 -7
  23. data/lib/burner/{jobs → library}/dummy.rb +4 -4
  24. data/lib/burner/{jobs → library}/echo.rb +3 -3
  25. data/lib/burner/{jobs → library}/io/base.rb +4 -4
  26. data/lib/burner/{jobs → library}/io/exist.rb +11 -9
  27. data/lib/burner/{jobs → library}/io/read.rb +7 -6
  28. data/lib/burner/{jobs → library}/io/write.rb +9 -6
  29. data/lib/burner/{jobs → library}/serialize/csv.rb +5 -6
  30. data/lib/burner/{jobs → library}/serialize/json.rb +6 -5
  31. data/lib/burner/{jobs → library}/serialize/yaml.rb +6 -5
  32. data/lib/burner/{jobs/set.rb → library/set_value.rb} +8 -7
  33. data/lib/burner/{jobs → library}/sleep.rb +3 -3
  34. data/lib/burner/modeling.rb +3 -0
  35. data/lib/burner/modeling/attribute.rb +29 -0
  36. data/lib/burner/modeling/attribute_renderer.rb +32 -0
  37. data/lib/burner/modeling/validations.rb +23 -0
  38. data/lib/burner/modeling/validations/base.rb +35 -0
  39. data/lib/burner/modeling/validations/blank.rb +31 -0
  40. data/lib/burner/modeling/validations/present.rb +31 -0
  41. data/lib/burner/payload.rb +52 -15
  42. data/lib/burner/pipeline.rb +23 -4
  43. data/lib/burner/side_effects.rb +10 -0
  44. data/lib/burner/side_effects/written_file.rb +28 -0
  45. data/lib/burner/step.rb +1 -5
  46. data/lib/burner/util.rb +11 -0
  47. data/lib/burner/util/arrayable.rb +30 -0
  48. data/lib/burner/util/string_template.rb +42 -0
  49. data/lib/burner/version.rb +1 -1
  50. metadata +40 -28
  51. data/lib/burner/jobs/collection/arrays_to_objects.rb +0 -43
  52. data/lib/burner/jobs/collection/objects_to_arrays.rb +0 -54
  53. data/lib/burner/jobs/collection/transform/attribute.rb +0 -33
  54. data/lib/burner/jobs/collection/transform/attribute_renderer.rb +0 -36
  55. data/lib/burner/string_template.rb +0 -40
  56. data/lib/burner/written_file.rb +0 -28
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Copyright (c) 2020-present, Blue Marble Payroll, LLC
5
+ #
6
+ # This source code is licensed under the MIT license found in the
7
+ # LICENSE file in the root directory of this source tree.
8
+ #
9
+
10
+ module Burner
11
+ module Library
12
+ module Collection
13
+ # Take an array of objects and call #values on each object.
14
+ # If include_keys is true (it is false by default), then call #keys on the first
15
+ # object and inject that as a "header" object.
16
+ #
17
+ # Expected Payload#value input: array of objects.
18
+ # Payload#value output: An array of arrays.
19
+ class Values < JobWithRegister
20
+ attr_reader :include_keys
21
+
22
+ def initialize(name:, include_keys: false, register: '')
23
+ super(name: name, register: register)
24
+
25
+ @include_keys = include_keys || false
26
+
27
+ freeze
28
+ end
29
+
30
+ def perform(_output, payload)
31
+ payload[register] = array(payload[register])
32
+ keys = include_keys ? [keys(payload[register].first)] : []
33
+ values = payload[register].map { |object| values(object) }
34
+ payload[register] = keys + values
35
+ end
36
+
37
+ private
38
+
39
+ def keys(object)
40
+ object.respond_to?(:keys) ? object.keys : []
41
+ end
42
+
43
+ def values(object)
44
+ object.respond_to?(:values) ? object.values : []
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -8,19 +8,18 @@
8
8
  #
9
9
 
10
10
  module Burner
11
- class Jobs
11
+ module Library
12
12
  module Deserialize
13
13
  # Take a CSV string and de-serialize into object(s).
14
+ #
14
15
  # Expected Payload#value input: nothing.
15
16
  # Payload#value output: an array of arrays. Each inner array represents one data row.
16
- class Csv < Job
17
+ class Csv < JobWithRegister
17
18
  # This currently only supports returning an array of arrays, including the header row.
18
19
  # In the future this could be extended to offer more customizable options, such as
19
20
  # making it return an array of hashes with the columns mapped, etc.)
20
21
  def perform(_output, payload)
21
- payload.value = CSV.new(payload.value, headers: false).to_a
22
-
23
- nil
22
+ payload[register] = CSV.new(payload[register], headers: false).to_a
24
23
  end
25
24
  end
26
25
  end
@@ -8,14 +8,15 @@
8
8
  #
9
9
 
10
10
  module Burner
11
- class Jobs
11
+ module Library
12
12
  module Deserialize
13
13
  # Take a JSON string and deserialize into object(s).
14
- class Json < Job
14
+ #
15
+ # Expected Payload#value input: string of JSON data.
16
+ # Payload#value output: anything, as specified by the JSON de-serializer.
17
+ class Json < JobWithRegister
15
18
  def perform(_output, payload)
16
- payload.value = JSON.parse(payload.value)
17
-
18
- nil
19
+ payload[register] = JSON.parse(payload[register])
19
20
  end
20
21
  end
21
22
  end
@@ -8,14 +8,20 @@
8
8
  #
9
9
 
10
10
  module Burner
11
- class Jobs
11
+ module Library
12
12
  module Deserialize
13
- # Take a YAML string and deserialize into object(s).
14
- class Yaml < Job
13
+ # Take a YAML string and deserialize into object(s). It uses YAML#safe_load by default,
14
+ # which ensures only a limited number of Ruby object constants can be hydrated by the
15
+ # YAML. If you wish to ease this restriction, for example if you have custom serialization
16
+ # for custom classes, then you can pass in safe: false.
17
+ #
18
+ # Expected Payload#value input: string of YAML data.
19
+ # Payload#value output: anything as specified by the YAML de-serializer.
20
+ class Yaml < JobWithRegister
15
21
  attr_reader :safe
16
22
 
17
- def initialize(name:, safe: true)
18
- super(name: name)
23
+ def initialize(name:, register: '', safe: true)
24
+ super(name: name, register: register)
19
25
 
20
26
  @safe = safe
21
27
 
@@ -30,9 +36,9 @@ module Burner
30
36
  def perform(output, payload)
31
37
  output.detail('Warning: loading YAML not using safe_load.') unless safe
32
38
 
33
- payload.value = safe ? YAML.safe_load(payload.value) : YAML.load(payload.value)
39
+ value = payload[register]
34
40
 
35
- nil
41
+ payload[register] = safe ? YAML.safe_load(value) : YAML.load(value)
36
42
  end
37
43
  # rubocop:enable Security/YAMLLoad
38
44
  end
@@ -8,12 +8,12 @@
8
8
  #
9
9
 
10
10
  module Burner
11
- class Jobs
11
+ module Library
12
12
  # Do nothing.
13
+ #
14
+ # Note: this does not use Payload#value.
13
15
  class Dummy < Job
14
- def perform(_output, _payload)
15
- nil
16
- end
16
+ def perform(_output, _payload); end
17
17
  end
18
18
  end
19
19
  end
@@ -8,8 +8,10 @@
8
8
  #
9
9
 
10
10
  module Burner
11
- class Jobs
11
+ module Library
12
12
  # Output a simple message to the output.
13
+ #
14
+ # Note: this does not use Payload#value.
13
15
  class Echo < Job
14
16
  attr_reader :message
15
17
 
@@ -25,8 +27,6 @@ module Burner
25
27
  compiled_message = job_string_template(message, output, payload)
26
28
 
27
29
  output.detail(compiled_message)
28
-
29
- nil
30
30
  end
31
31
  end
32
32
  end
@@ -8,14 +8,14 @@
8
8
  #
9
9
 
10
10
  module Burner
11
- class Jobs
11
+ module Library
12
12
  module IO
13
13
  # Common configuration/code for all IO Job subclasses.
14
- class Base < Job
14
+ class Base < JobWithRegister
15
15
  attr_reader :path
16
16
 
17
- def initialize(name:, path:)
18
- super(name: name)
17
+ def initialize(name:, path:, register: '')
18
+ super(name: name, register: register)
19
19
 
20
20
  raise ArgumentError, 'path is required' if path.to_s.empty?
21
21
 
@@ -10,19 +10,22 @@
10
10
  require_relative 'base'
11
11
 
12
12
  module Burner
13
- class Jobs
13
+ module Library
14
14
  module IO
15
15
  # Check to see if a file exists. If short_circuit is set to true and the file
16
16
  # does not exist then the job will return false and short circuit the pipeline.
17
- class Exist < Base
18
- attr_reader :short_circuit
17
+ #
18
+ # Note: this does not use Payload#value.
19
+ class Exist < Job
20
+ attr_reader :path, :short_circuit
19
21
 
20
22
  def initialize(name:, path:, short_circuit: false)
21
- super(name: name, path: path)
23
+ super(name: name)
22
24
 
23
- @short_circuit = short_circuit || false
25
+ raise ArgumentError, 'path is required' if path.to_s.empty?
24
26
 
25
- freeze
27
+ @path = path.to_s
28
+ @short_circuit = short_circuit || false
26
29
  end
27
30
 
28
31
  def perform(output, payload)
@@ -33,9 +36,8 @@ module Burner
33
36
 
34
37
  output.detail("The path: #{compiled_path} #{verb} exist")
35
38
 
36
- # if anything but false is returned then the pipeline will not short circuit. So
37
- # we need to make sure we explicitly return false.
38
- short_circuit && !exists ? false : nil
39
+ # if anything but false is returned then the pipeline will not short circuit.
40
+ payload.halt_pipeline if short_circuit && !exists
39
41
  end
40
42
  end
41
43
  end
@@ -10,14 +10,17 @@
10
10
  require_relative 'base'
11
11
 
12
12
  module Burner
13
- class Jobs
13
+ module Library
14
14
  module IO
15
15
  # Read value from disk.
16
+ #
17
+ # Expected Payload#value input: nothing.
18
+ # Payload#value output: contents of the specified file.
16
19
  class Read < Base
17
20
  attr_reader :binary
18
21
 
19
- def initialize(name:, path:, binary: false)
20
- super(name: name, path: path)
22
+ def initialize(name:, path:, binary: false, register: '')
23
+ super(name: name, path: path, register: register)
21
24
 
22
25
  @binary = binary || false
23
26
 
@@ -29,9 +32,7 @@ module Burner
29
32
 
30
33
  output.detail("Reading: #{compiled_path}")
31
34
 
32
- payload.value = File.open(compiled_path, mode, &:read)
33
-
34
- nil
35
+ payload[register] = File.open(compiled_path, mode, &:read)
35
36
  end
36
37
 
37
38
  private
@@ -10,14 +10,17 @@
10
10
  require_relative 'base'
11
11
 
12
12
  module Burner
13
- class Jobs
13
+ module Library
14
14
  module IO
15
15
  # Write value to disk.
16
+ #
17
+ # Expected Payload#value input: anything.
18
+ # Payload#value output: whatever was passed in.
16
19
  class Write < Base
17
20
  attr_reader :binary
18
21
 
19
- def initialize(name:, path:, binary: false)
20
- super(name: name, path: path)
22
+ def initialize(name:, path:, binary: false, register: '')
23
+ super(name: name, path: path, register: register)
21
24
 
22
25
  @binary = binary || false
23
26
 
@@ -32,16 +35,16 @@ module Burner
32
35
  output.detail("Writing: #{compiled_path}")
33
36
 
34
37
  time_in_seconds = Benchmark.measure do
35
- File.open(compiled_path, mode) { |io| io.write(payload.value) }
38
+ File.open(compiled_path, mode) { |io| io.write(payload[register]) }
36
39
  end.real
37
40
 
38
- payload.add_written_file(
41
+ side_effect = SideEffects::WrittenFile.new(
39
42
  logical_filename: compiled_path,
40
43
  physical_filename: compiled_path,
41
44
  time_in_seconds: time_in_seconds
42
45
  )
43
46
 
44
- nil
47
+ payload.add_side_effect(side_effect)
45
48
  end
46
49
 
47
50
  private
@@ -8,20 +8,19 @@
8
8
  #
9
9
 
10
10
  module Burner
11
- class Jobs
11
+ module Library
12
12
  module Serialize
13
13
  # Take an array of arrays and create a CSV.
14
+ #
14
15
  # Expected Payload#value input: array of arrays.
15
16
  # Payload#value output: a serialized CSV string.
16
- class Csv < Job
17
+ class Csv < JobWithRegister
17
18
  def perform(_output, payload)
18
- payload.value = CSV.generate(options) do |csv|
19
- (payload.value || []).each do |row|
19
+ payload[register] = CSV.generate(options) do |csv|
20
+ array(payload[register]).each do |row|
20
21
  csv << row
21
22
  end
22
23
  end
23
-
24
- nil
25
24
  end
26
25
 
27
26
  private
@@ -8,14 +8,15 @@
8
8
  #
9
9
 
10
10
  module Burner
11
- class Jobs
11
+ module Library
12
12
  module Serialize
13
13
  # Treat value like a Ruby object and serialize it using JSON.
14
- class Json < Job
14
+ #
15
+ # Expected Payload#value input: anything.
16
+ # Payload#value output: string representing the output of the JSON serializer.
17
+ class Json < JobWithRegister
15
18
  def perform(_output, payload)
16
- payload.value = payload.value.to_json
17
-
18
- nil
19
+ payload[register] = payload[register].to_json
19
20
  end
20
21
  end
21
22
  end
@@ -8,14 +8,15 @@
8
8
  #
9
9
 
10
10
  module Burner
11
- class Jobs
11
+ module Library
12
12
  module Serialize
13
13
  # Treat value like a Ruby object and serialize it using YAML.
14
- class Yaml < Job
14
+ #
15
+ # Expected Payload#value input: anything.
16
+ # Payload#value output: string representing the output of the YAML serializer.
17
+ class Yaml < JobWithRegister
15
18
  def perform(_output, payload)
16
- payload.value = payload.value.to_yaml
17
-
18
- nil
19
+ payload[register] = payload[register].to_yaml
19
20
  end
20
21
  end
21
22
  end
@@ -8,13 +8,16 @@
8
8
  #
9
9
 
10
10
  module Burner
11
- class Jobs
11
+ module Library
12
12
  # Arbitrarily set value
13
- class Set < Job
13
+ #
14
+ # Expected Payload#value input: anything.
15
+ # Payload#value output: whatever value was specified in this job.
16
+ class SetValue < JobWithRegister
14
17
  attr_reader :value
15
18
 
16
- def initialize(name:, value: nil)
17
- super(name: name)
19
+ def initialize(name:, register: '', value: nil)
20
+ super(name: name, register: register)
18
21
 
19
22
  @value = value
20
23
 
@@ -22,9 +25,7 @@ module Burner
22
25
  end
23
26
 
24
27
  def perform(_output, payload)
25
- payload.value = value
26
-
27
- nil
28
+ payload[register] = value
28
29
  end
29
30
  end
30
31
  end
@@ -8,8 +8,10 @@
8
8
  #
9
9
 
10
10
  module Burner
11
- class Jobs
11
+ module Library
12
12
  # Arbitrarily put thread to sleep for X number of seconds
13
+ #
14
+ # Payload#value output: whatever value was specified in this job.
13
15
  class Sleep < Job
14
16
  attr_reader :seconds
15
17
 
@@ -25,8 +27,6 @@ module Burner
25
27
  output.detail("Going to sleep for #{seconds} second(s)")
26
28
 
27
29
  Kernel.sleep(seconds)
28
-
29
- nil
30
30
  end
31
31
  end
32
32
  end
@@ -7,4 +7,7 @@
7
7
  # LICENSE file in the root directory of this source tree.
8
8
  #
9
9
 
10
+ require_relative 'modeling/attribute'
11
+ require_relative 'modeling/attribute_renderer'
10
12
  require_relative 'modeling/key_index_mapping'
13
+ require_relative 'modeling/validations'
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Copyright (c) 2020-present, Blue Marble Payroll, LLC
5
+ #
6
+ # This source code is licensed under the MIT license found in the
7
+ # LICENSE file in the root directory of this source tree.
8
+ #
9
+
10
+ module Burner
11
+ module Modeling
12
+ # Defines a top-level key and the associated transformers for deriving the final value
13
+ # to set the key to.
14
+ class Attribute
15
+ acts_as_hashable
16
+
17
+ attr_reader :key, :transformers
18
+
19
+ def initialize(key:, transformers: [])
20
+ raise ArgumentError, 'key is required' if key.to_s.empty?
21
+
22
+ @key = key.to_s
23
+ @transformers = Realize::Transformers.array(transformers)
24
+
25
+ freeze
26
+ end
27
+ end
28
+ end
29
+ end