fandango 0.2.1 → 1.0.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.
Files changed (46) hide show
  1. data/.irbrc +5 -0
  2. data/README.md +1 -3
  3. data/fandango.gemspec +4 -20
  4. data/lib/fandango.rb +27 -16
  5. data/lib/fandango/movie.rb +30 -0
  6. data/lib/fandango/parser.rb +34 -19
  7. data/lib/fandango/theater.rb +50 -0
  8. data/lib/fandango/version.rb +1 -1
  9. data/spec/fandango.spec.rb +23 -25
  10. data/spec/movie.spec.rb +24 -0
  11. data/spec/parser.spec.rb +12 -0
  12. data/spec/spec_helper.rb +5 -3
  13. data/spec/support/fixtures/item.html +1 -0
  14. data/spec/support/fixtures/movies_near_me_73142.rss +1 -1
  15. data/spec/support/fixtures/movies_near_me_73142.yaml +349 -0
  16. data/spec/support/helpers.rb +25 -0
  17. data/spec/support/vcr_cassettes/movies_near_me_123BADZIP.yml +48 -0
  18. data/spec/support/vcr_cassettes/movies_near_me_73142.yml +247 -0
  19. data/spec/theater.spec.rb +16 -0
  20. metadata +41 -130
  21. data/lib/fandango/parsers/movie.rb +0 -30
  22. data/lib/fandango/parsers/theater.rb +0 -37
  23. data/lib/feedzirra.rb +0 -4
  24. data/lib/vendor/feedzirra/.gitignore +0 -6
  25. data/lib/vendor/feedzirra/.rspec +0 -1
  26. data/lib/vendor/feedzirra/lib/feedzirra.rb +0 -19
  27. data/lib/vendor/feedzirra/lib/feedzirra/core_ext.rb +0 -3
  28. data/lib/vendor/feedzirra/lib/feedzirra/core_ext/date.rb +0 -19
  29. data/lib/vendor/feedzirra/lib/feedzirra/core_ext/string.rb +0 -9
  30. data/lib/vendor/feedzirra/lib/feedzirra/feed.rb +0 -383
  31. data/lib/vendor/feedzirra/lib/feedzirra/feed_entry_utilities.rb +0 -65
  32. data/lib/vendor/feedzirra/lib/feedzirra/feed_utilities.rb +0 -72
  33. data/lib/vendor/feedzirra/lib/feedzirra/parser.rb +0 -17
  34. data/lib/vendor/feedzirra/lib/feedzirra/parser/atom.rb +0 -29
  35. data/lib/vendor/feedzirra/lib/feedzirra/parser/atom_entry.rb +0 -30
  36. data/lib/vendor/feedzirra/lib/feedzirra/parser/atom_feed_burner.rb +0 -21
  37. data/lib/vendor/feedzirra/lib/feedzirra/parser/atom_feed_burner_entry.rb +0 -31
  38. data/lib/vendor/feedzirra/lib/feedzirra/parser/itunes_rss.rb +0 -50
  39. data/lib/vendor/feedzirra/lib/feedzirra/parser/itunes_rss_item.rb +0 -32
  40. data/lib/vendor/feedzirra/lib/feedzirra/parser/itunes_rss_owner.rb +0 -12
  41. data/lib/vendor/feedzirra/lib/feedzirra/parser/rss.rb +0 -22
  42. data/lib/vendor/feedzirra/lib/feedzirra/parser/rss_entry.rb +0 -34
  43. data/lib/vendor/feedzirra/lib/feedzirra/parser/rss_feed_burner.rb +0 -22
  44. data/lib/vendor/feedzirra/lib/feedzirra/parser/rss_feed_burner_entry.rb +0 -40
  45. data/lib/vendor/feedzirra/lib/feedzirra/version.rb +0 -3
  46. data/spec/support/macros.rb +0 -18
