http-cookie 0.1.0

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.
@@ -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