arrow 1.0.7

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 (198) hide show
  1. data/ChangeLog +1590 -0
  2. data/LICENSE +28 -0
  3. data/README +75 -0
  4. data/Rakefile +366 -0
  5. data/Rakefile.local +63 -0
  6. data/data/arrow/applets/TEMPLATE.rb.tpl +53 -0
  7. data/data/arrow/applets/args.rb +50 -0
  8. data/data/arrow/applets/config.rb +55 -0
  9. data/data/arrow/applets/error.rb +63 -0
  10. data/data/arrow/applets/files.rb +46 -0
  11. data/data/arrow/applets/inspect.rb +46 -0
  12. data/data/arrow/applets/nosuchapplet.rb +31 -0
  13. data/data/arrow/applets/status.rb +92 -0
  14. data/data/arrow/applets/test.rb +133 -0
  15. data/data/arrow/applets/tutorial/counter.rb +96 -0
  16. data/data/arrow/applets/tutorial/dingus.rb +67 -0
  17. data/data/arrow/applets/tutorial/hello.rb +34 -0
  18. data/data/arrow/applets/tutorial/hello2.rb +73 -0
  19. data/data/arrow/applets/tutorial/imgtext.rb +90 -0
  20. data/data/arrow/applets/tutorial/imgtext2.rb +286 -0
  21. data/data/arrow/applets/tutorial/index.rb +36 -0
  22. data/data/arrow/applets/tutorial/logo.rb +98 -0
  23. data/data/arrow/applets/tutorial/memcache.rb +61 -0
  24. data/data/arrow/applets/tutorial/missing.rb +37 -0
  25. data/data/arrow/applets/tutorial/protected.rb +100 -0
  26. data/data/arrow/applets/tutorial/redirector.rb +52 -0
  27. data/data/arrow/applets/tutorial/rndimages.rb +159 -0
  28. data/data/arrow/applets/tutorial/sharenotes.rb +83 -0
  29. data/data/arrow/applets/tutorial/subclassed-hello.rb +32 -0
  30. data/data/arrow/applets/tutorial/superhello.rb +72 -0
  31. data/data/arrow/applets/tutorial/timeclock.rb +78 -0
  32. data/data/arrow/applets/view-applet.rb +123 -0
  33. data/data/arrow/applets/view-template.rb +85 -0
  34. data/data/arrow/applets/wiki.rb +274 -0
  35. data/data/arrow/templates/TEMPLATE.tmpl.tpl +36 -0
  36. data/data/arrow/templates/applet-status.tmpl +153 -0
  37. data/data/arrow/templates/args-display.tmpl +120 -0
  38. data/data/arrow/templates/config/display-table.tmpl +36 -0
  39. data/data/arrow/templates/config/display.tmpl +36 -0
  40. data/data/arrow/templates/counter-deleted.tmpl +33 -0
  41. data/data/arrow/templates/counter.tmpl +59 -0
  42. data/data/arrow/templates/dingus.tmpl +55 -0
  43. data/data/arrow/templates/enumtable.tmpl +8 -0
  44. data/data/arrow/templates/error-display.tmpl +92 -0
  45. data/data/arrow/templates/filemap.tmpl +89 -0
  46. data/data/arrow/templates/hello-world-src.tmpl +34 -0
  47. data/data/arrow/templates/hello-world.tmpl +60 -0
  48. data/data/arrow/templates/imgtext/fontlist.tmpl +46 -0
  49. data/data/arrow/templates/imgtext/form.tmpl +70 -0
  50. data/data/arrow/templates/imgtext/reload-error.tmpl +40 -0
  51. data/data/arrow/templates/imgtext/reload.tmpl +55 -0
  52. data/data/arrow/templates/inspect/display.tmpl +80 -0
  53. data/data/arrow/templates/loginform.tmpl +64 -0
  54. data/data/arrow/templates/logout.tmpl +32 -0
  55. data/data/arrow/templates/memcache/display.tmpl +41 -0
  56. data/data/arrow/templates/navbar.incl +27 -0
  57. data/data/arrow/templates/nosuchapplet.tmpl +32 -0
  58. data/data/arrow/templates/printsource.tmpl +35 -0
  59. data/data/arrow/templates/protected.tmpl +36 -0
  60. data/data/arrow/templates/rndimages.tmpl +38 -0
  61. data/data/arrow/templates/service-response.tmpl +13 -0
  62. data/data/arrow/templates/sharenotes/display.tmpl +38 -0
  63. data/data/arrow/templates/status.tmpl +120 -0
  64. data/data/arrow/templates/templateviewer.tmpl +43 -0
  65. data/data/arrow/templates/test/harness.tmpl +57 -0
  66. data/data/arrow/templates/test/list.tmpl +48 -0
  67. data/data/arrow/templates/test/problem.tmpl +42 -0
  68. data/data/arrow/templates/tutorial/index.tmpl +37 -0
  69. data/data/arrow/templates/tutorial/missingapplet.tmpl +29 -0
  70. data/data/arrow/templates/view-applet-nosuch.tmpl +32 -0
  71. data/data/arrow/templates/view-applet.tmpl +40 -0
  72. data/data/arrow/templates/view-template.tmpl +83 -0
  73. data/data/arrow/templates/wiki/formerror.tmpl +47 -0
  74. data/data/arrow/templates/wiki/markup_help.incl +6 -0
  75. data/data/arrow/templates/wiki/new.tmpl +56 -0
  76. data/data/arrow/templates/wiki/new_system.tmpl +122 -0
  77. data/data/arrow/templates/wiki/sectionlist.tmpl +43 -0
  78. data/data/arrow/templates/wiki/show.tmpl +34 -0
  79. data/docs/manual/layouts/default.page +43 -0
  80. data/docs/manual/lib/api-filter.rb +81 -0
  81. data/docs/manual/lib/editorial-filter.rb +64 -0
  82. data/docs/manual/lib/examples-filter.rb +244 -0
  83. data/docs/manual/lib/links-filter.rb +117 -0
  84. data/lib/apache/fakerequest.rb +448 -0
  85. data/lib/apache/logger.rb +33 -0
  86. data/lib/arrow.rb +51 -0
  87. data/lib/arrow/acceptparam.rb +207 -0
  88. data/lib/arrow/applet.rb +725 -0
  89. data/lib/arrow/appletmixins.rb +218 -0
  90. data/lib/arrow/appletregistry.rb +590 -0
  91. data/lib/arrow/applettestcase.rb +503 -0
  92. data/lib/arrow/broker.rb +255 -0
  93. data/lib/arrow/cache.rb +176 -0
  94. data/lib/arrow/config-loaders/yaml.rb +75 -0
  95. data/lib/arrow/config.rb +615 -0
  96. data/lib/arrow/constants.rb +24 -0
  97. data/lib/arrow/cookie.rb +359 -0
  98. data/lib/arrow/cookieset.rb +108 -0
  99. data/lib/arrow/dispatcher.rb +368 -0
  100. data/lib/arrow/dispatcherloader.rb +50 -0
  101. data/lib/arrow/exceptions.rb +61 -0
  102. data/lib/arrow/fallbackhandler.rb +48 -0
  103. data/lib/arrow/formvalidator.rb +631 -0
  104. data/lib/arrow/htmltokenizer.rb +343 -0
  105. data/lib/arrow/logger.rb +488 -0
  106. data/lib/arrow/logger/apacheoutputter.rb +69 -0
  107. data/lib/arrow/logger/arrayoutputter.rb +63 -0
  108. data/lib/arrow/logger/coloroutputter.rb +111 -0
  109. data/lib/arrow/logger/fileoutputter.rb +96 -0
  110. data/lib/arrow/logger/htmloutputter.rb +54 -0
  111. data/lib/arrow/logger/outputter.rb +123 -0
  112. data/lib/arrow/mixins.rb +425 -0
  113. data/lib/arrow/monkeypatches.rb +94 -0
  114. data/lib/arrow/object.rb +117 -0
  115. data/lib/arrow/path.rb +196 -0
  116. data/lib/arrow/service.rb +447 -0
  117. data/lib/arrow/session.rb +289 -0
  118. data/lib/arrow/session/dbstore.rb +100 -0
  119. data/lib/arrow/session/filelock.rb +160 -0
  120. data/lib/arrow/session/filestore.rb +132 -0
  121. data/lib/arrow/session/id.rb +98 -0
  122. data/lib/arrow/session/lock.rb +253 -0
  123. data/lib/arrow/session/md5id.rb +42 -0
  124. data/lib/arrow/session/nulllock.rb +42 -0
  125. data/lib/arrow/session/posixlock.rb +166 -0
  126. data/lib/arrow/session/sha1id.rb +54 -0
  127. data/lib/arrow/session/store.rb +366 -0
  128. data/lib/arrow/session/usertrackid.rb +52 -0
  129. data/lib/arrow/spechelpers.rb +73 -0
  130. data/lib/arrow/template.rb +713 -0
  131. data/lib/arrow/template/attr.rb +31 -0
  132. data/lib/arrow/template/call.rb +31 -0
  133. data/lib/arrow/template/comment.rb +33 -0
  134. data/lib/arrow/template/container.rb +118 -0
  135. data/lib/arrow/template/else.rb +41 -0
  136. data/lib/arrow/template/elsif.rb +44 -0
  137. data/lib/arrow/template/escape.rb +53 -0
  138. data/lib/arrow/template/export.rb +87 -0
  139. data/lib/arrow/template/for.rb +145 -0
  140. data/lib/arrow/template/if.rb +78 -0
  141. data/lib/arrow/template/import.rb +119 -0
  142. data/lib/arrow/template/include.rb +206 -0
  143. data/lib/arrow/template/iterator.rb +208 -0
  144. data/lib/arrow/template/nodes.rb +734 -0
  145. data/lib/arrow/template/parser.rb +571 -0
  146. data/lib/arrow/template/prettyprint.rb +53 -0
  147. data/lib/arrow/template/render.rb +191 -0
  148. data/lib/arrow/template/selectlist.rb +94 -0
  149. data/lib/arrow/template/set.rb +87 -0
  150. data/lib/arrow/template/timedelta.rb +81 -0
  151. data/lib/arrow/template/unless.rb +78 -0
  152. data/lib/arrow/template/urlencode.rb +51 -0
  153. data/lib/arrow/template/yield.rb +139 -0
  154. data/lib/arrow/templatefactory.rb +125 -0
  155. data/lib/arrow/testcase.rb +567 -0
  156. data/lib/arrow/transaction.rb +608 -0
  157. data/rake/191_compat.rb +26 -0
  158. data/rake/dependencies.rb +76 -0
  159. data/rake/documentation.rb +114 -0
  160. data/rake/helpers.rb +502 -0
  161. data/rake/hg.rb +282 -0
  162. data/rake/manual.rb +787 -0
  163. data/rake/packaging.rb +129 -0
  164. data/rake/publishing.rb +278 -0
  165. data/rake/style.rb +62 -0
  166. data/rake/svn.rb +668 -0
  167. data/rake/testing.rb +187 -0
  168. data/rake/verifytask.rb +64 -0
  169. data/spec/arrow/acceptparam_spec.rb +157 -0
  170. data/spec/arrow/applet_spec.rb +575 -0
  171. data/spec/arrow/appletmixins_spec.rb +409 -0
  172. data/spec/arrow/appletregistry_spec.rb +294 -0
  173. data/spec/arrow/broker_spec.rb +153 -0
  174. data/spec/arrow/config_spec.rb +224 -0
  175. data/spec/arrow/cookieset_spec.rb +164 -0
  176. data/spec/arrow/dispatcher_spec.rb +137 -0
  177. data/spec/arrow/dispatcherloader_spec.rb +65 -0
  178. data/spec/arrow/formvalidator_spec.rb +781 -0
  179. data/spec/arrow/logger_spec.rb +346 -0
  180. data/spec/arrow/mixins_spec.rb +120 -0
  181. data/spec/arrow/service_spec.rb +645 -0
  182. data/spec/arrow/session_spec.rb +121 -0
  183. data/spec/arrow/template/iterator_spec.rb +222 -0
  184. data/spec/arrow/templatefactory_spec.rb +185 -0
  185. data/spec/arrow/transaction_spec.rb +319 -0
  186. data/spec/arrow_spec.rb +37 -0
  187. data/spec/lib/appletmatchers.rb +281 -0
  188. data/spec/lib/constants.rb +77 -0
  189. data/spec/lib/helpers.rb +41 -0
  190. data/spec/lib/matchers.rb +44 -0
  191. data/tests/cookie.tests.rb +310 -0
  192. data/tests/path.tests.rb +157 -0
  193. data/tests/session.tests.rb +111 -0
  194. data/tests/session_id.tests.rb +82 -0
  195. data/tests/session_lock.tests.rb +191 -0
  196. data/tests/session_store.tests.rb +53 -0
  197. data/tests/template.tests.rb +1360 -0
  198. metadata +339 -0
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env ruby
2
+
3
+
4
+ module Arrow
5
+ module Constants
6
+
7
+ # The domain to put Arrow objects into
8
+ YAML_DOMAIN = "rubycrafters.com,2003-10-22"
9
+
10
+ # The mimetype to use for XHTML content
11
+ XHTML_MIMETYPE = 'application/xhtml+xml'
12
+
13
+ # The mimetype to use for HTML content
14
+ HTML_MIMETYPE = 'text/html'
15
+
16
+ # The mimetype to use for marshalled Ruby objects
17
+ RUBY_MARSHALLED_MIMETYPE = 'application/x-ruby-marshalled-object'
18
+
19
+ # The mimetype to use for raw Ruby objects
20
+ RUBY_OBJECT_MIMETYPE = 'application/x-ruby-object'
21
+
22
+ end # module Constants
23
+ end # module Arrow
24
+
@@ -0,0 +1,359 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'date'
4
+ require 'time'
5
+ require 'uri'
6
+
7
+ require 'arrow/object'
8
+
9
+ # The Arrow::Cookie class, a class for parsing and generating HTTP cookies.
10
+ #
11
+ # Large parts of this code were copied from the Webrick::Cookie class
12
+ # in the Ruby standard library. The copyright statements for that module
13
+ # are:
14
+ #
15
+ # Author: IPR -- Internet Programming with Ruby -- writers
16
+ # Copyright (c) 2000, 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou
17
+ # Copyright (c) 2002 Internet Programming with Ruby writers. All rights
18
+ # reserved.
19
+ #
20
+ # == VCS Id
21
+ #
22
+ # $Id$
23
+ #
24
+ # == Authors
25
+ #
26
+ # * Michael Granger <ged@FaerieMUD.org>
27
+ #
28
+ # Please see the file LICENSE in the top-level directory for licensing details.
29
+ #
30
+ class Arrow::Cookie < Arrow::Object
31
+
32
+ CookieDateFormat = '%a, %d-%b-%Y %H:%M:%S GMT'
33
+
34
+ ### Strip surrounding double quotes from a copy of the specified string
35
+ ### and return it.
36
+ def self::dequote( string )
37
+ /^"((?:[^"]+|\\.)*)"/.match( string ) ? $1 : string.dup
38
+ end
39
+
40
+
41
+ ### Parse a cookie value string, returning an Array of Strings
42
+ def self::parse_valuestring( valstr )
43
+ return [] unless valstr
44
+ valstr = dequote( valstr )
45
+
46
+ return valstr.split('&').collect{|str| URI.unescape(str) }
47
+ end
48
+
49
+
50
+ ### RFC 2109: HTTP State Management Mechanism
51
+ # When it sends a request to an origin server, the user agent sends a
52
+ # Cookie request header to the origin server if it has cookies that are
53
+ # applicable to the request, based on
54
+ #
55
+ # * the request-host;
56
+ # * the request-URI;
57
+ # * the cookie's age.
58
+ #
59
+ # The syntax for the header is:
60
+ #
61
+ # cookie = "Cookie:" cookie-version
62
+ # 1*((";" | ",") cookie-value)
63
+ # cookie-value = NAME "=" VALUE [";" path] [";" domain]
64
+ # cookie-version = "$Version" "=" value
65
+ # NAME = attr
66
+ # VALUE = value
67
+ # path = "$Path" "=" value
68
+ # domain = "$Domain" "=" value
69
+
70
+ CookieVersion = /\$Version\s*=\s*(.+)\s*[,;]/
71
+ CookiePath = /\$Path/i
72
+ CookieDomain = /\$Domain/i
73
+
74
+ ### RFC2068: Hypertext Transfer Protocol -- HTTP/1.1
75
+ # CTL = <any US-ASCII control character
76
+ # (octets 0 - 31) and DEL (127)>
77
+ # token = 1*<any CHAR except CTLs or tspecials>
78
+ #
79
+ # tspecials = "(" | ")" | "<" | ">" | "@"
80
+ # | "," | ";" | ":" | "\" | <">
81
+ # | "/" | "[" | "]" | "?" | "="
82
+ # | "{" | "}" | SP | HT
83
+ CTLs = "[:cntrl:]"
84
+ TSpecials = Regexp.quote ' "(),/:;<=>?@[\\]{}'
85
+ NonTokenChar = /[#{CTLs}#{TSpecials}]/s
86
+ HTTPToken = /\A[^#{CTLs}#{TSpecials}]+\z/s
87
+
88
+ ### Parse the specified 'Cookie:' +header+ value and return a Hash of
89
+ ### one or more new Arrow::Cookie objects, keyed by name.
90
+ def self::parse( header )
91
+ return {} if header.nil? or header.empty?
92
+ Arrow::Logger[self].debug "Parsing cookie header: %p" % [ header ]
93
+ cookies = []
94
+ version = 0
95
+ header = header.strip
96
+
97
+ # "$Version" = value
98
+ if CookieVersion.match( header )
99
+ Arrow::Logger[self].debug " Found cookie version %p" % [ $1 ]
100
+ version = Integer( dequote($1) )
101
+ header.slice!( CookieVersion )
102
+ end
103
+
104
+ # 1*((";" | ",") NAME "=" VALUE [";" path] [";" domain])
105
+ header.split( /[,;]\s*/ ).each do |pair|
106
+ Arrow::Logger[self].debug " Found pair %p" % [ pair ]
107
+ key, valstr = pair.split( /=/, 2 ).collect {|s| s.strip }
108
+
109
+ case key
110
+ when CookiePath
111
+ Arrow::Logger[self].debug " -> cookie-path %p" % [ valstr ]
112
+ cookies.last.path = dequote( valstr ) unless cookies.empty?
113
+
114
+ when CookieDomain
115
+ Arrow::Logger[self].debug " -> cookie-domain %p" % [ valstr ]
116
+ cookies.last.domain = dequote( valstr ) unless cookies.empty?
117
+
118
+ when HTTPToken
119
+ values = parse_valuestring( valstr )
120
+ Arrow::Logger[self].debug " -> cookie-values %p" % [ values ]
121
+ cookies << new( key, values, :version => version )
122
+
123
+ else
124
+ Arrow::Logger[self].warning \
125
+ "Malformed cookie header %p: %p is not a valid token; ignoring" %
126
+ [ header, key ]
127
+ end
128
+ end
129
+
130
+ # Turn the array into a Hash, ignoring all but the first instance of
131
+ # a cookie with the same name
132
+ return cookies.inject({}) do |hash,cookie|
133
+ hash[cookie.name] = cookie unless hash.key?( cookie.name )
134
+ hash
135
+ end
136
+ end
137
+
138
+
139
+ #################################################################
140
+ ### I N S T A N C E M E T H O D S
141
+ #################################################################
142
+
143
+ ### Create a new Arrow::Cookie object with the specified +name+ and
144
+ ### +values+.
145
+ def initialize( name, values, options={} )
146
+ values = [ values ] unless values.is_a?( Array )
147
+ @name = name
148
+ @values = values
149
+
150
+ @domain = nil
151
+ @path = nil
152
+ @secure = false
153
+ @comment = nil
154
+ @max_age = nil
155
+ @expires = nil
156
+ @version = 0
157
+
158
+ options.each do |meth, val|
159
+ self.__send__( "#{meth}=", val )
160
+ end
161
+ end
162
+
163
+
164
+
165
+ ######
166
+ public
167
+ ######
168
+
169
+ # The name of the cookie
170
+ attr_accessor :name
171
+
172
+ # The Array of cookie values
173
+ attr_accessor :values
174
+
175
+ # The cookie version. 0 (the default) is fine for most uses
176
+ attr_accessor :version
177
+
178
+ # The domain the cookie belongs to
179
+ attr_reader :domain
180
+
181
+ # The path the cookie applies to
182
+ attr_accessor :path
183
+
184
+ # The cookie's 'secure' flag.
185
+ attr_writer :secure
186
+
187
+ # The cookie's expiration (a Time object)
188
+ attr_reader :expires
189
+
190
+ # The lifetime of the cookie, in seconds.
191
+ attr_reader :max_age
192
+
193
+ # Because cookies can contain private information about a
194
+ # user, the Cookie attribute allows an origin server to document its
195
+ # intended use of a cookie. The user can inspect the information to
196
+ # decide whether to initiate or continue a session with this cookie.
197
+ attr_accessor :comment
198
+
199
+
200
+ ### Return the first value stored in the cookie as a String.
201
+ def value
202
+ @values.first
203
+ end
204
+
205
+
206
+ ### Returns +true+ if the secure flag is set
207
+ def secure?
208
+ return @secure ? true : false
209
+ end
210
+
211
+ # Set the lifetime of the cookie. The value is a decimal non-negative
212
+ # integer. After +delta_seconds+ seconds elapse, the client should
213
+ # discard the cookie. A value of zero means the cookie should be
214
+ # discarded immediately.
215
+ def max_age=( delta_seconds )
216
+ @max_age = Integer( delta_seconds )
217
+ end
218
+
219
+
220
+ ### Set the domain for which the cookie is valid.
221
+ def domain=( newdomain )
222
+ newdomain = ".#{newdomain}" unless newdomain[0] == ?.
223
+ @domain = newdomain.dup
224
+ end
225
+
226
+
227
+ ### Set the cookie's expires field. The value can be either a Time object
228
+ ### or a String in any of the following formats:
229
+ ### +30s::
230
+ ### 30 seconds from now
231
+ ### +10m::
232
+ ### ten minutes from now
233
+ ### +1h::
234
+ ### one hour from now
235
+ ### -1d::
236
+ ### yesterday (i.e. "ASAP!")
237
+ ### now::
238
+ ### immediately
239
+ ### +3M::
240
+ ### in three months
241
+ ### +10y::
242
+ ### in ten years time
243
+ ### Thursday, 25-Apr-1999 00:40:33 GMT::
244
+ ### at the indicated time & date
245
+ def expires=( time )
246
+ case time
247
+ when NilClass
248
+ @expires = nil
249
+
250
+ when Date
251
+ @expires = Time.parse( time.ctime )
252
+
253
+ when Time
254
+ @expires = time
255
+
256
+ else
257
+ @expires = parse_time_delta( time )
258
+ end
259
+ rescue => err
260
+ raise err, caller(1)
261
+ end
262
+
263
+
264
+ ### Set the cookie expiration to a time in the past
265
+ def expire!
266
+ self.expires = Time.at(0)
267
+ end
268
+
269
+
270
+
271
+ ### Return the cookie as a String
272
+ def to_s
273
+ rval = "%s=%s" % [ self.name, make_valuestring(self.values) ]
274
+
275
+ rval << make_field( "Version", self.version ) if self.version.nonzero?
276
+ rval << make_field( "Domain", self.domain )
277
+ rval << make_field( "Expires", make_cookiedate(self.expires) ) if self.expires
278
+ rval << make_field( "Max-Age", self.max_age )
279
+ rval << make_field( "Comment", self.comment )
280
+ rval << make_field( "Path", self.path )
281
+
282
+ rval << "; " << "Secure" if self.secure?
283
+
284
+ return rval
285
+ end
286
+
287
+
288
+ ### Return +true+ if other_cookie has the same name as the receiver.
289
+ def eql?( other_cookie )
290
+ return (self.name == other_cookie.name) ? true : false
291
+ end
292
+
293
+
294
+ ### Generate a Fixnum hash value for this object. Uses the hash of the cookie's name.
295
+ def hash
296
+ return self.name.hash
297
+ end
298
+
299
+
300
+
301
+ #######
302
+ private
303
+ #######
304
+
305
+ ### Make a cookie field for appending to the outgoing header for the
306
+ ### specified +value+ and +field_name+. If +value+ is nil, an empty
307
+ ### string will be returned.
308
+ def make_field( field_name, value )
309
+ return '' if value.nil? || (value.is_a?(String) && value.empty?)
310
+
311
+ return "; %s=%s" % [
312
+ field_name.capitalize,
313
+ value
314
+ ]
315
+ end
316
+
317
+
318
+ # Number of seconds in the various offset types
319
+ Seconds = {
320
+ 's' => 1,
321
+ 'm' => 60,
322
+ 'h' => 60*60,
323
+ 'd' => 60*60*24,
324
+ 'M' => 60*60*24*30,
325
+ 'y' => 60*60*24*365,
326
+ }
327
+
328
+ ### Parse a time delta like those accepted by #expires= into a Time
329
+ ### object.
330
+ def parse_time_delta( time )
331
+ return Time.now if time.nil? || time == 'now'
332
+ return Time.at( Integer(time) ) if /^\d+$/.match( time )
333
+
334
+ if /^([+-]?(?:\d+|\d*\.\d*))([mhdMy]?)/.match( time )
335
+ offset = (Seconds[$2] || 1) * Integer($1)
336
+ return Time.now + offset
337
+ end
338
+
339
+ return Time.parse( time )
340
+ end
341
+
342
+ ### Make a uri-escaped value string for the given +values+
343
+ def make_valuestring( values )
344
+ values.collect {|val| URI.escape(val, NonTokenChar) }.join('&')
345
+ end
346
+
347
+
348
+ ### Make an RFC2109-formatted date out of +date+.
349
+ def make_cookiedate( date )
350
+ return date.gmtime.strftime( CookieDateFormat )
351
+ end
352
+
353
+
354
+ ### Quote a copy of the given string and return it.
355
+ def quote( val )
356
+ %q{"%s"} % [ val.to_s.gsub(/"/, '\\"') ]
357
+ end
358
+
359
+ end # class Arrow::Cookie
@@ -0,0 +1,108 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'arrow'
4
+ require 'arrow/cookie'
5
+ require 'set'
6
+ require 'forwardable'
7
+
8
+
9
+ # An object class which provides a convenient way of accessing a set of Arrow::Cookies.
10
+ #
11
+ # == Synopsis
12
+ #
13
+ # cset = Arrow::CookieSet.new()
14
+ # cset = Arrow::CookieSet.new( cookies )
15
+ #
16
+ # cset['cookiename'] # => Arrow::Cookie
17
+ #
18
+ # cset['cookiename'] = cookie_object
19
+ # cset['cookiename'] = 'cookievalue'
20
+ # cset[:cookiename] = 'cookievalue'
21
+ # cset << Arrow::Cookie.new( *args )
22
+ #
23
+ # cset.include?( 'cookiename' )
24
+ # cset.include?( cookie_object )
25
+ #
26
+ # cset.each do |cookie|
27
+ # ...
28
+ # end
29
+ #
30
+ # == Authors
31
+ #
32
+ # * Michael Granger <ged@FaerieMUD.org>
33
+ # * Jeremiah Jordan <phaedrus@FaerieMUD.org>
34
+ #
35
+ # Please see the file LICENSE in the top-level directory for licensing details.
36
+ #
37
+ class Arrow::CookieSet < Arrow::Object
38
+ extend Forwardable
39
+ include Enumerable
40
+
41
+
42
+
43
+ #################################################################
44
+ ### I N S T A N C E M E T H O D S
45
+ #################################################################
46
+
47
+ ### Create a new CookieSet prepopulated with the given cookies
48
+ def initialize( *cookies )
49
+ @cookie_set = Set.new( cookies.flatten )
50
+ end
51
+
52
+
53
+ ######
54
+ public
55
+ ######
56
+
57
+ def_delegators :@cookie_set, :each
58
+
59
+
60
+ ### Returns the number of cookies in the cookieset
61
+ def length
62
+ return @cookie_set.length
63
+ end
64
+ alias_method :size, :length
65
+
66
+
67
+ ### Index operator method: returns the Arrow::Cookie with the given +name+ if it
68
+ ### exists in the cookieset.
69
+ def []( name )
70
+ name = name.to_s
71
+ return @cookie_set.find() {|cookie| cookie.name == name }
72
+ end
73
+
74
+
75
+ ### Index set operator method: set the cookie that corresponds to the given +name+
76
+ ### to +value+. If +value+ is not an Arrow::Cookie, one with be created and its
77
+ ### value set to +value+.
78
+ def []=( name, value )
79
+ value = Arrow::Cookie.new( name.to_s, value ) unless value.is_a?( Arrow::Cookie )
80
+ raise ArgumentError, "cannot set a cookie named '%s' with a key of '%s'" %
81
+ [ value.name, name ] if value.name.to_s != name.to_s
82
+
83
+ self << value
84
+ end
85
+
86
+
87
+ ### Returns +true+ if the CookieSet includes either a cookie with the given name or
88
+ ### an Arrow::Cookie object.
89
+ def include?( name_or_cookie )
90
+ return true if @cookie_set.include?( name_or_cookie )
91
+ name = name_or_cookie.to_s
92
+ return self[name] ? true : false
93
+ end
94
+ alias_method :key?, :include?
95
+
96
+
97
+ ### Append operator: Add the given +cookie+ to the set, replacing an existing
98
+ ### cookie with the same name if one exists.
99
+ def <<( cookie )
100
+ @cookie_set.delete( cookie )
101
+ @cookie_set.add( cookie )
102
+
103
+ return self
104
+ end
105
+
106
+
107
+ end # class Arrow::CookieSet
108
+