metacrunch 2.2.3 → 3.0.0
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/.travis.yml +2 -1
- data/Gemfile +11 -13
- data/License.txt +1 -1
- data/Readme.md +139 -2
- data/bin/console +9 -6
- data/exe/metacrunch +1 -2
- data/lib/metacrunch/cli.rb +62 -14
- data/lib/metacrunch/db/reader.rb +27 -0
- data/lib/metacrunch/db/writer.rb +23 -0
- data/lib/metacrunch/db.rb +8 -0
- data/lib/metacrunch/fs/entry.rb +17 -0
- data/lib/metacrunch/{file_reader.rb → fs/reader.rb} +9 -10
- data/lib/metacrunch/fs.rb +6 -0
- data/lib/metacrunch/job/buffer.rb +26 -0
- data/lib/metacrunch/job/dsl/option_support.rb +102 -0
- data/lib/metacrunch/job/dsl.rb +42 -0
- data/lib/metacrunch/job.rb +149 -0
- data/lib/metacrunch/test_utils/dummy_callable.rb +14 -0
- data/lib/metacrunch/test_utils/dummy_destination.rb +21 -0
- data/lib/metacrunch/test_utils/dummy_source.rb +22 -0
- data/lib/metacrunch/test_utils.rb +7 -0
- data/lib/metacrunch/version.rb +1 -1
- data/lib/metacrunch.rb +14 -27
- data/metacrunch.gemspec +5 -10
- metadata +24 -144
- data/lib/metacrunch/cli/base.rb +0 -29
- data/lib/metacrunch/cli/command_definition.rb +0 -41
- data/lib/metacrunch/cli/command_registry.rb +0 -17
- data/lib/metacrunch/cli/main.rb +0 -16
- data/lib/metacrunch/command.rb +0 -27
- data/lib/metacrunch/file/reader/file_system_fetcher.rb +0 -21
- data/lib/metacrunch/file/reader/plain_file_reader.rb +0 -33
- data/lib/metacrunch/file/reader/scp_fetcher.rb +0 -56
- data/lib/metacrunch/file/reader/tar_file_reader.rb +0 -37
- data/lib/metacrunch/file/reader/zip_file_reader.rb +0 -30
- data/lib/metacrunch/file/reader.rb +0 -72
- data/lib/metacrunch/file/writer/plain_file_writer.rb +0 -19
- data/lib/metacrunch/file/writer/tar_file_writer.rb +0 -26
- data/lib/metacrunch/file/writer/zip_file_writer.rb +0 -29
- data/lib/metacrunch/file/writer.rb +0 -26
- data/lib/metacrunch/file.rb +0 -24
- data/lib/metacrunch/file_reader_entry.rb +0 -21
- data/lib/metacrunch/file_writer.rb +0 -40
- data/lib/metacrunch/hash.rb +0 -51
- data/lib/metacrunch/parallel.rb +0 -69
- data/lib/metacrunch/processor.rb +0 -10
- data/lib/metacrunch/snr/field.rb +0 -31
- data/lib/metacrunch/snr/section.rb +0 -74
- data/lib/metacrunch/snr.rb +0 -117
- data/lib/metacrunch/tar_writer.rb +0 -26
- data/lib/metacrunch/transformator/transformation/step.rb +0 -45
- data/lib/metacrunch/transformator/transformation.rb +0 -48
- data/lib/metacrunch/transformator.rb +0 -5
- data/lib/metacrunch/transformer/helper.rb +0 -29
- data/lib/metacrunch/transformer/step.rb +0 -37
- data/lib/metacrunch/transformer.rb +0 -38
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 657c0cedb16066bee69a76a30a0e02783e5d0209
|
4
|
+
data.tar.gz: 7c0290c94a11af5daf8bfb40e0b0abab3f92c8df
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ee0834cbe3f70238f1c5f7fbaf37284c0c7adedbdafe4c01e7b31819cfa8274585a4cd8407fd5cc35ca32c033f0944c86f872d09d8ee5d56ba22d48435898d4d
|
7
|
+
data.tar.gz: d9663a2b36247dcd20721e9e2016bc3f1d42dae47aa89acc671fb112437c925779bf5949edfb5409e3e6ff3aa2612216c93468847deb51ebc8b4ae6da064eb97
|
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
@@ -1,25 +1,23 @@
|
|
1
1
|
source "https://rubygems.org"
|
2
2
|
|
3
|
-
# Specify your gem's dependencies in your gemspec
|
4
3
|
gemspec
|
5
4
|
|
6
5
|
group :development do
|
7
|
-
gem "bundler"
|
8
|
-
gem "
|
9
|
-
gem "
|
10
|
-
gem "
|
11
|
-
gem "
|
6
|
+
gem "bundler", ">= 1.7"
|
7
|
+
gem "rake", ">= 11.1"
|
8
|
+
gem "rspec", ">= 3.0.0", "< 4.0.0"
|
9
|
+
gem "simplecov", ">= 0.11.0"
|
10
|
+
gem "sqlite3", ">= 1.3.11", platform: :ruby
|
11
|
+
gem "jdbc-sqlite3", ">= 3.8", platform: :jruby
|
12
12
|
|
13
13
|
if !ENV["CI"]
|
14
|
-
gem "hashdiff"
|
15
|
-
gem "pry",
|
16
|
-
gem "pry-
|
17
|
-
gem "pry-
|
18
|
-
gem "pry-stack_explorer", "~> 0.4.9.1"
|
19
|
-
gem "pry-syntax-hacks", "~> 0.0.6"
|
14
|
+
gem "hashdiff", ">= 0.3.0", platform: :ruby
|
15
|
+
gem "pry-byebug", ">= 3.3.0", platform: :ruby
|
16
|
+
gem "pry-rescue", ">= 1.4.2", platform: :ruby
|
17
|
+
gem "pry-state", ">= 0.1.7", platform: :ruby
|
20
18
|
end
|
21
19
|
end
|
22
20
|
|
23
21
|
group :test do
|
24
|
-
gem "codeclimate-test-reporter", require: nil
|
22
|
+
gem "codeclimate-test-reporter", ">= 0.5.0", require: nil
|
25
23
|
end
|
data/License.txt
CHANGED
data/Readme.md
CHANGED
@@ -1,7 +1,144 @@
|
|
1
|
-
|
1
|
+
metacrunch
|
2
|
+
==========
|
2
3
|
|
3
4
|
[](http://badge.fury.io/rb/metacrunch)
|
4
5
|
[](https://codeclimate.com/github/ubpb/metacrunch)
|
5
6
|
[](https://travis-ci.org/ubpb/metacrunch)
|
6
7
|
|
7
|
-
|
8
|
+
metacrunch is a simple and lightweight data processing and ETL ([Extract-Transform-Load](http://en.wikipedia.org/wiki/Extract,_transform,_load))
|
9
|
+
toolkit for Ruby.
|
10
|
+
|
11
|
+
|
12
|
+
Installation
|
13
|
+
------------
|
14
|
+
|
15
|
+
```
|
16
|
+
$ gem install metacrunch
|
17
|
+
```
|
18
|
+
|
19
|
+
|
20
|
+
Create ETL jobs
|
21
|
+
---------------
|
22
|
+
|
23
|
+
The basic idea behind an ETL job in metacrunch is the concept of a data processing pipeline. Each ETL job reads data from one or more **sources** (extract step), runs one or more **transformations** (transform step) on the data and finally writes the transformed data back to one or more **destinations** (load step).
|
24
|
+
|
25
|
+
metacrunch provides you with a simple DSL to define such ETL jobs. Just create a text file with the extension `.metacrunch`. Note: The extension doesn't really matter but you should avoid `.rb` to not loading them by mistake from another Ruby component.
|
26
|
+
|
27
|
+
Let's take a look at an example. For a collection of working examples check out our [metacrunch-demo](https://github.com/ubpb/metacrunch-demo) repo.
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
# File: my_etl_job.metacrunch
|
31
|
+
|
32
|
+
# Every metacrunch job file is a regular Ruby file. So you can always use regular Ruby
|
33
|
+
# stuff like declaring methods
|
34
|
+
def my_helper
|
35
|
+
# ...
|
36
|
+
end
|
37
|
+
|
38
|
+
# ... declaring classes
|
39
|
+
class MyHelper
|
40
|
+
# ...
|
41
|
+
end
|
42
|
+
|
43
|
+
# ... declaring variables
|
44
|
+
foo = "bar"
|
45
|
+
|
46
|
+
# ... or loading other ruby files
|
47
|
+
require_relative "./some/other/ruby/file"
|
48
|
+
|
49
|
+
# Declare a source (use a build-in or 3rd party source or implement it – see notes below).
|
50
|
+
# At least one source is required to allow the job to run.
|
51
|
+
source MySource.new
|
52
|
+
# ... maybe another one. Sources are processed in the order they are defined.
|
53
|
+
source MyOtherSource.new
|
54
|
+
|
55
|
+
# Declare a destination (use a build-in or 3rd party destination or implement it – see notes below).
|
56
|
+
# Technically a destination is optional, but a job that doesn't store it's
|
57
|
+
# output doesn't really makes sense.
|
58
|
+
destination MyDestination.new
|
59
|
+
# ... you can have more destinations if you like
|
60
|
+
destination MyOtherDestination.new
|
61
|
+
|
62
|
+
# To process data use the #transformation hook.
|
63
|
+
transformation do |data|
|
64
|
+
# Called for each data object that has been put in the pipeline by a source.
|
65
|
+
|
66
|
+
# Do your data transformation process here.
|
67
|
+
|
68
|
+
# You must return the data to keep it in the pipeline. Dismiss the
|
69
|
+
# data conditionally by returning nil.
|
70
|
+
data
|
71
|
+
end
|
72
|
+
|
73
|
+
# Instead of passing a block to #transformation you can pass a
|
74
|
+
# `callable` object (an object responding to #call).
|
75
|
+
transformation Proc.new {
|
76
|
+
# Procs and Lambdas responds to #call
|
77
|
+
}
|
78
|
+
|
79
|
+
# MyTransformation defines #call
|
80
|
+
transformation MyTransformation.new
|
81
|
+
|
82
|
+
# To run arbitrary code before the first transformation use the #pre_process hook.
|
83
|
+
pre_process do
|
84
|
+
# Called before the first transformation
|
85
|
+
end
|
86
|
+
|
87
|
+
# To run arbitrary code after the last transformation use the #post_process hook.
|
88
|
+
post_process do
|
89
|
+
# Called after the last transformation
|
90
|
+
end
|
91
|
+
|
92
|
+
# Instead of passing a block to #pre_process or #post_process you can pass a
|
93
|
+
# `callable` object (an object responding to #call).
|
94
|
+
pre_process Proc.new {
|
95
|
+
# Procs and Lambdas responds to #call
|
96
|
+
}
|
97
|
+
|
98
|
+
# MyCallable class defines #call
|
99
|
+
post_process MyCallable.new
|
100
|
+
|
101
|
+
```
|
102
|
+
|
103
|
+
|
104
|
+
Run ETL jobs
|
105
|
+
------------
|
106
|
+
|
107
|
+
metacrunch comes with a handy command line tool. In your terminal just call
|
108
|
+
|
109
|
+
|
110
|
+
```
|
111
|
+
$ metacrunch run my_etl_job.metacrunch
|
112
|
+
```
|
113
|
+
|
114
|
+
to run the job.
|
115
|
+
|
116
|
+
Implementing sources
|
117
|
+
--------------------
|
118
|
+
|
119
|
+
TBD.
|
120
|
+
|
121
|
+
Implementing transformations
|
122
|
+
----------------------------
|
123
|
+
|
124
|
+
TBD.
|
125
|
+
|
126
|
+
Implementing writers
|
127
|
+
---------------------
|
128
|
+
|
129
|
+
TBD.
|
130
|
+
|
131
|
+
Defining job dependencies
|
132
|
+
-------------------------
|
133
|
+
|
134
|
+
TBD.
|
135
|
+
|
136
|
+
Defining job options
|
137
|
+
--------------------
|
138
|
+
|
139
|
+
TBD.
|
140
|
+
|
141
|
+
License
|
142
|
+
-------
|
143
|
+
|
144
|
+
metacrunch is available at [github](https://github.com/ubpb/metacrunch) under [MIT license](https://github.com/ubpb/metacrunch/blob/master/License.txt).
|
data/bin/console
CHANGED
@@ -1,11 +1,14 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
-
|
3
2
|
require "bundler/setup"
|
4
3
|
require "metacrunch"
|
5
4
|
|
6
|
-
|
7
|
-
|
5
|
+
begin
|
6
|
+
require "pry"
|
7
|
+
rescue LoadError ; end
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
if defined?(Pry)
|
10
|
+
Pry.start
|
11
|
+
else
|
12
|
+
require "irb"
|
13
|
+
IRB.start
|
14
|
+
end
|
data/exe/metacrunch
CHANGED
data/lib/metacrunch/cli.rb
CHANGED
@@ -1,26 +1,74 @@
|
|
1
1
|
module Metacrunch
|
2
2
|
class Cli
|
3
|
-
|
4
|
-
require_relative "./cli/base"
|
5
|
-
require_relative "./cli/command_registry"
|
6
|
-
require_relative "./cli/command_definition"
|
3
|
+
ARGS_SEPERATOR = "@@"
|
7
4
|
|
8
|
-
def
|
9
|
-
|
5
|
+
def run
|
6
|
+
init_commander!
|
7
|
+
init_run_command!
|
8
|
+
run_commander!
|
10
9
|
end
|
11
10
|
|
12
|
-
|
13
|
-
|
14
|
-
|
11
|
+
private
|
12
|
+
def commander
|
13
|
+
@commander ||= Commander::Runner.new(metacrunch_args)
|
14
|
+
end
|
15
|
+
|
16
|
+
def init_commander!
|
17
|
+
commander.program :name, "metacrunch"
|
18
|
+
commander.program :version, Metacrunch::VERSION
|
19
|
+
commander.program :description, "Data processing and ETL toolkit for Ruby."
|
20
|
+
commander.default_command :help
|
21
|
+
end
|
22
|
+
|
23
|
+
def run_commander!
|
24
|
+
commander.run!
|
25
|
+
end
|
26
|
+
|
27
|
+
def init_run_command!
|
28
|
+
commander.command :run do |c|
|
29
|
+
c.syntax = "metacrunch run [options] FILE [@@ job_options]"
|
30
|
+
c.description = "Runs a metacrunch job description."
|
31
|
+
|
32
|
+
c.action do |filenames, program_options|
|
33
|
+
if filenames.empty?
|
34
|
+
say "You need to provide a job description file."
|
35
|
+
exit(1)
|
36
|
+
elsif filenames.count > 1
|
37
|
+
say "You must provide exactly one job description file."
|
38
|
+
else
|
39
|
+
filename = File.expand_path(filenames.first)
|
40
|
+
dir = File.dirname(filename)
|
15
41
|
|
16
|
-
|
17
|
-
yield(registry)
|
42
|
+
setup_bundler(dir)
|
18
43
|
|
19
|
-
|
20
|
-
|
44
|
+
Dir.chdir(dir) do
|
45
|
+
contents = File.read(filename)
|
46
|
+
context = Metacrunch::Job.define(contents, filename: filename, args: job_args)
|
47
|
+
context.run
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
21
51
|
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def metacrunch_args
|
55
|
+
index = ARGV.index(ARGS_SEPERATOR)
|
56
|
+
@metacrunch_args ||= index ? ARGV[0..index-1] : ARGV
|
57
|
+
end
|
22
58
|
|
23
|
-
|
59
|
+
def job_args
|
60
|
+
index = ARGV.index(ARGS_SEPERATOR)
|
61
|
+
@job_args ||= index ? ARGV[index+1..-1] : nil
|
24
62
|
end
|
63
|
+
|
64
|
+
def setup_bundler(dir)
|
65
|
+
ENV['BUNDLE_GEMFILE'] ||= File.join(dir, "Gemfile")
|
66
|
+
if File.exists?(ENV['BUNDLE_GEMFILE'])
|
67
|
+
puts "Using Gemfile `#{ENV['BUNDLE_GEMFILE']}`."
|
68
|
+
Bundler.setup
|
69
|
+
Bundler.require
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
25
73
|
end
|
26
74
|
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Metacrunch
|
2
|
+
class Db::Reader
|
3
|
+
|
4
|
+
def initialize(database_connection_or_url, dataset_proc, options = {})
|
5
|
+
@rows_per_fetch = options.delete(:rows_per_fetch) || 1000
|
6
|
+
|
7
|
+
@db = if database_connection_or_url.is_a?(String)
|
8
|
+
Sequel.connect(database_connection_or_url, options)
|
9
|
+
else
|
10
|
+
database_connection_or_url
|
11
|
+
end
|
12
|
+
|
13
|
+
@dataset = dataset_proc.call(@db)
|
14
|
+
end
|
15
|
+
|
16
|
+
def each(&block)
|
17
|
+
return enum_for(__method__) unless block_given?
|
18
|
+
|
19
|
+
@dataset.paged_each(rows_per_fetch: @rows_per_fetch) do |row|
|
20
|
+
yield(row)
|
21
|
+
end
|
22
|
+
|
23
|
+
self
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Metacrunch
|
2
|
+
class Db::Writer
|
3
|
+
|
4
|
+
def initialize(database_connection_or_url, dataset_proc, options = {})
|
5
|
+
@db = if database_connection_or_url.is_a?(String)
|
6
|
+
Sequel.connect(database_connection_or_url, options)
|
7
|
+
else
|
8
|
+
database_connection_or_url
|
9
|
+
end
|
10
|
+
|
11
|
+
@dataset = dataset_proc.call(@db)
|
12
|
+
end
|
13
|
+
|
14
|
+
def write(data)
|
15
|
+
@dataset.insert(data)
|
16
|
+
end
|
17
|
+
|
18
|
+
def close
|
19
|
+
@db.disconnect
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Metacrunch
|
2
|
+
class Fs::Entry
|
3
|
+
|
4
|
+
attr_reader :filename, :archive_filename, :contents
|
5
|
+
|
6
|
+
def initialize(filename:, archive_filename: nil, contents: nil)
|
7
|
+
@filename = filename
|
8
|
+
@archive_filename = archive_filename.presence
|
9
|
+
@contents = contents
|
10
|
+
end
|
11
|
+
|
12
|
+
def from_archive?
|
13
|
+
@archive_filename != nil
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
@@ -1,10 +1,9 @@
|
|
1
1
|
require "rubygems/package"
|
2
|
-
require_relative "./file_reader_entry"
|
3
2
|
|
4
3
|
module Metacrunch
|
5
|
-
class
|
4
|
+
class Fs::Reader
|
6
5
|
|
7
|
-
def initialize(filenames)
|
6
|
+
def initialize(filenames = nil)
|
8
7
|
@filenames = [*filenames].map{|f| f.presence}.compact
|
9
8
|
end
|
10
9
|
|
@@ -23,27 +22,27 @@ module Metacrunch
|
|
23
22
|
private
|
24
23
|
|
25
24
|
def is_archive?(filename)
|
26
|
-
filename.ends_with?(".tar") || filename.ends_with?(".tar.gz")
|
25
|
+
filename.ends_with?(".tar") || filename.ends_with?(".tar.gz") || filename.ends_with?(".tgz")
|
27
26
|
end
|
28
27
|
|
29
28
|
def is_gzip_file?(filename)
|
30
|
-
filename.ends_with?(".gz")
|
29
|
+
filename.ends_with?(".gz") || filename.ends_with?(".tgz")
|
31
30
|
end
|
32
31
|
|
33
32
|
def read_regular_file(filename, &block)
|
34
|
-
if
|
35
|
-
io = is_gzip_file?(filename) ? Zlib::GzipReader.open(filename) :
|
36
|
-
yield Entry.new(filename: filename, archive_filename: nil, contents: io.read)
|
33
|
+
if File.file?(filename)
|
34
|
+
io = is_gzip_file?(filename) ? Zlib::GzipReader.open(filename) : File.open(filename, "r")
|
35
|
+
yield Fs::Entry.new(filename: filename, archive_filename: nil, contents: io.read)
|
37
36
|
end
|
38
37
|
end
|
39
38
|
|
40
39
|
def read_archive(filename, &block)
|
41
|
-
io = is_gzip_file?(filename) ? Zlib::GzipReader.open(filename) :
|
40
|
+
io = is_gzip_file?(filename) ? Zlib::GzipReader.open(filename) : File.open(filename, "r")
|
42
41
|
tarReader = Gem::Package::TarReader.new(io)
|
43
42
|
|
44
43
|
tarReader.each do |_tar_entry|
|
45
44
|
if _tar_entry.file?
|
46
|
-
yield Entry.new(
|
45
|
+
yield Fs::Entry.new(
|
47
46
|
filename: filename,
|
48
47
|
archive_filename: _tar_entry.full_name,
|
49
48
|
contents: _tar_entry.read
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Metacrunch
|
2
|
+
class Job::Buffer
|
3
|
+
|
4
|
+
def initialize(size)
|
5
|
+
@size = size
|
6
|
+
end
|
7
|
+
|
8
|
+
def buffer(data)
|
9
|
+
storage << data
|
10
|
+
flush if storage.count >= @size
|
11
|
+
end
|
12
|
+
|
13
|
+
def flush
|
14
|
+
storage
|
15
|
+
ensure
|
16
|
+
@buffer = nil
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def storage
|
22
|
+
@buffer ||= []
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
module Metacrunch
|
2
|
+
class Job::Dsl::OptionSupport
|
3
|
+
|
4
|
+
def register_options(args, require_args: false, &block)
|
5
|
+
options = {}
|
6
|
+
registry.instance_eval(&block)
|
7
|
+
|
8
|
+
registry.each do |key, opt_def|
|
9
|
+
# Set default value
|
10
|
+
options[key] = opt_def[:default]
|
11
|
+
|
12
|
+
# Register with OptionParser
|
13
|
+
if opt_def[:args].present?
|
14
|
+
option = parser.define(*opt_def[:args]) { |value| options[key] = value }
|
15
|
+
|
16
|
+
option.desc << "REQUIRED" if opt_def[:required]
|
17
|
+
option.desc << "DEFAULT: #{opt_def[:default]}" if opt_def[:default].present?
|
18
|
+
|
19
|
+
parser_options[key] = option
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Finally parse CLI options with OptionParser
|
24
|
+
args = parser.parse(args || [])
|
25
|
+
|
26
|
+
# Make sure required options are present
|
27
|
+
ensure_required_options!(options)
|
28
|
+
|
29
|
+
# Make sure args are present if required
|
30
|
+
ensure_required_args!(args) if require_args
|
31
|
+
|
32
|
+
options
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def parser
|
38
|
+
@parser ||= OptionParser.new do |parser|
|
39
|
+
parser.banner = "Usage: metacrunch run [options] JOB_FILE @@ [job-options] [ARGS]\nJob options:"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def parser_options
|
44
|
+
@parser_options ||= {}
|
45
|
+
end
|
46
|
+
|
47
|
+
def registry
|
48
|
+
@registry ||= OptionRegistry.new
|
49
|
+
end
|
50
|
+
|
51
|
+
def ensure_required_options!(options)
|
52
|
+
registry.each do |key, opt_def|
|
53
|
+
if opt_def[:required] && options[key].blank?
|
54
|
+
long_option = parser_options[key].long.try(:[], 0)
|
55
|
+
short_option = parser_options[key].short.try(:[], 0)
|
56
|
+
|
57
|
+
puts "Error: Required job option `#{long_option || short_option}` missing."
|
58
|
+
puts parser.help
|
59
|
+
|
60
|
+
exit(1)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def ensure_required_args!(args)
|
66
|
+
if args.blank?
|
67
|
+
puts "Error: Required ARGS are missing."
|
68
|
+
puts parser.help
|
69
|
+
|
70
|
+
exit(1)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
class OptionRegistry
|
77
|
+
|
78
|
+
def add(name, *args, default: nil, required: false)
|
79
|
+
if default && required
|
80
|
+
raise ArgumentError, "You can't use `default` and `required` option at the same time."
|
81
|
+
end
|
82
|
+
|
83
|
+
options[name.to_sym] = {
|
84
|
+
args: args,
|
85
|
+
default: default,
|
86
|
+
required: required
|
87
|
+
}
|
88
|
+
end
|
89
|
+
|
90
|
+
def each(&block)
|
91
|
+
options.each(&block)
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
def options
|
97
|
+
@options ||= {}
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Metacrunch
|
2
|
+
class Job::Dsl
|
3
|
+
require_relative "dsl/option_support"
|
4
|
+
|
5
|
+
def initialize(job)
|
6
|
+
@_job = job
|
7
|
+
end
|
8
|
+
|
9
|
+
def source(source)
|
10
|
+
@_job.add_source(source)
|
11
|
+
end
|
12
|
+
|
13
|
+
def destination(destination)
|
14
|
+
@_job.add_destination(destination)
|
15
|
+
end
|
16
|
+
|
17
|
+
def pre_process(callable = nil, &block)
|
18
|
+
@_job.add_pre_process(callable, &block)
|
19
|
+
end
|
20
|
+
|
21
|
+
def post_process(callable = nil, &block)
|
22
|
+
@_job.add_post_process(callable, &block)
|
23
|
+
end
|
24
|
+
|
25
|
+
def transformation_buffer(size)
|
26
|
+
@_job.add_transformation_buffer(size)
|
27
|
+
end
|
28
|
+
|
29
|
+
def transformation(callable = nil, &block)
|
30
|
+
@_job.add_transformation(callable, &block)
|
31
|
+
end
|
32
|
+
|
33
|
+
def options(require_args: false, &block)
|
34
|
+
if block_given?
|
35
|
+
@_options = OptionSupport.new.register_options(@_job.args, require_args: require_args, &block)
|
36
|
+
else
|
37
|
+
@_options ||= {}
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|