realize 1.0.0.pre.alpha → 1.1.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: 77bcd7b4fedcf6a51318375889b4ab11c4222015ddc7da3ada67315d5e6817dc
4
- data.tar.gz: eb9d90c4d49a0ab19da44543b99e355c557012c1155740efb9613ac5e095e5b5
3
+ metadata.gz: 0f1004b6b97c8ebd485bac4fb9859ffd28511240f4a9943c73875664be35722c
4
+ data.tar.gz: 979d68bb86ecc8ba32251c2346a0183158686bf04fb9949f04646e7e16a6a03b
5
5
  SHA512:
6
- metadata.gz: 48875ff1d61c486f648955f2e62b9d1527dd40b2be7454a531dce21df3186017f06d2275d80587653867dc10a249666effb2e11f85ad10b5d31ca5e51a27d4d1
7
- data.tar.gz: e3cb298a91c99011883d3763b01585548679388cc2b3c93269571d2c96e7eca002727035cd156a6cc8799d854930de2aa637cec6e005385ef810cd11aa976572
6
+ metadata.gz: f2398a44d581a5e8685ef9c29acbb140fbc2488cb7ffc6411317254325154e30b72b3828b96df5aa7e17f299a79c802f4ece1a50486c0ec62d20bdcb9bd5f073
7
+ data.tar.gz: 9b87926382b38afda00be7ab8055ab26eea572228489f0d0efd341dd0b709d11d1becafa3e925d79baa5c30bccd0fab30130c8792e646891c902fc6cb7a23aa9
@@ -1,3 +1,7 @@
1
- # 1.0.0 (June 8th, 2020)
1
+ # 1.1.0 (June 24th, 2020)
2
+
3
+ Addition of r/collection/at_index, r/collection/first, and r/collection/last
4
+
5
+ # 1.0.0 (June 9th, 2020)
2
6
 
3
7
  Initial Release, includes 15 initial transformers.
