openlogic-resourceful 1.2.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 (47) hide show
  1. data/History.txt +45 -0
  2. data/MIT-LICENSE +21 -0
  3. data/Manifest +46 -0
  4. data/README.markdown +92 -0
  5. data/Rakefile +91 -0
  6. data/lib/resourceful.rb +27 -0
  7. data/lib/resourceful/abstract_form_data.rb +30 -0
  8. data/lib/resourceful/authentication_manager.rb +107 -0
  9. data/lib/resourceful/cache_manager.rb +242 -0
  10. data/lib/resourceful/exceptions.rb +34 -0
  11. data/lib/resourceful/header.rb +355 -0
  12. data/lib/resourceful/http_accessor.rb +103 -0
  13. data/lib/resourceful/memcache_cache_manager.rb +75 -0
  14. data/lib/resourceful/multipart_form_data.rb +46 -0
  15. data/lib/resourceful/net_http_adapter.rb +84 -0
  16. data/lib/resourceful/promiscuous_basic_authenticator.rb +18 -0
  17. data/lib/resourceful/request.rb +235 -0
  18. data/lib/resourceful/resource.rb +179 -0
  19. data/lib/resourceful/response.rb +221 -0
  20. data/lib/resourceful/simple.rb +36 -0
  21. data/lib/resourceful/stubbed_resource_proxy.rb +47 -0
  22. data/lib/resourceful/urlencoded_form_data.rb +19 -0
  23. data/lib/resourceful/util.rb +6 -0
  24. data/openlogic-resourceful.gemspec +51 -0
  25. data/resourceful.gemspec +51 -0
  26. data/spec/acceptance/authorization_spec.rb +16 -0
  27. data/spec/acceptance/caching_spec.rb +190 -0
  28. data/spec/acceptance/header_spec.rb +24 -0
  29. data/spec/acceptance/redirecting_spec.rb +12 -0
  30. data/spec/acceptance/resource_spec.rb +84 -0
  31. data/spec/acceptance/resourceful_spec.rb +56 -0
  32. data/spec/acceptance_shared_specs.rb +44 -0
  33. data/spec/caching_spec.rb +89 -0
  34. data/spec/old_acceptance_specs.rb +378 -0
  35. data/spec/resourceful/header_spec.rb +153 -0
  36. data/spec/resourceful/http_accessor_spec.rb +56 -0
  37. data/spec/resourceful/multipart_form_data_spec.rb +84 -0
  38. data/spec/resourceful/promiscuous_basic_authenticator_spec.rb +30 -0
  39. data/spec/resourceful/resource_spec.rb +20 -0
  40. data/spec/resourceful/response_spec.rb +51 -0
  41. data/spec/resourceful/urlencoded_form_data_spec.rb +64 -0
  42. data/spec/resourceful_spec.rb +79 -0
  43. data/spec/simple_sinatra_server.rb +74 -0
  44. data/spec/simple_sinatra_server_spec.rb +98 -0
  45. data/spec/spec.opts +3 -0
  46. data/spec/spec_helper.rb +31 -0
  47. metadata +192 -0
