chronicle-etl 0.4.0 → 0.4.3
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 +4 -4
- data/.github/workflows/ruby.yml +2 -2
- data/.rubocop.yml +3 -0
- data/README.md +156 -81
- data/chronicle-etl.gemspec +3 -0
- data/lib/chronicle/etl/cli/cli_base.rb +31 -0
- data/lib/chronicle/etl/cli/connectors.rb +4 -11
- data/lib/chronicle/etl/cli/jobs.rb +49 -22
- data/lib/chronicle/etl/cli/main.rb +32 -1
- data/lib/chronicle/etl/cli/plugins.rb +62 -0
- data/lib/chronicle/etl/cli/subcommand_base.rb +1 -1
- data/lib/chronicle/etl/cli.rb +3 -0
- data/lib/chronicle/etl/config.rb +7 -4
- data/lib/chronicle/etl/configurable.rb +15 -2
- data/lib/chronicle/etl/exceptions.rb +29 -2
- data/lib/chronicle/etl/extractors/csv_extractor.rb +24 -17
- data/lib/chronicle/etl/extractors/extractor.rb +5 -5
- data/lib/chronicle/etl/extractors/file_extractor.rb +33 -13
- data/lib/chronicle/etl/extractors/helpers/input_reader.rb +76 -0
- data/lib/chronicle/etl/extractors/json_extractor.rb +21 -12
- data/lib/chronicle/etl/job.rb +7 -1
- data/lib/chronicle/etl/job_definition.rb +32 -6
- data/lib/chronicle/etl/loaders/csv_loader.rb +35 -8
- data/lib/chronicle/etl/loaders/helpers/encoding_helper.rb +18 -0
- data/lib/chronicle/etl/loaders/json_loader.rb +44 -0
- data/lib/chronicle/etl/loaders/loader.rb +24 -1
- data/lib/chronicle/etl/loaders/table_loader.rb +13 -26
- data/lib/chronicle/etl/logger.rb +6 -2
- data/lib/chronicle/etl/models/base.rb +3 -0
- data/lib/chronicle/etl/models/entity.rb +8 -2
- data/lib/chronicle/etl/models/raw.rb +26 -0
- data/lib/chronicle/etl/registry/connector_registration.rb +5 -0
- data/lib/chronicle/etl/registry/plugin_registry.rb +75 -0
- data/lib/chronicle/etl/registry/registry.rb +27 -14
- data/lib/chronicle/etl/runner.rb +35 -17
- data/lib/chronicle/etl/serializers/jsonapi_serializer.rb +6 -0
- data/lib/chronicle/etl/serializers/raw_serializer.rb +10 -0
- data/lib/chronicle/etl/serializers/serializer.rb +2 -1
- data/lib/chronicle/etl/transformers/null_transformer.rb +1 -1
- data/lib/chronicle/etl/version.rb +1 -1
- data/lib/chronicle/etl.rb +11 -4
- metadata +53 -6
- data/lib/chronicle/etl/extractors/helpers/filesystem_reader.rb +0 -104
- data/lib/chronicle/etl/loaders/stdout_loader.rb +0 -14
- data/lib/chronicle/etl/models/generic.rb +0 -23
@@ -0,0 +1,44 @@
|
|
1
|
+
module Chronicle
|
2
|
+
module ETL
|
3
|
+
class JSONLoader < Chronicle::ETL::Loader
|
4
|
+
register_connector do |r|
|
5
|
+
r.description = 'json'
|
6
|
+
end
|
7
|
+
|
8
|
+
setting :serializer
|
9
|
+
setting :output, default: $stdout
|
10
|
+
|
11
|
+
def start
|
12
|
+
if @config.output == $stdout
|
13
|
+
@output = @config.output
|
14
|
+
else
|
15
|
+
@output = File.open(@config.output, "w")
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def load(record)
|
20
|
+
serialized = serializer.serialize(record)
|
21
|
+
|
22
|
+
# When dealing with raw data, we can get improperly encoded strings
|
23
|
+
# (eg from sqlite database columns). We force conversion to UTF-8
|
24
|
+
# before converting into JSON
|
25
|
+
encoded = serialized.transform_values do |value|
|
26
|
+
next value unless value.is_a?(String)
|
27
|
+
|
28
|
+
force_utf8(value)
|
29
|
+
end
|
30
|
+
@output.puts encoded.to_json
|
31
|
+
end
|
32
|
+
|
33
|
+
def finish
|
34
|
+
@output.close
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def serializer
|
40
|
+
@config.serializer || Chronicle::ETL::RawSerializer
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -1,11 +1,17 @@
|
|
1
|
+
require_relative 'helpers/encoding_helper'
|
2
|
+
|
1
3
|
module Chronicle
|
2
4
|
module ETL
|
3
5
|
# Abstract class representing a Loader for an ETL job
|
4
6
|
class Loader
|
5
7
|
extend Chronicle::ETL::Registry::SelfRegistering
|
6
8
|
include Chronicle::ETL::Configurable
|
9
|
+
include Chronicle::ETL::Loaders::Helpers::EncodingHelper
|
7
10
|
|
8
11
|
setting :output
|
12
|
+
setting :fields
|
13
|
+
setting :fields_limit, default: nil
|
14
|
+
setting :fields_exclude
|
9
15
|
|
10
16
|
# Construct a new instance of this loader. Options are passed in from a Runner
|
11
17
|
# == Parameters:
|
@@ -25,11 +31,28 @@ module Chronicle
|
|
25
31
|
|
26
32
|
# Called once there are no more records to process
|
27
33
|
def finish; end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def build_headers(records)
|
38
|
+
headers =
|
39
|
+
if @config.fields && @config.fields.any?
|
40
|
+
Set[*@config.fields]
|
41
|
+
else
|
42
|
+
# use all the keys of the flattened record hash
|
43
|
+
Set[*records.map(&:keys).flatten.map(&:to_s).uniq]
|
44
|
+
end
|
45
|
+
|
46
|
+
headers = headers.delete_if { |header| header.end_with?(*@config.fields_exclude) }
|
47
|
+
headers = headers.first(@config.fields_limit) if @config.fields_limit
|
48
|
+
|
49
|
+
headers.to_a.map(&:to_sym)
|
50
|
+
end
|
28
51
|
end
|
29
52
|
end
|
30
53
|
end
|
31
54
|
|
32
55
|
require_relative 'csv_loader'
|
56
|
+
require_relative 'json_loader'
|
33
57
|
require_relative 'rest_loader'
|
34
|
-
require_relative 'stdout_loader'
|
35
58
|
require_relative 'table_loader'
|
@@ -9,51 +9,38 @@ module Chronicle
|
|
9
9
|
r.description = 'an ASCII table'
|
10
10
|
end
|
11
11
|
|
12
|
-
setting :fields_limit, default: nil
|
13
|
-
setting :fields_exclude, default: ['lids', 'type']
|
14
|
-
setting :fields_include, default: []
|
15
12
|
setting :truncate_values_at, default: 40
|
16
13
|
setting :table_renderer, default: :basic
|
14
|
+
setting :fields_exclude, default: ['lids', 'type']
|
15
|
+
setting :header_row, default: true
|
17
16
|
|
18
17
|
def load(record)
|
19
|
-
|
20
|
-
@records << record.to_h_flattened
|
18
|
+
records << record.to_h_flattened
|
21
19
|
end
|
22
20
|
|
23
21
|
def finish
|
24
|
-
return if
|
22
|
+
return if records.empty?
|
25
23
|
|
26
|
-
headers = build_headers(
|
27
|
-
rows = build_rows(
|
24
|
+
headers = build_headers(records)
|
25
|
+
rows = build_rows(records, headers)
|
28
26
|
|
29
|
-
@table = TTY::Table.new(header: headers, rows: rows)
|
27
|
+
@table = TTY::Table.new(header: (headers if @config.header_row), rows: rows)
|
30
28
|
puts @table.render(
|
31
29
|
@config.table_renderer.to_sym,
|
32
30
|
padding: [0, 2, 0, 0]
|
33
31
|
)
|
34
32
|
end
|
35
33
|
|
36
|
-
|
37
|
-
|
38
|
-
def build_headers(records)
|
39
|
-
headers =
|
40
|
-
if @config.fields_include.any?
|
41
|
-
Set[*@config.fields_include]
|
42
|
-
else
|
43
|
-
# use all the keys of the flattened record hash
|
44
|
-
Set[*records.map(&:keys).flatten.map(&:to_s).uniq]
|
45
|
-
end
|
46
|
-
|
47
|
-
headers = headers.delete_if { |header| header.end_with?(*@config.fields_exclude) } if @config.fields_exclude.any?
|
48
|
-
headers = headers.first(@config.fields_limit) if @config.fields_limit
|
49
|
-
|
50
|
-
headers.to_a.map(&:to_sym)
|
34
|
+
def records
|
35
|
+
@records ||= []
|
51
36
|
end
|
52
37
|
|
38
|
+
private
|
39
|
+
|
53
40
|
def build_rows(records, headers)
|
54
41
|
records.map do |record|
|
55
|
-
values = record.values_at(*headers).map{|value| value.to_s }
|
56
|
-
|
42
|
+
values = record.transform_keys(&:to_sym).values_at(*headers).map{|value| value.to_s }
|
43
|
+
values = values.map { |value| force_utf8(value) }
|
57
44
|
if @config.truncate_values_at
|
58
45
|
values = values.map{ |value| value.truncate(@config.truncate_values_at) }
|
59
46
|
end
|
data/lib/chronicle/etl/logger.rb
CHANGED
@@ -8,11 +8,11 @@ module Chronicle
|
|
8
8
|
WARN = 2
|
9
9
|
ERROR = 3
|
10
10
|
FATAL = 4
|
11
|
+
SILENT = 5
|
11
12
|
|
12
13
|
attr_accessor :log_level
|
13
14
|
|
14
15
|
@log_level = INFO
|
15
|
-
@destination = $stderr
|
16
16
|
|
17
17
|
def output message, level
|
18
18
|
return unless level >= @log_level
|
@@ -20,10 +20,14 @@ module Chronicle
|
|
20
20
|
if @progress_bar
|
21
21
|
@progress_bar.log(message)
|
22
22
|
else
|
23
|
-
|
23
|
+
$stderr.puts(message)
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
27
|
+
def fatal(message)
|
28
|
+
output(message, FATAL)
|
29
|
+
end
|
30
|
+
|
27
31
|
def error(message)
|
28
32
|
output(message, ERROR)
|
29
33
|
end
|
@@ -5,6 +5,9 @@ module Chronicle
|
|
5
5
|
module Models
|
6
6
|
# Represents a record that's been transformed by a Transformer and
|
7
7
|
# ready to be loaded. Loosely based on ActiveModel.
|
8
|
+
#
|
9
|
+
# @todo Experiment with just mixing in ActiveModel instead of this
|
10
|
+
# this reimplementation
|
8
11
|
class Base
|
9
12
|
ATTRIBUTES = [:provider, :provider_id, :lat, :lng, :metadata].freeze
|
10
13
|
ASSOCIATIONS = [].freeze
|
@@ -5,13 +5,19 @@ module Chronicle
|
|
5
5
|
module Models
|
6
6
|
class Entity < Chronicle::ETL::Models::Base
|
7
7
|
TYPE = 'entities'.freeze
|
8
|
-
ATTRIBUTES = [:title, :body, :represents, :slug, :myself, :metadata].freeze
|
8
|
+
ATTRIBUTES = [:title, :body, :provider_url, :represents, :slug, :myself, :metadata].freeze
|
9
|
+
|
10
|
+
# TODO: This desperately needs a validation system
|
9
11
|
ASSOCIATIONS = [
|
12
|
+
:involvements, # inverse of activity's `involved`
|
13
|
+
|
10
14
|
:attachments,
|
11
15
|
:abouts,
|
16
|
+
:aboutables, # inverse of above
|
12
17
|
:depicts,
|
13
18
|
:consumers,
|
14
|
-
:contains
|
19
|
+
:contains,
|
20
|
+
:containers # inverse of above
|
15
21
|
].freeze # TODO: add these to reflect Chronicle Schema
|
16
22
|
|
17
23
|
attr_accessor(*ATTRIBUTES, *ASSOCIATIONS)
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'chronicle/etl/models/base'
|
2
|
+
|
3
|
+
module Chronicle
|
4
|
+
module ETL
|
5
|
+
module Models
|
6
|
+
# A record from an extraction with no processing or normalization applied
|
7
|
+
class Raw
|
8
|
+
TYPE = 'raw'
|
9
|
+
|
10
|
+
attr_accessor :raw_data
|
11
|
+
|
12
|
+
def initialize(raw_data)
|
13
|
+
@raw_data = raw_data
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_h
|
17
|
+
@raw_data.to_h
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_h_flattened
|
21
|
+
Chronicle::ETL::Utils::HashUtilities.flatten_hash(to_h)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rubygems/command'
|
3
|
+
require 'rubygems/commands/install_command'
|
4
|
+
require 'rubygems/uninstaller'
|
5
|
+
|
6
|
+
module Chronicle
|
7
|
+
module ETL
|
8
|
+
module Registry
|
9
|
+
# Responsible for managing plugins available to chronicle-etl
|
10
|
+
#
|
11
|
+
# @todo Better validation for whether a gem is actually a plugin
|
12
|
+
# @todo Add ways to load a plugin that don't require a gem on rubygems.org
|
13
|
+
module PluginRegistry
|
14
|
+
# Does this plugin exist?
|
15
|
+
def self.exists?(name)
|
16
|
+
# TODO: implement this. Could query rubygems.org or have a
|
17
|
+
# hardcoded approved list
|
18
|
+
true
|
19
|
+
end
|
20
|
+
|
21
|
+
# All versions of all plugins currently installed
|
22
|
+
def self.all_installed
|
23
|
+
# TODO: add check for chronicle-etl dependency
|
24
|
+
Gem::Specification.filter { |s| s.name.match(/^chronicle-/) && s.name != "chronicle-etl" }
|
25
|
+
end
|
26
|
+
|
27
|
+
# Latest version of each installed plugin
|
28
|
+
def self.all_installed_latest
|
29
|
+
all_installed.group_by(&:name)
|
30
|
+
.transform_values { |versions| versions.sort_by(&:version).reverse.first }
|
31
|
+
.values
|
32
|
+
end
|
33
|
+
|
34
|
+
# Activate a plugin with given name by `require`ing it
|
35
|
+
def self.activate(name)
|
36
|
+
# By default, activates the latest available version of a gem
|
37
|
+
# so don't have to run Kernel#gem separately
|
38
|
+
require "chronicle/#{name}"
|
39
|
+
rescue Gem::ConflictError => e
|
40
|
+
# TODO: figure out if there's more we can do here
|
41
|
+
raise Chronicle::ETL::PluginConflictError.new(name), "Plugin '#{name}' couldn't be loaded. #{e.message}"
|
42
|
+
rescue LoadError => e
|
43
|
+
raise Chronicle::ETL::PluginLoadError.new(name), "Plugin '#{name}' couldn't be loaded" if exists?(name)
|
44
|
+
|
45
|
+
raise Chronicle::ETL::PluginNotAvailableError.new(name), "Plugin #{name} doesn't exist"
|
46
|
+
end
|
47
|
+
|
48
|
+
# Install a plugin to local gems
|
49
|
+
def self.install(name)
|
50
|
+
gem_name = "chronicle-#{name}"
|
51
|
+
raise(Chronicle::ETL::PluginNotAvailableError.new(gem_name), "Plugin #{name} doesn't exist") unless exists?(gem_name)
|
52
|
+
|
53
|
+
Gem::DefaultUserInteraction.ui = Gem::SilentUI.new
|
54
|
+
Gem.install(gem_name)
|
55
|
+
|
56
|
+
activate(name)
|
57
|
+
rescue Gem::UnsatisfiableDependencyError
|
58
|
+
# TODO: we need to catch a lot more than this here
|
59
|
+
raise Chronicle::ETL::PluginNotAvailableError.new(name), "Plugin #{name} could not be installed."
|
60
|
+
end
|
61
|
+
|
62
|
+
# Uninstall a plugin
|
63
|
+
def self.uninstall(name)
|
64
|
+
gem_name = "chronicle-#{name}"
|
65
|
+
Gem::DefaultUserInteraction.ui = Gem::SilentUI.new
|
66
|
+
uninstaller = Gem::Uninstaller.new(gem_name)
|
67
|
+
uninstaller.uninstall
|
68
|
+
rescue Gem::InstallError
|
69
|
+
# TODO: strengthen this exception handling
|
70
|
+
raise(Chronicle::ETL::PluginError.new(name), "Plugin #{name} wasn't uninstalled")
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -20,28 +20,40 @@ module Chronicle
|
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
|
-
def
|
24
|
-
|
25
|
-
Gem.install(gem_name)
|
23
|
+
def register connector
|
24
|
+
connectors << connector
|
26
25
|
end
|
27
26
|
|
28
|
-
def
|
27
|
+
def connectors
|
29
28
|
@connectors ||= []
|
30
|
-
@connectors << connector
|
31
29
|
end
|
32
30
|
|
33
31
|
def find_by_phase_and_identifier(phase, identifier)
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
32
|
+
# Simple case: built in connector
|
33
|
+
connector = connectors.find { |c| c.phase == phase && c.identifier == identifier }
|
34
|
+
return connector if connector
|
35
|
+
|
36
|
+
# if not available in built-in connectors, try to activate a
|
37
|
+
# relevant plugin and try again
|
38
|
+
if identifier.include?(":")
|
39
|
+
plugin, name = identifier.split(":")
|
40
|
+
else
|
41
|
+
# This case handles the case where the identifier is a
|
42
|
+
# shorthand (ie `imessage`) because there's only one default
|
43
|
+
# connector.
|
44
|
+
plugin = identifier
|
39
45
|
end
|
40
|
-
connector || raise(ConnectorNotAvailableError.new("Connector '#{identifier}' not found"))
|
41
|
-
end
|
42
46
|
|
43
|
-
|
44
|
-
|
47
|
+
PluginRegistry.activate(plugin)
|
48
|
+
|
49
|
+
candidates = connectors.select { |c| c.phase == phase && c.plugin == plugin }
|
50
|
+
# if no name given, just use first connector with right phase/plugin
|
51
|
+
# TODO: set up a property for connectors to specify that they're the
|
52
|
+
# default connector for the plugin
|
53
|
+
candidates = candidates.select { |c| c.identifier == name } if name
|
54
|
+
connector = candidates.first
|
55
|
+
|
56
|
+
connector || raise(ConnectorNotAvailableError, "Connector '#{identifier}' not found")
|
45
57
|
end
|
46
58
|
end
|
47
59
|
end
|
@@ -50,3 +62,4 @@ end
|
|
50
62
|
|
51
63
|
require_relative 'self_registering'
|
52
64
|
require_relative 'connector_registration'
|
65
|
+
require_relative 'plugin_registry'
|
data/lib/chronicle/etl/runner.rb
CHANGED
@@ -8,19 +8,41 @@ class Chronicle::ETL::Runner
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def run!
|
11
|
-
|
12
|
-
|
11
|
+
validate_job
|
12
|
+
instantiate_connectors
|
13
|
+
prepare_job
|
14
|
+
prepare_ui
|
15
|
+
run_extraction
|
16
|
+
finish_job
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def validate_job
|
22
|
+
@job.job_definition.validate!
|
23
|
+
end
|
13
24
|
|
25
|
+
def instantiate_connectors
|
26
|
+
@extractor = @job.instantiate_extractor
|
27
|
+
@loader = @job.instantiate_loader
|
28
|
+
end
|
29
|
+
|
30
|
+
def prepare_job
|
31
|
+
Chronicle::ETL::Logger.info(tty_log_job_start)
|
14
32
|
@job_logger.start
|
15
|
-
loader.start
|
33
|
+
@loader.start
|
34
|
+
@extractor.prepare
|
35
|
+
end
|
16
36
|
|
17
|
-
|
18
|
-
total = extractor.results_count
|
37
|
+
def prepare_ui
|
38
|
+
total = @extractor.results_count
|
19
39
|
@progress_bar = Chronicle::ETL::Utils::ProgressBar.new(title: 'Running job', total: total)
|
20
40
|
Chronicle::ETL::Logger.attach_to_progress_bar(@progress_bar)
|
41
|
+
end
|
21
42
|
|
22
|
-
|
23
|
-
|
43
|
+
# TODO: refactor this further
|
44
|
+
def run_extraction
|
45
|
+
@extractor.extract do |extraction|
|
24
46
|
unless extraction.is_a?(Chronicle::ETL::Extraction)
|
25
47
|
raise Chronicle::ETL::RunnerTypeError, "Extracted should be a Chronicle::ETL::Extraction"
|
26
48
|
end
|
@@ -28,14 +50,10 @@ class Chronicle::ETL::Runner
|
|
28
50
|
transformer = @job.instantiate_transformer(extraction)
|
29
51
|
record = transformer.transform
|
30
52
|
|
31
|
-
unless record.is_a?(Chronicle::ETL::Models::Base)
|
32
|
-
raise Chronicle::ETL::RunnerTypeError, "Transformed data should be a type of Chronicle::ETL::Models"
|
33
|
-
end
|
34
|
-
|
35
53
|
Chronicle::ETL::Logger.info(tty_log_transformation(transformer))
|
36
54
|
@job_logger.log_transformation(transformer)
|
37
55
|
|
38
|
-
loader.load(record) unless @job.dry_run?
|
56
|
+
@loader.load(record) unless @job.dry_run?
|
39
57
|
rescue Chronicle::ETL::TransformationError => e
|
40
58
|
Chronicle::ETL::Logger.error(tty_log_transformation_failure(e))
|
41
59
|
ensure
|
@@ -43,22 +61,22 @@ class Chronicle::ETL::Runner
|
|
43
61
|
end
|
44
62
|
|
45
63
|
@progress_bar.finish
|
46
|
-
loader.finish
|
64
|
+
@loader.finish
|
47
65
|
@job_logger.finish
|
48
66
|
rescue Interrupt
|
49
67
|
Chronicle::ETL::Logger.error("\n#{'Job interrupted'.red}")
|
50
68
|
@job_logger.error
|
51
69
|
rescue StandardError => e
|
52
70
|
raise e
|
53
|
-
|
71
|
+
end
|
72
|
+
|
73
|
+
def finish_job
|
54
74
|
@job_logger.save
|
55
|
-
@progress_bar
|
75
|
+
@progress_bar&.finish
|
56
76
|
Chronicle::ETL::Logger.detach_from_progress_bar
|
57
77
|
Chronicle::ETL::Logger.info(tty_log_completion)
|
58
78
|
end
|
59
79
|
|
60
|
-
private
|
61
|
-
|
62
80
|
def tty_log_job_start
|
63
81
|
output = "Beginning job "
|
64
82
|
output += "'#{@job.name}'".bold if @job.name
|
@@ -1,6 +1,12 @@
|
|
1
1
|
module Chronicle
|
2
2
|
module ETL
|
3
3
|
class JSONAPISerializer < Chronicle::ETL::Serializer
|
4
|
+
def initialize(*args)
|
5
|
+
super
|
6
|
+
|
7
|
+
raise(SerializationError, "Record must be a subclass of Chronicle::ETL::Model::Base") unless @record.is_a?(Chronicle::ETL::Models::Base)
|
8
|
+
end
|
9
|
+
|
4
10
|
def serializable_hash
|
5
11
|
@record
|
6
12
|
.identifier_hash
|
data/lib/chronicle/etl.rb
CHANGED
@@ -3,23 +3,30 @@ require_relative 'etl/config'
|
|
3
3
|
require_relative 'etl/configurable'
|
4
4
|
require_relative 'etl/exceptions'
|
5
5
|
require_relative 'etl/extraction'
|
6
|
-
require_relative 'etl/extractors/extractor'
|
7
6
|
require_relative 'etl/job_definition'
|
8
7
|
require_relative 'etl/job_log'
|
9
8
|
require_relative 'etl/job_logger'
|
10
9
|
require_relative 'etl/job'
|
11
|
-
require_relative 'etl/loaders/loader'
|
12
10
|
require_relative 'etl/logger'
|
13
11
|
require_relative 'etl/models/activity'
|
14
12
|
require_relative 'etl/models/attachment'
|
15
13
|
require_relative 'etl/models/base'
|
14
|
+
require_relative 'etl/models/raw'
|
16
15
|
require_relative 'etl/models/entity'
|
17
|
-
require_relative 'etl/models/generic'
|
18
16
|
require_relative 'etl/runner'
|
19
17
|
require_relative 'etl/serializers/serializer'
|
20
|
-
require_relative 'etl/transformers/transformer'
|
21
18
|
require_relative 'etl/utils/binary_attachments'
|
22
19
|
require_relative 'etl/utils/hash_utilities'
|
23
20
|
require_relative 'etl/utils/text_recognition'
|
24
21
|
require_relative 'etl/utils/progress_bar'
|
25
22
|
require_relative 'etl/version'
|
23
|
+
|
24
|
+
require_relative 'etl/extractors/extractor'
|
25
|
+
require_relative 'etl/loaders/loader'
|
26
|
+
require_relative 'etl/transformers/transformer'
|
27
|
+
|
28
|
+
begin
|
29
|
+
require 'pry'
|
30
|
+
rescue LoadError
|
31
|
+
# Pry not available
|
32
|
+
end
|