zozo 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (5) hide show
  1. data/LICENSE +18 -0
  2. data/README.rdoc +61 -0
  3. data/bin/zozo +135 -0
  4. data/spec/zozo_spec.rb +254 -0
  5. metadata +78 -0
data/LICENSE ADDED
@@ -0,0 +1,18 @@
1
+ Copyright (c) 2010 Jeremy Evans
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to
5
+ deal in the Software without restriction, including without limitation the
6
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7
+ sell copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
16
+ THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,61 @@
1
+ = What?
2
+
3
+ zozo is a tool that makes it easy to reduce the memory footprint of your applications by having them not load rubygems/bundler at runtime:
4
+
5
+ $ unicorn -c unicorn.conf -D
6
+ $ ps ux
7
+ USER PID %CPU %MEM VSZ RSS TT STAT STARTED TIME COMMAND
8
+ jeremy 18226 0.0 0.5 17196 4496 ?? S 1:34PM 0:00.01 ruby: unicorn master -c unicorn.conf -D (ruby)
9
+ jeremy 8473 31.3 3.3 27180 30172 ?? S 1:34PM 0:00.62 ruby: unicorn worker[0] -c unicorn.conf -D (ruby)
10
+
11
+ $ zozo -R config.ru unicorn
12
+ $ ruby -I lib bin/unicorn -c unicorn.conf -D
13
+ $ ps ux
14
+ USER PID %CPU %MEM VSZ RSS TT STAT STARTED TIME COMMAND
15
+ jeremy 17561 0.0 0.4 5548 3908 ?? S 1:35PM 0:00.01 ruby: unicorn master -c unicorn.conf -D (ruby)
16
+ jeremy 22626 4.2 2.0 15016 17904 ?? S 1:35PM 0:00.25 ruby: unicorn worker[0] -c unicorn.conf -D (ruby)
17
+
18
+ As you can see, the memory footprint is reduced dramatically:
19
+
20
+ master process:
21
+ VSZ: 5548/17196 => 68% reduction
22
+ RSS: 3908/4496 => 13% reduction
23
+ worker process:
24
+ VSZ: 15016/27180 => 45% reduction
25
+ RSS: 17904/30172 => 41% reduction
26
+
27
+ That's a major difference, as a 41% reduction in memory footprint means you can host 68% more workers in the same amount of memory. It also makes your applications faster. They will start faster because zozo loads all necessary library files into a single directory tree. They will run faster as there will be fewer ruby objects to check every time the garbage collector is run.
28
+
29
+ = Why?
30
+
31
+ Rubygems is a fine package distribution system, but it is not very efficient from a runtime memory standpoint. If your application uses rubygems in production, every time it starts, rubygems needs to figure out which packages to load. zozo makes it so that this is calculation is only done once, and the result is cached into a local directory.
32
+
33
+ = How?
34
+
35
+ zozo works by starting ruby and checking the current load path. It then requires all of the command line arguments given, and checks the load path again. Any new entries in the load path are checked and their contents are loaded into a local directory (lib by default). By default, zozo uses symlinks, but it can use hard links (-H) or make copies (-c) via a command line option.
36
+
37
+ In addition, new entries in the load path that end in /bin are loaded into a separate local directory (bin by default). This allows you to run them with loading rubygems via:
38
+
39
+ ruby -I lib bin/$program
40
+
41
+ zozo adds replacement rubygems.rb, ubygems.rb, and bundler.rb files to the lib directory it creates, so it works transparently if your program requires rubygems and/or bundler. If you run your program without adding the lib directory zozo creates to the load path, rubygems/bundler will be used as it was. If you run your program with the lib directory zozo creates in the load path, then rubygems/bundler will not be loaded, and it won't need to be because all other libraries your program uses will already be in the load path.
42
+
43
+ = Where?
44
+
45
+ http://github.com/jeremyevans/zozo
46
+
47
+ = Who?
48
+
49
+ Jeremy Evans / code@jeremyevans.net
50
+
51
+ = When?
52
+
53
+ Now:
54
+
55
+ sudo gem install zozo
56
+
57
+ = Does not work with Rails!
58
+
59
+ The replacement rubygems.rb and bundler.rb files only do the bare minimum. The rubygems.rb file adds Kernel#gem, and the bundler.rb file adds Bundler.setup, both of which are defined to do nothing and return nil. No other features are mocked out. This means that frameworks that rely on introspecting the running Gem/Bundler configuration (notably Rails) will not work.
60
+
61
+ This is probably fixable, and I'll accept patches that allow zozo to work with Rails, but I don't plan on working on the issue myself. As Rails uses a substantial amount of memory by itself, it benefits less from zozo than more memory friendly frameworks such as Sinatra.
@@ -0,0 +1,135 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # bin/zozo is currently a completely procedural program, with
4
+ # no classes or methods defined. This may change in the future
5
+ # if more extensibility is desired.
6
+
7
+ require 'optparse'
8
+ require 'find'
9
+ require 'fileutils'
10
+
11
+ sep = File::SEPARATOR
12
+ lib_dir = 'lib'
13
+ bin_dir = 'bin'
14
+ ln_method = :ln_s
15
+ ln_opts = {}
16
+ loads = []
17
+ rackups = []
18
+
19
+ # Parse all command line options
20
+ opt_parser = OptionParser.new do |o|
21
+ o.banner = "zozo: Simple $LOAD_PATH management for ruby projects"
22
+ o.define_head "Usage: zozo [-l lib_dir] [-b bin_dir] [-L path] [-R ru_file] [-cfhHnv] lib"
23
+ o.separator ""
24
+ o.separator "zozo creates a directory with symbolic links to other directories"
25
+ o.separator "and files, allowing you to avoid loading rubygems and bundler when"
26
+ o.separator "running in production with a simple addition of one directory to the"
27
+ o.separator "$LOAD_PATH."
28
+ o.separator ""
29
+ o.separator "Options:"
30
+
31
+ o.on("-b", '--bin-dir LIBDIR', "Use a nonstandard bin dir (default: bin)") do |v|
32
+ bin_dir = v
33
+ end
34
+
35
+ o.on("-c", '--copy', "Copy files instead of creating symbolic links") do
36
+ ln_method = :cp
37
+ end
38
+
39
+ o.on("-f", '--force', "Force file system modifications") do
40
+ ln_opts[:force] = true
41
+ end
42
+
43
+ o.on("-h", "-?", "--help", "Show this message and exit") do
44
+ puts o
45
+ exit
46
+ end
47
+
48
+ o.on("-H", '--hard-link', "Create hard links instead of symbolic links") do
49
+ ln_method = :ln
50
+ end
51
+
52
+ o.on("-l", '--lib-dir LIBDIR', "Use a nonstandard lib dir (default: lib)") do |v|
53
+ lib_dir = v
54
+ end
55
+
56
+ o.on("-L", '--load path', "Use load instead of require for the given argument") do |v|
57
+ loads << v
58
+ end
59
+
60
+ o.on("-n", '--noop', "Don't make any file system modifications") do
61
+ ln_opts[:noop] = true
62
+ end
63
+
64
+ o.on("-R", '--rackup ru_file', "Load the given file as a rackup file") do |v|
65
+ rackups << v
66
+ end
67
+
68
+ o.on("-v", '--verbose', "Log all file system modifications to stderr") do
69
+ ln_opts[:verbose] = true
70
+ end
71
+ end
72
+ opt_parser.parse!
73
+ mkdir_opts = ln_opts.reject{|k,v| k == :force}
74
+
75
+ # Require/load the files specifying by the arguments, and check for new
76
+ # load path entries
77
+ start_dirs = $LOAD_PATH.dup
78
+ ENV['ZOZO'] = '1'
79
+ loads.each{|f| load f}
80
+ unless rackups.empty?
81
+ begin
82
+ require 'rack'
83
+ rescue LoadError
84
+ require 'rubygems'
85
+ require 'rack'
86
+ end
87
+ rackups.each{|f| eval("Rack::Builder.new{( " + File.read(f) + "\n )}.to_app", nil, f)}
88
+ end
89
+ ARGV.each{|f| require f}
90
+ new_dirs = ($LOAD_PATH - start_dirs).uniq
91
+
92
+ # Find all files/directories that we want to link to/create
93
+ lns = {}
94
+ bins = {}
95
+ new_dirs.each do |d|
96
+ Find.find(d) do |f|
97
+ next if f == d
98
+ p = f.sub(d, '').sub(sep, '')
99
+ h = (d =~ /#{sep}bin\z/) ? bins : lns
100
+ if File.directory?(f)
101
+ raise(StandardError, "File/directory overlap (file: #{h[p]}, directory: #{f})") if h[p].is_a?(String)
102
+ h[p] = :dir
103
+ elsif File.file?(f)
104
+ raise(StandardError, "File/directory overlap (file: #{f}, directory: #{p})") if h[p] == :dir
105
+ # Other entry comes before this one in the load path, so ignore it
106
+ h[p] ||= f
107
+ end
108
+ end
109
+ end
110
+
111
+ # Handle directory creation and file linking/copying
112
+ [[lib_dir, lns], [bin_dir, bins]].each do |dir, h|
113
+ FileUtils.mkdir(dir, mkdir_opts)
114
+ h.sort.each do |k, v|
115
+ p = File.join(dir, k)
116
+ if v == :dir
117
+ FileUtils.mkdir(p, mkdir_opts)
118
+ else
119
+ FileUtils.send(ln_method, v, p, ln_opts)
120
+ end
121
+ end
122
+ end
123
+
124
+ # Create special files for rubygems and bundler
125
+ %w'rubygems.rb ubygems.rb bundler.rb'
126
+ [['rubygems.rb', "module Kernel; def gem(*args); end end"],
127
+ ['ubygems.rb', "require 'rubygems'"],
128
+ ['bundler.rb', "module Bundler; def self.setup(*args); end end"]
129
+ ].each do |file, data|
130
+ p = File.join(lib_dir, file)
131
+ $stderr.puts("Writing #{p}") if ln_opts[:verbose]
132
+ if ln_opts[:force] || !File.exist?(p)
133
+ File.open(p, 'wb'){|f| f.puts(data)} unless ln_opts[:noop]
134
+ end
135
+ end
@@ -0,0 +1,254 @@
1
+ #!/usr/bin/env spec
2
+
3
+ require 'fileutils'
4
+ require 'open3'
5
+ Dir.chdir(File.dirname(File.expand_path(__FILE__)))
6
+
7
+ context "zozo" do
8
+ after do
9
+ FileUtils.rm_r('lib') if File.directory?('lib')
10
+ FileUtils.rm_r('bin') if File.directory?('bin')
11
+ end
12
+
13
+ def run(args='test1')
14
+ `#{File.join(File.dirname(File.dirname(File.dirname(File.expand_path(__FILE__)))), 'bin', 'zozo')} #{args}`
15
+ end
16
+
17
+ def run3(args='test1', &block)
18
+ Open3.popen3("#{File.join(File.dirname(File.dirname(File.dirname(File.expand_path(__FILE__)))), 'bin', 'zozo')} #{args}", &block)
19
+ end
20
+
21
+ def ino(f)
22
+ File.stat(f).ino
23
+ end
24
+
25
+ def should_be_same(f1, f2)
26
+ File.read(f1).should == File.read(f2)
27
+ end
28
+
29
+ it "should create bin and lib directories with symbolic links by default" do
30
+ run.should == ''
31
+ File.file?('lib/a').should be_true
32
+ File.symlink?('lib/a').should be_true
33
+ should_be_same('lib/a', 'test1/a')
34
+ File.directory?('lib/b').should be_true
35
+ File.file?('lib/b/c').should be_true
36
+ File.symlink?('lib/b/c').should be_true
37
+ should_be_same('lib/b/c', 'test1/b/c')
38
+
39
+ File.file?('bin/b1').should be_true
40
+ File.symlink?('bin/b1').should be_true
41
+ should_be_same('bin/b1', 'b/bin/b1')
42
+ File.directory?('bin/d').should be_true
43
+ File.file?('bin/d/b2').should be_true
44
+ File.symlink?('bin/d/b2').should be_true
45
+ should_be_same('bin/d/b2', 'b/bin/d/b2')
46
+ end
47
+
48
+ it "should print out help if -h option is used" do
49
+ run('-h').should =~ /Usage: zozo/
50
+ end
51
+
52
+ it "should create bin and lib directories with hard links if the -H option is used" do
53
+ run('-H test1').should == ''
54
+ File.file?('lib/a').should be_true
55
+ File.symlink?('lib/a').should be_false
56
+ ino('lib/a').should == ino('test1/a')
57
+ File.directory?('lib/b').should be_true
58
+ File.file?('lib/b/c').should be_true
59
+ File.symlink?('lib/b/c').should be_false
60
+ ino('lib/b/c').should == ino('test1/b/c')
61
+
62
+ File.file?('bin/b1').should be_true
63
+ File.symlink?('bin/b1').should be_false
64
+ ino('bin/b1').should == ino('b/bin/b1')
65
+ File.directory?('bin/d').should be_true
66
+ File.file?('bin/d/b2').should be_true
67
+ File.symlink?('bin/d/b2').should be_false
68
+ ino('bin/d/b2').should == ino('b/bin/d/b2')
69
+ end
70
+
71
+ it "should create bin and lib directories with file copies if the -c option is used" do
72
+ run('-c test1').should == ''
73
+ File.file?('lib/a').should be_true
74
+ File.symlink?('lib/a').should be_false
75
+ ino('lib/a').should_not == ino('test1/a')
76
+ should_be_same('lib/a', 'test1/a')
77
+ File.directory?('lib/b').should be_true
78
+ File.file?('lib/b/c').should be_true
79
+ File.symlink?('lib/b/c').should be_false
80
+ ino('lib/b/c').should_not == ino('test1/b/c')
81
+ should_be_same('lib/b/c', 'test1/b/c')
82
+
83
+ File.file?('bin/b1').should be_true
84
+ File.symlink?('bin/b1').should be_false
85
+ ino('bin/b1').should_not == ino('b/bin/b1')
86
+ should_be_same('bin/b1', 'b/bin/b1')
87
+ File.directory?('bin/d').should be_true
88
+ File.file?('bin/d/b2').should be_true
89
+ File.symlink?('bin/d/b2').should be_false
90
+ ino('bin/d/b2').should_not == ino('b/bin/d/b2')
91
+ should_be_same('bin/d/b2', 'b/bin/d/b2')
92
+ end
93
+
94
+ it "should load files with load instead of require if the -L option is used" do
95
+ run3('load_test') do |si, so, se|
96
+ se.read.should =~ /no such file to load -- load_test \(LoadError\)/
97
+ so.read.should == ''
98
+ end
99
+ run('-L load_test').should == ''
100
+ File.file?('lib/a').should be_true
101
+ File.symlink?('lib/a').should be_true
102
+ should_be_same('lib/a', 'test1/a')
103
+ File.directory?('lib/b').should be_true
104
+ File.file?('lib/b/c').should be_true
105
+ File.symlink?('lib/b/c').should be_true
106
+ should_be_same('lib/b/c', 'test1/b/c')
107
+
108
+ File.file?('bin/b1').should be_true
109
+ File.symlink?('bin/b1').should be_true
110
+ should_be_same('bin/b1', 'b/bin/b1')
111
+ File.directory?('bin/d').should be_true
112
+ File.file?('bin/d/b2').should be_true
113
+ File.symlink?('bin/d/b2').should be_true
114
+ should_be_same('bin/d/b2', 'b/bin/d/b2')
115
+ end
116
+
117
+ it "should load rackup files with Rack::Builder instead of require if the -R option is used" do
118
+ run3('test.ru') do |si, so, se|
119
+ se.read.should =~ /no such file to load -- test.ru \(LoadError\)/
120
+ so.read.should == ''
121
+ end
122
+ run('-R test.ru').should == ''
123
+ File.file?('lib/a').should be_true
124
+ File.symlink?('lib/a').should be_true
125
+ should_be_same('lib/a', 'test1/a')
126
+ File.directory?('lib/b').should be_true
127
+ File.file?('lib/b/c').should be_true
128
+ File.symlink?('lib/b/c').should be_true
129
+ should_be_same('lib/b/c', 'test1/b/c')
130
+
131
+ File.file?('bin/b1').should be_true
132
+ File.symlink?('bin/b1').should be_true
133
+ should_be_same('bin/b1', 'b/bin/b1')
134
+ File.directory?('bin/d').should be_true
135
+ File.file?('bin/d/b2').should be_true
136
+ File.symlink?('bin/d/b2').should be_true
137
+ should_be_same('bin/d/b2', 'b/bin/d/b2')
138
+ end
139
+
140
+ it "should not make any file system modifications if -n option is used" do
141
+ run('-n test1').should == ''
142
+ File.exist?('lib').should be_false
143
+ File.exist?('bin').should be_false
144
+ end
145
+
146
+ it "should force file system modifications over existing files if -f option is used" do
147
+ run.should == ''
148
+ run3 do |si, so, se|
149
+ se.read.should =~ %r{File exists - /data/code/zozo/spec/test1/a or lib/a \(Errno::EEXIST\)}
150
+ so.read.should == ''
151
+ end
152
+ run('-f test1').should == ''
153
+ File.file?('lib/a').should be_true
154
+ File.symlink?('lib/a').should be_true
155
+ should_be_same('lib/a', 'test1/a')
156
+ File.directory?('lib/b').should be_true
157
+ File.file?('lib/b/c').should be_true
158
+ File.symlink?('lib/b/c').should be_true
159
+ should_be_same('lib/b/c', 'test1/b/c')
160
+
161
+ File.file?('bin/b1').should be_true
162
+ File.symlink?('bin/b1').should be_true
163
+ should_be_same('bin/b1', 'b/bin/b1')
164
+ File.directory?('bin/d').should be_true
165
+ File.file?('bin/d/b2').should be_true
166
+ File.symlink?('bin/d/b2').should be_true
167
+ should_be_same('bin/d/b2', 'b/bin/d/b2')
168
+ end
169
+
170
+ it "should log all file system modifications to stderr if -v option is used" do
171
+ run3('-v test1') do |si, so, se|
172
+ se.read.split("\n").should == ["mkdir lib", "ln -s /data/code/zozo/spec/test1/a lib/a", "mkdir lib/b", "ln -s /data/code/zozo/spec/test1/b/c lib/b/c", "mkdir bin", "ln -s /data/code/zozo/spec/b/bin/b1 bin/b1", "mkdir bin/d", "ln -s /data/code/zozo/spec/b/bin/d/b2 bin/d/b2", "Writing lib/rubygems.rb", "Writing lib/ubygems.rb", "Writing lib/bundler.rb"]
173
+ se.read.should == ''
174
+ end
175
+ File.file?('lib/a').should be_true
176
+ File.symlink?('lib/a').should be_true
177
+ should_be_same('lib/a', 'test1/a')
178
+ File.directory?('lib/b').should be_true
179
+ File.file?('lib/b/c').should be_true
180
+ File.symlink?('lib/b/c').should be_true
181
+ should_be_same('lib/b/c', 'test1/b/c')
182
+
183
+ File.file?('bin/b1').should be_true
184
+ File.symlink?('bin/b1').should be_true
185
+ should_be_same('bin/b1', 'b/bin/b1')
186
+ File.directory?('bin/d').should be_true
187
+ File.file?('bin/d/b2').should be_true
188
+ File.symlink?('bin/d/b2').should be_true
189
+ should_be_same('bin/d/b2', 'b/bin/d/b2')
190
+ end
191
+
192
+ it "should handle rubygems' gem method correctly" do
193
+ run
194
+ `ruby -I lib test_rubygems.rb`.should == 'no_error'
195
+ end
196
+
197
+ it "should handle requiring rubygems as ubygems" do
198
+ run
199
+ `ruby -I lib test_ubygems.rb`.should == 'no_error'
200
+ end
201
+
202
+ it "should handle bundler's Bundler.setup method correctly" do
203
+ run
204
+ `ruby -I lib test_bundler.rb`.should == 'no_error'
205
+ end
206
+
207
+ it "should raise an error on file/directory overlap, with directory after file" do
208
+ run3('test2') do |si, so, se|
209
+ se.read.should =~ %r{File/directory overlap \(file: .*/zozo/spec/test2/b, directory: .*/zozo/spec/test1/b\) \(StandardError\)}
210
+ so.read.should == ''
211
+ end
212
+ end
213
+
214
+ it "should raise an error on file/directory overlap, with file after directory" do
215
+ run3('test3') do |si, so, se|
216
+ se.read.should =~ %r{File/directory overlap \(file: .*/zozo/spec/test2/b, directory: b\) \(StandardError\)}
217
+ so.read.should == ''
218
+ end
219
+ end
220
+
221
+ it "should define ZOZO environment variable when running under zozo" do
222
+ run('test_zozo').should == 'no_error'
223
+ end
224
+ end
225
+
226
+ context "zozo -b and -l arguments" do
227
+ after do
228
+ FileUtils.rm_r('li')
229
+ FileUtils.rm_r('bi')
230
+ end
231
+
232
+ def should_be_same(f1, f2)
233
+ File.read(f1).should == File.read(f2)
234
+ end
235
+
236
+ it "should create bin and lib directories with given names" do
237
+ `#{File.join(File.dirname(File.dirname(File.dirname(File.expand_path(__FILE__)))), 'bin', 'zozo')} -b bi -l li test1`
238
+ File.file?('li/a').should be_true
239
+ File.symlink?('li/a').should be_true
240
+ should_be_same('li/a', 'test1/a')
241
+ File.directory?('li/b').should be_true
242
+ File.file?('li/b/c').should be_true
243
+ File.symlink?('li/b/c').should be_true
244
+ should_be_same('li/b/c', 'test1/b/c')
245
+
246
+ File.file?('bi/b1').should be_true
247
+ File.symlink?('bi/b1').should be_true
248
+ should_be_same('bi/b1', 'b/bin/b1')
249
+ File.directory?('bi/d').should be_true
250
+ File.file?('bi/d/b2').should be_true
251
+ File.symlink?('bi/d/b2').should be_true
252
+ should_be_same('bi/d/b2', 'b/bin/d/b2')
253
+ end
254
+ end
metadata ADDED
@@ -0,0 +1,78 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: zozo
3
+ version: !ruby/object:Gem::Version
4
+ hash: 23
5
+ prerelease: false
6
+ segments:
7
+ - 1
8
+ - 0
9
+ - 0
10
+ version: 1.0.0
11
+ platform: ruby
12
+ authors:
13
+ - Jeremy Evans
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-07-01 00:00:00 -07:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description:
23
+ email: code@jeremyevans.net
24
+ executables:
25
+ - zozo
26
+ extensions: []
27
+
28
+ extra_rdoc_files:
29
+ - README.rdoc
30
+ - LICENSE
31
+ - bin/zozo
32
+ files:
33
+ - README.rdoc
34
+ - LICENSE
35
+ - bin/zozo
36
+ - spec/zozo_spec.rb
37
+ has_rdoc: true
38
+ homepage: http://github.com/jeremyevans/zozo
39
+ licenses: []
40
+
41
+ post_install_message:
42
+ rdoc_options:
43
+ - --quiet
44
+ - --line-numbers
45
+ - --inline-source
46
+ - --title
47
+ - "zozo: Simple $LOAD_PATH management for ruby projects"
48
+ - --main
49
+ - README.rdoc
50
+ require_paths:
51
+ - bin
52
+ required_ruby_version: !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ hash: 3
58
+ segments:
59
+ - 0
60
+ version: "0"
61
+ required_rubygems_version: !ruby/object:Gem::Requirement
62
+ none: false
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ hash: 3
67
+ segments:
68
+ - 0
69
+ version: "0"
70
+ requirements: []
71
+
72
+ rubyforge_project:
73
+ rubygems_version: 1.3.7
74
+ signing_key:
75
+ specification_version: 3
76
+ summary: Simple $LOAD_PATH management for ruby projects
77
+ test_files:
78
+ - spec/zozo_spec.rb