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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1262b0170c7b1fdbd343d510a6e8bab6edb3bc01
4
- data.tar.gz: 4eeafb6b53d22901171185786c297b3b18fbfcb6
3
+ metadata.gz: 5fb3e97322814aed123d196693eacd2eb5560719
4
+ data.tar.gz: 778407acae9f684ddaa8b746d0c8dd751ed2e9be
5
5
  SHA512:
6
- metadata.gz: ff134b73c4e49380cb63b6a2251d6d2a1823c8ff4c8c87fbae64860d0e96bfb4b740b3670a2858db00e1aac84ffdc7cb8251aeb93750e4544a563c5b4c2d0233
7
- data.tar.gz: f02e88e10931ac4ff35e33223f539bb408e55faf2e9d5d7212fbeffd871a410f10fdc3f8df131b1b7be71f1370fea807839420f09e2cb4703b3b26e84bc429f1
6
+ metadata.gz: 8b0182706dbf3a4350d53cb967b576075a9fbf85dd1dd1efb778bc0bd4d648b15bddea9904ece0a973bf15896d60ed38ae4833f09b1c5ffc986f48650415fa8d
7
+ data.tar.gz: 29a71295ac8f7025fde450c1f4f079e100fe082c2891d20fe34950f28f588c12affe2a0e1e8bcc50a3bd43ca20e80ad6ea97f9839fccf8535514b6c43a93fe84
@@ -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
@@ -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,7 @@
1
+ class Configuration
2
+ attr_accessor :raise_errors
3
+
4
+ def initialize
5
+ self.raise_errors = true
6
+ end
7
+ 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
@@ -2,7 +2,7 @@ require_relative "step_handler"
2
2
 
3
3
  module Retl
4
4
  class ExplodeHandler < StepHandler
5
- def push_in(data, context)
5
+ def call(data, context)
6
6
  context.execute_step(step, data).each do |result|
7
7
  push_out result
8
8
  end
@@ -2,7 +2,7 @@ require_relative "step_handler"
2
2
 
3
3
  module Retl
4
4
  class FilterHandler < StepHandler
5
- def push_in(data, context)
5
+ def call(data, context)
6
6
  keep = context.execute_step(step, data)
7
7
  push_out data if keep
8
8
  end
@@ -7,7 +7,7 @@ module Retl
7
7
  @fork = fork
8
8
  end
9
9
 
10
- def push_in(data, context)
10
+ def call(data, context)
11
11
  context._events.trigger(:fork_data, fork_name: @fork, data: data.dup)
12
12
  push_out data
13
13
  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,7 +2,7 @@ require_relative "step_handler"
2
2
 
3
3
  module Retl
4
4
  class InspectHandler < StepHandler
5
- def push_in(data, context)
5
+ def call(data, context)
6
6
  context.execute_step(step, data.dup)
7
7
  push_out data
8
8
  end
@@ -9,7 +9,7 @@ module Retl
9
9
  @context = Context.new(@path, dependencies)
10
10
  end
11
11
 
12
- def push_in(data, context)
12
+ def call(data, context)
13
13
  @context.execute_step(@path, data).each do |result|
14
14
  push_out(result)
15
15
  end
@@ -9,7 +9,7 @@ module Retl
9
9
  @step = step
10
10
  end
11
11
 
12
- def push_in(data, context)
12
+ def call(data, context)
13
13
  push_out context.execute_step(step, data)
14
14
  end
15
15
  end
@@ -2,9 +2,10 @@ require_relative "step_handler"
2
2
 
3
3
  module Retl
4
4
  class TransformHandler < StepHandler
5
- def push_in(data, context)
6
- context.execute_step(step, data)
7
- push_out data
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
@@ -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(data, context=Context.new(self))
110
- @steps.reduce([data]) do |queue, handler|
110
+ def call(input, context=Context.new(self))
111
+ @steps.reduce([input]) do |queue, handler|
111
112
  queue.each do |data|
112
- handler.push_in(data, context)
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
@@ -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
- @path.add_step step, handler: handler
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
@@ -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
- @path.call(data, @context).each do |data|
60
- yield data if block_given?
61
- result << data
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
@@ -1,3 +1,3 @@
1
1
  module Retl
2
- VERSION = "0.0.4"
2
+ VERSION = "0.0.5"
3
3
  end
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
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-22 00:00:00.000000000 Z
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