@@ -0,0 +1,45 @@
1
+ Version NEXT
2
+ ============
3
+ * Added PromiscuousBasicAuthenticator to support basic authentication
4
+ with servers that do not return challenge headers.
5
+
6
+
7
+ Version 1.0.0
8
+ ============
9
+ * Added .request() convenience method to Resourceful module.
10
+ * Added .get(), .post(), .put(), .delete() and .head() convenience method to Resourceful module
11
+
12
+ Version 0.7.0
13
+ =============
14
+ * Multiple values in a single header field are treated the same as repeated header fields. (Peter Williams)
15
+
16
+ Compatibility issues
17
+ --------------------
18
+
19
+ * The semantics of the Resourceful::Header API have changed slightly.
20
+ Previously multiple values in a single header field
21
+ (e.g. `Accept: application/xml, application/json`) where treated
22
+ differently from multiple values occurring in multiple fields (e.g
23
+ `Accept: application/xml\n\rAccept: application/json`). This lead
24
+ to some unfortunate complexity when interacting with multi-valued
25
+ fields.
26
+
27
+
28
+ Version 0.6.0
29
+ =============
30
+
31
+ * Improved support for multi-valued header fields. (Peter Williams)
32
+ * Added convenience mechanisms for making URL encoded and multipart form data requests. (Peter Williams)
33
+ * Ruby 1.9 support (Peter Williams)
34
+
35
+ Compatibility issues
36
+ --------------------
37
+
38
+ * The semantics of the Resourceful::Header API have changed slightly.
39
+ Previously, any header might return a string or an array. Now
40
+ fields are either "single-value" and always return a string, or
41
+ "multi-value" and always return an array. See API doc for more
42
+ details.
43
+
44
+
45
+
@@ -0,0 +1,21 @@
1
+ Copyright (c) 2008 Absolute-Performance, Inc
2
+ Copyright (c) 2008 Peter Williams
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining
5
+ a copy of this software and associated documentation files (the
6
+ "Software"), to deal in the Software without restriction, including
7
+ without limitation the rights to use, copy, modify, merge, publish,
8
+ distribute, sublicense, and/or sell copies of the Software, and to
9
+ permit persons to whom the Software is furnished to do so, subject to
10
+ the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,46 @@
1
+ History.txt
2
+ MIT-LICENSE
3
+ Manifest
4
+ README.markdown
5
+ Rakefile
6
+ lib/resourceful.rb
7
+ lib/resourceful/abstract_form_data.rb
8
+ lib/resourceful/authentication_manager.rb
9
+ lib/resourceful/cache_manager.rb
10
+ lib/resourceful/exceptions.rb
11
+ lib/resourceful/header.rb
12
+ lib/resourceful/http_accessor.rb
13
+ lib/resourceful/memcache_cache_manager.rb
14
+ lib/resourceful/multipart_form_data.rb
15
+ lib/resourceful/net_http_adapter.rb
16
+ lib/resourceful/promiscuous_basic_authenticator.rb
17
+ lib/resourceful/request.rb
18
+ lib/resourceful/resource.rb
19
+ lib/resourceful/response.rb
20
+ lib/resourceful/simple.rb
21
+ lib/resourceful/stubbed_resource_proxy.rb
22
+ lib/resourceful/urlencoded_form_data.rb
23
+ lib/resourceful/util.rb
24
+ openlogic-resourceful.gemspec
25
+ resourceful.gemspec
26
+ spec/acceptance/authorization_spec.rb
27
+ spec/acceptance/caching_spec.rb
28
+ spec/acceptance/header_spec.rb
29
+ spec/acceptance/redirecting_spec.rb
30
+ spec/acceptance/resource_spec.rb
31
+ spec/acceptance/resourceful_spec.rb
32
+ spec/acceptance_shared_specs.rb
33
+ spec/caching_spec.rb
34
+ spec/old_acceptance_specs.rb
35
+ spec/resourceful/header_spec.rb
36
+ spec/resourceful/http_accessor_spec.rb
37
+ spec/resourceful/multipart_form_data_spec.rb
38
+ spec/resourceful/promiscuous_basic_authenticator_spec.rb
39
+ spec/resourceful/resource_spec.rb
40
+ spec/resourceful/response_spec.rb
41
+ spec/resourceful/urlencoded_form_data_spec.rb
42
+ spec/resourceful_spec.rb
43
+ spec/simple_sinatra_server.rb
44
+ spec/simple_sinatra_server_spec.rb
45
+ spec/spec.opts
46
+ spec/spec_helper.rb
@@ -0,0 +1,92 @@
1
+ Resourceful
2
+ ===========
3
+
4
+ Resourceful provides a convenient Ruby API for making HTTP requests.
5
+
6
+ Features:
7
+
8
+ * GET, PUT, POST and DELETE HTTP requests
9
+ * HTTP Basic and Digest authentication
10
+ * HTTP Caching with pluggable backends
11
+ * Follow redirects based on the results of a callback
12
+
13
+ More Info
14
+ =========
15
+
16
+ * Source: [Github](http://github.com/paul/resourceful/tree/master)
17
+ * Bug Tracking: [Lighthouse](http://resourceful.lighthouseapp.com)
18
+ * Project Page: [Rubyforge](http://rubyforge.org/projects/resourceful/)
19
+ * Documentation: [API Docs](http://resourceful.rubyforge.org)
20
+
21
+ Examples
22
+ ========
23
+
24
+ Getting started
25
+ ---------------
26
+
27
+ gem install resourceful
28
+
29
+ Simplest example
30
+ ---------------
31
+
32
+ require 'resourceful'
33
+ resp = Resourceful.get('http://rubyforge.org')
34
+ puts resp.body
35
+
36
+ Get a page requiring HTTP Authentication
37
+ ----------------------------------------
38
+
39
+ my_realm_authenticator = Resourceful::BasicAuthenticator.new('My Realm', 'admin', 'secret')
40
+ http = Resourceful::HttpAccessor.new(:authenticator => my_realm_authenticator)
41
+ resp = http.resource('http://example.com/').get
42
+ puts resp.body
43
+
44
+ Redirection based on callback results
45
+ -------------------------------------
46
+
47
+ Resourceful will by default follow redirects on read requests (GET and HEAD), but not for
48
+ POST, etc. If you want to follow a redirect after a post, you will need to set the resource#on_redirect
49
+ callback. If the callback evaluates to true, it will follow the redirect.
50
+
51
+ resource = http.resource('http://example.com/redirect_me')
52
+ resource.on_redirect { |req, resp| resp.header['Location'] =~ /example.com/ }
53
+ resource.get # Will only follow the redirect if the new location is example.com
54
+
55
+ Post a URL encoded form
56
+ -----------------------
57
+
58
+ require 'resourceful'
59
+ http = Resourceful::HttpAccessor.new
60
+ resp = http.resource('http://mysite.example/service').
61
+ post(Resourceful::UrlencodedFormData.new(:hostname => 'test', :level => 'super'))
62
+
63
+ Post a Mulitpart form with a file
64
+ -----------------------
65
+
66
+ require 'resourceful'
67
+ http = Resourceful::HttpAccessor.new
68
+ form_data = Resourceful::MultipartFormData.new(:username => 'me')
69
+ form_data.add_file('avatar', '/tmp/my_avatar.png', 'image/png')
70
+ resp = http.resource('http://mysite.example/service').post(form_data)
71
+
72
+ Put an XML document
73
+ -------------------
74
+
75
+ require 'resourceful'
76
+ http = Resourceful::HttpAccessor.new
77
+ resp = http.resource('http://mysite.example/service').
78
+ put('<?xml version="1.0"?><test/>', :content_type => 'application/xml')
79
+
80
+ Delete a resource
81
+ -----------------
82
+
83
+ require 'resourceful'
84
+ http = Resourceful::HttpAccessor.new
85
+ resp = http.resource('http://mysite.example/service').delete
86
+
87
+
88
+ Copyright
89
+ ---------
90
+
91
+ Copyright (c) 2008 Absolute Performance, Inc, Peter Williams. Released under the MIT License.
92
+
@@ -0,0 +1,91 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'lib/resourceful'
4
+
5
+ begin
6
+ require 'echoe'
7
+
8
+ Echoe.new('openlogic-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 = "OpenLogic"
12
+ p.email = "engteam@openlogic.com"
13
+
14
+ p.ignore_pattern = ["pkg/*", "tmp/*"]
15
+ p.dependencies = [['addressable', '>= 2.1.0'], 'httpauth', ['options', '>= 2.1.1']]
16
+ p.development_dependencies = ['thin', 'yard', 'sinatra', 'rspec']
17
+ p.retain_gemspec = true
18
+ end
19
+ rescue LoadError => e
20
+ puts "install 'echoe' gem to be able to build the gem"
21
+ end
22
+
23
+ require 'spec/rake/spectask'
24
+
25
+ desc 'Run all acceptance specs'
26
+
27
+ Spec::Rake::SpecTask.new(:spec) do |t|
28
+ t.spec_opts << '--options' << 'spec/spec.opts' if File.exists?('spec/spec.opts')
29
+ t.libs << 'lib'
30
+ t.spec_files = FileList['spec/**/*_spec.rb']
31
+ end
32
+
33
+ namespace :spec do
34
+ desc 'Run all acceptance specs'
35
+ Spec::Rake::SpecTask.new(:acceptance) do |t|
36
+ t.spec_opts << '--options' << 'spec/spec.opts' if File.exists?('spec/spec.opts')
37
+ t.libs << 'lib'
38
+ t.spec_files = FileList['spec/acceptance/*_spec.rb']
39
+ end
40
+
41
+ desc 'Run all unit specs'
42
+ Spec::Rake::SpecTask.new(:unit) do |t|
43
+ t.spec_opts << '--options' << 'spec/spec.opts' if File.exists?('spec/spec.opts')
44
+ t.libs << 'lib'
45
+ t.spec_files = FileList['spec/**/*_spec.rb'] - (FileList['spec/acceptance/*_spec.rb'] + FileList['spec/simple_sinatra_server_spec.rb'] )
46
+ end
47
+
48
+ desc 'Run the specs for the server'
49
+ Spec::Rake::SpecTask.new('server') do |t|
50
+ t.spec_opts << '--options' << 'spec/spec.opts' if File.exists?('spec/spec.opts')
51
+ t.libs << 'lib'
52
+ t.spec_files = FileList['spec/simple_sinatra_server_spec.rb']
53
+ end
54
+ end
55
+
56
+ task :server do
57
+ begin
58
+ require 'spec/simple_sinatra_server'
59
+ desc "Run the sinatra echo server, with loggin"
60
+ task :server do
61
+ Sinatra::Default.set(
62
+ :run => true,
63
+ :logging => true
64
+ )
65
+ end
66
+ rescue LoadError => e
67
+ puts "Install 'sinatra' gem to run the server"
68
+ end
69
+ end
70
+
71
+ desc 'Default: Run Specs'
72
+ task :default => :spec
73
+
74
+ desc 'Run all tests'
75
+ task :test => :spec
76
+
77
+ begin
78
+ require 'yard'
79
+
80
+ desc "Generate Yardoc"
81
+ YARD::Rake::YardocTask.new do |t|
82
+ t.files = ['lib/**/*.rb', 'README.markdown']
83
+ end
84
+ rescue LoadError => e
85
+ puts "Install 'yard' gem to generate docs"
86
+ end
87
+
88
+ desc "Update rubyforge documentation"
89
+ task :update_docs => :yardoc do
90
+ puts %x{rsync -aPz doc/* psadauskas@resourceful.rubyforge.org:/var/www/gforge-projects/resourceful/}
91
+ end
@@ -0,0 +1,27 @@
1
+
2
+ __DIR__ = File.dirname(__FILE__)
3
+
4
+ $LOAD_PATH.unshift __DIR__ unless
5
+ $LOAD_PATH.include?(__DIR__) ||
6
+ $LOAD_PATH.include?(File.expand_path(__DIR__))
7
+
8
+ require 'resourceful/util'
9
+
10
+ require 'resourceful/header'
11
+ require 'resourceful/http_accessor'
12
+ require 'resourceful/simple'
13
+
14
+ module Resourceful
15
+ autoload :MultipartFormData, 'resourceful/multipart_form_data'
16
+ autoload :UrlencodedFormData, 'resourceful/urlencoded_form_data'
17
+ autoload :StubbedResourceProxy, 'resourceful/stubbed_resource_proxy'
18
+ autoload :PromiscuousBasicAuthenticator, 'resourceful/promiscuous_basic_authenticator'
19
+
20
+ extend Simple
21
+ end
22
+
23
+ # Resourceful is a library that provides a high level HTTP interface.
24
+ module Resourceful
25
+ VERSION = "1.2.0"
26
+ RESOURCEFUL_USER_AGENT_TOKEN = "openlogic-Resourceful/#{VERSION}(Ruby/#{RUBY_VERSION})"
27
+ end
@@ -0,0 +1,30 @@
1
+ module Resourceful
2
+ class AbstractFormData
3
+ def initialize(contents = {})
4
+ @form_data = []
5
+
6
+ contents.each do |k,v|
7
+ add(k, v)
8
+ end
9
+ end
10
+
11
+ # Add a name-value pair to this form data representation.
12
+ #
13
+ # @param [#to_s] name The name of the new name-value pair.
14
+ # @param [#to_s] value The value of the new name-value pair.
15
+ def add(name, value)
16
+ form_data << [name.to_s, value]
17
+ end
18
+
19
+ # Resets representation so that #read can be called again.
20
+ #
21
+ # ----
22
+ #
23
+ # Form data representations do not need to be rewound so this is a no-op.
24
+ def rewind
25
+ end
26
+
27
+ protected
28
+ attr_reader :form_data
29
+ end
30
+ end
@@ -0,0 +1,107 @@
1
+ #require 'rubygems'
2
+ require 'httpauth'
3
+ require 'addressable/uri'
4
+
5
+ module Resourceful
6
+
7
+ class AuthenticationManager
8
+ def initialize
9
+ @authenticators = []
10
+ end
11
+
12
+ def add_auth_handler(authenticator)
13
+ @authenticators << authenticator
14
+ end
15
+
16
+ def associate_auth_info(challenge)
17
+ @authenticators.each do |authenticator|
18
+ authenticator.update_credentials(challenge) if authenticator.valid_for?(challenge)
19
+ end
20
+ end
21
+
22
+ def add_credentials(request)
23
+ @authenticators.each do |authenticator|
24
+ authenticator.add_credentials_to(request) if authenticator.can_handle?(request)
25
+ end
26
+ end
27
+
28
+ end
29
+
30
+ class BasicAuthenticator
31
+
32
+ def initialize(realm, username, password)
33
+ @realm, @username, @password = realm, username, password
34
+ @domain = nil
35
+ end
36
+
37
+ def valid_for?(challenge_response)
38
+ return false unless challenge_response.header['WWW-Authenticate']
39
+
40
+ !challenge_response.header['WWW-Authenticate'].grep(/^\s*basic/i).find do |a_challenge|
41
+ @realm.downcase == /realm="([^"]+)"/i.match(a_challenge)[1].downcase
42
+ end.nil?
43
+ end
44
+
45
+ def update_credentials(challenge)
46
+ @domain = Addressable::URI.parse(challenge.uri).host
47
+ end
48
+
49
+ def can_handle?(request)
50
+ Addressable::URI.parse(request.uri).host == @domain
51
+ end
52
+
53
+ def add_credentials_to(request)
54
+ request.header['Authorization'] = credentials
55
+ end
56
+
57
+ def credentials
58
+ HTTPAuth::Basic.pack_authorization(@username, @password)
59
+ end
60
+ end
61
+
62
+ class DigestAuthenticator
63
+
64
+ attr_reader :username, :password, :realm, :domain, :challenge
65
+
66
+ def initialize(realm, username, password)
67
+ @realm = realm
68
+ @username, @password = username, password
69
+ @domain = nil
70
+ end
71
+
72
+ def update_credentials(challenge_response)
73
+ @domain = Addressable::URI.parse(challenge_response.uri).host
74
+ @challenge = HTTPAuth::Digest::Challenge.from_header(challenge_response.header['WWW-Authenticate'].first)
75
+ end
76
+
77
+ def valid_for?(challenge_response)
78
+ return false unless challenge_header = challenge_response.header['WWW-Authenticate']
79
+ begin
80
+ challenge = HTTPAuth::Digest::Challenge.from_header(challenge_header.first)
81
+ rescue HTTPAuth::UnwellformedHeader
82
+ return false
83
+ end
84
+ challenge.realm == @realm
85
+ end
86
+
87
+ def can_handle?(request)
88
+ Addressable::URI.parse(request.uri).host == @domain
89
+ end
90
+
91
+ def add_credentials_to(request)
92
+ request.header['Authorization'] = credentials_for(request)
93
+ end
94
+
95
+ def credentials_for(request)
96
+ HTTPAuth::Digest::Credentials.from_challenge(@challenge,
97
+ :username => @username,
98
+ :password => @password,
99
+ :method => request.method.to_s.upcase,
100
+ :uri => Addressable::URI.parse(request.uri).path).to_header
101
+ end
102
+
103
+ end
104
+
105
+
106
+ end
107
+