chronicle-etl 0.1.4 → 0.2.4

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.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +8 -0
  3. data/.yardopts +1 -0
  4. data/Gemfile.lock +15 -1
  5. data/README.md +31 -13
  6. data/chronicle-etl.gemspec +6 -1
  7. data/exe/chronicle-etl +2 -2
  8. data/lib/chronicle/etl.rb +15 -2
  9. data/lib/chronicle/etl/catalog.rb +67 -17
  10. data/lib/chronicle/etl/cli/connectors.rb +32 -0
  11. data/lib/chronicle/etl/cli/jobs.rb +116 -0
  12. data/lib/chronicle/etl/cli/main.rb +83 -0
  13. data/lib/chronicle/etl/cli/subcommand_base.rb +37 -0
  14. data/lib/chronicle/etl/config.rb +53 -0
  15. data/lib/chronicle/etl/exceptions.rb +19 -0
  16. data/lib/chronicle/etl/extractors/csv_extractor.rb +2 -3
  17. data/lib/chronicle/etl/extractors/extractor.rb +21 -5
  18. data/lib/chronicle/etl/extractors/file_extractor.rb +2 -2
  19. data/lib/chronicle/etl/extractors/stdin_extractor.rb +2 -2
  20. data/lib/chronicle/etl/job.rb +71 -0
  21. data/lib/chronicle/etl/job_definition.rb +51 -0
  22. data/lib/chronicle/etl/job_log.rb +85 -0
  23. data/lib/chronicle/etl/job_logger.rb +78 -0
  24. data/lib/chronicle/etl/loaders/csv_loader.rb +4 -8
  25. data/lib/chronicle/etl/loaders/loader.rb +11 -2
  26. data/lib/chronicle/etl/loaders/rest_loader.rb +33 -0
  27. data/lib/chronicle/etl/loaders/stdout_loader.rb +5 -5
  28. data/lib/chronicle/etl/loaders/table_loader.rb +7 -6
  29. data/lib/chronicle/etl/models/activity.rb +15 -0
  30. data/lib/chronicle/etl/models/base.rb +103 -0
  31. data/lib/chronicle/etl/models/entity.rb +15 -0
  32. data/lib/chronicle/etl/models/generic.rb +23 -0
  33. data/lib/chronicle/etl/runner.rb +24 -46
  34. data/lib/chronicle/etl/transformers/null_transformer.rb +5 -6
  35. data/lib/chronicle/etl/transformers/transformer.rb +23 -7
  36. data/lib/chronicle/etl/utils/hash_utilities.rb +19 -0
  37. data/lib/chronicle/etl/utils/jsonapi.rb +28 -0
  38. data/lib/chronicle/etl/utils/progress_bar.rb +2 -2
  39. data/lib/chronicle/etl/version.rb +2 -2
  40. metadata +91 -5
  41. data/CHANGELOG.md +0 -23
  42. data/lib/chronicle/etl/cli.rb +0 -56
  43. data/lib/chronicle/etl/transformers/json_transformer.rb +0 -11
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3ab7fe84d09764034f061236d75fdf7403af9e1835d568831ae4896b90fc39b5
4
- data.tar.gz: c4c7a1ff47ecaf7d364e35d3439eef1345f90fee5a6610735124592109e2c02c
3
+ metadata.gz: 7a02a2377d0e8d4135f3b931bc73641eac28058d736d9c1dba0a97107c1d4c0e
4
+ data.tar.gz: 810d5bff80e852fa08ef9824ed6b313aa309bb69e84228bc1fbb7595069e043b
5
5
  SHA512:
