ruby-web 1.1.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.
Files changed (190) hide show
  1. data/ChangeLog +474 -0
  2. data/INSTALL.txt +9 -0
  3. data/InstalledFiles +180 -0
  4. data/LICENSE.txt +74 -0
  5. data/Rakefile +529 -0
  6. data/TODO +65 -0
  7. data/doc/additional.xml +149 -0
  8. data/doc/core.xml +652 -0
  9. data/doc/credits/index.xml +52 -0
  10. data/doc/credits/php.contributors.xml +118 -0
  11. data/doc/credits/php.language-snippets.ent +622 -0
  12. data/doc/install/index.xml +136 -0
  13. data/doc/install/mac/index.xml +21 -0
  14. data/doc/install/ruby-web.install.rb.instructions.xml +7 -0
  15. data/doc/install/unix/index.xml +46 -0
  16. data/doc/install/win/apache1.xml +166 -0
  17. data/doc/install/win/apache2.xml +141 -0
  18. data/doc/install/win/iis.xml +162 -0
  19. data/doc/install/win/index.xml +24 -0
  20. data/doc/install/win/installer.xml +31 -0
  21. data/doc/install/win/manual.xml +43 -0
  22. data/doc/manual.xml +69 -0
  23. data/doc/old/apache_cgi.txt +23 -0
  24. data/doc/old/fastcgi.txt +23 -0
  25. data/doc/old/mod_ruby.txt +21 -0
  26. data/doc/old/snippets.rdoc +183 -0
  27. data/doc/old/webrick.txt +23 -0
  28. data/doc/old/windows_cgi.txt +9 -0
  29. data/doc/tutorial.xml +14 -0
  30. data/doc/xsl/manual-multi.xsl +10 -0
  31. data/doc/xsl/manual-pdf.xsl +6 -0
  32. data/doc/xsl/manual-single.xsl +6 -0
  33. data/doc/xsl/manual.css +22 -0
  34. data/install.rb +1022 -0
  35. data/lib/formatter.rb +314 -0
  36. data/lib/html-parser.rb +429 -0
  37. data/lib/htmlrepair.rb +113 -0
  38. data/lib/htmlsplit.rb +842 -0
  39. data/lib/sgml-parser.rb +332 -0
  40. data/lib/web.rb +68 -0
  41. data/lib/web/assertinclude.rb +129 -0
  42. data/lib/web/config.rb +50 -0
  43. data/lib/web/connection.rb +1070 -0
  44. data/lib/web/convenience.rb +154 -0
  45. data/lib/web/formreader.rb +318 -0
  46. data/lib/web/htmlparser/html-parser.rb +429 -0
  47. data/lib/web/htmlparser/sgml-parser.rb +332 -0
  48. data/lib/web/htmltools/element.rb +296 -0
  49. data/lib/web/htmltools/stparser.rb +276 -0
  50. data/lib/web/htmltools/tags.rb +286 -0
  51. data/lib/web/htmltools/tree.rb +139 -0
  52. data/lib/web/htmltools/xmltree.rb +160 -0
  53. data/lib/web/htmltools/xpath.rb +71 -0
  54. data/lib/web/info.rb +63 -0
  55. data/lib/web/load.rb +210 -0
  56. data/lib/web/mime.rb +87 -0
  57. data/lib/web/phprb.rb +340 -0
  58. data/lib/web/resources/test/cookie.rb +33 -0
  59. data/lib/web/resources/test/counter.rb +20 -0
  60. data/lib/web/resources/test/multipart.rb +14 -0
  61. data/lib/web/resources/test/redirect.rb +8 -0
  62. data/lib/web/resources/test/stock.rb +33 -0
  63. data/lib/web/sapi/apache.rb +129 -0
  64. data/lib/web/sapi/fastcgi.rb +22 -0
  65. data/lib/web/sapi/install/apache.rb +180 -0
  66. data/lib/web/sapi/install/iis.rb +93 -0
  67. data/lib/web/sapi/install/macosx.rb +90 -0
  68. data/lib/web/sapi/webrick.rb +86 -0
  69. data/lib/web/session.rb +83 -0
  70. data/lib/web/shim/cgi.rb +129 -0
  71. data/lib/web/shim/rails.rb +175 -0
  72. data/lib/web/stringio.rb +78 -0
  73. data/lib/web/strscanparser.rb +24 -0
  74. data/lib/web/tagparser.rb +96 -0
  75. data/lib/web/testing.rb +666 -0
  76. data/lib/web/traceoutput.rb +75 -0
  77. data/lib/web/unit.rb +56 -0
  78. data/lib/web/upload.rb +59 -0
  79. data/lib/web/validate.rb +52 -0
  80. data/lib/web/wiki.rb +557 -0
  81. data/lib/web/wiki/linker.rb +72 -0
  82. data/lib/web/wiki/page.rb +201 -0
  83. data/lib/webunit.rb +27 -0
  84. data/lib/webunit/assert.rb +152 -0
  85. data/lib/webunit/converter.rb +154 -0
  86. data/lib/webunit/cookie.rb +118 -0
  87. data/lib/webunit/domwalker.rb +185 -0
  88. data/lib/webunit/exception.rb +14 -0
  89. data/lib/webunit/form.rb +116 -0
  90. data/lib/webunit/frame.rb +37 -0
  91. data/lib/webunit/htmlelem.rb +122 -0
  92. data/lib/webunit/image.rb +26 -0
  93. data/lib/webunit/jscript.rb +31 -0
  94. data/lib/webunit/link.rb +33 -0
  95. data/lib/webunit/params.rb +321 -0
  96. data/lib/webunit/parser.rb +229 -0
  97. data/lib/webunit/response.rb +464 -0
  98. data/lib/webunit/runtest.rb +41 -0
  99. data/lib/webunit/table.rb +148 -0
  100. data/lib/webunit/testcase.rb +45 -0
  101. data/lib/webunit/ui/cui/testrunner.rb +50 -0
  102. data/lib/webunit/utils.rb +68 -0
  103. data/lib/webunit/webunit.rb +28 -0
  104. data/test/dev/action.rb +83 -0
  105. data/test/dev/forms.rb +104 -0
  106. data/test/dev/forms2.rb +104 -0
  107. data/test/dev/parser.rb +17 -0
  108. data/test/dev/scripts/dump.rb +24 -0
  109. data/test/dev/scripts/makedist.rb +62 -0
  110. data/test/dev/scripts/uri.rb +41 -0
  111. data/test/dev/scripts/uri/common.rb +432 -0
  112. data/test/dev/scripts/uri/ftp.rb +149 -0
  113. data/test/dev/scripts/uri/generic.rb +1106 -0
  114. data/test/dev/scripts/uri/http.rb +76 -0
  115. data/test/dev/scripts/uri/https.rb +26 -0
  116. data/test/dev/scripts/uri/ldap.rb +238 -0
  117. data/test/dev/scripts/uri/mailto.rb +260 -0
  118. data/test/dev/scripts/urireg.rb +174 -0
  119. data/test/dev/simpledispatcher.rb +156 -0
  120. data/test/dev/test.action.rb +146 -0
  121. data/test/dev/test.formreader.rb +463 -0
  122. data/test/dev/test.simpledispatcher.rb +186 -0
  123. data/test/dev/webunit/conv/digit-0.rb +21 -0
  124. data/test/dev/webunit/conv/digit-1.rb +17 -0
  125. data/test/dev/webunit/conv/digit.rb +23 -0
  126. data/test/dev/webunit/conv/test_digit-0.rb +16 -0
  127. data/test/dev/webunit/conv/test_digit-1.rb +19 -0
  128. data/test/dev/webunit/conv/test_digit.rb +26 -0
  129. data/test/dev/webunit/conv/test_digit_view-0.rb +76 -0
  130. data/test/dev/webunit/conv/test_digit_view-1.rb +102 -0
  131. data/test/dev/webunit/conv/test_digit_view.rb +134 -0
  132. data/test/installation/htdocs/cgi_test.rb +296 -0
  133. data/test/installation/htdocs/test_install.rb +4 -0
  134. data/test/installation/runwebtest.rb +5 -0
  135. data/test/installation/test_cookie.rb +128 -0
  136. data/test/installation/test_form.rb +47 -0
  137. data/test/installation/test_multipart.rb +51 -0
  138. data/test/installation/test_request.rb +24 -0
  139. data/test/installation/test_response.rb +35 -0
  140. data/test/unit/htdocs/cookie.rb +32 -0
  141. data/test/unit/htdocs/multipart.rb +28 -0
  142. data/test/unit/htdocs/redirect.rb +12 -0
  143. data/test/unit/htdocs/simple.rb +13 -0
  144. data/test/unit/htdocs/stock.rb +33 -0
  145. data/test/unit/test_assert.rb +162 -0
  146. data/test/unit/test_cookie.rb +114 -0
  147. data/test/unit/test_domwalker.rb +77 -0
  148. data/test/unit/test_form.rb +42 -0
  149. data/test/unit/test_frame.rb +40 -0
  150. data/test/unit/test_htmlelem.rb +74 -0
  151. data/test/unit/test_image.rb +45 -0
  152. data/test/unit/test_jscript.rb +57 -0
  153. data/test/unit/test_link.rb +85 -0
  154. data/test/unit/test_multipart.rb +51 -0
  155. data/test/unit/test_params.rb +210 -0
  156. data/test/unit/test_parser.rb +53 -0
  157. data/test/unit/test_response.rb +150 -0
  158. data/test/unit/test_table.rb +70 -0
  159. data/test/unit/test_utils.rb +106 -0
  160. data/test/unit/test_webunit.rb +28 -0
  161. data/test/web/mod_ruby_stub.rb +39 -0
  162. data/test/web/test.assertinclude.rb +109 -0
  163. data/test/web/test.buffer.rb +182 -0
  164. data/test/web/test.code.loader.rb +78 -0
  165. data/test/web/test.config.rb +31 -0
  166. data/test/web/test.error.handling.rb +91 -0
  167. data/test/web/test.formreader-2.0.rb +352 -0
  168. data/test/web/test.load.rb +125 -0
  169. data/test/web/test.mime-type.rb +23 -0
  170. data/test/web/test.narf.cgi.rb +106 -0
  171. data/test/web/test.phprb.rb +239 -0
  172. data/test/web/test.request.rb +368 -0
  173. data/test/web/test.response.rb +637 -0
  174. data/test/web/test.ruby-web.rb +10 -0
  175. data/test/web/test.session.rb +50 -0
  176. data/test/web/test.shim.cgi.rb +96 -0
  177. data/test/web/test.tagparser.rb +65 -0
  178. data/test/web/test.template2.rb +297 -0
  179. data/test/web/test.testing2.rb +318 -0
  180. data/test/web/test.upload.rb +45 -0
  181. data/test/web/test.validate.rb +46 -0
  182. data/test/web/test.web.test.rb +495 -0
  183. data/test/wiki/test.history.rb +297 -0
  184. data/test/wiki/test.illustration_page.rb +287 -0
  185. data/test/wiki/test.linker.rb +197 -0
  186. data/test/wiki/test.tarpit.rb +56 -0
  187. data/test/wiki/test.wiki.rb +300 -0
  188. data/test/wikitestroot/admin.rb +7 -0
  189. data/test/wikitestroot/wiki.rb +6 -0
  190. metadata +234 -0
