rubys-gorp 0.1.1

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