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.
Files changed (38) hide show
  1. data/Manifest +24 -28
  2. data/Rakefile +44 -14
  3. data/lib/resourceful.rb +11 -21
  4. data/lib/resourceful/authentication_manager.rb +3 -2
  5. data/lib/resourceful/cache_manager.rb +58 -1
  6. data/lib/resourceful/exceptions.rb +34 -0
  7. data/lib/resourceful/header.rb +95 -0
  8. data/lib/resourceful/http_accessor.rb +0 -2
  9. data/lib/resourceful/memcache_cache_manager.rb +3 -13
  10. data/lib/resourceful/net_http_adapter.rb +15 -5
  11. data/lib/resourceful/request.rb +180 -18
  12. data/lib/resourceful/resource.rb +38 -141
  13. data/lib/resourceful/response.rb +142 -95
  14. data/resourceful.gemspec +9 -7
  15. data/spec/acceptance/authorization_spec.rb +16 -0
  16. data/spec/acceptance/caching_spec.rb +192 -0
  17. data/spec/acceptance/header_spec.rb +24 -0
  18. data/spec/acceptance/redirecting_spec.rb +12 -0
  19. data/spec/acceptance/resource_spec.rb +84 -0
  20. data/spec/acceptance_shared_specs.rb +12 -17
  21. data/spec/{acceptance_spec.rb → old_acceptance_specs.rb} +27 -57
  22. data/spec/simple_sinatra_server.rb +74 -0
  23. data/spec/simple_sinatra_server_spec.rb +98 -0
  24. data/spec/spec_helper.rb +21 -7
  25. metadata +50 -42
  26. data/spec/resourceful/authentication_manager_spec.rb +0 -249
  27. data/spec/resourceful/cache_manager_spec.rb +0 -223
  28. data/spec/resourceful/header_spec.rb +0 -38
  29. data/spec/resourceful/http_accessor_spec.rb +0 -164
  30. data/spec/resourceful/memcache_cache_manager_spec.rb +0 -111
  31. data/spec/resourceful/net_http_adapter_spec.rb +0 -96
  32. data/spec/resourceful/options_interpreter_spec.rb +0 -102
  33. data/spec/resourceful/request_spec.rb +0 -186
  34. data/spec/resourceful/resource_spec.rb +0 -600
  35. data/spec/resourceful/response_spec.rb +0 -238
  36. data/spec/resourceful/stubbed_resource_proxy_spec.rb +0 -58
  37. data/spec/simple_http_server_shared_spec.rb +0 -162
  38. data/spec/simple_http_server_shared_spec_spec.rb +0 -212
data/Manifest CHANGED
@@ -1,34 +1,30 @@
1
- lib/resourceful.rb
2
- lib/resourceful/authentication_manager.rb
3
- lib/resourceful/util.rb
4
- lib/resourceful/resource.rb
5
- lib/resourceful/memcache_cache_manager.rb
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/cache_manager.rb
11
- lib/resourceful/options_interpreter.rb
12
- lib/resourceful/response.rb
20
+ lib/resourceful/authentication_manager.rb
13
21
  lib/resourceful/request.rb
14
- spec/acceptance_shared_specs.rb
15
- spec/spec.opts
16
- spec/acceptance_spec.rb
17
- spec/simple_http_server_shared_spec_spec.rb
18
- spec/spec_helper.rb
19
- spec/resourceful/header_spec.rb
20
- spec/resourceful/authentication_manager_spec.rb
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 'echoe'
3
+ require 'lib/resourceful'
4
4
 
5
- Echoe.new('resourceful', '0.3.1') do |p|
6
- p.description = "An HTTP library for Ruby that takes advantage of everything HTTP has to offer."
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
- p.ignore_pattern = ["pkg/*", "tmp/*"]
12
- p.dependencies = ['addressable', 'httpauth', 'rspec', 'facets', 'andand']
13
- p.development_dependencies = ['thin', 'yard']
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/**/*_spec.rb']
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
- require 'yard'
57
+ begin
58
+ require 'yard'
32
59
 
33
- desc "Generate Yardoc"
34
- YARD::Rake::YardocTask.new do |t|
35
- t.files = ['lib/**/*.rb', 'README.markdown']
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
- require 'resourceful/util'
2
+ __DIR__ = File.dirname(__FILE__)
4
3
 
5
- # Resourceful is a library that provides a high level HTTP interface.
6
- module Resourceful
4
+ $LOAD_PATH.unshift __DIR__ unless
5
+ $LOAD_PATH.include?(__DIR__) ||
6
+ $LOAD_PATH.include?(File.expand_path(__DIR__))
7
7
 
8
- HOP_BY_HOP_HEADERS = %w{
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
- NON_MODIFIABLE_HEADERS = %w{
20
- Content-Location
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
- authenticator = @authenticators.find { |authenticator| authenticator.can_handle?(request) }
24
- authenticator.add_credentials_to(request) if authenticator
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.cachable?
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
@@ -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 File.dirname(__FILE__) + "/cache_manager"
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(Digest::MD5.hexdigest(uri))
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.to_mc_key) || Resourceful::CacheEntryCollection.new
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