cartage 2.2 → 2.2.1
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/History.md +99 -80
- data/README.rdoc +13 -7
- data/Rakefile +52 -38
- data/lib/cartage/backport.rb +3 -3
- data/lib/cartage/cli.rb +76 -76
- data/lib/cartage/commands/echo.rb +6 -6
- data/lib/cartage/commands/info.rb +17 -17
- data/lib/cartage/commands/manifest.rb +46 -46
- data/lib/cartage/commands/metadata.rb +2 -2
- data/lib/cartage/commands/pack.rb +6 -6
- data/lib/cartage/config.rb +27 -25
- data/lib/cartage/core.rb +18 -18
- data/lib/cartage/gli_ext.rb +10 -10
- data/lib/cartage/minitest.rb +7 -7
- data/lib/cartage/plugin.rb +10 -9
- data/lib/cartage/plugins/build_tarball.rb +2 -2
- data/lib/cartage/plugins/manifest.rb +85 -85
- data/lib/cartage.rb +67 -67
- data/test/minitest_config.rb +8 -8
- data/test/test_cartage.rb +130 -130
- data/test/test_cartage_build_tarball.rb +22 -22
- data/test/test_cartage_config.rb +27 -27
- data/test/test_cartage_core.rb +36 -36
- data/test/test_cartage_manifest.rb +51 -53
- data/test/test_cartage_plugin.rb +21 -21
- metadata +37 -20
data/lib/cartage/core.rb
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
class Cartage
|
5
5
|
# Extensions for us to use to help define Cartage with its attr_readers with
|
6
6
|
# defaults and attr_writers with transforms.
|
7
|
-
module Core
|
7
|
+
module Core # :nodoc:
|
8
8
|
private
|
9
9
|
|
10
10
|
# Define an attr_reader with a memoized default value. The +default+ is
|
@@ -25,8 +25,8 @@ class Cartage
|
|
25
25
|
# # Does the same thing
|
26
26
|
# attr_reader_with_default :answer, 42
|
27
27
|
def attr_reader_with_default(name, default = nil, &block)
|
28
|
-
fail ArgumentError,
|
29
|
-
fail ArgumentError,
|
28
|
+
fail ArgumentError, "No default provided." unless default || block
|
29
|
+
fail ArgumentError, "Too many defaults provided." if default && block
|
30
30
|
|
31
31
|
default_ivar = :"@default_#{name}"
|
32
32
|
default_name = :"default_#{name}"
|
@@ -43,10 +43,10 @@ class Cartage
|
|
43
43
|
end
|
44
44
|
|
45
45
|
dblk = if default.respond_to?(:call)
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
46
|
+
default
|
47
|
+
else
|
48
|
+
block || -> { default }
|
49
|
+
end
|
50
50
|
|
51
51
|
define_method(default_name) do
|
52
52
|
if instance_variable_defined?(default_ivar)
|
@@ -64,18 +64,18 @@ class Cartage
|
|
64
64
|
# The +transform+ may be provided as a callable (such as a proc), an object
|
65
65
|
# that responds to #to_proc (such as a Symbol), or a block.
|
66
66
|
def attr_writer_with_transform(name, transform = nil, &block)
|
67
|
-
fail ArgumentError,
|
68
|
-
fail ArgumentError,
|
67
|
+
fail ArgumentError, "No transform provided." unless transform || block
|
68
|
+
fail ArgumentError, "Too many transforms provided." if transform && block
|
69
69
|
|
70
70
|
tblk = if transform.respond_to?(:call)
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
71
|
+
transform
|
72
|
+
elsif transform.respond_to?(:to_proc)
|
73
|
+
transform.to_proc
|
74
|
+
elsif block
|
75
|
+
block
|
76
|
+
else
|
77
|
+
fail ArgumentError, "Transform is not callable."
|
78
|
+
end
|
79
79
|
|
80
80
|
define_method(:"#{name}=") do |v|
|
81
81
|
instance_variable_set(:"@#{name}", tblk.call(v))
|
@@ -109,4 +109,4 @@ class Cartage
|
|
109
109
|
extend Core
|
110
110
|
end
|
111
111
|
|
112
|
-
require_relative
|
112
|
+
require_relative "backport"
|
data/lib/cartage/gli_ext.rb
CHANGED
@@ -1,18 +1,18 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require "gli"
|
4
4
|
|
5
5
|
##
|
6
|
-
module Cartage::CLIOptionsSupport
|
6
|
+
module Cartage::CLIOptionsSupport # :nodoc:
|
7
7
|
# Clears defaults from a flag-set. By default, only clears symbolic defaults
|
8
8
|
# (e.g., <tt>:'default-value'</tt>.)
|
9
9
|
def clear_defaults_from(opts, flag_set: flags, symbol_defaults_only: true)
|
10
10
|
flag_set.each do |name, flag|
|
11
11
|
next unless flag.default_value
|
12
|
-
next if symbol_defaults_only && !flag.default_value.
|
12
|
+
next if symbol_defaults_only && !flag.default_value.is_a?(Symbol)
|
13
13
|
next unless opts[name] == flag.default_value
|
14
14
|
|
15
|
-
aliases = [
|
15
|
+
aliases = [name, *flag.aliases].compact
|
16
16
|
aliases += aliases.map(&:to_s)
|
17
17
|
aliases.each { |aka| opts[aka] = nil }
|
18
18
|
end
|
@@ -20,7 +20,7 @@ module Cartage::CLIOptionsSupport #:nodoc:
|
|
20
20
|
end
|
21
21
|
|
22
22
|
# Work around a bug with the RdocDocumentListener
|
23
|
-
module RdocDocumentListenerAppFix
|
23
|
+
module RdocDocumentListenerAppFix # :nodoc:
|
24
24
|
def initialize(_global_options, _options, _arguments, app)
|
25
25
|
super
|
26
26
|
@app = app
|
@@ -28,12 +28,12 @@ module RdocDocumentListenerAppFix #:nodoc:
|
|
28
28
|
end
|
29
29
|
|
30
30
|
##
|
31
|
-
class GLI::Commands::RdocDocumentListener
|
31
|
+
class GLI::Commands::RdocDocumentListener # :nodoc:
|
32
32
|
prepend RdocDocumentListenerAppFix
|
33
33
|
end
|
34
34
|
|
35
35
|
##
|
36
|
-
module GLI::App
|
36
|
+
module GLI::App # :nodoc:
|
37
37
|
include Cartage::CLIOptionsSupport
|
38
38
|
|
39
39
|
# Indicate the parent GLI application.
|
@@ -43,7 +43,7 @@ module GLI::App #:nodoc:
|
|
43
43
|
end
|
44
44
|
|
45
45
|
##
|
46
|
-
class GLI::Command
|
46
|
+
class GLI::Command # :nodoc:
|
47
47
|
include Cartage::CLIOptionsSupport
|
48
48
|
|
49
49
|
# Indicate the parent GLI application.
|
@@ -57,8 +57,8 @@ class GLI::Command #:nodoc:
|
|
57
57
|
end
|
58
58
|
|
59
59
|
def plugin_version_command(*plugin_classes)
|
60
|
-
desc
|
61
|
-
command
|
60
|
+
desc "Show the plug-in version"
|
61
|
+
command "version" do |version|
|
62
62
|
version.hide!
|
63
63
|
version.action do |_g, _o, _a|
|
64
64
|
plugin_classes.each do |plugin_class|
|
data/lib/cartage/minitest.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require "cartage"
|
4
4
|
|
5
5
|
##
|
6
6
|
# Provide helper methods for testing Cartage and plug-ins using Minitest.
|
7
7
|
module Cartage::Minitest
|
8
|
-
|
8
|
+
# :nocov:
|
9
9
|
|
10
10
|
##
|
11
11
|
# A helper to stub ENV lookups against an +env+ hash. If +options+ has a key
|
@@ -43,7 +43,7 @@ module Cartage::Minitest
|
|
43
43
|
|
44
44
|
# A helper to stub Cartage#repo_url to return +value+.
|
45
45
|
def stub_cartage_repo_url(value = nil, &block)
|
46
|
-
stub_instance_method Cartage, :repo_url, -> { value ||
|
46
|
+
stub_instance_method Cartage, :repo_url, -> { value || "git://host/repo-url.git" },
|
47
47
|
&block
|
48
48
|
end
|
49
49
|
|
@@ -76,8 +76,8 @@ module Cartage::Minitest
|
|
76
76
|
|
77
77
|
Dir.singleton_class.send(:alias_method, :__minitest_stub_chdir__, :chdir)
|
78
78
|
Dir.singleton_class.send(:define_method, :chdir) do |path, &block|
|
79
|
-
assert_equal.(expected, path)
|
80
|
-
block
|
79
|
+
assert_equal.call(expected, path)
|
80
|
+
block&.call(path)
|
81
81
|
end
|
82
82
|
|
83
83
|
yield
|
@@ -90,14 +90,14 @@ module Cartage::Minitest
|
|
90
90
|
# Stubs Cartage#run and asserts that the array of commands provided are
|
91
91
|
# matched for each call and that they are all consumed.
|
92
92
|
def stub_cartage_run(*expected)
|
93
|
-
expected = [
|
93
|
+
expected = [expected].flatten(1)
|
94
94
|
stub_instance_method Cartage, :run, ->(v) { assert_equal expected.shift, v } do
|
95
95
|
yield
|
96
96
|
end
|
97
97
|
assert_empty expected
|
98
98
|
end
|
99
99
|
|
100
|
-
|
100
|
+
# :nocov:
|
101
101
|
|
102
102
|
private
|
103
103
|
|
data/lib/cartage/plugin.rb
CHANGED
@@ -7,7 +7,7 @@ class Cartage
|
|
7
7
|
class Plugin
|
8
8
|
class << self
|
9
9
|
# Register a plugin.
|
10
|
-
def inherited(plugin)
|
10
|
+
def inherited(plugin) # :nodoc:
|
11
11
|
registered[plugin.plugin_name] = plugin
|
12
12
|
end
|
13
13
|
|
@@ -19,8 +19,8 @@ class Cartage
|
|
19
19
|
|
20
20
|
# The name of the plugin.
|
21
21
|
def plugin_name
|
22
|
-
@name ||= name.split(
|
23
|
-
sub(/^_/,
|
22
|
+
@name ||= name.split("::").last.gsub(/([A-Z])/, '_\1').downcase
|
23
|
+
.sub(/^_/, "")
|
24
24
|
end
|
25
25
|
|
26
26
|
# Iterate the plugins by +name+.
|
@@ -38,7 +38,7 @@ class Cartage
|
|
38
38
|
end
|
39
39
|
|
40
40
|
# A utility method to load and decorate an object with Cartage plug-ins.
|
41
|
-
def load_for(klass)
|
41
|
+
def load_for(klass) # :nodoc:
|
42
42
|
load
|
43
43
|
decorate(klass)
|
44
44
|
end
|
@@ -46,16 +46,17 @@ class Cartage
|
|
46
46
|
# A utility method that will find all Cartage plug-ins and load them. A
|
47
47
|
# Cartage plug-in is found in the Gems as <tt>cartage/plugins/*.rb</tt>
|
48
48
|
# and descends from Cartage::Plugin.
|
49
|
-
def load(rescan: false)
|
49
|
+
def load(rescan: false) # :nodoc:
|
50
50
|
@found ||= {}
|
51
51
|
@loaded ||= {}
|
52
|
+
@files = nil unless defined?(@files)
|
52
53
|
|
53
54
|
if @files.nil? || rescan
|
54
|
-
@files = Gem.find_files(
|
55
|
+
@files = Gem.find_files("cartage/plugins/*.rb")
|
55
56
|
end
|
56
57
|
|
57
58
|
@files.reverse_each do |path|
|
58
|
-
name = File.basename(path,
|
59
|
+
name = File.basename(path, ".rb").to_sym
|
59
60
|
@found[name] = path
|
60
61
|
end
|
61
62
|
|
@@ -65,7 +66,7 @@ class Cartage
|
|
65
66
|
end
|
66
67
|
|
67
68
|
# Decorate the provided class with lazy initialization methods.
|
68
|
-
def decorate(klass)
|
69
|
+
def decorate(klass) # :nodoc:
|
69
70
|
registered.each do |name, plugin|
|
70
71
|
ivar = "@#{name}"
|
71
72
|
|
@@ -122,7 +123,7 @@ class Cartage
|
|
122
123
|
|
123
124
|
# A plug-in is given, as +cartage+, the instance of Cartage that owns it.
|
124
125
|
def initialize(cartage)
|
125
|
-
fail NotImplementedError,
|
126
|
+
fail NotImplementedError, "not a subclass" if instance_of?(Cartage::Plugin)
|
126
127
|
@cartage = cartage
|
127
128
|
@disabled = false
|
128
129
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require "tempfile"
|
4
4
|
|
5
5
|
# Manage and use the package manifest ('Manifest.txt') and the ignore file
|
6
6
|
# ('.cartignore').
|
@@ -8,21 +8,21 @@ class Cartage::Manifest < Cartage::Plugin
|
|
8
8
|
# This exception is raised if the package manifest is missing.
|
9
9
|
class MissingError < StandardError
|
10
10
|
def message # :nodoc:
|
11
|
-
|
12
|
-
Cartage cannot create a package without a Manifest.txt file. You may generate
|
13
|
-
or update the Manifest.txt file with the following command:
|
11
|
+
<<~EXCEPTION
|
12
|
+
Cartage cannot create a package without a Manifest.txt file. You may generate
|
13
|
+
or update the Manifest.txt file with the following command:
|
14
14
|
|
15
|
-
|
15
|
+
cartage manifest generate
|
16
16
|
|
17
|
-
|
17
|
+
EXCEPTION
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
21
|
-
DIFF = if system(
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
21
|
+
DIFF = if system("gdiff", __FILE__, __FILE__) # :nodoc:
|
22
|
+
"gdiff"
|
23
|
+
else
|
24
|
+
"diff"
|
25
|
+
end
|
26
26
|
|
27
27
|
# Resolve the Manifest to something that can be used by <tt>tar -T</tt>. The
|
28
28
|
# manifest should be relative to the files in the repository, as reported
|
@@ -40,13 +40,13 @@ or update the Manifest.txt file with the following command:
|
|
40
40
|
# A block is required and is provided the +resolved_path+.
|
41
41
|
def resolve(path = nil) # :yields: resolved_path
|
42
42
|
fail MissingError unless manifest_file.exist?
|
43
|
-
fail ArgumentError,
|
43
|
+
fail ArgumentError, "A block is required." unless block_given?
|
44
44
|
|
45
45
|
data = strip_comments_and_empty_lines(manifest_file.readlines)
|
46
|
-
fail
|
46
|
+
fail "Manifest.txt is empty." if data.empty?
|
47
47
|
|
48
48
|
path = Pathname(path || Dir.pwd).expand_path.basename
|
49
|
-
tmpfile = Tempfile.new(
|
49
|
+
tmpfile = Tempfile.new("Manifest.")
|
50
50
|
|
51
51
|
tmpfile.puts prune(data, with_slugignore: true).map { |line|
|
52
52
|
path.join(line).to_s
|
@@ -70,19 +70,19 @@ or update the Manifest.txt file with the following command:
|
|
70
70
|
# Checks Manifest.txt
|
71
71
|
def check
|
72
72
|
fail MissingError unless manifest_file.exist?
|
73
|
-
tmp = create_file_list(
|
73
|
+
tmp = create_file_list("Manifest.tmp")
|
74
74
|
|
75
|
-
args = [
|
75
|
+
args = [DIFF, "-du", manifest_file.basename.to_s, tmp.to_s]
|
76
76
|
|
77
77
|
if cartage.quiet
|
78
|
-
|
78
|
+
`#{(args << "-q").join(" ")}`
|
79
79
|
else
|
80
80
|
system(*args)
|
81
81
|
end
|
82
82
|
|
83
83
|
$?.success?
|
84
84
|
ensure
|
85
|
-
tmp
|
85
|
+
tmp&.unlink
|
86
86
|
end
|
87
87
|
|
88
88
|
# Installs the default .cartignore file. Will either +overwrite+ or +merge+
|
@@ -90,8 +90,8 @@ or update the Manifest.txt file with the following command:
|
|
90
90
|
def install_default_ignore(mode: nil)
|
91
91
|
save = mode || !ignore_file.exist?
|
92
92
|
|
93
|
-
if mode ==
|
94
|
-
cartage.display(
|
93
|
+
if mode == "merge"
|
94
|
+
cartage.display("Merging .cartignore...")
|
95
95
|
data = strip_comments_and_empty_lines(ignore_file.readlines)
|
96
96
|
|
97
97
|
if data.empty?
|
@@ -101,10 +101,10 @@ or update the Manifest.txt file with the following command:
|
|
101
101
|
data = data.uniq.join("\n")
|
102
102
|
end
|
103
103
|
elsif save
|
104
|
-
cartage.display(
|
104
|
+
cartage.display("Creating .cartignore...")
|
105
105
|
data = DEFAULT_IGNORE
|
106
106
|
else
|
107
|
-
cartage.display(
|
107
|
+
cartage.display(".cartignore already exists, skipping...")
|
108
108
|
end
|
109
109
|
|
110
110
|
ignore_file.write(data) if save
|
@@ -113,37 +113,37 @@ or update the Manifest.txt file with the following command:
|
|
113
113
|
private
|
114
114
|
|
115
115
|
def ignore_file
|
116
|
-
@ignore_file ||= @cartage.root_path.join(
|
116
|
+
@ignore_file ||= @cartage.root_path.join(".cartignore")
|
117
117
|
end
|
118
118
|
|
119
119
|
def slugignore_file
|
120
|
-
@slugignore_file ||= @cartage.root_path.join(
|
120
|
+
@slugignore_file ||= @cartage.root_path.join(".slugignore")
|
121
121
|
end
|
122
122
|
|
123
123
|
def manifest_file
|
124
|
-
@manifest_file ||= @cartage.root_path.join(
|
124
|
+
@manifest_file ||= @cartage.root_path.join("Manifest.txt")
|
125
125
|
end
|
126
126
|
|
127
127
|
def create_file_list(filename)
|
128
|
-
files = prune(
|
128
|
+
files = prune(`git ls-files`.split.map(&:chomp)).sort.uniq.join("\n")
|
129
129
|
Pathname(filename).tap { |f| f.write("#{files}\n") }
|
130
130
|
end
|
131
131
|
|
132
132
|
def ignore_patterns(with_slugignore: false)
|
133
133
|
pats = if ignore_file.exist?
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
134
|
+
ignore_file.readlines
|
135
|
+
elsif with_slugignore && slugignore_file.exist?
|
136
|
+
slugignore_file.readlines
|
137
|
+
else
|
138
|
+
DEFAULT_IGNORE.split($/)
|
139
|
+
end
|
140
140
|
|
141
141
|
pats = strip_comments_and_empty_lines(pats)
|
142
142
|
|
143
143
|
pats.map { |pat|
|
144
|
-
if
|
144
|
+
if %r{\A/[^*?]+\z}.match?(pat)
|
145
145
|
Regexp.new(%r{\A#{pat.sub(%r{\A/}, '')}/})
|
146
|
-
elsif pat
|
146
|
+
elsif pat.end_with?("/")
|
147
147
|
Regexp.new(/\A#{pat}/)
|
148
148
|
else
|
149
149
|
pat
|
@@ -153,7 +153,7 @@ or update the Manifest.txt file with the following command:
|
|
153
153
|
|
154
154
|
def strip_comments_and_empty_lines(list)
|
155
155
|
list.map { |item|
|
156
|
-
item = item.chomp.gsub(/(?:^|[^\\])#.*\z/,
|
156
|
+
item = item.chomp.gsub(/(?:^|[^\\])#.*\z/, "").strip
|
157
157
|
if item.empty?
|
158
158
|
nil
|
159
159
|
else
|
@@ -182,55 +182,55 @@ or update the Manifest.txt file with the following command:
|
|
182
182
|
end
|
183
183
|
end
|
184
184
|
|
185
|
-
DEFAULT_IGNORE =
|
186
|
-
# Some of these are in .gitignore, but let’s remove these just in case they got
|
187
|
-
# checked in.
|
188
|
-
|
189
|
-
# Exact files to remove. Matches with ==.
|
190
|
-
.DS_Store
|
191
|
-
.autotest
|
192
|
-
.editorconfig
|
193
|
-
.env
|
194
|
-
.git-wtfrc
|
195
|
-
.gitignore
|
196
|
-
.local.vimrc
|
197
|
-
.lvimrc
|
198
|
-
.cartignore
|
199
|
-
.powenv
|
200
|
-
.rake_tasks~
|
201
|
-
.rspec
|
202
|
-
.rubocop.yml
|
203
|
-
.rvmrc
|
204
|
-
.semaphore-cache
|
205
|
-
.workenv
|
206
|
-
Guardfile
|
207
|
-
README.md
|
208
|
-
bin/build
|
209
|
-
bin/notify-project-board
|
210
|
-
bin/osx-bootstrap
|
211
|
-
bin/setup
|
212
|
-
|
213
|
-
# Patterns to remove. These have a *, **, or ? in them. Uses File.fnmatch with
|
214
|
-
# File::FNM_DOTMATCH and File::FNM_EXTGLOB.
|
215
|
-
*.rbc
|
216
|
-
.*.swp
|
217
|
-
**/.DS_Store
|
218
|
-
|
219
|
-
# Directories to remove. These should end with a slash. Matches as the regular
|
220
|
-
# expression %r{\A#{pattern}}.
|
221
|
-
db/seeds/development/
|
222
|
-
db/seeds/test/
|
223
|
-
# db/seeds/dit/
|
224
|
-
# db/seeds/staging/
|
225
|
-
log/
|
226
|
-
test/
|
227
|
-
tests/
|
228
|
-
rspec/
|
229
|
-
spec/
|
230
|
-
specs/
|
231
|
-
feature/
|
232
|
-
features/
|
233
|
-
tmp/
|
234
|
-
vendor/bundle/
|
185
|
+
DEFAULT_IGNORE = <<~'EOM' # :nodoc:
|
186
|
+
# Some of these are in .gitignore, but let’s remove these just in case they got
|
187
|
+
# checked in.
|
188
|
+
|
189
|
+
# Exact files to remove. Matches with ==.
|
190
|
+
.DS_Store
|
191
|
+
.autotest
|
192
|
+
.editorconfig
|
193
|
+
.env
|
194
|
+
.git-wtfrc
|
195
|
+
.gitignore
|
196
|
+
.local.vimrc
|
197
|
+
.lvimrc
|
198
|
+
.cartignore
|
199
|
+
.powenv
|
200
|
+
.rake_tasks~
|
201
|
+
.rspec
|
202
|
+
.rubocop.yml
|
203
|
+
.rvmrc
|
204
|
+
.semaphore-cache
|
205
|
+
.workenv
|
206
|
+
Guardfile
|
207
|
+
README.md
|
208
|
+
bin/build
|
209
|
+
bin/notify-project-board
|
210
|
+
bin/osx-bootstrap
|
211
|
+
bin/setup
|
212
|
+
|
213
|
+
# Patterns to remove. These have a *, **, or ? in them. Uses File.fnmatch with
|
214
|
+
# File::FNM_DOTMATCH and File::FNM_EXTGLOB.
|
215
|
+
*.rbc
|
216
|
+
.*.swp
|
217
|
+
**/.DS_Store
|
218
|
+
|
219
|
+
# Directories to remove. These should end with a slash. Matches as the regular
|
220
|
+
# expression %r{\A#{pattern}}.
|
221
|
+
db/seeds/development/
|
222
|
+
db/seeds/test/
|
223
|
+
# db/seeds/dit/
|
224
|
+
# db/seeds/staging/
|
225
|
+
log/
|
226
|
+
test/
|
227
|
+
tests/
|
228
|
+
rspec/
|
229
|
+
spec/
|
230
|
+
specs/
|
231
|
+
feature/
|
232
|
+
features/
|
233
|
+
tmp/
|
234
|
+
vendor/bundle/
|
235
235
|
EOM
|
236
236
|
end
|