resourceful 0.3.1 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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