6
- metadata.gz: a5a4b0e3769cd6063cb78843828ab15766df9d1ac1cd5d65fb81145d1415e95e46583c578312418fca91bfe02b05a8db3277a41a8a6b420f12263135deb49022
7
- data.tar.gz: e6d6e23d3c164d6fc5b9283d468fe9c9a450329d2d778e3ad49962f42fbe0316299111cdd02d2abc92eb52dc46823c5cbaf1c195203fc4e78129e9b28528c5ef
6
+ metadata.gz: 0d5fbea3c63349bb3f566e6137755f6cc8a4060d0e401abf5a0e7d8b44a4c4278089c10ffb8bb9cf2d783a238449140e5e54d90f3ad158aa362c6335eedca5aa
7
+ data.tar.gz: bf6fa83b1d5e55760e62d3cc090bf09bb69a7c761ae4a9358fb4d82192c7efc7500b6db361f39adac3581982862654aa4603a78dfbb3aed53b51d01137ffd736
@@ -0,0 +1,8 @@
1
+ AllCops:
2
+ EnabledByDefault: true
3
+
4
+ Style/StringLiterals:
5
+ Enabled: false
6
+
7
+ Style/MethodCallWithArgsParentheses:
8
+ Enabled: false
@@ -0,0 +1 @@
1
+ --markup=markdown
@@ -1,8 +1,10 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- chronicle-etl (0.1.4)
4
+ chronicle-etl (0.2.4)
5
5
  colorize (~> 0.8.1)
6
+ deep_merge (~> 1.2)
7
+ sequel (~> 5.35)
6
8
  thor (~> 0.20)
7
9
  tty-progressbar (~> 0.17)
8
10
  tty-table (~> 0.11)
@@ -13,6 +15,7 @@ GEM
13
15
  byebug (11.1.3)
14
16
  coderay (1.1.3)
15
17
  colorize (0.8.1)
18
+ deep_merge (1.2.1)
16
19
  diff-lcs (1.4.4)
17
20
  equatable (0.6.1)
18
21
  method_source (1.0.0)
@@ -27,6 +30,8 @@ GEM
27
30
  byebug (~> 11.0)
28
31
  pry (~> 0.13.0)
29
32
  rake (13.0.1)
33
+ redcarpet (3.5.0)
34
+ refinements (7.7.0)
30
35
  rspec (3.9.0)
31
36
  rspec-core (~> 3.9.0)
32
37
  rspec-expectations (~> 3.9.0)
@@ -40,6 +45,11 @@ GEM
40
45
  diff-lcs (>= 1.2.0, < 2.0)
41
46
  rspec-support (~> 3.9.0)
42
47
  rspec-support (3.9.3)
48
+ runcom (6.2.0)
49
+ refinements (~> 7.4)
50
+ xdg (~> 4.0)
51
+ sequel (5.36.0)
52
+ sqlite3 (1.4.2)
43
53
  strings (0.1.8)
44
54
  strings-ansi (~> 0.1)
45
55
  unicode-display_width (~> 1.5)
@@ -62,6 +72,7 @@ GEM
62
72
  tty-screen (~> 0.7)
63
73
  unicode-display_width (1.7.0)
64
74
  unicode_utils (1.4.0)
75
+ xdg (4.2.0)
65
76
 
66
77
  PLATFORMS
67
78
  ruby
@@ -71,7 +82,10 @@ DEPENDENCIES
71
82
  chronicle-etl!
72
83
  pry-byebug (~> 3.9)
73
84
  rake (~> 13.0)
85
+ redcarpet (~> 3.5)
74
86
  rspec (~> 3.9)
87
+ runcom (~> 6.2)
88
+ sqlite3 (~> 1.4)
75
89
 
76
90
  BUNDLED WITH
77
91
  2.1.4
