remi 0.2.11 → 0.2.12
Sign up to get free protection for your applications and to get access to all the features.
- 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
|