pezra-resourceful 0.6.0 → 0.7.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.
@@ -1,3 +1,19 @@
1
+ Version 0.7.0
2
+ =============
3
+ * Multiple values in a single header field are treated the same as repeated header fields. (Peter Williams)
4
+
5
+ Compatibility issues
6
+ --------------------
7
+
8
+ * The semantics of the Resourceful::Header API have changed slightly.
9
+ Previously multiple values in a single header field
10
+ (e.g. `Accept: application/xml, application/json`) where treated
11
+ differently from multiple values occurring in multiple fields (e.g
12
+ `Accept: application/xml\n\rAccept: application/json`). This lead
13
+ to some unfortunate complexity when interacting with multi-valued
14
+ fields.
15
+
16
+
1
17
  Version 0.6.0
2
18
  =============
3
19
 
data/Manifest CHANGED
@@ -1,39 +1,40 @@
1
- lib/resourceful.rb
2
- lib/resourceful/net_http_adapter.rb
3
- lib/resourceful/options_interpretation.rb
4
- lib/resourceful/stubbed_resource_proxy.rb
5
- lib/resourceful/urlencoded_form_data.rb
6
- lib/resourceful/header.rb
7
- lib/resourceful/memcache_cache_manager.rb
8
- lib/resourceful/response.rb
9
- lib/resourceful/util.rb
1
+ History.txt
10
2
  lib/resourceful/abstract_form_data.rb
3
+ lib/resourceful/authentication_manager.rb
11
4
  lib/resourceful/cache_manager.rb
12
- lib/resourceful/request.rb
13
- lib/resourceful/resource.rb
14
5
  lib/resourceful/exceptions.rb
15
- lib/resourceful/multipart_form_data.rb
6
+ lib/resourceful/header.rb
16
7
  lib/resourceful/http_accessor.rb
17
- lib/resourceful/authentication_manager.rb
18
- History.txt
19
- resourceful.gemspec
20
- README.markdown
8
+ lib/resourceful/memcache_cache_manager.rb
9
+ lib/resourceful/multipart_form_data.rb
10
+ lib/resourceful/net_http_adapter.rb
11
+ lib/resourceful/request.rb
12
+ lib/resourceful/resource.rb
13
+ lib/resourceful/response.rb
14
+ lib/resourceful/stubbed_resource_proxy.rb
15
+ lib/resourceful/urlencoded_form_data.rb
16
+ lib/resourceful/util.rb
17
+ lib/resourceful.rb
18
+ Manifest
21
19
  MIT-LICENSE
22
20
  Rakefile
23
- Manifest
24
- spec/simple_sinatra_server_spec.rb
25
- spec/old_acceptance_specs.rb
26
- spec/acceptance_shared_specs.rb
27
- spec/spec_helper.rb
28
- spec/simple_sinatra_server.rb
21
+ README.markdown
22
+ resourceful.gemspec
29
23
  spec/acceptance/authorization_spec.rb
30
- spec/acceptance/header_spec.rb
31
- spec/acceptance/resource_spec.rb
32
24
  spec/acceptance/caching_spec.rb
25
+ spec/acceptance/header_spec.rb
33
26
  spec/acceptance/redirecting_spec.rb
34
- spec/resourceful/multipart_form_data_spec.rb
27
+ spec/acceptance/resource_spec.rb
28
+ spec/acceptance_shared_specs.rb
29
+ spec/caching_spec.rb
30
+ spec/old_acceptance_specs.rb
35
31
  spec/resourceful/header_spec.rb
32
+ spec/resourceful/http_accessor_spec.rb
33
+ spec/resourceful/multipart_form_data_spec.rb
36
34
  spec/resourceful/resource_spec.rb
35
+ spec/resourceful/response_spec.rb
37
36
  spec/resourceful/urlencoded_form_data_spec.rb
38
- spec/caching_spec.rb
37
+ spec/simple_sinatra_server.rb
38
+ spec/simple_sinatra_server_spec.rb
39
39
  spec/spec.opts
40
+ spec/spec_helper.rb
data/Rakefile CHANGED
@@ -12,7 +12,7 @@ begin
12
12
  p.email = "psadauskas@gmail.com"
13
13
 
14
14
  p.ignore_pattern = ["pkg/*", "tmp/*"]
15
- p.dependencies = [['addressable', '>= 2.1.0'], 'httpauth']
15
+ p.dependencies = [['addressable', '>= 2.1.0'], 'httpauth', ['options', '>= 2.2.0']]
16
16
  p.development_dependencies = ['thin', 'yard', 'sinatra', 'rspec']
17
17
  p.retain_gemspec = true
18
18
  end
@@ -13,10 +13,11 @@ require 'resourceful/http_accessor'
13
13
  module Resourceful
14
14
  autoload :MultipartFormData, 'resourceful/multipart_form_data'
15
15
  autoload :UrlencodedFormData, 'resourceful/urlencoded_form_data'
16
+ autoload :StubbedResourceProxy, 'resourceful/stubbed_resource_proxy'
16
17
  end
17
18
 
18
19
  # Resourceful is a library that provides a high level HTTP interface.
19
20
  module Resourceful
20
- VERSION = "0.6.0"
21
+ VERSION = "0.7.0"
21
22
  RESOURCEFUL_USER_AGENT_TOKEN = "Resourceful/#{VERSION}(Ruby/#{RUBY_VERSION})"
22
23
  end
@@ -1,5 +1,6 @@
1
- require 'resourceful/options_interpretation'
1
+ require 'options'
2
2
  require 'set'
3
+ require 'facets/memoize'
3
4
 
4
5
  # Represents the header fields of an HTTP message. To access a field
5
6
  # you can use `#[]` and `#[]=`. For example, to get the content type
@@ -53,11 +54,25 @@ module Resourceful
53
54
  def has_key?(k)
54
55
  field_def(k).exists_in?(@raw_fields)
55
56
  end
57
+ alias has_field? has_key?
56
58
 
57
59
  def each(&blk)
58
60
  @raw_fields.each(&blk)
59
61
  end