data/README.md CHANGED
@@ -1,10 +1,10 @@
1
- # Chronicle::Etl
1
+ # Chronicle::ETL
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/chronicle-etl.svg)](https://badge.fury.io/rb/chronicle-etl)
4
4
 
5
- Chronicle ETL is a utility tool for archiving and processing personal data. You can extract it from a variety of source, transform it, and load it to different APIs or file formats.
5
+ Chronicle ETL is a utility that helps you archive and processes personal data. You can *extract* it from a variety of sources, *transform* it, and *load* it to an external API, file, or stdout.
6
6
 
7
- This project is an adaptation of Andrew Louis's experimental [Memex project](https://hyfen.net/memex).
7
+ This tool is an adaptation of Andrew Louis's experimental [Memex project](https://hyfen.net/memex) and the dozens of existing importers are being migrated to Chronicle.
8
8
 
9
9
  ## Installation
10
10
 
@@ -16,12 +16,24 @@ $ gem install chronicle-etl
16
16
 
17
17
  After installing the gem, `chronicle-etl` is available to run in your shell.
18
18
 
19
+ ```bash
20
+ # read test.csv and display it as a table
21
+ $ chronicle-etl jobs:run --extractor csv --extractor-opts filename:test.csv --loader table
22
+
23
+ # Display help for the jobs:run command
24
+ $ chronicle-etl jobs help run
19
25
  ```
20
- chronicle-etl --extractor csv --extractor-opts filename:test.csv --loader table
21
- cat test.csv | chronicle-etl --extractor csv --loader table
26
+
27
+ ## Connectors
28
+
29
+ Connectors are available to read, process, and load data from different formats or external services.
30
+
31
+ ```bash
32
+ # List all available connectors
33
+ $ chronicle-etl connectors:list
22
34
  ```
23
35
 
24
- ## Available importers
36
+ Built in connectors:
25
37
 
26
38
  ### Extractors
27
39
  - `stdin` - (default) Load records from line-separated stdin
@@ -40,7 +52,7 @@ cat test.csv | chronicle-etl --extractor csv --loader table
40
52
 
41
53
  In addition to the built-in importers, importers for third-party platforms are available. They are packaged as individual Ruby gems.
42
54
 
43
- - [email](https://github.com/chronicle-app/chronicle-email). Extractors for `mbox` files. Transformers for chronicle schema
55
+ - [email](https://github.com/chronicle-app/chronicle-email). Extractors for `mbox` and other email files. Transformers for chronicle schema
44
56
  - [bash](https://github.com/chronicle-app/chronicle-bash). Extract bash history from `~/.bash_history`. Transform it for chronicle schema
45
57
 
46
58
  To install any of these, run `gem install chronicle-PROVIDER`.
@@ -54,17 +66,23 @@ I'll be open-sourcing more importers. Please [contact me](mailto:andrew@hyfen.ne
54
66
  ```
55
67
  $ chronicle-etl help
56
68
 
57
- Commands:
58
- chronicle-etl help [COMMAND] # Describe available commands or one specific command
59
- chronicle-etl job # Runs an ETL job
60
- chronicle-etl list # List all ETL classes
69
+ ALL COMMANDS
70
+ help # This help menu
71
+ connectors help [COMMAND] # Describe subcommands or one specific subcommand
72
+ connectors:install NAME # Installs connector NAME
73
+ connectors:list # Lists available connectors
74
+ jobs help [COMMAND] # Describe subcommands or one specific subcommand
75
+ jobs:create # Create a job
76
+ jobs:list # List all available jobs
77
+ jobs:run # Start a job
78
+ jobs:show # Show a job
61
79
  ```
62
80
 
63
81
  ### Job options
64
82
 
65
83
  ```
66
84
  Usage:
67
- chronicle-etl job
85
+ chronicle-etl jobs:run
68
86
 
69
87
  Options:
70
88
  -e, [--extractor=extractor-name] # Extractor class (available: stdin, csv, file)
@@ -97,4 +115,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
97
115
 
98
116
  ## Code of Conduct
99
117
 
100
- Everyone interacting in the Chronicle::Etl project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/chronicle-app/chronicle-etl/blob/master/CODE_OF_CONDUCT.md).
118
+ Everyone interacting in the Chronicle::ETL project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/chronicle-app/chronicle-etl/blob/master/CODE_OF_CONDUCT.md).
@@ -5,7 +5,7 @@ require "chronicle/etl/version"
5
5
 
6
6
  Gem::Specification.new do |spec|
7
7
  spec.name = "chronicle-etl"
8
- spec.version = Chronicle::Etl::VERSION
8
+ spec.version = Chronicle::ETL::VERSION
9
9
  spec.authors = ["Andrew Louis"]
10
10
  spec.email = ["andrew@hyfen.net"]
11
11
 
@@ -40,9 +40,14 @@ Gem::Specification.new do |spec|
40
40
  spec.add_dependency "colorize", "~> 0.8.1"
41
41
  spec.add_dependency "tty-table", "~> 0.11"
42
42
  spec.add_dependency "tty-progressbar", "~> 0.17"
43
+ spec.add_dependency 'sequel', '~> 5.35'
44
+ spec.add_dependency 'deep_merge', '~> 1.2'
43
45
 
44
46
  spec.add_development_dependency "bundler", "~> 2.1"
45
47
  spec.add_development_dependency "rake", "~> 13.0"
46
48
  spec.add_development_dependency "rspec", "~> 3.9"
47
49
  spec.add_development_dependency "pry-byebug", "~> 3.9"
50
+ spec.add_development_dependency 'runcom', '~> 6.2'
51
+ spec.add_development_dependency 'redcarpet', '~> 3.5'
52
+ spec.add_development_dependency 'sqlite3', '~> 1.4'
48
53
  end
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require "chronicle/etl/cli"
3
+ require "chronicle/etl/cli/main"
4
4
 
5
- Chronicle::Etl::CLI.start(ARGV)
5
+ Chronicle::ETL::CLI::Main.start(ARGV)
@@ -1,6 +1,19 @@
1
1
  require_relative 'etl/catalog'
2
+ require_relative 'etl/config'
3
+ require_relative 'etl/exceptions'
2
4
  require_relative 'etl/extractors/extractor'
3
- require_relative 'etl/transformers/transformer'
5
+ require_relative 'etl/job_definition'
6
+ require_relative 'etl/job_log'
7
+ require_relative 'etl/job_logger'
8
+ require_relative 'etl/job'
4
9
  require_relative 'etl/loaders/loader'
5
- require_relative 'etl/utils/progress_bar'
10
+ require_relative 'etl/models/activity'
11
+ require_relative 'etl/models/base'
12
+ require_relative 'etl/models/entity'
13
+ require_relative 'etl/models/generic'
6
14
  require_relative 'etl/runner'
15
+ require_relative 'etl/transformers/transformer'
16
+ require_relative 'etl/utils/hash_utilities'
17
+ require_relative 'etl/utils/jsonapi'
18
+ require_relative 'etl/utils/progress_bar'
19
+ require_relative 'etl/version'
@@ -1,30 +1,37 @@
1
1
  module Chronicle
2
- module Etl
2
+ module ETL
3
3
  # Utility methods to catalogue which Extractor, Transformer, and
4
- # Loader classes are available to chronicle-etl
4
+ # Loader connector classes are available to chronicle-etl
5
5
  module Catalog
6
- def self.available_classes
7
- parent_klasses = [
8
- Chronicle::Etl::Extractor,
9
- Chronicle::Etl::Transformer,
10
- Chronicle::Etl::Loader
11
- ]
6
+ PHASES = [:extractor, :transformer, :loader]
7
+ PLUGINS = ['email', 'bash']
8
+ BUILTIN = {
9
+ extractor: ['stdin', 'json', 'csv', 'file'],
10
+ transformer: ['null'],
11
+ loader: ['stdout', 'csv', 'table', 'rest']
12
+ }.freeze
12
13
 
14
+ # Return which ETL connectors are available, both built in and externally-defined
15
+ def self.available_classes
13
16
  # TODO: have a registry of plugins
14
- plugins = ['email', 'bash']
15
17
 
16
18
  # Attempt to load each chronicle plugin that we might know about so
17
19
  # that we can later search for subclasses to build our list of
18
20
  # available classes
19
- plugins.each do |plugin|
21
+ PLUGINS.each do |plugin|
20
22
  require "chronicle/#{plugin}"
21
23
  rescue LoadError
22
- # this will happen if the gem isn't available globally
24
+ # this will happen if the gem isn't available globally
23
25
  end
24
26
 
27
+ parent_klasses = [
28
+ ::Chronicle::ETL::Extractor,
29
+ ::Chronicle::ETL::Transformer,
30
+ ::Chronicle::ETL::Loader
31
+ ]
25
32
  klasses = []
26
- parent_klasses.each do |parent|
27
- klasses += ObjectSpace.each_object(Class).select { |klass| klass < parent }
33
+ parent_klasses.map do |parent|
34
+ klasses += ::ObjectSpace.each_object(::Class).select { |klass| klass < parent }
28
35
  end
29
36
 
30
37
  klasses.map do |klass|
@@ -37,21 +44,64 @@ module Chronicle
37
44
  end
38
45
  end
39
46
 
47
+ # Take a phase (e, t, or l) and an identifier and return the right class
48
+ def self.phase_and_identifier_to_klass(phase, identifier)
49
+ Chronicle::ETL::Catalog.identifier_to_klass(phase: phase, identifier: identifier)
50
+ end
51
+
52
+ # For a given connector identifier, return the class (either builtin, or from a
53
+ # external chronicle gem)
54
+ def self.identifier_to_klass(identifier:, phase:)
55
+ if BUILTIN[phase].include? identifier
56
+ load_builtin_klass(name: identifier, phase: phase)
57
+ else
58
+ provider, name = identifier.split(':')
59
+ name ||= ''
60
+ load_provider_klass(provider: provider, name: name, phase: phase)
61
+ end
62
+ end
63
+
64
+ # Returns whether a class is an Extractor, Transformer, or Loader
40
65
  def phase
41
66
  ancestors = self.ancestors
42
- return :extractor if ancestors.include? Chronicle::Etl::Extractor
43
- return :transformer if ancestors.include? Chronicle::Etl::Transformer
44
- return :loader if ancestors.include? Chronicle::Etl::Loader
67
+ return :extractor if ancestors.include? Chronicle::ETL::Extractor
68
+ return :transformer if ancestors.include? Chronicle::ETL::Transformer
69
+ return :loader if ancestors.include? Chronicle::ETL::Loader
45
70
  end
46
71
 
72
+ # Returns which third-party provider this connector is associated wtih
47
73
  def provider
48
74
  # TODO: needs better convention for a gem reporting its provider name
49
75
  provider = to_s.split('::')[1].downcase
50
76
  provider == 'etl' ? 'chronicle' : provider
51
77
  end
52
78
 
79
+ # Returns whether this connector is a built-in one
53
80
  def built_in?
54
- to_s.include? 'Chronicle::Etl'
81
+ to_s.include? 'Chronicle::ETL'
82
+ end
83
+
84
+ private
85
+
86
+ def self.load_builtin_klass(name:, phase:)
87
+ klass_str = "Chronicle::ETL::#{name.capitalize}#{phase.capitalize}"
88
+ begin
89
+ Object.const_get(klass_str)
90
+ rescue NameError => e
91
+ raise ConnectorNotAvailableError.new("Connector not found", name: name)
92
+ end
93
+ end
94
+
95
+ def self.load_provider_klass(name: '', phase:, provider:)
96
+ begin
97
+ require "chronicle/#{provider}"
98
+ klass_str = "Chronicle::#{provider.capitalize}::#{name.capitalize}#{phase.capitalize}"
99
+ Object.const_get(klass_str)
100
+ rescue LoadError => e
101
+ raise ProviderNotAvailableError.new("Provider '#{provider.capitalize}' could not be loaded", provider: provider)
102
+ rescue NameError => e
103
+ raise ProviderConnectorNotAvailableError.new("Connector '#{name}' in '#{provider}' could not be found", provider: provider, name: name)
104
+ end
55
105
  end
56
106
  end
57
107
  end
@@ -0,0 +1,32 @@
1
+ module Chronicle
2
+ module ETL
3
+ module CLI
4
+ # CLI commands for working with ETL connectors
5
+ class Connectors < SubcommandBase
6
+ default_task 'list'
7
+ namespace :connectors
8
+
9
+ desc "install NAME", "Installs connector NAME"
10
+ def install
11
+ puts "Installing"
12
+ end
13
+
14
+ desc "list", "Lists available connectors"
15
+ # Display all available connectors that chronicle-etl has access to
16
+ def list
17
+ klasses = Chronicle::ETL::Catalog.available_classes
18
+ klasses = klasses.sort_by do |a|
19
+ [a[:built_in].to_s, a[:provider], a[:phase]]
20
+ end
21
+
22
+ headers = klasses.first.keys.map do |key|
23
+ key.to_s.upcase.bold
24
+ end
25
+
26
+ table = TTY::Table.new(headers, klasses.map(&:values))
27
+ puts table.render(indent: 0, padding: [0, 2])
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,116 @@
1
+ require 'pp'
2
+ module Chronicle
3
+ module ETL
4
+ module CLI
5
+ # CLI commands for working with ETL jobs
6
+ class Jobs < SubcommandBase
7
+ default_task "start"
8
+ namespace :jobs
9
+
10
+ class_option :extractor, aliases: '-e', desc: 'Extractor class (available: stdin, csv, file)', default: 'stdin', banner: 'extractor-name'
11
+ class_option :'extractor-opts', desc: 'Extractor options', type: :hash, default: {}
12
+ class_option :transformer, aliases: '-t', desc: 'Transformer class (available: null)', default: 'null', banner: 'transformer-name'
13
+ class_option :'transformer-opts', desc: 'Transformer options', type: :hash, default: {}
14
+ class_option :loader, aliases: '-l', desc: 'Loader class (available: stdout, csv, table)', default: 'stdout', banner: 'loader-name'
15
+ class_option :'loader-opts', desc: 'Loader options', type: :hash, default: {}
16
+ class_option :name, aliases: '-j', desc: 'Job configuration name'
17
+
18
+ map run: :start # Thor doesn't like `run` as a command name
19
+ desc "run", "Start a job"
20
+ long_desc <<-LONG_DESC
21
+ This will run an ETL job. Each job needs three parts:
22
+
23
+ 1. #{'Extractor'.underline}: pulls data from an external source. By default, this is stdout. Other common options including pulling data from an API or reading JSON from a file.
24
+
25
+ 2. #{'Transformer'.underline}: transforms data into a new format. If none is specified, we use the `null` transformer which does nothing to the data.
26
+
27
+ 3. #{'Loader'.underline}: takes that transformed data and loads it externally. This can be an API, flat files, (or by default), stdout.
28
+
29
+ If you do not want to use the command line flags, you can also configure a job with a .yml config file. You can either specify the path to this file or use the filename and place the file in ~/.config/chronicle/etl/jobs/NAME.yml and call it with `--job NAME`
30
+ LONG_DESC
31
+ # Run an ETL job
32
+ def start
33
+ job_definition = build_job_definition(options)
34
+ job = Chronicle::ETL::Job.new(job_definition)
35
+ runner = Chronicle::ETL::Runner.new(job)
36
+ runner.run!
37
+ rescue Chronicle::ETL::ProviderNotAvailableError => e
38
+ warn(e.message.red)
39
+ warn(" Perhaps you haven't installed it yet: `$ gem install chronicle-#{e.provider}`")
40
+ exit(false)
41
+ rescue Chronicle::ETL::ConnectorNotAvailableError => e
42
+ warn(e.message.red)
43
+ exit(false)
44
+ end
45
+
46
+ desc "create", "Create a job"
47
+ # Create an ETL job
48
+ def create
49
+ job_definition = build_job_definition(options)
50
+ path = File.join('chronicle', 'etl', 'jobs', options[:name])
51
+ Chronicle::ETL::Config.write(path, job_definition)
52
+ end
53
+
54
+ desc "show", "Show details about a job"
55
+ # Show an ETL job
56
+ def show
57
+ job_config = build_job_definition(options)
58
+ pp job_config
59
+ end
60
+
61
+ desc "list", "List all available jobs"
62
+ # List available ETL jobs
63
+ def list
64
+ jobs = Chronicle::ETL::Config.available_jobs
65
+
66
+ job_details = jobs.map do |job|
67
+ r = Chronicle::ETL::Config.load("chronicle/etl/jobs/#{job}.yml")
68
+
69
+ extractor = r[:extractor][:name] if r[:extractor]
70
+ transformer = r[:transformer][:name] if r[:transformer]
71
+ loader = r[:loader][:name] if r[:loader]
72
+
73
+ [job, extractor, transformer, loader]
74
+ end
75
+
76
+ headers = ['name', 'extractor', 'transformer', 'loader'].map{|h| h.upcase.bold }
77
+
78
+ table = TTY::Table.new(headers, job_details)
79
+ puts table.render(indent: 0, padding: [0, 2])
80
+ end
81
+
82
+ private
83
+
84
+ # Create job definition by reading config file and then overwriting with flag options
85
+ def build_job_definition(options)
86
+ definition = Chronicle::ETL::JobDefinition.new
87
+ definition.add_config(process_flag_options(options))
88
+ definition.add_config(load_job_config(options[:name]))
89
+ definition
90
+ end
91
+
92
+ def load_job_config name
93
+ Chronicle::ETL::Config.load_job_from_config(name)
94
+ end
95
+
96
+ # Takes flag options and turns them into a runner config
97
+ def process_flag_options options
98
+ {
99
+ extractor: {
100
+ name: options[:extractor],
101
+ options: options[:'extractor-opts']
102
+ },
103
+ transformer: {
104
+ name: options[:transformer],
105
+ options: options[:'transformer-opts']
106
+ },
107
+ loader: {
108
+ name: options[:loader],
109
+ options: options[:'loader-opts']
110
+ }
111
+ }
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end