http-cookie 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in http-cookie.gemspec
4
+ gemspec
@@ -0,0 +1,24 @@
1
+ Copyright (c) 2013 Akinori MUSHA
2
+ Copyright (c) 2011-2012 Akinori MUSHA, Eric Hodel
3
+ Copyright (c) 2006-2011 Aaron Patterson, Mike Dalessio
4
+
5
+ MIT License
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining
8
+ a copy of this software and associated documentation files (the
9
+ "Software"), to deal in the Software without restriction, including
10
+ without limitation the rights to use, copy, modify, merge, publish,
11
+ distribute, sublicense, and/or sell copies of the Software, and to
12
+ permit persons to whom the Software is furnished to do so, subject to
13
+ the following conditions:
14
+
15
+ The above copyright notice and this permission notice shall be
16
+ included in all copies or substantial portions of the Software.
17
+
18
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,139 @@
1
+ # HTTP::Cookie
2
+
3
+ `HTTP::Cookie` is a ruby library to handle HTTP cookies in a way both
4
+ compliant with RFCs and compatible with today's major browsers.
5
+
6
+ It was originally a part of the
7
+ [Mechanize](https://github.com/sparklemotion/mechanize) library,
8
+ separated as an independent library in the hope of serving as a common
9
+ component that is reusable from any HTTP related piece of software.
10
+
11
+ ## Installation
12
+
13
+ Add this line to your application's `Gemfile`:
14
+
15
+ gem 'http-cookie'
16
+
17
+ And then execute:
18
+
19
+ $ bundle
20
+
21
+ Or install it yourself as:
22
+
23
+ $ gem install http-cookie
24
+
25
+ ## Usage
26
+
27
+ ########################
28
+ # Client side example
29
+ ########################
30
+
31
+ # Initialize a cookie jar
32
+ jar = HTTP::CookieJar.new
33
+
34
+ # Load from a file
35
+ jar.load(filename) if File.exist?(filename)
36
+
37
+ # Store received cookies
38
+ HTTP::Cookie.parse(set_cookie_header_value, origin: uri) { |cookie|
39
+ jar << cookie
40
+ }
41
+
42
+ # Get the value for the Cookie field of a request header
43
+ cookie_header_value = jar.cookies(uri).join(', ')
44
+
45
+ # Save to a file
46
+ jar.save(filename)
47
+
48
+
49
+ ########################
50
+ # Server side example
51
+ ########################
52
+
53
+ # Generate a cookie
54
+ cookies = HTTP::Cookie.new("uid", "a12345", domain: 'example.org',
55
+ for_domain: true,
56
+ path: '/',
57
+ max_age: 7*86400)
58
+
59
+ # Get the value for the Set-Cookie field of a response header
60
+ set_cookie_header_value = cookies.set_cookie_value(my_url)
61
+
62
+
63
+ ## Incompatibilities with `Mechanize::Cookie`/`CookieJar`
64
+
65
+ There are several incompatibilities between
66
+ `Mechanize::Cookie`/`CookieJar` and `HTTP::Cookie`/`CookieJar`. Below
67
+ is how to rewrite existing code written for `Mechanize::Cookie` with
68
+ equivalent using `HTTP::Cookie`:
69
+
70
+ - `Mechanize::Cookie.parse`
71
+
72
+ # before
73
+ cookie1 = Mechanize::Cookie.parse(uri, set_cookie1)
74
+ cookie2 = Mechanize::Cookie.parse(uri, set_cookie2, log)
75
+
76
+ # after
77
+ cookie1 = HTTP::Cookie.parse(set_cookie1, :origin => uri)
78
+ cookie2 = HTTP::Cookie.parse(set_cookie2, :origin => uri, :logger => log)
79
+
80
+ - `Mechanize::Cookie#set_domain`
81
+
82
+ # before
83
+ cookie.set_domain(domain)
84
+
85
+ # after
86
+ cookie.domain = domain
87
+
88
+ - `Mechanize::CookieJar#add`, `#add!`
89
+
90
+ # before
91
+ jar.add!(cookie1)
92
+ jar.add(uri, cookie2)
93
+
94
+ # after
95
+ jar.add(cookie1)
96
+ cookie2.origin = uri; jar.add(cookie2) # or specify origin in parse() or new()
97
+
98
+ - `Mechanize::CookieJar#clear!`
99
+
100
+ # before
101
+ jar.clear!
102
+
103
+ # after
104
+ jar.clear
105
+
106
+ - `Mechanize::CookieJar#save_as`
107
+
108
+ # before
109
+ jar.save_as(file)
110
+
111
+ # after
112
+ jar.save(file)
113
+
114
+ `HTTP::Cookie`/`CookieJar` raise runtime errors to help migration, so
115
+ after replacing the class names, try running your test code once to
116
+ find out how to fix your code base.
117
+
118
+ ### File formats
119
+
120
+ The YAML serialization format has changed, and `HTTP::CookieJar#load`
121
+ cannot import what is written in a YAML file saved by
122
+ `Mechanize::CookieJar#save_as`. `HTTP::CookieJar#load` will not raise
123
+ an exception if an incompatible YAML file is given, but the content is
124
+ silently ignored.
125
+
126
+ Note that there is (obviously) no forward compatibillity with this.
127
+ Trying to load a YAML file saved by `HTTP::CookieJar` with
128
+ `Mechanize::CookieJar` will fail in runtime error.
129
+
130
+ On the other hand, there has been (and will ever be) no change in the
131
+ cookies.txt format, so use it instead if compatibility is required.
132
+
133
+ ## Contributing
134
+
135
+ 1. Fork it
136
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
137
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
138
+ 4. Push to the branch (`git push origin my-new-feature`)
139
+ 5. Create new Pull Request
@@ -0,0 +1,26 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ if RUBY_VERSION >= '1.9.0'
4
+ require 'rake/testtask'
5
+ Rake::TestTask
6
+ else
7
+ require 'rcov/rcovtask'
8
+ Rcov::RcovTask
9
+ end.new(:test) do |test|
10
+ test.libs << 'lib' << 'test'
11
+ test.ruby_opts << '-r./test/simplecov_start.rb' if !defined?(Rcov)
12
+ test.pattern = 'test/**/test_*.rb'
13
+ test.verbose = true
14
+ end
15
+
16
+ task :default => :test
17
+
18
+ require 'rdoc/task'
19
+ Rake::RDocTask.new do |rdoc|
20
+ version = HTTP::Cookie::VERSION
21
+
22
+ rdoc.rdoc_dir = 'rdoc'
23
+ rdoc.title = "http-cookie #{version}"
24
+ rdoc.rdoc_files.include('lib/**/*.rb')
25
+ rdoc.rdoc_files.include(Bundler::GemHelper.gemspec.extra_rdoc_files)
26
+ end
@@ -0,0 +1,29 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'http/cookie/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "http-cookie"
8
+ gem.version = HTTP::Cookie::VERSION
9
+ gem.authors, gem.email = {
10
+ 'Akinori MUSHA' => 'knu@idaemons.org',
11
+ 'Aaron Patterson' => 'aaronp@rubyforge.org',
12
+ 'Eric Hodel' => 'drbrain@segment7.net',
13
+ 'Mike Dalessio' => 'mike.dalessio@gmail.com',
14
+ }.instance_eval { [keys, values] }
15
+
16
+ gem.description = %q{A Ruby library to handle HTTP Cookies}
17
+ gem.summary = %q{A Ruby library to handle HTTP Cookies}
18
+ gem.homepage = "https://github.com/sparklemotion/http-cookie"
19
+
20
+ gem.files = `git ls-files`.split($/)
21
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
22
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
23
+ gem.require_paths = ["lib"]
24
+
25
+ gem.add_runtime_dependency("domain_name", ["~> 0.5"])
26
+ gem.add_development_dependency("bundler", [">= 1.2.0"])
27
+ gem.add_development_dependency("test-unit", [">= 2.4.3"])
28
+ gem.add_development_dependency("simplecov", [">= 0"])
29
+ end
@@ -0,0 +1 @@
1
+ require 'http/cookie'
@@ -0,0 +1,447 @@
1
+ require 'http/cookie/version'
2
+ require 'time'
3
+ require 'webrick/httputils'
4
+ require 'domain_name'
5
+
6
+ module HTTP
7
+ autoload :CookieJar, 'http/cookie_jar'
8
+ end
9
+
10
+ # This class is used to represent an HTTP Cookie.
11
+ class HTTP::Cookie
12
+ # Maximum number of bytes per cookie (RFC 6265 6.1 requires 4096 at least)
13
+ MAX_LENGTH = 4096
14
+ # Maximum number of cookies per domain (RFC 6265 6.1 requires 50 at least)
15
+ MAX_COOKIES_PER_DOMAIN = 50
16
+ # Maximum number of cookies total (RFC 6265 6.1 requires 3000 at least)
17
+ MAX_COOKIES_TOTAL = 3000
18
+
19
+ UNIX_EPOCH = Time.at(0)
20
+
21
+ PERSISTENT_PROPERTIES = %w[
22
+ name value
23
+ domain for_domain path
24
+ secure httponly
25
+ expires created_at accessed_at
26
+ ]
27
+
28
+ # In Ruby < 1.9.3 URI() does not accept an URI object.
29
+ if RUBY_VERSION < "1.9.3"
30
+ module URIFix
31
+ def URI(url)
32
+ url.is_a?(URI) ? url : Kernel::URI(url)
33
+ end
34
+ private :URI
35
+ end
36
+ end
37
+
38
+ if String.respond_to?(:try_convert)
39
+ def check_string_type(object)
40
+ String.try_convert(object)
41
+ end
42
+ private :check_string_type
43
+ else
44
+ def check_string_type(object)
45
+ if object.is_a?(String) ||
46
+ (object.respond_to?(:to_str) && (object = object.to_str).is_a?(String))
47
+ object
48
+ else
49
+ nil
50
+ end
51
+ end
52
+ private :check_string_type
53
+ end
54
+
55
+ include URIFix if defined?(URIFix)
56
+
57
+ attr_reader :name, :domain, :path, :origin
58
+ attr_accessor :secure, :httponly, :value, :version
59
+ attr_reader :domain_name, :expires
60
+ attr_accessor :comment, :max_age
61
+
62
+ attr_accessor :session
63
+
64
+ attr_accessor :created_at
65
+ attr_accessor :accessed_at
66
+
67
+ # :call-seq:
68
+ # new(name, value)
69
+ # new(name, value, attr_hash)
70
+ # new(attr_hash)
71
+ #
72
+ # Creates a cookie object. For each key of +attr_hash+, the setter
73
+ # is called if defined. Each key can be either a symbol or a
74
+ # string, downcased or not.
75
+ #
76
+ # e.g.
77
+ # new("uid", "a12345")
78
+ # new("uid", "a12345", :domain => 'example.org',
79
+ # :for_domain => true, :expired => Time.now + 7*86400)
80
+ # new("name" => "uid", "value" => "a12345", "Domain" => 'www.example.org')
81
+ #
82
+ def initialize(*args)
83
+ @version = 0 # Netscape Cookie
84
+
85
+ @origin = @domain = @path =
86
+ @secure = @httponly =
87
+ @expires = @max_age =
88
+ @comment = nil
89
+
90
+ @created_at = @accessed_at = Time.now
91
+ case args.size
92
+ when 2
93
+ self.name, self.value = *args
94
+ @for_domain = false
95
+ return
96
+ when 3
97
+ self.name, self.value, attr_hash = *args
98
+ when 1
99
+ attr_hash = args.first
100
+ else
101
+ raise ArgumentError, "wrong number of arguments (#{args.size} for 1-3)"
102
+ end
103
+ for_domain = false
104
+ origin = nil
105
+ attr_hash.each_pair { |key, val|
106
+ skey = key.to_s.downcase
107
+ if skey.sub!(/\?\z/, '')
108
+ val = val ? true : false
109
+ end
110
+ case skey
111
+ when 'for_domain'
112
+ for_domain = !!val
113
+ when 'origin'
114
+ origin = val
115
+ else
116
+ setter = :"#{skey}="
117
+ send(setter, val) if respond_to?(setter)
118
+ end
119
+ }
120
+ if @name.nil? || @value.nil?
121
+ raise ArgumentError, "at least name and value must be specified"
122
+ end
123
+ @for_domain = for_domain
124
+ if origin
125
+ self.origin = origin
126
+ end
127
+ end
128
+
129
+ # If this flag is true, this cookie will be sent to any host in the
130
+ # +domain+. If it is false, this cookie will be sent only to the
131
+ # host indicated by the +domain+.
132
+ attr_accessor :for_domain
133
+ alias for_domain? for_domain
134
+
135
+ class << self
136
+ include URIFix if defined?(URIFix)
137
+
138
+ # Normalizes a given path. If it is empty, the root path '/' is
139
+ # returned. If a URI object is given, returns a new URI object
140
+ # with the path part normalized.
141
+ def normalize_path(uri)
142
+ # Currently does not replace // to /
143
+ case uri
144
+ when URI
145
+ uri.path.empty? ? uri + '/' : uri
146
+ else
147
+ uri.empty? ? '/' : uri
148
+ end
149
+ end
150
+
151
+ # Parses a Set-Cookie header value +set_cookie+ into an array of
152
+ # Cookie objects. Parts (separated by commas) that are malformed
153
+ # are ignored.
154
+ #
155
+ # If a block is given, each cookie object is passed to the block.
156
+ #
157
+ # Available option keywords are below:
158
+ #
159
+ # * +origin+
160
+ # The cookie's origin URI/URL
161
+ # * +date+
162
+ # The base date used for interpreting Max-Age attribute values
163
+ # instead of the current time
164
+ # * +logger+
165
+ # Logger object useful for debugging
166
+ def parse(set_cookie, options = nil, *_, &block)
167
+ _.empty? && !options.is_a?(String) or
168
+ raise ArgumentError, 'HTTP::Cookie equivalent for Mechanize::Cookie.parse(uri, set_cookie[, log]) is HTTP::Cookie.parse(set_cookie, :origin => uri[, :logger => log]).'
169
+
170
+ if options
171
+ logger = options[:logger]
172
+ origin = options[:origin] and origin = URI(origin)
173
+ date = options[:date]
174
+ end
175
+ date ||= Time.now
176
+
177
+ [].tap { |cookies|
178
+ set_cookie.split(/,(?=[^;,]*=)|,$/).each { |c|
179
+ if c.bytesize > MAX_LENGTH
180
+ logger.warn("Cookie definition too long: #{c}") if logger
181
+ next
182
+ end
183
+
184
+ cookie_elem = c.split(/;+/)
185
+ first_elem = cookie_elem.shift
186
+ first_elem.strip!
187
+ key, value = first_elem.split(/\=/, 2)
188
+
189
+ begin
190
+ cookie = new(key, value.dup)
191
+ rescue
192
+ logger.warn("Couldn't parse key/value: #{first_elem}") if logger
193
+ next
194
+ end
195
+
196
+ cookie_elem.each do |pair|
197
+ pair.strip!
198
+ key, value = pair.split(/=/, 2) #/)
199
+ next unless key
200
+ value = WEBrick::HTTPUtils.dequote(value.strip) if value
201
+
202
+ case key.downcase
203
+ when 'domain'
204
+ next unless value && !value.empty?
205
+ begin
206
+ cookie.domain = value
207
+ cookie.for_domain = true
208
+ rescue
209
+ logger.warn("Couldn't parse domain: #{value}") if logger
210
+ end
211
+ when 'path'
212
+ next unless value && !value.empty?
213
+ cookie.path = value
214
+ when 'expires'
215
+ next unless value && !value.empty?
216
+ begin
217
+ cookie.expires = Time.parse(value)
218
+ rescue
219
+ logger.warn("Couldn't parse expires: #{value}") if logger
220
+ end
221
+ when 'max-age'
222
+ next unless value && !value.empty?
223
+ begin
224
+ cookie.max_age = Integer(value)
225
+ rescue
226
+ logger.warn("Couldn't parse max age '#{value}'") if logger
227
+ end
228
+ when 'comment'
229
+ next unless value
230
+ cookie.comment = value
231
+ when 'version'
232
+ next unless value
233
+ begin
234
+ cookie.version = Integer(value)
235
+ rescue
236
+ logger.warn("Couldn't parse version '#{value}'") if logger
237
+ cookie.version = nil
238
+ end
239
+ when 'secure'
240
+ cookie.secure = true
241
+ when 'httponly'
242
+ cookie.httponly = true
243
+ end
244
+ end
245
+
246
+ cookie.secure ||= false
247
+ cookie.httponly ||= false
248
+
249
+ # RFC 6265 4.1.2.2
250
+ cookie.expires = date + cookie.max_age if cookie.max_age
251
+ cookie.session = !cookie.expires
252
+
253
+ if origin
254
+ begin
255
+ cookie.origin = origin
256
+ rescue => e
257
+ logger.warn("Invalid cookie for the origin: #{origin} (#{e})") if logger
258
+ next
259
+ end
260
+ end
261
+
262
+ yield cookie if block_given?
263
+
264
+ cookies << cookie
265
+ }
266
+ }
267
+ end
268
+ end
269
+
270
+ def name=(name)
271
+ if name.nil? || name.empty?
272
+ raise ArgumentError, "cookie name cannot be empty"
273
+ elsif name.match(/[\x00-\x1F=\x7F]/)
274
+ raise ArgumentError, "cookie name cannot contain a control character or an equal sign"
275
+ end
276
+ @name = name
277
+ end
278
+
279
+ # Sets the domain attribute. A leading dot in +domain+ implies
280
+ # turning the +for_domain?+ flag on.
281
+ def domain=(domain)
282
+ if DomainName === domain
283
+ @domain_name = domain
284
+ else
285
+ domain = check_string_type(domain) or
286
+ raise TypeError, "#{domain.class} is not a String"
287
+ if domain.start_with?('.')
288
+ @for_domain = true
289
+ domain = domain[1..-1]
290
+ end
291
+ # Do we really need to support this?
292
+ if domain.match(/\A([^:]+):[0-9]+\z/)
293
+ domain = $1
294
+ end
295
+ @domain_name = DomainName.new(domain)
296
+ end
297
+ @domain = @domain_name.hostname
298
+ end
299
+
300
+ # Used to exist in Mechanize::CookieJar. Use #domain=().
301
+ def set_domain(domain)
302
+ raise NoMethodError, 'HTTP::Cookie equivalent for Mechanize::CookieJar#set_domain() is #domain=().'
303
+ end
304
+
305
+ def path=(path)
306
+ @path = HTTP::Cookie.normalize_path(path)
307
+ end
308
+
309
+ def origin=(origin)
310
+ @origin.nil? or
311
+ raise ArgumentError, "origin cannot be changed once it is set"
312
+ origin = URI(origin)
313
+ self.domain ||= origin.host
314
+ self.path ||= (HTTP::Cookie.normalize_path(origin) + './').path
315
+ acceptable_from_uri?(origin) or
316
+ raise ArgumentError, "unacceptable cookie sent from URI #{origin}"
317
+ @origin = origin
318
+ end
319
+
320
+ def expires=(t)
321
+ case t
322
+ when nil, Time
323
+ @expires = t
324
+ else
325
+ @expires = Time.parse(t)
326
+ end
327
+ end
328
+
329
+ def expired?(time = Time.now)
330
+ return false unless @expires
331
+ time > @expires
332
+ end
333
+
334
+ def expire
335
+ @expires = UNIX_EPOCH
336
+ self
337
+ end
338
+
339
+ alias secure? secure
340
+ alias httponly? httponly
341
+ alias session? session
342
+
343
+ def acceptable_from_uri?(uri)
344
+ uri = URI(uri)
345
+ host = DomainName.new(uri.host)
346
+
347
+ # RFC 6265 5.3
348
+ # When the user agent "receives a cookie":
349
+ return @domain.nil? || host.hostname == @domain unless @for_domain
350
+
351
+ if host.cookie_domain?(@domain_name)
352
+ true
353
+ elsif host.hostname == @domain
354
+ @for_domain = false
355
+ true
356
+ else
357
+ false
358
+ end
359
+ end
360
+
361
+ def valid_for_uri?(uri)
362
+ uri = URI(uri)
363
+ if @domain.nil?
364
+ raise "cannot tell if this cookie is valid because the domain is unknown"
365
+ end
366
+ return false if secure? && uri.scheme != 'https'
367
+ acceptable_from_uri?(uri) && HTTP::Cookie.normalize_path(uri.path).start_with?(@path)
368
+ end
369
+
370
+ # Returns a string for use in a Cookie header value,
371
+ # i.e. "name=value".
372
+ def cookie_value
373
+ "#{@name}=#{@value}"
374
+ end
375
+ alias to_s cookie_value
376
+
377
+ # Returns a string for use in a Set-Cookie header value. If the
378
+ # cookie does not have an origin set, one must be given from the
379
+ # argument.
380
+ #
381
+ # This method does not check if this cookie will be accepted from
382
+ # the origin.
383
+ def set_cookie_value(origin = nil)
384
+ origin = origin ? URI(origin) : @origin or
385
+ raise "origin must be specified to produce a value for Set-Cookie"
386
+
387
+ string = cookie_value
388
+ if @for_domain || @domain != DomainName.new(origin.host).hostname
389
+ string << "; domain=#{@domain}"
390
+ end
391
+ if (HTTP::Cookie.normalize_path(origin) + './').path != @path
392
+ string << "; path=#{@path}"
393
+ end
394
+ if expires = @expires
395
+ string << "; expires=#{@expires.httpdate}"
396
+ end
397
+ if comment = @comment
398
+ string << "; comment=#{@comment}"
399
+ end
400
+ if httponly?
401
+ string << "; HttpOnly"
402
+ end
403
+ if secure?
404
+ string << "; secure"
405
+ end
406
+ string
407
+ end
408
+
409
+ # Compares the cookie with another. When there are many cookies with
410
+ # the same name for a URL, the value of the smallest must be used.
411
+ def <=>(other)
412
+ # RFC 6265 5.4
413
+ # Precedence: 1. longer path 2. older creation
414
+ (@name <=> other.name).nonzero? ||
415
+ (other.path.length <=> @path.length).nonzero? ||
416
+ (@created_at <=> other.created_at).nonzero? ||
417
+ @value <=> other.value
418
+ end
419
+ include Comparable
420
+
421
+ # YAML serialization helper for Syck.
422
+ def to_yaml_properties
423
+ PERSISTENT_PROPERTIES.map { |name| "@#{name}" }
424
+ end
425
+
426
+ # YAML serialization helper for Psych.
427
+ def encode_with(coder)
428
+ PERSISTENT_PROPERTIES.each { |key|
429
+ coder[key.to_s] = instance_variable_get(:"@#{key}")
430
+ }
431
+ end
432
+
433
+ # YAML deserialization helper for Syck.
434
+ def init_with(coder)
435
+ yaml_initialize(coder.tag, coder.map)
436
+ end
437
+
438
+ # YAML deserialization helper for Psych.
439
+ def yaml_initialize(tag, map)
440
+ map.each { |key, value|
441
+ case key
442
+ when *PERSISTENT_PROPERTIES
443
+ send(:"#{key}=", value)
444
+ end
445
+ }
446
+ end
447
+ end