60
- alias each_field each
62
+
63
+ # Iterates through the fields with values provided as message
64
+ # ready strings.
65
+ def each_field(&blk)
66
+ each do |k,v|
67
+ str_v = if field_def(k).multivalued?
68
+ v.join(', ')
69
+ else
70
+ v.to_s
71
+ end
72
+
73
+ yield k, str_v
74
+ end
75
+ end
61
76
 
62
77
  def merge!(another)
63
78
  another.each do |k,v|
@@ -78,43 +93,61 @@ module Resourceful
78
93
  self.class.new(@raw_fields.dup)
79
94
  end
80
95
 
81
-
82
96
  # Class to handle the details of each type of field.
83
- class HeaderFieldDef
97
+ class FieldDesc
84
98
  include Comparable
85
- include OptionsInterpretation
86
-
99
+
87
100
  ##
88
101
  attr_reader :name
89
-
102
+
103
+ # Create a new header field descriptor.
104
+ #
105
+ # @param [String] name The canonical name of this field.
106
+ #
107
+ # @param [Hash] options hash containing extra information about
108
+ # this header fields. Valid keys are:
109
+ #
110
+ # `:multivalued`
111
+ # `:multivalue`
112
+ # `:repeatable`
113
+ # : Values of this field are comma separated list of values.
114
+ # (n#VALUE per HTTP spec.) Default: false
115
+ #
116
+ # `:hop_by_hop`
117
+ # : True if the header is a hop-by-hop header. Default: false
118
+ #
119
+ # `:modifiable`
120
+ # : False if the header should not be modified by intermediates or caches. Default: true
121
+ #
90
122
  def initialize(name, options = {})
91
123
  @name = name
92
- extract_opts(options) do |opts|
93
- @repeatable = opts.extract(:repeatable, :default => false)
94
- @hop_by_hop = opts.extract(:hop_by_hop, :default => false)
95
- @modifiable = opts.extract(:modifiable, :default => true)
96
- end
124
+ options = Options.for(options).validate(:repeatable, :hop_by_hop, :modifiable)
125
+
126
+ @repeatable = options.getopt([:repeatable, :multivalue, :multivalued]) || false
127
+ @hop_by_hop = options.getopt(:hop_by_hop) || false
128
+ @modifiable = options.getopt(:modifiable, true)
97
129
  end
98
-
130
+
99
131
  def repeatable?
100
132
  @repeatable
101
133
  end
102
-
134
+ alias multivalued? repeatable?
135
+
103
136
  def hop_by_hop?
104
137
  @hop_by_hop
105
138
  end
106
-
139
+
107
140
  def modifiable?
108
141
  @modifiable
109
142
  end
110
-
143
+
111
144
  def get_from(raw_fields_hash)
112
145
  raw_fields_hash[name]
113
146
  end
114
-
147
+
115
148
  def set_to(value, raw_fields_hash)
116
- raw_fields_hash[name] = if repeatable?
117
- Array(value)
149
+ raw_fields_hash[name] = if multivalued?
150
+ Array(value).map{|v| v.split(/,\s*/)}.flatten
118
151
  elsif value.kind_of?(Array)
119
152
  raise ArgumentError, "#{name} field may only have one value" if value.size > 1
120
153
  value.first
@@ -122,86 +155,140 @@ module Resourceful
122
155
  value
123
156
  end
124
157
  end
125
-
158
+
126
159
  def exists_in?(raw_fields_hash)
127
160
  raw_fields_hash.has_key?(name)
128
161
  end
129
-
162
+
130
163
  def <=>(another)
131
164
  name <=> another.name
132
165
  end
133
-
166
+
134
167
  def ==(another)
135
- name_pattern === another.name
168
+ name_pattern === another.to_s
136
169
  end
137
170
  alias eql? ==
138
-
171
+
139
172
  def ===(another)
140
- if another.kind_of?(HeaderFieldDef)
173
+ if another.kind_of?(FieldDesc)
141
174
  self == another
142
175
  else
143
176
  name_pattern === another
144
177
  end
145
178
  end
146
-
179
+
147
180
  def name_pattern
148
- Regexp.new('^' + name.gsub('-', '[_-]') + '$', Regexp::IGNORECASE)
181
+ @name_pattern ||= Regexp.new('^' + name.gsub('-', '[_-]') + '$', Regexp::IGNORECASE)
149
182
  end
150
-
183
+
151
184
  def methodized_name
152
- name.downcase.gsub('-', '_')
185
+ @methodized_name ||= name.downcase.gsub('-', '_')
153
186
  end
154
-
187
+
188
+ def constantized_name
189
+ @constantized_name ||= name.upcase.gsub('-', '_')
190
+ end
191
+
155
192
  alias to_s name
