remi 0.2.27 → 0.2.28
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 +3 -0
- data/Gemfile.lock +34 -5
- data/features/metadata.feature +17 -0
- data/features/step_definitions/remi_step.rb +6 -6
- data/features/transforms/date_diff.feature +1 -0
- data/jobs/aggregate_job.rb +0 -1
- data/jobs/all_jobs_shared.rb +0 -2
- data/jobs/copy_source_job.rb +0 -1
- data/jobs/csv_file_target_job.rb +0 -1
- data/jobs/metadata_job.rb +60 -0
- data/jobs/parameters_job.rb +1 -1
- data/jobs/sample_job.rb +19 -20
- data/jobs/sftp_file_target_job.rb +0 -1
- data/jobs/transforms/date_diff_job.rb +1 -1
- data/jobs/transforms/nvl_job.rb +1 -1
- data/jobs/transforms/parse_date_job.rb +7 -4
- data/jobs/transforms/prefix_job.rb +1 -1
- data/jobs/transforms/truncate_job.rb +1 -1
- data/lib/remi.rb +10 -15
- data/lib/remi/cucumber/business_rules.rb +23 -23
- data/lib/remi/cucumber/data_source.rb +2 -1
- data/lib/remi/data_frame.rb +36 -0
- data/lib/remi/data_frame/daru.rb +67 -0
- data/lib/remi/data_subject.rb +71 -10
- data/lib/remi/data_subject/csv_file.rb +151 -0
- data/lib/remi/data_subject/data_frame.rb +53 -0
- data/lib/remi/data_subject/postgres.rb +136 -0
- data/lib/remi/data_subject/salesforce.rb +136 -0
- data/lib/remi/data_subject/sftp_file.rb +66 -0
- data/lib/remi/fields.rb +8 -0
- data/lib/remi/source_to_target_map.rb +56 -32
- data/lib/remi/transform.rb +426 -83
- data/lib/remi/version.rb +1 -1
- data/remi.gemspec +2 -1
- data/spec/metadata_spec.rb +62 -0
- metadata +15 -28
- data/lib/remi/data_source.rb +0 -13
- data/lib/remi/data_source/csv_file.rb +0 -101
- data/lib/remi/data_source/data_frame.rb +0 -16
- data/lib/remi/data_source/postgres.rb +0 -58
- data/lib/remi/data_source/salesforce.rb +0 -87
- data/lib/remi/data_target.rb +0 -15
- data/lib/remi/data_target/csv_file.rb +0 -42
- data/lib/remi/data_target/data_frame.rb +0 -14
- data/lib/remi/data_target/postgres.rb +0 -74
- data/lib/remi/data_target/salesforce.rb +0 -54
- data/lib/remi/data_target/sftp_file.rb +0 -54
- data/lib/remi/refinements/daru.rb +0 -85
data/lib/remi/transform.rb
CHANGED
@@ -1,152 +1,495 @@
|
|
1
1
|
module Remi
|
2
|
-
|
3
|
-
extend self
|
2
|
+
class Transform
|
4
3
|
|
5
|
-
|
6
|
-
|
4
|
+
# Public: Initializes the static arguments of a transform.
|
5
|
+
#
|
6
|
+
# source_metadata - Metadata for the transform source.
|
7
|
+
# target_metadata - Metadata for the transform target.
|
8
|
+
def initialize(*args, source_metadata: {}, target_metadata: {}, **kargs, &block)
|
9
|
+
@source_metadata = source_metadata
|
10
|
+
@target_metadata = target_metadata
|
11
|
+
@multi_args = false
|
7
12
|
end
|
8
13
|
|
9
|
-
#
|
10
|
-
|
11
|
-
def memoize_as_lambda(func, *args, &block)
|
12
|
-
iv = instance_variable_get("@#{func}")
|
13
|
-
return iv[args] if iv
|
14
|
+
# Public: Accessor for source metadata
|
15
|
+
attr_accessor :source_metadata
|
14
16
|
|
15
|
-
|
16
|
-
|
17
|
+
# Public: Accessor for target metadata
|
18
|
+
attr_accessor :target_metadata
|
19
|
+
|
20
|
+
# Public: Set to true if the transform expects multiple arguments (default: false)
|
21
|
+
attr_reader :multi_arg
|
22
|
+
|
23
|
+
# Public: Defines the operation of this transform class.
|
24
|
+
#
|
25
|
+
# value - The value to be transformed
|
26
|
+
#
|
27
|
+
# Returns the transformed value.
|
28
|
+
def transform(value)
|
29
|
+
raise NoMethodError, "#{__method__} not defined for #{self.class.name}"
|
30
|
+
end
|
31
|
+
|
32
|
+
# Public: Allows one to call the proc defined by the transform so that
|
33
|
+
# Remi::Transform instances can be used interchangeably with normal lambdas.
|
34
|
+
#
|
35
|
+
# values - The values to be transformed.
|
36
|
+
#
|
37
|
+
# Returns the transformed value.
|
38
|
+
def call(*values)
|
39
|
+
if @multi_arg
|
40
|
+
to_proc.call(*values)
|
41
|
+
else
|
42
|
+
to_proc.call(Array(values).first)
|
17
43
|
end
|
18
|
-
instance_variable_set("@#{func}", hash_memo)[args]
|
19
44
|
end
|
20
45
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
46
|
+
# Public: Returns the transform as a lambda.
|
47
|
+
def to_proc
|
48
|
+
@to_proc ||= method(:transform).to_proc
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
|
53
|
+
|
54
|
+
|
55
|
+
|
56
|
+
# Public: Transform used to prefix string values in a vector.
|
57
|
+
#
|
58
|
+
# prefix - The string prefix.
|
59
|
+
# if_blank - String value to substitute if the value is blank (default: '').
|
60
|
+
#
|
61
|
+
# Examples:
|
62
|
+
#
|
63
|
+
# Prefix.new('CU').to_proc.call('123') # => "CU123"
|
64
|
+
class Prefix < Transform
|
65
|
+
def initialize(prefix, *args, if_blank: '', **kargs, &block)
|
66
|
+
super
|
67
|
+
@prefix = prefix
|
68
|
+
@if_blank = if_blank
|
69
|
+
end
|
70
|
+
|
71
|
+
def transform(value)
|
72
|
+
if value.blank?
|
73
|
+
@if_blank
|
25
74
|
else
|
26
|
-
"#{
|
75
|
+
"#{@prefix}#{value}"
|
27
76
|
end
|
28
77
|
end
|
29
78
|
end
|
30
79
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
80
|
+
|
81
|
+
# Public: Transform used to postfix values in a vector.
|
82
|
+
#
|
83
|
+
# postfix - The string postfix.
|
84
|
+
# if_blank - String value to substitute if the value is blank (default: '').
|
85
|
+
#
|
86
|
+
# Examples:
|
87
|
+
#
|
88
|
+
# Postfix.new('A').to_proc.call('123') # => "123A"
|
89
|
+
class Postfix < Transform
|
90
|
+
def initialize(postfix, *args, if_blank: '', **kargs, &block)
|
91
|
+
super
|
92
|
+
@postfix = postfix
|
93
|
+
@if_blank = if_blank
|
94
|
+
end
|
95
|
+
|
96
|
+
def transform(value)
|
97
|
+
if value.blank?
|
98
|
+
@if_blank
|
35
99
|
else
|
36
|
-
"#{
|
100
|
+
"#{value}#{@postfix}"
|
37
101
|
end
|
38
102
|
end
|
39
103
|
end
|
40
104
|
|
41
|
-
|
42
|
-
|
43
|
-
|
105
|
+
|
106
|
+
# Public: Transform used to truncate values in a vector.
|
107
|
+
#
|
108
|
+
# len - The maximum length of the string.
|
109
|
+
#
|
110
|
+
# Examples:
|
111
|
+
#
|
112
|
+
# Truncate.new(3).to_proc.call('1234') # => "123"
|
113
|
+
class Truncate < Transform
|
114
|
+
def initialize(len, *args, **kargs, &block)
|
115
|
+
super
|
116
|
+
@len = len
|
117
|
+
end
|
118
|
+
|
119
|
+
def transform(value)
|
120
|
+
value.slice(0,@len)
|
44
121
|
end
|
45
122
|
end
|
46
123
|
|
47
|
-
|
48
|
-
|
49
|
-
|
124
|
+
# Public: Transform used to concatenate a list of values, joined by a delimiter.
|
125
|
+
#
|
126
|
+
# delimiter - The delimiter used between values in the list (default: '').
|
127
|
+
#
|
128
|
+
# Examples:
|
129
|
+
#
|
130
|
+
# Concatenate.new('-').to_proc.call('a', 'b', 'c') # => "a-b-c"
|
131
|
+
class Concatenate < Transform
|
132
|
+
def initialize(delimiter='', *args, **kargs, &block)
|
133
|
+
super
|
134
|
+
@multi_args = true
|
135
|
+
@delimiter = delimiter
|
136
|
+
end
|
137
|
+
|
138
|
+
def transform(*values)
|
139
|
+
Array(values).join(@delimiter)
|
50
140
|
end
|
51
141
|
end
|
52
142
|
|
53
|
-
|
54
|
-
|
55
|
-
|
143
|
+
|
144
|
+
# Public: Transform used to do key-value lookup on hash-like objects
|
145
|
+
#
|
146
|
+
# lookup - The lookup object that takes keys and returns values.
|
147
|
+
# missing - What to use if a key is not found in the lookup (default: nil). If this
|
148
|
+
# is a proc, it is sent the key as an argument.
|
149
|
+
#
|
150
|
+
# Examples:
|
151
|
+
#
|
152
|
+
# my_lookup = { 1 => 'one', 2 => 'two }
|
153
|
+
# Lookup.new().to_proc.call(1) # => "1"
|
154
|
+
# Lookup.new().to_proc.call(3) # => nil
|
155
|
+
# Lookup.new().to_proc.call(3, missing: 'UNK') # => "UNK"
|
156
|
+
# Lookup.new().to_proc.call(3, missing: ->(v) { "I don't know #{v}" }) # => "I don't know 3"
|
157
|
+
class Lookup < Transform
|
158
|
+
def initialize(lookup, *args, missing: nil, **kargs, &block)
|
159
|
+
super
|
160
|
+
@lookup = lookup
|
161
|
+
@missing = missing
|
162
|
+
end
|
163
|
+
|
164
|
+
def transform(value)
|
165
|
+
result = @lookup[value]
|
56
166
|
|
57
167
|
if !result.nil?
|
58
168
|
result
|
59
|
-
elsif
|
60
|
-
|
169
|
+
elsif @missing.respond_to? :call
|
170
|
+
@missing.call(value)
|
61
171
|
else
|
62
|
-
|
172
|
+
@missing
|
63
173
|
end
|
64
174
|
end
|
65
175
|
end
|
66
176
|
|
67
|
-
|
68
|
-
|
69
|
-
|
177
|
+
# Public: (Next-Value-Lookup) transform used to find the first non-blank value in a list.
|
178
|
+
#
|
179
|
+
# default - What to use if all values are blank (default: '').
|
180
|
+
#
|
181
|
+
# Examples:
|
182
|
+
#
|
183
|
+
# Nvl.new.to_proc.call(nil,'','a','b') # => "a"
|
184
|
+
class Nvl < Transform
|
185
|
+
def initialize(default='', *args, **kargs, &block)
|
186
|
+
super
|
187
|
+
@multi_args = true
|
188
|
+
@default = default
|
189
|
+
end
|
190
|
+
|
191
|
+
def transform(*values)
|
192
|
+
Array(values).find(->() { @default }) { |arg| !arg.blank? }
|
70
193
|
end
|
71
194
|
end
|
72
195
|
|
73
|
-
|
74
|
-
|
75
|
-
|
196
|
+
# Public: Used to replace blank values.
|
197
|
+
#
|
198
|
+
# replace_with - Use this if the source value is blank (default: '').
|
199
|
+
#
|
200
|
+
# Examples:
|
201
|
+
#
|
202
|
+
# IfBlank.new('MISSING VALUE').to_proc.call('alpha') # => "alpha"
|
203
|
+
# IfBlank.new('MISSING VALUE').to_proc.call('') # => "MISSING VALUE"
|
204
|
+
class IfBlank < Transform
|
205
|
+
def initialize(replace_with='', *args, **kargs, &block)
|
206
|
+
super
|
207
|
+
@replace_with = replace_with
|
208
|
+
end
|
209
|
+
|
210
|
+
def transform(value)
|
211
|
+
value.blank? ? @replace_with : value
|
76
212
|
end
|
77
213
|
end
|
78
214
|
|
79
|
-
|
80
|
-
|
215
|
+
# Public: Parses a string and converts it to a date.
|
216
|
+
# This transform is metadata aware and will use :in_format metadata
|
217
|
+
# from the source
|
218
|
+
#
|
219
|
+
# in_format - The date format to use to convert the string (default: uses :in_format
|
220
|
+
# from the source metadata. If that is not defined, use '%Y-%m-%d').
|
221
|
+
# if_blank - Value to use if the the incoming value is blank (default: uses :if_blank
|
222
|
+
# from the source metadata. If that is not defined, use nil). If set to
|
223
|
+
# :high, then use the largest date, if set to :ow, use the lowest date.
|
224
|
+
#
|
225
|
+
# Examples:
|
226
|
+
#
|
227
|
+
# ParseDate.new(in_format: '%m/%d/%Y').to_proc.call('02/22/2013') # => Date.new(2013,2,22)
|
228
|
+
#
|
229
|
+
# tform = ParseDate.new
|
230
|
+
# tform.source_metadata = { in_format: '%m/%d/%Y' }
|
231
|
+
# tform.to_proc.call('02/22/2013') # => Date.new(2013,2,22)
|
232
|
+
class ParseDate < Transform
|
233
|
+
def initialize(*args, in_format: nil, if_blank: nil, **kargs, &block)
|
234
|
+
super
|
235
|
+
@in_format = in_format
|
236
|
+
@if_blank = if_blank
|
237
|
+
end
|
238
|
+
|
239
|
+
def in_format
|
240
|
+
@in_format ||= @source_metadata.fetch(:in_format, '%Y-%m-%d')
|
241
|
+
end
|
242
|
+
|
243
|
+
def if_blank
|
244
|
+
@if_blank ||= @source_metadata.fetch(:if_blank, nil)
|
245
|
+
end
|
246
|
+
|
247
|
+
def transform(value)
|
81
248
|
begin
|
82
|
-
if
|
83
|
-
|
84
|
-
elsif
|
85
|
-
|
249
|
+
if value.respond_to?(:strftime)
|
250
|
+
value
|
251
|
+
elsif value.blank? then
|
252
|
+
blank_handler(value)
|
86
253
|
else
|
87
|
-
|
254
|
+
string_to_date(value)
|
88
255
|
end
|
89
256
|
rescue ArgumentError => err
|
90
|
-
|
91
|
-
|
257
|
+
raise err, "Error parsing date (#{value.class}): '#{value}' with format #{in_format})"
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
def string_to_date(value)
|
262
|
+
Date.strptime(value, in_format)
|
263
|
+
end
|
264
|
+
|
265
|
+
def blank_handler(value)
|
266
|
+
if if_blank == :low
|
267
|
+
Date.new(1900,01,01)
|
268
|
+
elsif if_blank == :high
|
269
|
+
Date.new(2999,12,31)
|
270
|
+
elsif if_blank.respond_to? :call
|
271
|
+
if_blank.call(value)
|
272
|
+
else
|
273
|
+
if_blank
|
92
274
|
end
|
93
275
|
end
|
94
276
|
end
|
95
277
|
|
96
278
|
|
97
|
-
|
98
|
-
|
279
|
+
# Public: (Re)formats a date.
|
280
|
+
# This transform is metadata aware and will use :in_format/:out_format metadata
|
281
|
+
# from the source.
|
282
|
+
#
|
283
|
+
# in_format - The date format to used to parse the input value. If the input value
|
284
|
+
# is a date, then then parameter is ignored. (default: uses :in_format
|
285
|
+
# from the source metadata. If that is not defined, use '%Y-%m-%d')
|
286
|
+
# out_format - The date format applied to provide the resulting string. (default:
|
287
|
+
# uses :out_format from the source metadata. If that is not defined,
|
288
|
+
# use '%Y-%m-%d')
|
289
|
+
#
|
290
|
+
# Examples:
|
291
|
+
#
|
292
|
+
# FormatDate.new(in_format: '%m/%d/%Y', out_format: '%Y-%m-%d').to_proc.call('02/22/2013') # => "2013-02-22"
|
293
|
+
#
|
294
|
+
# tform = FormatDate.new
|
295
|
+
# tform.source_metadata = { in_format: '%m/%d/%Y', out_format: '%Y-%m-%d' }
|
296
|
+
# tform.to_proc.call('02/22/2013') # => "2013-02-22"
|
297
|
+
class FormatDate < Transform
|
298
|
+
def initialize(*args, in_format: nil, out_format: nil, **kargs, &block)
|
299
|
+
super
|
300
|
+
@in_format = in_format
|
301
|
+
@out_format = out_format
|
302
|
+
end
|
303
|
+
|
304
|
+
def in_format
|
305
|
+
@in_format ||= @source_metadata.fetch(:in_format, '%Y-%m-%d')
|
306
|
+
end
|
307
|
+
|
308
|
+
def out_format
|
309
|
+
@out_format ||= @source_metadata.fetch(:out_format, '%Y-%m-%d')
|
310
|
+
end
|
311
|
+
|
312
|
+
def transform(value)
|
99
313
|
begin
|
100
|
-
if
|
101
|
-
|
102
|
-
elsif
|
103
|
-
|
104
|
-
Date.new(1900,01,01)
|
105
|
-
elsif mif_blank == :high
|
106
|
-
Date.new(2999,12,31)
|
107
|
-
else
|
108
|
-
mif_blank
|
109
|
-
end
|
314
|
+
if value.blank? then
|
315
|
+
''
|
316
|
+
elsif value.respond_to? :strftime
|
317
|
+
value.strftime(out_format)
|
110
318
|
else
|
111
|
-
Date.strptime(
|
319
|
+
Date.strptime(value, in_format).strftime(out_format)
|
112
320
|
end
|
113
321
|
rescue ArgumentError => err
|
114
|
-
|
115
|
-
raise err
|
322
|
+
raise err, "Error parsing date (#{value.class}): '#{value}' using the format #{in_format} => #{out_format}"
|
116
323
|
end
|
117
324
|
end
|
118
325
|
end
|
119
326
|
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
327
|
+
# Public: Used to calculate differences between dates by a given measure.
|
328
|
+
#
|
329
|
+
# measure - One of :days, :months, or :years. (default: :days).
|
330
|
+
#
|
331
|
+
# Examples:
|
332
|
+
#
|
333
|
+
# DateDiff.new(:months).to_proc.call([Date.new(2016,1,30), Date.new(2016,3,1)]) # => 2
|
334
|
+
class DateDiff < Transform
|
335
|
+
def initialize(measure = :days, *args, **kargs, &block)
|
336
|
+
super
|
337
|
+
@multi_args = true
|
338
|
+
@measure = measure
|
339
|
+
end
|
340
|
+
|
341
|
+
def transform(from_date, to_date)
|
342
|
+
|
343
|
+
case @measure.to_sym
|
344
|
+
when :days
|
345
|
+
(to_date - from_date).to_i
|
346
|
+
when :months
|
347
|
+
(to_date.year * 12 + to_date.month) - (from_date.year * 12 + from_date.month)
|
348
|
+
when :years
|
349
|
+
to_date.year - from_date.year
|
128
350
|
else
|
129
|
-
raise "
|
351
|
+
raise ArgumentError, "Unknown date difference measure: #{@measure}"
|
130
352
|
end
|
131
353
|
end
|
132
354
|
end
|
133
355
|
|
134
|
-
|
135
|
-
|
136
|
-
|
356
|
+
# Public: Simply returns a constant.
|
357
|
+
#
|
358
|
+
# constant - The constant value to return.
|
359
|
+
#
|
360
|
+
# Examples:
|
361
|
+
#
|
362
|
+
# Constant.new('ewoks').to_proc.call('whatever') # => 'ewoks'
|
363
|
+
class Constant < Transform
|
364
|
+
def initialize(constant, *args, **kargs, &block)
|
365
|
+
super
|
366
|
+
@constant = constant
|
367
|
+
end
|
368
|
+
|
369
|
+
def transform(values)
|
370
|
+
@constant
|
137
371
|
end
|
138
372
|
end
|
139
373
|
|
140
|
-
|
141
|
-
|
142
|
-
|
374
|
+
# Public: Replaces one substring with another.
|
375
|
+
#
|
376
|
+
# to_replace - The string or regex to be replaced.
|
377
|
+
# repalce_with - The value to substitute.
|
378
|
+
#
|
379
|
+
# Examples:
|
380
|
+
#
|
381
|
+
# Replace.new(/\s/, '-').to_proc.call('hey jude') #=> 'hey-jude'
|
382
|
+
class Replace < Transform
|
383
|
+
def initialize(to_replace, replace_with, *args, **kargs, &block)
|
384
|
+
super
|
385
|
+
@to_replace = to_replace
|
386
|
+
@replace_with = replace_with
|
387
|
+
end
|
388
|
+
|
389
|
+
def transform(value)
|
390
|
+
value.gsub(@to_replace, @replace_with)
|
391
|
+
end
|
392
|
+
end
|
393
|
+
|
394
|
+
# Public: Checks to see if an email validates against a regex (imperfect)
|
395
|
+
# and will substitute it with some value if not.
|
396
|
+
#
|
397
|
+
# substitute - The value used to substitute for an invalid email. Can use a proc
|
398
|
+
# that accepts the value of the invalid email
|
399
|
+
#
|
400
|
+
# Examples:
|
401
|
+
#
|
402
|
+
# ValidateEmail.new('invalid@example.com').to_proc.call('uhave.email') #=> 'invalid@example.com'
|
403
|
+
# ValidateEmail.new(->(v) { "#{SecureRandom.uuid}@example.com" }).to_proc.call('uhave.email') #=> '3f158f29-bc75-44f0-91ed-22fbe5157297@example.com'
|
404
|
+
class ValidateEmail < Transform
|
405
|
+
def initialize(substitute='', *args, **kargs, &block)
|
406
|
+
super
|
407
|
+
@substitute = substitute
|
408
|
+
end
|
409
|
+
|
410
|
+
def transform(value)
|
411
|
+
value = value || ''
|
412
|
+
if value.match(/^[A-Z0-9._%+-]+@(?:[A-Z0-9-]+\.)+[A-Z]{2,}$/i)
|
413
|
+
value
|
414
|
+
elsif @substitute.respond_to? :call
|
415
|
+
@substitute.call value
|
416
|
+
else
|
417
|
+
@substitute
|
418
|
+
end
|
143
419
|
end
|
144
420
|
end
|
145
421
|
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
422
|
+
|
423
|
+
|
424
|
+
# Public: Enforces the type declared in the :type metadata field (if it exists)
|
425
|
+
#
|
426
|
+
# Examples:
|
427
|
+
#
|
428
|
+
# tform = EnforceType.new
|
429
|
+
# tform.source_metadata = { type: :date, in_format: '%m/%d/%Y' }
|
430
|
+
# tform.to_proc.call('02/22/2013') # => Date.new(2013,2,22)
|
431
|
+
#
|
432
|
+
# tform = EnforceType.new
|
433
|
+
# tform.source_metadata = { type: :integer }
|
434
|
+
# tform.to_proc.call('12') # => 12
|
435
|
+
#
|
436
|
+
# tform = EnforceType.new
|
437
|
+
# tform.source_metadata = { type: :integer }
|
438
|
+
# tform.to_proc.call('12A') # => ArgumentError: invalid value for Integer(): "12A"
|
439
|
+
class EnforceType < Transform
|
440
|
+
def initialize(*args, **kargs, &block)
|
441
|
+
super
|
442
|
+
end
|
443
|
+
|
444
|
+
def type
|
445
|
+
@type ||= @source_metadata.fetch(:type, :string)
|
446
|
+
end
|
447
|
+
|
448
|
+
def in_format
|
449
|
+
@in_format ||= @source_metadata.fetch(:in_format, '')
|
450
|
+
end
|
451
|
+
|
452
|
+
def scale
|
453
|
+
@scale ||= @source_metadata.fetch(:scale, 0)
|
454
|
+
end
|
455
|
+
|
456
|
+
def if_blank
|
457
|
+
return @if_blank if @if_blank_set
|
458
|
+
@if_blank_set = true
|
459
|
+
@if_blank = @source_metadata.fetch(:if_blank, nil)
|
460
|
+
end
|
461
|
+
|
462
|
+
def blank_handler(value)
|
463
|
+
return value unless value.blank?
|
464
|
+
|
465
|
+
if if_blank.respond_to? :to_proc
|
466
|
+
if_blank.to_proc.call(value)
|
467
|
+
else
|
468
|
+
if_blank
|
469
|
+
end
|
470
|
+
end
|
471
|
+
|
472
|
+
def transform(value)
|
473
|
+
if value.blank?
|
474
|
+
blank_handler(value)
|
475
|
+
else
|
476
|
+
case type
|
477
|
+
when :string
|
478
|
+
value
|
479
|
+
when :integer
|
480
|
+
Integer(value)
|
481
|
+
when :float
|
482
|
+
Float(value)
|
483
|
+
when :decimal
|
484
|
+
Float("%.#{scale}f" % Float(value))
|
485
|
+
when :date
|
486
|
+
Date.strptime(value, in_format)
|
487
|
+
when :datetime
|
488
|
+
Time.strptime(value, in_format)
|
489
|
+
else
|
490
|
+
raise ArgumentError, "Unknown type enforcement: #{type}"
|
491
|
+
end
|
492
|
+
end
|
150
493
|
end
|
151
494
|
end
|
152
495
|
|