burner 1.3.0.pre.alpha → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 340ebade1c4d3cc238925cb21718571dd666c3c16b6ee60d8de73c4fe017d562
4
- data.tar.gz: 33b272e7a4b4a15a8e92b5f7eccb9df7ec6d685f767ecbf8434dedcf7ed901b5
3
+ metadata.gz: 432bc8b6df5023860fce0b34698d777e30389d3af17c800c978f304ec5eef53c
4
+ data.tar.gz: e75d8dac2a871e44b427f62dd727aefe32beefaad9838af28d7d5a34da6bf6ed
5
5
  SHA512:
6
- metadata.gz: a5d082d14302d38e2acf89ab1275004ae69f78087e40da2a68c15e25770a9ebfc7fa2cb8e9e7268d132e2ade6c3d6db9e259ab3e8254ae476fcd1d46a4f40eb2
7
- data.tar.gz: c1a9bda279afa44c5cb5a41c4ac792a62af317d91985799a14609c7b6d33e5494d322ad14262281b3fead76cc6471bebd7b507e1158dea7f9278196b36ab1726
6
+ metadata.gz: 2c040e39a740139a320a98fc76e75180558585546a97218d7842cb746e9f0d451665b6abd647595b64ecf0ffb8e67de8c1103649bae80d88f801f228f0cb1980
7
+ data.tar.gz: a39ea1c4eca448a39e969d52bae28f1fc30c3e50009a96058beb9f519ca73d1dd3ee46adb8ca99a5c9e63edffdb5a858e561869d93b26adc36cc4e7f7f05f611
@@ -1,4 +1,20 @@
1
- # 1.3.0 (TBD)
1
+
2
+ # 1.5.0 (December 21st, 2020)
3
+
4
+ Added Jobs:
5
+
6
+ * b/collection/zip
7
+ # 1.4.0 (December 17th, 2020)
8
+
9
+ Additions:
10
+
11
+ * byte_order_mark option for b/serialize/csv job
12
+
13
+ Added Jobs:
14
+
15
+ * b/compress/row_reader
16
+ * b/io/row_reader
17
+ # 1.3.0 (December 11th, 2020)
2
18
 
3
19
  Additions:
4
20
 
