resourceful 0.6.1 → 0.6.3
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +16 -0
- data/Manifest +27 -26
- data/Rakefile +1 -1
- data/lib/resourceful.rb +2 -1
- data/lib/resourceful/header.rb +154 -67
- data/lib/resourceful/http_accessor.rb +12 -13
- data/lib/resourceful/request.rb +1 -0
- data/lib/resourceful/response.rb +8 -11
- data/resourceful.gemspec +7 -4
- data/spec/caching_spec.rb +1 -1
- data/spec/resourceful/header_spec.rb +148 -3
- data/spec/resourceful/http_accessor_spec.rb +56 -0
- data/spec/resourceful/response_spec.rb +51 -0
- data/spec/spec_helper.rb +4 -2
- metadata +49 -39
- data/lib/resourceful/options_interpretation.rb +0 -72
data/History.txt
CHANGED
@@ -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
|
-
|
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/
|
6
|
+
lib/resourceful/header.rb
|
16
7
|
lib/resourceful/http_accessor.rb
|
17
|
-
lib/resourceful/
|
18
|
-
|
19
|
-
resourceful.
|
20
|
-
|
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
|
-
|
24
|
-
|
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/
|
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/
|
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
|
data/lib/resourceful.rb
CHANGED
@@ -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.
|
21
|
+
VERSION = "0.6.3"
|
21
22
|
RESOURCEFUL_USER_AGENT_TOKEN = "Resourceful/#{VERSION}(Ruby/#{RUBY_VERSION})"
|
22
23
|
end
|
data/lib/resourceful/header.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
|
-
require '
|
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
|
-
|
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|
|
@@ -82,43 +97,61 @@ module Resourceful
|
|
82
97
|
self.class.new(@raw_fields.dup)
|
83
98
|
end
|
84
99
|
|
85
|
-
|
86
100
|
# Class to handle the details of each type of field.
|
87
|
-
class
|
101
|
+
class FieldDesc
|
88
102
|
include Comparable
|
89
|
-
|
90
|
-
|
103
|
+
|
91
104
|
##
|
92
105
|
attr_reader :name
|
93
|
-
|
106
|
+
|
107
|
+
# Create a new header field descriptor.
|
108
|
+
#
|
109
|
+
# @param [String] name The canonical name of this field.
|
110
|
+
#
|
111
|
+
# @param [Hash] options hash containing extra information about
|
112
|
+
# this header fields. Valid keys are:
|
113
|
+
#
|
114
|
+
# `:multivalued`
|
115
|
+
# `:multivalue`
|
116
|
+
# `:repeatable`
|
117
|
+
# : Values of this field are comma separated list of values.
|
118
|
+
# (n#VALUE per HTTP spec.) Default: false
|
119
|
+
#
|
120
|
+
# `:hop_by_hop`
|
121
|
+
# : True if the header is a hop-by-hop header. Default: false
|
122
|
+
#
|
123
|
+
# `:modifiable`
|
124
|
+
# : False if the header should not be modified by intermediates or caches. Default: true
|
125
|
+
#
|
94
126
|
def initialize(name, options = {})
|
95
127
|
@name = name
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
128
|
+
options = Options.for(options).validate(:repeatable, :hop_by_hop, :modifiable)
|
129
|
+
|
130
|
+
@repeatable = options.getopt([:repeatable, :multivalue, :multivalued]) || false
|
131
|
+
@hop_by_hop = options.getopt(:hop_by_hop) || false
|
132
|
+
@modifiable = options.getopt(:modifiable, true)
|
101
133
|
end
|
102
|
-
|
134
|
+
|
103
135
|
def repeatable?
|
104
136
|
@repeatable
|
105
137
|
end
|
106
|
-
|
138
|
+
alias multivalued? repeatable?
|
139
|
+
|
107
140
|
def hop_by_hop?
|
108
141
|
@hop_by_hop
|
109
142
|
end
|
110
|
-
|
143
|
+
|
111
144
|
def modifiable?
|
112
145
|
@modifiable
|
113
146
|
end
|
114
|
-
|
147
|
+
|
115
148
|
def get_from(raw_fields_hash)
|
116
149
|
raw_fields_hash[name]
|
117
150
|
end
|
118
|
-
|
151
|
+
|
119
152
|
def set_to(value, raw_fields_hash)
|
120
|
-
raw_fields_hash[name] = if
|
121
|
-
Array(value)
|
153
|
+
raw_fields_hash[name] = if multivalued?
|
154
|
+
Array(value).map{|v| v.split(/,\s*/)}.flatten
|
122
155
|
elsif value.kind_of?(Array)
|
123
156
|
raise ArgumentError, "#{name} field may only have one value" if value.size > 1
|
124
157
|
value.first
|
@@ -126,86 +159,140 @@ module Resourceful
|
|
126
159
|
value
|
127
160
|
end
|
128
161
|
end
|
129
|
-
|
162
|
+
|
130
163
|
def exists_in?(raw_fields_hash)
|
131
164
|
raw_fields_hash.has_key?(name)
|
132
165
|
end
|
133
|
-
|
166
|
+
|
134
167
|
def <=>(another)
|
135
168
|
name <=> another.name
|
136
169
|
end
|
137
|
-
|
170
|
+
|
138
171
|
def ==(another)
|
139
|
-
name_pattern === another.
|
172
|
+
name_pattern === another.to_s
|
140
173
|
end
|
141
174
|
alias eql? ==
|
142
|
-
|
175
|
+
|
143
176
|
def ===(another)
|
144
|
-
if another.kind_of?(
|
177
|
+
if another.kind_of?(FieldDesc)
|
145
178
|
self == another
|
146
179
|
else
|
147
180
|
name_pattern === another
|
148
181
|
end
|
149
182
|
end
|
150
|
-
|
183
|
+
|
151
184
|
def name_pattern
|
152
|
-
Regexp.new('^' + name.gsub('-', '[_-]') + '$', Regexp::IGNORECASE)
|
185
|
+
@name_pattern || @name_pattern = Regexp.new('^' + name.gsub('-', '[_-]') + '$', Regexp::IGNORECASE)
|
153
186
|
end
|
154
|
-
|
187
|
+
|
155
188
|
def methodized_name
|
156
|
-
name.downcase.gsub('-', '_')
|
189
|
+
@methodized_name ||= name.downcase.gsub('-', '_')
|
157
190
|
end
|
158
|
-
|
191
|
+
|
192
|
+
def constantized_name
|
193
|
+
@constantized_name ||= name.upcase.gsub('-', '_')
|
194
|
+
end
|
195
|
+
|
159
196
|
alias to_s name
|
160
|
-
|
161
|
-
def
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
197
|
+
|
198
|
+
def accessor_module
|
199
|
+
@accessor_module ||= begin
|
200
|
+
Module.new.tap{|m| m.module_eval(<<-RUBY)}
|
201
|
+
#{constantized_name} = '#{name}'
|
202
|
+
|
203
|
+
def #{methodized_name} # def accept
|
204
|
+
self[#{constantized_name}] # self[ACCEPT]
|
205
|
+
end # end
|
206
|
+
|
207
|
+
def #{methodized_name}=(val) # def accept=(val)
|
208
|
+
self[#{constantized_name}] = val # self[ACCEPT] = val
|
209
|
+
end # end
|
210
|
+
RUBY
|
211
|
+
end
|
167
212
|
end
|
168
213
|
|
169
|
-
def
|
170
|
-
|
171
|
-
def #{methodized_name} # def accept
|
172
|
-
self['#{name}'] # self['Accept']
|
173
|
-
end # end
|
174
|
-
RUBY
|
214
|
+
def hash
|
215
|
+
@name.hash
|
175
216
|
end
|
176
|
-
|
177
|
-
def gen_canonical_name_const(klass)
|
178
|
-
const_name = name.upcase.gsub('-', '_')
|
179
217
|
|
180
|
-
|
218
|
+
# Yields each commonly used lookup key for this header field.
|
219
|
+
def lookup_keys(&blk)
|
220
|
+
yield name
|
221
|
+
yield name.upcase
|
222
|
+
yield name.downcase
|
223
|
+
yield methodized_name
|
224
|
+
yield methodized_name.to_sym
|
225
|
+
yield constantized_name
|
226
|
+
yield constantized_name.to_sym
|
181
227
|
end
|
182
|
-
end
|
183
|
-
|
184
|
-
@@
|
185
|
-
|
228
|
+
end # FieldDesc
|
229
|
+
|
230
|
+
@@known_fields = Set.new
|
231
|
+
@@known_fields_lookup = Hash.new
|
232
|
+
|
233
|
+
# Declares a common header field. Header fields do not have to be
|
234
|
+
# defined this way but accessing them is easier, safer and faster
|
235
|
+
# if you do. Declaring a field does the following things:
|
236
|
+
#
|
237
|
+
# * defines accessor methods (e.g. `#content_type` and
|
238
|
+
# `#content_type=`) on `Header`
|
239
|
+
#
|
240
|
+
# * defines constant that can be used to reference there field
|
241
|
+
# name (e.g. `some_header[Header::CONTENT_TYPE]`)
|
242
|
+
#
|
243
|
+
# * includes the field in the appropriate *_fields groups (e.g. `Header.non_modifiable_fields`)
|
244
|
+
#
|
245
|
+
# * provides improved multiple value parsing
|
246
|
+
#
|
247
|
+
# Create a new header field descriptor.
|
248
|
+
#
|
249
|
+
# @param [String] name The canonical name of this field.
|
250
|
+
#
|
251
|
+
# @param [Hash] options hash containing extra information about
|
252
|
+
# this header fields. Valid keys are:
|
253
|
+
#
|
254
|
+
# `:multivalued`
|
255
|
+
# `:multivalue`
|
256
|
+
# `:repeatable`
|
257
|
+
# : Values of this field are comma separated list of values.
|
258
|
+
# (n#VALUE per HTTP spec.) Default: false
|
259
|
+
#
|
260
|
+
# `:hop_by_hop`
|
261
|
+
# : True if the header is a hop-by-hop header. Default: false
|
262
|
+
#
|
263
|
+
# `:modifiable`
|
264
|
+
# : False if the header should not be modified by intermediates or caches. Default: true
|
265
|
+
#
|
186
266
|
def self.header_field(name, options = {})
|
187
|
-
hfd =
|
188
|
-
|
189
|
-
@@
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
hfd.gen_canonical_name_const(self)
|
194
|
-
end
|
267
|
+
hfd = FieldDesc.new(name, options)
|
268
|
+
|
269
|
+
@@known_fields << hfd
|
270
|
+
hfd.lookup_keys do |a_key|
|
271
|
+
@@known_fields_lookup[a_key] = hfd
|
272
|
+
end
|
195
273
|
|
196
|
-
|
197
|
-
@@header_field_defs.select{|hfd| hfd.hop_by_hop?}
|
274
|
+
include(hfd.accessor_module)
|
198
275
|
end
|
199
|
-
|
200
|
-
def self.
|
201
|
-
@@
|
276
|
+
|
277
|
+
def self.hop_by_hop_fields
|
278
|
+
@@known_fields.select{|hfd| hfd.hop_by_hop?}
|
202
279
|
end
|
203
280
|
|
204
|
-
def
|
205
|
-
@@
|
206
|
-
HeaderFieldDef.new(name.to_s.downcase.gsub(/^.|[-_\s]./) { |x| x.upcase }.gsub('_', '-'), :repeatable => true)
|
281
|
+
def self.non_modifiable_fields
|
282
|
+
@@known_fields.reject{|hfd| hfd.modifiable?}
|
207
283
|
end
|
208
284
|
|
285
|
+
protected
|
286
|
+
|
287
|
+
# ---
|
288
|
+
#
|
289
|
+
# We have to fall back on a slow iteration to find the header
|
290
|
+
# field some times because field names are
|
291
|
+
def field_def(name)
|
292
|
+
@@known_fields_lookup[name] || # the fast way
|
293
|
+
@@known_fields.find{|hfd| hfd === name} || # the slow way
|
294
|
+
FieldDesc.new(name.to_s.downcase.gsub(/^.|[-_\s]./) { |x| x.upcase }.gsub('_', '-'), :repeatable => true) # make up as we go
|
295
|
+
end
|
209
296
|
|
210
297
|
header_field('Accept', :repeatable => true)
|
211
298
|
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
|
-
|
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
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
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
|
|
data/lib/resourceful/request.rb
CHANGED
@@ -123,6 +123,7 @@ module Resourceful
|
|
123
123
|
logger.info("Authentication Required. Retrying with auth info")
|
124
124
|
accessor.auth_manager.associate_auth_info(response)
|
125
125
|
add_credentials!
|
126
|
+
@body.rewind if @body # Its a stringIO, and we already fed it to the adapter once, so rewind it when we try again
|
126
127
|
response = fetch_response
|
127
128
|
end
|
128
129
|
|
data/lib/resourceful/response.rb
CHANGED
@@ -23,11 +23,10 @@ module Resourceful
|
|
23
23
|
#
|
24
24
|
# @return true|false
|
25
25
|
def expired?
|
26
|
-
if header
|
27
|
-
|
28
|
-
|
29
|
-
|
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
|
-
|
42
|
-
|
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
|
79
|
-
date_value = Time.httpdate(header
|
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
|
data/resourceful.gemspec
CHANGED
@@ -2,15 +2,15 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = %q{resourceful}
|
5
|
-
s.version = "0.6.
|
5
|
+
s.version = "0.6.3"
|
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-09-
|
9
|
+
s.date = %q{2009-09-29}
|
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
|
13
|
-
s.files = ["
|
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
14
|
s.homepage = %q{http://github.com/paul/resourceful}
|
15
15
|
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Resourceful", "--main", "README.markdown"]
|
16
16
|
s.require_paths = ["lib"]
|
@@ -25,6 +25,7 @@ Gem::Specification.new do |s|
|
|
25
25
|
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
26
26
|
s.add_runtime_dependency(%q<addressable>, [">= 2.1.0"])
|
27
27
|
s.add_runtime_dependency(%q<httpauth>, [">= 0"])
|
28
|
+
s.add_runtime_dependency(%q<options>, [">= 2.2.0"])
|
28
29
|
s.add_development_dependency(%q<thin>, [">= 0"])
|
29
30
|
s.add_development_dependency(%q<yard>, [">= 0"])
|
30
31
|
s.add_development_dependency(%q<sinatra>, [">= 0"])
|
@@ -32,6 +33,7 @@ Gem::Specification.new do |s|
|
|
32
33
|
else
|
33
34
|
s.add_dependency(%q<addressable>, [">= 2.1.0"])
|
34
35
|
s.add_dependency(%q<httpauth>, [">= 0"])
|
36
|
+
s.add_dependency(%q<options>, [">= 2.2.0"])
|
35
37
|
s.add_dependency(%q<thin>, [">= 0"])
|
36
38
|
s.add_dependency(%q<yard>, [">= 0"])
|
37
39
|
s.add_dependency(%q<sinatra>, [">= 0"])
|
@@ -40,6 +42,7 @@ Gem::Specification.new do |s|
|
|
40
42
|
else
|
41
43
|
s.add_dependency(%q<addressable>, [">= 2.1.0"])
|
42
44
|
s.add_dependency(%q<httpauth>, [">= 0"])
|
45
|
+
s.add_dependency(%q<options>, [">= 2.2.0"])
|
43
46
|
s.add_dependency(%q<thin>, [">= 0"])
|
44
47
|
s.add_dependency(%q<yard>, [">= 0"])
|
45
48
|
s.add_dependency(%q<sinatra>, [">= 0"])
|
data/spec/caching_spec.rb
CHANGED
@@ -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
|
-
|
5
|
-
|
6
|
-
|
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
|
data/spec/spec_helper.rb
CHANGED
@@ -2,10 +2,12 @@ require 'rubygems'
|
|
2
2
|
require 'spec'
|
3
3
|
require 'pp'
|
4
4
|
|
5
|
-
|
5
|
+
__DIR__ = File.dirname(__FILE__)
|
6
|
+
|
7
|
+
$LOAD_PATH << File.join(__DIR__, "..", "lib")
|
6
8
|
require 'resourceful'
|
7
9
|
|
8
|
-
$LOAD_PATH <<
|
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: resourceful
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.6.
|
4
|
+
version: 0.6.3
|
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-09-
|
12
|
+
date: 2009-09-29 00:00:00 -06: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,64 +89,64 @@ 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/
|
96
|
+
- lib/resourceful/header.rb
|
97
97
|
- lib/resourceful/http_accessor.rb
|
98
|
-
- lib/resourceful/
|
99
|
-
-
|
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/
|
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/
|
115
|
+
- lib/resourceful/header.rb
|
116
116
|
- lib/resourceful/http_accessor.rb
|
117
|
-
- lib/resourceful/
|
118
|
-
-
|
119
|
-
- resourceful.
|
120
|
-
-
|
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
|
-
-
|
124
|
-
-
|
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/
|
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/
|
146
|
+
- spec/simple_sinatra_server.rb
|
147
|
+
- spec/simple_sinatra_server_spec.rb
|
139
148
|
- spec/spec.opts
|
149
|
+
- spec/spec_helper.rb
|
140
150
|
has_rdoc: true
|
141
151
|
homepage: http://github.com/paul/resourceful
|
142
152
|
licenses: []
|
@@ -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
|