chronicle-etl 0.2.1 → 0.2.2

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
  SHA256:
3
- metadata.gz: 3d9be4b073385d9758a8b709bb6726d8dae648b8c4dbef474343840c011d1178
4
- data.tar.gz: 0d95395407d37f7e322287a0920bba60e1b4f81eb8649190d021e13580604a2d
3
+ metadata.gz: e1c08bc4f71c807525090abbf1701be19ab72cce08a99cc3bbec9b0db7150a02
4
+ data.tar.gz: 172a5d7e7ba7a9424ef7b5ab4da2b8c44defdb4e0a34c833248ff1b63f40407e
5
5
  SHA512:
6
- metadata.gz: 386c96518aa2d2810ae2a93bbe3af5bb08e26e132608b4e6ed8a278da076783e453854a2120c5016b6d02cd5dea406146d10ef3c7c1e77d854acd8ff2608eaf7
7
- data.tar.gz: eb14402be5d6db44a6f06e6ec930acc5103b36e5c2e5a13e89137c9ee45f5f11c1e9e6ab13d4d44e6ee06bd9b02309e02ac33e81256645ef71d5b431c97eb199
6
+ metadata.gz: 0f671c00928b15f9c0f6fa159ac106ff9c4f65a8bd16048e5d0cab82d680945317f7680e7796e98c665bb5cc757e0657f1a36d773d89e3e1587d9eebc12abdd8
7
+ data.tar.gz: 449d1368e0054f39006c7903218300b9b97ca839d6eff43b6b7bd659e5146d443a31c53325c4769ae7a56db9d42417020ccde17362ae024c01aca2ed63029044
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- chronicle-etl (0.2.1)
4
+ chronicle-etl (0.2.2)
5
5
  colorize (~> 0.8.1)
6
6
  thor (~> 0.20)
7
7
  tty-progressbar (~> 0.17)
@@ -1,5 +1,6 @@
1
1
  require_relative 'etl/catalog'
2
2
  require_relative 'etl/config'
3
+ require_relative 'etl/exceptions'
3
4
  require_relative 'etl/extractors/extractor'
4
5
  require_relative 'etl/loaders/loader'
5
6
  require_relative 'etl/runner'
@@ -4,6 +4,11 @@ module Chronicle
4
4
  # Loader connector classes are available to chronicle-etl
5
5
  module Catalog
6
6
  PLUGINS = ['email', 'bash']
7
+ BUILTIN = {
8
+ extractor: ['stdin', 'json', 'csv', 'file'],
9
+ transformer: ['null'],
10
+ loader: ['stdout', 'csv', 'table', 'rest']
11
+ }.freeze
7
12
 
8
13
  # Return which ETL connectors are available, both built in and externally-defined
9
14
  def self.available_classes
@@ -15,7 +20,7 @@ module Chronicle
15
20
  PLUGINS.each do |plugin|
16
21
  require "chronicle/#{plugin}"
17
22
  rescue LoadError
18
- # this will happen if the gem isn't available globally
23
+ # this will happen if the gem isn't available globally
19
24
  end
20
25
 
