ghostbuster 0.0.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.
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
+