resourceful 0.3.1 → 0.5.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.
- data/Manifest +24 -28
- data/Rakefile +44 -14
- data/lib/resourceful.rb +11 -21
- data/lib/resourceful/authentication_manager.rb +3 -2
- data/lib/resourceful/cache_manager.rb +58 -1
- data/lib/resourceful/exceptions.rb +34 -0
- data/lib/resourceful/header.rb +95 -0
- data/lib/resourceful/http_accessor.rb +0 -2
- data/lib/resourceful/memcache_cache_manager.rb +3 -13
- data/lib/resourceful/net_http_adapter.rb +15 -5
- data/lib/resourceful/request.rb +180 -18
- data/lib/resourceful/resource.rb +38 -141
- data/lib/resourceful/response.rb +142 -95
- data/resourceful.gemspec +9 -7
- data/spec/acceptance/authorization_spec.rb +16 -0
- data/spec/acceptance/caching_spec.rb +192 -0
- data/spec/acceptance/header_spec.rb +24 -0
- data/spec/acceptance/redirecting_spec.rb +12 -0
- data/spec/acceptance/resource_spec.rb +84 -0
- data/spec/acceptance_shared_specs.rb +12 -17
- data/spec/{acceptance_spec.rb → old_acceptance_specs.rb} +27 -57
- data/spec/simple_sinatra_server.rb +74 -0
- data/spec/simple_sinatra_server_spec.rb +98 -0
- data/spec/spec_helper.rb +21 -7
- metadata +50 -42
- data/spec/resourceful/authentication_manager_spec.rb +0 -249
- data/spec/resourceful/cache_manager_spec.rb +0 -223
- data/spec/resourceful/header_spec.rb +0 -38
- data/spec/resourceful/http_accessor_spec.rb +0 -164
- data/spec/resourceful/memcache_cache_manager_spec.rb +0 -111
- data/spec/resourceful/net_http_adapter_spec.rb +0 -96
- data/spec/resourceful/options_interpreter_spec.rb +0 -102
- data/spec/resourceful/request_spec.rb +0 -186
- data/spec/resourceful/resource_spec.rb +0 -600
- data/spec/resourceful/response_spec.rb +0 -238
- data/spec/resourceful/stubbed_resource_proxy_spec.rb +0 -58
- data/spec/simple_http_server_shared_spec.rb +0 -162
- data/spec/simple_http_server_shared_spec_spec.rb +0 -212
data/Manifest
CHANGED
@@ -1,34 +1,30 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
1
|
+
Manifest
|
2
|
+
spec/spec.opts
|
3
|
+
spec/simple_sinatra_server_spec.rb
|
4
|
+
spec/simple_sinatra_server.rb
|
5
|
+
spec/acceptance/header_spec.rb
|
6
|
+
spec/acceptance/caching_spec.rb
|
7
|
+
spec/acceptance/resource_spec.rb
|
8
|
+
spec/acceptance/redirecting_spec.rb
|
9
|
+
spec/acceptance/authorization_spec.rb
|
10
|
+
spec/acceptance_shared_specs.rb
|
11
|
+
spec/spec_helper.rb
|
12
|
+
spec/old_acceptance_specs.rb
|
13
|
+
README.markdown
|
14
|
+
resourceful.gemspec
|
15
|
+
lib/resourceful/cache_manager.rb
|
16
|
+
lib/resourceful/exceptions.rb
|
6
17
|
lib/resourceful/net_http_adapter.rb
|
7
|
-
lib/resourceful/http_accessor.rb
|
8
18
|
lib/resourceful/stubbed_resource_proxy.rb
|
9
19
|
lib/resourceful/header.rb
|
10
|
-
lib/resourceful/
|
11
|
-
lib/resourceful/options_interpreter.rb
|
12
|
-
lib/resourceful/response.rb
|
20
|
+
lib/resourceful/authentication_manager.rb
|
13
21
|
lib/resourceful/request.rb
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
spec/resourceful/memcache_cache_manager_spec.rb
|
22
|
-
spec/resourceful/response_spec.rb
|
23
|
-
spec/resourceful/options_interpreter_spec.rb
|
24
|
-
spec/resourceful/http_accessor_spec.rb
|
25
|
-
spec/resourceful/stubbed_resource_proxy_spec.rb
|
26
|
-
spec/resourceful/request_spec.rb
|
27
|
-
spec/resourceful/resource_spec.rb
|
28
|
-
spec/resourceful/cache_manager_spec.rb
|
29
|
-
spec/resourceful/net_http_adapter_spec.rb
|
30
|
-
spec/simple_http_server_shared_spec.rb
|
31
|
-
Manifest
|
22
|
+
lib/resourceful/resource.rb
|
23
|
+
lib/resourceful/response.rb
|
24
|
+
lib/resourceful/util.rb
|
25
|
+
lib/resourceful/http_accessor.rb
|
26
|
+
lib/resourceful/options_interpreter.rb
|
27
|
+
lib/resourceful/memcache_cache_manager.rb
|
28
|
+
lib/resourceful.rb
|
32
29
|
Rakefile
|
33
|
-
README.markdown
|
34
30
|
MIT-LICENSE
|
data/Rakefile
CHANGED
@@ -1,16 +1,22 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'rake'
|
3
|
-
require '
|
3
|
+
require 'lib/resourceful'
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
p.url = "http://github.com/paul/resourceful"
|
8
|
-
p.author = "Paul Sadauskas"
|
9
|
-
p.email = "psadauskas@gmail.com"
|
5
|
+
begin
|
6
|
+
require 'echoe'
|
10
7
|
|
11
|
-
|
12
|
-
|
13
|
-
|
8
|
+
Echoe.new('resourceful', Resourceful::VERSION) do |p|
|
9
|
+
p.description = "An HTTP library for Ruby that takes advantage of everything HTTP has to offer."
|
10
|
+
p.url = "http://github.com/paul/resourceful"
|
11
|
+
p.author = "Paul Sadauskas"
|
12
|
+
p.email = "psadauskas@gmail.com"
|
13
|
+
|
14
|
+
p.ignore_pattern = ["pkg/*", "tmp/*"]
|
15
|
+
p.dependencies = ['addressable', 'httpauth', 'rspec', 'facets', 'andand']
|
16
|
+
p.development_dependencies = ['thin', 'yard', 'sinatra']
|
17
|
+
end
|
18
|
+
rescue LoadError => e
|
19
|
+
puts "install 'echoe' gem to be able to build the gem"
|
14
20
|
end
|
15
21
|
|
16
22
|
require 'spec/rake/spectask'
|
@@ -19,7 +25,27 @@ desc 'Run all specs'
|
|
19
25
|
Spec::Rake::SpecTask.new(:spec) do |t|
|
20
26
|
t.spec_opts << '--options' << 'spec/spec.opts' if File.exists?('spec/spec.opts')
|
21
27
|
t.libs << 'lib'
|
22
|
-
t.spec_files = FileList['spec
|
28
|
+
t.spec_files = FileList['spec/acceptance/*_spec.rb']
|
29
|
+
end
|
30
|
+
|
31
|
+
desc 'Run the specs for the server'
|
32
|
+
Spec::Rake::SpecTask.new('spec:server') do |t|
|
33
|
+
t.spec_opts << '--options' << 'spec/spec.opts' if File.exists?('spec/spec.opts')
|
34
|
+
t.libs << 'lib'
|
35
|
+
t.spec_files = FileList['spec/simple_sinatra_server_spec.rb']
|
36
|
+
end
|
37
|
+
|
38
|
+
begin
|
39
|
+
require 'spec/simple_sinatra_server'
|
40
|
+
desc "Run the sinatra echo server, with loggin"
|
41
|
+
task :server do
|
42
|
+
Sinatra::Default.set(
|
43
|
+
:run => true,
|
44
|
+
:logging => true
|
45
|
+
)
|
46
|
+
end
|
47
|
+
rescue LoadError => e
|
48
|
+
puts "Install 'sinatra' gem to run the server"
|
23
49
|
end
|
24
50
|
|
25
51
|
desc 'Default: Run Specs'
|
@@ -28,11 +54,15 @@ task :default => :spec
|
|
28
54
|
desc 'Run all tests'
|
29
55
|
task :test => :spec
|
30
56
|
|
31
|
-
|
57
|
+
begin
|
58
|
+
require 'yard'
|
32
59
|
|
33
|
-
desc "Generate Yardoc"
|
34
|
-
YARD::Rake::YardocTask.new do |t|
|
35
|
-
|
60
|
+
desc "Generate Yardoc"
|
61
|
+
YARD::Rake::YardocTask.new do |t|
|
62
|
+
t.files = ['lib/**/*.rb', 'README.markdown']
|
63
|
+
end
|
64
|
+
rescue LoadError => e
|
65
|
+
puts "Install 'yard' gem to generate docs"
|
36
66
|
end
|
37
67
|
|
38
68
|
desc "Update rubyforge documentation"
|
data/lib/resourceful.rb
CHANGED
@@ -1,28 +1,18 @@
|
|
1
|
-
require 'resourceful/http_accessor'
|
2
1
|
|
3
|
-
|
2
|
+
__DIR__ = File.dirname(__FILE__)
|
4
3
|
|
5
|
-
|
6
|
-
|
4
|
+
$LOAD_PATH.unshift __DIR__ unless
|
5
|
+
$LOAD_PATH.include?(__DIR__) ||
|
6
|
+
$LOAD_PATH.include?(File.expand_path(__DIR__))
|
7
7
|
|
8
|
-
|
9
|
-
Connection
|
10
|
-
Keep-Alive
|
11
|
-
Proxy-Authenticate
|
12
|
-
Proxy-Authorization
|
13
|
-
TE
|
14
|
-
Trailers
|
15
|
-
Transfer-Encoding
|
16
|
-
Upgrade
|
17
|
-
}
|
8
|
+
require 'resourceful/util'
|
18
9
|
|
19
|
-
|
20
|
-
|
21
|
-
Content-MD5
|
22
|
-
ETag
|
23
|
-
Last-Modified
|
24
|
-
Expires
|
25
|
-
}
|
10
|
+
require 'resourceful/header'
|
11
|
+
require 'resourceful/http_accessor'
|
26
12
|
|
13
|
+
# Resourceful is a library that provides a high level HTTP interface.
|
14
|
+
module Resourceful
|
15
|
+
VERSION = "0.5.0"
|
16
|
+
RESOURCEFUL_USER_AGENT_TOKEN = "Resourceful/#{VERSION}(Ruby/#{RUBY_VERSION})"
|
27
17
|
|
28
18
|
end
|
@@ -20,8 +20,9 @@ module Resourceful
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def add_credentials(request)
|
23
|
-
|
24
|
-
|
23
|
+
@authenticators.each do |authenticator|
|
24
|
+
authenticator.add_credentials_to(request) if authenticator.can_handle?(request)
|
25
|
+
end
|
25
26
|
end
|
26
27
|
|
27
28
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'resourceful/header'
|
2
2
|
require 'andand'
|
3
3
|
require 'facets/kernel/returning'
|
4
|
+
require 'digest/md5'
|
4
5
|
|
5
6
|
module Resourceful
|
6
7
|
|
@@ -40,6 +41,13 @@ module Resourceful
|
|
40
41
|
# @param uri<String>
|
41
42
|
# The uri of the resource to be invalidated
|
42
43
|
def invalidate(uri); end
|
44
|
+
|
45
|
+
protected
|
46
|
+
|
47
|
+
# Returns an alphanumeric hash of a URI
|
48
|
+
def uri_hash(uri)
|
49
|
+
Digest::MD5.hexdigest(uri)
|
50
|
+
end
|
43
51
|
end
|
44
52
|
|
45
53
|
# This is the default cache, and does not do any caching. All lookups
|
@@ -70,7 +78,7 @@ module Resourceful
|
|
70
78
|
end
|
71
79
|
|
72
80
|
def store(request, response)
|
73
|
-
return unless response.
|
81
|
+
return unless response.cacheable?
|
74
82
|
|
75
83
|
@collection[request.uri.to_s][request] = response
|
76
84
|
end
|
@@ -80,6 +88,55 @@ module Resourceful
|
|
80
88
|
end
|
81
89
|
end # class InMemoryCacheManager
|
82
90
|
|
91
|
+
# Stores cache entries in a directory on the filesystem. Similarly to the
|
92
|
+
# InMemoryCacheManager there are no limits on storage, so this will eventually
|
93
|
+
# eat up all your disk!
|
94
|
+
class FileCacheManager < AbstractCacheManager
|
95
|
+
# Create a new FileCacheManager
|
96
|
+
#
|
97
|
+
# @param [String] location
|
98
|
+
# A directory on the filesystem to store cache entries. This directory
|
99
|
+
# will be created if it doesn't exist
|
100
|
+
def initialize(location="/tmp/resourceful")
|
101
|
+
require 'fileutils'
|
102
|
+
require 'yaml'
|
103
|
+
@dir = FileUtils.mkdir_p(location)
|
104
|
+
end
|
105
|
+
|
106
|
+
def lookup(request)
|
107
|
+
returning(cache_entries_for(request)[request]) do |response|
|
108
|
+
response.authoritative = false if response
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def store(request, response)
|
113
|
+
return unless response.cacheable?
|
114
|
+
|
115
|
+
entries = cache_entries_for(request)
|
116
|
+
entries[request] = response
|
117
|
+
File.open(cache_file(request.uri), "w") {|fh| fh.write( YAML.dump(entries) ) }
|
118
|
+
end
|
119
|
+
|
120
|
+
def invalidate(uri);
|
121
|
+
File.unlink(cache_file(uri));
|
122
|
+
end
|
123
|
+
|
124
|
+
private
|
125
|
+
|
126
|
+
def cache_entries_for(request)
|
127
|
+
if File.readable?( cache_file(request.uri) )
|
128
|
+
YAML.load_file( cache_file(request.uri) )
|
129
|
+
else
|
130
|
+
Resourceful::CacheEntryCollection.new
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def cache_file(uri)
|
135
|
+
"#{@dir}/#{uri_hash(uri)}"
|
136
|
+
end
|
137
|
+
end # class FileCacheManager
|
138
|
+
|
139
|
+
|
83
140
|
# The collection of cached entries. Nominally all the entry in a
|
84
141
|
# collection of this sort will be for the same resource but that is
|
85
142
|
# not required to be true.
|
@@ -0,0 +1,34 @@
|
|
1
|
+
|
2
|
+
module Resourceful
|
3
|
+
|
4
|
+
# This exception used to indicate that the request did not succeed.
|
5
|
+
# The HTTP response is included so that the appropriate actions can
|
6
|
+
# be taken based on the details of that response
|
7
|
+
class UnsuccessfulHttpRequestError < Exception
|
8
|
+
attr_reader :http_response, :http_request
|
9
|
+
|
10
|
+
# Initialize new error from the HTTP request and response attributes.
|
11
|
+
def initialize(http_request, http_response)
|
12
|
+
super("#{http_request.method} request to <#{http_request.uri}> failed with code #{http_response.code}")
|
13
|
+
@http_request = http_request
|
14
|
+
@http_response = http_response
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class MalformedServerResponse < UnsuccessfulHttpRequestError
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
# Exception indicating that the server used a content coding scheme
|
23
|
+
# that Resourceful is unable to handle.
|
24
|
+
class UnsupportedContentCoding < Exception
|
25
|
+
end
|
26
|
+
|
27
|
+
# Raised when a body is supplied, but not a content-type header
|
28
|
+
class MissingContentType < ArgumentError
|
29
|
+
def initialize
|
30
|
+
super("A Content-Type must be specified when an entity-body is supplied.")
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
data/lib/resourceful/header.rb
CHANGED
@@ -25,6 +25,101 @@ module Resourceful
|
|
25
25
|
def capitalize(k)
|
26
26
|
k.to_s.downcase.gsub(/^.|[-_\s]./) { |x| x.upcase }.gsub('_', '-')
|
27
27
|
end
|
28
|
+
|
29
|
+
def each_field(&blk)
|
30
|
+
to_hash.each { |k,v|
|
31
|
+
blk.call capitalize(k), v
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
HEADERS = %w[
|
36
|
+
Accept
|
37
|
+
Accept-Charset
|
38
|
+
Accept-Encoding
|
39
|
+
Accept-Language
|
40
|
+
Accept-Ranges
|
41
|
+
Age
|
42
|
+
Allow
|
43
|
+
Authorization
|
44
|
+
Cache-Control
|
45
|
+
Connection
|
46
|
+
Content-Encoding
|
47
|
+
Content-Language
|
48
|
+
Content-Length
|
49
|
+
Content-Location
|
50
|
+
Content-MD5
|
51
|
+
Content-Range
|
52
|
+
Content-Type
|
53
|
+
Date
|
54
|
+
ETag
|
55
|
+
Expect
|
56
|
+
Expires
|
57
|
+
From
|
58
|
+
Host
|
59
|
+
If-Match
|
60
|
+
If-Modified-Since
|
61
|
+
If-None-Match
|
62
|
+
If-Range
|
63
|
+
If-Unmodified-Since
|
64
|
+
Keep-Alive
|
65
|
+
Last-Modified
|
66
|
+
Location
|
67
|
+
Max-Forwards
|
68
|
+
Pragma
|
69
|
+
Proxy-Authenticate
|
70
|
+
Proxy-Authorization
|
71
|
+
Range
|
72
|
+
Referer
|
73
|
+
Retry-After
|
74
|
+
Server
|
75
|
+
TE
|
76
|
+
Trailer
|
77
|
+
Transfer-Encoding
|
78
|
+
Upgrade
|
79
|
+
User-Agent
|
80
|
+
Vary
|
81
|
+
Via
|
82
|
+
Warning
|
83
|
+
WWW-Authenticate
|
84
|
+
]
|
85
|
+
|
86
|
+
HEADERS.each do |header|
|
87
|
+
const = header.upcase.gsub('-', '_')
|
88
|
+
meth = header.downcase.gsub('-', '_')
|
89
|
+
|
90
|
+
class_eval <<-RUBY, __FILE__, __LINE__
|
91
|
+
#{const} = "#{header}".freeze # ACCEPT = "accept".freeze
|
92
|
+
|
93
|
+
def #{meth} # def accept
|
94
|
+
self[#{const}] # self[ACCEPT]
|
95
|
+
end # end
|
96
|
+
|
97
|
+
def #{meth}=(str) # def accept=(str)
|
98
|
+
self[#{const}] = str # self[ACCEPT] = str
|
99
|
+
end # end
|
100
|
+
RUBY
|
101
|
+
|
102
|
+
end
|
103
|
+
|
104
|
+
HOP_BY_HOP_HEADERS = [
|
105
|
+
CONNECTION,
|
106
|
+
KEEP_ALIVE,
|
107
|
+
PROXY_AUTHENTICATE,
|
108
|
+
PROXY_AUTHORIZATION,
|
109
|
+
TE,
|
110
|
+
TRAILER,
|
111
|
+
TRANSFER_ENCODING,
|
112
|
+
UPGRADE
|
113
|
+
].freeze
|
114
|
+
|
115
|
+
NON_MODIFIABLE_HEADERS = [
|
116
|
+
CONTENT_LOCATION,
|
117
|
+
CONTENT_MD5,
|
118
|
+
ETAG,
|
119
|
+
LAST_MODIFIED,
|
120
|
+
EXPIRES
|
121
|
+
].freeze
|
122
|
+
|
28
123
|
end
|
29
124
|
end
|
30
125
|
|
@@ -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
|
-
RESOURCEFUL_VERSION = "0.3.1"
|
32
|
-
RESOURCEFUL_USER_AGENT_TOKEN = "Resourceful/#{RESOURCEFUL_VERSION}(Ruby/#{RUBY_VERSION})"
|
33
31
|
|
34
32
|
# A logger object to which messages about the activities of this
|
35
33
|
# object will be written. This should be an object that responds
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require "resourceful/cache_manager"
|
2
2
|
|
3
3
|
require 'memcache'
|
4
4
|
require 'facets/kernel/returning'
|
@@ -58,7 +58,7 @@ module Resourceful
|
|
58
58
|
# @param [String] uri
|
59
59
|
# The uri of the resource to be invalidated
|
60
60
|
def invalidate(uri)
|
61
|
-
@memcache.delete(
|
61
|
+
@memcache.delete(uri_hash(uri))
|
62
62
|
end
|
63
63
|
|
64
64
|
|
@@ -69,17 +69,7 @@ module Resourceful
|
|
69
69
|
attr_reader :memcache
|
70
70
|
|
71
71
|
def cache_entries_for(a_request)
|
72
|
-
@memcache.get(a_request.
|
72
|
+
@memcache.get(uri_hash(a_request.uri)) || Resourceful::CacheEntryCollection.new
|
73
73
|
end
|
74
74
|
end
|
75
|
-
|
76
|
-
module MemCacheKey
|
77
|
-
def to_mc_key
|
78
|
-
Digest::MD5.hexdigest(uri)
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
class Request
|
83
|
-
include MemCacheKey
|
84
|
-
end
|
85
75
|
end
|