ruby-web 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
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