data/README.md CHANGED
@@ -227,6 +227,11 @@ This library only ships with very basic, rudimentary jobs that are meant to just
227
227
  * **b/collection/unpivot** [pivot_set, register]: Take an array of objects and unpivot specific sets of keys into rows. Under the hood it uses [HashMath's Unpivot class](https://github.com/bluemarblepayroll/hash_math#unpivot-hash-key-coalescence-and-row-extrapolation).
228
228
  * **b/collection/validate** [invalid_register, join_char, message_key, register, separator, validations]: Take an array of objects, run it through each declared validator, and split the objects into two registers. The valid objects will be split into the current register while the invalid ones will go into the invalid_register as declared. Optional arguments, join_char and message_key, help determine the compiled error messages. The separator option can be utilized to use dot-notation for validating keys. See each validation's options by viewing their classes within the `lib/modeling/validations` directory.
229
229
  * **b/collection/values** [include_keys, register]: Take an array of objects and call `#values` on each object. If include_keys is true (it is false by default), then call `#keys` on the first object and inject that as a "header" object.
230
+ * **b/collection/zip** [base_register, register, with_register]: Combines `base_register` and `with_register`s' data to form one single array in `register`. It will combine each element, positionally in each array to form the final array. For example: ['hello', 'bugs'] + ['world', 'bunny'] => [['hello', 'world'], ['bugs', 'bunny']]
231
+
232
+ #### Compression
233
+
234
+ * **b/compress/row_reader** [data_key, ignore_blank_path, ignore_blank_data, path_key, register, separator]: Iterates over an array of objects, extracts a path and data in each object, and creates a zip file.
230
235
 
231
236
  #### De-serialization
232
237
 
@@ -240,11 +245,12 @@ By default all jobs will use the `Burner::Disks::Local` disk for its persistence
240
245
 
241
246
  * **b/io/exist** [disk, path, short_circuit]: Check to see if a file exists. The path parameter can be interpolated using `Payload#params`. If short_circuit was set to true (defaults to false) and the file does not exist then the pipeline will be short-circuited.
242
247
  * **b/io/read** [binary, disk, path, register]: Read in a local file. The path parameter can be interpolated using `Payload#params`. If the contents are binary, pass in `binary: true` to open it up in binary+read mode.
248
+ * **b/io/row_reader** [data_key, disk, ignore_blank_path, ignore_file_not_found, path_key, register, separator]: Iterates over an array of objects, extracts a filepath from a key in each object, and attempts to load the file's content for each record. The file's content will be stored at the specified data_key. By default missing paths or files will be treated as hard errors. If you wish to ignore these then pass in true for ignore_blank_path and/or ignore_file_not_found.
243
249
  * **b/io/write** [binary, disk, path, register]: Write to a local file. The path parameter can be interpolated using `Payload#params`. If the contents are binary, pass in `binary: true` to open it up in binary+write mode.
244
250
 
245
251
  #### Serialization
246
252
 
247
- * **b/serialize/csv** [register]: Take an array of arrays and create a CSV.
253
+ * **b/serialize/csv** [byte_order_mark, register]: Take an array of arrays and create a CSV. You can optionally pre-pend a byte order mark, see Burner::Modeling::ByteOrderMark for acceptable options.
248
254
  * **b/serialize/json** [register]: Convert value to JSON.
249
255
  * **b/serialize/yaml** [register]: Convert value to YAML.
250
256
 
@@ -33,6 +33,7 @@ Gem::Specification.new do |s|
33
33
  s.add_dependency('hash_math', '~>1.2')
34
34
  s.add_dependency('objectable', '~>1.0')
35
35
  s.add_dependency('realize', '~>1.3')
36
+ s.add_dependency('rubyzip', '~>1.2')
36
37
  s.add_dependency('stringento', '~>2.1')
37
38
 
38
39
  s.add_development_dependency('guard-rspec', '~>4.7')
@@ -22,6 +22,7 @@ require 'singleton'
22
22
  require 'stringento'
23
23
  require 'time'
24
24
  require 'yaml'
25
+ require 'zip'
25
26
 
26
27
  # Common/Shared
27
28
  require_relative 'burner/disks'
@@ -34,6 +34,9 @@ module Burner
34
34
  register 'b/collection/unpivot', Library::Collection::Unpivot
35
35
  register 'b/collection/values', Library::Collection::Values
36
36
  register 'b/collection/validate', Library::Collection::Validate
37
+ register 'b/collection/zip', Library::Collection::Zip
38
+
39
+ register 'b/compress/row_reader', Library::Compress::RowReader
37
40
 
38
41
  register 'b/deserialize/csv', Library::Deserialize::Csv
39
42
  register 'b/deserialize/json', Library::Deserialize::Json
@@ -41,6 +44,7 @@ module Burner
41
44
 
42
45
  register 'b/io/exist', Library::IO::Exist
43
46
  register 'b/io/read', Library::IO::Read
47
+ register 'b/io/row_reader', Library::IO::RowReader
44
48
  register 'b/io/write', Library::IO::Write
45
49
 
46
50
  register 'b/serialize/csv', Library::Serialize::Csv
@@ -25,6 +25,9 @@ require_relative 'library/collection/transform'
25
25
  require_relative 'library/collection/unpivot'
26
26
  require_relative 'library/collection/validate'
27
27
  require_relative 'library/collection/values'
28
+ require_relative 'library/collection/zip'
29
+
30
+ require_relative 'library/compress/row_reader'
28
31
 
29
32
  require_relative 'library/deserialize/csv'
30
33
  require_relative 'library/deserialize/json'
@@ -32,6 +35,7 @@ require_relative 'library/deserialize/yaml'
32
35
 
33
36
  require_relative 'library/io/exist'
34
37
  require_relative 'library/io/read'
38
+ require_relative 'library/io/row_reader'
35
39
  require_relative 'library/io/write'
36
40
 
37
41
  require_relative 'library/serialize/csv'
@@ -0,0 +1,53 @@
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
+ # This job can take two arrays and coalesces them by index. For example:
14
+ #
15
+ # input:
16
+ # base_register: [ 'hello', 'bugs' ]
17
+ # with_register: [ 'world', 'bunny' ]
18
+ # output:
19
+ # register: [ ['hello', 'world'], ['bugs', 'bunny'] ]
20
+ #
21
+ # Expected Payload[base_register] input: array of objects.
22
+ # Expected Payload[with_register] input: array of objects.
23
+ # Payload[register] output: An array of two-dimensional arrays.
24
+ class Zip < JobWithRegister
25
+ attr_reader :base_register, :with_register
26
+
27
+ def initialize(
28
+ name:,
29
+ with_register:,
30
+ base_register: DEFAULT_REGISTER,
31
+ register: DEFAULT_REGISTER
32
+ )
33
+ super(name: name, register: register)
34
+
35
+ @base_register = base_register.to_s
36
+ @with_register = with_register.to_s
37
+
38
+ freeze
39
+ end
40
+
41
+ def perform(output, payload)
42
+ base_data = array(payload[base_register])
43
+ with_data = array(payload[with_register])
44
+
45
+ output.detail("Combining register: #{base_register} (#{base_data.length} record(s))")
46
+ output.detail("With register: #{with_register} (#{with_data.length} record(s))")
47
+
48
+ payload[register] = base_data.zip(with_data)
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,102 @@
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 Compress
13
+ # Iterates over an array of objects, extracts a path and data in each object, and
14
+ # creates a zip file. By default, if a path is blank then an ArgumentError will be raised.
15
+ # If this is undesirable then you can set ignore_blank_path to true and the record will be
16
+ # skipped. You also have the option to supress blank files being added by configuring
17
+ # ignore_blank_data as true.
18
+ #
19
+ # Expected Payload[register] input: array of objects.
20
+ # Payload[register] output: compressed binary zip file contents.
21
+ class RowReader < JobWithRegister
22
+ Content = Struct.new(:path, :data)
23
+
24
+ private_constant :Content
25
+
26
+ DEFAULT_DATA_KEY = 'data'
27
+ DEFAULT_PATH_KEY = 'path'
28
+
29
+ attr_reader :data_key,
30
+ :ignore_blank_data,
31
+ :ignore_blank_path,
32
+ :path_key,
33
+ :resolver
34
+
35
+ def initialize(
36
+ name:,
37
+ data_key: DEFAULT_DATA_KEY,
38
+ ignore_blank_data: false,
39
+ ignore_blank_path: false,
40
+ path_key: DEFAULT_PATH_KEY,
41
+ register: DEFAULT_REGISTER,
42
+ separator: ''
43
+ )
44
+ super(name: name, register: register)
45
+
46
+ @data_key = data_key.to_s
47
+ @ignore_blank_data = ignore_blank_data || false
48
+ @ignore_blank_path = ignore_blank_path || false
49
+ @path_key = path_key.to_s
50
+ @resolver = Objectable.resolver(separator: separator)
51
+
52
+ freeze
53
+ end
54
+
55
+ def perform(output, payload)
56
+ payload[register] = Zip::OutputStream.write_buffer do |zip|
57
+ array(payload[register]).each.with_index(1) do |record, index|
58
+ content = extract_path_and_data(record, index, output)
59
+
60
+ next unless content
61
+
62
+ zip.put_next_entry(content.path)
63
+ zip.write(content.data)
64
+ end
65
+ end.string
66
+ end
67
+
68
+ private
69
+
70
+ def extract_path_and_data(record, index, output)
71
+ path = strip_leading_separator(resolver.get(record, path_key))
72
+ data = resolver.get(record, data_key)
73
+
74
+ return if assert_and_skip_missing_path?(path, index, output)
75
+ return if skip_missing_data?(data, index, output)
76
+
77
+ Content.new(path, data)
78
+ end
79
+
80
+ def strip_leading_separator(path)
81
+ path.to_s.start_with?(File::SEPARATOR) ? path.to_s[1..-1] : path.to_s
82
+ end
83
+
84
+ def assert_and_skip_missing_path?(path, index, output)
85
+ if ignore_blank_path && path.to_s.empty?
86
+ output.detail("Skipping record #{index} because of blank path")
87
+ true
88
+ elsif path.to_s.empty?
89
+ raise ArgumentError, "Record #{index} is missing a path at key: #{path_key}"
90
+ end
91
+ end
92
+
93
+ def skip_missing_data?(data, index, output)
94
+ return false unless ignore_blank_data && data.to_s.empty?
95
+
96
+ output.detail("Skipping record #{index} because of blank data")
97
+ true
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,119 @@
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
+ require_relative 'open_file_base'
11
+
12
+ module Burner
13
+ module Library
14
+ module IO
15
+ # Iterates over an array of objects, extracts a filepath from a key in each object,
16
+ # and attempts to load the file's content for each record. The file's content will be
17
+ # stored at the specified data_key. By default missing paths or files will be
18
+ # treated as hard errors. If you wish to ignore these then pass in true for
19
+ # ignore_blank_path and/or ignore_file_not_found.
20
+ #
21
+ # Expected Payload[register] input: array of objects.
22
+ # Payload[register] output: array of objects.
23
+ class RowReader < JobWithRegister
24
+ class FileNotFoundError < StandardError; end
25
+
26
+ DEFAULT_DATA_KEY = 'data'
27
+ DEFAULT_PATH_KEY = 'path'
28
+
29
+ attr_reader :binary,
30
+ :data_key,
31
+ :disk,
32
+ :ignore_blank_path,
33
+ :ignore_file_not_found,
34
+ :path_key,
35
+ :resolver
36
+
37
+ def initialize(
38
+ name:,
39
+ binary: false,
40
+ data_key: DEFAULT_DATA_KEY,
41
+ disk: {},
42
+ ignore_blank_path: false,
43
+ ignore_file_not_found: false,
44
+ path_key: DEFAULT_PATH_KEY,
45
+ register: DEFAULT_REGISTER,
46
+ separator: ''
47
+ )
48
+ super(name: name, register: register)
49
+
50
+ @binary = binary || false
51
+ @data_key = data_key.to_s
52
+ @disk = Disks.make(disk)
53
+ @ignore_blank_path = ignore_blank_path || false
54
+ @ignore_file_not_found = ignore_file_not_found || false
55
+ @path_key = path_key.to_s
56
+ @resolver = Objectable.resolver(separator: separator)
57
+
58
+ freeze
59
+ end
60
+
61
+ def perform(output, payload)
62
+ records = array(payload[register])
63
+
64
+ output.detail("Reading path_key: #{path_key} for #{payload[register].length} records(s)")
65
+ output.detail("Storing read data in: #{path_key}")
66
+
67
+ payload[register] = records.map.with_index(1) do |object, index|
68
+ load_data(object, index, output)
69
+ end
70
+ end
71
+
72
+ private
73
+
74
+ def assert_and_skip_missing_path?(path, index, output)
75
+ missing_path = path.to_s.empty?
76
+ blank_path_raises_error = !ignore_blank_path
77
+
78
+ if missing_path && blank_path_raises_error
79
+ output.detail("Record #{index} is missing a path, raising error")
80
+
81
+ raise ArgumentError, "Record #{index} is missing a path"
82
+ elsif missing_path
83
+ output.detail("Record #{index} is missing a path")
84
+
85
+ true
86
+ end
87
+ end
88
+
89
+ def assert_and_skip_file_not_found?(path, index, output)
90
+ does_not_exist = !disk.exist?(path)
91
+ file_not_found_raises_error = !ignore_file_not_found
92
+
93
+ if file_not_found_raises_error && does_not_exist
94
+ output.detail("Record #{index} path: '#{path}' does not exist, raising error")
95
+
96
+ raise FileNotFoundError, "#{path} does not exist"
97
+ elsif does_not_exist
98
+ output.detail("Record #{index} path: '#{path}' does not exist, skipping")
99
+
100
+ true
101
+ end
102
+ end
103
+
104
+ def load_data(object, index, output)
105
+ path = resolver.get(object, path_key)
106
+
107
+ return object if assert_and_skip_missing_path?(path, index, output)
108
+ return object if assert_and_skip_file_not_found?(path, index, output)
109
+
110
+ data = disk.read(path, binary: binary)
111
+
112
+ resolver.set(object, data_key, data)
113
+
114
+ object
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
@@ -10,17 +10,30 @@
10
10
  module Burner
11
11
  module Library
12
12
  module Serialize
13
- # Take an array of arrays and create a CSV.
13
+ # Take an array of arrays and create a CSV. You can optionally pre-pend a byte order mark,
14
+ # see Burner::Modeling::ByteOrderMark for acceptable options.
14
15
  #
15
16
  # Expected Payload[register] input: array of arrays.
16
17
  # Payload[register] output: a serialized CSV string.
17
18
  class Csv < JobWithRegister
19
+ attr_reader :byte_order_mark
20
+
21
+ def initialize(name:, byte_order_mark: nil, register: DEFAULT_REGISTER)
22
+ super(name: name, register: register)
23
+
24
+ @byte_order_mark = Modeling::ByteOrderMark.resolve(byte_order_mark)
25
+
26
+ freeze
27
+ end
28
+
18
29
  def perform(_output, payload)
19
- payload[register] = CSV.generate(options) do |csv|
30
+ serialized_rows = CSV.generate(options) do |csv|
20
31
  array(payload[register]).each do |row|
21
32
  csv << row
22
33
  end
23
34
  end
35
+
36
+ payload[register] = "#{byte_order_mark}#{serialized_rows}"
24
37
  end
25
38
 
26
39
  private
@@ -9,6 +9,7 @@
9
9
 
10
10
  require_relative 'modeling/attribute'
11
11
  require_relative 'modeling/attribute_renderer'
12
+ require_relative 'modeling/byte_order_mark'
12
13
  require_relative 'modeling/key_index_mapping'
13
14
  require_relative 'modeling/key_mapping'
14
15
  require_relative 'modeling/validations'
@@ -0,0 +1,27 @@
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
+ # Define all acceptable byte order mark values.
13
+ module ByteOrderMark
14
+ UTF_8 = "\xEF\xBB\xBF"
15
+ UTF_16BE = "\xFE\xFF"
16
+ UTF_16LE = "\xFF\xFE"
17
+ UTF_32BE = "\x00\x00\xFE\xFF"
18
+ UTF_32LE = "\xFE\xFF\x00\x00"
19
+
20
+ class << self
21
+ def resolve(value)
22
+ value ? const_get(value.to_s.upcase.to_sym) : nil
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -8,5 +8,5 @@
8
8
  #
9
9
 
10
10
  module Burner
11
- VERSION = '1.3.0-alpha'
11
+ VERSION = '1.5.0'
12
12
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: burner
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.0.pre.alpha
4
+ version: 1.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matthew Ruggio
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-12-09 00:00:00.000000000 Z
11
+ date: 2020-12-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: acts_as_hashable
@@ -80,6 +80,20 @@ dependencies:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
82
  version: '1.3'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rubyzip
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '1.2'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '1.2'
83
97
  - !ruby/object:Gem::Dependency
84
98
  name: stringento
85
99
  requirement: !ruby/object:Gem::Requirement
@@ -238,6 +252,8 @@ files:
238
252
  - lib/burner/library/collection/unpivot.rb
239
253
  - lib/burner/library/collection/validate.rb
240
254
  - lib/burner/library/collection/values.rb
255
+ - lib/burner/library/collection/zip.rb
256
+ - lib/burner/library/compress/row_reader.rb
241
257
  - lib/burner/library/deserialize/csv.rb
242
258
  - lib/burner/library/deserialize/json.rb
243
259
  - lib/burner/library/deserialize/yaml.rb
@@ -245,6 +261,7 @@ files:
245
261
  - lib/burner/library/io/exist.rb
246
262
  - lib/burner/library/io/open_file_base.rb
247
263
  - lib/burner/library/io/read.rb
264
+ - lib/burner/library/io/row_reader.rb
248
265
  - lib/burner/library/io/write.rb
249
266
  - lib/burner/library/nothing.rb
250
267
  - lib/burner/library/serialize/csv.rb
@@ -256,6 +273,7 @@ files:
256
273
  - lib/burner/modeling.rb
257
274
  - lib/burner/modeling/attribute.rb
258
275
  - lib/burner/modeling/attribute_renderer.rb
276
+ - lib/burner/modeling/byte_order_mark.rb
259
277
  - lib/burner/modeling/key_index_mapping.rb
260
278
  - lib/burner/modeling/key_mapping.rb
261
279
  - lib/burner/modeling/validations.rb
@@ -292,9 +310,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
292
310
  version: '2.5'
293
311
  required_rubygems_version: !ruby/object:Gem::Requirement
294
312
  requirements:
295
- - - ">"
313
+ - - ">="
296
314
  - !ruby/object:Gem::Version
297
- version: 1.3.1
315
+ version: '0'
298
316
  requirements: []
299
317
  rubygems_version: 3.0.3
300
318
  signing_key: