metacrunch 2.1.1 → 2.2.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.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +14 -6
  3. data/Rakefile +1 -0
  4. data/bin/console +11 -0
  5. data/lib/metacrunch/file/reader/file_system_fetcher.rb +21 -0
  6. data/lib/metacrunch/file/reader/plain_file_reader.rb +33 -0
  7. data/lib/metacrunch/file/reader/scp_fetcher.rb +56 -0
  8. data/lib/metacrunch/file/reader/tar_file_reader.rb +37 -0
  9. data/lib/metacrunch/file/reader/zip_file_reader.rb +30 -0
  10. data/lib/metacrunch/file/reader.rb +72 -0
  11. data/lib/metacrunch/file.rb +23 -0
  12. data/lib/metacrunch/file_reader.rb +3 -3
  13. data/lib/metacrunch/file_writer.rb +3 -3
  14. data/lib/metacrunch/hash.rb +51 -0
  15. data/lib/metacrunch/processor.rb +10 -0
  16. data/lib/metacrunch/snr/section.rb +15 -4
  17. data/lib/metacrunch/snr.rb +16 -0
  18. data/lib/metacrunch/transformator/transformation/step.rb +45 -0
  19. data/lib/metacrunch/transformator/transformation.rb +48 -0
  20. data/lib/metacrunch/transformator.rb +5 -0
  21. data/lib/metacrunch/transformer.rb +6 -2
  22. data/lib/metacrunch/version.rb +1 -1
  23. data/lib/metacrunch.rb +6 -6
  24. data/metacrunch.gemspec +5 -1
  25. data/spec/assets/file/some_file +1 -0
  26. data/spec/assets/file/some_file.gz +0 -0
  27. data/spec/assets/file/tar_archive.tar +0 -0
  28. data/spec/assets/file/tar_gz_archive.tar.gz +0 -0
  29. data/spec/assets/file/tgz_archive.tgz +0 -0
  30. data/spec/assets/file/zip_archive.zip +0 -0
  31. data/spec/assets/sql_lite_config.rb +17 -0
  32. data/spec/file_writer_spec.rb +3 -3
  33. data/spec/metacrunch/file/reader/plain_file_reader_spec.rb +11 -0
  34. data/spec/metacrunch/file/reader/tar_reader_spec.rb +15 -0
  35. data/spec/metacrunch/file/reader/zip_reader_spec.rb +13 -0
  36. data/spec/metacrunch/file/reader_spec.rb +47 -0
  37. data/spec/metacrunch/file_spec.rb +15 -0
  38. data/spec/metacrunch/hash_spec.rb +93 -0
  39. data/spec/metacrunch/transformator/transformation_spec.rb +28 -0
  40. data/spec/snr_spec.rb +2 -2
  41. data/spec/spec_helper.rb +31 -0
  42. metadata +100 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5bcfeeb60f1625e155e0af9a3b97fdc4d580f5d8
4
- data.tar.gz: 9f157ad5acb41c7fe3fc8bad5585653a5a6ff41a
3
+ metadata.gz: ca4236a24f7c856c544dc3eef7f933eca1e0b4d7
4
+ data.tar.gz: 4ae2e318c548e41183759499d67211b0ed316b7c
5
5
  SHA512:
6
- metadata.gz: d2a45e6ba7218f8b283c9b018b76129ce98187037c84986a11e27bffdad3cde4ce9f51c0112c5e34d17b40b5c46e101ddba74fe93f0fe48e00f51a4859da96a2
7
- data.tar.gz: 39ff3a35ab79367db1af87ae9ba7abc92a957eb8b92a1c040eaca3cdc4140cde0e15501fe7204afb451fc971ecf9ecae934f74aac39f68f71f6e0cc0fe95e7a6
6
+ metadata.gz: fbc4b38921697648b09fa6f5b70665b3fceb3f9fa9fe1407da2d578515389646405b3decb7a0ce10dd056bd53a85b0a4cf9db909644f7fe658fc347b2a1938ad
7
+ data.tar.gz: 861bee6defe53134bd865742eac8aeb7731d4cf9c7252f12cc57521105057241637354278ec598f900a70bb52785e6f3f5dda351b7d8b9b38ae5ea732e64e063
data/Gemfile CHANGED
@@ -1,17 +1,25 @@
1
- source 'https://rubygems.org'
1
+ source "https://rubygems.org"
2
2
 
3
+ # Specify your gem's dependencies in your gemspec
3
4
  gemspec
4
5
 
5
- gem "rake"
6
- gem "rspec", "~> 3.2.0"
6
+ group :development do
7
+ gem "bundler"
8
+ gem "nokogiri"
9
+ gem "rake"
10
+ gem "rspec", ">= 3.0.0", "< 4.0.0"
11
+ gem "simplecov", ">= 0.8.0"
7
12
 
8
- if !ENV["CI"]
9
- group :development do
13
+ if !ENV["CI"]
10
14
  gem "hashdiff"
11
15
  gem "pry", "~> 0.9.12.6"
12
16
  gem "pry-byebug", "<= 1.3.2"
13
- gem "pry-rescue", "~> 1.4.1", github: "ConradIrwin/pry-rescue", branch: :master
17
+ gem "pry-rescue", "~> 1.4.2"
14
18
  gem "pry-stack_explorer", "~> 0.4.9.1"
15
19
  gem "pry-syntax-hacks", "~> 0.0.6"
16
20
  end
17
21
  end
22
+
23
+ group :test do
24
+ gem "codeclimate-test-reporter", require: nil
25
+ end
data/Rakefile CHANGED
@@ -1,3 +1,4 @@
1
+ require "bundler/gem_tasks"
1
2
  require "rspec/core/rake_task"
2
3
 
3
4
  RSpec::Core::RakeTask.new(:spec)
data/bin/console ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "metacrunch"
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
@@ -0,0 +1,21 @@
1
+ require_relative "../reader"
2
+
3
+ # This fetcher does not do anything besides returning the filename withouzt the
4
+ # file:// protocol prefix. It only exists to keep the implementation consistent.
5
+ class Metacrunch::File::Reader::FileSystemFetcher
6
+ include Enumerable
7
+
8
+ RECOGNIZED_PROTOCOL_REGEX = /\Afile:\/\//
9
+
10
+ def self.accepts?(url)
11
+ !!url[RECOGNIZED_PROTOCOL_REGEX]
12
+ end
13
+
14
+ def initialize(url, options = {})
15
+ @url = url
16
+ end
17
+
18
+ def each
19
+ yield @url.sub(RECOGNIZED_PROTOCOL_REGEX, "")
20
+ end
21
+ end
@@ -0,0 +1,33 @@
1
+ require_relative "../reader"
2
+
3
+ class Metacrunch::File::Reader::PlainFileReader
4
+ include Enumerable
5
+
6
+ def self.accepts?(filename)
7
+ true
8
+ end
9
+
10
+ def initialize(filename)
11
+ @filename = filename
12
+ end
13
+
14
+ def each
15
+ return enum_for(__method__) unless block_given?
16
+
17
+ io =
18
+ if @filename.end_with?("gz") # catches tgz and tar.gz
19
+ Zlib::GzipReader.open(@filename)
20
+ else
21
+ File.open(@filename)
22
+ end
23
+
24
+ yield Metacrunch::File.new({
25
+ content: io.read,
26
+ entry_name: File.basename(@filename),
27
+ file_name: @filename,
28
+ is_directory: false
29
+ })
30
+
31
+ io.close
32
+ end
33
+ end
@@ -0,0 +1,56 @@
1
+ require "etc"
2
+ require "net/scp"
3
+ require "net/ssh"
4
+ require "securerandom"
5
+ require_relative "../reader"
6
+
7
+ class Metacrunch::File::Reader::ScpFetcher
8
+ include Enumerable
9
+
10
+ RECOGNIZED_PROTOCOL_REGEX = /\Ascp:\/\//
11
+ TILDE_REPLACEMENT = "/__TILDE__"
12
+
13
+ def self.accepts?(url)
14
+ !!url[RECOGNIZED_PROTOCOL_REGEX]
15
+ end
16
+
17
+ def initialize(url, options = {})
18
+ URI(url.sub("~", TILDE_REPLACEMENT)).try do |_uri|
19
+ @host = _uri.host
20
+ @password = options[:password] || _uri.password
21
+ @path = _uri.path.sub(TILDE_REPLACEMENT, "~")
22
+ @username = options[:username] || _uri.user || Etc.getlogin
23
+ end
24
+ end
25
+
26
+ def each
27
+ return enum_for(__method__) unless block_given?
28
+
29
+ begin
30
+ begin
31
+ Dir.mkdir temporary_directory = File.join(Dir.tmpdir, SecureRandom.hex)
32
+ rescue Errno::EEXIST
33
+ retry
34
+ end
35
+
36
+ remote_filenames.each do |_remote_filename|
37
+ _local_filename = File.join(temporary_directory, File.basename(_remote_filename))
38
+ Net::SCP.download!(@host, @username, _remote_filename, _local_filename, ssh: { password: @password })
39
+ yield _local_filename
40
+ File.delete(_local_filename)
41
+ end
42
+ ensure
43
+ FileUtils.remove_dir(temporary_directory)
44
+ end
45
+ end
46
+
47
+ private
48
+
49
+ def remote_filenames
50
+ @remote_filenames ||= [].tap do |_remote_filenames|
51
+ Net::SSH.start(@host, @username, password: @password) do |_ssh|
52
+ _remote_filenames.concat _ssh.exec!("ruby -e \"puts Dir.glob(File.expand_path('#{@path}'))\"").try(:split, "\n") || []
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,37 @@
1
+ require "rubygems/package"
2
+ require_relative "../reader"
3
+
4
+ class Metacrunch::File::Reader::TarFileReader
5
+ include Enumerable
6
+
7
+ def self.accepts?(filename)
8
+ !!filename[/\.tar\Z|\.tar\.gz\Z|\.tgz\Z/]
9
+ end
10
+
11
+ def initialize(filename)
12
+ @filename = filename
13
+ end
14
+
15
+ def each
16
+ return enum_for(__method__) unless block_given?
17
+
18
+ io =
19
+ if @filename.end_with?("gz") # catches tgz and tar.gz
20
+ Zlib::GzipReader.open(@filename)
21
+ else
22
+ File.open(@filename)
23
+ end
24
+
25
+ Gem::Package::TarReader.new(io).each do |_tar_entry|
26
+ unless _tar_entry.directory?
27
+ yield Metacrunch::File.new({
28
+ content: _tar_entry.read,
29
+ entry_name: _tar_entry.full_name,
30
+ file_name: @filename
31
+ })
32
+ end
33
+ end
34
+
35
+ io.close
36
+ end
37
+ end
@@ -0,0 +1,30 @@
1
+ require "zip"
2
+ require_relative "../reader"
3
+
4
+ class Metacrunch::File::Reader::ZipFileReader
5
+ include Enumerable
6
+
7
+ def self.accepts?(filename)
8
+ !!filename[/\.zip\Z/]
9
+ end
10
+
11
+ def initialize(filename)
12
+ @filename = filename
13
+ end
14
+
15
+ def each
16
+ return enum_for(__method__) unless block_given?
17
+
18
+ Zip::File.open(@filename) do |_zip_file|
19
+ _zip_file.each do |_zip_entry|
20
+ unless _zip_entry.directory?
21
+ yield Metacrunch::File.new({
22
+ content: _zip_entry.get_input_stream.read,
23
+ entry_name: _zip_entry.name,
24
+ file_name: @filename
25
+ })
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,72 @@
1
+ require_relative "../file"
2
+ require_relative "../processor"
3
+
4
+ class Metacrunch::File::Reader < Metacrunch::Processor
5
+ require_relative "./reader/file_system_fetcher"
6
+ require_relative "./reader/plain_file_reader"
7
+ require_relative "./reader/scp_fetcher"
8
+ require_relative "./reader/tar_file_reader"
9
+ require_relative "./reader/zip_file_reader"
10
+
11
+ include Enumerable
12
+
13
+ def initialize(options = {})
14
+ @bulk_size = options[:bulk_size].try(:to_i) || 1
15
+ @urls = [
16
+ options[:filename], options[:filenames],
17
+ options[:url], options[:urls]
18
+ ]
19
+ .flatten
20
+ .compact
21
+ .map do |_filename_or_url|
22
+ if (_url = _filename_or_url)[/\A\w+:\/\//]
23
+ _url
24
+ else
25
+ Dir.glob(File.expand_path(_filename_or_url)).map do |_filename|
26
+ "file://#{_filename}"
27
+ end
28
+ end
29
+ end
30
+ .flatten # because there might be arrays again because of Dir.glob
31
+
32
+ @force_content_encoding = options[:force_content_encoding]
33
+ @password = options[:password]
34
+ @username = options[:username]
35
+ end
36
+
37
+ def call(items = [], pipeline = nil)
38
+ @chunks_of_entries_enumerator ||= each_slice(@bulk_size) # instance method from Enumerable
39
+
40
+ begin
41
+ items.concat(@chunks_of_entries_enumerator.next)
42
+ rescue StopIteration
43
+ pipeline.try(:terminate!)
44
+ end
45
+ end
46
+
47
+ def each
48
+ return enum_for(__method__) unless block_given?
49
+
50
+ @urls.each do |_url|
51
+ [FileSystemFetcher, ScpFetcher].find do |_fetcher|
52
+ _fetcher.accepts?(_url)
53
+ end
54
+ .try do |_appropriate_fetcher|
55
+ _appropriate_fetcher.new(_url, username: @username, password: @password).each do |_filename|
56
+ [TarFileReader, ZipFileReader, PlainFileReader].find do |_reader| # PlainFileReader as last will read any file
57
+ _reader.accepts?(_filename)
58
+ end
59
+ .try do |_appropriate_reader|
60
+ _appropriate_reader.new(_filename).each do |_file|
61
+ if @force_content_encoding.present?
62
+ _file.content.try(:force_encoding, @force_content_encoding)
63
+ end
64
+
65
+ yield _file
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,23 @@
1
+ require_relative "../metacrunch"
2
+
3
+ class Metacrunch::File
4
+ require_relative "./file/reader"
5
+
6
+ attr_accessor :content
7
+ attr_accessor :entry_name # equals file_name for plain files
8
+ attr_accessor :file_name
9
+
10
+ def initialize(options = {})
11
+ @content = options[:content]
12
+ @entry_name = options[:entry_name]
13
+ @file_name = options[:file_name]
14
+ end
15
+
16
+ def to_h
17
+ {
18
+ content: @content,
19
+ entry_name: @entry_name,
20
+ file_name: @file_name
21
+ }
22
+ end
23
+ end
@@ -31,14 +31,14 @@ module Metacrunch
31
31
  end
32
32
 
33
33
  def read_regular_file(filename, &block)
34
- if File.file?(filename)
35
- io = is_gzip_file?(filename) ? Zlib::GzipReader.open(filename) : File.open(filename, "r")
34
+ if ::File.file?(filename)
35
+ io = is_gzip_file?(filename) ? Zlib::GzipReader.open(filename) : ::File.open(filename, "r")
36
36
  yield Entry.new(filename: filename, archive_filename: nil, contents: io.read)
37
37
  end
38
38
  end
39
39
 
40
40
  def read_archive(filename, &block)
41
- io = is_gzip_file?(filename) ? Zlib::GzipReader.open(filename) : File.open(filename, "r")
41
+ io = is_gzip_file?(filename) ? Zlib::GzipReader.open(filename) : ::File.open(filename, "r")
42
42
  tarReader = Gem::Package::TarReader.new(io)
43
43
 
44
44
  tarReader.each do |_tar_entry|
@@ -5,10 +5,10 @@ module Metacrunch
5
5
 
6
6
 
7
7
  def initialize(filename, override: false, compress: nil)
8
- @path = File.expand_path(filename)
8
+ @path = ::File.expand_path(filename)
9
9
  @compressed = (compress ||= @path.ends_with?(".gz"))
10
10
 
11
- if File.exist?(@path) && !override
11
+ if ::File.exist?(@path) && !override
12
12
  raise FileExistError, "File #{@path} already exists. Set override = true to override the existing file."
13
13
  end
14
14
  end
@@ -33,7 +33,7 @@ module Metacrunch
33
33
  private
34
34
 
35
35
  def io
36
- @io ||= (@compressed == true) ? Zlib::GzipWriter.open(@path) : File.open(@path, "w")
36
+ @io ||= (@compressed == true) ? Zlib::GzipWriter.open(@path) : ::File.open(@path, "w")
37
37
  end
38
38
 
39
39
  end
@@ -0,0 +1,51 @@
1
+ require_relative "../metacrunch"
2
+
3
+ module Metacrunch::Hash
4
+ def self.add(object, *args)
5
+ if args.length < 2
6
+ raise ArgumentError
7
+ else
8
+ return args.last if args.last.nil? || (args.last.respond_to?(:empty?) && args.last.empty?)
9
+
10
+ if args.length == 2
11
+ args_first_is_a_string = args.first.is_a?(String) # memoize
12
+
13
+ if args_first_is_a_string && args.first.include?("/")
14
+ add(object, *args.first.split("/"), args.last)
15
+ else
16
+ if args_first_is_a_string && args.first.start_with?(":")
17
+ _add(object, args.first[1..-1].to_sym, args.last)
18
+ else
19
+ _add(object, args.first, args.last)
20
+ end
21
+ end
22
+ else
23
+ nested_hash = args[0..-3].inject(object) do |_memo, _key|
24
+ if _key.is_a?(String) && _key.start_with?(":")
25
+ _key = _key[1..-1].to_sym
26
+ end
27
+
28
+ _memo[_key] ||= object.class.new
29
+ end
30
+
31
+ _add(nested_hash, args[-2], args[-1])
32
+ end
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ def self._add(hash, key, value)
39
+ #if value.is_a?(FalseClass) || (value.respond_to?(:empty?) ? !value.empty? : !!value) # like ActiveSupport implements blank?/present?
40
+ if hash[key].nil?
41
+ hash[key] = value.is_a?(Array) && value.length == 1 ? value.first : value
42
+ elsif hash[key].is_a?(Array)
43
+ (hash[key] << value).flatten!(1)
44
+ else
45
+ (hash[key] = [hash[key], value]).flatten!(1)
46
+ end
47
+ #end
48
+
49
+ hash[key]
50
+ end
51
+ end
@@ -0,0 +1,10 @@
1
+ require_relative "../metacrunch"
2
+
3
+ class Metacrunch::Processor
4
+ def initialize(options = {})
5
+ @options ||= options
6
+ end
7
+
8
+ def call(items = [], pipeline = nil)
9
+ end
10
+ end
@@ -19,7 +19,13 @@ module Metacrunch
19
19
  # Adds a field
20
20
  #
21
21
  def add(field_name, value)
22
- add_field(Field.new(field_name, value))
22
+ if value.is_a?(Array)
23
+ value.each do |_value|
24
+ add_field(Field.new(field_name, _value))
25
+ end
26
+ else
27
+ add_field(Field.new(field_name, value))
28
+ end
23
29
  end
24
30
 
25
31
  # ------------------------------------------------------------------------------
@@ -27,12 +33,17 @@ module Metacrunch
27
33
  # ------------------------------------------------------------------------------
28
34
 
29
35
  #
30
- # Return all fields.
36
+ # Return all fields. A name can be provided to filter fields by name.
31
37
  #
38
+ # @param [String, nil] name
32
39
  # @return [Array<Metacrunch::SNR::Section::Field>]
33
40
  #
34
- def fields
35
- @fields
41
+ def fields(name = nil)
42
+ if name
43
+ @fields.select{|field| field.name == name}
44
+ else
45
+ @fields
46
+ end
36
47
  end
37
48
 
38
49
  #
@@ -33,6 +33,22 @@ module Metacrunch
33
33
  add_section(section)
34
34
  end
35
35
 
36
+ #
37
+ # Returns field values for a given path.
38
+ #
39
+ # @param [String] path A path to the fields seperated by /. E.g. section/field
40
+ # @return [Array<*>]
41
+ #
42
+ def values(path)
43
+ section_name, field_name = path.split("/")
44
+ section = self.section(section_name)
45
+ if section && field_name
46
+ section.fields(field_name).map{|field| field.value}
47
+ else
48
+ []
49
+ end
50
+ end
51
+
36
52
  # ------------------------------------------------------------------------------
37
53
  # Sections
38
54
  # ------------------------------------------------------------------------------
@@ -0,0 +1,45 @@
1
+ require_relative "../transformation"
2
+
3
+ class Metacrunch::Transformator::Transformation::Step
4
+ attr_accessor :transformation
5
+
6
+ def initialize(transformation = nil, options = {})
7
+ if transformation.is_a?(Hash)
8
+ options = transformation
9
+ transformation = nil
10
+ end
11
+
12
+ if transformation
13
+ @transformation = transformation
14
+ else
15
+ @transformation = Struct.new(:source, :target).new.tap do |_struct|
16
+ _struct.source = options[:source]
17
+ _struct.target = options[:target]
18
+ end
19
+ end
20
+ end
21
+
22
+ def call
23
+ end
24
+
25
+ #
26
+ # Each step has transparent access to all methods of it's transformation
27
+ #
28
+ def method_missing(method_name, *args, &block)
29
+ if @transformation.respond_to?(method_name)
30
+ @transformation.send(method_name, *args, &block)
31
+ else
32
+ super
33
+ end
34
+ end
35
+
36
+ def respond_to_missing?(method_name, include_private = false)
37
+ @transformation.respond_to?(method_name) || super
38
+ end
39
+
40
+ # avoid method_missing penalty for the most used transformation methods
41
+ def source; @transformation.source; end
42
+ def source=(value); @transformation.source=(value); end
43
+ def target; @transformation.target; end
44
+ def target=(value); @transformation.target=(value); end
45
+ end
@@ -0,0 +1,48 @@
1
+ require_relative "../transformator"
2
+
3
+ class Metacrunch::Transformator::Transformation
4
+ require_relative "./transformation/step"
5
+
6
+ attr_accessor :source
7
+ attr_accessor :target
8
+
9
+ class << self
10
+ def steps(value = nil)
11
+ unless value
12
+ @steps
13
+ else
14
+ @steps = value
15
+ end
16
+ end
17
+ alias_method :sequence, :steps
18
+ end
19
+
20
+ def self.call(*args)
21
+ new.call(*args)
22
+ end
23
+
24
+ # since a transformation can have many steps, writing a "require" for each is tedious
25
+ def self.require_directory(directory)
26
+ Dir.glob("#{File.expand_path(directory)}/*.rb").each do |_filename|
27
+ require _filename
28
+ end
29
+ end
30
+
31
+ def initialize
32
+ # steps are instanced once, which means that instance variables retain
33
+ @steps = self.class.steps.flatten.map do |_step|
34
+ _step.is_a?(Class) ? _step.new(self) : _step
35
+ end
36
+ end
37
+
38
+ def call(source, options = {})
39
+ @source = source
40
+ @target = options[:target]
41
+
42
+ @steps.each do |_step|
43
+ _step.is_a?(Proc) ? instance_exec(&_step) : _step.call
44
+ end
45
+
46
+ return @target
47
+ end
48
+ end
@@ -0,0 +1,5 @@
1
+ require_relative "../metacrunch"
2
+
3
+ module Metacrunch::Transformator
4
+ require_relative "./transformator/transformation"
5
+ end
@@ -4,6 +4,7 @@ module Metacrunch
4
4
  require_relative "./transformer/helper"
5
5
 
6
6
  attr_accessor :source, :target, :options
7
+ attr_reader :step
7
8
 
8
9
 
9
10
  def initialize(source:nil, target:nil, options: {})
@@ -14,10 +15,13 @@ module Metacrunch
14
15
 
15
16
  def transform(step_class = nil, &block)
16
17
  if block_given?
17
- Step.new(self).instance_eval(&block) # TODO: Benchmark this
18
+ @step = Step.new(self)
19
+ @step.instance_eval(&block)
18
20
  else
19
21
  raise ArgumentError, "You need to provide a STEP or a block" if step_class.nil?
20
- step_class.new(self).perform
22
+ clazz = step_class.is_a?(Class) ? step_class : step_class.to_s.constantize
23
+ @step = clazz.new(self)
24
+ @step.perform
21
25
  end
22
26
  end
23
27
 
@@ -1,3 +1,3 @@
1
1
  module Metacrunch
2
- VERSION = "2.1.1"
2
+ VERSION = "2.2.0"
3
3
  end
data/lib/metacrunch.rb CHANGED
@@ -7,21 +7,21 @@ require "rubygems/package"
7
7
  require "thor"
8
8
  require "ox"
9
9
 
10
- begin
11
- require "pry"
12
- rescue LoadError ; end
13
-
14
10
  module Metacrunch
15
11
  require_relative "./metacrunch/version"
16
12
  require_relative "./metacrunch/cli"
17
13
  require_relative "./metacrunch/command"
14
+ require_relative "./metacrunch/file"
18
15
  require_relative "./metacrunch/file_reader"
19
16
  require_relative "./metacrunch/file_reader_entry"
20
17
  require_relative "./metacrunch/file_writer"
21
- require_relative "./metacrunch/tar_writer"
18
+ require_relative "./metacrunch/hash"
19
+ require_relative "./metacrunch/parallel"
20
+ require_relative "./metacrunch/processor"
22
21
  require_relative "./metacrunch/snr"
22
+ require_relative "./metacrunch/tar_writer"
23
+ require_relative "./metacrunch/transformator"
23
24
  require_relative "./metacrunch/transformer"
24
- require_relative "./metacrunch/parallel"
25
25
 
26
26
  def self.load_plugins
27
27
  Gem.find_latest_files("metacrunch_plugin.rb").each do |path|
data/metacrunch.gemspec CHANGED
@@ -9,7 +9,7 @@ Gem::Specification.new do |s|
9
9
  s.licenses = ["MIT"]
10
10
 
11
11
  s.files = `git ls-files`.split($\)
12
- s.executables = s.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
12
+ s.executables = s.files.grep(%r{^bin/metacrunch}) { |f| File.basename(f) }
13
13
  s.test_files = s.files.grep(%r{^(test|spec|features)/})
14
14
  s.name = "metacrunch"
15
15
  s.require_paths = ["lib"]
@@ -21,7 +21,11 @@ Gem::Specification.new do |s|
21
21
 
22
22
  s.add_dependency "activesupport", "~> 4.2", ">= 4.2.0"
23
23
  s.add_dependency "builder", "~> 3.2", ">= 3.2.2"
24
+ s.add_dependency "highline", "~> 1.7"
25
+ s.add_dependency "net-scp", "~> 1.2"
26
+ s.add_dependency "net-ssh", "~> 2.9"
24
27
  s.add_dependency "parallel", "~> 1.6", ">= 1.6.0"
28
+ s.add_dependency "rubyzip", ">= 1.0.0"
25
29
  s.add_dependency "thor", "~> 0.19"
26
30
  s.add_dependency "ox", "~> 2.1"
27
31
  end
@@ -0,0 +1 @@
1
+ Some text
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,17 @@
1
+ pipeline do
2
+ reader "Metacrunch::Archive::Reader", {
3
+ bulk_size: 2,
4
+ filenames: "foo.tar.gz"
5
+ }
6
+
7
+ processor -> (items, pipeline) {
8
+ items.map! do |_item|
9
+
10
+ end
11
+ }
12
+ #processor "Metacrunch::Sqlite::Writer", {
13
+ # database: "mab_xml.db"
14
+ #}
15
+
16
+ processor "Metacrunch::Debugger"
17
+ end
@@ -4,8 +4,8 @@ describe Metacrunch::FileWriter do
4
4
  let(:compressed_file) { "/tmp/metacrunch_spec_regular_file.txt.gz" }
5
5
 
6
6
  before do
7
- File.unlink(regular_file) if File.exist?(regular_file)
8
- File.unlink(compressed_file) if File.exist?(compressed_file)
7
+ ::File.unlink(regular_file) if ::File.exist?(regular_file)
8
+ ::File.unlink(compressed_file) if ::File.exist?(compressed_file)
9
9
  end
10
10
 
11
11
  it "can write a file" do
@@ -32,7 +32,7 @@ describe Metacrunch::FileWriter do
32
32
  end
33
33
 
34
34
  it "can override existing file" do
35
- File.write(regular_file, "FOO")
35
+ ::File.write(regular_file, "FOO")
36
36
 
37
37
  expect {
38
38
  Metacrunch::FileWriter.new(regular_file)
@@ -0,0 +1,11 @@
1
+ describe Metacrunch::File::Reader::PlainFileReader do
2
+ describe ".accepts?" do
3
+ it "accepts all filenames" do
4
+ expect(described_class.accepts?("foo")).to equal(true)
5
+ expect(described_class.accepts?("foo.gz")).to equal(true)
6
+ expect(described_class.accepts?("foo.tar.gz")).to equal(true)
7
+ expect(described_class.accepts?("foo.xml")).to equal(true)
8
+ expect(described_class.accepts?("foo.zip")).to equal(true)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,15 @@
1
+ describe Metacrunch::File::Reader::TarFileReader do
2
+ describe ".accepts?" do
3
+ it "accepts filenames this reader can process" do
4
+ expect(described_class.accepts?("foo.tar")).to equal(true)
5
+ expect(described_class.accepts?("foo.tar.gz")).to equal(true)
6
+ expect(described_class.accepts?("foo.tgz")).to equal(true)
7
+ end
8
+
9
+ it "does not accept filenames this reader cannot process" do
10
+ expect(described_class.accepts?("plain_file")).to equal(false)
11
+ expect(described_class.accepts?("plain_file.gz")).to equal(false)
12
+ expect(described_class.accepts?("tatar.gz")).to equal(false)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,13 @@
1
+ describe Metacrunch::File::Reader::ZipFileReader do
2
+ describe ".accepts?" do
3
+ it "accepts filenames this reader can process" do
4
+ expect(described_class.accepts?("foo.zip")).to equal(true)
5
+ end
6
+
7
+ it "does not accept filenames this reader cannot process" do
8
+ expect(described_class.accepts?("plain_file")).to equal(false)
9
+ expect(described_class.accepts?("plain.zip.file")).to equal(false)
10
+ expect(described_class.accepts?("foozip")).to equal(false)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,47 @@
1
+ describe Metacrunch::File::Reader do
2
+ def pipeline_factory
3
+ Class.new do
4
+ def terminate!; @terminated = true; end
5
+ def terminated?; !!@terminated; end
6
+ end
7
+ .new
8
+ end
9
+
10
+ def reader_factory(filename)
11
+ described_class.new({
12
+ bulk_size: 10,
13
+ filenames: [filename],
14
+ force_content_encoding: "utf-8"
15
+ })
16
+ end
17
+
18
+ # filenames
19
+ let(:plain_file_name) { File.join(asset_dir, "file/some_file") }
20
+ let(:plain_gz_file_name) { File.join(asset_dir, "file/some_file.gz") }
21
+ let(:tar_file_name) { File.join(asset_dir, "file/tar_archive.tar") }
22
+ let(:tar_gz_file_name) { File.join(asset_dir, "file/tar_gz_archive.tar.gz") }
23
+ let(:tgz_file_name) { File.join(asset_dir, "file/tgz_archive.tgz") }
24
+ let(:zip_file_name) { File.join(asset_dir, "file/zip_archive.zip") }
25
+
26
+ # readers
27
+ let(:plain_file_reader) { reader_factory(plain_file_name) }
28
+ let(:plain_gz_file_reader) { reader_factory(plain_gz_file_name) }
29
+ let(:tar_reader) { reader_factory(tar_file_name) }
30
+ let(:tar_gz_reader) { reader_factory(tar_gz_file_name) }
31
+ let(:tgz_reader) { reader_factory(tgz_file_name) }
32
+ let(:zip_reader) { reader_factory(zip_file_name) }
33
+
34
+ describe "#call" do
35
+ it "fills items with #{_file_type = Metacrunch::File} objects" do
36
+ [plain_file_reader, plain_gz_file_reader, tar_reader, tar_gz_reader,tgz_reader, zip_reader].each do |_reader|
37
+ pipeline = pipeline_factory
38
+
39
+ _reader.call(items = [], pipeline)
40
+ expect(items.length).not_to eq(0)
41
+ expect(items).to all(be_a(_file_type))
42
+
43
+ _reader.call(items, pipeline) until pipeline.terminated?
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,15 @@
1
+ describe Metacrunch::File do
2
+ describe "#to_h" do
3
+ let(:file) do
4
+ described_class.new({
5
+ content: "content",
6
+ entry_name: "entry_name",
7
+ file_name: "file_name"
8
+ })
9
+ end
10
+
11
+ it "returns a hash representation of the file object" do
12
+ expect(file.to_h).to be_a(Hash)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,93 @@
1
+ describe Metacrunch::Hash do
2
+ describe ".add" do
3
+ it "adds the given key/value pair" do
4
+ described_class.add(hash = {}, :key, "value")
5
+ expect(hash[:key]).to eq("value")
6
+ end
7
+
8
+ it "respects the type of the key" do
9
+ hash = {}
10
+
11
+ described_class.add(hash, :symbol_key1, "value")
12
+ described_class.add(hash, ":symbol_key2", "value")
13
+ described_class.add(hash, "string_key", "value")
14
+
15
+ expect(hash[:symbol_key1]).to eq("value")
16
+ expect(hash[:symbol_key2]).to eq("value")
17
+ expect(hash["string_key"]).to eq("value")
18
+ end
19
+
20
+ context "if wrong arguments are given" do
21
+ it "raises an #{exception_type = ArgumentError}" do
22
+ expect { described_class.add(hash) }.to raise_error(exception_type)
23
+ end
24
+ end
25
+
26
+ context "if key includes forward dashes" do
27
+ it "splits the key by forward dashes" do
28
+ described_class.add(hash = {}, "key/subkey", "value")
29
+ expect(hash["key"]["subkey"]).to eq("value")
30
+ end
31
+
32
+ it "respects the type of the keys" do
33
+ described_class.add(hash = {}, "key/:subkey/sub_sub_key", "value")
34
+ expect(hash["key"][:subkey]["sub_sub_key"]).to eq("value")
35
+ end
36
+ end
37
+
38
+ context "if multiple keys are given" do
39
+ it "dives into the object and adds the key/value pair" do
40
+ hash = { "key" => { "subkey" => "former_value" } }
41
+ described_class.add(hash, "key", "subkey", "value")
42
+ expect(hash["key"]["subkey"]).to eq(["former_value", "value"])
43
+ end
44
+ end
45
+
46
+ context "if there already exists a value for the given key" do
47
+ context "if the value is an array" do
48
+ it "adds the value to the array" do
49
+ hash = { "key" => ["former_value1", "former_value2"] }
50
+ described_class.add(hash, "key", "value")
51
+ expect(hash["key"]).to eq(["former_value1", "former_value2", "value"])
52
+ end
53
+
54
+ it "returns aggregated value" do
55
+ hash = { "key" => ["former_value1", "former_value2"] }
56
+ expect(described_class.add(hash, "key", "value")).to eq(["former_value1", "former_value2", "value"])
57
+ end
58
+ end
59
+
60
+ it "makes the existing value an array and adds the given value" do
61
+ hash = { "key" => "former_value" }
62
+ described_class.add(hash, "key", "value")
63
+ expect(hash["key"]).to eq(["former_value", "value"])
64
+ end
65
+
66
+ it "returns aggregated value" do
67
+ hash = { "key" => "former_value" }
68
+ expect(described_class.add(hash, "key", "value")).to eq(["former_value", "value"])
69
+ end
70
+ end
71
+
72
+ context "if the given value is nil" do
73
+ it "does not add anything" do
74
+ described_class.add(hash = {}, "key/subkey", nil)
75
+ expect(hash).to eq({})
76
+ end
77
+ end
78
+
79
+ context "if the given value is empty" do
80
+ it "does not add anything" do
81
+ hash = {}
82
+ described_class.add(hash, "key/subkey1", "")
83
+ described_class.add(hash, "key/subkey2", [])
84
+ described_class.add(hash, "key/subkey3", Class.new { def empty?; true; end }.new)
85
+ expect(hash).to eq({})
86
+ end
87
+ end
88
+
89
+ it "returns the value" do
90
+ expect(described_class.add({}, "key", "value")).to eq("value")
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,28 @@
1
+ describe Metacrunch::Transformator::Transformation do
2
+ class SomeStep < described_class::Step
3
+ def call
4
+ self.target = { muff: 1 }
5
+ end
6
+ end
7
+
8
+ class SomeTransformation < described_class
9
+ sequence [
10
+ SomeStep
11
+ ]
12
+ end
13
+
14
+ it "foo" do
15
+ result = SomeTransformation.call({
16
+ my: {
17
+ awesome: {
18
+ source: {
19
+ title: "foo",
20
+ description: "bar"
21
+ }
22
+ }
23
+ }
24
+ })
25
+
26
+ result
27
+ end
28
+ end
data/spec/snr_spec.rb CHANGED
@@ -59,7 +59,7 @@ describe Metacrunch::SNR do
59
59
  describe ".to_xml" do
60
60
  before {
61
61
  snr.add("section1", "title", "Foo Bar")
62
- snr.add("section1", "artists", ["Sievers, Michael", "Sprotte, René"])
62
+ snr.add("section1", "artist", ["Sievers, Michael", "Sprotte, René"])
63
63
  snr.add("section2", "link", {label: "Click here", url: "http://example.com"})
64
64
  }
65
65
 
@@ -67,7 +67,7 @@ describe Metacrunch::SNR do
67
67
 
68
68
  it { is_expected.not_to be_nil }
69
69
  it { is_expected.not_to be_empty }
70
- it { is_expected.to eq("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<snr>\n <section1>\n <title>Foo Bar</title>\n <artists type=\"array\">\n <artist>Sievers, Michael</artist>\n <artist>Sprotte, René</artist>\n </artists>\n </section1>\n <section2>\n <link>\n <label>Click here</label>\n <url>http://example.com</url>\n </link>\n </section2>\n</snr>\n") }
70
+ it { is_expected.to eq("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<snr>\n <section1>\n <title>Foo Bar</title>\n <artist>Sievers, Michael</artist>\n <artist>Sprotte, René</artist>\n </section1>\n <section2>\n <link>\n <label>Click here</label>\n <url>http://example.com</url>\n </link>\n </section2>\n</snr>\n") }
71
71
  end
72
72
 
73
73
  end
data/spec/spec_helper.rb CHANGED
@@ -1,6 +1,29 @@
1
+ if ENV["CODECLIMATE_REPO_TOKEN"]
2
+ require "codeclimate-test-reporter"
3
+ CodeClimate::TestReporter.start
4
+ else
5
+ require "simplecov"
6
+ SimpleCov.start
7
+ end
8
+
1
9
  require "metacrunch"
2
10
 
11
+ begin
12
+ require "pry"
13
+ require "yaml"
14
+ rescue LoadError
15
+ end
16
+
3
17
  RSpec.configure do |config|
18
+ # begin --- rspec 3.1 generator
19
+ config.expect_with :rspec do |expectations|
20
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
21
+ end
22
+
23
+ config.mock_with :rspec do |mocks|
24
+ mocks.verify_partial_doubles = true
25
+ end
26
+ # end --- rspec 3.1 generator
4
27
  end
5
28
 
6
29
  # Helper to provide RSpec.root
@@ -10,3 +33,11 @@ module ::RSpec
10
33
  @spec_root ||= Pathname.new(__dir__)
11
34
  end
12
35
  end
36
+
37
+ def asset_dir
38
+ File.expand_path(File.join(File.dirname(__FILE__), "assets"))
39
+ end
40
+
41
+ def read_asset(path_to_file)
42
+ File.read(File.expand_path(File.join(asset_dir, path_to_file)))
43
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: metacrunch
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.1
4
+ version: 2.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - René Sprotte
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2015-06-15 00:00:00.000000000 Z
13
+ date: 2015-09-25 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activesupport
@@ -52,6 +52,48 @@ dependencies:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: 3.2.2
55
+ - !ruby/object:Gem::Dependency
56
+ name: highline
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.7'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.7'
69
+ - !ruby/object:Gem::Dependency
70
+ name: net-scp
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.2'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.2'
83
+ - !ruby/object:Gem::Dependency
84
+ name: net-ssh
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '2.9'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '2.9'
55
97
  - !ruby/object:Gem::Dependency
56
98
  name: parallel
57
99
  requirement: !ruby/object:Gem::Requirement
@@ -72,6 +114,20 @@ dependencies:
72
114
  - - ">="
73
115
  - !ruby/object:Gem::Version
74
116
  version: 1.6.0
117
+ - !ruby/object:Gem::Dependency
118
+ name: rubyzip
119
+ requirement: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: 1.0.0
124
+ type: :runtime
125
+ prerelease: false
126
+ version_requirements: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ version: 1.0.0
75
131
  - !ruby/object:Gem::Dependency
76
132
  name: thor
77
133
  requirement: !ruby/object:Gem::Requirement
@@ -114,6 +170,7 @@ files:
114
170
  - License.txt
115
171
  - Rakefile
116
172
  - Readme.md
173
+ - bin/console
117
174
  - bin/metacrunch
118
175
  - lib/metacrunch.rb
119
176
  - lib/metacrunch/cli.rb
@@ -122,14 +179,26 @@ files:
122
179
  - lib/metacrunch/cli/command_registry.rb
123
180
  - lib/metacrunch/cli/main.rb
124
181
  - lib/metacrunch/command.rb
182
+ - lib/metacrunch/file.rb
183
+ - lib/metacrunch/file/reader.rb
184
+ - lib/metacrunch/file/reader/file_system_fetcher.rb
185
+ - lib/metacrunch/file/reader/plain_file_reader.rb
186
+ - lib/metacrunch/file/reader/scp_fetcher.rb
187
+ - lib/metacrunch/file/reader/tar_file_reader.rb
188
+ - lib/metacrunch/file/reader/zip_file_reader.rb
125
189
  - lib/metacrunch/file_reader.rb
126
190
  - lib/metacrunch/file_reader_entry.rb
127
191
  - lib/metacrunch/file_writer.rb
192
+ - lib/metacrunch/hash.rb
128
193
  - lib/metacrunch/parallel.rb
194
+ - lib/metacrunch/processor.rb
129
195
  - lib/metacrunch/snr.rb
130
196
  - lib/metacrunch/snr/field.rb
131
197
  - lib/metacrunch/snr/section.rb
132
198
  - lib/metacrunch/tar_writer.rb
199
+ - lib/metacrunch/transformator.rb
200
+ - lib/metacrunch/transformator/transformation.rb
201
+ - lib/metacrunch/transformator/transformation/step.rb
133
202
  - lib/metacrunch/transformer.rb
134
203
  - lib/metacrunch/transformer/helper.rb
135
204
  - lib/metacrunch/transformer/step.rb
@@ -137,10 +206,24 @@ files:
137
206
  - metacrunch.gemspec
138
207
  - spec/assets/archive.tar
139
208
  - spec/assets/archive.tar.gz
209
+ - spec/assets/file/some_file
210
+ - spec/assets/file/some_file.gz
211
+ - spec/assets/file/tar_archive.tar
212
+ - spec/assets/file/tar_gz_archive.tar.gz
213
+ - spec/assets/file/tgz_archive.tgz
214
+ - spec/assets/file/zip_archive.zip
140
215
  - spec/assets/regular_file.txt
141
216
  - spec/assets/regular_file.txt.gz
217
+ - spec/assets/sql_lite_config.rb
142
218
  - spec/file_reader_spec.rb
143
219
  - spec/file_writer_spec.rb
220
+ - spec/metacrunch/file/reader/plain_file_reader_spec.rb
221
+ - spec/metacrunch/file/reader/tar_reader_spec.rb
222
+ - spec/metacrunch/file/reader/zip_reader_spec.rb
223
+ - spec/metacrunch/file/reader_spec.rb
224
+ - spec/metacrunch/file_spec.rb
225
+ - spec/metacrunch/hash_spec.rb
226
+ - spec/metacrunch/transformator/transformation_spec.rb
144
227
  - spec/snr_spec.rb
145
228
  - spec/spec_helper.rb
146
229
  - spec/tar_writer_spec.rb
@@ -164,17 +247,31 @@ required_rubygems_version: !ruby/object:Gem::Requirement
164
247
  version: '0'
165
248
  requirements: []
166
249
  rubyforge_project:
167
- rubygems_version: 2.4.6
250
+ rubygems_version: 2.4.8
168
251
  signing_key:
169
252
  specification_version: 4
170
253
  summary: Data processing toolkit for Ruby
171
254
  test_files:
172
255
  - spec/assets/archive.tar
173
256
  - spec/assets/archive.tar.gz
257
+ - spec/assets/file/some_file
258
+ - spec/assets/file/some_file.gz
259
+ - spec/assets/file/tar_archive.tar
260
+ - spec/assets/file/tar_gz_archive.tar.gz
261
+ - spec/assets/file/tgz_archive.tgz
262
+ - spec/assets/file/zip_archive.zip
174
263
  - spec/assets/regular_file.txt
175
264
  - spec/assets/regular_file.txt.gz
265
+ - spec/assets/sql_lite_config.rb
176
266
  - spec/file_reader_spec.rb
177
267
  - spec/file_writer_spec.rb
268
+ - spec/metacrunch/file/reader/plain_file_reader_spec.rb
269
+ - spec/metacrunch/file/reader/tar_reader_spec.rb
270
+ - spec/metacrunch/file/reader/zip_reader_spec.rb
271
+ - spec/metacrunch/file/reader_spec.rb
272
+ - spec/metacrunch/file_spec.rb
273
+ - spec/metacrunch/hash_spec.rb
274
+ - spec/metacrunch/transformator/transformation_spec.rb
178
275
  - spec/snr_spec.rb
179
276
  - spec/spec_helper.rb
180
277
  - spec/tar_writer_spec.rb