retl 0.0.4 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +11 -0
- data/README.md +70 -3
- data/lib/retl.rb +11 -0
- data/lib/retl/configuration.rb +7 -0
- data/lib/retl/errors/step_execution_error.rb +17 -0
- data/lib/retl/handlers/explode_handler.rb +1 -1
- data/lib/retl/handlers/filter_handler.rb +1 -1
- data/lib/retl/handlers/fork_handler.rb +1 -1
- data/lib/retl/handlers/handler.rb +9 -2
- data/lib/retl/handlers/inspect_handler.rb +1 -1
- data/lib/retl/handlers/path_handler.rb +1 -1
- data/lib/retl/handlers/step_handler.rb +1 -1
- data/lib/retl/handlers/transform_handler.rb +4 -3
- data/lib/retl/next_description.rb +22 -0
- data/lib/retl/path.rb +12 -3
- data/lib/retl/path_builder.rb +11 -1
- data/lib/retl/transformation.rb +18 -4
- data/lib/retl/version.rb +1 -1
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5fb3e97322814aed123d196693eacd2eb5560719
|
4
|
+
data.tar.gz: 778407acae9f684ddaa8b746d0c8dd751ed2e9be
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8b0182706dbf3a4350d53cb967b576075a9fbf85dd1dd1efb778bc0bd4d648b15bddea9904ece0a973bf15896d60ed38ae4833f09b1c5ffc986f48650415fa8d
|
7
|
+
data.tar.gz: 29a71295ac8f7025fde450c1f4f079e100fe082c2891d20fe34950f28f588c12affe2a0e1e8bcc50a3bd43ca20e80ad6ea97f9839fccf8535514b6c43a93fe84
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,16 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
+
### 0.0.5
|
4
|
+
|
5
|
+
- New Feature: Step descriptions allow steps to be described while being
|
6
|
+
built. This will allow the description of the step to appear in the
|
7
|
+
error message if an error occurs.
|
8
|
+
- Bug Fix: transform was mutating source data. Instead we just want it to
|
9
|
+
mutate a duplicate and pass that along.
|
10
|
+
- New Feature: adds a configuration block to alter the behavior of rETL
|
11
|
+
- New Feature: error handling. Errors can simple be raised (for development)
|
12
|
+
or be captured so they can be evaluated later (for production)
|
13
|
+
|
3
14
|
### 0.0.4
|
4
15
|
|
5
16
|
- Bug Fix: fixes memoized fork results
|
data/README.md
CHANGED
@@ -232,8 +232,6 @@ end
|
|
232
232
|
result = my_path.transform([6])
|
233
233
|
result.to_a
|
234
234
|
#=> [3, 9, 15]
|
235
|
-
|
236
|
-
expect(result.to_a).to eq([3, 9, 15])
|
237
235
|
```
|
238
236
|
|
239
237
|
#### Path Reuse
|
@@ -355,6 +353,76 @@ result.to_a
|
|
355
353
|
#=> [{years_since_birth: 7, age_classification: "child"}]
|
356
354
|
```
|
357
355
|
|
356
|
+
### Error Handling
|
357
|
+
|
358
|
+
Sometimes errors happen while a step is being performed. By default, errors are
|
359
|
+
wrapped in a `StepExecutionError` and the transformation will abort (this
|
360
|
+
can be changed with configuration, however). The `StepExecutionError` will
|
361
|
+
provide context to aid in debugging the error.
|
362
|
+
|
363
|
+
Pay attention to the `desc` step in the path below. `desc` simply describes the
|
364
|
+
next step. Not only does this provide good documentation, but the description
|
365
|
+
will also be available in the `StepExecutionError` to make debugging easier.
|
366
|
+
|
367
|
+
```
|
368
|
+
my_path = Retl::Path.new do
|
369
|
+
desc "calculates the number 5"
|
370
|
+
calc(:five) { 5 }
|
371
|
+
|
372
|
+
desc "Check for sane ages"
|
373
|
+
transform do |row|
|
374
|
+
raise StandardError, "bad age" if row[:age] > 100
|
375
|
+
end
|
376
|
+
end
|
377
|
+
|
378
|
+
data = [
|
379
|
+
{age: 33},
|
380
|
+
{age: 3},
|
381
|
+
{age: 1000}
|
382
|
+
]
|
383
|
+
|
384
|
+
begin
|
385
|
+
result = my_path.transform(data).to_a
|
386
|
+
rescue Retl::StepExecutionError => e
|
387
|
+
e.message
|
388
|
+
#=> "bad age (at step: Check for sane ages))"
|
389
|
+
|
390
|
+
e.step_description
|
391
|
+
#=> "Check for sane ages"
|
392
|
+
|
393
|
+
e.input_data
|
394
|
+
#=> { :age => 1000 }
|
395
|
+
|
396
|
+
e.current_data
|
397
|
+
#=> { :age => 1000, :five => 5 }
|
398
|
+
end
|
399
|
+
```
|
400
|
+
|
401
|
+
rETL can be globally configured to continue transforming data despite an error
|
402
|
+
like so:
|
403
|
+
|
404
|
+
```
|
405
|
+
Retl.configure do |config|
|
406
|
+
config.raise_errors = false
|
407
|
+
end
|
408
|
+
```
|
409
|
+
|
410
|
+
With this configuration change, the transformation would continue to process the
|
411
|
+
data and errors would be accessible via the `#errors` method on the
|
412
|
+
transformation.
|
413
|
+
|
414
|
+
```
|
415
|
+
# same path and data as above
|
416
|
+
|
417
|
+
result = my_path.transform(data)
|
418
|
+
|
419
|
+
result.to_a
|
420
|
+
#=> [ { age: 3, five: 5 } ] # the error data isn't in the result
|
421
|
+
|
422
|
+
result.errors.to_a
|
423
|
+
#=> [ StepExecutionError ] # the error information is still available, see
|
424
|
+
# above for use.
|
425
|
+
```
|
358
426
|
|
359
427
|
## Roadmap
|
360
428
|
|
@@ -372,7 +440,6 @@ universal for any type of data transformation requirement in Ruby.
|
|
372
440
|
|
373
441
|
### Next Steps
|
374
442
|
|
375
|
-
- Error handling
|
376
443
|
- Tracing and logging
|
377
444
|
- Extract patterns
|
378
445
|
- Load patterns
|
data/lib/retl.rb
CHANGED
@@ -1,7 +1,18 @@
|
|
1
1
|
require "retl/version"
|
2
2
|
|
3
3
|
require "retl/path"
|
4
|
+
require "retl/configuration"
|
4
5
|
|
5
6
|
module Retl
|
7
|
+
@configuration = Configuration.new
|
8
|
+
|
9
|
+
class << self
|
10
|
+
attr_reader :configuration
|
11
|
+
|
12
|
+
def configure(&block)
|
13
|
+
block.call(@configuration)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
6
17
|
# Your code goes here...
|
7
18
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Retl
|
2
|
+
class StepExecutionError < StandardError
|
3
|
+
attr_reader :input_data, :current_data, :step, :cause
|
4
|
+
|
5
|
+
def initialize(input_data: nil, current_data: nil, step: nil, cause: $!)
|
6
|
+
@input_data, @current_data = input_data, current_data
|
7
|
+
@step, @cause = step, cause
|
8
|
+
|
9
|
+
super("#{cause} (at step: #{step_description}))")
|
10
|
+
set_backtrace(cause.backtrace)
|
11
|
+
end
|
12
|
+
|
13
|
+
def step_description
|
14
|
+
@step.description
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -1,7 +1,10 @@
|
|
1
1
|
module Retl
|
2
2
|
class Handler
|
3
|
+
attr_accessor :description
|
4
|
+
|
3
5
|
def initialize
|
4
|
-
@output
|
6
|
+
@output = []
|
7
|
+
@description = "unknown"
|
5
8
|
end
|
6
9
|
|
7
10
|
def output
|
@@ -9,6 +12,10 @@ module Retl
|
|
9
12
|
end
|
10
13
|
|
11
14
|
def push_in(data, context)
|
15
|
+
call(data, context)
|
16
|
+
end
|
17
|
+
|
18
|
+
def call(data, context)
|
12
19
|
raise NotImplementedError, "Handlers much implement the #push_in(data, context) method"
|
13
20
|
end
|
14
21
|
|
@@ -18,4 +25,4 @@ module Retl
|
|
18
25
|
@output.push data
|
19
26
|
end
|
20
27
|
end
|
21
|
-
end
|
28
|
+
end
|
@@ -2,9 +2,10 @@ require_relative "step_handler"
|
|
2
2
|
|
3
3
|
module Retl
|
4
4
|
class TransformHandler < StepHandler
|
5
|
-
def
|
6
|
-
|
7
|
-
|
5
|
+
def call(data, context)
|
6
|
+
dup = data.dup
|
7
|
+
context.execute_step(step, dup)
|
8
|
+
push_out dup
|
8
9
|
end
|
9
10
|
end
|
10
11
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Retl
|
2
|
+
class NextDescription
|
3
|
+
def initialize(default="unknown")
|
4
|
+
@default = default
|
5
|
+
reset
|
6
|
+
end
|
7
|
+
|
8
|
+
def reset
|
9
|
+
@next_description = @default
|
10
|
+
end
|
11
|
+
|
12
|
+
def describe_next(description)
|
13
|
+
@next_description = description
|
14
|
+
end
|
15
|
+
|
16
|
+
def take
|
17
|
+
description = @next_description
|
18
|
+
reset
|
19
|
+
description
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/retl/path.rb
CHANGED
@@ -5,6 +5,7 @@ require "retl/handlers/handler"
|
|
5
5
|
require "retl/handlers/step_handler"
|
6
6
|
require "retl/handlers/explode_handler"
|
7
7
|
require "retl/handlers/fork_handler"
|
8
|
+
require "retl/errors/step_execution_error"
|
8
9
|
|
9
10
|
module Retl
|
10
11
|
# A Path is a blueprint for transforming data
|
@@ -106,10 +107,18 @@ module Retl
|
|
106
107
|
# @param context [Context] the execution context for the transformation
|
107
108
|
#
|
108
109
|
# @return [Array<Hash>] the transformed data
|
109
|
-
def call(
|
110
|
-
@steps.reduce([
|
110
|
+
def call(input, context=Context.new(self))
|
111
|
+
@steps.reduce([input]) do |queue, handler|
|
111
112
|
queue.each do |data|
|
112
|
-
|
113
|
+
begin
|
114
|
+
handler.push_in(data, context)
|
115
|
+
rescue Exception => e
|
116
|
+
raise StepExecutionError.new(
|
117
|
+
input_data: input,
|
118
|
+
current_data: data,
|
119
|
+
step: handler
|
120
|
+
)
|
121
|
+
end
|
113
122
|
end
|
114
123
|
handler.output
|
115
124
|
end
|
data/lib/retl/path_builder.rb
CHANGED
@@ -4,18 +4,24 @@ require "retl/handlers/filter_handler"
|
|
4
4
|
require "retl/handlers/inspect_handler"
|
5
5
|
require "retl/handlers/explode_handler"
|
6
6
|
require "retl/handlers/path_handler"
|
7
|
+
require "retl/next_description"
|
7
8
|
|
8
9
|
|
9
10
|
module Retl
|
10
11
|
class PathBuilder
|
11
12
|
def initialize(path, &block)
|
12
13
|
@path = path
|
14
|
+
@next_descripion = NextDescription.new
|
13
15
|
instance_eval(&block)
|
14
16
|
end
|
15
17
|
|
16
18
|
def step(step=nil, handler: StepHandler, &block)
|
17
19
|
step ||= block
|
18
|
-
|
20
|
+
|
21
|
+
handler = handler.new(step)
|
22
|
+
handler.description = @next_descripion.take
|
23
|
+
|
24
|
+
@path.add_handler handler
|
19
25
|
end
|
20
26
|
alias_method :replace, :step
|
21
27
|
|
@@ -63,5 +69,9 @@ module Retl
|
|
63
69
|
def path(path, dependencies={}, &block)
|
64
70
|
@path.add_handler PathHandler.new(path, dependencies, &block)
|
65
71
|
end
|
72
|
+
|
73
|
+
def desc(step_description)
|
74
|
+
@next_descripion.describe_next(step_description)
|
75
|
+
end
|
66
76
|
end
|
67
77
|
end
|
data/lib/retl/transformation.rb
CHANGED
@@ -1,15 +1,17 @@
|
|
1
1
|
require "retl/context"
|
2
2
|
require "retl/fork_data_collector"
|
3
|
+
require "retl/errors/step_execution_error"
|
3
4
|
|
4
5
|
module Retl
|
5
6
|
class Transformation
|
6
7
|
include Enumerable
|
7
|
-
|
8
|
+
|
8
9
|
def initialize(enumerable, path, options={})
|
9
10
|
@enumerable, @path, @options = enumerable, path, options
|
10
11
|
@context = Context.new(@path, @options)
|
11
12
|
@fork_data = ForkDataCollector.new(@context)
|
12
13
|
@forks = {}
|
14
|
+
@errors = []
|
13
15
|
end
|
14
16
|
|
15
17
|
def each(&block)
|
@@ -52,13 +54,25 @@ module Retl
|
|
52
54
|
end
|
53
55
|
end
|
54
56
|
|
57
|
+
def errors
|
58
|
+
@errors.each
|
59
|
+
end
|
60
|
+
|
55
61
|
private
|
56
62
|
|
57
63
|
def build_each_result(&block)
|
58
64
|
@each ||= @enumerable.reduce([]) do |result, data|
|
59
|
-
|
60
|
-
|
61
|
-
|
65
|
+
begin
|
66
|
+
@path.call(data, @context).each do |data|
|
67
|
+
yield data if block_given?
|
68
|
+
result << data
|
69
|
+
end
|
70
|
+
rescue StepExecutionError => e
|
71
|
+
if Retl.configuration.raise_errors
|
72
|
+
raise e
|
73
|
+
else
|
74
|
+
@errors << e
|
75
|
+
end
|
62
76
|
end
|
63
77
|
result
|
64
78
|
end
|
data/lib/retl/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: retl
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Biehl
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-11-
|
11
|
+
date: 2015-11-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -71,7 +71,9 @@ files:
|
|
71
71
|
- bin/console
|
72
72
|
- bin/setup
|
73
73
|
- lib/retl.rb
|
74
|
+
- lib/retl/configuration.rb
|
74
75
|
- lib/retl/context.rb
|
76
|
+
- lib/retl/errors/step_execution_error.rb
|
75
77
|
- lib/retl/event_router.rb
|
76
78
|
- lib/retl/fork_data_collector.rb
|
77
79
|
- lib/retl/handlers/explode_handler.rb
|
@@ -82,6 +84,7 @@ files:
|
|
82
84
|
- lib/retl/handlers/path_handler.rb
|
83
85
|
- lib/retl/handlers/step_handler.rb
|
84
86
|
- lib/retl/handlers/transform_handler.rb
|
87
|
+
- lib/retl/next_description.rb
|
85
88
|
- lib/retl/path.rb
|
86
89
|
- lib/retl/path_builder.rb
|
87
90
|
- lib/retl/transformation.rb
|