ghostbuster 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ ghost/log/thin.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in ghostbuster.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,53 @@
1
+ # Ghostbuster
2
+
3
+ Automated browser testing via phantom.js, with all of the pain taken out!
4
+
5
+ ## Installation
6
+
7
+ To install first `gem install ghostbuster`. Once you've done that, you can run `setup-ghostbuster`. Right now this only works on Mac, so, otherwise, ghostbuster will look for a copy of the `phantomjs` binary in `~/.ghostbuster`.
8
+
9
+ ## Usage
10
+
11
+ Once installed, you can simply use `ghostbuster path/to/tests` to run your tests. You should get some output that looks something liek this.
12
+
13
+ ~~~~
14
+
15
+ GhostBuster
16
+ For /Users/joshbuddy/Development/ghostbuster/ghost/test_ghost.coffee
17
+ ✓ Simple index
18
+ ✓ Form input
19
+ ✓ Link traversal
20
+ ✗ Bad link traversal
21
+ Assert location failed: Excepted http://127.0.0.1:4567/not-correct, got http://127.0.0.1:4567/
22
+ ✗ Form input not equal
23
+ Assert first for selector #out did not meet expectations
24
+
25
+ For /Users/joshbuddy/Development/ghostbuster/ghost/test_ghostmore.coffee
26
+ ✓ Simple form
27
+ • Form should do more things
28
+
29
+ ~~~~
30
+
31
+ Your test directory should look something like this:
32
+
33
+ ghost_tests/start.sh # Used to start your web application
34
+ ghost_tests/stop.sh # Used to stop your web application
35
+ ghost_tests/test_*.coffee # Your tests
36
+
37
+ Look inside `ghost` to see some examples of what actual tests would look like. Let's dive into a couple of simple examples.
38
+
39
+ ~~~~
40
+
41
+ phantom.test.root = "http://127.0.0.1:4567" # you must specify your root.
42
+
43
+ phantom.test.add "Simple index", -> # this adds a test
44
+ @get '/', -> # this will get your a path relative to your root
45
+ @body.assertFirst 'p', (p) -> # this asserts the first paragraph's inner text
46
+ p.innerHTML == 'This is my paragraph' # is 'This is my paragraph'
47
+ @body.assertAll 'ul li', (li, idx) ->
48
+ li.innerHTML == "List item #{idx + 1}"
49
+ @succeed() # all tests must succeed
50
+
51
+ ~~~~
52
+
53
+ To use this within rake, just put `require 'ghostbuster/install_rake'` in your Rakefile.
data/Rakefile ADDED
@@ -0,0 +1,3 @@
1
+ require 'bundler/gem_tasks'
2
+ $: << 'lib'
3
+ require 'ghostbuster/install_rake'
data/bin/ghostbuster ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'ghostbuster'
4
+ Ghostbuster::Runner.new(ARGV).run
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ case RUBY_PLATFORM
4
+ when /darwin/
5
+ require 'fileutils'
6
+ puts "Creating ghostbuster directory in your home"
7
+ root = File.expand_path(File.join(ENV['HOME'], '.ghostbuster'))
8
+ cache = File.expand_path(File.join(root, 'cache'))
9
+ unless File.exist?(File.join(root, 'version')) && File.read(File.join(root, 'version')) == '1'
10
+ FileUtils.rm_rf(root)
11
+ FileUtils.mkdir_p(root)
12
+ FileUtils.mkdir_p(cache)
13
+ phantom_url = "http://phantomjs.googlecode.com/files/phantomjs-1.2.0-macosx-universal.dmg"
14
+ unless File.exist?(File.join(cache, File.basename(phantom_url)))
15
+ Dir.chdir(cache) do
16
+ puts "Downloading and install phantom.js"
17
+ system("curl #{phantom_url} -O") or raise("Unable to download from #{phantom_url}")
18
+ system("open #{File.basename(phantom_url)}") or raise("Unable to open disk image")
19
+ end
20
+ system("cp -r /Volumes/phantomjs/phantomjs.app #{root}")
21
+ File.exist?("#{root}/phantomjs.app") or raise("Unable to copy phantomjs from your disk image")
22
+ system("ln -s #{root}/phantomjs.app/Contents/MacOS/phantomjs #{root}/phantomjs") or raise("Unable to copy phantomjs from your disk image")
23
+ `hdiutil eject /Volumes/phantomjs`
24
+ File.open("#{root}/version", 'w') {|f| f << '1' }
25
+ else
26
+ puts "Already installed and at the right version"
27
+ end
28
+ end
29
+ else
30
+ puts "I don't know how to install for RUBY_PLATFORM #{RUBY_PLATFORM}"
31
+ exit(1)
32
+ end
data/ghost/config.ru ADDED
@@ -0,0 +1,14 @@
1
+ require 'rubygems'
2
+ require 'sinatra'
3
+
4
+ class App < Sinatra::Base
5
+ get "/" do
6
+ erb :index
7
+ end
8
+
9
+ get "/form" do
10
+ erb :form
11
+ end
12
+ end
13
+
14
+ run App
data/ghost/start.sh ADDED
@@ -0,0 +1,3 @@
1
+ #!/bin/bash
2
+
3
+ bundle exec thin --port 4567 -P thin.pid -d -R config.ru start
data/ghost/stop.sh ADDED
@@ -0,0 +1,3 @@
1
+ #!/bin/bash
2
+
3
+ bundle exec thin --port 4567 -P thin.pid -d -R config.ru stop
@@ -0,0 +1,37 @@
1
+ phantom.test.root = "http://127.0.0.1:4567"
2
+
3
+ phantom.test.add "Simple index", ->
4
+ @get '/', ->
5
+ @body.assertFirst 'p', (p) ->
6
+ p.innerHTML == 'This is my paragraph'
7
+ @body.assertAll 'ul li', (li, idx) ->
8
+ li.innerHTML == "List item #{idx + 1}"
9
+ @succeed()
10
+
11
+ phantom.test.add "Form input", ->
12
+ @get '/form', ->
13
+ @body.input "#in", "this is my input"
14
+ @body.click "#btn"
15
+ @body.assertFirst '#out', (out) ->
16
+ out.innerHTML == 'this is my input'
17
+ @succeed()
18
+
19
+ phantom.test.add "Link traversal", ->
20
+ @get '/', ->
21
+ @body.click 'a'
22
+ @body.assertLocation('/form')
23
+ @succeed()
24
+
25
+ phantom.test.add "Bad link traversal", ->
26
+ @get '/', ->
27
+ @body.click 'a'
28
+ @body.assertLocation('/not-correct')
29
+ @succeed()
30
+
31
+ phantom.test.add "Form input not equal", ->
32
+ @get '/form', ->
33
+ @body.input "#in", "this is my input"
34
+ @body.click "#btn"
35
+ @body.assertFirst '#out', (out) ->
36
+ out.innerHTML == 'this is NOT my input'
37
+ @succeed()
@@ -0,0 +1,8 @@
1
+ phantom.test.root = "http://127.0.0.1:4567"
2
+
3
+ phantom.test.add "Simple form", ->
4
+ @get '/form', ->
5
+ @succeed()
6
+
7
+ phantom.test.addPending "Form should do more things", ->
8
+ console.log "some thing here.."
@@ -0,0 +1,12 @@
1
+ <html>
2
+ <head>
3
+ <title>This is my index</title>
4
+ </head>
5
+ <body>
6
+ <form>
7
+ <input type="text" id="in">
8
+ <input type="button" id="btn" onclick="document.getElementById('out').innerHTML = document.getElementById('in').value;">
9
+ </form>
10
+ <p id="out"></p>
11
+ </body>
12
+ </html>
@@ -0,0 +1,15 @@
1
+ <html>
2
+ <head>
3
+ <title>This is my index</title>
4
+ </head>
5
+ <body>
6
+ <h1>First header</h1>
7
+ <p>This is my paragraph</p>
8
+ <ul>
9
+ <li>List item 1</li>
10
+ <li>List item 2</li>
11
+ <li>List item 3</li>
12
+ </ul>
13
+ <a href="/form">form</a>
14
+ </body>
15
+ </html>
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "ghostbuster/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "ghostbuster"
7
+ s.version = Ghostbuster::VERSION
8
+ s.authors = ["Josh Hull"]
9
+ s.email = ["joshbuddy@gmail.com"]
10
+ s.homepage = "https://github.com/joshbuddy/ghostbuster"
11
+ s.summary = %q{Integration testing ftw}
12
+ s.description = %q{Integration testing ftw.}
13
+
14
+ s.rubyforge_project = "ghostbuster"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ s.add_development_dependency 'thin', '~> 1.2.11'
22
+ s.add_development_dependency 'rake', '~> 0.8.7'
23
+ s.add_development_dependency 'bundler', '~> 1.0.14'
24
+ s.add_development_dependency 'sinatra'
25
+ end
@@ -0,0 +1,241 @@
1
+ class Test
2
+ constructor: (@runner, @name, @testBody) ->
3
+ @page = new WebPage()
4
+ @page.onConsoleMessage = (msg) ->
5
+ console.log "PAGE CONSOLE: #{msg}"
6
+ testName = @name
7
+ @page.onAlert = (msg) =>
8
+ @runner.lastErrors[testName] = msg
9
+ @lastError = null
10
+ @assertions = []
11
+ @seenCallbacks = []
12
+ waitForAssertions: (whenDone) ->
13
+ if @assertions.length == 0
14
+ whenDone.call(this)
15
+ else
16
+ test = this
17
+ waiting = ->
18
+ test.waitForAssertions(whenDone)
19
+ setTimeout waiting, 10
20
+ run: (@callback) ->
21
+ @testBody.call(this)
22
+ get: (path, getCallback) ->
23
+ @waitForAssertions ->
24
+ test = this
25
+ loadedCallback = (status) ->
26
+ return if test.seenCallbacks.indexOf(getCallback) != -1
27
+ test.seenCallbacks.push getCallback #traversing links causes this to get re-fired.
28
+ switch status
29
+ when 'success'
30
+ test.body = new Body(test)
31
+ getCallback.call(test) if getCallback
32
+ when 'fail'
33
+ test.fail()
34
+ @page.open @runner.normalizePath(path), loadedCallback
35
+ succeed: ->
36
+ @waitForAssertions ->
37
+ @callback(true)
38
+ fail: (msg) ->
39
+ @callback(false, msg)
40
+ assert: (valueFetcher) ->
41
+ @assertions.push(new Assertion(this, valueFetcher))
42
+ @assertions[0].start() if @assertions.length == 1
43
+
44
+ class Assertion
45
+ constructor: (@test, @fetcher) ->
46
+ @count = 0
47
+ start: ->
48
+ test = @test
49
+ assertion = this
50
+ failedCallback = ->
51
+ assertion.start()
52
+ if @count == 0
53
+ fatalCallback = ->
54
+ test.fail(test.lastError || "This assertion failed to complete.")
55
+ @fatal = setTimeout(fatalCallback, 1000)
56
+ @fetcher.call test, (val) ->
57
+ assertion.count++
58
+ if val == true
59
+ delete test.runner.lastErrors[test.name];
60
+ test.assertions.splice(test.assertions.indexOf(assertion), 1)
61
+ clearTimeout assertion.fatal
62
+ if test.assertions.length > 0
63
+ test.assertions[0].start()
64
+ else if assertion.count > 10
65
+ clearTimeout assertion.fatal
66
+ test.fail(test.lastError)
67
+ else
68
+ setTimeout(failedCallback, 75)
69
+
70
+ class Body
71
+ constructor: (@test) ->
72
+ input: (selector, value) ->
73
+ eval "
74
+ var input = function() {
75
+ var value = '#{value}';
76
+ var list = document.querySelectorAll('#{selector}');
77
+ for(var i = 0; i != list.length; i++) {
78
+ list[i].value = value;
79
+ }
80
+ }
81
+ "
82
+ @test.page.evaluate(input)
83
+
84
+ click: (selector) ->
85
+ eval "
86
+ var fn = function() {
87
+ var targets = document.querySelectorAll('#{selector}'),
88
+ evt = document.createEvent('MouseEvents'),
89
+ i, len;
90
+ evt.initMouseEvent('click', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
91
+
92
+ for ( i = 0, len = targets.length; i < len; ++i ) {
93
+ targets[i].dispatchEvent(evt);
94
+ }
95
+ };
96
+ "
97
+ @test.page.evaluate(fn)
98
+
99
+ assertLocation: (path) ->
100
+ test = @test
101
+ location = @test.runner.normalizePath(path)
102
+ @test.assert (withValue) ->
103
+ alerter = if test.runner.lastErrors[test.name]? then "" else "alert('Assert location failed: Excepted #{location}, got '+currentLocation);"
104
+ eval "
105
+ var fn = function() {
106
+ var currentLocation = window.location.href;
107
+ if (window.location.href === '#{location}') {
108
+ return true;
109
+ } else {
110
+ #{alerter}
111
+ return false;
112
+ }
113
+ }
114
+ "
115
+ withValue @page.evaluate(fn)
116
+
117
+ assertFirst: (selector, assertionCallback) ->
118
+ test = @test
119
+ @test.assert (withValue) ->
120
+ alerter = if test.runner.lastErrors[test.name]? then "" else "alert('Assert first for selector #{selector} did not meet expectations');"
121
+ eval "
122
+ var evaluator = function() {
123
+ try {
124
+ var assertionCallback = #{assertionCallback.toString()};
125
+ var ret = assertionCallback(document.querySelector('#{selector}'));
126
+ if (ret) {
127
+ return true;
128
+ } else {
129
+ #{alerter}
130
+ return false;
131
+ }
132
+ } catch(e) {
133
+ var err = 'Assert first for selector #{selector} encountered an unexpected error:'+e;
134
+ console.log(err);
135
+ alert(err);
136
+ return false;
137
+ }
138
+ };
139
+ "
140
+ withValue @page.evaluate(evaluator)
141
+
142
+ assertAll: (selector, assertionCallback) ->
143
+ @test.assert (withValue) ->
144
+ eval "
145
+ var evaluator = function() {
146
+ try {
147
+ var assertionCallback = #{assertionCallback.toString()};
148
+ var list = document.querySelectorAll('#{selector}');
149
+ if (list.length == 0) throw('list is empty');
150
+ for (var i=0; i != list.length; i++) {
151
+ if (!assertionCallback(list[i], i)) {
152
+ alert('Assert all for selector #{selector} on item '+i+' didn\\'t meet expectations');
153
+ return false;
154
+ }
155
+ }
156
+ return true;
157
+ } catch(e) {
158
+ alert('Assert all for selector #{selector} encountered an unexpected error:'+e);
159
+ return false;
160
+ }
161
+ };
162
+ "
163
+ withValue @page.evaluate(evaluator)
164
+
165
+ class PendingTest
166
+ constructor: (@runner, @name) ->
167
+ run: (callback) -> callback('pending')
168
+
169
+ class TestFile
170
+ constructor: (@suite, @name) ->
171
+ @tests = []
172
+ @lastErrors = {}
173
+ normalizePath: (path) -> if path.match(/^http/) then path else "#{@root}#{path}"
174
+ addPending: (name, body) -> @tests.push new PendingTest(this, name)
175
+ add: (name, body) -> @tests.push new Test(this, name, body)
176
+ run: (callback) ->
177
+ throw "No root is defined" unless @root?
178
+ count = 0
179
+ testFile = this
180
+ testStates = {}
181
+ nextTest = ->
182
+ testFile.tests[count].run (state) ->
183
+ testStates[testFile.tests[count].name] = state
184
+ count++
185
+ if count < testFile.tests.length
186
+ nextTest()
187
+ else
188
+ testFile.report(testStates)
189
+ callback()
190
+ nextTest()
191
+ report: (testStates) ->
192
+ success = 0
193
+ failure = 0
194
+ pending = 0
195
+ console.log "For \033[1m#{@name}\033[0m"
196
+ for name, state of testStates
197
+ if state == true
198
+ success++
199
+ console.log " \033[32m\u2713\033[0m #{name}"
200
+ else if state == 'pending'
201
+ pending++
202
+ console.log " \033[33m\u2022\033[0m #{name}"
203
+ else
204
+ failure++
205
+ console.log " \033[31m\u2717\033[0m #{name}\n #{@lastErrors[name] || "There was a problem"}"
206
+ console.log ""
207
+ @suite.report(success, failure, pending)
208
+ console.log "GhostBuster"
209
+
210
+ class TestSuite
211
+ constructor: (@args) ->
212
+ @success = 0
213
+ @failure = 0
214
+ @pending = 0
215
+ report: (success, failure, pending) ->
216
+ @success += success
217
+ @failure += failure
218
+ @pending += pending
219
+ run: ->
220
+ count = 0
221
+ suite = this
222
+ runNextTest = ->
223
+ if suite.args.length == count
224
+ console.log "#{suite.success} success, #{suite.failure} failure, #{suite.pending} pending"
225
+ phantom.exit (if suite.failure == 0 then 0 else 1)
226
+ else
227
+ testFile = suite.args[count]
228
+ phantom.test = new TestFile(suite, testFile)
229
+ if phantom.injectJs(testFile)
230
+ phantom.test.run ->
231
+ count++
232
+ runNextTest()
233
+ else
234
+ console.log "Unable to load #{testFile}"
235
+ runNextTest()
236
+
237
+ if phantom.args.length == 0
238
+ console.log("You need to specify a test file")
239
+ else
240
+ suite = new TestSuite(phantom.args)
241
+ suite.run()
@@ -0,0 +1,34 @@
1
+ require 'ghostbuster/version'
2
+ require 'ghostbuster/shell'
3
+
4
+ class Ghostbuster
5
+ include Shell
6
+ autoload :Rake, 'ghostbuster/rake'
7
+ autoload :Runner, 'ghostbuster/runner'
8
+
9
+ def initialize(path)
10
+ @path = path
11
+ @dir = File.directory?(path) ? path : File.dirname(path)
12
+ @ghost_lib = File.expand_path(File.join(File.dirname(__FILE__), "ghostbuster.coffee"))
13
+ @phantom_bin = File.join(ENV['HOME'], '.ghostbuster', 'phantomjs')
14
+ end
15
+
16
+ def run
17
+ files = Dir[@path].to_a.map{|f| File.expand_path(f)}
18
+ status = 1
19
+ Dir.chdir(@dir) do
20
+ sh "./start.sh"
21
+ sleep 2
22
+ begin
23
+ _, status = Process.waitpid2 fork { exec("#{@phantom_bin} #{@ghost_lib} #{files.join(' ')}") }
24
+ ensure
25
+ sh "./stop.sh"
26
+ end
27
+ end
28
+ exit(status)
29
+ end
30
+
31
+ def self.run(path)
32
+ new(path).run
33
+ end
34
+ end
@@ -0,0 +1,2 @@
1
+ require 'ghostbuster'
2
+ include Ghostbuster::Rake
@@ -0,0 +1,18 @@
1
+ class Ghostbuster
2
+ module Rake
3
+ def self.included(o)
4
+ o.class_eval do
5
+ include Rake::DSL if defined? Rake::DSL
6
+ Ghostbuster::Rake.include_rake_tasks
7
+ end
8
+ end
9
+ def self.include_rake_tasks(opts = {})
10
+ opts[:task_name] ||= :"test:ghostbuster"
11
+ opts[:file_pattern] ||= "ghost/test_*.{coffee,js}"
12
+ desc "Run ghostbuster tasks"
13
+ task opts[:task_name] do
14
+ Ghostbuster.new(opts[:file_pattern]).run
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,17 @@
1
+ class Ghostbuster
2
+ class Runner
3
+ def initialize(args)
4
+ @args = args
5
+ end
6
+
7
+ def run
8
+ if @args.size != 1
9
+ puts "ghostbuster <path/to/tests>"
10
+ puts " Version #{VERSION}"
11
+ exit(1)
12
+ else
13
+ Ghostbuster.new(@args.first).run
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,14 @@
1
+ class Ghostbuster
2
+ module Shell
3
+ def sh(cmd, &block)
4
+ out, code = sh_with_code(cmd, &block)
5
+ code == 0 ? out : raise(out.empty? ? "Running `#{cmd}' failed. Run this command directly for more detailed output." : out)
6
+ end
7
+
8
+ def sh_with_code(cmd, &block)
9
+ outbuf = `#{cmd}`
10
+ block.call(outbuf) if block && $? == 0
11
+ [outbuf, $?]
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,3 @@
1
+ class Ghostbuster
2
+ VERSION = '0.0.1'
3
+ end
data/log/thin.log ADDED
@@ -0,0 +1,24 @@
1
+ >> Writing PID to thin.pid
2
+ >> Exiting!
3
+ /Users/joshbuddy/.rvm/gems/ree-1.8.7-2011.03/gems/thin-1.2.11/lib/rack/adapter/loader.rb:35:in `read': No such file or directory - config.ru (Errno::ENOENT)
4
+ from /Users/joshbuddy/.rvm/gems/ree-1.8.7-2011.03/gems/thin-1.2.11/lib/rack/adapter/loader.rb:35:in `load'
5
+ from /Users/joshbuddy/.rvm/gems/ree-1.8.7-2011.03/gems/thin-1.2.11/lib/thin/controllers/controller.rb:181:in `load_rackup_config'
6
+ from /Users/joshbuddy/.rvm/gems/ree-1.8.7-2011.03/gems/thin-1.2.11/lib/thin/controllers/controller.rb:71:in `start'
7
+ from /Users/joshbuddy/.rvm/gems/ree-1.8.7-2011.03/gems/thin-1.2.11/lib/thin/runner.rb:185:in `send'
8
+ from /Users/joshbuddy/.rvm/gems/ree-1.8.7-2011.03/gems/thin-1.2.11/lib/thin/runner.rb:185:in `run_command'
9
+ from /Users/joshbuddy/.rvm/gems/ree-1.8.7-2011.03/gems/thin-1.2.11/lib/thin/runner.rb:151:in `run!'
10
+ from /Users/joshbuddy/.rvm/gems/ree-1.8.7-2011.03/gems/thin-1.2.11/bin/thin:6
11
+ from /Users/joshbuddy/.rvm/gems/ree-1.8.7-2011.03/bin/thin:19:in `load'
12
+ from /Users/joshbuddy/.rvm/gems/ree-1.8.7-2011.03/bin/thin:19
13
+ >> Writing PID to thin.pid
14
+ >> Exiting!
15
+ /Users/joshbuddy/.rvm/gems/ree-1.8.7-2011.03/gems/thin-1.2.11/lib/rack/adapter/loader.rb:35:in `read': No such file or directory - config.ru (Errno::ENOENT)
16
+ from /Users/joshbuddy/.rvm/gems/ree-1.8.7-2011.03/gems/thin-1.2.11/lib/rack/adapter/loader.rb:35:in `load'
17
+ from /Users/joshbuddy/.rvm/gems/ree-1.8.7-2011.03/gems/thin-1.2.11/lib/thin/controllers/controller.rb:181:in `load_rackup_config'
18
+ from /Users/joshbuddy/.rvm/gems/ree-1.8.7-2011.03/gems/thin-1.2.11/lib/thin/controllers/controller.rb:71:in `start'
19
+ from /Users/joshbuddy/.rvm/gems/ree-1.8.7-2011.03/gems/thin-1.2.11/lib/thin/runner.rb:185:in `send'
20
+ from /Users/joshbuddy/.rvm/gems/ree-1.8.7-2011.03/gems/thin-1.2.11/lib/thin/runner.rb:185:in `run_command'
21
+ from /Users/joshbuddy/.rvm/gems/ree-1.8.7-2011.03/gems/thin-1.2.11/lib/thin/runner.rb:151:in `run!'
22
+ from /Users/joshbuddy/.rvm/gems/ree-1.8.7-2011.03/gems/thin-1.2.11/bin/thin:6
23
+ from /Users/joshbuddy/.rvm/gems/ree-1.8.7-2011.03/bin/thin:19:in `load'
24
+ from /Users/joshbuddy/.rvm/gems/ree-1.8.7-2011.03/bin/thin:19
metadata ADDED
@@ -0,0 +1,151 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ghostbuster
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Josh Hull
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-08-26 00:00:00 -07:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ prerelease: false
23
+ version_requirements: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ~>
27
+ - !ruby/object:Gem::Version
28
+ hash: 9
29
+ segments:
30
+ - 1
31
+ - 2
32
+ - 11
33
+ version: 1.2.11
34
+ requirement: *id001
35
+ name: thin
36
+ type: :development
37
+ - !ruby/object:Gem::Dependency
38
+ prerelease: false
39
+ version_requirements: &id002 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ~>
43
+ - !ruby/object:Gem::Version
44
+ hash: 49
45
+ segments:
46
+ - 0
47
+ - 8
48
+ - 7
49
+ version: 0.8.7
50
+ requirement: *id002
51
+ name: rake
52
+ type: :development
53
+ - !ruby/object:Gem::Dependency
54
+ prerelease: false
55
+ version_requirements: &id003 !ruby/object:Gem::Requirement
56
+ none: false
57
+ requirements:
58
+ - - ~>
59
+ - !ruby/object:Gem::Version
60
+ hash: 11
61
+ segments:
62
+ - 1
63
+ - 0
64
+ - 14
65
+ version: 1.0.14
66
+ requirement: *id003
67
+ name: bundler
68
+ type: :development
69
+ - !ruby/object:Gem::Dependency
70
+ prerelease: false
71
+ version_requirements: &id004 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ hash: 3
77
+ segments:
78
+ - 0
79
+ version: "0"
80
+ requirement: *id004
81
+ name: sinatra
82
+ type: :development
83
+ description: Integration testing ftw.
84
+ email:
85
+ - joshbuddy@gmail.com
86
+ executables:
87
+ - ghostbuster
88
+ - setup-ghostbuster
89
+ extensions: []
90
+
91
+ extra_rdoc_files: []
92
+
93
+ files:
94
+ - .gitignore
95
+ - Gemfile
96
+ - README.md
97
+ - Rakefile
98
+ - bin/ghostbuster
99
+ - bin/setup-ghostbuster
100
+ - ghost/config.ru
101
+ - ghost/start.sh
102
+ - ghost/stop.sh
103
+ - ghost/test_ghost.coffee
104
+ - ghost/test_ghostmore.coffee
105
+ - ghost/views/form.erb
106
+ - ghost/views/index.erb
107
+ - ghostbuster.gemspec
108
+ - lib/ghostbuster.coffee
109
+ - lib/ghostbuster.rb
110
+ - lib/ghostbuster/install_rake.rb
111
+ - lib/ghostbuster/rake.rb
112
+ - lib/ghostbuster/runner.rb
113
+ - lib/ghostbuster/shell.rb
114
+ - lib/ghostbuster/version.rb
115
+ - log/thin.log
116
+ has_rdoc: true
117
+ homepage: https://github.com/joshbuddy/ghostbuster
118
+ licenses: []
119
+
120
+ post_install_message:
121
+ rdoc_options: []
122
+
123
+ require_paths:
124
+ - lib
125
+ required_ruby_version: !ruby/object:Gem::Requirement
126
+ none: false
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ hash: 3
131
+ segments:
132
+ - 0
133
+ version: "0"
134
+ required_rubygems_version: !ruby/object:Gem::Requirement
135
+ none: false
136
+ requirements:
137
+ - - ">="
138
+ - !ruby/object:Gem::Version
139
+ hash: 3
140
+ segments:
141
+ - 0
142
+ version: "0"
143
+ requirements: []
144
+
145
+ rubyforge_project: ghostbuster
146
+ rubygems_version: 1.6.2
147
+ signing_key:
148
+ specification_version: 3
149
+ summary: Integration testing ftw
150
+ test_files: []
151
+