156
-
157
- def gen_setter(klass)
158
- klass.class_eval <<-RUBY
159
- def #{methodized_name}=(val) # def accept=(val)
160
- self['#{name}'] = val # self['Accept'] = val
161
- end # end
162
- RUBY
193
+
194
+ def accessor_module
195
+ @accessor_module ||= begin
196
+ Module.new.tap{|m| m.module_eval(<<-RUBY)}
197
+ #{constantized_name} = '#{name}'
198
+
199
+ def #{methodized_name} # def accept
200
+ self[#{constantized_name}] # self[ACCEPT]
201
+ end # end
202
+
203
+ def #{methodized_name}=(val) # def accept=(val)
204
+ self[#{constantized_name}] = val # self[ACCEPT] = val
205
+ end # end
206
+ RUBY
207
+ end
163
208
  end
164
209
 
165
- def gen_getter(klass)
166
- klass.class_eval <<-RUBY
167
- def #{methodized_name} # def accept
168
- self['#{name}'] # self['Accept']
169
- end # end
170
- RUBY
210
+ def hash
211
+ @name.hash
171
212
  end
172
-
173
- def gen_canonical_name_const(klass)
174
- const_name = name.upcase.gsub('-', '_')
175
213
 
176
- klass.const_set(const_name, name)
214
+ # Yields each commonly used lookup key for this header field.
215
+ def lookup_keys(&blk)
216
+ yield name
217
+ yield name.upcase
218
+ yield name.downcase
219
+ yield methodized_name
220
+ yield methodized_name.to_sym
221
+ yield constantized_name
222
+ yield constantized_name.to_sym
177
223
  end
178
- end
179
-
180
- @@header_field_defs = Set.new
181
-
224
+ end # FieldDesc
225
+
226
+ @@known_fields = Set.new
227
+ @@known_fields_lookup = Hash.new
228
+
229
+ # Declares a common header field. Header fields do not have to be
230
+ # defined this way but accessing them is easier, safer and faster
231
+ # if you do. Declaring a field does the following things:
232
+ #
233
+ # * defines accessor methods (e.g. `#content_type` and
234
+ # `#content_type=`) on `Header`
235
+ #
236
+ # * defines constant that can be used to reference there field
237
+ # name (e.g. `some_header[Header::CONTENT_TYPE]`)
238
+ #
239
+ # * includes the field in the appropriate *_fields groups (e.g. `Header.non_modifiable_fields`)
240
+ #
241
+ # * provides improved multiple value parsing
242
+ #
243
+ # Create a new header field descriptor.
244
+ #
245
+ # @param [String] name The canonical name of this field.
246
+ #
247
+ # @param [Hash] options hash containing extra information about
248
+ # this header fields. Valid keys are:
249
+ #
250
+ # `:multivalued`
251
+ # `:multivalue`
252
+ # `:repeatable`
253
+ # : Values of this field are comma separated list of values.
254
+ # (n#VALUE per HTTP spec.) Default: false
255
+ #
256
+ # `:hop_by_hop`
257
+ # : True if the header is a hop-by-hop header. Default: false
258
+ #
259
+ # `:modifiable`
260
+ # : False if the header should not be modified by intermediates or caches. Default: true
261
+ #
182
262
  def self.header_field(name, options = {})
183
- hfd = HeaderFieldDef.new(name, options)
184
-
185
- @@header_field_defs << hfd
186
-
187
- hfd.gen_getter(self)
188
- hfd.gen_setter(self)
189
- hfd.gen_canonical_name_const(self)
190
- end
263
+ hfd = FieldDesc.new(name, options)
264
+
265
+ @@known_fields << hfd
266
+ hfd.lookup_keys do |a_key|
267
+ @@known_fields_lookup[a_key] = hfd
268
+ end
191
269
 
192
- def self.hop_by_hop_headers
193
- @@header_field_defs.select{|hfd| hfd.hop_by_hop?}
270
+ include(hfd.accessor_module)
194
271
  end
195
-
196
- def self.non_modifiable_headers
197
- @@header_field_defs.reject{|hfd| hfd.repeatable?}
272
+
273
+ def self.hop_by_hop_fields
274
+ @@known_fields.select{|hfd| hfd.hop_by_hop?}
198
275
  end
199
276
 
200
- def field_def(name)
201
- @@header_field_defs.find{|hfd| hfd === name} ||
202
- HeaderFieldDef.new(name.to_s.downcase.gsub(/^.|[-_\s]./) { |x| x.upcase }.gsub('_', '-'), :repeatable => true)
277
+ def self.non_modifiable_fields
278
+ @@known_fields.reject{|hfd| hfd.modifiable?}
203
279
  end
204
280
 
281
+ protected
282
+
283
+ # ---
284
+ #
285
+ # We have to fall back on a slow iteration to find the header
286
+ # field some times because field names are
287
+ def field_def(name)
288
+ @@known_fields_lookup[name] || # the fast way
289
+ @@known_fields.find{|hfd| hfd === name} || # the slow way
290
+ FieldDesc.new(name.to_s.downcase.gsub(/^.|[-_\s]./) { |x| x.upcase }.gsub('_', '-'), :repeatable => true) # make up as we go
291
+ end
205
292
 
206
293
  header_field('Accept', :repeatable => true)
207
294
  header_field('Accept-Charset', :repeatable => true)
@@ -1,10 +1,10 @@
1
1
  require 'net/http'
2
2
 
3
- require 'resourceful/options_interpretation'
4
3
  require 'resourceful/authentication_manager'
5
4
  require 'resourceful/cache_manager'
6
5
  require 'resourceful/resource'
7
- require 'resourceful/stubbed_resource_proxy'
6
+
7
+ require 'options'
8
8
 
9
9
  module Resourceful
10
10
  # This is an imitation Logger used when no real logger is
@@ -28,8 +28,6 @@ module Resourceful
28
28
  # provided by the Resourceful library. Conceptually this object
29
29
  # acts a collection of all the resources available via HTTP.
30
30
  class HttpAccessor
31
- include OptionsInterpretation
32
-
33
31
  # A logger object to which messages about the activities of this
34
32
  # object will be written. This should be an object that responds
35
33
  # to +#info(message)+ and +#debug(message)+.
@@ -67,18 +65,19 @@ module Resourceful
67
65
  #
68
66
  #
69
67
  def initialize(options = {})
68
+ options = Options.for(options).validate(:logger, :user_agent, :cache_manager, :authenticator, :authenticators, :http_adapter)
69
+
70
70
  @user_agent_tokens = [RESOURCEFUL_USER_AGENT_TOKEN]
71
71
  @auth_manager = AuthenticationManager.new()
72
72
 
73
- extract_opts(options) do |opts|
74
- @user_agent_tokens.push(*opts.extract(:user_agent, :default => []) {|ua| [ua].flatten})
75
-
76
- self.logger = opts.extract(:logger, :default => BitBucketLogger.new)
77
- @cache_manager = opts.extract(:cache_manager, :default => NullCacheManager.new)
78
- @http_adapter = opts.extract(:http_adapter, :default => lambda{NetHttpAdapter.new})
79
-
80
- opts.extract(:authenticator, :required => false).tap{|a| add_authenticator(a) if a}
81
- opts.extract(:authenticators, :default => []).each { |a| add_authenticator(a) }
73
+
74
+ @user_agent_tokens.push(*Array(options.getopt(:user_agent)).flatten.reverse)
75
+ self.logger = options.getopt(:logger) || BitBucketLogger.new
76
+ @cache_manager = options.getopt(:cache_manager) || NullCacheManager.new
77
+ @http_adapter = options.getopt(:http_adapter) || NetHttpAdapter.new
78
+
79
+ Array(options.getopt([:authenticator, :authenticators])).flatten.each do |an_authenticator|
80
+ add_authenticator(an_authenticator)
82
81
  end
83
82
  end
84
83
 
@@ -23,11 +23,10 @@ module Resourceful
23
23
  #
24
24
  # @return true|false
25
25
  def expired?
26
- if header['Cache-Control'] and header['Cache-Control'].first.include?('max-age')
27
- max_age = header['Cache-Control'].first.split(',').grep(/max-age/).first.split('=').last.to_i
28
- return true if current_age > max_age
29
- elsif header['Expires']
30
- return true if Time.httpdate(header['Expires']) < Time.now
26
+ if header.cache_control and m_age_str = header.cache_control.find{|cc| /^max-age=/ === cc}
27
+ return current_age > m_age_str[/\d+/].to_i
28
+ elsif header.expires
29
+ return Time.httpdate(header.expires) < Time.now
31
30
  end
32
31
 
33
32
  false
@@ -38,10 +37,8 @@ module Resourceful
38
37
  # @return true|false
39
38
  def stale?
40
39
  return true if expired?
41
- if header['Cache-Control']
42
- return true if header['Cache-Control'].include?('must-revalidate')
43
- return true if header['Cache-Control'].include?('no-cache')
44
- end
40
+ return false unless header.has_field?(Header::CACHE_CONTROL)
41
+ return true if header.cache_control.any?{|cc| /must-revalidate|no-cache/ === cc}
45
42
 
46
43
  false
47
44
  end
@@ -75,8 +72,8 @@ module Resourceful
75
72
 
76
73
  # Algorithm taken from RCF2616#13.2.3
77
74
  def current_age
78
- age_value = header['Age'] || 0
79
- date_value = Time.httpdate(header['Date'])
75
+ age_value = header.age.to_i
76
+ date_value = Time.httpdate(header.date)
80
77
  now = Time.now
81
78
 
82
79
  apparent_age = [0, response_time - date_value].max
@@ -2,29 +2,31 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{resourceful}
5
- s.version = "0.6.0"
5
+ s.version = "0.7.0"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["Paul Sadauskas"]
9
- s.date = %q{2009-08-14}
9
+ s.date = %q{2009-08-25}
10
10
  s.description = %q{An HTTP library for Ruby that takes advantage of everything HTTP has to offer.}
11
11
  s.email = %q{psadauskas@gmail.com}
12
- s.extra_rdoc_files = ["lib/resourceful.rb", "lib/resourceful/net_http_adapter.rb", "lib/resourceful/options_interpretation.rb", "lib/resourceful/stubbed_resource_proxy.rb", "lib/resourceful/urlencoded_form_data.rb", "lib/resourceful/header.rb", "lib/resourceful/memcache_cache_manager.rb", "lib/resourceful/response.rb", "lib/resourceful/util.rb", "lib/resourceful/abstract_form_data.rb", "lib/resourceful/cache_manager.rb", "lib/resourceful/request.rb", "lib/resourceful/resource.rb", "lib/resourceful/exceptions.rb", "lib/resourceful/multipart_form_data.rb", "lib/resourceful/http_accessor.rb", "lib/resourceful/authentication_manager.rb", "README.markdown"]
13
- s.files = ["lib/resourceful.rb", "lib/resourceful/net_http_adapter.rb", "lib/resourceful/options_interpretation.rb", "lib/resourceful/stubbed_resource_proxy.rb", "lib/resourceful/urlencoded_form_data.rb", "lib/resourceful/header.rb", "lib/resourceful/memcache_cache_manager.rb", "lib/resourceful/response.rb", "lib/resourceful/util.rb", "lib/resourceful/abstract_form_data.rb", "lib/resourceful/cache_manager.rb", "lib/resourceful/request.rb", "lib/resourceful/resource.rb", "lib/resourceful/exceptions.rb", "lib/resourceful/multipart_form_data.rb", "lib/resourceful/http_accessor.rb", "lib/resourceful/authentication_manager.rb", "History.txt", "resourceful.gemspec", "README.markdown", "MIT-LICENSE", "Rakefile", "Manifest", "spec/simple_sinatra_server_spec.rb", "spec/old_acceptance_specs.rb", "spec/acceptance_shared_specs.rb", "spec/spec_helper.rb", "spec/simple_sinatra_server.rb", "spec/acceptance/authorization_spec.rb", "spec/acceptance/header_spec.rb", "spec/acceptance/resource_spec.rb", "spec/acceptance/caching_spec.rb", "spec/acceptance/redirecting_spec.rb", "spec/resourceful/multipart_form_data_spec.rb", "spec/resourceful/header_spec.rb", "spec/resourceful/resource_spec.rb", "spec/resourceful/urlencoded_form_data_spec.rb", "spec/caching_spec.rb", "spec/spec.opts"]
12
+ s.extra_rdoc_files = ["lib/resourceful/abstract_form_data.rb", "lib/resourceful/authentication_manager.rb", "lib/resourceful/cache_manager.rb", "lib/resourceful/exceptions.rb", "lib/resourceful/header.rb", "lib/resourceful/http_accessor.rb", "lib/resourceful/memcache_cache_manager.rb", "lib/resourceful/multipart_form_data.rb", "lib/resourceful/net_http_adapter.rb", "lib/resourceful/request.rb", "lib/resourceful/resource.rb", "lib/resourceful/response.rb", "lib/resourceful/stubbed_resource_proxy.rb", "lib/resourceful/urlencoded_form_data.rb", "lib/resourceful/util.rb", "lib/resourceful.rb", "README.markdown"]
13
+ s.files = ["History.txt", "lib/resourceful/abstract_form_data.rb", "lib/resourceful/authentication_manager.rb", "lib/resourceful/cache_manager.rb", "lib/resourceful/exceptions.rb", "lib/resourceful/header.rb", "lib/resourceful/http_accessor.rb", "lib/resourceful/memcache_cache_manager.rb", "lib/resourceful/multipart_form_data.rb", "lib/resourceful/net_http_adapter.rb", "lib/resourceful/request.rb", "lib/resourceful/resource.rb", "lib/resourceful/response.rb", "lib/resourceful/stubbed_resource_proxy.rb", "lib/resourceful/urlencoded_form_data.rb", "lib/resourceful/util.rb", "lib/resourceful.rb", "Manifest", "MIT-LICENSE", "Rakefile", "README.markdown", "resourceful.gemspec", "spec/acceptance/authorization_spec.rb", "spec/acceptance/caching_spec.rb", "spec/acceptance/header_spec.rb", "spec/acceptance/redirecting_spec.rb", "spec/acceptance/resource_spec.rb", "spec/acceptance_shared_specs.rb", "spec/caching_spec.rb", "spec/old_acceptance_specs.rb", "spec/resourceful/header_spec.rb", "spec/resourceful/http_accessor_spec.rb", "spec/resourceful/multipart_form_data_spec.rb", "spec/resourceful/resource_spec.rb", "spec/resourceful/response_spec.rb", "spec/resourceful/urlencoded_form_data_spec.rb", "spec/simple_sinatra_server.rb", "spec/simple_sinatra_server_spec.rb", "spec/spec.opts", "spec/spec_helper.rb"]
14
+ s.has_rdoc = true
14
15
  s.homepage = %q{http://github.com/paul/resourceful}
15
16
  s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Resourceful", "--main", "README.markdown"]
16
17
  s.require_paths = ["lib"]
17
18
  s.rubyforge_project = %q{resourceful}
18
- s.rubygems_version = %q{1.3.5}
19
+ s.rubygems_version = %q{1.3.1}
19
20
  s.summary = %q{An HTTP library for Ruby that takes advantage of everything HTTP has to offer.}
20
21
 
21
22
  if s.respond_to? :specification_version then
22
23
  current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
23
- s.specification_version = 3
24
+ s.specification_version = 2
24
25
 
25
26
  if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
26
27
  s.add_runtime_dependency(%q<addressable>, [">= 2.1.0"])
27
28
  s.add_runtime_dependency(%q<httpauth>, [">= 0"])
29
+ s.add_runtime_dependency(%q<options>, [">= 2.2.0"])
28
30
  s.add_development_dependency(%q<thin>, [">= 0"])
29
31
  s.add_development_dependency(%q<yard>, [">= 0"])
30
32
  s.add_development_dependency(%q<sinatra>, [">= 0"])
@@ -32,6 +34,7 @@ Gem::Specification.new do |s|
32
34
  else
33
35
  s.add_dependency(%q<addressable>, [">= 2.1.0"])
34
36
  s.add_dependency(%q<httpauth>, [">= 0"])
37
+ s.add_dependency(%q<options>, [">= 2.2.0"])
35
38
  s.add_dependency(%q<thin>, [">= 0"])
36
39
  s.add_dependency(%q<yard>, [">= 0"])
37
40
  s.add_dependency(%q<sinatra>, [">= 0"])
@@ -40,6 +43,7 @@ Gem::Specification.new do |s|
40
43
  else
41
44
  s.add_dependency(%q<addressable>, [">= 2.1.0"])
42
45
  s.add_dependency(%q<httpauth>, [">= 0"])
46
+ s.add_dependency(%q<options>, [">= 2.2.0"])
43
47
  s.add_dependency(%q<thin>, [">= 0"])
44
48
  s.add_dependency(%q<yard>, [">= 0"])
45
49
  s.add_dependency(%q<sinatra>, [">= 0"])
@@ -54,7 +54,7 @@ describe "Caching" do
54
54
 
55
55
  resp = @resource.get
56
56
  resp.headers['X-Updateme'].should == ["bar"]
57
- resp.headers['Cache-Control'].should == ["private,max-age=0"]
57
+ resp.headers['Cache-Control'].should == ["private", "max-age=0"]
58
58
  end
59
59
 
60
60
  end
@@ -1,8 +1,153 @@
1
1
  require File.dirname(__FILE__) + "/../spec_helper.rb"
2
2
 
3
+ module Resourceful
4
+ describe Header do
5
+ def self.should_support_header(name)
6
+ const_name = name.upcase.gsub('-', '_')
7
+ meth_name = name.downcase.gsub('-', '_')
3
8
 
4
- describe Resourceful::Header do
5
- it "should have constants for header names" do
6
- Resourceful::Header::CONTENT_TYPE.should == 'Content-Type'
9
+ eval <<-RUBY
10
+ it "should have constant `#{const_name}` for header `#{name}`" do
11
+ Resourceful::Header::#{const_name}.should == '#{name}'
12
+ end
13
+
14
+ it "should have accessor method `#{meth_name}` for header `#{name}`" do
15
+ Resourceful::Header.new.should respond_to(:#{meth_name})
16
+ end
17
+
18
+ RUBY
19
+ end
20
+
21
+ should_support_header('Accept')
22
+ should_support_header('Accept-Charset')
23
+ should_support_header('Accept-Encoding')
24
+ should_support_header('Accept-Language')
25
+ should_support_header('Accept-Ranges')
26
+ should_support_header('Age')
27
+ should_support_header('Allow')
28
+ should_support_header('Authorization')
29
+ should_support_header('Cache-Control')
30
+ should_support_header('Connection')
31
+ should_support_header('Content-Encoding')
32
+ should_support_header('Content-Language')
33
+ should_support_header('Content-Length')
34
+ should_support_header('Content-Location')
35
+ should_support_header('Content-MD5')
36
+ should_support_header('Content-Range')
37
+ should_support_header('Content-Type')
38
+ should_support_header('Date')
39
+ should_support_header('ETag')
40
+ should_support_header('Expect')
41
+ should_support_header('Expires')
42
+ should_support_header('From')
43
+ should_support_header('Host')
44
+ should_support_header('If-Match')
45
+ should_support_header('If-Modified-Since')
46
+ should_support_header('If-None-Match')
47
+ should_support_header('If-Range')
48
+ should_support_header('If-Unmodified-Since')
49
+ should_support_header('Keep-Alive')
50
+ should_support_header('Last-Modified')
51
+ should_support_header('Location')
52
+ should_support_header('Max-Forwards')
53
+ should_support_header('Pragma')
54
+ should_support_header('Proxy-Authenticate')
55
+ should_support_header('Proxy-Authorization')
56
+ should_support_header('Range')
57
+ should_support_header('Referer')
58
+ should_support_header('Retry-After')
59
+ should_support_header('Server')
60
+ should_support_header('TE')
61
+ should_support_header('Trailer')
62
+ should_support_header('Transfer-Encoding')
63
+ should_support_header('Upgrade')
64
+ should_support_header('User-Agent')
65
+ should_support_header('Vary')
66
+ should_support_header('Via')
67
+ should_support_header('Warning')
68
+ should_support_header('WWW-Authenticate')
69
+
70
+
71
+ it "should be instantiatable w/ single valued header fields" do
72
+ Header.new('Host' => 'foo.example').
73
+ host.should eql('foo.example')
74
+ end
75
+
76
+ it "should gracefully handle repeated values for single valued header fields" do
77
+ lambda {
78
+ Header.new('Host' => ['foo.example', 'bar.example'])
79
+ }.should raise_error(ArgumentError, 'Host field may only have one value')
80
+ end
81
+
82
+ it "should provide #each_fields to iterate through all header fields and values as strings" do
83
+ field_names = []
84
+ Header.new('Accept' => "this", :content_type => "that", 'pragma' => 'test').each_field do |fname, _|
85
+ field_names << fname
86
+ end
87
+
88
+ field_names.should include('Accept')
89
+ field_names.should include('Content-Type')
90
+ field_names.should include('Pragma')
91
+ field_names.should have(3).items
92
+ end
93
+
94
+ it "should provide #to_hash as a way to dump the header fields" do
95
+ Header.new('Accept' => "this", :content_type => "that", 'date' => 'today').to_hash.tap do |h|
96
+ h.should have_pair('Accept', ['this'])
97
+ h.should have_pair('Content-Type', 'that')
98
+ h.should have_pair('Date', 'today')
99
+ end
100
+ end
101
+
102
+ it "should provide a list of hop-by-hop fields" do
103
+ Header.header_field('X-Hop-By-Hop-Header', :hop_by_hop => true)
104
+ Header.hop_by_hop_fields.should include('X-Hop-By-Hop-Header')
105
+ end
106
+
107
+ it "should provide a list of not modified fields" do
108
+ Header.header_field('X-Dont-Modify-Me', :modifiable => false)
109
+ Header.non_modifiable_fields.should include('X-Dont-Modify-Me')
110
+ end
111
+
112
+ describe "multi-valued fields" do
113
+ it "should be instantiatable w/ repeated multi-valued header fields" do
114
+ Header.new('Accept' => ['application/foo', 'application/bar']).
115
+ accept.should eql(['application/foo', 'application/bar'])
116
+ end
117
+
118
+ it "should be instantiatable w/ repeated multi-valued header fields w/ multiple values" do
119
+ Header.new('Accept' => ['application/foo, application/bar', 'text/plain']).
120
+ accept.should eql(['application/foo', 'application/bar', 'text/plain'])
121
+ end
122
+
123
+ it "should be instantiatable w/ multi-valued header fields w/ multiple values" do
124
+ Header.new('Accept' => 'application/foo, application/bar').
125
+ accept.should eql(['application/foo', 'application/bar'])
126
+ end
127
+
128
+ it "should be instantiatable w/ multi-valued header fields w/ one value" do
129
+ Header.new('Accept' => 'application/foo').
130
+ accept.should eql(['application/foo'])
131
+ end
132
+
133
+ it "should provide values to #each_field as a comma separated string" do
134
+ Header.new('Accept' => ['this', 'that']).each_field do |fname, fval|
135
+ fval.should == 'this, that'
136
+ end
137
+ end
138
+
139
+ it "should provide #each as a way to iterate through fields as w/ higher level values" do
140
+ Header.new('Accept' => ['this', 'that']).each do |fname, fval|
141
+ fval.should == ['this', 'that']
142
+ end
143
+ end
144
+ end
145
+
146
+ Spec::Matchers.define :have_pair do |name, value|
147
+ match do |header_hash|
148
+ header_hash.has_key?(name)
149
+ header_hash[name] == value
150
+ end
151
+ end
7
152
  end
8
153
  end
@@ -0,0 +1,56 @@
1
+ require File.dirname(__FILE__) + "/../spec_helper"
2
+
3
+ require "resourceful/http_accessor"
4
+
5
+ module Resourceful
6
+ describe HttpAccessor do
7
+ describe "instantiation" do
8
+ it "should accept logger option" do
9
+ test_logger = stub('logger', :debug => false)
10
+ ha = HttpAccessor.new(:logger => test_logger)
11
+ ha.logger.should equal(test_logger)
12
+ end
13
+
14
+ it "should accept array user_agent option" do
15
+ ha = HttpAccessor.new(:user_agent => ['foo/3.2', 'bar/1.0'])
16
+ ha.user_agent_string.should match(/^foo\/3.2 bar\/1.0 Resourceful/)
17
+ end
18
+
19
+ it "should accept string user_agent option" do
20
+ ha = HttpAccessor.new(:user_agent => 'foo')
21
+ ha.user_agent_string.should match(/^foo Resourceful/)
22
+ end
23
+
24
+ it "should accept cache_manager option" do
25
+ test_cache_manager = stub('cache_manager', :debug => false)
26
+ ha = HttpAccessor.new(:cache_manager => test_cache_manager)
27
+ ha.cache_manager.should equal(test_cache_manager)
28
+ end
29
+
30
+ it "should accept http_adapter option" do
31
+ test_http_adapter = stub('http_adapter', :debug => false)
32
+ ha = HttpAccessor.new(:http_adapter => test_http_adapter)
33
+ ha.http_adapter.should equal(test_http_adapter)
34
+ end
35
+
36
+ it "should accept authenticator option" do
37
+ test_authenticator = stub('authenticator', :debug => false)
38
+ ha = HttpAccessor.new(:authenticator => test_authenticator)
39
+ # cannot really be tested safely so we just rely on the fact that the option was accepted
40
+ end
41
+
42
+ it "should accept authenticators option" do
43
+ test_authenticator1 = stub('authenticator1', :debug => false)
44
+ test_authenticator2 = stub('authenticator2', :debug => false)
45
+ ha = HttpAccessor.new(:authenticator => [test_authenticator1, test_authenticator2])
46
+ # cannot really be tested safely so we just rely on the fact that the option was accepted
47
+ end
48
+
49
+ it "should reject unrecognized options" do
50
+ lambda {
51
+ HttpAccessor.new(:not_a_valid_option => "this")
52
+ }.should raise_error(ArgumentError)
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,51 @@
1
+ require File.dirname(__FILE__) + "/../spec_helper"
2
+
3
+ require 'resourceful/response'
4
+ require 'resourceful/header'
5
+
6
+ module Resourceful
7
+ describe Response do
8
+
9
+ it "should know when it is expired" do
10
+ resp = Response.new(nil, nil, Header.new('Cache-Control' => 'max-age=2', 'Date' => (Time.now - 2).httpdate), nil)
11
+ resp.request_time = Time.now
12
+
13
+ resp.expired?.should be_true
14
+ end
15
+
16
+ it "should know when it is not expired" do
17
+ resp = Response.new(nil, nil, Header.new('Cache-Control' => 'max-age=1', 'Date' => Time.now.httpdate), nil)
18
+ resp.request_time = Time.now
19
+
20
+ resp.expired?.should be_false
21
+ end
22
+
23
+ it "know when it is stale due to expiration" do
24
+ resp = Response.new(nil, nil, Header.new('Cache-Control' => 'max-age=1', 'Date' => (Time.now - 2).httpdate), nil)
25
+ resp.request_time = Time.now
26
+
27
+ resp.stale?.should be_true
28
+ end
29
+
30
+ it "know when it is stale due to no-cache" do
31
+ resp = Response.new(nil, nil, Header.new('Cache-Control' => 'no-cache', 'Date' => Time.now.httpdate), nil)
32
+ resp.request_time = Time.now
33
+
34
+ resp.stale?.should be_true
35
+ end
36
+
37
+ it "know when it is stale due to must-revalidate" do
38
+ resp = Response.new(nil, nil, Header.new('Cache-Control' => 'must-revalidate', 'Date' => Time.now.httpdate), nil)
39
+ resp.request_time = Time.now
40
+
41
+ resp.stale?.should be_true
42
+ end
43
+
44
+ it "know when it is not stale" do
45
+ resp = Response.new(nil, nil, Header.new('Cache-Control' => 'max-age=1', 'Date' => Time.now.httpdate), nil)
46
+ resp.request_time = Time.now
47
+
48
+ resp.stale?.should be_false
49
+ end
50
+ end
51
+ end
@@ -2,10 +2,12 @@ require 'rubygems'
2
2
  require 'spec'
3
3
  require 'pp'
4
4
 
5
- $LOAD_PATH << File.join(File.dirname(__FILE__), "..", "lib")
5
+ __DIR__ = File.dirname(__FILE__)
6
+
7
+ $LOAD_PATH << File.join(__DIR__, "..", "lib")
6
8
  require 'resourceful'
7
9
 
8
- $LOAD_PATH << File.dirname(__FILE__) # ./spec
10
+ $LOAD_PATH << __DIR__ # ./spec
9
11
 
10
12
  # Spawn the server in another process
11
13
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pezra-resourceful
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Paul Sadauskas
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-08-14 00:00:00 -07:00
12
+ date: 2009-08-25 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -32,6 +32,16 @@ dependencies:
32
32
  - !ruby/object:Gem::Version
33
33
  version: "0"
34
34
  version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: options
37
+ type: :runtime
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 2.2.0
44
+ version:
35
45
  - !ruby/object:Gem::Dependency
36
46
  name: thin
37
47
  type: :development
@@ -79,65 +89,65 @@ executables: []
79
89
  extensions: []
80
90
 
81
91
  extra_rdoc_files:
82
- - lib/resourceful.rb
83
- - lib/resourceful/net_http_adapter.rb
84
- - lib/resourceful/options_interpretation.rb
85
- - lib/resourceful/stubbed_resource_proxy.rb
86
- - lib/resourceful/urlencoded_form_data.rb
87
- - lib/resourceful/header.rb
88
- - lib/resourceful/memcache_cache_manager.rb
89
- - lib/resourceful/response.rb
90
- - lib/resourceful/util.rb
91
92
  - lib/resourceful/abstract_form_data.rb
93
+ - lib/resourceful/authentication_manager.rb
92
94
  - lib/resourceful/cache_manager.rb
93
- - lib/resourceful/request.rb
94
- - lib/resourceful/resource.rb
95
95
  - lib/resourceful/exceptions.rb
96
- - lib/resourceful/multipart_form_data.rb
96
+ - lib/resourceful/header.rb
97
97
  - lib/resourceful/http_accessor.rb
98
- - lib/resourceful/authentication_manager.rb
99
- - README.markdown
100
- files:
101
- - lib/resourceful.rb
98
+ - lib/resourceful/memcache_cache_manager.rb
99
+ - lib/resourceful/multipart_form_data.rb
102
100
  - lib/resourceful/net_http_adapter.rb
103
- - lib/resourceful/options_interpretation.rb
101
+ - lib/resourceful/request.rb
102
+ - lib/resourceful/resource.rb
103
+ - lib/resourceful/response.rb
104
104
  - lib/resourceful/stubbed_resource_proxy.rb
105
105
  - lib/resourceful/urlencoded_form_data.rb
106
- - lib/resourceful/header.rb
107
- - lib/resourceful/memcache_cache_manager.rb
108
- - lib/resourceful/response.rb
109
106
  - lib/resourceful/util.rb
107
+ - lib/resourceful.rb
108
+ - README.markdown
109
+ files:
110
+ - History.txt
110
111
  - lib/resourceful/abstract_form_data.rb
112
+ - lib/resourceful/authentication_manager.rb
111
113
  - lib/resourceful/cache_manager.rb
112
- - lib/resourceful/request.rb
113
- - lib/resourceful/resource.rb
114
114
  - lib/resourceful/exceptions.rb
115
- - lib/resourceful/multipart_form_data.rb
115
+ - lib/resourceful/header.rb
116
116
  - lib/resourceful/http_accessor.rb
117
- - lib/resourceful/authentication_manager.rb
118
- - History.txt
119
- - resourceful.gemspec
120
- - README.markdown
117
+ - lib/resourceful/memcache_cache_manager.rb
118
+ - lib/resourceful/multipart_form_data.rb
119
+ - lib/resourceful/net_http_adapter.rb
120
+ - lib/resourceful/request.rb
121
+ - lib/resourceful/resource.rb
122
+ - lib/resourceful/response.rb
123
+ - lib/resourceful/stubbed_resource_proxy.rb
124
+ - lib/resourceful/urlencoded_form_data.rb
125
+ - lib/resourceful/util.rb
126
+ - lib/resourceful.rb
127
+ - Manifest
121
128
  - MIT-LICENSE
122
129
  - Rakefile
123
- - Manifest
124
- - spec/simple_sinatra_server_spec.rb
125
- - spec/old_acceptance_specs.rb
126
- - spec/acceptance_shared_specs.rb
127
- - spec/spec_helper.rb
128
- - spec/simple_sinatra_server.rb
130
+ - README.markdown
131
+ - resourceful.gemspec
129
132
  - spec/acceptance/authorization_spec.rb
130
- - spec/acceptance/header_spec.rb
131
- - spec/acceptance/resource_spec.rb
132
133
  - spec/acceptance/caching_spec.rb
134
+ - spec/acceptance/header_spec.rb
133
135
  - spec/acceptance/redirecting_spec.rb
134
- - spec/resourceful/multipart_form_data_spec.rb
136
+ - spec/acceptance/resource_spec.rb
137
+ - spec/acceptance_shared_specs.rb
138
+ - spec/caching_spec.rb
139
+ - spec/old_acceptance_specs.rb
135
140
  - spec/resourceful/header_spec.rb
141
+ - spec/resourceful/http_accessor_spec.rb
142
+ - spec/resourceful/multipart_form_data_spec.rb
136
143
  - spec/resourceful/resource_spec.rb
144
+ - spec/resourceful/response_spec.rb
137
145
  - spec/resourceful/urlencoded_form_data_spec.rb
138
- - spec/caching_spec.rb
146
+ - spec/simple_sinatra_server.rb
147
+ - spec/simple_sinatra_server_spec.rb
139
148
  - spec/spec.opts
140
- has_rdoc: false
149
+ - spec/spec_helper.rb
150
+ has_rdoc: true
141
151
  homepage: http://github.com/paul/resourceful
142
152
  licenses:
143
153
  post_install_message:
@@ -167,7 +177,7 @@ requirements: []
167
177
  rubyforge_project: resourceful
168
178
  rubygems_version: 1.3.5
169
179
  signing_key:
170
- specification_version: 3
180
+ specification_version: 2
171
181
  summary: An HTTP library for Ruby that takes advantage of everything HTTP has to offer.
172
182
  test_files: []
173
183
 
@@ -1,72 +0,0 @@
1
- module Resourceful
2
- # Declarative way of interpreting options hashes
3
- #
4
- # include OptionsInterpretion
5
- # def my_method(opts = {})
6
- # extract_opts(opts) do |opts|
7
- # host = opts.extract(:host)
8
- # port = opts.extract(:port, :default => 80) {|p| Integer(p)}
9
- # end
10
- # end
11
- #
12
- module OptionsInterpretation
13
- # Interpret an options hash
14
- #
15
- # @param [Hash] opts
16
- # The options to interpret.
17
- #
18
- # @yield block that used to interpreter options hash
19
- #
20
- # @yieldparam [Resourceful::OptionsInterpretion::OptionsInterpreter] interpeter
21
- # An interpreter that can be used to extract option information from the options hash.
22
- def extract_opts(opts, &blk)
23
- opts = opts.clone
24
- yield OptionsInterpreter.new(opts)
25
-
26
- unless opts.empty?
27
- raise ArgumentError, "Unrecognized options: #{opts.keys.join(", ")}"
28
- end
29
-
30
- end
31
-
32
- class OptionsInterpreter
33
- def initialize(options_hash)
34
- @options_hash = options_hash
35
- end
36
-
37
- # Extract a particular option.
38
- #
39
- # @param [String] name
40
- # Name of option to extract
41
- # @param [Hash] interpreter_opts
42
- # ':default'
43
- # :: The default value, or an object that responds to #call
44
- # with the default value.
45
- # ':required'
46
- # :: Boolean indicating if this option is required. Default:
47
- # false if a default is provided; otherwise true.
48
- def extract(name, interpreter_opts = {}, &blk)
49
- option_required = !interpreter_opts.has_key?(:default)
50
- option_required = interpreter_opts[:required] if interpreter_opts.has_key?(:required)
51
-
52
- raise ArgumentError, "Required option #{name} not provided" if option_required && !@options_hash.has_key?(name)
53
- # We have the option we need
54
-
55
- orig_val = @options_hash.delete(name)
56
-
57
- if block_given?
58
- yield orig_val
59
-
60
- elsif orig_val
61
- orig_val
62
-
63
- elsif interpreter_opts[:default] && interpreter_opts[:default].respond_to?(:call)
64
- interpreter_opts[:default].call()
65
-
66
- elsif interpreter_opts[:default]
67
- interpreter_opts[:default]
68
- end
69
- end
70
- end
71
- end
72
- end