retl 0.0.4 → 0.0.5

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 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