arrow 1.0.7

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