21
26
  parent_klasses = [
@@ -38,6 +43,18 @@ module Chronicle
38
43
  end
39
44
  end
40
45
 
46
+ # For a given connector identifier, return the class (either builtin, or from a
47
+ # external chronicle gem)
48
+ def self.identifier_to_klass(identifier:, phase:)
49
+ if BUILTIN[phase].include? identifier
50
+ load_builtin_klass(name: identifier, phase: phase)
51
+ else
52
+ provider, name = identifier.split(':')
53
+ name ||= ''
54
+ load_provider_klass(provider: provider, name: name, phase: phase)
55
+ end
56
+ end
57
+
41
58
  # Returns whether a class is an Extractor, Transformer, or Loader
42
59
  def phase
43
60
  ancestors = self.ancestors
@@ -57,6 +74,29 @@ module Chronicle
57
74
  def built_in?
58
75
  to_s.include? 'Chronicle::ETL'
59
76
  end
77
+
78
+ private
79
+
80
+ def self.load_builtin_klass(name:, phase:)
81
+ klass_str = "Chronicle::ETL::#{name.capitalize}#{phase.capitalize}"
82
+ begin
83
+ Object.const_get(klass_str)
84
+ rescue NameError => e
85
+ raise ConnectorNotAvailableError.new("Connector not found", name: name)
86
+ end
87
+ end
88
+
89
+ def self.load_provider_klass(name: '', phase:, provider:)
90
+ begin
91
+ require "chronicle/#{provider}"
92
+ klass_str = "Chronicle::#{provider.capitalize}::#{name.capitalize}#{phase.capitalize}"
93
+ Object.const_get(klass_str)
94
+ rescue LoadError => e
95
+ raise ProviderNotAvailableError.new("Provider '#{provider.capitalize}' could not be loaded", provider: provider)
96
+ rescue NameError => e
97
+ raise ProviderConnectorNotAvailableError.new("Connector '#{name}' in '#{provider}' could not be found", provider: provider, name: name)
98
+ end
99
+ end
60
100
  end
61
101
  end
62
102
  end
@@ -1,5 +1,4 @@
1
1
  require 'pp'
2
- require 'pry'
3
2
 
4
3
  module Chronicle
5
4
  module ETL
@@ -0,0 +1,17 @@
1
+ module Chronicle
2
+ module ETL
3
+ class Error < StandardError; end;
4
+
5
+ class ConnectorNotAvailableError < Error
6
+ def initialize(message, provider: nil, name: nil)
7
+ super(message)
8
+ @provider = provider
9
+ @name = name
10
+ end
11
+ attr_reader :name, :provider
12
+ end
13
+
14
+ class ProviderNotAvailableError < ConnectorNotAvailableError; end
15
+ class ProviderConnectorNotAvailableError < ConnectorNotAvailableError; end
16
+ end
17
+ end
@@ -1,59 +1,46 @@
1
- class Chronicle::ETL::Runner
2
- BUILTIN = {
3
- extractor: ['stdin', 'json', 'csv', 'file'],
4
- transformer: ['null'],
5
- loader: ['stdout', 'csv', 'table']
6
- }.freeze
1
+ require 'colorize'
7
2
 
8
- def initialize(options)
3
+ class Chronicle::ETL::Runner
4
+ def initialize(options = {})
9
5
  @options = options
10
-
11
- instantiate_etl_classes
12
6
  end
13
7
 
14
8
  def run!
15
- total = @extractor.results_count
9
+ extractor = instantiate_klass(:extractor)
10
+ loader = instantiate_klass(:loader)
11
+
12
+ total = extractor.results_count
16
13
  progress_bar = Chronicle::ETL::Utils::ProgressBar.new(title: 'Running job', total: total)
17
- count = 0
18
14
 
19
- @loader.start
15
+ loader.start
20
16
 
21
- @extractor.extract do |data, metadata|
22
- transformed_data = @transformer.transform(data)
23
- @loader.load(transformed_data)
17
+ extractor.extract do |data, metadata|
18
+ transformer = instantiate_klass(:transformer, data)
19
+ transformed_data = transformer.transform
24
20
 
21
+ loader.load(transformed_data)
25
22
  progress_bar.increment
26
- count += 1
27
23
  end
28
24
 
29
25
  progress_bar.finish
30
- @loader.finish
26
+ loader.finish
31
27
  end
32
28
 
33
29
  private
34
30
 
35
- def instantiate_etl_classes
36
- @extractor = load_etl_class(:extractor, @options[:extractor][:name]).new(@options[:extractor][:options])
37
- @transformer = load_etl_class(:transformer, @options[:transformer][:name]).new(@options[:transformer][:options])
38
- @loader = load_etl_class(:loader, @options[:loader][:name]).new(@options[:loader][:options])
31
+ def instantiate_klass(phase, *args)
32
+ klass = load_etl_class(phase, @options[phase][:name])
33
+ klass.new(@options[phase][:options], *args)
39
34
  end
40
35
 
41
- def load_etl_class(phase, x)
42
- if BUILTIN[phase].include? x
43
- klass_name = "Chronicle::ETL::#{x.capitalize}#{phase.to_s.capitalize}"
44
- else
45
- # TODO: come up with syntax for specifying a particular extractor in a provider library
46
- provider, name = x.split(":")
47
- provider = x unless provider
48
- begin
49
- require "chronicle/#{provider}"
50
- rescue LoadError => e
51
- warn("Error loading #{phase} '#{provider}'".red)
52
- warn(" Perhaps you haven't installed it yet: `$ gem install chronicle-#{provider}`")
53
- exit(false)
54
- end
55
- klass_name = "Chronicle::#{provider.capitalize}::#{name&.capitalize}#{phase.capitalize}"
56
- end
57
- Object.const_get(klass_name)
36
+ def load_etl_class(phase, identifier)
37
+ Chronicle::ETL::Catalog.identifier_to_klass(phase: phase, identifier: identifier)
38
+ rescue Chronicle::ETL::ProviderNotAvailableError => e
39
+ warn(e.message.red)
40
+ warn(" Perhaps you haven't installed it yet: `$ gem install chronicle-#{e.provider}`")
41
+ exit(false)
42
+ rescue Chronicle::ETL::ConnectorNotAvailableError => e
43
+ warn(e.message.red)
44
+ exit(false)
58
45
  end
59
46
  end
@@ -1,8 +1,8 @@
1
1
  module Chronicle
2
2
  module ETL
3
3
  class NullTransformer < Chronicle::ETL::Transformer
4
- def transform data
5
- return data
4
+ def transform
5
+ return @data
6
6
  end
7
7
  end
8
8
 
@@ -8,12 +8,13 @@ module Chronicle
8
8
  # == Paramters:
9
9
  # options::
10
10
  # Options for configuring this Transformer
11
- def initialize(options = {})
11
+ def initialize(options = {}, data)
12
12
  @options = options
13
+ @data = data
13
14
  end
14
15
 
15
16
  # The main entrypoint for transforming a record. Called by a Runner on each extracted record
16
- def transform data
17
+ def transform
17
18
  raise NotImplementedError
18
19
  end
19
20
 
@@ -1,5 +1,5 @@
1
1
  module Chronicle
2
2
  module ETL
3
- VERSION = "0.2.1"
3
+ VERSION = "0.2.2"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: chronicle-etl
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Louis
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-08-27 00:00:00.000000000 Z
11
+ date: 2020-08-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor
@@ -183,6 +183,7 @@ files:
183
183
  - lib/chronicle/etl/cli/main.rb
184
184
  - lib/chronicle/etl/cli/subcommand_base.rb
185
185
  - lib/chronicle/etl/config.rb
186
+ - lib/chronicle/etl/exceptions.rb
186
187
  - lib/chronicle/etl/extractors/csv_extractor.rb
187
188
  - lib/chronicle/etl/extractors/extractor.rb
188
189
  - lib/chronicle/etl/extractors/file_extractor.rb