data/README.md CHANGED
@@ -2,7 +2,11 @@
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/realize.svg)](https://badge.fury.io/rb/realize) [![Build Status](https://travis-ci.org/bluemarblepayroll/realize.svg?branch=master)](https://travis-ci.org/bluemarblepayroll/realize) [![Maintainability](https://api.codeclimate.com/v1/badges/115f0c5a1d0a4cce7230/maintainability)](https://codeclimate.com/github/bluemarblepayroll/realize/maintainability) [![Test Coverage](https://api.codeclimate.com/v1/badges/115f0c5a1d0a4cce7230/test_coverage)](https://codeclimate.com/github/bluemarblepayroll/realize/test_coverage) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
4
4
 
5
- TODO
5
+ This library provides a pluggable and configurable data transformation framework. The general use-case is:
6
+
7
+ > We need to be able to configure the data transformation pipeline, within an application, for a system-to-system integration.
8
+
9
+ It is currently used in production at Blue Marble to power the transformation pipeline within a larger ETL framework.
6
10
 
7
11
  ## Installation
8
12
 
@@ -20,7 +24,90 @@ bundle add realize
20
24
 
21
25
  ## Examples
22
26
 
23
- TODO
27
+ ### Basic Transformer Example
28
+
29
+ Here is a simple record we will use for data derivation and transformation:
30
+
31
+ ````ruby
32
+ record = {
33
+ id: 1,
34
+ created_at: '2020-03-04T12:34:56Z',
35
+ first: 'Frank',
36
+ last: 'Rizzo'
37
+ }
38
+ ````
39
+
40
+ Let's say we wanted to retrieve the created_at formatted as: 'MM/DD/YY', we could write:
41
+
42
+ ````ruby
43
+ transformers = [
44
+ {
45
+ type: 'r/value/resolve',
46
+ key: :created_at
47
+ },
48
+ {
49
+ type: 'r/format/date',
50
+ output_format: '%D'
51
+ }
52
+ ]
53
+
54
+ value = Realize.pipeline(transformers).transform(record) # 03/04/20
55
+ ````
56
+
57
+ Notice how all built-in transformers are prefixed with 'r'. This should help isolate the built-in transformers from potential externally registered transformers.
58
+
59
+ ### Transformer Gallery
60
+
61
+ Here is a list of each built-in transformer, their options, and what their function is:
62
+
63
+ #### Collection-oriented Transformers
64
+
65
+ * **r/collection/at_index** [index]: Takes an array (or coerces value to an array) and returns the value at the given index position.
66
+ * **r/collection/first** []: Takes an array (or coerces value to an array) and returns the value at the first index position.
67
+ * **r/collection/last** []: Takes an array (or coerces value to an array) and returns the value at the last index position.
68
+ * **r/collection/sort** [key, direction]: Takes an array (or coerces value to an array) and sort it either ascending or descending by some defined key's value.
69
+
70
+ #### Filtering Transformers
71
+
72
+ * **r/filter/by_key_record_value** [key, value]: Takes an array (or coerces value to an array) and selects only the records that match the key's value. In this case the value is derived off of the main record.
73
+ * **r/filter/by_key_value_presence** [key]: Takes an array (or coerces value to an array) and selects only the records where the key's value is present (not nil and not empty).
74
+ * **r/filter/by_key_record_value** [key, value]: Takes an array (or coerces value to an array) and selects only the records that match the key's value. In this case, the value is statically defined.
75
+ * **r/filter/inactive** [key, value]: Takes an array (or coerces value to an array) and selects only the records where the current time is between the start and end times as derived from the record. Note that *current time* can be passed in but defaults to current UTC time.
76
+
77
+ #### Format-oriented Transformers
78
+
79
+ * **r/format/date** [input_format, output_format]: Parses the incoming value into a Time object using the configured input_format and outputs it as formatted by the configured output_format.
80
+ * **r/format/remove_whitespace** []: Removes all whitespace from the incoming value.
81
+ * **r/format/string_replace** [original, replacement]: Replaces all occurrences of the configured original value with the replacement value.
82
+
83
+ #### Logical Transformers
84
+
85
+ * **r/logical/switch** [cases, default_transformers, key]: Provides a value-based logic branching. If a value matches a specific case, the specific cases transformers will be executed. If it does not match any case then the default_transformers will be executed.
86
+
87
+ #### Value-oriented Transformers
88
+
89
+ * **r/value/blank** []: Always return a blank string.
90
+ * **r/value/map** [values]: Do a lookup on the value using the `values` hash. If a value is found, then its corresponding value is returned. If it isn't then the input is returned.
91
+ * **r/value/null** []: Always returns null.
92
+ * **r/value/resolve** [key]: Dynamically resolves a value based on the record's key. It uses the [Objectable](https://github.com/bluemarblepayroll/objectable) library by default, which provides some goodies like dot-notation for nested objects and type-insensitivity (works for Ruby objects, hashes, structs, etc.)
93
+ * **r/value/static** [value]: Always returns a hard-coded value.
94
+ * **r/value/verbatim** []: Default transformer, simply echos back the input.
95
+
96
+ ### Plugging in Transformers
97
+
98
+ Custom transformers can be externally created and registered as long as it complies with the general transformer interface:
99
+
100
+ * constructor accepts keyword arguments from which the configuration provides
101
+ * responds to a method called transform with the signature: transform(resolver, value, time, record)
102
+
103
+ After you have implemented the custom transformer, you can externally register is by:
104
+
105
+ ````ruby
106
+ Realize::Transformers.register('some_custom_transformer', SomeCustomTransformer)
107
+ ````
108
+
109
+ You should now be able to use the type: 'some_custom_transformer' within your transformation configuration.
110
+
24
111
 
25
112
  ## Contributing
26
113
 
@@ -18,11 +18,8 @@ require_relative 'realize/pipeline'
18
18
  # Top-level syntactic sugar.
19
19
  module Realize
20
20
  class << self
21
- def pipeline(resolver: Objectable.resolver, transformers: [])
22
- Pipeline.new(
23
- resolver: resolver,
24
- transformers: transformers
25
- )
21
+ def pipeline(transformers = [], resolver: Objectable.resolver)
22
+ Pipeline.new(transformers, resolver: resolver)
26
23
  end
27
24
  end
28
25
  end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Realize
4
+ class Collection
5
+ # Transformer to get an item of a collection
6
+ class AtIndex
7
+ acts_as_hashable
8
+
9
+ attr_reader :index
10
+
11
+ def initialize(index:)
12
+ raise ArgumentError, 'index is required' if index.to_s.empty?
13
+
14
+ @index = index.to_i
15
+
16
+ freeze
17
+ end
18
+
19
+ def transform(_resolver, value, _time, _record)
20
+ value.is_a?(Array) ? value[index] : value
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Realize
4
+ class Collection
5
+ # Transformer to get the first item of a collection
6
+ class First
7
+ acts_as_hashable
8
+
9
+ def transform(resolver, value, time, record)
10
+ AtIndex.new(index: 0).transform(resolver, value, time, record)
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Realize
4
+ class Collection
5
+ # Transformer to get the last item of a collection
6
+ class Last
7
+ acts_as_hashable
8
+
9
+ def transform(resolver, value, time, record)
10
+ AtIndex.new(index: -1).transform(resolver, value, time, record)
11
+ end
12
+ end
13
+ end
14
+ end
@@ -1,16 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'sort/direction'
4
+
3
5
  module Realize
4
6
  class Collection
5
- # Transformer to take an array of oibjects and sort by the given key
7
+ # Transformer to take an array of objects and sort by the given key
6
8
  # and by the given sort direction. Defaulting to ascending.
7
9
  class Sort
8
- module Direction
9
- ASCENDING = 'ASC'
10
- ASC = 'ASC'
11
- DESCENDING = 'DESC'
12
- DESC = 'DESC'
13
- end
14
10
  include Arrays
15
11
  include Direction
16
12
  acts_as_hashable
@@ -28,10 +24,10 @@ module Realize
28
24
  freeze
29
25
  end
30
26
 
31
- def transform(_resolver, value, _time, _record)
27
+ def transform(resolver, value, _time, _record)
32
28
  records = array(value)
33
29
 
34
- sorted = records.sort_by { |hsh| hsh[key.to_sym] }
30
+ sorted = records.sort_by { |hsh| resolver.get(hsh, key) }
35
31
 
36
32
  order == DESCENDING ? sorted.reverse : sorted
37
33
  end
@@ -1,35 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'sort/direction'
4
-
5
3
  module Realize
6
4
  class Collection
7
- # Transformer to take an array of oibjects and sort by the given key
8
- # and by the given sort direction. Defaulting to ascending.
9
5
  class Sort
10
- include Arrays
11
- include Direction
12
- acts_as_hashable
13
-
14
- DEFAULT_ORDER = ASCENDING
15
-
16
- attr_reader :key, :order
17
-
18
- def initialize(key:, order: DEFAULT_ORDER)
19
- raise ArgumentError, 'key is required' if key.to_s.empty?
20
-
21
- @key = key
22
- @order = Direction.const_get(order.to_s.upcase.to_sym)
23
-
24
- freeze
25
- end
26
-
27
- def transform(_resolver, value, _time, _record)
28
- records = array(value)
29
-
30
- sorted = records.sort_by { |hsh| hsh[key.to_sym] }
31
-
32
- order == DESCENDING ? sorted.reverse : sorted
6
+ # Constants describing the possible values of 'direction' for a Sort instance.
7
+ module Direction
8
+ # providing a few different ways to specify.
9
+ ASCENDING = ASC = 'ASC'
10
+ DESCENDING = DESC = 'DESC'
33
11
  end
34
12
  end
35
13
  end
@@ -7,8 +7,6 @@ module Realize
7
7
  class RemoveWhitespace
8
8
  acts_as_hashable
9
9
 
10
- def initialize(_opts = {}); end
11
-
12
10
  def transform(_resolver, value, _time, _record)
13
11
  value.to_s.gsub(/\s+/, ' ')
14
12
  end
@@ -6,7 +6,7 @@ module Realize
6
6
  class Pipeline
7
7
  attr_reader :resolver, :transformers
8
8
 
9
- def initialize(resolver: Objectable.resolver, transformers: [])
9
+ def initialize(transformers = [], resolver: Objectable.resolver)
10
10
  raise ArgumentError, 'resolver is required' unless resolver
11
11
 
12
12
  @resolver = resolver
@@ -15,7 +15,7 @@ module Realize
15
15
  freeze
16
16
  end
17
17
 
18
- def transform(record, time)
18
+ def transform(record, time = Time.now.utc)
19
19
  transformers.inject(record) do |memo, transformer|
20
20
  transformer.transform(resolver, memo, time, record)
21
21
  end
@@ -1,5 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'collection/at_index'
4
+ require_relative 'collection/first'
5
+ require_relative 'collection/last'
3
6
  require_relative 'collection/sort'
4
7
  require_relative 'filter/by_key_record_value'
5
8
  require_relative 'filter/by_key_value'
@@ -25,6 +28,9 @@ module Realize
25
28
  acts_as_hashable_factory
26
29
 
27
30
  register '', Value::Verbatim
31
+ register 'r/collection/at_index', Collection::AtIndex
32
+ register 'r/collection/first', Collection::First
33
+ register 'r/collection/last', Collection::Last
28
34
  register 'r/collection/sort', Collection::Sort
29
35
  register 'r/filter/by_key_record_value', Filter::ByKeyRecordValue
30
36
  register 'r/filter/by_key_value', Filter::ByKeyValue
@@ -6,8 +6,6 @@ module Realize
6
6
  class Blank
7
7
  acts_as_hashable
8
8
 
9
- def initialize(_opts = {}); end
10
-
11
9
  def transform(_resolver, _value, _time, _record)
12
10
  ''
13
11
  end
@@ -6,8 +6,6 @@ module Realize
6
6
  class Null
7
7
  acts_as_hashable
8
8
 
9
- def initialize(_opts = {}); end
10
-
11
9
  def transform(_resolver, _value, _time, _record)
12
10
  nil
13
11
  end
@@ -6,11 +6,6 @@ module Realize
6
6
  class Verbatim
7
7
  acts_as_hashable
8
8
 
9
- # This is here to satisfy an underlying issue in acts_as_hashable.
10
- # The #make calls in the factory and hashable module should be calling #new with no
11
- # args if no keys are detected.
12
- def initialize(_opts = {}); end
13
-
14
9
  def transform(_resolver, value, _time, _record)
15
10
  value
16
11
  end
@@ -8,5 +8,5 @@
8
8
  #
9
9
 
10
10
  module Realize
11
- VERSION = '1.0.0-alpha'
11
+ VERSION = '1.1.0'
12
12
  end
@@ -11,8 +11,8 @@ Gem::Specification.new do |s|
11
11
  Derive and transform a value using a configuration-first pipeline.
12
12
  DESCRIPTION
13
13
 
14
- s.authors = ['Matthew Ruggio']
15
- s.email = ['mruggio@bluemarblepayroll.com']
14
+ s.authors = ['Matthew Ruggio', 'Dan Dewar']
15
+ s.email = ['mruggio@bluemarblepayroll.com', 'ddewar@bluemarblepayroll.com']
16
16
  s.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
17
  s.bindir = 'exe'
18
18
  s.executables = []
@@ -28,7 +28,7 @@ Gem::Specification.new do |s|
28
28
 
29
29
  s.required_ruby_version = '>= 2.5'
30
30
 
31
- s.add_dependency('acts_as_hashable', '~>1')
31
+ s.add_dependency('acts_as_hashable', '~>1', '>=1.2.0')
32
32
  s.add_dependency('objectable', '~>1')
33
33
 
34
34
  s.add_development_dependency('guard-rspec', '~>4.7')
metadata CHANGED
@@ -1,14 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: realize
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0.pre.alpha
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matthew Ruggio
8
+ - Dan Dewar
8
9
  autorequire:
9
10
  bindir: exe
10
11
  cert_chain: []
11
- date: 2020-06-08 00:00:00.000000000 Z
12
+ date: 2020-06-24 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
15
  name: acts_as_hashable
@@ -17,6 +18,9 @@ dependencies:
17
18
  - - "~>"
18
19
  - !ruby/object:Gem::Version
19
20
  version: '1'
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.2.0
20
24
  type: :runtime
21
25
  prerelease: false
22
26
  version_requirements: !ruby/object:Gem::Requirement
@@ -24,6 +28,9 @@ dependencies:
24
28
  - - "~>"
25
29
  - !ruby/object:Gem::Version
26
30
  version: '1'
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 1.2.0
27
34
  - !ruby/object:Gem::Dependency
28
35
  name: objectable
29
36
  requirement: !ruby/object:Gem::Requirement
@@ -139,6 +146,7 @@ dependencies:
139
146
  description: " Derive and transform a value using a configuration-first pipeline.\n"
140
147
  email:
141
148
  - mruggio@bluemarblepayroll.com
149
+ - ddewar@bluemarblepayroll.com
142
150
  executables: []
143
151
  extensions: []
144
152
  extra_rdoc_files: []
@@ -159,6 +167,9 @@ files:
159
167
  - exe/.gitkeep
160
168
  - lib/realize.rb
161
169
  - lib/realize/arrays.rb
170
+ - lib/realize/collection/at_index.rb
171
+ - lib/realize/collection/first.rb
172
+ - lib/realize/collection/last.rb
162
173
  - lib/realize/collection/sort.rb
163
174
  - lib/realize/collection/sort/direction.rb
164
175
  - lib/realize/filter/by_key_record_value.rb
@@ -200,9 +211,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
200
211
  version: '2.5'
201
212
  required_rubygems_version: !ruby/object:Gem::Requirement
202
213
  requirements:
203
- - - ">"
214
+ - - ">="
204
215
  - !ruby/object:Gem::Version
205
- version: 1.3.1
216
+ version: '0'
206
217
  requirements: []
207
218
  rubygems_version: 3.0.3
208
219
  signing_key: