advance 0.1.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 +7 -0
- data/.gitignore +9 -0
- data/.travis.yml +7 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +24 -0
- data/LICENSE.txt +21 -0
- data/README.md +152 -0
- data/Rakefile +10 -0
- data/advance.gemspec +35 -0
- data/bin/concat_csv.rb +31 -0
- data/bin/console +14 -0
- data/bin/csv_select.rb +21 -0
- data/bin/csv_split_on_change.rb +29 -0
- data/bin/setup +8 -0
- data/bin/split_csv.rb +21 -0
- data/lib/advance/version.rb +3 -0
- data/lib/advance.rb +153 -0
- metadata +127 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 0704814d6913c4e1e3bccbc24d697e2681ba71d7
|
4
|
+
data.tar.gz: 0408447d2efe199de361c058262065193341e964
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: cdb7d8f6c80d3f7df834d5eab82846c055bfc968d68d0df3ee863c08994adf2c3a09f50d79d42653119bf51d7c5407df6a0dc69832fbdc4be90d412f7f1441b2
|
7
|
+
data.tar.gz: e77444b0f5e04ed12f31df208c22cfbb6d2b2eed99b2310caa040d17a01c7dc438b030a36b69d89121a161ef4bfb644086453c35ca13c418cb4bf1939ceaa4dc
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
advance (0.1.0)
|
5
|
+
team_effort
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
minitest (5.10.1)
|
11
|
+
rake (10.4.2)
|
12
|
+
team_effort (1.0.0)
|
13
|
+
|
14
|
+
PLATFORMS
|
15
|
+
ruby
|
16
|
+
|
17
|
+
DEPENDENCIES
|
18
|
+
advance!
|
19
|
+
bundler (~> 1.16)
|
20
|
+
minitest (~> 5.0)
|
21
|
+
rake (~> 10.0)
|
22
|
+
|
23
|
+
BUNDLED WITH
|
24
|
+
1.16.6
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2019 janemacfarlane
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,152 @@
|
|
1
|
+
# Advance
|
2
|
+
|
3
|
+
Advance is a framework for building data transformation pipelines.
|
4
|
+
Advance allows you to concisely script your
|
5
|
+
data transformation process and to
|
6
|
+
incrementally build and easily debug that process.
|
7
|
+
Each data transformation is a step and the results of each
|
8
|
+
step become the input to the next step.
|
9
|
+
|
10
|
+
The artifacts of each step are preserved in step named directories.
|
11
|
+
When the results of a step are not right, just
|
12
|
+
adjust the Advance script, delete the step directory with the bad data and
|
13
|
+
rerun the script. Previously successful steps are skipped so the script
|
14
|
+
moves quickly to the incomplete step. Similarly, when steps fail the results
|
15
|
+
are preserved in directories prefixed with "tmp_". This isolates incomplete
|
16
|
+
step data and ensures that the step is re-processed when the problem is
|
17
|
+
resolved.
|
18
|
+
|
19
|
+
Advance scripts are easy to understand. They are ruby scripts,
|
20
|
+
composed of a series of function calls that invoke your scripts
|
21
|
+
or commands to transform your data. Each step is composed of a
|
22
|
+
step processing type function, followed by a
|
23
|
+
slug for the step, followed by the command or script. For example:
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
single :unzip_7z_raw_data_file, "7z x {previous_file}"
|
27
|
+
single :split_files, "split -l 10000 -a 3 {previous_file} gps_data_"
|
28
|
+
multi :add_local_time, "cat {file_path} | add_local_time.rb timestamp local_time US/Pacific > {file}"
|
29
|
+
# ...
|
30
|
+
```
|
31
|
+
|
32
|
+
The step processing functions are `single` and `multi`. `Single` applies the command
|
33
|
+
to the last output, which should be a single file. `Multi` speeds processing of multiple
|
34
|
+
files by doing working in parallel (via the [TeamEffort gem][1]).
|
35
|
+
|
36
|
+
[1]: https://rubygems.org/gems/team_effort
|
37
|
+
|
38
|
+
> _[Advance][2]: To help the progress of (something); to further._
|
39
|
+
|
40
|
+
[2]: https://en.wiktionary.org/wiki/advance
|
41
|
+
|
42
|
+
## Installation
|
43
|
+
|
44
|
+
Advance is meant to augment a standalone ruby script. The advance gem needs to be
|
45
|
+
available to your instance of ruby. Here are 2 techniques to make Advance available
|
46
|
+
to your script:
|
47
|
+
|
48
|
+
* simply install the gem:
|
49
|
+
|
50
|
+
$ gem install advance
|
51
|
+
|
52
|
+
* install [bundler][3], and add this ruby snippet to the beginning of your script:
|
53
|
+
|
54
|
+
[3]: https://rubygems.org/gems/bundler
|
55
|
+
|
56
|
+
```ruby
|
57
|
+
#!/usr/bin/env ruby
|
58
|
+
require "bundler/inline"
|
59
|
+
|
60
|
+
gemfile do
|
61
|
+
source "https://rubygems.org"
|
62
|
+
gem "advance"
|
63
|
+
end
|
64
|
+
```
|
65
|
+
|
66
|
+
## Usage
|
67
|
+
|
68
|
+
You will likely need multiple supporting scripts. Ideally you will
|
69
|
+
create your Advance script and your supporting scripts in a single directory.
|
70
|
+
|
71
|
+
Creating your Advance script is an incremental process. Start with a single
|
72
|
+
step, run the script and check the results. When the output is as you expect,
|
73
|
+
add the next step. After you add a step to your script you can simply rerun
|
74
|
+
the script. Previously successful steps are skipped and your script moves on
|
75
|
+
to the first incomplete step.
|
76
|
+
|
77
|
+
When the results are not what you expect, just delete the step directory with
|
78
|
+
the bad data, adjust your step, and rerun. Advance will rerun that step and
|
79
|
+
all subsequent steps.
|
80
|
+
|
81
|
+
Steps have 3 components:
|
82
|
+
|
83
|
+
* a step processing type (single or multi)
|
84
|
+
* a descriptive slug describing the step (as a ruby symbol)
|
85
|
+
* the command that transforms the data
|
86
|
+
|
87
|
+
Advance adds the bin dir of the Advance gem to PATH, so that you can invoke the
|
88
|
+
supporting advance scripts in your pipeline without specifying the full path
|
89
|
+
of the script. Advance also adds the path of your script to PATH so that you can
|
90
|
+
invoke scripts in the same directory as your main script without specifying
|
91
|
+
the full path of the script. Of course, you can invoke any script if the path
|
92
|
+
to the script is fully specified or the path is already on PATH.
|
93
|
+
|
94
|
+
**Specifying Script Input and Output**
|
95
|
+
|
96
|
+
Since your command is transforming data, you need a way to specify the input
|
97
|
+
file or directory and the output file name. Advance provides a few tokens
|
98
|
+
that can be inserted in the command string for this purpose:
|
99
|
+
|
100
|
+
* **{previous_file}** indicates the output file from the previous step when
|
101
|
+
the output of the previous step was a single output file. It is also used
|
102
|
+
to indicate the first file to be used and it finds that file in the current
|
103
|
+
working dir.
|
104
|
+
* **{file_path}** indicates an output file from the previous step when the
|
105
|
+
previous step generated multiple output files and the current step is a
|
106
|
+
`multi` step.
|
107
|
+
* **{file}** indicates an output file name, which is the basename from
|
108
|
+
{file_path}. Commands often process multiple files from previous steps,
|
109
|
+
generating multiple output files. Those output files are placed in the
|
110
|
+
step directory.
|
111
|
+
* **{previous_dir}** indicates the directory a previous step.
|
112
|
+
|
113
|
+
**Example Script**
|
114
|
+
|
115
|
+
```ruby
|
116
|
+
#!/usr/bin/env ruby
|
117
|
+
require "bundler/inline"
|
118
|
+
|
119
|
+
gemfile do
|
120
|
+
source "https://rubygems.org"
|
121
|
+
gem "advance"
|
122
|
+
end
|
123
|
+
|
124
|
+
ensure_bin_on_path # ensures the directory for this script is on
|
125
|
+
# the path so that related scripts can be referenced
|
126
|
+
# without paths
|
127
|
+
|
128
|
+
single :unzip_7z_raw_data_file, "7z x {previous_file}" # uses 7z to inflate a file in the current dir
|
129
|
+
single :split_files, "split -l 10000 -a 3 {previous_file} gps_data_" # split the file
|
130
|
+
multi :add_local_time, "cat {file_path} | add_local_time.rb timestamp local_time US/Pacific > {file}" # adds a local_time column to a csv
|
131
|
+
```
|
132
|
+
|
133
|
+
**Running Your Script**
|
134
|
+
|
135
|
+
When running your pipeline, it is helpful to have a directory with the single, initial file.
|
136
|
+
|
137
|
+
1. Move to your data directory with your single initial file.
|
138
|
+
2. invoke your script from there.
|
139
|
+
|
140
|
+
## Development
|
141
|
+
|
142
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
143
|
+
|
144
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
145
|
+
|
146
|
+
## Contributing
|
147
|
+
|
148
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/doctorjane/advance.
|
149
|
+
|
150
|
+
## License
|
151
|
+
|
152
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/advance.gemspec
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require "advance/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "advance"
|
8
|
+
spec.version = Advance::VERSION
|
9
|
+
spec.authors = ["janemacfarlane"]
|
10
|
+
spec.email = ["jfmacfarlane@lbl.gov"]
|
11
|
+
|
12
|
+
spec.summary = %q{A framework for building data transformation pipelines}
|
13
|
+
spec.description = %q{Advance allows you to concisely script your
|
14
|
+
data transformation process and to
|
15
|
+
incrementally build and easily debug that process.
|
16
|
+
Each data transformation is a step and the results of each
|
17
|
+
step become the input to the next step.
|
18
|
+
}
|
19
|
+
spec.homepage = "https://github.com/doctorjane/advance"
|
20
|
+
spec.license = "MIT"
|
21
|
+
|
22
|
+
# Specify which files should be added to the gem when it is released.
|
23
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
24
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
25
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
26
|
+
end
|
27
|
+
spec.bindir = "bin"
|
28
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
29
|
+
spec.require_paths = ["lib"]
|
30
|
+
|
31
|
+
spec.add_runtime_dependency "team_effort"
|
32
|
+
spec.add_development_dependency "bundler", "~> 1.16"
|
33
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
34
|
+
spec.add_development_dependency "minitest", "~> 5.0"
|
35
|
+
end
|
data/bin/concat_csv.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "find"
|
4
|
+
require "team_effort"
|
5
|
+
|
6
|
+
def do_cmd(cmd)
|
7
|
+
system cmd
|
8
|
+
status = $?
|
9
|
+
raise "'#{cmd}' failed with #{status}" if !status.success?
|
10
|
+
end
|
11
|
+
|
12
|
+
files_dir_path = ARGV[0]
|
13
|
+
output_file = ARGV[1]
|
14
|
+
files = Find.find(files_dir_path).reject { |p| FileTest.directory?(p) || p =~ %r(\b(stdout|stderr)$) }
|
15
|
+
|
16
|
+
# 1. capture the header from the first file
|
17
|
+
do_cmd "ghead -n 1 #{files.first} > header"
|
18
|
+
|
19
|
+
# 2. strip the header from all files
|
20
|
+
TeamEffort.work(files) do |file_path|
|
21
|
+
file = File.basename(file_path)
|
22
|
+
do_cmd "gtail -n +2 #{file_path} > #{file}"
|
23
|
+
end
|
24
|
+
|
25
|
+
# 3. concate the header and all files
|
26
|
+
tmp_files = files.map{|f| File.basename(f)}
|
27
|
+
(["header"] + tmp_files).each_slice(20) do |files_to_concat|
|
28
|
+
file_list = files_to_concat.join(' ')
|
29
|
+
do_cmd "gcat #{file_list} >> #{output_file}"
|
30
|
+
do_cmd "rm #{file_list}"
|
31
|
+
end
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "advance"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/csv_select.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'csv'
|
3
|
+
# $stderr.puts "#{__FILE__}:#{__LINE__}"
|
4
|
+
|
5
|
+
test_proc = eval "lambda {|row| #{ARGV.shift}}"
|
6
|
+
|
7
|
+
input = CSV.new(ARGF, :headers => true, :return_headers => true, :converters => :numeric)
|
8
|
+
output = CSV.new($stdout, :headers => true, :write_headers => true)
|
9
|
+
|
10
|
+
input.each.with_index do |row, index|
|
11
|
+
# $stderr.puts "#{index}: >>#{row.to_s.chomp}<<"
|
12
|
+
if row.header_row?
|
13
|
+
output << row
|
14
|
+
next
|
15
|
+
end
|
16
|
+
|
17
|
+
if test_proc.call(row)
|
18
|
+
output << row
|
19
|
+
next
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'csv'
|
3
|
+
|
4
|
+
def columns_changed?(previous_row, row, change_columns)
|
5
|
+
changed = false
|
6
|
+
change_columns.each do |column|
|
7
|
+
changed ||= previous_row[column] != row[column]
|
8
|
+
end
|
9
|
+
changed
|
10
|
+
end
|
11
|
+
|
12
|
+
def file_name_from_changed_columns(row, change_columns)
|
13
|
+
change_columns.map { |column| row[column] }.join("_") + ".csv"
|
14
|
+
end
|
15
|
+
|
16
|
+
change_columns = ARGV[0].split(/,/).map(&:to_i)
|
17
|
+
input_file = ARGV[1]
|
18
|
+
|
19
|
+
previous_row = output_csv = nil
|
20
|
+
CSV.foreach(input_file) do |row|
|
21
|
+
if previous_row.nil? || (previous_row && columns_changed?(previous_row, row, change_columns))
|
22
|
+
output_csv.close if output_csv
|
23
|
+
output_file_name = file_name_from_changed_columns(row, change_columns)
|
24
|
+
output_csv = CSV.open(output_file_name, "w")
|
25
|
+
end
|
26
|
+
output_csv << row
|
27
|
+
previous_row = row
|
28
|
+
end
|
29
|
+
output_csv.close
|
data/bin/setup
ADDED
data/bin/split_csv.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'team_effort'
|
3
|
+
|
4
|
+
def do_cmd(cmd)
|
5
|
+
`#{cmd}`
|
6
|
+
raise "'#{cmd}' failed with #{status}" if !$?.success?
|
7
|
+
end
|
8
|
+
|
9
|
+
csv_file = ARGV[0]
|
10
|
+
lines = ARGV[1]
|
11
|
+
csv_file_name = File.basename(csv_file)
|
12
|
+
system "ghead -n 1 #{csv_file} > #{csv_file_name}_header"
|
13
|
+
system "gtail -n +2 #{csv_file} | gsplit -l #{lines} -a 3 - #{csv_file_name}_"
|
14
|
+
files = Dir.entries(".").reject { |f| f =~ %r{^(\.\.?|stdout|stderr)$} }
|
15
|
+
TeamEffort.work(files, 1) do |file|
|
16
|
+
tmp_file = "tmp_#{file}"
|
17
|
+
do_cmd "gcat #{csv_file_name}_header #{file} >> #{tmp_file}"
|
18
|
+
do_cmd "mv #{tmp_file} #{file}"
|
19
|
+
end
|
20
|
+
do_cmd "rm #{csv_file_name}_header"
|
21
|
+
puts ""
|
data/lib/advance.rb
ADDED
@@ -0,0 +1,153 @@
|
|
1
|
+
require "advance/version"
|
2
|
+
require 'open3'
|
3
|
+
|
4
|
+
require "team_effort"
|
5
|
+
|
6
|
+
module Advance
|
7
|
+
RESET="\e[0m"
|
8
|
+
|
9
|
+
BOLD="\e[1m"
|
10
|
+
ITALIC="\e[3m"
|
11
|
+
UNDERLINE="\e[4m"
|
12
|
+
|
13
|
+
CYAN="\e[36m"
|
14
|
+
GRAY="\e[37m"
|
15
|
+
GREEN="\e[32m"
|
16
|
+
MAGENTA="\e[35m"
|
17
|
+
RED="\e[31m"
|
18
|
+
WHITE="\e[1;37m"
|
19
|
+
YELLOW="\e[33m"
|
20
|
+
|
21
|
+
def do_command(command, feedback = true)
|
22
|
+
puts "#{YELLOW}#{command}#{RESET} " if feedback
|
23
|
+
start_time = Time.now
|
24
|
+
stdout, stderr, status = Open3.capture3(command)
|
25
|
+
elapsed_time = Time.now - start_time
|
26
|
+
File.open("log", "w") do |f|
|
27
|
+
f.puts "%%% command: >#{command}<"
|
28
|
+
f.puts "%%% returned status: >#{status}<"
|
29
|
+
f.puts "%%% elapsed time: #{elapsed_time} seconds"
|
30
|
+
f.puts "%%% stdout:"
|
31
|
+
f.puts stdout
|
32
|
+
f.puts "%%% stderr:"
|
33
|
+
f.puts stderr
|
34
|
+
end
|
35
|
+
if !status.success?
|
36
|
+
raise "step #{$step} #{label} failed with #{status}"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def previous_dir_path
|
41
|
+
relative_path = case $step
|
42
|
+
when 1
|
43
|
+
".."
|
44
|
+
else
|
45
|
+
File.join("..", Dir.entries("..").find { |d| d =~ /^#{step_dir_prefix($step - 1)}/ })
|
46
|
+
end
|
47
|
+
File.expand_path(relative_path)
|
48
|
+
end
|
49
|
+
|
50
|
+
def previous_file_path
|
51
|
+
dir_entries = Dir.glob(File.join(previous_dir_path, "*"))
|
52
|
+
dir_entries_clean = dir_entries.reject { |f| File.directory?(f) || f =~ %r{^\.\.?|log} }
|
53
|
+
dir_entries_clean.first
|
54
|
+
end
|
55
|
+
|
56
|
+
def single(label, command)
|
57
|
+
step(label) do
|
58
|
+
if command =~ /\{previous_file\}/
|
59
|
+
command.gsub!("{previous_file}", previous_file_path)
|
60
|
+
end
|
61
|
+
if command =~ /\{previous_dir\}/
|
62
|
+
command.gsub!("{previous_dir}", previous_dir_path)
|
63
|
+
end
|
64
|
+
do_command command
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def multi(label, command)
|
69
|
+
no_feedback = false
|
70
|
+
step(label) do
|
71
|
+
# previous_dir_path = File.expand_path(previous_dir_path)
|
72
|
+
files = Dir.entries(previous_dir_path).reject { |f| f =~ %r{^(\.\.?|log)$} }
|
73
|
+
file_path_template = file_path_template(previous_dir_path, files)
|
74
|
+
last_progress = ""
|
75
|
+
progress_proc = ->(index, max_index) do
|
76
|
+
latest_progress = sprintf("%3i%", index.to_f / max_index * 100)
|
77
|
+
puts latest_progress if last_progress != latest_progress
|
78
|
+
last_progress = latest_progress
|
79
|
+
end
|
80
|
+
TeamEffort.work(files, $cores, progress_proc: progress_proc) do |file|
|
81
|
+
begin
|
82
|
+
previous_file_path = file_path_template.gsub("{file}", file)
|
83
|
+
command.gsub!("{file_path}", previous_file_path) unless $step == 1
|
84
|
+
command.gsub!("{file}", file) unless $step == 1
|
85
|
+
puts "#{YELLOW}#{command}#{RESET}"
|
86
|
+
dir_name = file
|
87
|
+
work_in_sub_dir(dir_name) do
|
88
|
+
do_command command, no_feedback
|
89
|
+
end
|
90
|
+
rescue
|
91
|
+
puts "%%%% error while processing #{file}"
|
92
|
+
raise
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def file_path_template(dir_path, files)
|
99
|
+
file = files.first
|
100
|
+
file_path = File.join(dir_path, file)
|
101
|
+
if File.directory?(file_path)
|
102
|
+
File.join(dir_path, "{file}", "{file}")
|
103
|
+
else
|
104
|
+
File.join(dir_path, "{file}")
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def work_in_sub_dir(dir_name, existing_message = nil)
|
109
|
+
return if Dir.exist? dir_name
|
110
|
+
|
111
|
+
tmp_dir_name = "tmp_#{dir_name}"
|
112
|
+
FileUtils.rm_rf tmp_dir_name
|
113
|
+
FileUtils.mkdir_p tmp_dir_name
|
114
|
+
FileUtils.cd tmp_dir_name
|
115
|
+
|
116
|
+
yield
|
117
|
+
|
118
|
+
FileUtils.cd ".."
|
119
|
+
FileUtils.mv tmp_dir_name, dir_name
|
120
|
+
end
|
121
|
+
|
122
|
+
def step_dir_prefix(step_no)
|
123
|
+
"step_%03d" % [step_no]
|
124
|
+
end
|
125
|
+
|
126
|
+
def step(label)
|
127
|
+
$step ||= 0
|
128
|
+
$step += 1
|
129
|
+
dir_name = "#{step_dir_prefix($step)}_#{label}"
|
130
|
+
$previous_dir = File.join(FileUtils.pwd, dir_name)
|
131
|
+
puts "#{CYAN}step #{$step} #{label}#{WHITE}... #{RESET}"
|
132
|
+
|
133
|
+
work_in_sub_dir(dir_name, "#{GREEN}OK#{RESET}") do
|
134
|
+
yield
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def ensure_bin_on_path
|
139
|
+
advance_path = File.dirname(__FILE__)
|
140
|
+
add_dir_to_path(advance_path)
|
141
|
+
|
142
|
+
caller_path = File.dirname(caller[0].split(/:/).first)
|
143
|
+
add_dir_to_path(caller_path)
|
144
|
+
end
|
145
|
+
|
146
|
+
def add_dir_to_path(dir)
|
147
|
+
bin_dir = File.expand_path(dir)
|
148
|
+
path = ENV["PATH"]
|
149
|
+
|
150
|
+
return if path.include?(bin_dir)
|
151
|
+
ENV["PATH"] = [path, bin_dir].join(":")
|
152
|
+
end
|
153
|
+
end
|
metadata
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: advance
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- janemacfarlane
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2019-01-10 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: team_effort
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.16'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.16'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '10.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '10.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: minitest
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '5.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '5.0'
|
69
|
+
description: |
|
70
|
+
Advance allows you to concisely script your
|
71
|
+
data transformation process and to
|
72
|
+
incrementally build and easily debug that process.
|
73
|
+
Each data transformation is a step and the results of each
|
74
|
+
step become the input to the next step.
|
75
|
+
email:
|
76
|
+
- jfmacfarlane@lbl.gov
|
77
|
+
executables:
|
78
|
+
- concat_csv.rb
|
79
|
+
- console
|
80
|
+
- csv_select.rb
|
81
|
+
- csv_split_on_change.rb
|
82
|
+
- setup
|
83
|
+
- split_csv.rb
|
84
|
+
extensions: []
|
85
|
+
extra_rdoc_files: []
|
86
|
+
files:
|
87
|
+
- ".gitignore"
|
88
|
+
- ".travis.yml"
|
89
|
+
- Gemfile
|
90
|
+
- Gemfile.lock
|
91
|
+
- LICENSE.txt
|
92
|
+
- README.md
|
93
|
+
- Rakefile
|
94
|
+
- advance.gemspec
|
95
|
+
- bin/concat_csv.rb
|
96
|
+
- bin/console
|
97
|
+
- bin/csv_select.rb
|
98
|
+
- bin/csv_split_on_change.rb
|
99
|
+
- bin/setup
|
100
|
+
- bin/split_csv.rb
|
101
|
+
- lib/advance.rb
|
102
|
+
- lib/advance/version.rb
|
103
|
+
homepage: https://github.com/doctorjane/advance
|
104
|
+
licenses:
|
105
|
+
- MIT
|
106
|
+
metadata: {}
|
107
|
+
post_install_message:
|
108
|
+
rdoc_options: []
|
109
|
+
require_paths:
|
110
|
+
- lib
|
111
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
112
|
+
requirements:
|
113
|
+
- - ">="
|
114
|
+
- !ruby/object:Gem::Version
|
115
|
+
version: '0'
|
116
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
117
|
+
requirements:
|
118
|
+
- - ">="
|
119
|
+
- !ruby/object:Gem::Version
|
120
|
+
version: '0'
|
121
|
+
requirements: []
|
122
|
+
rubyforge_project:
|
123
|
+
rubygems_version: 2.4.8
|
124
|
+
signing_key:
|
125
|
+
specification_version: 4
|
126
|
+
summary: A framework for building data transformation pipelines
|
127
|
+
test_files: []
|