@@ -1,30 +0,0 @@
1
- module Fandango
2
- class Parser::Movie
3
-
4
- def initialize(entry)
5
- @entry = entry
6
- end
7
-
8
- # Return array of movie attributes.
9
- def parse
10
- @entry.summary_doc.css('li').map do |li|
11
- {
12
- title: parse_title(li),
13
- id: parse_id(li),
14
- }
15
- end
16
- end
17
-
18
- private
19
-
20
- def parse_title(li)
21
- li.at_css('a').content
22
- end
23
-
24
- # E.g. '141081' in fandango.com/the+adventures+of+tintin+3d_141081/movietimes
25
- def parse_id(li)
26
- li.at_css('a')['href'].match(%r{fandango\.com/.*_(?<id>\d+)/movietimes})[:id]
27
- end
28
-
29
- end
30
- end
@@ -1,37 +0,0 @@
1
- module Fandango
2
- class Parser::Theater
3
-
4
- def initialize(entry)
5
- @entry = entry
6
- end
7
-
8
- # Compose hash with each attribute as key and result of #parse_<attribute> as value.
9
- def parse
10
- atts = [:name, :id, :address, :postal_code]
11
- atts.each_with_object({}) do |attr, hash|
12
- hash[attr] = send :"parse_#{attr}"
13
- end
14
- end
15
-
16
- private
17
-
18
- def parse_name
19
- @entry[:title]
20
- end
21
-
22
- # E.g. 'aaicu' in http://www.fandango.com/northpark7_aaicu/theaterpage
23
- def parse_id
24
- @entry[:url].match(%r{fandango\.com/.*_(?<id>.*)/theaterpage})[:id]
25
- end
26
-
27
- # Cache address in variable for postal_code.
28
- def parse_address
29
- @address = @entry.summary_doc.at_css('p').content
30
- end
31
-
32
- def parse_postal_code
33
- @address.match(/(?<postal_code>\d+)$/)[:postal_code]
34
- end
35
-
36
- end
37
- end
@@ -1,4 +0,0 @@
1
- # See fandango.gemspec.
2
-
3
- $LOAD_PATH << File.dirname(__FILE__) +'/vendor/feedzirra/lib'
4
- require 'vendor/feedzirra/lib/feedzirra'
@@ -1,6 +0,0 @@
1
- .DS_Store
2
- .rvm
3
- TODO
4
- Gemfile.lock
5
- rdoc/
6
- doc/
@@ -1 +0,0 @@
1
- --color
@@ -1,19 +0,0 @@
1
- require 'zlib'
2
- require 'curb'
3
- require 'sax-machine'
4
- require 'loofah'
5
- require 'uri'
6
-
7
- require 'active_support/basic_object'
8
- require 'active_support/core_ext/module'
9
- require 'active_support/core_ext/object'
10
- require 'active_support/time'
11
-
12
- require 'feedzirra/core_ext'
13
-
14
- module Feedzirra
15
- autoload :FeedEntryUtilities, 'feedzirra/feed_entry_utilities'
16
- autoload :FeedUtilities, 'feedzirra/feed_utilities'
17
- autoload :Feed, 'feedzirra/feed'
18
- autoload :Parser, 'feedzirra/parser'
19
- end
@@ -1,3 +0,0 @@
1
- Dir["#{File.dirname(__FILE__)}/core_ext/*.rb"].sort.each do |path|
2
- require "feedzirra/core_ext/#{File.basename(path, '.rb')}"
3
- end
@@ -1,19 +0,0 @@
1
- # Date code pulled and adapted from:
2
- # Ruby Cookbook by Lucas Carlson and Leonard Richardson
3
- # Published by O'Reilly
4
- # ISBN: 0-596-52369-6
5
- class Date
6
- def feed_utils_to_gm_time
7
- feed_utils_to_time(new_offset, :gm)
8
- end
9
-
10
- def feed_utils_to_local_time
11
- feed_utils_to_time(new_offset(DateTime.now.offset-offset), :local)
12
- end
13
-
14
- private
15
- def feed_utils_to_time(dest, method)
16
- Time.send(method, dest.year, dest.month, dest.day, dest.hour, dest.min,
17
- dest.sec, dest.zone)
18
- end
19
- end
@@ -1,9 +0,0 @@
1
- class String
2
- def sanitize!
3
- self.replace(sanitize)
4
- end
5
-
6
- def sanitize
7
- Loofah.scrub_fragment(self, :prune).to_s
8
- end
9
- end
@@ -1,383 +0,0 @@
1
- module Feedzirra
2
- class NoParserAvailable < StandardError; end
3
-
4
- class Feed
5
- USER_AGENT = "feedzirra http://github.com/pauldix/feedzirra/tree/master"
6
-
7
- # Takes a raw XML feed and attempts to parse it. If no parser is available a Feedzirra::NoParserAvailable exception is raised.
8
- # You can pass a block to be called when there's an error during the parsing.
9
- # === Parameters
10
- # [xml<String>] The XML that you would like parsed.
11
- # === Returns
12
- # An instance of the determined feed type. By default a Feedzirra::Atom, Feedzirra::AtomFeedBurner, Feedzirra::RDF, or Feedzirra::RSS object.
13
- # === Raises
14
- # Feedzirra::NoParserAvailable : If no valid parser classes could be found for the feed.
15
- def self.parse(xml, &block)
16
- if parser = determine_feed_parser_for_xml(xml)
17
- parser.parse(xml, block)
18
- else
19
- raise NoParserAvailable.new("No valid parser for XML.")
20
- end
21
- end
22
-
23
- # Determines the correct parser class to use for parsing the feed.
24
- #
25
- # === Parameters
26
- # [xml<String>] The XML that you would like determine the parser for.
27
- # === Returns
28
- # The class name of the parser that can handle the XML.
29
- def self.determine_feed_parser_for_xml(xml)
30
- start_of_doc = xml.slice(0, 2000)
31
- feed_classes.detect {|klass| klass.able_to_parse?(start_of_doc)}
32
- end
33
-
34
- # Adds a new feed parsing class that will be used for parsing.
35
- #
36
- # === Parameters
37
- # [klass<Constant>] The class/constant that you want to register.
38
- # === Returns
39
- # A updated array of feed parser class names.
40
- def self.add_feed_class(klass)
41
- feed_classes.unshift klass
42
- end
43
-
44
- # Provides a list of registered feed parsing classes.
45
- #
46
- # === Returns
47
- # A array of class names.
48
- def self.feed_classes
49
- @feed_classes ||= [Feedzirra::Parser::RSSFeedBurner, Feedzirra::Parser::RSS, Feedzirra::Parser::AtomFeedBurner, Feedzirra::Parser::Atom]
50
- end
51
-
52
- # Makes all registered feeds types look for the passed in element to parse.
53
- # This is actually just a call to element (a SAXMachine call) in the class.
54
- #
55
- # === Parameters
56
- # [element_tag<String>] The element tag
57
- # [options<Hash>] Valid keys are same as with SAXMachine
58
- def self.add_common_feed_element(element_tag, options = {})
59
- feed_classes.each do |k|
60
- k.element element_tag, options
61
- end
62
- end
63
-
64
- # Makes all registered feeds types look for the passed in elements to parse.
65
- # This is actually just a call to elements (a SAXMachine call) in the class.
66
- #
67
- # === Parameters
68
- # [element_tag<String>] The element tag
69
- # [options<Hash>] Valid keys are same as with SAXMachine
70
- def self.add_common_feed_elements(element_tag, options = {})
71
- feed_classes.each do |k|
72
- k.elements element_tag, options
73
- end
74
- end
75
-
76
- # Makes all registered entry types look for the passed in element to parse.
77
- # This is actually just a call to element (a SAXMachine call) in the class.
78
- #
79
- # === Parameters
80
- # [element_tag<String>]
81
- # [options<Hash>] Valid keys are same as with SAXMachine
82
- def self.add_common_feed_entry_element(element_tag, options = {})
83
- call_on_each_feed_entry :element, element_tag, options
84
- end
85
-
86
- # Makes all registered entry types look for the passed in elements to parse.
87
- # This is actually just a call to element (a SAXMachine call) in the class.
88
- #
89
- # === Parameters
90
- # [element_tag<String>]
91
- # [options<Hash>] Valid keys are same as with SAXMachine
92
- def self.add_common_feed_entry_elements(element_tag, options = {})
93
- call_on_each_feed_entry :elements, element_tag, options
94
- end
95
-
96
- # Call a method on all feed entries classes.
97
- #
98
- # === Parameters
99
- # [method<Symbol>] The method name
100
- # [parameters<Array>] The method parameters
101
- def self.call_on_each_feed_entry(method, *parameters)
102
- feed_classes.each do |k|
103
- # iterate on the collections defined in the sax collection
104
- k.sax_config.collection_elements.each_value do |vl|
105
- # vl is a list of CollectionConfig mapped to an attribute name
106
- # we'll look for the one set as 'entries' and add the new element
107
- vl.find_all{|v| (v.accessor == 'entries') && (v.data_class.class == Class)}.each do |v|
108
- v.data_class.send(method, *parameters)
109
- end
110
- end
111
- end
112
- end
113
-
114
- # Setup curl from options.
115
- # Possible parameters:
116
- # * :user_agent - overrides the default user agent.
117
- # * :compress - any value to enable compression
118
- # * :http_authentication - array containing http authentication parameters
119
- # * :proxy_url - proxy url
120
- # * :proxy_port - proxy port
121
- # * :max_redirects - max number of redirections
122
- # * :timeout - timeout
123
- def self.setup_easy curl, options
124
- curl.headers["Accept-encoding"] = 'gzip, deflate' if options.has_key?(:compress)
125
- curl.headers["User-Agent"] = (options[:user_agent] || USER_AGENT)
126
-
127
- curl.userpwd = options[:http_authentication].join(':') if options.has_key?(:http_authentication)
128
- curl.proxy_url = options[:proxy_url] if options.has_key?(:proxy_url)
129
- curl.proxy_port = options[:proxy_port] if options.has_key?(:proxy_port)
130
- curl.max_redirects = options[:max_redirects] if options[:max_redirects]
131
- curl.timeout = options[:timeout] if options[:timeout]
132
-
133
- curl.follow_location = true
134
- end
135
-
136
- # Fetches and returns the raw XML for each URL provided.
137
- #
138
- # === Parameters
139
- # [urls<String> or <Array>] A single feed URL, or an array of feed URLs.
140
- # [options<Hash>] Valid keys for this argument as as followed:
141
- # :if_modified_since - Time object representing when the feed was last updated.
142
- # :if_none_match - String that's normally an etag for the request that was stored previously.
143
- # :on_success - Block that gets executed after a successful request.
144
- # :on_failure - Block that gets executed after a failed request.
145
- # * all parameters defined in setup_easy
146
- # === Returns
147
- # A String of XML if a single URL is passed.
148
- #
149
- # A Hash if multiple URL's are passed. The key will be the URL, and the value the XML.
150
- def self.fetch_raw(urls, options = {})
151
- url_queue = [*urls]
152
- multi = Curl::Multi.new
153
- responses = {}
154
- url_queue.each do |url|
155
- easy = Curl::Easy.new(url) do |curl|
156
- setup_easy curl, options
157
-
158
- curl.headers["If-Modified-Since"] = options[:if_modified_since].httpdate if options.has_key?(:if_modified_since)
159
- curl.headers["If-None-Match"] = options[:if_none_match] if options.has_key?(:if_none_match)
160
-
161
- curl.on_success do |c|
162
- responses[url] = decode_content(c)
163
- end
164
- curl.on_failure do |c, err|
165
- responses[url] = c.response_code
166
- end
167
- end
168
- multi.add(easy)
169
- end
170
-
171
- multi.perform
172
- urls.is_a?(String) ? responses.values.first : responses
173
- end
174
-
175
- # Fetches and returns the parsed XML for each URL provided.
176
- #
177
- # === Parameters
178
- # [urls<String> or <Array>] A single feed URL, or an array of feed URLs.
179
- # [options<Hash>] Valid keys for this argument as as followed:
180
- # * :user_agent - String that overrides the default user agent.
181
- # * :if_modified_since - Time object representing when the feed was last updated.
182
- # * :if_none_match - String, an etag for the request that was stored previously.
183
- # * :on_success - Block that gets executed after a successful request.
184
- # * :on_failure - Block that gets executed after a failed request.
185
- # === Returns
186
- # A Feed object if a single URL is passed.
187
- #
188
- # A Hash if multiple URL's are passed. The key will be the URL, and the value the Feed object.
189
- def self.fetch_and_parse(urls, options = {})
190
- url_queue = [*urls]
191
- multi = Curl::Multi.new
192
- responses = {}
193
-
194
- # I broke these down so I would only try to do 30 simultaneously because
195
- # I was getting weird errors when doing a lot. As one finishes it pops another off the queue.
196
- url_queue.slice!(0, 30).each do |url|
197
- add_url_to_multi(multi, url, url_queue, responses, options)
198
- end
199
-
200
- multi.perform
201
- return urls.is_a?(String) ? responses.values.first : responses
202
- end
203
-
204
- # Decodes the XML document if it was compressed.
205
- #
206
- # === Parameters
207
- # [curl_request<Curl::Easy>] The Curl::Easy response object from the request.
208
- # === Returns
209
- # A decoded string of XML.
210
- def self.decode_content(c)
211
- if c.header_str.match(/Content-Encoding: gzip/i)
212
- begin
213
- gz = Zlib::GzipReader.new(StringIO.new(c.body_str))
214
- xml = gz.read
215
- gz.close
216
- rescue Zlib::GzipFile::Error
217
- # Maybe this is not gzipped?
218
- xml = c.body_str
219
- end
220
- elsif c.header_str.match(/Content-Encoding: deflate/i)
221
- xml = Zlib::Inflate.inflate(c.body_str)
222
- else
223
- xml = c.body_str
224
- end
225
-
226
- xml
227
- end
228
-
229
- # Updates each feed for each Feed object provided.
230
- #
231
- # === Parameters
232
- # [feeds<Feed> or <Array>] A single feed object, or an array of feed objects.
233
- # [options<Hash>] Valid keys for this argument as as followed:
234
- # * :on_success - Block that gets executed after a successful request.
235
- # * :on_failure - Block that gets executed after a failed request.
236
- # * all parameters defined in setup_easy
237
- # === Returns
238
- # A updated Feed object if a single URL is passed.
239
- #
240
- # A Hash if multiple Feeds are passed. The key will be the URL, and the value the updated Feed object.
241
- def self.update(feeds, options = {})
242
- feed_queue = [*feeds]
243
- multi = Curl::Multi.new
244
- responses = {}
245
-
246
- feed_queue.slice!(0, 30).each do |feed|
247
- add_feed_to_multi(multi, feed, feed_queue, responses, options)
248
- end
249
-
250
- multi.perform
251
- responses.is_a?(Array)? responses.values : responses.values.first
252
- end
253
-
254
- # An abstraction for adding a feed by URL to the passed Curb::multi stack.
255
- #
256
- # === Parameters
257
- # [multi<Curl::Multi>] The Curl::Multi object that the request should be added too.
258
- # [url<String>] The URL of the feed that you would like to be fetched.
259
- # [url_queue<Array>] An array of URLs that are queued for request.
260
- # [responses<Hash>] Existing responses that you want the response from the request added to.
261
- # [feeds<String> or <Array>] A single feed object, or an array of feed objects.
262
- # [options<Hash>] Valid keys for this argument as as followed:
263
- # * :on_success - Block that gets executed after a successful request.
264
- # * :on_failure - Block that gets executed after a failed request.
265
- # * all parameters defined in setup_easy
266
- # === Returns
267
- # The updated Curl::Multi object with the request details added to it's stack.
268
- def self.add_url_to_multi(multi, url, url_queue, responses, options)
269
- easy = Curl::Easy.new(url) do |curl|
270
- setup_easy curl, options
271
- curl.headers["If-Modified-Since"] = options[:if_modified_since].httpdate if options.has_key?(:if_modified_since)
272
- curl.headers["If-None-Match"] = options[:if_none_match] if options.has_key?(:if_none_match)
273
-
274
- curl.on_success do |c|
275
- add_url_to_multi(multi, url_queue.shift, url_queue, responses, options) unless url_queue.empty?
276
- xml = decode_content(c)
277
- klass = determine_feed_parser_for_xml(xml)
278
-
279
- if klass
280
- begin
281
- feed = klass.parse(xml, Proc.new{|message| puts "Error while parsing [#{url}] #{message}" })
282
- feed.feed_url = c.last_effective_url
283
- feed.etag = etag_from_header(c.header_str)
284
- feed.last_modified = last_modified_from_header(c.header_str)
285
- responses[url] = feed
286
- options[:on_success].call(url, feed) if options.has_key?(:on_success)
287
- rescue Exception => e
288
- options[:on_failure].call(url, c.response_code, c.header_str, c.body_str) if options.has_key?(:on_failure)
289
- end
290
- else
291
- # puts "Error determining parser for #{url} - #{c.last_effective_url}"
292
- # raise NoParserAvailable.new("no valid parser for content.") (this would unfortunately fail the whole 'multi', so it's not really usable)
293
- options[:on_failure].call(url, c.response_code, c.header_str, c.body_str) if options.has_key?(:on_failure)
294
- end
295
- end
296
-
297
- curl.on_failure do |c, err|
298
- add_url_to_multi(multi, url_queue.shift, url_queue, responses, options) unless url_queue.empty?
299
- responses[url] = c.response_code
300
- if c.response_code == 304 # it's not modified. this isn't an error condition
301
- options[:on_success].call(url, nil) if options.has_key?(:on_success)
302
- else
303
- options[:on_failure].call(url, c.response_code, c.header_str, c.body_str) if options.has_key?(:on_failure)
304
- end
305
- end
306
- end
307
- multi.add(easy)
308
- end
309
-
310
- # An abstraction for adding a feed by a Feed object to the passed Curb::multi stack.
311
- #
312
- # === Parameters
313
- # [multi<Curl::Multi>] The Curl::Multi object that the request should be added too.
314
- # [feed<Feed>] A feed object that you would like to be fetched.
315
- # [url_queue<Array>] An array of feed objects that are queued for request.
316
- # [responses<Hash>] Existing responses that you want the response from the request added to.
317
- # [feeds<String>] or <Array> A single feed object, or an array of feed objects.
318
- # [options<Hash>] Valid keys for this argument as as followed:
319
- # * :on_success - Block that gets executed after a successful request.
320
- # * :on_failure - Block that gets executed after a failed request.
321
- # * all parameters defined in setup_easy
322
- # === Returns
323
- # The updated Curl::Multi object with the request details added to it's stack.
324
- def self.add_feed_to_multi(multi, feed, feed_queue, responses, options)
325
- easy = Curl::Easy.new(feed.feed_url) do |curl|
326
- setup_easy curl, options
327
- curl.headers["If-Modified-Since"] = feed.last_modified.httpdate if feed.last_modified
328
- curl.headers["If-Modified-Since"] = options[:if_modified_since] if options[:if_modified_since] && (!feed.last_modified || (Time.parse(options[:if_modified_since].to_s) > feed.last_modified))
329
- curl.headers["If-None-Match"] = feed.etag if feed.etag
330
-
331
- curl.on_success do |c|
332
- begin
333
- add_feed_to_multi(multi, feed_queue.shift, feed_queue, responses, options) unless feed_queue.empty?
334
- updated_feed = Feed.parse(c.body_str){ |message| puts "Error while parsing [#{feed.feed_url}] #{message}" }
335
- updated_feed.feed_url = c.last_effective_url
336
- updated_feed.etag = etag_from_header(c.header_str)
337
- updated_feed.last_modified = last_modified_from_header(c.header_str)
338
- feed.update_from_feed(updated_feed)
339
- responses[feed.feed_url] = feed
340
- options[:on_success].call(feed) if options.has_key?(:on_success)
341
- rescue Exception => e
342
- options[:on_failure].call(feed, c.response_code, c.header_str, c.body_str) if options.has_key?(:on_failure)
343
- end
344
- end
345
-
346
- curl.on_failure do |c, err|
347
- add_feed_to_multi(multi, feed_queue.shift, feed_queue, responses, options) unless feed_queue.empty?
348
- response_code = c.response_code
349
- if response_code == 304 # it's not modified. this isn't an error condition
350
- responses[feed.feed_url] = feed
351
- options[:on_success].call(feed) if options.has_key?(:on_success)
352
- else
353
- responses[feed.url] = c.response_code
354
- options[:on_failure].call(feed, c.response_code, c.header_str, c.body_str) if options.has_key?(:on_failure)
355
- end
356
- end
357
- end
358
- multi.add(easy)
359
- end
360
-
361
- # Determines the etag from the request headers.
362
- #
363
- # === Parameters
364
- # [header<String>] Raw request header returned from the request
365
- # === Returns
366
- # A string of the etag or nil if it cannot be found in the headers.
367
- def self.etag_from_header(header)
368
- header =~ /.*ETag:\s(.*)\r/
369
- $1
370
- end
371
-
372
- # Determines the last modified date from the request headers.
373
- #
374
- # === Parameters
375
- # [header<String>] Raw request header returned from the request
376
- # === Returns
377
- # A Time object of the last modified date or nil if it cannot be found in the headers.
378
- def self.last_modified_from_header(header)
379
- header =~ /.*Last-Modified:\s(.*)\r/
380
- Time.parse($1) if $1
381
- end
382
- end
383
- end