gorp 0.7.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Manifest +7 -0
- data/README +41 -0
- data/Rakefile +31 -0
- data/gorp.gemspec +61 -0
- data/lib/gorp/test.rb +182 -0
- data/lib/gorp.rb +705 -0
- data/lib/version.rb +9 -0
- metadata +148 -0
data/Manifest
ADDED
data/README
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
Running this should only require Rails 2.2.2 or later, and the command line
|
2
|
+
interfaces for sqlite3 and curl. Tested on Ubuntu Linux 9.04 ("Jaunty
|
3
|
+
Jackalope") and Mac OSX 10.5.6 ("Leopard").
|
4
|
+
|
5
|
+
Installation of all necessary dependencies from a fresh install of Ubuntu 9.04:
|
6
|
+
|
7
|
+
sudo apt-get install rails git-core sqlite3 curl
|
8
|
+
sudo gem install rubygems-update
|
9
|
+
sudo /var/lib/gems/1.8/bin/update_rubygems
|
10
|
+
sudo gem install rails
|
11
|
+
sudo gem sources -a http://gems.github.com
|
12
|
+
sudo gem install rubys-gorp
|
13
|
+
|
14
|
+
Execution instructions:
|
15
|
+
|
16
|
+
This is a library which, among other things, will interpret ARGV. Here's
|
17
|
+
an example based on http://github.com/rubys/awdwr:
|
18
|
+
|
19
|
+
ruby makedepot.rb [VERSION] [restore] [RANGE]... [save]
|
20
|
+
|
21
|
+
"restore" - restore from snapshot before resuming execution
|
22
|
+
|
23
|
+
"VERSION" specifies the Rails version to test. Examples:
|
24
|
+
edge
|
25
|
+
_2.2.2_
|
26
|
+
~/git
|
27
|
+
|
28
|
+
"RANGE" specifies a set of sections to execute. Examples:
|
29
|
+
6.2..6.5
|
30
|
+
7.1-9.5
|
31
|
+
16
|
32
|
+
|
33
|
+
"save" - save snapshot after execution completes
|
34
|
+
|
35
|
+
Tests against the output produced (e.g., makedepot.html) can also be run
|
36
|
+
separately:
|
37
|
+
|
38
|
+
ruby checkdepot.rb [partial]
|
39
|
+
|
40
|
+
"partial" means that tests which cover sections that are not included in
|
41
|
+
makedepot.html are to be omitted.
|
data/Rakefile
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'echoe'
|
4
|
+
|
5
|
+
require File.expand_path(File.dirname(__FILE__) + "/lib/version")
|
6
|
+
|
7
|
+
Echoe.new('gorp', Gorp::VERSION::STRING) do |p|
|
8
|
+
p.summary = "Rails scenario testing support library"
|
9
|
+
p.description = <<-EOF
|
10
|
+
Enables the creation of scenarios that involve creating a rails project,
|
11
|
+
starting and stoppping of servers, generating projects, editing files,
|
12
|
+
issuing http requests, running of commands, etc. Output is captured as
|
13
|
+
a single HTML file that can be viewed locally or uploaded.
|
14
|
+
|
15
|
+
Additionally, there is support for verification, in the form of defining
|
16
|
+
assertions based on selections (typically CSS) against the generated HTML.
|
17
|
+
EOF
|
18
|
+
p.url = "http://github.com/rubys/gorp"
|
19
|
+
p.author = "Sam Ruby"
|
20
|
+
p.email = "rubys@intertwingly.net"
|
21
|
+
p.dependencies = %w(
|
22
|
+
arel
|
23
|
+
builder
|
24
|
+
erubis
|
25
|
+
rack
|
26
|
+
rack-test
|
27
|
+
rake
|
28
|
+
sqlite3-ruby
|
29
|
+
tzinfo
|
30
|
+
)
|
31
|
+
end
|
data/gorp.gemspec
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{gorp}
|
5
|
+
s.version = "0.7.1"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Sam Ruby"]
|
9
|
+
s.date = %q{2009-10-25}
|
10
|
+
s.description = %q{ Enables the creation of scenarios that involve creating a rails project,
|
11
|
+
starting and stoppping of servers, generating projects, editing files,
|
12
|
+
issuing http requests, running of commands, etc. Output is captured as
|
13
|
+
a single HTML file that can be viewed locally or uploaded.
|
14
|
+
|
15
|
+
Additionally, there is support for verification, in the form of defining
|
16
|
+
assertions based on selections (typically CSS) against the generated HTML.
|
17
|
+
}
|
18
|
+
s.email = %q{rubys@intertwingly.net}
|
19
|
+
s.extra_rdoc_files = ["README", "lib/version.rb", "lib/gorp.rb", "lib/gorp/test.rb"]
|
20
|
+
s.files = ["README", "Rakefile", "Manifest", "gorp.gemspec", "lib/version.rb", "lib/gorp.rb", "lib/gorp/test.rb"]
|
21
|
+
s.homepage = %q{http://github.com/rubys/gorp}
|
22
|
+
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Gorp", "--main", "README"]
|
23
|
+
s.require_paths = ["lib"]
|
24
|
+
s.rubyforge_project = %q{gorp}
|
25
|
+
s.rubygems_version = %q{1.3.3}
|
26
|
+
s.summary = %q{Rails scenario testing support library}
|
27
|
+
|
28
|
+
if s.respond_to? :specification_version then
|
29
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
30
|
+
s.specification_version = 3
|
31
|
+
|
32
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
33
|
+
s.add_runtime_dependency(%q<arel>, [">= 0"])
|
34
|
+
s.add_runtime_dependency(%q<builder>, [">= 0"])
|
35
|
+
s.add_runtime_dependency(%q<erubis>, [">= 0"])
|
36
|
+
s.add_runtime_dependency(%q<rack>, [">= 0"])
|
37
|
+
s.add_runtime_dependency(%q<rack-test>, [">= 0"])
|
38
|
+
s.add_runtime_dependency(%q<rake>, [">= 0"])
|
39
|
+
s.add_runtime_dependency(%q<sqlite3-ruby>, [">= 0"])
|
40
|
+
s.add_runtime_dependency(%q<tzinfo>, [">= 0"])
|
41
|
+
else
|
42
|
+
s.add_dependency(%q<arel>, [">= 0"])
|
43
|
+
s.add_dependency(%q<builder>, [">= 0"])
|
44
|
+
s.add_dependency(%q<erubis>, [">= 0"])
|
45
|
+
s.add_dependency(%q<rack>, [">= 0"])
|
46
|
+
s.add_dependency(%q<rack-test>, [">= 0"])
|
47
|
+
s.add_dependency(%q<rake>, [">= 0"])
|
48
|
+
s.add_dependency(%q<sqlite3-ruby>, [">= 0"])
|
49
|
+
s.add_dependency(%q<tzinfo>, [">= 0"])
|
50
|
+
end
|
51
|
+
else
|
52
|
+
s.add_dependency(%q<arel>, [">= 0"])
|
53
|
+
s.add_dependency(%q<builder>, [">= 0"])
|
54
|
+
s.add_dependency(%q<erubis>, [">= 0"])
|
55
|
+
s.add_dependency(%q<rack>, [">= 0"])
|
56
|
+
s.add_dependency(%q<rack-test>, [">= 0"])
|
57
|
+
s.add_dependency(%q<rake>, [">= 0"])
|
58
|
+
s.add_dependency(%q<sqlite3-ruby>, [">= 0"])
|
59
|
+
s.add_dependency(%q<tzinfo>, [">= 0"])
|
60
|
+
end
|
61
|
+
end
|
data/lib/gorp/test.rb
ADDED
@@ -0,0 +1,182 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'builder'
|
3
|
+
|
4
|
+
begin
|
5
|
+
# installed Rails (2.3.3 ish)
|
6
|
+
require 'active_support'
|
7
|
+
$:.unshift 'work/depot/vendor/rails/activesupport/lib'
|
8
|
+
require 'active_support/version'
|
9
|
+
$:.shift
|
10
|
+
rescue LoadError
|
11
|
+
# testing Rails (3.0 ish)
|
12
|
+
$:.unshift 'work/depot/vendor/rails/activesupport/lib'
|
13
|
+
require 'active_support'
|
14
|
+
require 'active_support/version'
|
15
|
+
end
|
16
|
+
|
17
|
+
require 'active_support/test_case'
|
18
|
+
|
19
|
+
module Book
|
20
|
+
end
|
21
|
+
|
22
|
+
class Book::TestCase < ActiveSupport::TestCase
|
23
|
+
# just enough infrastructure to get 'assert_select' to work
|
24
|
+
begin
|
25
|
+
# installed Rails (2.3.3 ish)
|
26
|
+
require 'action_controller'
|
27
|
+
require 'action_controller/assertions/selector_assertions'
|
28
|
+
include ActionController::Assertions::SelectorAssertions
|
29
|
+
rescue LoadError
|
30
|
+
# testing Rails (3.0 ish)
|
31
|
+
$:.unshift 'work/depot/vendor/rails/actionpack/lib'
|
32
|
+
require 'action_controller'
|
33
|
+
require 'action_dispatch/testing/assertions'
|
34
|
+
require 'action_dispatch/testing/assertions/selector'
|
35
|
+
include ActionDispatch::Assertions::SelectorAssertions
|
36
|
+
$:.shift
|
37
|
+
end
|
38
|
+
|
39
|
+
# micro DSL allowing the definition of optional tests
|
40
|
+
def self.section number, title, &tests
|
41
|
+
number = (sprintf "%f", number).sub(/0+$/,'') if number.kind_of? Float
|
42
|
+
return if ARGV.include? 'partial' and !@@sections.has_key? number.to_s
|
43
|
+
test "#{number} #{title}" do
|
44
|
+
instance_eval {select number}
|
45
|
+
instance_eval &tests
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# read and pre-process $input.html (only done once, and cached)
|
50
|
+
def self.input filename
|
51
|
+
# read $input output; remove front matter and footer
|
52
|
+
input = open("#{filename}.html").read
|
53
|
+
head, body, tail = input.split /<body>\s+|\s+<\/body>/m
|
54
|
+
|
55
|
+
# split into sections
|
56
|
+
@@sections = body.split(/<a class="toc" id="section-(.*?)">/)
|
57
|
+
@@sections[-1], env = @@sections.last.split(/<a class="toc" id="env">/)
|
58
|
+
env, todos = env.split(/<a class="toc" id="todos">/)
|
59
|
+
|
60
|
+
# convert to a Hash
|
61
|
+
@@sections = Hash[*@@sections.unshift(:contents)]
|
62
|
+
@@sections[:head] = head
|
63
|
+
@@sections[:todos] = todos
|
64
|
+
@@sections[:env] = env
|
65
|
+
@@sections[:tail] = tail
|
66
|
+
|
67
|
+
# reattach anchors
|
68
|
+
@@sections.each do |key,value|
|
69
|
+
next unless key =~ /^\d/
|
70
|
+
@@sections[key] = "<a class=\"toc\" name=\"section-#{key}\">#{value}"
|
71
|
+
end
|
72
|
+
|
73
|
+
# report version
|
74
|
+
body =~ /rails .*?-v<\/pre>\s+.*?>(.*)<\/pre>/
|
75
|
+
@@version = $1
|
76
|
+
@@version += ' (git)' if body =~ /ln -s.*vendor.rails/
|
77
|
+
@@version += ' (edge)' if body =~ /rails:freeze:edge/
|
78
|
+
STDERR.puts @@version
|
79
|
+
end
|
80
|
+
|
81
|
+
def self.output filename
|
82
|
+
$output = filename
|
83
|
+
at_exit { HTMLRunner.run(self) }
|
84
|
+
end
|
85
|
+
|
86
|
+
# select an individual section from the HTML
|
87
|
+
def select number
|
88
|
+
raise "Section #{number} not found" unless @@sections.has_key? number.to_s
|
89
|
+
@selected = HTML::Document.new(@@sections[number.to_s]).root.children
|
90
|
+
assert @@sections[number.to_s] !~
|
91
|
+
/<pre class="traceback">\s+#<IndexError: regexp not matched>/,
|
92
|
+
"edit failed"
|
93
|
+
end
|
94
|
+
|
95
|
+
def collect_stdout
|
96
|
+
css_select('.stdout').map do |tag|
|
97
|
+
tag.children.join.gsub('<','<').gsub('>','>')
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def sort_hash line
|
102
|
+
line.sub(/^(=> )?\{.*\}$/) do |match|
|
103
|
+
"#{$1}{#{match.scan(/:?"?\w+"?=>[^\[].*?(?=, |\})/).sort.join(', ')}}"
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def self.sections
|
108
|
+
@@sections
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# insert failure indicators into #{output}.html
|
113
|
+
require 'test/unit/ui/console/testrunner'
|
114
|
+
class HTMLRunner < Test::Unit::UI::Console::TestRunner
|
115
|
+
def self.run suite
|
116
|
+
@@sections = suite.sections
|
117
|
+
super
|
118
|
+
end
|
119
|
+
|
120
|
+
def attach_to_mediator
|
121
|
+
super
|
122
|
+
@html_tests = []
|
123
|
+
@mediator.add_listener(Test::Unit::TestResult::FAULT,
|
124
|
+
&method(:html_fault))
|
125
|
+
@mediator.add_listener(Test::Unit::UI::TestRunnerMediator::FINISHED,
|
126
|
+
&method(:html_summary))
|
127
|
+
end
|
128
|
+
|
129
|
+
def html_fault fault
|
130
|
+
if fault.test_name =~ /^test_([\d.]+)_.*\(\w+\)$/
|
131
|
+
name = $1
|
132
|
+
sections = @@sections
|
133
|
+
return unless sections.has_key? name
|
134
|
+
|
135
|
+
# indicate failure in the toc
|
136
|
+
sections[:contents][/<a href="#section-#{name}"()>/,1] =
|
137
|
+
' style="color:red; font-weight:bold"'
|
138
|
+
|
139
|
+
# provide details in the section itself
|
140
|
+
x = Builder::XmlMarkup.new(:indent => 2)
|
141
|
+
if fault.respond_to? :location
|
142
|
+
x.pre fault.message.sub(".\n<false> is not true",'') +
|
143
|
+
"\n\nTraceback:\n " + fault.location.join("\n "),
|
144
|
+
:class=>'traceback'
|
145
|
+
else
|
146
|
+
x.pre fault.message, :class=>'traceback'
|
147
|
+
end
|
148
|
+
sections[name][/<\/a>()/,1] = x.target!
|
149
|
+
|
150
|
+
# add to the todos
|
151
|
+
x = Builder::XmlMarkup.new(:indent => 2)
|
152
|
+
x.li do
|
153
|
+
x.a "Section #{name}", :href => "#section-#{name}"
|
154
|
+
x.tt fault.message.sub(".\n<false> is not true",'').
|
155
|
+
sub(/ but was\n.*/, '.')
|
156
|
+
end
|
157
|
+
sections[:todos][/() *<\/ul>/,1] = x.target!.gsub(/^/,' ')
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
def html_summary elapsed
|
162
|
+
open("#{$output}.html",'w') do |output|
|
163
|
+
sections = @@sections
|
164
|
+
output.write(sections.delete(:head))
|
165
|
+
output.write("<body>\n ")
|
166
|
+
output.write(sections.delete(:contents))
|
167
|
+
env = sections.delete(:env)
|
168
|
+
todos = sections.delete(:todos)
|
169
|
+
tail = sections.delete(:tail)
|
170
|
+
sections.keys.sort_by {|key| key.split('.').map {|n| n.to_i}}.each do |n|
|
171
|
+
output.write(sections[n])
|
172
|
+
end
|
173
|
+
output.write('<a class="toc" id="env">')
|
174
|
+
output.write(env)
|
175
|
+
output.write('<a class="toc" id="todos">')
|
176
|
+
todos.sub! /<ul.*\/ul>/m, '<h2>None!</h2>' unless todos.include? '<li>'
|
177
|
+
output.write(todos)
|
178
|
+
output.write("\n </body>")
|
179
|
+
output.write(tail)
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
data/lib/gorp.rb
ADDED
@@ -0,0 +1,705 @@
|
|
1
|
+
# The following is under the "I ain't too proud" school of programming.
|
2
|
+
# global variables, repetition, and brute force abounds.
|
3
|
+
#
|
4
|
+
# You have been warned.
|
5
|
+
|
6
|
+
require 'fileutils'
|
7
|
+
require 'open3'
|
8
|
+
require 'net/http'
|
9
|
+
require 'builder'
|
10
|
+
require 'stringio'
|
11
|
+
|
12
|
+
require 'rbconfig'
|
13
|
+
$ruby = File.join(Config::CONFIG["bindir"], Config::CONFIG["RUBY_INSTALL_NAME"])
|
14
|
+
|
15
|
+
# Micro DSL for declaring an ordered set of book sections
|
16
|
+
$sections = []
|
17
|
+
def section number, title, &steps
|
18
|
+
number = (sprintf "%f", number).sub(/0+$/,'') if number.kind_of? Float
|
19
|
+
$sections << [number, title, steps]
|
20
|
+
end
|
21
|
+
|
22
|
+
# verify that port is available for testing
|
23
|
+
if (Net::HTTP.get_response('localhost','/',3000).code == '200' rescue false)
|
24
|
+
STDERR.puts 'local server already running on port 3000'
|
25
|
+
exit
|
26
|
+
end
|
27
|
+
|
28
|
+
$BASE=File.expand_path(File.dirname(caller.last.split(':').first)) unless $BASE
|
29
|
+
$WORK = File.join($BASE,'work')
|
30
|
+
$DATA = File.join($BASE,'data')
|
31
|
+
$CODE = File.join($DATA,'code')
|
32
|
+
$x = Builder::XmlMarkup.new(:indent => 2)
|
33
|
+
$toc = Builder::XmlMarkup.new(:indent => 2)
|
34
|
+
$todos = Builder::XmlMarkup.new(:indent => 2)
|
35
|
+
$issue = 0
|
36
|
+
$style = Builder::XmlMarkup.new(:indent => 2)
|
37
|
+
|
38
|
+
FileUtils.mkdir_p $WORK
|
39
|
+
|
40
|
+
class String
|
41
|
+
def unindent(n)
|
42
|
+
gsub Regexp.new("^#{' '*n}"), ''
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def read name
|
47
|
+
open(File.join($DATA, name)) {|file| file.read}
|
48
|
+
end
|
49
|
+
|
50
|
+
def overview message
|
51
|
+
$x.p message.gsub(/(^|\n)\s+/, ' ').strip, :class=>'overview'
|
52
|
+
end
|
53
|
+
|
54
|
+
def desc message
|
55
|
+
$x.p message, :class=>'desc'
|
56
|
+
end
|
57
|
+
|
58
|
+
def log type, message
|
59
|
+
type = type.to_s.ljust(5).upcase
|
60
|
+
STDOUT.puts Time.now.strftime("[%Y-%m-%d %H:%M:%S] #{type} #{message}")
|
61
|
+
end
|
62
|
+
|
63
|
+
def head number, title
|
64
|
+
$section = "#{number} #{title}"
|
65
|
+
log '====>', $section
|
66
|
+
|
67
|
+
$x.a(:class => 'toc', :id => "section-#{number}") {$x.h2 $section}
|
68
|
+
$toc.li {$toc.a $section, :href => "#section-#{number}"}
|
69
|
+
end
|
70
|
+
|
71
|
+
def issue text, options={}
|
72
|
+
log :issue, text
|
73
|
+
|
74
|
+
$issue+=1
|
75
|
+
$x.p :class => 'issue', :id => "issue-#{$issue}" do
|
76
|
+
$x.text! text
|
77
|
+
if options[:ticket]
|
78
|
+
$x.text! ' ('
|
79
|
+
$x.a "ticket #{options[:ticket]}", :href=>
|
80
|
+
'https://rails.lighthouseapp.com/projects/8994/tickets/' +
|
81
|
+
options[:ticket].to_s
|
82
|
+
$x.text! ')'
|
83
|
+
end
|
84
|
+
end
|
85
|
+
$todos.li do
|
86
|
+
section = $section.split(' ').first
|
87
|
+
$todos.a "Section #{section}:", :href => "#section-#{$section}"
|
88
|
+
$todos.a "#{text}", :href => "#issue-#{$issue}"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def db statement, hilight=[]
|
93
|
+
log :db, statement
|
94
|
+
$x.pre "sqlite3> #{statement}", :class=>'stdin'
|
95
|
+
cmd = "sqlite3 --line db/development.sqlite3 #{statement.inspect}"
|
96
|
+
popen3 cmd, hilight
|
97
|
+
end
|
98
|
+
|
99
|
+
def ruby args
|
100
|
+
cmd "ruby #{args}"
|
101
|
+
end
|
102
|
+
|
103
|
+
def console script
|
104
|
+
cmd "echo #{script.inspect} | ruby script/console '--irb=irb -f'"
|
105
|
+
end
|
106
|
+
|
107
|
+
def cmd args, hilight=[]
|
108
|
+
log :cmd, args
|
109
|
+
$x.pre args, :class=>'stdin'
|
110
|
+
if args == 'rake db:migrate'
|
111
|
+
Dir.chdir 'db/migrate' do
|
112
|
+
date = '20100301000000'
|
113
|
+
Dir['[0-9]*'].sort_by {|file| file=~/201003/?file:'x'+file}.each do |file|
|
114
|
+
file =~ /^([0-9]*)_(.*)$/
|
115
|
+
FileUtils.mv file, "#{date}_#{$2}" unless $1 == date.next!
|
116
|
+
$x.pre "mv #{file} #{date}_#{$2}" unless $1 == date
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
args += ' -C' if args == 'ls -p'
|
121
|
+
popen3 args, hilight
|
122
|
+
end
|
123
|
+
|
124
|
+
def popen3 args, hilight=[]
|
125
|
+
Open3.popen3(args) do |pin, pout, perr|
|
126
|
+
terr = Thread.new do
|
127
|
+
$x.pre perr.readline.chomp, :class=>'stderr' until perr.eof?
|
128
|
+
end
|
129
|
+
pin.close
|
130
|
+
until pout.eof?
|
131
|
+
line = pout.readline
|
132
|
+
if hilight.any? {|pattern| line.include? pattern}
|
133
|
+
outclass='hilight'
|
134
|
+
elsif line =~ /\x1b\[\d/
|
135
|
+
line.gsub! /\x1b\[1m\x1b\[3\dm(.*?)\x1b\[0m/, '\1'
|
136
|
+
outclass = 'logger'
|
137
|
+
else
|
138
|
+
outclass='stdout'
|
139
|
+
end
|
140
|
+
|
141
|
+
if line.strip.size == 0
|
142
|
+
$x.pre ' ', :class=>outclass
|
143
|
+
else
|
144
|
+
$x.pre line.chomp, :class=>outclass
|
145
|
+
end
|
146
|
+
end
|
147
|
+
terr.join
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def irb file
|
152
|
+
$x.pre "irb #{file}", :class=>'stdin'
|
153
|
+
log :irb, file
|
154
|
+
cmd = "irb -f -rubygems -r ./config/boot --prompt-mode simple #{$CODE}/#{file}"
|
155
|
+
Open3.popen3(cmd) do |pin, pout, perr|
|
156
|
+
terr = Thread.new do
|
157
|
+
until perr.eof?
|
158
|
+
line = perr.readline.chomp
|
159
|
+
line.gsub! /\x1b\[4(;\d+)*m(.*?)\x1b\[0m/, '\2'
|
160
|
+
line.gsub! /\x1b\[0(;\d+)*m(.*?)\x1b\[0m/, '\2'
|
161
|
+
line.gsub! /\x1b\[0(;\d+)*m/, ''
|
162
|
+
$x.pre line, :class=>'stderr'
|
163
|
+
end
|
164
|
+
end
|
165
|
+
pin.close
|
166
|
+
prompt = nil
|
167
|
+
until pout.eof?
|
168
|
+
line = pout.readline
|
169
|
+
if line =~ /^([?>]>)\s*#\s*(START|END):/
|
170
|
+
prompt = $1
|
171
|
+
elsif line =~ /^([?>]>)\s+$/
|
172
|
+
$x.pre ' ', :class=>'irb'
|
173
|
+
prompt ||= $1
|
174
|
+
elsif line =~ /^([?>]>)(.*)\n/
|
175
|
+
prompt ||= $1
|
176
|
+
$x.pre prompt + $2, :class=>'irb'
|
177
|
+
prompt = nil
|
178
|
+
elsif line =~ /^\w+(::\w+)*: /
|
179
|
+
$x.pre line.chomp, :class=>'stderr'
|
180
|
+
elsif line =~ /^\s+from [\/.:].*:\d+:in `\w.*'\s*$/
|
181
|
+
$x.pre line.chomp, :class=>'stderr'
|
182
|
+
else
|
183
|
+
$x.pre line.chomp, :class=>'stdout'
|
184
|
+
end
|
185
|
+
end
|
186
|
+
terr.join
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
def edit filename, tag=nil
|
191
|
+
log :edit, filename
|
192
|
+
$x.pre "edit #{filename}", :class=>'stdin'
|
193
|
+
|
194
|
+
stale = File.mtime(filename) rescue Time.now-2
|
195
|
+
data = open(filename) {|file| file.read} rescue ''
|
196
|
+
before = data.split("\n")
|
197
|
+
|
198
|
+
begin
|
199
|
+
yield data
|
200
|
+
open(filename,'w') {|file| file.write data}
|
201
|
+
File.utime(stale+2, stale+2, filename) if File.mtime(filename) <= stale
|
202
|
+
|
203
|
+
rescue Exception => e
|
204
|
+
$x.pre :class => 'traceback' do
|
205
|
+
STDERR.puts e.inspect
|
206
|
+
$x.text! "#{e.inspect}\n"
|
207
|
+
e.backtrace.each {|line| $x.text! " #{line}\n"}
|
208
|
+
end
|
209
|
+
tag = nil
|
210
|
+
|
211
|
+
ensure
|
212
|
+
include = tag.nil?
|
213
|
+
hilight = false
|
214
|
+
data.split("\n").each do |line|
|
215
|
+
if line =~ /START:(\w+)/
|
216
|
+
include = true if $1 == tag
|
217
|
+
elsif line =~ /END:(\w+)/
|
218
|
+
include = false if $1 == tag
|
219
|
+
elsif line =~ /START_HIGHLIGHT/
|
220
|
+
hilight = true
|
221
|
+
elsif line =~ /END_HIGHLIGHT/
|
222
|
+
hilight = false
|
223
|
+
elsif include
|
224
|
+
if hilight or ! before.include?(line)
|
225
|
+
outclass='hilight'
|
226
|
+
else
|
227
|
+
outclass='stdout'
|
228
|
+
end
|
229
|
+
|
230
|
+
if line.empty?
|
231
|
+
$x.pre ' ', :class=>outclass
|
232
|
+
else
|
233
|
+
$x.pre line, :class=>outclass
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
# pluggable XML parser support
|
241
|
+
begin
|
242
|
+
raise LoadError if ARGV.include? 'rexml'
|
243
|
+
require 'nokogiri'
|
244
|
+
def xhtmlparse(text)
|
245
|
+
Nokogiri::HTML(text)
|
246
|
+
end
|
247
|
+
Comment=Nokogiri::XML::Comment
|
248
|
+
rescue LoadError
|
249
|
+
require 'rexml/document'
|
250
|
+
|
251
|
+
HTML_VOIDS = %w(area base br col command embed hr img input keygen link meta
|
252
|
+
param source)
|
253
|
+
|
254
|
+
def xhtmlparse(text)
|
255
|
+
begin
|
256
|
+
require 'htmlentities'
|
257
|
+
text.gsub! '&', '&amp;'
|
258
|
+
text.gsub! '<', '&lt;'
|
259
|
+
text.gsub! '>', '&gt;'
|
260
|
+
text.gsub! ''', '&apos;'
|
261
|
+
text.gsub! '"', '&quot;'
|
262
|
+
text.force_encoding('utf-8') if text.respond_to? :force_encoding
|
263
|
+
text = HTMLEntities.new.decode(text)
|
264
|
+
rescue LoadError
|
265
|
+
end
|
266
|
+
doc = REXML::Document.new(text)
|
267
|
+
doc.get_elements('//*[not(* or text())]').each do |e|
|
268
|
+
e.text='' unless HTML_VOIDS.include? e.name
|
269
|
+
end
|
270
|
+
doc
|
271
|
+
end
|
272
|
+
|
273
|
+
class REXML::Element
|
274
|
+
def has_attribute? name
|
275
|
+
self.attributes.has_key? name
|
276
|
+
end
|
277
|
+
|
278
|
+
def at xpath
|
279
|
+
self.elements[xpath]
|
280
|
+
end
|
281
|
+
|
282
|
+
def search xpath
|
283
|
+
self.elements.to_a(xpath)
|
284
|
+
end
|
285
|
+
|
286
|
+
def content=(string)
|
287
|
+
self.text=string
|
288
|
+
end
|
289
|
+
|
290
|
+
def [](index)
|
291
|
+
if index.instance_of? String
|
292
|
+
self.attributes[index]
|
293
|
+
else
|
294
|
+
super(index)
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
def []=(index, value)
|
299
|
+
if index.instance_of? String
|
300
|
+
self.attributes[index] = value
|
301
|
+
else
|
302
|
+
super(index, value)
|
303
|
+
end
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
module REXML::Node
|
308
|
+
def before(node)
|
309
|
+
self.parent.insert_before(self, node)
|
310
|
+
end
|
311
|
+
|
312
|
+
def add_previous_sibling(node)
|
313
|
+
self.parent.insert_before(self, node)
|
314
|
+
end
|
315
|
+
|
316
|
+
def serialize
|
317
|
+
self.to_s
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
# monkey patch for Ruby 1.8.6
|
322
|
+
doc = REXML::Document.new '<doc xmlns="ns"><item name="foo"/></doc>'
|
323
|
+
if not doc.root.elements["item[@name='foo']"]
|
324
|
+
class REXML::Element
|
325
|
+
def attribute( name, namespace=nil )
|
326
|
+
prefix = nil
|
327
|
+
prefix = namespaces.index(namespace) if namespace
|
328
|
+
prefix = nil if prefix == 'xmlns'
|
329
|
+
attributes.get_attribute( "#{prefix ? prefix + ':' : ''}#{name}" )
|
330
|
+
end
|
331
|
+
end
|
332
|
+
end
|
333
|
+
|
334
|
+
Comment = REXML::Comment
|
335
|
+
end
|
336
|
+
|
337
|
+
def snap response, form={}
|
338
|
+
if response.content_type == 'text/plain' or response.content_type =~ /xml/
|
339
|
+
$x.div :class => 'body' do
|
340
|
+
response.body.split("\n").each do |line|
|
341
|
+
$x.pre line.chomp, :class=>'stdout'
|
342
|
+
end
|
343
|
+
end
|
344
|
+
return
|
345
|
+
end
|
346
|
+
|
347
|
+
if response.body =~ /<body/
|
348
|
+
body = response.body
|
349
|
+
else
|
350
|
+
body = "<body>#{response.body}</body>"
|
351
|
+
end
|
352
|
+
|
353
|
+
begin
|
354
|
+
doc = xhtmlparse(body)
|
355
|
+
rescue
|
356
|
+
body.split("\n").each {|line| $x.pre line.chomp, :class=>'hilight'}
|
357
|
+
raise
|
358
|
+
end
|
359
|
+
|
360
|
+
title = doc.at('html/head/title').text rescue ''
|
361
|
+
body = doc.at('//body')
|
362
|
+
doc.search('//link[@rel="stylesheet"]').each do |sheet|
|
363
|
+
body.children.first.add_previous_sibling(sheet)
|
364
|
+
end
|
365
|
+
|
366
|
+
if ! form.empty?
|
367
|
+
body.search('//input[@name]').each do |input|
|
368
|
+
input['value'] ||= form[input['name']].to_s
|
369
|
+
end
|
370
|
+
body.search('//textarea[@name]').each do |textarea|
|
371
|
+
textarea.text ||= form[textarea['name']].to_s
|
372
|
+
end
|
373
|
+
end
|
374
|
+
|
375
|
+
%w{ a[@href] form[@action] }.each do |xpath|
|
376
|
+
name = xpath[/@(\w+)/,1]
|
377
|
+
body.search("//#{xpath}").each do |element|
|
378
|
+
next if element[name] =~ /^http:\/\//
|
379
|
+
element[name] = URI.join('http://localhost:3000/', element[name]).to_s
|
380
|
+
end
|
381
|
+
end
|
382
|
+
|
383
|
+
%w{ img[@src] }.each do |xpath|
|
384
|
+
name = xpath[/@(\w+)/,1]
|
385
|
+
body.search("//#{xpath}").each do |element|
|
386
|
+
if element[name][0] == ?/
|
387
|
+
element[name] = 'data' + element[name]
|
388
|
+
end
|
389
|
+
end
|
390
|
+
end
|
391
|
+
|
392
|
+
body.search('//textarea').each do |element|
|
393
|
+
element.content=''
|
394
|
+
end
|
395
|
+
|
396
|
+
attrs = {:class => 'body', :title => title}
|
397
|
+
attrs[:class] = 'traceback' if response.code == '500'
|
398
|
+
attrs[:id] = body['id'] if body['id']
|
399
|
+
$x.div(attrs) do
|
400
|
+
body.children.each do |child|
|
401
|
+
$x << child.serialize unless child.instance_of?(Comment)
|
402
|
+
end
|
403
|
+
end
|
404
|
+
$x.div :style => "clear: both"
|
405
|
+
end
|
406
|
+
|
407
|
+
def get path
|
408
|
+
post path, {}
|
409
|
+
end
|
410
|
+
|
411
|
+
def post path, form
|
412
|
+
$x.pre "get #{path}", :class=>'stdin'
|
413
|
+
|
414
|
+
if path.include? ':'
|
415
|
+
host, port, path = URI.parse(path).select(:host, :port, :path)
|
416
|
+
else
|
417
|
+
host, port = '127.0.0.1', 3000
|
418
|
+
end
|
419
|
+
|
420
|
+
Net::HTTP.start(host, port) do |http|
|
421
|
+
get = Net::HTTP::Get.new(path)
|
422
|
+
get['Cookie'] = $COOKIE if $COOKIE
|
423
|
+
response = http.request(get)
|
424
|
+
snap response, form
|
425
|
+
$COOKIE = response.response['set-cookie'] if response.response['set-cookie']
|
426
|
+
|
427
|
+
if ! form.empty?
|
428
|
+
body = xhtmlparse(response.body).at('//body')
|
429
|
+
body = xhtmlparse(response.body).root unless body
|
430
|
+
xform = body.at('//form[.//input[@name="commit"]]')
|
431
|
+
return unless xform
|
432
|
+
path = xform.attribute('action').to_s unless
|
433
|
+
xform.attribute('action').to_s.empty?
|
434
|
+
$x.pre "post #{path}", :class=>'stdin'
|
435
|
+
|
436
|
+
$x.ul do
|
437
|
+
form.each do |name, value|
|
438
|
+
$x.li "#{name} => #{value}"
|
439
|
+
end
|
440
|
+
end
|
441
|
+
|
442
|
+
body.search('//input[@type="hidden"]').each do |element|
|
443
|
+
form[element['name']] ||= element['value']
|
444
|
+
end
|
445
|
+
|
446
|
+
post = Net::HTTP::Post.new(path)
|
447
|
+
post.form_data = form
|
448
|
+
post['Cookie'] = $COOKIE
|
449
|
+
response=http.request(post)
|
450
|
+
snap response
|
451
|
+
end
|
452
|
+
|
453
|
+
if response.code == '302'
|
454
|
+
$COOKIE=response.response['set-cookie'] if response.response['set-cookie']
|
455
|
+
path = response['Location']
|
456
|
+
$x.pre "get #{path}", :class=>'stdin'
|
457
|
+
get = Net::HTTP::Get.new(path)
|
458
|
+
get['Cookie'] = $COOKIE if $COOKIE
|
459
|
+
response = http.request(get)
|
460
|
+
snap response
|
461
|
+
end
|
462
|
+
end
|
463
|
+
end
|
464
|
+
|
465
|
+
# select a version of Rails
|
466
|
+
if ARGV.first =~ /^_\d[.\d]*_$/
|
467
|
+
$rails = "rails #{ARGV.first}"
|
468
|
+
elsif File.directory?(ARGV.first.to_s)
|
469
|
+
$rails = ARGV.first
|
470
|
+
$rails = File.join($rails,'rails') if
|
471
|
+
File.directory?(File.join($rails,'rails'))
|
472
|
+
$rails = File.expand_path($rails)
|
473
|
+
else
|
474
|
+
$rails = 'rails'
|
475
|
+
end
|
476
|
+
|
477
|
+
def which_rails rails
|
478
|
+
railties = File.join(rails, 'railties', 'bin', 'rails')
|
479
|
+
rails = railties if File.exists?(railties)
|
480
|
+
if File.exists?(rails)
|
481
|
+
firstline = open(rails) {|file| file.readlines.first}
|
482
|
+
rails = 'ruby ' + rails unless firstline =~ /^#!/
|
483
|
+
end
|
484
|
+
rails
|
485
|
+
end
|
486
|
+
|
487
|
+
def rails name, app=nil
|
488
|
+
Dir.chdir($WORK)
|
489
|
+
FileUtils.rm_rf name
|
490
|
+
log :rails, name
|
491
|
+
|
492
|
+
# determine how to invoke rails
|
493
|
+
rails = which_rails $rails
|
494
|
+
|
495
|
+
$x.pre "#{rails} #{name}", :class=>'stdin'
|
496
|
+
popen3 "#{rails} #{name}"
|
497
|
+
|
498
|
+
# make paths seem Mac OSX'ish
|
499
|
+
Dir["#{name}/public/dispatch.*"].each do |dispatch|
|
500
|
+
code = open(dispatch) {|file| file.read}
|
501
|
+
code.sub! /^#!.*/, '#!/opt/local/bin/ruby'
|
502
|
+
open(dispatch,'w') {|file| file.write code}
|
503
|
+
end
|
504
|
+
|
505
|
+
Dir.chdir(name)
|
506
|
+
FileUtils.rm_rf 'public/.htaccess'
|
507
|
+
|
508
|
+
cmd 'rake rails:freeze:edge' if ARGV.include? 'edge'
|
509
|
+
|
510
|
+
if $rails != 'rails' and File.directory?($rails)
|
511
|
+
cmd "ln -s #{$rails} vendor/rails"
|
512
|
+
end
|
513
|
+
end
|
514
|
+
|
515
|
+
def restart_server
|
516
|
+
log :server, 'restart'
|
517
|
+
if $server
|
518
|
+
$x.h3 'Restart the server.'
|
519
|
+
Process.kill "INT", $server
|
520
|
+
Process.wait($server)
|
521
|
+
else
|
522
|
+
$x.h3 'Start the server.'
|
523
|
+
end
|
524
|
+
|
525
|
+
$server = fork
|
526
|
+
if $server
|
527
|
+
# wait for server to start
|
528
|
+
60.times do
|
529
|
+
sleep 0.5
|
530
|
+
begin
|
531
|
+
status = Net::HTTP.get_response('localhost','/',3000).code
|
532
|
+
break if %(200 404).include? status
|
533
|
+
rescue Errno::ECONNREFUSED
|
534
|
+
end
|
535
|
+
end
|
536
|
+
else
|
537
|
+
begin
|
538
|
+
if File.exist?('config.ru')
|
539
|
+
require 'rack'
|
540
|
+
server = Rack::Builder.new {eval open('config.ru').read}
|
541
|
+
Rack::Handler::WEBrick.run(server, :Port => 3000)
|
542
|
+
else
|
543
|
+
# start server, redirecting stdout to a string
|
544
|
+
$stdout = StringIO.open('','w')
|
545
|
+
require './config/boot'
|
546
|
+
if Rails::VERSION::MAJOR == 2
|
547
|
+
require 'commands/server'
|
548
|
+
else
|
549
|
+
require 'rails/commands/server'
|
550
|
+
end
|
551
|
+
end
|
552
|
+
rescue
|
553
|
+
STDERR.puts $!
|
554
|
+
$!.backtrace.each {|method| STDERR.puts "\tfrom " + method}
|
555
|
+
ensure
|
556
|
+
Process.exit!
|
557
|
+
end
|
558
|
+
end
|
559
|
+
end
|
560
|
+
|
561
|
+
def secsplit section
|
562
|
+
section.to_s.split('.').map {|n| n.to_i}
|
563
|
+
end
|
564
|
+
|
565
|
+
at_exit do
|
566
|
+
$x.html :xmlns => 'http://www.w3.org/1999/xhtml' do
|
567
|
+
$x.header do
|
568
|
+
$x.title $title
|
569
|
+
$x.meta 'http-equiv'=>'text/html; charset=UTF-8'
|
570
|
+
$x.style :type => "text/css" do
|
571
|
+
$x.text! <<-'EOF'.unindent(2)
|
572
|
+
body {background-color: #F5F5DC}
|
573
|
+
#banner {margin-top: 0}
|
574
|
+
pre {font-weight: bold; margin: 0; padding: 0}
|
575
|
+
pre.stdin {color: #800080; margin-top: 1em; padding: 0}
|
576
|
+
pre.irb {color: #800080; padding: 0}
|
577
|
+
pre.stdout {color: #000; padding: 0}
|
578
|
+
pre.logger {color: #088; padding: 0}
|
579
|
+
pre.hilight {color: #000; background-color: #FF0; padding: 0}
|
580
|
+
pre.stderr {color: #F00; padding: 0}
|
581
|
+
div.body {border-style: solid; border-color: #800080; padding: 0.5em}
|
582
|
+
.issue, .traceback {background:#FDD; border: 4px solid #F00;
|
583
|
+
font-weight: bold; margin-top: 1em; padding: 0.5em}
|
584
|
+
div.body, .issue, .traceback {
|
585
|
+
-webkit-border-radius: 0.7em; -moz-border-radius: 0.7em;}
|
586
|
+
ul.toc {list-style: none}
|
587
|
+
ul a {text-decoration: none}
|
588
|
+
ul a:hover {text-decoration: underline; color: #000;
|
589
|
+
background-color: #F5F5DC}
|
590
|
+
a.toc h2 {background-color: #981A21; color:#FFF; padding: 6px}
|
591
|
+
ul a:visited {color: #000}
|
592
|
+
h2 {clear: both}
|
593
|
+
p.desc {font-style: italic}
|
594
|
+
p.overview {border-width: 2px; border-color: #000;
|
595
|
+
border-style: solid; border-radius: 4em;
|
596
|
+
background-color: #CCF; margin: 1.5em 1.5em; padding: 1em 2em;
|
597
|
+
-webkit-border-radius: 4em; -moz-border-radius: 4em;}
|
598
|
+
EOF
|
599
|
+
end
|
600
|
+
end
|
601
|
+
|
602
|
+
$x.body do
|
603
|
+
$x.h1 $title, :id=>'banner'
|
604
|
+
$x.h2 'Table of Contents'
|
605
|
+
$x.ul :class => 'toc'
|
606
|
+
|
607
|
+
# determine which range(s) of steps are to be executed
|
608
|
+
ranges = ARGV.grep(/^ \d+(.\d+)? ( (-|\.\.) \d+(.\d+)? )? /x).map do |arg|
|
609
|
+
bounds = arg.split(/-|\.\./)
|
610
|
+
Range.new(secsplit(bounds.first), secsplit(bounds.last))
|
611
|
+
end
|
612
|
+
ARGV.push 'partial' unless ranges.empty?
|
613
|
+
|
614
|
+
# optionally save a snapshot
|
615
|
+
if ARGV.include? 'restore'
|
616
|
+
log :snap, 'restore'
|
617
|
+
Dir.chdir $BASE
|
618
|
+
FileUtils.rm_rf "work"
|
619
|
+
FileUtils.cp_r "snapshot", "work", :preserve => true
|
620
|
+
Dir.chdir $WORK
|
621
|
+
if $autorestart and File.directory? $autorestart
|
622
|
+
Dir.chdir $autorestart
|
623
|
+
restart_server
|
624
|
+
end
|
625
|
+
end
|
626
|
+
|
627
|
+
# run steps
|
628
|
+
e = nil
|
629
|
+
begin
|
630
|
+
$sections.each do |section, title, steps|
|
631
|
+
next if !ranges.empty? and
|
632
|
+
!ranges.any? do |range|
|
633
|
+
# was (in Ruby 1.8): range.include?(secsplit(section))
|
634
|
+
ss = secsplit(section)
|
635
|
+
(range.first <=> ss) <= 0 and (range.last <=> ss) >= 0
|
636
|
+
end
|
637
|
+
head section, title
|
638
|
+
steps.call
|
639
|
+
end
|
640
|
+
rescue Exception => e
|
641
|
+
$x.pre :class => 'traceback' do
|
642
|
+
STDERR.puts e.inspect
|
643
|
+
$x.text! "#{e.inspect}\n"
|
644
|
+
e.backtrace.each {|line| $x.text! " #{line}\n"}
|
645
|
+
end
|
646
|
+
ensure
|
647
|
+
if e.class != SystemExit
|
648
|
+
$cleanup.call if $cleanup
|
649
|
+
|
650
|
+
# terminate server
|
651
|
+
Process.kill "INT", $server if $server
|
652
|
+
Process.wait($server) if $server
|
653
|
+
|
654
|
+
# optionally save a snapshot
|
655
|
+
if ARGV.include? 'save'
|
656
|
+
log :snap, 'save'
|
657
|
+
Dir.chdir $BASE
|
658
|
+
FileUtils.rm_rf "snapshot"
|
659
|
+
FileUtils.cp_r "work", "snapshot", :preserve => true
|
660
|
+
end
|
661
|
+
end
|
662
|
+
end
|
663
|
+
|
664
|
+
$x.a(:class => 'toc', :id => 'env') {$x.h2 'Environment'}
|
665
|
+
cmd which_rails($rails) + ' -v'
|
666
|
+
|
667
|
+
cmd "#{$ruby} -v"
|
668
|
+
cmd 'gem -v'
|
669
|
+
cmd 'gem list'
|
670
|
+
cmd 'echo $RUBYLIB | sed "s/:/\n/g"'
|
671
|
+
|
672
|
+
$x.a(:class => 'toc', :id => 'todos') {$x.h2 'Todos'}
|
673
|
+
$x.ul :class => 'todos'
|
674
|
+
end
|
675
|
+
end
|
676
|
+
|
677
|
+
# output results as HTML, after inserting style and toc information
|
678
|
+
$x.target![/<style.*?>()/,1] = "\n#{$style.target!.strip.gsub(/^/,' '*6)}\n"
|
679
|
+
$x.target!.sub! /<ul class="toc"\/>/,
|
680
|
+
"<ul class=\"toc\">\n#{$toc.target!.gsub(/^/,' '*6)} </ul>"
|
681
|
+
$x.target!.sub! /<ul class="todos"\/>/,
|
682
|
+
"<ul class=\"todos\">\n#{$todos.target!.gsub(/^/,' '*6)} </ul>"
|
683
|
+
$x.target!.gsub! '<strong/>', '<strong></strong>'
|
684
|
+
log :WRITE, "#{$output}.html"
|
685
|
+
open("#{$BASE}/#{$output}.html",'w') do |file|
|
686
|
+
file.write <<-EOF.unindent(6)
|
687
|
+
<!DOCTYPE html
|
688
|
+
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
689
|
+
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
690
|
+
EOF
|
691
|
+
file.write $x.target!
|
692
|
+
end
|
693
|
+
|
694
|
+
# run tests
|
695
|
+
if $checker
|
696
|
+
log :CHECK, "#{$output}.html"
|
697
|
+
Dir.chdir $BASE
|
698
|
+
STDOUT.puts
|
699
|
+
if $checker =~ /^[-\w]+$/
|
700
|
+
require File.join($BASE,$checker)
|
701
|
+
else
|
702
|
+
require $checker
|
703
|
+
end
|
704
|
+
end
|
705
|
+
end
|
data/lib/version.rb
ADDED
metadata
ADDED
@@ -0,0 +1,148 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: gorp
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.7.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Sam Ruby
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-10-25 00:00:00 -04:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: arel
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: builder
|
27
|
+
type: :runtime
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: "0"
|
34
|
+
version:
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: erubis
|
37
|
+
type: :runtime
|
38
|
+
version_requirement:
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: "0"
|
44
|
+
version:
|
45
|
+
- !ruby/object:Gem::Dependency
|
46
|
+
name: rack
|
47
|
+
type: :runtime
|
48
|
+
version_requirement:
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: "0"
|
54
|
+
version:
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rack-test
|
57
|
+
type: :runtime
|
58
|
+
version_requirement:
|
59
|
+
version_requirements: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: "0"
|
64
|
+
version:
|
65
|
+
- !ruby/object:Gem::Dependency
|
66
|
+
name: rake
|
67
|
+
type: :runtime
|
68
|
+
version_requirement:
|
69
|
+
version_requirements: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: "0"
|
74
|
+
version:
|
75
|
+
- !ruby/object:Gem::Dependency
|
76
|
+
name: sqlite3-ruby
|
77
|
+
type: :runtime
|
78
|
+
version_requirement:
|
79
|
+
version_requirements: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: "0"
|
84
|
+
version:
|
85
|
+
- !ruby/object:Gem::Dependency
|
86
|
+
name: tzinfo
|
87
|
+
type: :runtime
|
88
|
+
version_requirement:
|
89
|
+
version_requirements: !ruby/object:Gem::Requirement
|
90
|
+
requirements:
|
91
|
+
- - ">="
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: "0"
|
94
|
+
version:
|
95
|
+
description: " Enables the creation of scenarios that involve creating a rails project,\n starting and stoppping of servers, generating projects, editing files,\n issuing http requests, running of commands, etc. Output is captured as\n a single HTML file that can be viewed locally or uploaded.\n\n Additionally, there is support for verification, in the form of defining\n assertions based on selections (typically CSS) against the generated HTML.\n"
|
96
|
+
email: rubys@intertwingly.net
|
97
|
+
executables: []
|
98
|
+
|
99
|
+
extensions: []
|
100
|
+
|
101
|
+
extra_rdoc_files:
|
102
|
+
- README
|
103
|
+
- lib/version.rb
|
104
|
+
- lib/gorp.rb
|
105
|
+
- lib/gorp/test.rb
|
106
|
+
files:
|
107
|
+
- README
|
108
|
+
- Rakefile
|
109
|
+
- Manifest
|
110
|
+
- gorp.gemspec
|
111
|
+
- lib/version.rb
|
112
|
+
- lib/gorp.rb
|
113
|
+
- lib/gorp/test.rb
|
114
|
+
has_rdoc: true
|
115
|
+
homepage: http://github.com/rubys/gorp
|
116
|
+
licenses: []
|
117
|
+
|
118
|
+
post_install_message:
|
119
|
+
rdoc_options:
|
120
|
+
- --line-numbers
|
121
|
+
- --inline-source
|
122
|
+
- --title
|
123
|
+
- Gorp
|
124
|
+
- --main
|
125
|
+
- README
|
126
|
+
require_paths:
|
127
|
+
- lib
|
128
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
129
|
+
requirements:
|
130
|
+
- - ">="
|
131
|
+
- !ruby/object:Gem::Version
|
132
|
+
version: "0"
|
133
|
+
version:
|
134
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: "1.2"
|
139
|
+
version:
|
140
|
+
requirements: []
|
141
|
+
|
142
|
+
rubyforge_project: gorp
|
143
|
+
rubygems_version: 1.3.3
|
144
|
+
signing_key:
|
145
|
+
specification_version: 3
|
146
|
+
summary: Rails scenario testing support library
|
147
|
+
test_files: []
|
148
|
+
|