burner 1.3.0 → 1.4.0.pre.alpha

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: be4b0f77b37a352fc98dc859e4593750670442f8b9353ef86836749e0b9dee8c
4
- data.tar.gz: b5cd388f6886fd3956a33db754fbad454e8d94a68d9920cbdb0f82f40bec9a04
3
+ metadata.gz: e4694264d232ea9b8c353973096b144283967676421524aa7aa2b9582f73469f
4
+ data.tar.gz: 1730498b8d7b18a1fee81a51b16817cb17e69f78bcd3091be7152b3139955806
5
5
  SHA512:
6
- metadata.gz: 942fc77175829d47a2b070d5dbdc7f7bb024e6f3be58254cd3b42ab5e70a64f688dd2d3fcd48aa7b193d349a1b497cdde0a7b2dfd95fd25078b56d59f52d94ad
7
- data.tar.gz: dd6e6e5ce6de426c2fbf0e4950adbd3c908f7ed49e2314d242e43538bf90768de5216db34fe940050c02231b2dac6115ded03a29ae415a2c5d591aeb771d49ab
6
+ metadata.gz: 1c473531a5422691fb36e14f5fbf7d7421fd19abe889d08e84e14265c146616f5761acbc3458c8cdd656ac784755dc96ec68370637234f260dbac16b0b8486b6
7
+ data.tar.gz: 88bce907acfcbb8fcec5c39d4be5ba8876fb630583ed55b268d96c3eca65c66412d15439f5abe4af8c29505099b54796dd4e9cdcb898a707d5e32a3e4dc0d838
@@ -1,3 +1,13 @@
1
+ # 1.4.0 (TBD)
2
+
3
+ Additions:
4
+
5
+ * byte_order_mark option for b/serialize/csv job
6
+
7
+ Added Jobs:
8
+
9
+ * b/compress/row_reader
10
+ * b/io/row_reader
1
11
  # 1.3.0 (December 11th, 2020)
2
12
 
3
13
  Additions:
data/README.md CHANGED
@@ -228,6 +228,10 @@ This library only ships with very basic, rudimentary jobs that are meant to just
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
230
 
231
+ #### Compression
232
+
233
+ * **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.
234
+
231
235
  #### De-serialization
232
236
 
233
237
  * **b/deserialize/csv** [register]: Take a CSV string and de-serialize into object(s). Currently it will return an array of arrays, with each nested array representing one row.
@@ -240,11 +244,12 @@ By default all jobs will use the `Burner::Disks::Local` disk for its persistence
240
244
 
241
245
  * **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
246
  * **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.
247
+ * **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
248
  * **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
249
 
245
250
  #### Serialization
246
251
 
247
- * **b/serialize/csv** [register]: Take an array of arrays and create a CSV.
252
+ * **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
253
  * **b/serialize/json** [register]: Convert value to JSON.
249
254
  * **b/serialize/yaml** [register]: Convert value to YAML.
250
255
 
@@ -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'
@@ -35,12 +35,15 @@ module Burner
35
35
  register 'b/collection/values', Library::Collection::Values
36
36
  register 'b/collection/validate', Library::Collection::Validate
37
37
 
38
+ register 'b/compress/row_reader', Library::Compress::RowReader
39
+
38
40
  register 'b/deserialize/csv', Library::Deserialize::Csv
39
41
  register 'b/deserialize/json', Library::Deserialize::Json
40
42
  register 'b/deserialize/yaml', Library::Deserialize::Yaml
41
43
 
42
44
  register 'b/io/exist', Library::IO::Exist
43
45
  register 'b/io/read', Library::IO::Read
46
+ register 'b/io/row_reader', Library::IO::RowReader
44
47
  register 'b/io/write', Library::IO::Write
45
48
 
46
49
  register 'b/serialize/csv', Library::Serialize::Csv
@@ -26,12 +26,15 @@ require_relative 'library/collection/unpivot'
26
26
  require_relative 'library/collection/validate'
27
27
  require_relative 'library/collection/values'
28
28
 
29
+ require_relative 'library/compress/row_reader'
30
+
29
31
  require_relative 'library/deserialize/csv'
30
32
  require_relative 'library/deserialize/json'
31
33
  require_relative 'library/deserialize/yaml'
32
34
 
33
35
  require_relative 'library/io/exist'
34
36
  require_relative 'library/io/read'
37
+ require_relative 'library/io/row_reader'
35
38
  require_relative 'library/io/write'
36
39
 
37
40
  require_relative 'library/serialize/csv'
@@ -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'
11
+ VERSION = '1.4.0-alpha'
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
4
+ version: 1.4.0.pre.alpha
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-12 00:00:00.000000000 Z
11
+ date: 2020-12-17 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,7 @@ 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/compress/row_reader.rb
241
256
  - lib/burner/library/deserialize/csv.rb
242
257
  - lib/burner/library/deserialize/json.rb
243
258
  - lib/burner/library/deserialize/yaml.rb
@@ -245,6 +260,7 @@ files:
245
260
  - lib/burner/library/io/exist.rb
246
261
  - lib/burner/library/io/open_file_base.rb
247
262
  - lib/burner/library/io/read.rb
263
+ - lib/burner/library/io/row_reader.rb
248
264
  - lib/burner/library/io/write.rb
249
265
  - lib/burner/library/nothing.rb
250
266
  - lib/burner/library/serialize/csv.rb
@@ -256,6 +272,7 @@ files:
256
272
  - lib/burner/modeling.rb
257
273
  - lib/burner/modeling/attribute.rb
258
274
  - lib/burner/modeling/attribute_renderer.rb
275
+ - lib/burner/modeling/byte_order_mark.rb
259
276
  - lib/burner/modeling/key_index_mapping.rb
260
277
  - lib/burner/modeling/key_mapping.rb
261
278
  - lib/burner/modeling/validations.rb
@@ -292,9 +309,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
292
309
  version: '2.5'
293
310
  required_rubygems_version: !ruby/object:Gem::Requirement
294
311
  requirements:
295
- - - ">="
312
+ - - ">"
296
313
  - !ruby/object:Gem::Version
297
- version: '0'
314
+ version: 1.3.1
298
315
  requirements: []
299
316
  rubygems_version: 3.0.3
300
317
  signing_key: