burner 1.0.0.pre.alpha.5 → 1.0.0.pre.alpha.6
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 +4 -4
- data/README.md +12 -12
- data/exe/burner +2 -3
- data/lib/burner.rb +2 -0
- data/lib/burner/cli.rb +2 -0
- data/lib/burner/job.rb +25 -5
- data/lib/burner/jobs.rb +21 -41
- data/lib/burner/library.rb +30 -0
- data/lib/burner/library/collection/arrays_to_objects.rb +77 -0
- data/lib/burner/{jobs → library}/collection/graph.rb +3 -2
- data/lib/burner/{jobs → library}/collection/objects_to_arrays.rb +40 -4
- data/lib/burner/{jobs → library}/collection/shift.rb +5 -4
- data/lib/burner/{jobs → library}/collection/transform.rb +13 -9
- data/lib/burner/{jobs → library}/collection/unpivot.rb +7 -5
- data/lib/burner/{jobs → library}/collection/values.rb +5 -4
- data/lib/burner/{jobs → library}/deserialize/csv.rb +2 -1
- data/lib/burner/{jobs → library}/deserialize/json.rb +4 -1
- data/lib/burner/{jobs → library}/deserialize/yaml.rb +8 -2
- data/lib/burner/{jobs → library}/dummy.rb +3 -1
- data/lib/burner/{jobs → library}/echo.rb +3 -1
- data/lib/burner/{jobs → library}/io/base.rb +1 -1
- data/lib/burner/{jobs → library}/io/exist.rb +3 -1
- data/lib/burner/{jobs → library}/io/read.rb +4 -1
- data/lib/burner/{jobs → library}/io/write.rb +7 -2
- data/lib/burner/{jobs → library}/serialize/csv.rb +3 -2
- data/lib/burner/{jobs → library}/serialize/json.rb +4 -1
- data/lib/burner/{jobs → library}/serialize/yaml.rb +4 -1
- data/lib/burner/{jobs/set.rb → library/set_value.rb} +5 -2
- data/lib/burner/{jobs → library}/sleep.rb +3 -1
- data/lib/burner/modeling.rb +2 -0
- data/lib/burner/modeling/attribute.rb +29 -0
- data/lib/burner/modeling/attribute_renderer.rb +32 -0
- data/lib/burner/payload.rb +15 -12
- data/lib/burner/pipeline.rb +20 -1
- data/lib/burner/side_effects.rb +10 -0
- data/lib/burner/side_effects/written_file.rb +28 -0
- data/lib/burner/util.rb +10 -0
- data/lib/burner/util/arrayable.rb +30 -0
- data/lib/burner/version.rb +1 -1
- metadata +30 -26
- data/lib/burner/jobs/collection/arrays_to_objects.rb +0 -43
- data/lib/burner/jobs/collection/transform/attribute.rb +0 -33
- data/lib/burner/jobs/collection/transform/attribute_renderer.rb +0 -36
- data/lib/burner/written_file.rb +0 -28
@@ -7,14 +7,17 @@
|
|
7
7
|
# LICENSE file in the root directory of this source tree.
|
8
8
|
#
|
9
9
|
|
10
|
-
require_relative 'transform/attribute'
|
11
|
-
require_relative 'transform/attribute_renderer'
|
12
|
-
|
13
10
|
module Burner
|
14
|
-
|
11
|
+
module Library
|
15
12
|
module Collection
|
16
|
-
# Iterate over all objects and return a new set of transformed objects.
|
17
|
-
#
|
13
|
+
# Iterate over all objects and return a new set of transformed objects. The object is
|
14
|
+
# transformed per the "transformers" attribute for its attributes. An attribute defines
|
15
|
+
# the ultimate key to place the value in and then the transformer pipeline to use to
|
16
|
+
# derive the value. Under the hood this uses the Realize library:
|
17
|
+
# https://github.com/bluemarblepayroll/realize
|
18
|
+
# For more information on the specific contract for attributes, see the
|
19
|
+
# Burner::Modeling::Attribute class.
|
20
|
+
#
|
18
21
|
# Expected Payload#value input: array of objects.
|
19
22
|
# Payload#value output: An array of objects.
|
20
23
|
class Transform < Job
|
@@ -30,14 +33,15 @@ module Burner
|
|
30
33
|
@resolver = Objectable.resolver(separator: separator)
|
31
34
|
@exclusive = exclusive || false
|
32
35
|
|
33
|
-
@attribute_renderers =
|
34
|
-
|
36
|
+
@attribute_renderers =
|
37
|
+
Modeling::Attribute.array(attributes)
|
38
|
+
.map { |a| Modeling::AttributeRenderer.new(a, resolver) }
|
35
39
|
|
36
40
|
freeze
|
37
41
|
end
|
38
42
|
|
39
43
|
def perform(output, payload)
|
40
|
-
payload.value = (payload.value
|
44
|
+
payload.value = array(payload.value).map { |row| transform(row, payload.time) }
|
41
45
|
|
42
46
|
attr_count = attribute_renderers.length
|
43
47
|
row_count = payload.value.length
|
@@ -8,11 +8,12 @@
|
|
8
8
|
#
|
9
9
|
|
10
10
|
module Burner
|
11
|
-
|
11
|
+
module Library
|
12
12
|
module Collection
|
13
13
|
# Take an array of objects and un-pivot groups of keys into rows.
|
14
14
|
# Under the hood it uses HashMath's Unpivot class:
|
15
15
|
# https://github.com/bluemarblepayroll/hash_math
|
16
|
+
#
|
16
17
|
# Expected Payload#value input: array of objects.
|
17
18
|
# Payload#value output: An array of objects.
|
18
19
|
class Unpivot < Job
|
@@ -27,15 +28,16 @@ module Burner
|
|
27
28
|
end
|
28
29
|
|
29
30
|
def perform(output, payload)
|
30
|
-
pivot_count
|
31
|
-
key_count
|
32
|
-
|
31
|
+
pivot_count = unpivot.pivot_set.pivots.length
|
32
|
+
key_count = unpivot.pivot_set.pivots.map { |p| p.keys.length }.sum
|
33
|
+
payload.value = array(payload.value)
|
34
|
+
object_count = payload.value.length || 0
|
33
35
|
|
34
36
|
message = "#{pivot_count} Pivots, Key(s): #{key_count} key(s), #{object_count} objects(s)"
|
35
37
|
|
36
38
|
output.detail(message)
|
37
39
|
|
38
|
-
payload.value =
|
40
|
+
payload.value = payload.value.flat_map { |object| unpivot.expand(object) }
|
39
41
|
|
40
42
|
nil
|
41
43
|
end
|
@@ -8,11 +8,12 @@
|
|
8
8
|
#
|
9
9
|
|
10
10
|
module Burner
|
11
|
-
|
11
|
+
module Library
|
12
12
|
module Collection
|
13
13
|
# Take an array of objects and call #values on each object.
|
14
14
|
# If include_keys is true (it is false by default), then call #keys on the first
|
15
15
|
# object and inject that as a "header" object.
|
16
|
+
#
|
16
17
|
# Expected Payload#value input: array of objects.
|
17
18
|
# Payload#value output: An array of arrays.
|
18
19
|
class Values < Job
|
@@ -27,9 +28,9 @@ module Burner
|
|
27
28
|
end
|
28
29
|
|
29
30
|
def perform(_output, payload)
|
30
|
-
|
31
|
-
|
32
|
-
|
31
|
+
payload.value = array(payload.value)
|
32
|
+
keys = include_keys ? [keys(payload.value.first)] : []
|
33
|
+
values = payload.value.map { |object| values(object) }
|
33
34
|
payload.value = keys + values
|
34
35
|
|
35
36
|
nil
|
@@ -8,9 +8,10 @@
|
|
8
8
|
#
|
9
9
|
|
10
10
|
module Burner
|
11
|
-
|
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
17
|
class Csv < Job
|
@@ -8,9 +8,12 @@
|
|
8
8
|
#
|
9
9
|
|
10
10
|
module Burner
|
11
|
-
|
11
|
+
module Library
|
12
12
|
module Deserialize
|
13
13
|
# Take a JSON string and deserialize into object(s).
|
14
|
+
#
|
15
|
+
# Expected Payload#value input: string of JSON data.
|
16
|
+
# Payload#value output: anything, as specified by the JSON de-serializer.
|
14
17
|
class Json < Job
|
15
18
|
def perform(_output, payload)
|
16
19
|
payload.value = JSON.parse(payload.value)
|
@@ -8,9 +8,15 @@
|
|
8
8
|
#
|
9
9
|
|
10
10
|
module Burner
|
11
|
-
|
11
|
+
module Library
|
12
12
|
module Deserialize
|
13
|
-
# Take a YAML string and deserialize into object(s).
|
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.
|
14
20
|
class Yaml < Job
|
15
21
|
attr_reader :safe
|
16
22
|
|
@@ -10,10 +10,12 @@
|
|
10
10
|
require_relative 'base'
|
11
11
|
|
12
12
|
module Burner
|
13
|
-
|
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
|
+
#
|
18
|
+
# Note: this does not use Payload#value.
|
17
19
|
class Exist < Base
|
18
20
|
attr_reader :short_circuit
|
19
21
|
|
@@ -10,9 +10,12 @@
|
|
10
10
|
require_relative 'base'
|
11
11
|
|
12
12
|
module Burner
|
13
|
-
|
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
|
|
@@ -10,9 +10,12 @@
|
|
10
10
|
require_relative 'base'
|
11
11
|
|
12
12
|
module Burner
|
13
|
-
|
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
|
|
@@ -35,12 +38,14 @@ module Burner
|
|
35
38
|
File.open(compiled_path, mode) { |io| io.write(payload.value) }
|
36
39
|
end.real
|
37
40
|
|
38
|
-
|
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
|
|
47
|
+
payload.add_side_effect(side_effect)
|
48
|
+
|
44
49
|
nil
|
45
50
|
end
|
46
51
|
|
@@ -8,15 +8,16 @@
|
|
8
8
|
#
|
9
9
|
|
10
10
|
module Burner
|
11
|
-
|
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
17
|
class Csv < Job
|
17
18
|
def perform(_output, payload)
|
18
19
|
payload.value = CSV.generate(options) do |csv|
|
19
|
-
(payload.value
|
20
|
+
array(payload.value).each do |row|
|
20
21
|
csv << row
|
21
22
|
end
|
22
23
|
end
|
@@ -8,9 +8,12 @@
|
|
8
8
|
#
|
9
9
|
|
10
10
|
module Burner
|
11
|
-
|
11
|
+
module Library
|
12
12
|
module Serialize
|
13
13
|
# Treat value like a Ruby object and serialize it using JSON.
|
14
|
+
#
|
15
|
+
# Expected Payload#value input: anything.
|
16
|
+
# Payload#value output: string representing the output of the JSON serializer.
|
14
17
|
class Json < Job
|
15
18
|
def perform(_output, payload)
|
16
19
|
payload.value = payload.value.to_json
|
@@ -8,9 +8,12 @@
|
|
8
8
|
#
|
9
9
|
|
10
10
|
module Burner
|
11
|
-
|
11
|
+
module Library
|
12
12
|
module Serialize
|
13
13
|
# Treat value like a Ruby object and serialize it using YAML.
|
14
|
+
#
|
15
|
+
# Expected Payload#value input: anything.
|
16
|
+
# Payload#value output: string representing the output of the YAML serializer.
|
14
17
|
class Yaml < Job
|
15
18
|
def perform(_output, payload)
|
16
19
|
payload.value = payload.value.to_yaml
|
@@ -8,9 +8,12 @@
|
|
8
8
|
#
|
9
9
|
|
10
10
|
module Burner
|
11
|
-
|
11
|
+
module Library
|
12
12
|
# Arbitrarily set value
|
13
|
-
|
13
|
+
#
|
14
|
+
# Expected Payload#value input: anything.
|
15
|
+
# Payload#value output: whatever value was specified in this job.
|
16
|
+
class SetValue < Job
|
14
17
|
attr_reader :value
|
15
18
|
|
16
19
|
def initialize(name:, value: nil)
|
data/lib/burner/modeling.rb
CHANGED
@@ -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
|
@@ -0,0 +1,32 @@
|
|
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
|
+
# Composed of an Attribute instance and a Pipeline instance. It knows how to
|
13
|
+
# render/transform an Attribute. Since this library is data-first, these intermediary
|
14
|
+
# objects are necessary for non-data-centric modeling.
|
15
|
+
class AttributeRenderer
|
16
|
+
extend Forwardable
|
17
|
+
|
18
|
+
attr_reader :attribute, :pipeline
|
19
|
+
|
20
|
+
def_delegators :attribute, :key
|
21
|
+
|
22
|
+
def_delegators :pipeline, :transform
|
23
|
+
|
24
|
+
def initialize(attribute, resolver)
|
25
|
+
@attribute = attribute
|
26
|
+
@pipeline = Realize::Pipeline.new(attribute.transformers, resolver: resolver)
|
27
|
+
|
28
|
+
freeze
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/burner/payload.rb
CHANGED
@@ -7,35 +7,38 @@
|
|
7
7
|
# LICENSE file in the root directory of this source tree.
|
8
8
|
#
|
9
9
|
|
10
|
-
require_relative 'written_file'
|
11
|
-
|
12
10
|
module Burner
|
13
11
|
# The input for all Job#perform methods. The main notion of this object is its "value"
|
14
12
|
# attribute. This is dynamic and weak on purpose and is subject to whatever the Job#perform
|
15
13
|
# methods decides it is. This definitely adds an order-of-magnitude complexity to this whole
|
16
14
|
# library and lifecycle, but I am not sure there is any other way around it: trying to build
|
17
15
|
# a generic, open-ended object pipeline to serve almost any use case.
|
16
|
+
#
|
17
|
+
# The side_effects attribute can also be utilized as a way for jobs to emit any data in a more
|
18
|
+
# structured/additive manner. The initial use case for this was for Burner's core IO jobs to
|
19
|
+
# report back the files it has written in a more structured data way (as opposed to simply
|
20
|
+
# writing some information to the output.)
|
18
21
|
class Payload
|
19
22
|
attr_accessor :value
|
20
23
|
|
21
24
|
attr_reader :params,
|
22
|
-
:
|
23
|
-
:
|
25
|
+
:side_effects,
|
26
|
+
:time
|
24
27
|
|
25
28
|
def initialize(
|
26
29
|
params: {},
|
30
|
+
side_effects: [],
|
27
31
|
time: Time.now.utc,
|
28
|
-
value: nil
|
29
|
-
written_files: []
|
32
|
+
value: nil
|
30
33
|
)
|
31
|
-
@params
|
32
|
-
@
|
33
|
-
@
|
34
|
-
@
|
34
|
+
@params = params || {}
|
35
|
+
@side_effects = side_effects || []
|
36
|
+
@time = time || Time.now.utc
|
37
|
+
@value = value
|
35
38
|
end
|
36
39
|
|
37
|
-
def
|
38
|
-
tap {
|
40
|
+
def add_side_effect(side_effect)
|
41
|
+
tap { side_effects << side_effect }
|
39
42
|
end
|
40
43
|
end
|
41
44
|
end
|