bake.clj 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (5) hide show
  1. data/bake.clj.gemspec +14 -0
  2. data/bin/bake +600 -0
  3. data/lib/bake.jar +0 -0
  4. data/lib/cake.jar +0 -0
  5. metadata +71 -0
data/bake.clj.gemspec ADDED
@@ -0,0 +1,14 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'bake.clj'
3
+ s.rubyforge_project = 'bake.clj'
4
+ s.version = ENV['BAKE_VERSION']
5
+ s.authors = ['Justin Balthrop', 'Lance Bradley']
6
+ s.date = Time.now
7
+ s.default_executable = 'bake'
8
+ s.summary = 'Build tools got you down? Get baked!'
9
+ s.description = 'Add equal parts clojure, ruby, love, and tenderness. Bake at 350 for 1 minute. Delicious.'
10
+ s.email = 'code@justinbalthrop.com'
11
+ s.executables = ['bake']
12
+ s.files = ['bake.clj.gemspec', 'bin/bake', 'lib/bake.jar', 'lib/cake.jar']
13
+ s.homepage = 'http://github.com/ninjudd/bake'
14
+ end
data/bin/bake ADDED
@@ -0,0 +1,600 @@
1
+ #!/usr/bin/env ruby
2
+ # Save your fork, there's bake!"
3
+ require 'ftools'
4
+ require 'find'
5
+ require 'open-uri'
6
+ require 'rexml/document'
7
+ require 'socket'
8
+ require 'timeout'
9
+ require 'fileutils'
10
+
11
+ begin
12
+ require 'readline'
13
+ rescue LoadError => e
14
+ module Readline
15
+ HISTORY = []
16
+ attr_accessor :basic_word_break_characters, :completion_proc
17
+ def readline(prompt)
18
+ $stdout.print prompt
19
+ $stdout.flush
20
+ $stdin.gets
21
+ end
22
+ extend Readline
23
+ end
24
+ end
25
+
26
+ def project_dir(dir)
27
+ while dir != "/"
28
+ return dir if [".bake", "project.clj", "build.clj"].any? {|file| File.exists?("#{dir}/#{file}")}
29
+ dir = File.dirname(dir)
30
+ end
31
+ File.expand_path("~")
32
+ end
33
+
34
+ PATH_SEP = ':'
35
+ file = File.readlink(__FILE__) rescue __FILE__
36
+ $cakedir = project_dir(Dir.getwd)
37
+ $bakedir = File.dirname(File.dirname(file))
38
+ $repo = "http://clojars.org/repo/bake/bake"
39
+ $confdir = ".bake"
40
+ Dir.chdir($cakedir)
41
+ File.makedirs($confdir)
42
+
43
+ def get_bake(version, dest = nil)
44
+ jar = version =~ /(.*)-SNAPSHOT/ ? "bake-#{$1}-#{snapshot(version)}.jar" : "bake-#{version}.jar"
45
+ repo = File.expand_path("~/.m2/repository/bake/bake/#{version}")
46
+ path = "#{repo}/#{jar}"
47
+
48
+ if not File.exists?(path)
49
+ log(:deps, "fetching bake libraries. this may take a moment...")
50
+ url = "#{$repo}/#{version}/#{jar}"
51
+ File.makedirs(repo)
52
+ open(url) do |jarfile|
53
+ open(path, "wb") do |file|
54
+ while (buf = jarfile.read(8192))
55
+ file.write buf
56
+ end
57
+ end
58
+ end
59
+ end
60
+ return path unless dest
61
+
62
+ dest = File.expand_path(dest)
63
+ File.makedirs(dest)
64
+ File.copy(path, dest)
65
+ "#{dest}/#{jar}"
66
+ rescue OpenURI::HTTPError => e
67
+ log(:deps, "unable to find bake version #{version} on clojars.")
68
+ log(:deps, "please check http://github.com/ninjudd/bake for latest install instructions.")
69
+ exit
70
+ end
71
+
72
+ def current_version
73
+ open("#{$repo}/maven-metadata.xml") do |file|
74
+ doc = REXML::Document.new file
75
+ doc.elements['metadata'].elements['versioning'].elements['versions'].elements.to_a('version').last.get_text.to_s
76
+ end
77
+ end
78
+
79
+ def snapshot(version)
80
+ open("#{$repo}/#{version}/maven-metadata.xml") do |file|
81
+ doc = REXML::Document.new file
82
+ snapshot = doc.elements['metadata'].elements['versioning'].elements['snapshot'].elements
83
+ timestamp = snapshot['timestamp'].get_text
84
+ build_num = snapshot['buildNumber'].get_text
85
+ "#{timestamp}-#{build_num}"
86
+ end
87
+ end
88
+
89
+ def extract(jar, file, dest = File.dirname(jar))
90
+ if not File.exists?("#{dest}/#{file}")
91
+ log(:deps, "extracting #{file} from #{jar}") if verbose?
92
+ ret = system %{ jar xf #{jar} #{file} }
93
+ raise "cannot find jar command" unless ret
94
+ File.makedirs(dest)
95
+ File.move(file, dest)
96
+ end
97
+ "#{dest}/#{file}"
98
+ end
99
+
100
+ def newer?(file1, file2)
101
+ return false unless File.exists?(file1)
102
+ not File.exists?(file2) or test(?>, file1, file2)
103
+ end
104
+
105
+ def add_opt!(key, *vals)
106
+ ($opts[key.to_sym] ||= []).concat vals
107
+ end
108
+
109
+ def parse_opts!
110
+ ARGV.unshift('default') if ARGV.empty? or ARGV.first[0,1] == '-'
111
+ $command = ARGV.shift.to_sym
112
+ $opts = {}
113
+ ARGV.each do |opt|
114
+ case opt
115
+ when /^-(\w*)$/ then $1.split('').each {|c| add_opt!(c, '')}
116
+ when /^--?([-\w]*)=([-,\w]+)$/ then add_opt!($1, *$2.split(','))
117
+ when /^--?([-\w]*)$/ then add_opt!($1, "")
118
+ else add_opt!($command, opt)
119
+ end
120
+ end
121
+ $opts.freeze
122
+ end
123
+ parse_opts!
124
+
125
+ def debug?
126
+ ENV['BAKE_DEBUG'] or $opts[:d] or $opts[:debug]
127
+ end
128
+
129
+ def verbose?
130
+ debug? or $opts[:v] or $opts[:verbose]
131
+ end
132
+
133
+ def config_updated?
134
+ newer?(".bake/config", ".bake/bake.pid") or (File.exists?(".bake/cake.pid") and newer?(".bake/config", ".bake/cake.pid"))
135
+ end
136
+
137
+ def restart?
138
+ $opts[:r] or $opts[:restart] or [:stop, :restart].include?($command)
139
+ end
140
+
141
+ def admin_command?
142
+ [:start, :stop, :reload, :restart].include?($command)
143
+ end
144
+
145
+ def log(command, *message)
146
+ message.each do |line|
147
+ printf("%11s %s\n", "[#{command}]", line)
148
+ end
149
+ end
150
+
151
+ class Config < Hash
152
+ def initialize(path)
153
+ File.open(path, 'r') do |file|
154
+ file.each do |line|
155
+ next if ['#', '!'].include?(line[0,1])
156
+ key, value = line.split('=', 2)
157
+ next unless key and value
158
+ self[key.strip] = value.strip
159
+ end
160
+ end if File.exists?(path)
161
+ end
162
+
163
+ def [](*keys)
164
+ if keys.first.kind_of?(Symbol)
165
+ key = keys.join('.') + '.'
166
+ clone.delete_if {|k,v| not k.index(key) == 0}
167
+ else
168
+ super
169
+ end
170
+ end
171
+ end
172
+
173
+ def config
174
+ @config ||= Config.new(".bake/config")
175
+ end
176
+
177
+ class JVM
178
+ attr_reader :type, :classpath, :libpath, :port, :pid, :pidfile, :load_time
179
+
180
+ def initialize(classpath, libpath)
181
+ @type = self.class.name.downcase
182
+ @classpath = make_path(classpath)
183
+ @libpath = make_path(libpath)
184
+ @pidfile = "#{$confdir}/#{type}.pid"
185
+ @load_time = File.exists?(pidfile) ? File.mtime(pidfile) : Time.now
186
+ refresh
187
+ rescue Errno::ENOENT, Errno::ESRCH, Errno::ECONNREFUSED => e
188
+ log(:start, "defunct #{type} jvm") if debug? and e.kind_of?(Errno::ECONNREFUSED)
189
+ reset! # no pidfile or invalid pid or connection refused
190
+ end
191
+
192
+ def running?
193
+ not pid.nil?
194
+ end
195
+
196
+ def enabled?
197
+ true
198
+ end
199
+
200
+ def init
201
+ stale = reload_stale_files if enabled? and running?
202
+ start
203
+ stale
204
+ end
205
+
206
+ def refresh
207
+ @pid, @port = IO.read(pidfile).map {|l| l.to_i}
208
+ Process.kill(0, @pid) # make sure pid is valid
209
+ TCPSocket.new("localhost", @port) if @port # make sure jvm is running on port
210
+ end
211
+
212
+ def reload
213
+ refresh
214
+ init
215
+ end
216
+
217
+ def reload_stale_files
218
+ stale_files = files.select {|f| stale?(f)}
219
+
220
+ if stale_files.empty?
221
+ log(:reload, "no stale #{type} files found") if verbose? and admin_command?
222
+ false
223
+ else
224
+ log(:reload, "the following #{type} files have changed: #{stale_files.join(', ')}") if debug?
225
+
226
+ if stale_files.any? {|f| File.extname(f) != ".clj"}
227
+ log(:reload, "non-clojure #{type} files have changed, restarting") if verbose?
228
+ stop(:reload)
229
+ else
230
+ log(:reload, "clojure #{type} files have changed, reloading") if verbose?
231
+ with_socket(0) do |socket|
232
+ stale_files = stale_files.collect {|f| %{"#{f}"} }
233
+ socket.write ":reload [#{stale_files.join(" ")}]\n"
234
+ if socket.eof?
235
+ FileUtils.touch(pidfile)
236
+ @load_time = Time.now
237
+ else
238
+ inspect(socket) if debug?
239
+ log(:reload, "unable to reload all #{type} files, restarting") if verbose?
240
+ stop(:reload)
241
+ end
242
+ end
243
+ end
244
+ true
245
+ end
246
+ end
247
+
248
+ def java_opts
249
+ opts = "-cp #{classpath} -Djava.library.path=#{libpath}"
250
+ opts << " -Djava.home=#{ENV['JAVA_HOME']} -Djava.ext.dirs=#{ENV['JAVA_HOME']}/lib" if ENV['JAVA_HOME']
251
+ opts
252
+ end
253
+
254
+ MIN_PORT = 2**13
255
+ MAX_PORT = 2**16
256
+
257
+ def start
258
+ return unless enabled?
259
+ if running?
260
+ log(:start, "#{type} jvm already running") if $command == :start
261
+ return
262
+ else
263
+ log(:start, "starting #{type} jvm") if verbose? or $command == :start
264
+ @port = rand(MAX_PORT - MIN_PORT) + MIN_PORT
265
+ @pid = daemon %{ java -Dbake.project=#{$cakedir} #{java_opts} clojure.main -e "(use '#{type})(start-server #{port})" /dev/null }
266
+ File.open(pidfile, 'w') {|f| f.write("#{pid}\n#{port}\n") }
267
+ end
268
+ rescue Errno::EADDRNOTAVAIL => e
269
+ retry
270
+ end
271
+
272
+ def stop(mode = :stop)
273
+ with_socket(0) do |socket|
274
+ action = mode == :reload ? 'quit' : 'force-quit'
275
+ log(mode, "sending #{action} to #{type} jvm on port #{port}") if debug?
276
+ socket.write(":#{action}\n")
277
+ if socket.eof?
278
+ log(mode, "#{type} jvm stopped") if restart?
279
+ reset!
280
+ else
281
+ inspect(socket) if debug?
282
+ if mode == :stop
283
+ log(mode, "error stopping #{type} jvm, try 'bake kill' or 'bake kill -9'")
284
+ else
285
+ log(mode, "warning: classes have changed but can't restart #{type} jvm with active connections")
286
+ log(mode, " close active connections or use 'bake stop' to force quit")
287
+ end
288
+ end
289
+ end || (log(mode, "#{type} jvm not running") if $command == :stop)
290
+ end
291
+
292
+ def kill
293
+ Process.kill($opts[:"9"] ? "KILL" : "TERM", pid) if pid
294
+ end
295
+
296
+ def files
297
+ files = ["project.clj"]
298
+ classpath.split(PATH_SEP).uniq.each do |path|
299
+ if path =~ /(.*)\/\*$/
300
+ files.concat Dir["#{path}.jar"] << "#{$1}/." # include the directory itself so deletions will be detected
301
+ else
302
+ Find.find(path) do |f|
303
+ Find.prune if f == "#{$cakedir}/src/jvm"
304
+ files << f if File.exists?(f) and not File.directory?(f)
305
+ end
306
+ end
307
+ end
308
+ files
309
+ end
310
+
311
+ def ping
312
+ return unless enabled?
313
+ with_socket(10) do |socket|
314
+ socket.write ":ping\n"
315
+ log($command, "#{type} jvm not running") unless socket.gets == "pong\n"
316
+ end
317
+ end
318
+
319
+ REPL_PROMPT = "REPL_PROMPT__#{rand}"
320
+ def repl
321
+ load_history
322
+ loop do
323
+ with_socket do |socket|
324
+ socket.write %{:repl "#{REPL_PROMPT}"}
325
+ while @ns = read_until_prompt(socket)
326
+ line = readline
327
+ return unless line
328
+ socket.write(line + "\n")
329
+ end
330
+ end
331
+ end
332
+ ensure
333
+ save_history
334
+ end
335
+
336
+ private
337
+
338
+ def make_path(paths)
339
+ paths.flatten.compact.collect do |path|
340
+ path[0,1] == '/' ? path : "#{$cakedir}/#{path}"
341
+ end.join(PATH_SEP)
342
+ end
343
+
344
+ def inspect(socket)
345
+ while line = socket.gets
346
+ puts line
347
+ end
348
+ end
349
+
350
+ def reset!
351
+ File.unlink(pidfile) if File.exists?(pidfile)
352
+ @pid, @port = []
353
+ @load_time = Time.now
354
+ end
355
+
356
+ def stale?(file)
357
+ File.exists?(file) and File.mtime(file) > load_time
358
+ end
359
+
360
+ def daemon(cmd)
361
+ puts cmd if debug?
362
+ pid = fork do
363
+ Process.setsid
364
+ exec(cmd)
365
+ end
366
+ Process.detach(pid)
367
+ pid
368
+ end
369
+
370
+ def with_socket(retries = 10)
371
+ return unless port
372
+ socket = TCPSocket.new("localhost", port)
373
+ result = yield(socket)
374
+ result
375
+ rescue Errno::ECONNREFUSED => e
376
+ sleep 1
377
+ retry if (retries -= 1) >= 0
378
+ exit
379
+ ensure
380
+ socket.close if socket
381
+ end
382
+
383
+ HISTORY_NUM = 500
384
+ HISTORY_FILE = ".bake/history"
385
+ def load_history
386
+ open(HISTORY_FILE) do |file|
387
+ file.each {|line| Readline::HISTORY << line.chomp}
388
+ end if File.exists?(HISTORY_FILE)
389
+ end
390
+
391
+ def save_history
392
+ open(HISTORY_FILE, 'w') do |file|
393
+ history = Readline::HISTORY.to_a
394
+ file.puts(history[-HISTORY_NUM..-1] || history)
395
+ end
396
+ end
397
+
398
+ def read_until_prompt(socket)
399
+ while line = socket.gets
400
+ return $1 if line =~ /^#{REPL_PROMPT}(.*)$/
401
+ puts line
402
+ end
403
+ end
404
+
405
+ def complete?(input)
406
+ return true if input.empty?
407
+ with_socket do |socket|
408
+ socket.write(":validate #{input.join("\n").strip}")
409
+ socket.close_write # send eof
410
+ socket.gets != "incomplete\n"
411
+ end
412
+ end
413
+
414
+ Readline.basic_word_break_characters = " \t\n\"'`~@;#&{}()[]"
415
+ def readline
416
+ input = []
417
+ prompt = "#{@ns}=> "
418
+ Readline.completion_proc = method(:completions)
419
+ while line = Readline.readline(prompt)
420
+ input << line
421
+ if complete?(input)
422
+ Readline::HISTORY.push(input.join(' ').strip)
423
+ return input.join("\n").strip
424
+ end
425
+ prompt[-2] = ?*
426
+ end
427
+ rescue Interrupt => e
428
+ return nil if input.empty?
429
+ Readline::HISTORY.push(input)
430
+ retry
431
+ end
432
+
433
+ def completions(prefix)
434
+ return [] if prefix.empty?
435
+ with_socket do |socket|
436
+ socket.write(":completions [\"#{prefix}\" #{@ns}]\n")
437
+ completions = []
438
+ while line = socket.gets
439
+ completions << line.chomp
440
+ end
441
+ completions
442
+ end
443
+ end
444
+ end
445
+
446
+ class Bake < JVM
447
+ attr_accessor :cakeport
448
+
449
+ READLINE = "READLINE__#{rand}"
450
+ def send_command(command, args = [])
451
+ with_socket do |socket|
452
+ args = args.collect{|arg| '"' + arg.gsub('"', '\"').gsub("\n", "\\n") + '"'}
453
+ cmd = %{(#{command} [#{args.join(' ')}] #{cakeport}) "#{READLINE}"}
454
+ log(command, "sending: " + cmd) if debug?
455
+ socket.write(cmd)
456
+ while (line = socket.gets)
457
+ if line =~ /^#{READLINE}(.*)$/
458
+ socket.write(prompt($1))
459
+ elsif line =~ /^@#{READLINE}(.*)$/
460
+ socket.write(prompt($1, :echo => false))
461
+ else
462
+ puts line
463
+ end
464
+ end
465
+ end
466
+ end
467
+
468
+ def files
469
+ super.concat ["build.clj", ".bake/swank"]
470
+ end
471
+
472
+ def java_opts
473
+ [ENV['BAKE_JAVA_OPTS'], config['bake.java_opts'], super].compact.join(' ')
474
+ end
475
+
476
+ private
477
+
478
+ def prompt(prompt, opts = {})
479
+ if opts[:echo] == false
480
+ echo_off = system("stty -echo 2&> /dev/null")
481
+ prompt << " (WARNING, input will be visible on console!)" unless echo_off
482
+ end
483
+ input = Readline.readline(prompt + ": ") || ''
484
+ input + "\n"
485
+ ensure
486
+ if echo_off
487
+ system("stty echo 2&> /dev/null")
488
+ puts
489
+ end
490
+ end
491
+ end
492
+
493
+ class Cake < JVM
494
+ def java_opts
495
+ [ENV['JAVA_OPTS'], config['project.java_opts'], super].compact.join(' ')
496
+ end
497
+
498
+ def enabled?
499
+ Dir["lib/*.jar"].any?
500
+ end
501
+ end
502
+
503
+ # Bootstrap bake dependencies.
504
+ lib = "#{$bakedir}/lib"
505
+ if File.exists?("#{$bakedir}/.gitignore")
506
+ if Dir["#{lib}/*.jar"].empty? or Dir["#{lib}/dev/*.jar"].empty?
507
+ # In a new git checkout, need to fetch dependencies.
508
+ version = IO.read("#{$bakedir}/project.clj").split("\n").first.match(/defproject bake \"(.*)\"/)[1]
509
+ bakejar = get_bake(version, lib)
510
+ extract(bakejar, "cake-#{version}.jar", "#{lib}/dev")
511
+ end
512
+
513
+ bakepath = ["#{$bakedir}/src", "#{lib}/*:#{lib}/dev/*"].join(PATH_SEP)
514
+ cakepath = ["#{$bakedir}/cake", "#{lib}/dev/*"].join(PATH_SEP)
515
+ else
516
+ bakejar = "#{lib}/bake.jar"
517
+ cakejar = "#{lib}/cake.jar"
518
+ if File.exists?(bakejar) and File.exists?(cakejar)
519
+ # Inside a gem install.
520
+ bakepath = bakejar
521
+ cakepath = cakejar
522
+ else
523
+ # Naked script.
524
+ version = current_version
525
+ bakepath = get_bake(version)
526
+ cakepath = extract(bakepath, "cake-#{version}.jar")
527
+ end
528
+ end
529
+
530
+ bake = Bake.new(
531
+ [bakepath, "src", "src/clj", config['bake.claspath'], "lib/dev/*"],
532
+ [config['bake.library.path'], "lib/dev/native"]
533
+ )
534
+ cake = Cake.new(
535
+ [cakepath, "src", "src/clj", "classes", "test", config['project.classpath'], "lib/*", "lib/dev/*"],
536
+ [config['project.library.path'], "lib/native", "lib/dev/native"]
537
+ )
538
+
539
+ if $command == :kill
540
+ if $opts[:all]
541
+ `jps -v | grep bake.project`.split("\n").each do |line|
542
+ pid = line.split(' ').first.to_i
543
+ Process.kill($opts[:"9"] ? "KILL" : "TERM", pid)
544
+ end
545
+ else
546
+ bake.kill
547
+ cake.kill
548
+ end
549
+ exit
550
+ elsif $command == :ps
551
+ puts `jps -v | grep bake.project`.sort.reverse
552
+ exit
553
+ elsif restart? or config_updated?
554
+ bake.stop
555
+ cake.stop
556
+ exit if $command == :stop
557
+ end
558
+
559
+ bake.init
560
+ if not [:deps, :clean].include?($command) and File.exists?('project.clj')
561
+ if newer?('project.clj', 'pom.xml')
562
+ bake.send_command(:deps)
563
+ bake.init
564
+ elsif File.exist?('.bake/swank')
565
+ bake.send_command(:"swank-deps")
566
+ bake.init
567
+ end
568
+
569
+ cake.init
570
+ end
571
+
572
+ if $command == :repl
573
+ if File.exists?('project.clj') and not $opts[:bake]
574
+ cake.repl
575
+ else
576
+ bake.repl
577
+ end
578
+ elsif [:start, :reload, :restart].include?($command)
579
+ if $opts[:l] or $opts[:log]
580
+ system("tail -f .bake/cake.log")
581
+ else
582
+ bake.ping
583
+ cake.ping
584
+ end
585
+ else
586
+ bake.cakeport = cake.port
587
+ if $command == :test and $opts[:auto]
588
+ interval = $opts[:auto].last.to_i
589
+ interval = config['test.auto.interval'].to_i if config['test.auto.interval'] || 1 if interval == 0
590
+ run_tests = true
591
+ while true
592
+ bake.send_command($command, ARGV) if run_tests
593
+ run_tests = cake.reload
594
+ bake.reload
595
+ sleep(interval)
596
+ end
597
+ else
598
+ bake.send_command($command, ARGV)
599
+ end
600
+ end
data/lib/bake.jar ADDED
Binary file
data/lib/cake.jar ADDED
Binary file
metadata ADDED
@@ -0,0 +1,71 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bake.clj
3
+ version: !ruby/object:Gem::Version
4
+ hash: 17
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 3
9
+ - 1
10
+ version: 0.3.1
11
+ platform: ruby
12
+ authors:
13
+ - Justin Balthrop
14
+ - Lance Bradley
15
+ autorequire:
16
+ bindir: bin
17
+ cert_chain: []
18
+
19
+ date: 2010-07-23 00:00:00 -07:00
20
+ default_executable: bake
21
+ dependencies: []
22
+
23
+ description: Add equal parts clojure, ruby, love, and tenderness. Bake at 350 for 1 minute. Delicious.
24
+ email: code@justinbalthrop.com
25
+ executables:
26
+ - bake
27
+ extensions: []
28
+
29
+ extra_rdoc_files: []
30
+
31
+ files:
32
+ - bake.clj.gemspec
33
+ - bin/bake
34
+ - lib/bake.jar
35
+ - lib/cake.jar
36
+ has_rdoc: true
37
+ homepage: http://github.com/ninjudd/bake
38
+ licenses: []
39
+
40
+ post_install_message:
41
+ rdoc_options: []
42
+
43
+ require_paths:
44
+ - lib
45
+ required_ruby_version: !ruby/object:Gem::Requirement
46
+ none: false
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ hash: 3
51
+ segments:
52
+ - 0
53
+ version: "0"
54
+ required_rubygems_version: !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ hash: 3
60
+ segments:
61
+ - 0
62
+ version: "0"
63
+ requirements: []
64
+
65
+ rubyforge_project: bake.clj
66
+ rubygems_version: 1.3.7
67
+ signing_key:
68
+ specification_version: 3
69
+ summary: Build tools got you down? Get baked!
70
+ test_files: []
71
+