remi 0.2.11 → 0.2.12
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/Gemfile.lock +8 -8
- data/features/step_definitions/remi_step.rb +21 -0
- data/features/support/env.rb +16 -0
- data/features/transforms/nvl.feature +38 -0
- data/jobs/sample_job.rb +3 -3
- data/jobs/transforms/nvl_job.rb +23 -0
- data/jobs/transforms/transform_jobs.rb +1 -0
- data/lib/remi.rb +1 -1
- data/lib/remi/cucumber/business_rules.rb +2 -1
- data/lib/remi/job.rb +0 -27
- data/lib/remi/transform.rb +3 -3
- data/lib/remi/version.rb +1 -1
- data/remi.gemspec +1 -0
- metadata +19 -3
- data/lib/remi/lookup/regex_sieve.rb +0 -55
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2ee4f5ef99f18b36083cf0fa0a31e3c5e9dbab17
|
4
|
+
data.tar.gz: f78cdac5e10cc84cde78e7b54ef1955a591eb115
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 35b23fcdf2de08e344a12b8326159bb6ddb92dd322f732c0f5e3f5693ef0ffdf1731b1293b652581a2353aeb6b2daa732b93684813b8096cdeb0d4eddc9abbbf
|
7
|
+
data.tar.gz: f1953467dbc6f9b91cd804014bfb9b427308b974508272891d399f09c3142408f9426ba2af5283c05751b8270a347a6a25570e19d337f575611dcf322cf855ed
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
remi (0.2.
|
4
|
+
remi (0.2.12)
|
5
5
|
activesupport (~> 4.2)
|
6
6
|
bond (~> 0.5)
|
7
7
|
cucumber (~> 2.1)
|
@@ -9,6 +9,7 @@ PATH
|
|
9
9
|
docile (~> 1.1)
|
10
10
|
net-sftp (~> 2.1)
|
11
11
|
pg (~> 0.18)
|
12
|
+
regex_sieve (~> 0.1)
|
12
13
|
regexp-examples (~> 1.1)
|
13
14
|
restforce (~> 2.1)
|
14
15
|
rspec (~> 3.3)
|
@@ -17,7 +18,7 @@ PATH
|
|
17
18
|
GEM
|
18
19
|
remote: https://rubygems.org/
|
19
20
|
specs:
|
20
|
-
activesupport (4.2.5)
|
21
|
+
activesupport (4.2.5.1)
|
21
22
|
i18n (~> 0.7)
|
22
23
|
json (~> 1.7, >= 1.7.7)
|
23
24
|
minitest (~> 5.1)
|
@@ -26,12 +27,11 @@ GEM
|
|
26
27
|
bond (0.5.1)
|
27
28
|
builder (3.2.2)
|
28
29
|
clbustos-rtf (0.4.2)
|
29
|
-
cucumber (2.3.
|
30
|
+
cucumber (2.3.2)
|
30
31
|
builder (>= 2.1.2)
|
31
32
|
cucumber-core (~> 1.4.0)
|
32
33
|
cucumber-wire (~> 0.0.1)
|
33
34
|
diff-lcs (>= 1.1.3)
|
34
|
-
event-bus (~> 0.1.0)
|
35
35
|
gherkin (~> 3.2.0)
|
36
36
|
multi_json (>= 1.7.5, < 2.0)
|
37
37
|
multi_test (>= 0.1.2)
|
@@ -43,7 +43,6 @@ GEM
|
|
43
43
|
spreadsheet (~> 1.0.3)
|
44
44
|
diff-lcs (1.2.5)
|
45
45
|
docile (1.1.5)
|
46
|
-
event-bus (0.1.0)
|
47
46
|
faraday (0.9.2)
|
48
47
|
multipart-post (>= 1.2, < 3)
|
49
48
|
faraday_middleware (0.10.0)
|
@@ -58,7 +57,7 @@ GEM
|
|
58
57
|
rbczmq (~> 1.7)
|
59
58
|
json (1.8.3)
|
60
59
|
mimemagic (0.3.1)
|
61
|
-
minitest (5.8.
|
60
|
+
minitest (5.8.4)
|
62
61
|
multi_json (1.11.2)
|
63
62
|
multi_test (0.1.2)
|
64
63
|
multipart-post (2.0.0)
|
@@ -76,7 +75,8 @@ GEM
|
|
76
75
|
prawn-svg (0.9.1.11)
|
77
76
|
prawn (>= 0.8.4)
|
78
77
|
rbczmq (1.7.9)
|
79
|
-
|
78
|
+
regex_sieve (0.1.0)
|
79
|
+
regexp-examples (1.2.0)
|
80
80
|
reportbuilder (1.4.2)
|
81
81
|
clbustos-rtf (~> 0.4.0)
|
82
82
|
prawn (~> 0.8.4)
|
@@ -91,7 +91,7 @@ GEM
|
|
91
91
|
rspec-core (~> 3.4.0)
|
92
92
|
rspec-expectations (~> 3.4.0)
|
93
93
|
rspec-mocks (~> 3.4.0)
|
94
|
-
rspec-core (3.4.
|
94
|
+
rspec-core (3.4.2)
|
95
95
|
rspec-support (~> 3.4.0)
|
96
96
|
rspec-expectations (3.4.0)
|
97
97
|
diff-lcs (>= 1.2.0, < 2.0)
|
@@ -283,6 +283,27 @@ Then /^the target field '(.+)' is populated with "([^"]*)" using the format "([^
|
|
283
283
|
expect(@brt.target.field.value).to eq target_reformatted
|
284
284
|
end
|
285
285
|
|
286
|
+
Then /^the target field '(.+)' is the first non-blank value from source fields '(.+)'$/ do |target_field_name, source_field_list|
|
287
|
+
source_field_names = "'#{source_field_list}'".split(',').map do |field_with_quotes|
|
288
|
+
field_with_quotes.match(/'(.+)'/)[1]
|
289
|
+
end
|
290
|
+
|
291
|
+
source_field_names.each do |name|
|
292
|
+
step "the source field '#{name}'"
|
293
|
+
end
|
294
|
+
step "the target field '#{target_field_name}'"
|
295
|
+
|
296
|
+
source_field_names.each do |source_field_name|
|
297
|
+
@brt.run_transforms
|
298
|
+
|
299
|
+
source_values = source_field_names.map { |name| @brt.source.fields[name].value }
|
300
|
+
source_values_nvl = source_values.find { |arg| !arg.blank? }
|
301
|
+
|
302
|
+
expect_cucumber { expect(@brt.target.fields[target_field_name].value).to eq source_values_nvl }
|
303
|
+
@brt.source.fields[source_field_name].value = ''
|
304
|
+
end
|
305
|
+
|
306
|
+
end
|
286
307
|
|
287
308
|
When /^in the source field, periods have been used in place of commas$/ do
|
288
309
|
@brt.source.field.value = @brt.source.field.value.gsub(/\./, ',')
|
data/features/support/env.rb
CHANGED
@@ -8,3 +8,19 @@ require 'remi/cucumber'
|
|
8
8
|
require_relative 'env_app.rb'
|
9
9
|
|
10
10
|
Remi::Settings.log_level = Logger::ERROR
|
11
|
+
|
12
|
+
Before '~@fails' do
|
13
|
+
def expect_cucumber(&block)
|
14
|
+
block.call
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
Before '@fails' do
|
19
|
+
def expect_cucumber(&block)
|
20
|
+
begin
|
21
|
+
block.call
|
22
|
+
rescue RSpec::Expectations::ExpectationNotMetError => err
|
23
|
+
puts "Expected error: #{err}"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
Feature: Test the NVL (Next Value Lookup) transformer.
|
2
|
+
|
3
|
+
Background:
|
4
|
+
Given the job is 'Nvl'
|
5
|
+
And the job source 'Source Data'
|
6
|
+
And the job target 'Target Data'
|
7
|
+
|
8
|
+
Scenario Outline: Performing an NVL
|
9
|
+
Given the source 'Source Data'
|
10
|
+
And the target 'Target Data'
|
11
|
+
|
12
|
+
And the source field 'Field1' is set to the value "<Field1>"
|
13
|
+
And the source field 'Field2' is set to the value "<Field2>"
|
14
|
+
And the source field 'Field3' is set to the value "<Field3>"
|
15
|
+
And the job parameter 'default' is "<Default>"
|
16
|
+
Then the target field 'Result Field' is set to the value "<Expected>"
|
17
|
+
|
18
|
+
Examples:
|
19
|
+
| Field1 | Field2 | Field3 | Default | Expected |
|
20
|
+
| A | B | C | | A |
|
21
|
+
| | B | C | | B |
|
22
|
+
| | | C | | C |
|
23
|
+
| | | | | |
|
24
|
+
| | | | UNK | UNK |
|
25
|
+
|
26
|
+
|
27
|
+
Scenario: Testing an NVL with the short form version
|
28
|
+
Given the source 'Source Data'
|
29
|
+
And the target 'Target Data'
|
30
|
+
|
31
|
+
Then the target field 'Result Field' is the first non-blank value from source fields 'Field1', 'Field2', 'Field3'
|
32
|
+
|
33
|
+
@fails
|
34
|
+
Scenario: Testing that the NVL with the short form version fails appropriately
|
35
|
+
Given the source 'Source Data'
|
36
|
+
And the target 'Target Data'
|
37
|
+
|
38
|
+
Then the target field 'Field2 Copy' is the first non-blank value from source fields 'Field1', 'Field2', 'Field3'
|
data/jobs/sample_job.rb
CHANGED
@@ -66,14 +66,14 @@ class SampleJob
|
|
66
66
|
operation: :create,
|
67
67
|
api: :bulk
|
68
68
|
|
69
|
-
|
69
|
+
define_param :program_name_lookup, RegexSieve.new({
|
70
70
|
/^BIO$/ => "Biology",
|
71
71
|
/^Fake Biology$/ => nil,
|
72
72
|
/(?:B|Microb)iology/ => "Biology",
|
73
73
|
/^CHEM$/ => "Chemistry",
|
74
74
|
/Chemistry/ => "Chemistry",
|
75
75
|
/Physics/ => "Physics"
|
76
|
-
}
|
76
|
+
})
|
77
77
|
|
78
78
|
define_transform :map_common_fields, sources: [:sample_file, :existing_contacts], targets: :all_contacts do
|
79
79
|
|
@@ -81,7 +81,7 @@ class SampleJob
|
|
81
81
|
all_contacts.df = sample_file.df.dup
|
82
82
|
Remi::SourceToTargetMap.apply(all_contacts.df) do
|
83
83
|
map source(:program) .target(:Major__c)
|
84
|
-
.transform(Remi::Transform[:lookup][program_name_lookup])
|
84
|
+
.transform(Remi::Transform[:lookup][params[:program_name_lookup]])
|
85
85
|
end
|
86
86
|
all_contacts.df = all_contacts.df.where(all_contacts.df[:Major__c].not_eq(nil))
|
87
87
|
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require_relative '../all_jobs_shared'
|
2
|
+
|
3
|
+
class NvlJob
|
4
|
+
include AllJobsShared
|
5
|
+
|
6
|
+
define_param :default, ''
|
7
|
+
define_source :source_data, Remi::DataSource::DataFrame,
|
8
|
+
fields: {
|
9
|
+
:field1 => {},
|
10
|
+
:field2 => {},
|
11
|
+
:field3 => {}
|
12
|
+
}
|
13
|
+
define_target :target_data, Remi::DataTarget::DataFrame
|
14
|
+
|
15
|
+
define_transform :main, sources: :source_data, targets: :target_data do
|
16
|
+
Remi::SourceToTargetMap.apply(source_data.df, target_data.df) do
|
17
|
+
map source(:field1, :field2, :field3) .target(:result_field)
|
18
|
+
.transform(Remi::Transform[:nvl].(params[:default]))
|
19
|
+
|
20
|
+
map source(:field2) .target(:field2_copy)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/remi.rb
CHANGED
@@ -10,6 +10,7 @@ require 'daru'
|
|
10
10
|
require 'docile'
|
11
11
|
require 'net/sftp'
|
12
12
|
require 'pg'
|
13
|
+
require 'regex_sieve'
|
13
14
|
|
14
15
|
# ActiveSupport extensions
|
15
16
|
require 'active_support'
|
@@ -56,5 +57,4 @@ require 'remi/data_target/data_frame'
|
|
56
57
|
require 'remi/data_target/salesforce'
|
57
58
|
require 'remi/data_target/csv_file'
|
58
59
|
|
59
|
-
require 'remi/lookup/regex_sieve'
|
60
60
|
require 'remi/transform'
|
@@ -15,6 +15,7 @@ module Remi::BusinessRules
|
|
15
15
|
'windows' => "\r\n",
|
16
16
|
'unix' => "\n",
|
17
17
|
'windows or unix' => :auto,
|
18
|
+
'null character' => 0.chr,
|
18
19
|
}
|
19
20
|
end
|
20
21
|
|
@@ -31,7 +32,7 @@ module Remi::BusinessRules
|
|
31
32
|
end
|
32
33
|
|
33
34
|
def formulas
|
34
|
-
@formulas ||=
|
35
|
+
@formulas ||= RegexSieve.new({
|
35
36
|
/(today|yesterday|tomorrow)/i => [:date_reference, :match_single_day],
|
36
37
|
/(this|last|previous|next) (day|month|year|week)/i => [:date_reference, :match_single_unit],
|
37
38
|
/(\d+)\s(day|days|month|months|year|years|week|weeks) (ago|from now)/i => [:date_reference, :match_multiple]
|
data/lib/remi/job.rb
CHANGED
@@ -2,7 +2,6 @@ module Remi
|
|
2
2
|
module Job
|
3
3
|
module JobClassMethods
|
4
4
|
attr_accessor :params
|
5
|
-
attr_accessor :lookups
|
6
5
|
attr_accessor :sources
|
7
6
|
attr_accessor :targets
|
8
7
|
attr_accessor :transforms
|
@@ -12,23 +11,6 @@ module Remi
|
|
12
11
|
@params[key] = value
|
13
12
|
end
|
14
13
|
|
15
|
-
def define_lookup(name, type_class, options)
|
16
|
-
@lookups ||= []
|
17
|
-
@lookups << name
|
18
|
-
|
19
|
-
define_method(name) do
|
20
|
-
iv_name = instance_variable_get("@#{name}")
|
21
|
-
return iv_name if iv_name
|
22
|
-
|
23
|
-
if type_class == Hash
|
24
|
-
lookup = options
|
25
|
-
else
|
26
|
-
lookup = type_class.new(options)
|
27
|
-
end
|
28
|
-
instance_variable_set("@#{name}", lookup)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
14
|
def define_source(name, type_class, **options)
|
33
15
|
@sources ||= []
|
34
16
|
@sources << name
|
@@ -69,10 +51,6 @@ module Remi
|
|
69
51
|
@params || {}
|
70
52
|
end
|
71
53
|
|
72
|
-
def lookups
|
73
|
-
@lookups || []
|
74
|
-
end
|
75
|
-
|
76
54
|
def sources
|
77
55
|
@sources || []
|
78
56
|
end
|
@@ -96,7 +74,6 @@ module Remi
|
|
96
74
|
def included(receiver)
|
97
75
|
receiver.extend(JobClassMethods)
|
98
76
|
receiver.params = self.params.merge(receiver.params)
|
99
|
-
receiver.lookups = self.lookups + receiver.lookups
|
100
77
|
receiver.sources = self.sources + receiver.sources
|
101
78
|
receiver.targets = self.targets + receiver.targets
|
102
79
|
receiver.transforms = self.transforms.merge(receiver.transforms)
|
@@ -112,10 +89,6 @@ module Remi
|
|
112
89
|
self.class.params
|
113
90
|
end
|
114
91
|
|
115
|
-
def lookups
|
116
|
-
self.class.lookups
|
117
|
-
end
|
118
|
-
|
119
92
|
def sources
|
120
93
|
self.class.sources
|
121
94
|
end
|
data/lib/remi/transform.rb
CHANGED
@@ -50,9 +50,9 @@ module Remi
|
|
50
50
|
end
|
51
51
|
end
|
52
52
|
|
53
|
-
def nvl
|
54
|
-
memoize_as_lambda(__method__) do
|
55
|
-
Array(largs).find(
|
53
|
+
def nvl(default='')
|
54
|
+
memoize_as_lambda(__method__, default) do |(mdefault), *largs|
|
55
|
+
Array(largs).find(->() { mdefault }) { |arg| !arg.blank? }
|
56
56
|
end
|
57
57
|
end
|
58
58
|
|
data/lib/remi/version.rb
CHANGED
data/remi.gemspec
CHANGED
@@ -19,6 +19,7 @@ Gem::Specification.new do |s|
|
|
19
19
|
s.add_runtime_dependency 'docile', ['~> 1.1']
|
20
20
|
s.add_runtime_dependency 'net-sftp', ['~> 2.1']
|
21
21
|
s.add_runtime_dependency 'pg', ['~> 0.18']
|
22
|
+
s.add_runtime_dependency 'regex_sieve', ['~> 0.1']
|
22
23
|
|
23
24
|
s.add_runtime_dependency "cucumber", ["~> 2.1"]
|
24
25
|
s.add_runtime_dependency "rspec", ["~> 3.3"]
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: remi
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.12
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sterling Paramore
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-02-
|
11
|
+
date: 2016-02-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: daru
|
@@ -80,6 +80,20 @@ dependencies:
|
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '0.18'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: regex_sieve
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0.1'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0.1'
|
83
97
|
- !ruby/object:Gem::Dependency
|
84
98
|
name: cucumber
|
85
99
|
requirement: !ruby/object:Gem::Requirement
|
@@ -204,6 +218,7 @@ files:
|
|
204
218
|
- features/support/env.rb
|
205
219
|
- features/support/env_app.rb
|
206
220
|
- features/transforms/date_diff.feature
|
221
|
+
- features/transforms/nvl.feature
|
207
222
|
- features/transforms/parse_date.feature
|
208
223
|
- features/transforms/prefix.feature
|
209
224
|
- jobs/aggregate_job.rb
|
@@ -211,6 +226,7 @@ files:
|
|
211
226
|
- jobs/copy_source_job.rb
|
212
227
|
- jobs/sample_job.rb
|
213
228
|
- jobs/transforms/date_diff_job.rb
|
229
|
+
- jobs/transforms/nvl_job.rb
|
214
230
|
- jobs/transforms/parse_date_job.rb
|
215
231
|
- jobs/transforms/prefix_job.rb
|
216
232
|
- jobs/transforms/transform_jobs.rb
|
@@ -232,7 +248,6 @@ files:
|
|
232
248
|
- lib/remi/extractor/sftp_file.rb
|
233
249
|
- lib/remi/field_symbolizers.rb
|
234
250
|
- lib/remi/job.rb
|
235
|
-
- lib/remi/lookup/regex_sieve.rb
|
236
251
|
- lib/remi/refinements/daru.rb
|
237
252
|
- lib/remi/refinements/symbolizer.rb
|
238
253
|
- lib/remi/settings.rb
|
@@ -276,5 +291,6 @@ test_files:
|
|
276
291
|
- features/support/env.rb
|
277
292
|
- features/support/env_app.rb
|
278
293
|
- features/transforms/date_diff.feature
|
294
|
+
- features/transforms/nvl.feature
|
279
295
|
- features/transforms/parse_date.feature
|
280
296
|
- features/transforms/prefix.feature
|
@@ -1,55 +0,0 @@
|
|
1
|
-
module Remi
|
2
|
-
module Lookup
|
3
|
-
|
4
|
-
# Public: RegexSieve class. The RegexSieve functions in a manner similar
|
5
|
-
# a hash. The regex sieve is initialized with a hash where the keys are
|
6
|
-
# regular expressions and the values can be any valid Ruby object. The order
|
7
|
-
# of the keys matters. When the regex sieve is accessed using the array
|
8
|
-
# accessor [], it returns the first matching record. By default, only
|
9
|
-
# the values are returned, but the key and all matching capture groups
|
10
|
-
# can optionally be returned.
|
11
|
-
#
|
12
|
-
# Examples:
|
13
|
-
#
|
14
|
-
# r = RegexSieve.new({
|
15
|
-
# /something/ => 'Something',
|
16
|
-
# /something else/ => 'This will never get matched because the one above will match first',
|
17
|
-
# /cool$/ => 'Cool',
|
18
|
-
# /cool beans/ => 'Really Cool'
|
19
|
-
# })
|
20
|
-
#
|
21
|
-
# r['something else'] # => 'Something'
|
22
|
-
# r['cool beans'] # => 'Really Cool'
|
23
|
-
class RegexSieve
|
24
|
-
def initialize(sieve)
|
25
|
-
@sieve = sieve
|
26
|
-
end
|
27
|
-
|
28
|
-
# Public: Array accessor for Regex Sieve.
|
29
|
-
#
|
30
|
-
# key - The string that will be matched to the keys in the sieve.
|
31
|
-
# opt - By default, only the values in the hash used to initialize the sieve
|
32
|
-
# will be returned. However, if you want to return the keys or the
|
33
|
-
# capture groups then use :regex, :match, or both, respectively.
|
34
|
-
#
|
35
|
-
# Example:
|
36
|
-
# r['something'] # => 'Something
|
37
|
-
# r['something', :regex] # => { value: 'Something', regex: /something/ }
|
38
|
-
# r['sometinng', :match, :regex] # => { value: 'Something', regex: /something/, match: #<MatchData "something"> }
|
39
|
-
def [](key, *opt)
|
40
|
-
opt = opt | [:value]
|
41
|
-
|
42
|
-
regex_match = nil
|
43
|
-
found = @sieve.find do |regex, v|
|
44
|
-
regex_match = regex.match(key)
|
45
|
-
end
|
46
|
-
|
47
|
-
return nil if found.nil?
|
48
|
-
full_result = { value: found[1], regex: found[0], match: regex_match }
|
49
|
-
|
50
|
-
full_result.select! { |k, v| opt.include?(k) }
|
51
|
-
full_result.size > 1 ? full_result : full_result.values.first
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|