resourceful 0.6.1 → 0.6.3
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.
- 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
|