@@ -0,0 +1,175 @@
1
+ #require 'action_controller/cgi_ext/cgi_ext'
2
+ #require 'action_controller/cgi_ext/cookie_performance_fix'
3
+ #require 'action_controller/cgi_ext/raw_post_data_fix'
4
+ #require 'action_controller/session/drb_store'
5
+ #require 'action_controller/session/mem_cache_store'
6
+ #if Object.const_defined?(:ActiveRecord)
7
+ # require 'action_controller/session/active_record_store'
8
+ #end
9
+
10
+ module ActionController #:nodoc:
11
+ class Base
12
+ # Derived from CGI controller.
13
+ # Process a request extracted from an CGI object and return a response. Pass false as <tt>session_options</tt> to disable
14
+ # sessions (large performance increase if sessions are not needed). The <tt>session_options</tt> are the same as for CGI::Session:
15
+ #
16
+ # * <tt>:database_manager</tt> - standard options are CGI::Session::FileStore, CGI::Session::MemoryStore, and CGI::Session::PStore
17
+ # (default). Additionally, there is CGI::Session::DRbStore and CGI::Session::ActiveRecordStore. Read more about these in
18
+ # lib/action_controller/session.
19
+ # * <tt>:session_key</tt> - the parameter name used for the session id. Defaults to '_session_id'.
20
+ # * <tt>:session_id</tt> - the session id to use. If not provided, then it is retrieved from the +session_key+ parameter
21
+ # of the request, or automatically generated for a new session.
22
+ # * <tt>:new_session</tt> - if true, force creation of a new session. If not set, a new session is only created if none currently
23
+ # exists. If false, a new session is never created, and if none currently exists and the +session_id+ option is not set,
24
+ # an ArgumentError is raised.
25
+ # * <tt>:session_expires</tt> - the time the current session expires, as a +Time+ object. If not set, the session will continue
26
+ # indefinitely.
27
+ # * <tt>:session_domain</tt> - the hostname domain for which this session is valid. If not set, defaults to the hostname of the
28
+ # server.
29
+ # * <tt>:session_secure</tt> - if +true+, this session will only work over HTTPS.
30
+ # * <tt>:session_path</tt> - the path for which this session applies. Defaults to the directory of the CGI script.
31
+ def self.process_cgi(cgi = CGI.new, session_options = {})
32
+ new.process_cgi(cgi, session_options)
33
+ end
34
+
35
+ def process_cgi(cgi, session_options = {}) #:nodoc:
36
+ process(CgiRequest.new(cgi, session_options), CgiResponse.new(cgi)).out
37
+ end
38
+ end
39
+
40
+ class CgiRequest < AbstractRequest #:nodoc:
41
+ attr_accessor :cgi
42
+
43
+ DEFAULT_SESSION_OPTIONS = {
44
+ :database_manager => CGI::Session::PStore,
45
+ :prefix => "ruby_sess.",
46
+ :session_path => "/"
47
+ } unless const_defined?(:DEFAULT_SESSION_OPTIONS)
48
+
49
+ def initialize(cgi, session_options = {})
50
+ @cgi = cgi
51
+ @session_options = session_options
52
+ super()
53
+ end
54
+
55
+ def query_string
56
+ return @cgi.query_string unless @cgi.query_string.nil? || @cgi.query_string.empty?
57
+ unless env['REQUEST_URI'].nil?
58
+ parts = env['REQUEST_URI'].split('?')
59
+ else
60
+ return env['QUERY_STRING'] || ''
61
+ end
62
+ parts.shift
63
+ return parts.join('?')
64
+ end
65
+
66
+ def query_parameters
67
+ qs = self.query_string
68
+ qs.empty? ? {} : CGIMethods.parse_query_parameters(query_string)
69
+ end
70
+
71
+ def request_parameters
72
+ if formatted_post?
73
+ CGIMethods.parse_formatted_request_parameters(post_format, env['RAW_POST_DATA'])
74
+ else
75
+ CGIMethods.parse_request_parameters(@cgi.params)
76
+ end
77
+ end
78
+
79
+ def env
80
+ @cgi.send(:env_table)
81
+ end
82
+
83
+ def cookies
84
+ @cgi.cookies.freeze
85
+ end
86
+
87
+ def host
88
+ env["HTTP_X_FORWARDED_HOST"] || @cgi.host.to_s.split(":").first
89
+ end
90
+
91
+ def session
92
+ return @session unless @session.nil?
93
+
94
+ begin
95
+ @session = (@session_options == false ? {} : CGI::Session.new(@cgi, session_options_with_string_keys))
96
+ @session["__valid_session"]
97
+ return @session
98
+ rescue ArgumentError => e
99
+ if e.message =~ %r{undefined class/module (\w+)}
100
+ begin
101
+ Module.const_missing($1)
102
+ rescue LoadError, NameError => e
103
+ raise(
104
+ ActionController::SessionRestoreError,
105
+ "Session contained objects where the class definition wasn't available. " +
106
+ "Remember to require classes for all objects kept in the session. " +
107
+ "(Original exception: #{e.message} [#{e.class}])"
108
+ )
109
+ end
110
+
111
+ retry
112
+ else
113
+ raise
114
+ end
115
+ end
116
+ end
117
+
118
+ def reset_session
119
+ @session.delete
120
+ @session = (@session_options == false ? {} : new_session)
121
+ end
122
+
123
+ def method_missing(method_id, *arguments)
124
+ @cgi.send(method_id, *arguments) rescue super
125
+ end
126
+
127
+ private
128
+ def new_session
129
+ CGI::Session.new(@cgi, session_options_with_string_keys.merge("new_session" => true))
130
+ end
131
+
132
+ def session_options_with_string_keys
133
+ DEFAULT_SESSION_OPTIONS.merge(@session_options).inject({}) { |options, pair| options[pair.first.to_s] = pair.last; options }
134
+ end
135
+ end
136
+
137
+ class CgiResponse < AbstractResponse #:nodoc:
138
+ def initialize(cgi)
139
+ @cgi = cgi
140
+ super()
141
+ end
142
+
143
+ def out(output = $stdout)
144
+ convert_content_type!(@headers)
145
+ output.binmode if output.respond_to?(:binmode)
146
+ output.sync = false if output.respond_to?(:sync=)
147
+
148
+ begin
149
+ output.write(@cgi.header(@headers))
150
+
151
+ if @cgi.send(:env_table)['REQUEST_METHOD'] == 'HEAD'
152
+ return
153
+ elsif @body.respond_to?(:call)
154
+ @body.call(self)
155
+ else
156
+ output.write(@body)
157
+ end
158
+
159
+ output.flush if output.respond_to?(:flush)
160
+ rescue Errno::EPIPE => e
161
+ # lost connection to the FCGI process -- ignore the output, then
162
+ end
163
+ end
164
+
165
+ private
166
+ def convert_content_type!(headers)
167
+ %w( Content-Type Content-type content-type ).each do |ct|
168
+ if headers[ct]
169
+ headers["type"] = headers[ct]
170
+ headers.delete(ct)
171
+ end
172
+ end
173
+ end
174
+ end
175
+ end
@@ -0,0 +1,78 @@
1
+ module Web
2
+ # WritableIO uses << operator to add io style write, print, and puts methods
3
+ module WritableIO # :nodoc:
4
+ # writes object to output stream
5
+ def write object
6
+ self << object
7
+ end
8
+
9
+ # writes objects to the output stream
10
+ def print( *objects )
11
+ if (objects.empty?)
12
+ self << $_
13
+ else
14
+ self << objects.collect{ |s| s.to_s }.join("")
15
+ end
16
+ if $\ then self << $\ end
17
+ end
18
+
19
+ # writes multiple lines to the output stream
20
+ def puts( *objects )
21
+ objects.flatten!
22
+ self << objects.collect{ |s|
23
+ s.to_s
24
+ }.collect{ |s|
25
+ unless ( /(\r|\n)\z/ =~ s )
26
+ s + $/
27
+ else
28
+ s
29
+ end
30
+ }.join("")
31
+ end
32
+ end
33
+ end
34
+
35
+ begin
36
+ require('stringio')
37
+ rescue LoadError
38
+ module Web
39
+ class StringIO # :nodoc:
40
+ def initialize
41
+ @___content___ = ""
42
+ end
43
+
44
+ def read
45
+ @___content___
46
+ end
47
+
48
+ def clear
49
+ @___content___ = ""
50
+ end
51
+
52
+ def string
53
+ @___content___
54
+ end
55
+
56
+ def puts(*args)
57
+ @___content___ << args.join("\n") << "\n"
58
+ end
59
+
60
+ def << var
61
+ @___content___ << var.to_s
62
+ end
63
+
64
+ def write var
65
+ @___content___ << var.to_s
66
+ end
67
+
68
+ def print var
69
+ @___content___ << var.to_s
70
+ end
71
+ end
72
+ end
73
+
74
+ unless Kernel.const_defined? "StringIO"
75
+ StringIO = Web::StringIO.clone
76
+ end
77
+ end
78
+
@@ -0,0 +1,24 @@
1
+ class StrScanParserR # :nodoc:
2
+ def initialize scanner
3
+ @scanner = scanner
4
+ end
5
+
6
+ def match regex, &block
7
+ if s = @scanner.scan(regex)
8
+ # $stderr.puts "got: #{s}"
9
+ yield @scanner if block
10
+ s
11
+ else
12
+ # $stderr.puts "tried #{regex.source} on: #{@scanner.rest}"
13
+ nil
14
+ end
15
+ end
16
+
17
+ def rest?
18
+ @scanner.rest?
19
+ end
20
+
21
+ def rest
22
+ @scanner.rest
23
+ end
24
+ end
@@ -0,0 +1,96 @@
1
+ Struct.new("Tag",:name,:attributes)
2
+ Tag = Struct::Tag
3
+
4
+ class StrScanParser < StrScanParserR #:nodoc:
5
+
6
+ TAG_START = /<narf:/
7
+ TAG_END = />/
8
+ TAG_CLOSE_START = /<\/narf:/
9
+ TAG_NAME = /(\w+)/
10
+ WHITESPACE = /\s*/
11
+ QUOTE = /"/
12
+ QUOTED_ATTRIBUTE_VALUE = /([^"?]+)/
13
+ UNQUOTED_ATTRIBUTE_VALUE = /([\w.\/\$?]+)/
14
+ EQUALS = /=/
15
+ ATTRIBUTE_NAME = /(\w+)/
16
+
17
+ def match_value
18
+ if @scanner.scan(QUOTE)
19
+ if @scanner.scan(QUOTED_ATTRIBUTE_VALUE)
20
+ yield @scanner[1]
21
+ @scanner.scan(QUOTE) || raise(MissingQuoteException.new)
22
+ end
23
+ elsif @scanner.scan(UNQUOTED_ATTRIBUTE_VALUE)
24
+ yield @scanner[1]
25
+ end
26
+ end
27
+
28
+ def match_attribute
29
+ if @scanner.scan(ATTRIBUTE_NAME)
30
+ name = @scanner[1]
31
+ if @scanner.scan(EQUALS)
32
+ match_value do |value|
33
+ yield({ name => value })
34
+ end || raise(MissingAttributeException.new)
35
+ else
36
+ yield({ name => nil })
37
+ end
38
+ end
39
+ end
40
+
41
+ def match_tag
42
+ if @scanner.scan(TAG_START)
43
+ name = ""
44
+ nvs = {}
45
+ if @scanner.scan(TAG_NAME)
46
+ name = @scanner[1]
47
+ while true
48
+ @scanner.scan(WHITESPACE)
49
+ match_attribute do |nv|
50
+ nvs.update(nv)
51
+ end || if @scanner.scan(TAG_END)
52
+ yield Tag.new(name,nvs)
53
+ return true
54
+ else
55
+ raise(TagNotClosedException.new)
56
+ end
57
+ end
58
+ else
59
+ raise(TagNotNamedException.new)
60
+ end
61
+ end
62
+ end
63
+
64
+ def match_close_tag
65
+ if @scanner.scan(TAG_CLOSE_START)
66
+ tag = nil
67
+ if @scanner.scan(TAG_NAME)
68
+ tag = @scanner[1]
69
+ end # TODO raise missing name exception
70
+ @scanner.scan(TAG_END) # TODO raise missing end tag exception
71
+ yield tag if tag
72
+ end
73
+ end
74
+
75
+ def match_identifier
76
+ if @scanner.scan(/\{\$([\w.\?]*?)\}/)
77
+ yield @scanner[1]
78
+ end
79
+ end
80
+ end
81
+
82
+ class MissingAttributeException < Exception # :nodoc:
83
+
84
+ end
85
+
86
+ class TagNotClosedException < Exception # :nodoc:
87
+
88
+ end
89
+
90
+ class TagNotNamedException < Exception # :nodoc:
91
+
92
+ end
93
+
94
+ class MissingQuoteException < Exception # :nodoc:
95
+
96
+ end
@@ -0,0 +1,666 @@
1
+ require 'web'
2
+
3
+ require 'web/htmltools/tree'
4
+ module HTMLTree # :nodoc: all
5
+ module TreeElement
6
+ def get_elements aname, elements=[]
7
+ children.each { |element|
8
+ if element.tag == aname
9
+ elements.push element
10
+ else
11
+ element.get_elements aname, elements
12
+ end
13
+ }
14
+ elements
15
+ end
16
+ end
17
+ end
18
+
19
+ require 'web/assertinclude'
20
+ require 'web/strscanparser'
21
+ require 'web/tagparser'
22
+ require 'web/formreader.rb'
23
+
24
+ require 'test/unit/assertions'
25
+
26
+ module Web
27
+ def Web::setup( options = {} )
28
+ options[:out] ||= ''
29
+ Web::config[:error_style] = :testing
30
+ Web.connection = Web::Connection.new(options)
31
+ end
32
+
33
+ def Web::teardown
34
+ Web::config[:error_style] = :default
35
+ end
36
+
37
+ class Connection
38
+ include Test::Unit::Assertions
39
+
40
+ # for testing: raises Test::Unit exception if content
41
+ # is not set to the provided string.
42
+ def assert_content expected, message=""
43
+ assert_equal( expected, get_content, message );
44
+ end
45
+
46
+ def get_formreader #:nodoc:
47
+ return @form_fields_cache ||= FormReader.new( get_content )
48
+ end
49
+
50
+ def get_form( name ) #:nodoc:
51
+ get_formreader[name]
52
+ end
53
+
54
+ def get_form_fields(name) #:nodoc:
55
+ get_formreader.get_fields(name)
56
+ end
57
+
58
+ def get_form_value formname, name #:nodoc:
59
+ form = get_form_fields(formname)
60
+ if form == nil
61
+ raise Web::Error.new("Form '#{formname}' does not exist")
62
+ end
63
+
64
+ value = form[name]
65
+ value
66
+ end
67
+
68
+ # assert output content contains a form
69
+ # that includes the given hash of values. See Web::Testing
70
+ def assert_form_includes formname, vars
71
+ assert_includes vars, get_form_fields(formname)
72
+ end
73
+
74
+ # Raises Test::Unit::AssertionFailedError if the header
75
+ # has not been set to the provided value(s)
76
+ def assert_header( name, values, message="" )
77
+ assert_equal( [values].flatten, get_header(name), message )
78
+ end
79
+
80
+ # Throws Test::Unit::AssertionFailedException if
81
+ # cookie values are not present.
82
+ def assert_cookie( name, values, message="" )
83
+ assert_equal( [ values ].flatten, [ cookies_sent[name] ].flatten, message )
84
+ end
85
+
86
+ # Check that a particluar template was used
87
+ def assert_template_used expected, message =""
88
+ _wrap_assertion {
89
+ flunk("No template used") if @templates == nil
90
+ unless @templates.include? expected
91
+ msg = "expected template:<#{expected}> but not one of:<#{@templates.inspect}>"
92
+ flunk(msg)
93
+ end
94
+ }
95
+ end
96
+
97
+ # Check that a particluar template was not used
98
+ def assert_template_not_used expected, message =""
99
+ _wrap_assertion {
100
+ if (@templates.kind_of?(Array) && @templates.include?(expected) )
101
+ msg = "template:<#{expected}> not supposed to be used in:<#{@templates.inspect}>"
102
+ flunk(msg)
103
+ end
104
+ }
105
+ end
106
+
107
+ # Check that these values were included in the output
108
+ def assert_local_includes expected
109
+ assert_includes expected, @local
110
+ end
111
+
112
+ def assert_vars_equals
113
+ raise Exception.new("Not yet implemented")
114
+ end
115
+
116
+
117
+ end
118
+
119
+ # == Purpose
120
+ # The testing module facilitates the testing of Web applications
121
+ # without the overhead of a web server to run.
122
+ #
123
+ # Given these files:
124
+ #
125
+ # script.rb
126
+ # test.rb
127
+ #
128
+ # where script.rb is:
129
+ #
130
+ # #!/usr/bin/ruby
131
+ # require 'web'
132
+ #
133
+ # Web::process {
134
+ # Web.write "param is #{Web["param"]}"
135
+ # }
136
+ #
137
+ # and test.rb is:
138
+ #
139
+ # require 'web'
140
+ # require 'test/unit'
141
+ #
142
+ # class MyAppTest < Test::Unit::TestCase
143
+ # include Web::Testing
144
+ #
145
+ # def test_prints_content
146
+ # do_request "script.rb", "param" => "THIS!"
147
+ # assert_content "param is THIS!"
148
+ # end
149
+ # end
150
+ #
151
+ # Do this to run tests:
152
+ #
153
+ # ruby test.rb
154
+ #
155
+ # If you have a more complicated app, where the tests live in
156
+ # a different place than your scripts, you can use:
157
+ #
158
+ # Web::docroot = path
159
+ #
160
+ # To tell narf where to find your cgi scripts.
161
+ #
162
+ #
163
+ # === Testing with Templates
164
+ #
165
+ # Using Narflates you can test functionality without having to
166
+ # do lengthly string comparisons. For example, create the following
167
+ # file in 'mytemplate.html'
168
+ #
169
+ # <html>
170
+ # <body>
171
+ # {$myvar}
172
+ # </body>
173
+ # </html>
174
+ #
175
+ # Create a 'script.rb' as follows:
176
+ #
177
+ # #!/usr/bin/narf
178
+ # require 'web'
179
+ #
180
+ # Web::process{
181
+ # Web.print_template "mytemplate.html", { "myvar" => "Hello World" }
182
+ # }
183
+ #
184
+ # Now, we can check that the right values got displayed without
185
+ # needing to check that the template is correct as a side effect.
186
+ # Save this into 'test.rb' and run it:
187
+ #
188
+ # require 'web'
189
+ #
190
+ # class MyAppTest < Test::Unit::TestCase
191
+ # include Web::Testing # adds the modules
192
+ #
193
+ # def test_prints_content
194
+ # do_request "script.rb"
195
+ # assert_vars_includes "myvar" => "Hello World"
196
+ # end
197
+ # end
198
+ #
199
+ # === Testing Forms
200
+ #
201
+ # The following example demonstrates testing a simple HTML form.
202
+ # Creating mytemplate.html as:
203
+ #
204
+ #
205
+ # <html>
206
+ # <body>
207
+ # <form name="myform">
208
+ # <input type="text" name="foo">
209
+ # <input type="submit" name="submit" value="Submit">
210
+ # </body>
211
+ # </html>
212
+ #
213
+ # To print this form and handle a submit save this as 'script.rb':
214
+ #
215
+ # #!/usr/bin/narf
216
+ #
217
+ # require 'web'
218
+ #
219
+ # Web::process {
220
+ # if Web["submit"] # check to see whether a form was
221
+ # Web.puts "Form Submitted with value '#{Web["foo"]}'"
222
+ # else
223
+ # Web.print_template "mytemplate.html"
224
+ # end
225
+ # }
226
+ #
227
+ # Use this 'test.rb' to test it:
228
+ #
229
+ # class MyAppTest < Test::Unit::TestCase
230
+ # include Web::Testing # adds the modules
231
+ #
232
+ # def test_prints_content
233
+ # do_request "script.rb"
234
+ # do_submit "myform", "foo" => "bar"
235
+ # assert_content "Form Submitted with value '#{Web["foo"]}'"
236
+ # end
237
+ # end
238
+ #
239
+ #
240
+ # === Test <input type="text|password|hidden"> and <textarea>
241
+ #
242
+ # html:
243
+ #
244
+ # <form name='aForm'>
245
+ # <input name="bare">
246
+ # <input name="named" type="text" value="foo">
247
+ # <input name="pass" type="password" value="secret">
248
+ # <input name="obscure" type="hidden" value="discrete">
249
+ # <textarea name="big_text">
250
+ # big paragraph here
251
+ # </textarea>
252
+ # </form>
253
+ #
254
+ # assert:
255
+ #
256
+ # assert_form_includes( 'aForm', "bare" => "",
257
+ # "named" => "foo",
258
+ # "pass" => "secret",
259
+ # "obscure" => "discrete",
260
+ # "big_text" => "big paragraph here" )
261
+ #
262
+ # submit:
263
+ #
264
+ # do_submit( 'aForm', "bare" => "empty",
265
+ # "named" => "bare",
266
+ # "pass" => "shhhhh",
267
+ # "obscure" => "secretive",
268
+ # "big_text" => "windbag" )
269
+ #
270
+ # === Test <input type="file">
271
+ #
272
+ # html:
273
+ #
274
+ # <form name='aForm' enctype='multipart/form-data'>
275
+ # <input name="upload" type="file">
276
+ # </form>
277
+ #
278
+ # assert:
279
+ #
280
+ # assert_form_includes( 'aForm', "upload" => "" )
281
+ #
282
+ # submit:
283
+ #
284
+ # do_submit( 'aForm', "upload" => Web::Upload.new(
285
+ # File.new( "testfile" ),
286
+ # "content-type",
287
+ # "original-filename" ) )
288
+ #
289
+ # === Test <select> and <input type="radio">
290
+ #
291
+ #
292
+ #
293
+ # === Test <select multiple> and <input type="checkbox">
294
+ #
295
+ #
296
+ # === Bugs: Unsupported behaviour
297
+ #
298
+ # The following situations will have unknown results:
299
+ #
300
+ # * Combining different types of elements into one field;
301
+ # i.e. <input name="field" type="text"> and <select name="field">
302
+ # * Comparing <input name="page.name"> and <input name="page.content">
303
+ # with assert_vars_include("page" => { "name" => ..., "content" => ... } )
304
+ #
305
+ module Testing
306
+ class FormNotFoundException < Exception #:nodoc:
307
+ end
308
+
309
+ class FieldNotFoundException < Exception #:nodoc:
310
+ end
311
+
312
+ class MustSetDocrootForAbsolutePathException < Exception #:nodoc:
313
+ end
314
+
315
+ def write_file( path, content )
316
+ filename = File.join( Web::docroot, path )
317
+ filedir = File.dirname(filename)
318
+ unless File.exists? filedir
319
+ require 'fileutils'
320
+ FileUtils::mkdir_p(filedir, :verbose => false)
321
+ end
322
+
323
+ File.open( filename, "w" ) do |f|
324
+ f.write content
325
+ end
326
+ end
327
+
328
+ def select( *args )
329
+ hash = {}
330
+
331
+ if (args.length == 1 and args[0].kind_of? Hash)
332
+ hash = args[0]
333
+ else
334
+ args.each { |e|
335
+ hash[e] = true
336
+ }
337
+ end
338
+
339
+ hash
340
+ end
341
+
342
+ @@test_session = nil
343
+
344
+ # Reset the session used by the test framework. Call prior to all tests
345
+ # that rely on the session being clean
346
+ def reset_session
347
+ @@test_session = nil
348
+ end
349
+
350
+ # Run a request, parameters are the name value pairs that would be
351
+ # passed in the query string. The webpath is a document root relative
352
+ # path to a ruby script.
353
+ def do_request(webpath, parameters={})
354
+ options = {}
355
+ @@test_session ||= {}
356
+ options[:session] = @@test_session
357
+ options[:env] = parameters[:env]
358
+ options[:request] = parameters
359
+ options[:document_root] = Web::docroot
360
+ options[:out] = StringIO.new
361
+ script_path, script_name, path_info = get_script_part(webpath)
362
+
363
+ options[:path_info] = path_info
364
+ options[:script_name] = script_name
365
+ load_request( options, script_path, webpath )
366
+ end
367
+
368
+ def load_request( options, script_path, webpath ) #:nodoc:
369
+ options[:webpath] = webpath
370
+ ### re-throw errors
371
+ Web::config[:error_style] = :custom
372
+ Web::config[:error_handler] = lambda do |error|
373
+ raise error
374
+ end
375
+
376
+ # out with the old....
377
+ #Web::connection.close
378
+ Web::connection = nil
379
+ error = nil
380
+ Web::open( options ) do
381
+ Web::load( script_path )
382
+ end
383
+
384
+ if Web.status == "302"
385
+ Web.location =~ /(.*)\?(.*)/
386
+ target = $1
387
+ params = Connection::parse_query_string($2)
388
+
389
+ unless (target =~ /^\//)
390
+ webpath =~ /(.*)#{File.basename(script_path)}/
391
+ target = $1 + target
392
+ end
393
+ do_request( target, params )
394
+ end
395
+ end
396
+
397
+ # Submit the form 'formname' with the formfields described in newvalues
398
+ def do_submit( formname, newvalues={} )
399
+ form = Web.get_form(formname)
400
+
401
+ if form == nil
402
+ #if Web.get_form_fields[formname] == nil
403
+ raise FormNotFoundException.new("Form '#{formname}' does not exist")
404
+ end
405
+
406
+ #MultiHashTree::flatten(newvalues).keys.each do |key|
407
+ #unless( Web.get_form_fields[formname].valid_key?(key) )
408
+ # raise FieldNotFoundException.new( "#{ key } is not present in form" )
409
+ #end
410
+ #end
411
+
412
+ webpath = nil
413
+
414
+ #Web.get_html_tree.get_elements("form").each { |form|
415
+ # if (form.attribute("name") == formname)
416
+ # webpath = form.attribute("action")
417
+ # end
418
+ #}
419
+ webpath = form.action
420
+
421
+ unless (webpath)
422
+ webpath = Web.options[:webpath]
423
+ end
424
+
425
+ script_path, script_name, path_info = get_script_part(webpath)
426
+
427
+ old_connection = Web.connection
428
+
429
+ options = {}
430
+ @@test_session ||= {}
431
+ options[:session] = @@test_session
432
+ options[:out] = StringIO.new
433
+ ### patrick
434
+ options[:request] = form.merge_fields( newvalues )
435
+ #options[:request] = Web.get_form_fields[formname].params(MultiHashTree::flatten(newvalues))
436
+ options[:document_root] = old_connection.document_root
437
+
438
+ options[:path_info] = path_info #old_connection.path_info
439
+ options[:script_name] = script_name #old_connection.script_name
440
+
441
+ load_request( options, script_path, webpath )
442
+ end
443
+
444
+ # Assert that a give template was displayed
445
+ def assert_template_used filename, msg=""
446
+ Web.assert_template_used filename, msg
447
+ end
448
+
449
+ # Assert that a give template was not displayed
450
+ def assert_template_not_used filename, msg=""
451
+ Web.assert_template_not_used filename, msg
452
+ end
453
+
454
+ # Assert that the values passed in to expected were set on the template
455
+ def assert_vars_includes expected
456
+ Web.assert_vars_includes expected
457
+ end
458
+
459
+ # Assert that the form displayed contains particular values
460
+ def assert_form_includes formname, expected
461
+ Web::assert_form_includes formname, expected
462
+ end
463
+
464
+ # Assert that the entire content displayed is equal to expected
465
+ def assert_content expected, msg=""
466
+ Web.assert_content expected, msg
467
+ end
468
+
469
+ # Assert that the header key has the value 'value'
470
+ def assert_header key, value, msg=""
471
+ Web.assert_header key, value, msg
472
+ end
473
+
474
+ # Assert that the cookie key, has the cookie value
475
+ def assert_cookie key, value, msg=""
476
+ Web.assert_cookie key, value, msg
477
+ end
478
+
479
+ # Assert that a form field has exactly the given options
480
+ # can't assert order, though
481
+ def assert_options( formname, expected={})
482
+ options = Web.get_formreader.get_options(formname)
483
+ expected.each{ |k,v|
484
+ assert_equal( v.sort, options[k].sort )
485
+ }
486
+ end
487
+
488
+ def remove_trailing_slashes(filename) #:nodoc:
489
+ /(.*?)\/?$/.match(filename)[1]
490
+ end
491
+
492
+ def get_script_part(webpath) #:nodoc:
493
+ # two cases:
494
+ # absolute webpath (requires Web::docroot to be correct)
495
+ # relative webpath (doesn't require Web::docroot to be correct)
496
+ if ( webpath =~ /^\// )
497
+ prefix = Web::docroot
498
+ raise MustSetDocrootForAbsolutePathException unless prefix
499
+ path = "/"
500
+ else
501
+ prefix = ""
502
+ path = ""
503
+ end
504
+
505
+ new_script_path = ""
506
+ webpath.split("/").each do |file|
507
+ file =~ /^([a-zA-Z]{1,1}:)$/
508
+
509
+ if (path.empty?)
510
+ newpath = file
511
+ else
512
+ newpath = File.join( path , file ).gsub("//","/")
513
+ end
514
+
515
+ new_script_path = ""
516
+ if (prefix.empty?)
517
+ new_script_path = newpath
518
+ else
519
+ new_script_path = File.join( prefix, newpath ).gsub("//","/")
520
+ end
521
+
522
+ if (File.file?(new_script_path))
523
+ return new_script_path, "/" + File.basename( new_script_path ), webpath[newpath.length...webpath.length]
524
+ end
525
+
526
+ path = newpath
527
+ end
528
+
529
+ if (!File.exists?(new_script_path) && prefix.empty?)
530
+ return get_script_part( "/" + webpath )
531
+ end
532
+
533
+ return new_script_path, "/" + File.basename( new_script_path ), ""
534
+ end
535
+
536
+ class SelectHash < Hash #:nodoc:
537
+ def == other
538
+ if (other.kind_of? Hash)
539
+ super(other)
540
+ elsif (other.class == TrueClass)
541
+ result = true
542
+ self.each{ |k, v|
543
+ result = false unless v
544
+ }
545
+ result
546
+ elsif (other.class == FalseClass)
547
+ result = true
548
+ self.each{ |k,v|
549
+ result = false if v
550
+ }
551
+ result
552
+ else
553
+ ### this is wrong
554
+ other.to_s == self.default.join(",")
555
+ end
556
+ end
557
+
558
+ def field_value
559
+ values = [ ]
560
+ self.each{ |k,v|
561
+ values.push k if v
562
+ }
563
+ values.join(",")
564
+ end
565
+
566
+ end
567
+
568
+ # could I get an explanation of this?
569
+ # just for me? please?
570
+ # not to long... probably not longer than this comment here.
571
+ class MultiHashTree # :nodoc:
572
+ attr_reader :fields, :unmodified_fields
573
+
574
+ def initialize fields = {}
575
+ @fields = {}
576
+ @unmodified_fields = {}
577
+ fields.each do |k,value_array|
578
+ value_array.each{ |v|
579
+ push_field k, v
580
+ }
581
+ end
582
+ end
583
+
584
+ def valid_key? (aKey)
585
+ unmodified_fields.has_key?(aKey) || fields.has_key?(aKey)
586
+ end
587
+
588
+ def push_impl hash, name, value
589
+ if name =~ Web::Connection::MULTIPLE_KEY && !value.kind_of?(Web::Testing::SelectHash)
590
+ hash[name] ||= []
591
+ hash[name].push value
592
+ elsif m = /^(\w+(\[\])?)\.(.+)/.match(name)
593
+ hash[m[1]] ||= {}
594
+ push_impl(hash[m[1]], m[3], value)
595
+ elsif m = /^(\w+)\[(\d+)\].(.+)/.match(name)
596
+ hash[m[1]] ||= []
597
+ hash[m[1]][m[2].to_i] ||= {}
598
+ push_impl hash[m[1]][m[2].to_i], m[3], value
599
+ else
600
+ hash[name] ||= []
601
+ hash[name].push(value)
602
+ end
603
+ end
604
+
605
+ def push_field name, value
606
+ @unmodified_fields[name] ||= []
607
+ @unmodified_fields[name].push value
608
+ push_impl @fields, name, value
609
+ end
610
+
611
+ def params values
612
+ retval = @unmodified_fields.clone
613
+ values.each do |key,value|
614
+ if value.kind_of? SelectHash
615
+ selected_options = []
616
+ value.each{ |k,v|
617
+ selected_options.push k if v
618
+ }
619
+
620
+ if m = Web::Connection::MULTIPLE_KEY.match(key)
621
+ retval[key] ||= []
622
+ retval[key] = (retval[key] + selected_options).uniq
623
+ else
624
+ retval[key] = selected_options.join(",")
625
+ end
626
+
627
+ else
628
+ if m = Web::Connection::MULTIPLE_KEY.match(key)
629
+ retval[key] ||= []
630
+ retval[key] << value
631
+ else
632
+ retval[key] = value
633
+ end
634
+ end
635
+ end
636
+ retval
637
+ end
638
+
639
+ def MultiHashTree.flatten node, newhash = {} , nameroot = "" # :nodoc:
640
+ if node.kind_of? Hash
641
+ node.each do |k,v|
642
+ if nameroot == ""
643
+ varname = k
644
+ else
645
+ varname = "#{nameroot}.#{k}"
646
+ end
647
+ flatten( v, newhash, varname )
648
+ end
649
+ elsif node.kind_of? Array
650
+ unless node.find { |i| !i.kind_of? String }
651
+ newhash[nameroot] = node
652
+ else
653
+ node.each_with_index do |v,i|
654
+ flatten( v, newhash, "#{nameroot}[#{i}]" )
655
+ end
656
+ end
657
+ else
658
+ newhash[ nameroot ]=node
659
+ end
660
+ newhash
661
+ end
662
+
663
+ end
664
+
665
+ end
666
+ end