Vendor gems to make installing the plugins work properly.

The `gems:vendor` Rake task will properly pull everything needed into vendor/ruby
This commit is contained in:
R. Tyler Croy 2012-12-08 16:04:12 -08:00
parent 98fcdd5a07
commit 1c36a292c2
135 changed files with 8741 additions and 7 deletions

18
Gemfile
View File

@ -1,10 +1,16 @@
source :gemcutter
source :rubygems
gem 'rake'
gem 'heroku'
gem 'sauce'
gem 'launchy'
gem 'httparty', :path => 'vendor/httparty'
# This group is for vendoring changes for a "release"
group :vendor do
gem 'httparty'
end
group :development do
gem 'rake'
gem 'heroku'
gem 'sauce'
gem 'launchy'
end
group :test do
gem 'rspec'

8
Gemfile.deploy Normal file
View File

@ -0,0 +1,8 @@
source :rubygems
# This group is for vendoring changes for a "release"
group :vendor do
gem 'httparty'
end
# vim: ft=ruby

14
Gemfile.deploy.lock Normal file
View File

@ -0,0 +1,14 @@
GEM
remote: http://rubygems.org/
specs:
httparty (0.9.0)
multi_json (~> 1.0)
multi_xml
multi_json (1.4.0)
multi_xml (0.5.1)
PLATFORMS
ruby
DEPENDENCIES
httparty

View File

@ -7,3 +7,25 @@ RSpec::Core::RakeTask.new('spec') do |t|
end
Cucumber::Rake::Task.new('cucumber')
BUNDLER_VARS = %w(BUNDLE_GEMFILE RUBYOPT GEM_HOME)
def with_clean_env
begin
bundled_env = ENV.to_hash
BUNDLER_VARS.each{ |var| ENV.delete(var) }
yield
ensure
ENV.replace(bundled_env.to_hash)
end
end
namespace :gems do
desc 'Vendor the gems in Gemfile.deploy'
task :vendor do
with_clean_env do
sh 'bundle install --deployment --standalone=vendor --gemfile=Gemfile.deploy --path=vendor'
sh 'bundle install --no-deployment'
end
end
end

View File

@ -1 +1,2 @@
require File.expand_path(File.dirname(__FILE__) + '/vendor/bundler/setup.rb')
require 'sauce/heroku/cli'

View File

@ -1,7 +1,8 @@
require 'heroku'
require 'heroku/command/base'
require 'httparty'
require 'sauce'
require 'json'
#require 'sauce'
require 'yaml'
module Heroku

4
vendor/bundler/setup.rb vendored Normal file
View File

@ -0,0 +1,4 @@
path = File.expand_path('..', __FILE__)
$:.unshift File.expand_path("#{path}/../ruby/1.9.1/gems/multi_json-1.4.0/lib")
$:.unshift File.expand_path("#{path}/../ruby/1.9.1/gems/multi_xml-0.5.1/lib")
$:.unshift File.expand_path("#{path}/../ruby/1.9.1/gems/httparty-0.9.0/lib")

19
vendor/ruby/1.9.1/bin/httparty vendored Executable file
View File

@ -0,0 +1,19 @@
#!/usr/bin/env ruby
#
# This file was generated by RubyGems.
#
# The application 'httparty' is installed as part of a gem, and
# this file is here to facilitate running it.
#
require 'rubygems'
version = ">= 0"
if ARGV.first =~ /^_(.*)_$/ and Gem::Version.correct? $1 then
version = $1
ARGV.shift
end
gem 'httparty', version
load Gem.bin_path('httparty', 'httparty', version)

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,10 @@
Gemfile.lock
.DS_Store
.yardoc/
doc/
tmp/
log/
pkg/
*.swp
/.bundle
.rvmrc

View File

@ -0,0 +1,8 @@
language: ruby
rvm:
- 1.8.7
- ree
- 1.9.3
notifications:
email: false
bundler_args: --without development

View File

@ -0,0 +1,15 @@
source :rubygems
gemspec
gem 'rake'
gem 'cucumber', '~> 0.7'
gem 'fakeweb', '~> 1.2'
gem 'rspec', '~> 1.3'
gem 'mongrel', '1.2.0.pre2'
gem 'multi_json', '~> 1.3'
group :development do
gem 'guard'
gem 'guard-rspec'
gem 'guard-bundler'
end

View File

@ -0,0 +1,16 @@
rspec_options = {
:version => 1,
:all_after_pass => false,
:all_on_start => false,
}
guard 'rspec', rspec_options do
watch(%r{^spec/.+_spec\.rb$})
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
watch('spec/spec_helper.rb') { "spec" }
end
guard 'bundler' do
watch('Gemfile')
watch(/^.+\.gemspec/)
end

View File

@ -0,0 +1,293 @@
== 0.9.0 2012-09-07
* new
* [support for connection adapters](https://github.com/jnunemaker/httparty/pull/157)
* [allow ssl_version on ruby 1.9](https://github.com/jnunemaker/httparty/pull/159)
* bug fixes
* [don't treat port 4430 as ssl](https://github.com/jnunemaker/httparty/commit/a296b1c97f83d7dcc6ef85720a43664c265685ac)
* [deep clone default options](https://github.com/jnunemaker/httparty/commit/f74227d30f9389b4b23a888c9af49fb9b8248e1f)
* a few net digest auth fixes
== 0.8.3 2012-04-21
* new
* [lazy parsing of responses](https://github.com/jnunemaker/httparty/commit/9fd5259c8dab00e426082b66af44ede2c9068f45)
* [add support for PATCH requests](https://github.com/jnunemaker/httparty/commit/7ab6641e37a9e31517e46f6124f38c615395d38a)
* bug fixes
* [subclasses no longer override superclass options](https://github.com/jnunemaker/httparty/commit/682af8fbf672e7b3009e650da776c85cdfe78d39)
== 0.8.2 2012-04-12
* new
* add -r to make CLI return failure code if status >= 400
* allow blank username from CLI
* bug fixes
* return nil for null body
* automatically deflate responses with a Content-Encoding: x-gzip header
* Do not HEAD on POST request with digest authentication
* add support for proxy authentication
* fix posting data with CLI
* require rexml/document if xml format from CLI
* support for fragmented responses
== 0.8.1 2011-10-05
* bug fixes
* content-encoding header should be removed when automatically inflating the body
== 0.8.0 2011-09-13
* new
* switch to multi json/xml for parsing by default
* bug fixes
* fix redirects to relative uri's
== 0.7.8 2011-06-06
* bug fix
* Make response honor respond to
* net http timeout can also be a float
== 0.7.7 2011-04-16
* bug fix
* Fix NoMethodError when using the NON_RAILS_QUERY_STRING_NORMALIZER with a hash whose key is a symbol and value is nil
== 0.7.5 2011-04-16
* bug fix
* caused issue with latest rubygems
== 0.7.4 2011-02-13
* bug fixes
* Set VERIFY_NONE when using https. Ruby 1.9.2 no longer sets this for us. gh-67
== 0.7.3 2011-01-20
* bug fixes
* Fix digest auth for unspecified quality of protection (bjoernalbers, mtrudel, dwo)
== 0.7.2 2011-01-20
* bug fixes
* Fix gem dependencies
== 0.7.1 2011-01-19
* bug fixes
* Fix uninitialized constant HTTParty::Response::Net in 1.9.2 (cap10morgan)
* Other fixes for 1.9.2, full suite still fails (cap10morgan)
== 0.7.0 2011-01-18
* minor enhancements
* Added query methods for HTTP status codes, i.e. response.success?
response.created? (thanks citizenparker)
* Added support for ssl_ca_file and ssl_ca_path (dlitz)
* Allow custom query string normalization. gh-8
* Unlock private keys with password (freerange)
* Added high level request documentation (phildarnowsky)
* Added basic post example (pbuckley)
* Response object has access to its corresponding request object
* Added example of siginin into tripit.com
* Added option to follow redirects (rkj). gh-56
* bug fixes
* Fixed superclass mismatch exception while running tests
(thanks dlitz http://github.com/dlitz/httparty/commit/48224f0615b32133afcff4718ad426df7a4b401b)
== 0.6.1 2010-07-07
* minor enhancements
* updated to crack 0.1.8
* bug fixes
* subclasses always merge into the parent's default_options and
default_cookies (l4rk).
* subclasses play nicely with grand parents. gh-49
== 0.6.0 2010-06-13
* major enhancements
* Digest Auth (bartiaco, sbecker, gilles, and aaronrussell)
* Maintain HTTP method across redirects (bartiaco and sbecker)
* HTTParty::Response#response returns the Net::HTTPResponse object
* HTTParty::Response#headers returns a HTTParty::Response::Headers object
which quacks like a Hash + Net::HTTPHeader. The #headers method continues
to be backwards-compatible with the old Hash return value but may become
deprecated in the future.
* minor enhancements
* Update crack requirement to version 0.1.7
You may still get a warning because Crack's version constant is out of date
* Timeout option can be set for all requests using HTTParty.default_timeout (taazza)
* Closed #38 "headers hash should downcase keys so canonical header name can be used"
* Closed #40 "Gzip response" wherein gziped and deflated responses are
automatically inflated. (carsonmcdonald)
== 0.5.2 2010-01-31
* minor enhancements
* Update crack requirement to version 0.1.6
== 0.5.1 2010-01-30
* bug fixes
* Handle 304 response correctly by returning the HTTParty::Response object instead of redirecting (seth and hellvinz)
* Only redirect 300 responses if the header contains a Location
* Don't append empty query strings to the uri. Closes #31
* When no_follow is enabled, only raise the RedirectionTooDeep exception when a response tries redirecting. Closes #28
* major enhancements
* Removed rubygems dependency. I suggest adding rubygems to RUBYOPT if this causes problems for you.
$ export RUBYOPT='rubygems'
* HTTParty#debug_output prints debugging information for the current request (iwarshak)
* HTTParty#no_follow now available as a class-level option. Sets whether or not to follow redirects.
* minor enhancements
* HTTParty::VERSION now available
* Update crack requirement to version 0.1.5
== 0.5.0 2009-12-07
* bug fixes
* inheritable attributes no longer mutable by subclasses (yyyc514)
* namespace BasicObject within HTTParty to avoid class name collisions (eric)
* major enhancements
* Custom Parsers via class or proc
* Deprecation warning on HTTParty::AllowedFormats
moved to HTTParty::Parser::SupportedFormats
* minor enhancements
* Curl inspired output when using the binary in verbose mode (alexvollmer)
* raise UnsupportedURIScheme when scheme is not HTTP or HTTPS (djspinmonkey)
* Allow SSL for ports other than 443 when scheme is HTTPS (stefankroes)
* Accept PEM certificates via HTTParty#pem (chrislo)
* Support HEAD and OPTION verbs (grempe)
* Verify SSL certificates when providing a PEM file (collectiveidea/danielmorrison)
== 0.4.5 2009-09-12
* bug fixes
* Fixed class-level headers overwritten by cookie management code. Closes #19
* Fixed "superclass mismatch for class BlankSlate" error. Closes #20
* Fixed reading files as post data from the command line (vesan)
* minor enhancements
* Timeout option added; will raise a Timeout::Error after the timeout has elapsed (attack). Closes #17
HTTParty.get "http://github.com", :timeout => 1
* Building gem with Jeweler
== 0.4.4 2009-07-19
* 2 minor update
* :query no longer sets form data. Use body and set content type to application/x-www-form-urlencoded if you need it. :query was wrong for that.
* Fixed a bug in the cookies class method that caused cookies to be forgotten after the first request.
* Also, some general cleanup of tests and such.
== 0.4.3 2009-04-23
* 1 minor update
* added message to the response object
== 0.4.2 2009-03-30
* 2 minor changes
* response code now returns an integer instead of a string (jqr)
* rubyforge project setup for crack so i'm now depending on that instead of jnunemaker-crack
== 0.4.1 2009-03-29
* 1 minor fix
* gem 'jnunemaker-crack' instead of gem 'crack'
== 0.4.0 2009-03-29
* 1 minor change
* Switched xml and json parsing to crack (same code as before just moved to gem for easier reuse in other projects)
== 0.3.1 2009-02-10
* 1 minor fix, 1 minor enhancement
* Fixed unescaping umlauts (siebertm)
* Added yaml response parsing (Miha Filej)
== 0.3.0 2009-01-31
* 1 major enhancement, 1 bug fix
* JSON gem no longer a requirement. It was conflicting with rails json stuff so I just stole ActiveSupport's json decoding and bundled it with HTTParty.
* Fixed bug where query strings were being duplicated on redirects
* Added a bunch of specs and moved some code around.
== 0.2.10 2009-01-29
* 1 minor enhancement
* Made encoding on query parameters treat everything except URI::PATTERN::UNRESERVED as UNSAFE to force encoding of '+' character (Julian Russell)
== 0.2.9 2009-01-29
* 3 minor enhancements
* Added a 'headers' accessor to the response with a hash of any HTTP headers. (Don Peterson)
* Add support for a ":cookies" option to be used at the class level, or as an option on any individual call. It should be passed a hash, which will be converted to the proper format and added to the request headers when the call is made. (Don Peterson)
* Refactored several specs and added a full suite of cucumber features (Don Peterson)
== 0.2.8 2009-01-28
* 1 major fix
* fixed major bug with response where it wouldn't iterate or really work at all with parsed responses
== 0.2.7 2009-01-28
* 2 minor fixes, 2 minor enhancements, 2 major enhancements
* fixed undefined method add_node for nil class error that occasionally happened (juliocesar)
* Handle nil or unexpected values better when typecasting. (Brian Landau)
* More robust handling of mime types (Alex Vollmer)
* Fixed support for specifying headers and added support for basic auth to CLI. (Alex Vollmer)
* Added first class response object that includes original body and status code (Alex Vollmer)
* Now parsing all response types as some non-200 responses provide important information, this means no more exception raising (Alex Vollmer)
== 0.2.6 2009-01-05
* 1 minor bug fix
* added explicit require of time as Time#parse failed outside of rails (willcodeforfoo)
== 0.2.5 2009-01-05
* 1 major enhancement
* Add command line interface to HTTParty (Alex Vollmer)
== 0.2.4 2008-12-23
* 1 bug fix
* Fixed that mimetype detection was failing if no mimetype was returned from service (skippy)
== 0.2.3 2008-12-23
* 1 bug fix
* Fixed typecasting class variable naming issue
== 0.2.2 2008-12-08
* 1 bug fix
* Added the missing core extension hash method to_xml_attributes
== 0.2.1 2008-12-08
* 1 bug fix
* Fixed that HTTParty was borking ActiveSupport and as such Rails (thanks to Rob Sanheim)
== 0.2.0 2008-12-07
* 1 major enhancement
* Removed ActiveSupport as a dependency. Now requires json gem for json deserialization and uses an included class to do the xml parsing.
== 0.1.8 2008-11-30
* 3 major enhancements
* Moved base_uri normalization into request class and out of httparty module, fixing
the problem where base_uri was not always being normalized.
* Stupid simple support for HTTParty.get/post/put/delete. (jqr)
* Switched gem management to Echoe from newgem.
== 0.1.7 2008-11-30
* 1 major enhancement
* fixed multiple class definitions overriding each others options
== 0.1.6 2008-11-26
* 1 major enhancement
* now passing :query to set_form_data if post request to avoid content length errors
== 0.1.5 2008-11-14
* 2 major enhancements
* Refactored send request method out into its own object.
* Added :html format if you just want to do that.
== 0.1.4 2008-11-08
* 3 major enhancements:
* Removed some cruft
* Added ability to follow redirects automatically and turn that off (Alex Vollmer)
== 0.1.3 2008-08-22
* 3 major enhancements:
* Added http_proxy key for setting proxy server and port (francxk@gmail.com)
* Now raises exception when http error occurs (francxk@gmail.com)
* Changed auto format detection from file extension to response content type (Jay Pignata)
== 0.1.2 2008-08-09
* 1 major enhancement:
* default_params were not being appended to query string if option[:query] was blank
== 0.1.1 2008-07-30
* 2 major enhancement:
* Added :basic_auth key for options when making a request
* :query and :body both now work with query string or hash
== 0.1.0 2008-07-27
* 1 major enhancement:
* Initial release

View File

@ -0,0 +1,20 @@
Copyright (c) 2008 John Nunemaker
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,79 @@
# httparty
Makes http fun again!
## Install
```
gem install httparty
```
## Requirements
* multi_json and multi_xml
* You like to party!
## Examples
```ruby
# Use the class methods to get down to business quickly
response = HTTParty.get('http://twitter.com/statuses/public_timeline.json')
puts response.body, response.code, response.message, response.headers.inspect
response.each do |item|
puts item['user']['screen_name']
end
# Or wrap things up in your own class
class Twitter
include HTTParty
base_uri 'twitter.com'
def initialize(u, p)
@auth = {:username => u, :password => p}
end
# which can be :friends, :user or :public
# options[:query] can be things like since, since_id, count, etc.
def timeline(which=:friends, options={})
options.merge!({:basic_auth => @auth})
self.class.get("/statuses/#{which}_timeline.json", options)
end
def post(text)
options = { :body => {:status => text}, :basic_auth => @auth }
self.class.post('/statuses/update.json', options)
end
end
twitter = Twitter.new(config['email'], config['password'])
pp twitter.timeline
```
See the [examples directory](http://github.com/jnunemaker/httparty/tree/master/examples) for even more goodies.
## Command Line Interface
httparty also includes the executable `httparty` which can be
used to query web services and examine the resulting output. By default
it will output the response as a pretty-printed Ruby object (useful for
grokking the structure of output). This can also be overridden to output
formatted XML or JSON. Execute `httparty --help` for all the
options. Below is an example of how easy it is.
```
httparty "http://twitter.com/statuses/public_timeline.json"
```
## Help and Docs
* https://groups.google.com/forum/#!forum/httparty-gem
* http://rdoc.info/projects/jnunemaker/httparty
## Contributing
* Fork the project.
* Make your feature addition or bug fix.
* Add tests for it. This is important so I don't break it in a future version unintentionally.
* Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself in another branch so I can ignore when I pull)
* Send me a pull request. Bonus points for topic branches.

View File

@ -0,0 +1,15 @@
require 'bundler'
Bundler::GemHelper.install_tasks
require 'spec/rake/spectask'
Spec::Rake::SpecTask.new(:spec) do |spec|
spec.ruby_opts << '-rubygems'
spec.libs << 'lib' << 'spec'
spec.spec_files = FileList['spec/**/*_spec.rb']
spec.spec_opts = ['--options', 'spec/spec.opts']
end
require 'cucumber/rake/task'
Cucumber::Rake::Task.new(:features)
task :default => [:spec, :features]

View File

@ -0,0 +1,114 @@
#!/usr/bin/env ruby
require "optparse"
require "pp"
$:.unshift(File.join(File.dirname(__FILE__), "/../lib"))
require "httparty"
opts = {
:action => :get,
:headers => {},
:verbose => false
}
OptionParser.new do |o|
o.banner = "USAGE: #{$0} [options] [url]"
o.on("-f",
"--format [FORMAT]",
"Output format to use instead of pretty-print ruby: " +
"plain, json or xml") do |f|
opts[:output_format] = f.downcase.to_sym
end
o.on("-a",
"--action [ACTION]",
"HTTP action: get (default), post, put, delete, head, or options") do |a|
opts[:action] = a.downcase.to_sym
end
o.on("-d",
"--data [BODY]",
"Data to put in request body (prefix with '@' for file)") do |d|
if d =~ /^@/
opts[:body] = open(d[1..-1]).read
else
opts[:body] = d
end
end
o.on("-H", "--header [NAME:VALUE]", "Additional HTTP headers in NAME:VALUE form") do |h|
abort "Invalid header specification, should be Name:Value" unless h =~ /.+:.+/
name, value = h.split(':')
opts[:headers][name.strip] = value.strip
end
o.on("-v", "--verbose", "If set, print verbose output") do |v|
opts[:verbose] = true
end
o.on("-u", "--user [CREDS]", "Use basic authentication. Value should be user:password") do |u|
abort "Invalid credentials format. Must be user:password" unless u =~ /.*:.+/
user, password = u.split(':')
opts[:basic_auth] = { :username => user, :password => password }
end
o.on("-r", "--response-code", "Command fails if response code >= 400") do
opts[:response_code] = true
end
o.on("-h", "--help", "Show help documentation") do |h|
puts o
exit
end
end.parse!
if ARGV.empty?
STDERR.puts "You need to provide a URL"
STDERR.puts "USAGE: #{$0} [options] [url]"
end
def dump_headers(response)
resp_type = Net::HTTPResponse::CODE_TO_OBJ[response.code.to_s]
puts "#{response.code} #{resp_type.to_s.sub(/^Net::HTTP/, '')}"
response.headers.each do |n,v|
puts "#{n}: #{v}"
end
puts
end
if opts[:verbose]
puts "#{opts[:action].to_s.upcase} #{ARGV.first}"
opts[:headers].each do |n,v|
puts "#{n}: #{v}"
end
puts
end
response = HTTParty.send(opts[:action], ARGV.first, opts)
if opts[:output_format].nil?
dump_headers(response) if opts[:verbose]
pp response
else
print_format = opts[:output_format]
dump_headers(response) if opts[:verbose]
case opts[:output_format]
when :json
begin
require 'json'
puts JSON.pretty_generate(response.delegate)
rescue LoadError
puts YAML.dump(response.delegate)
end
when :xml
require 'rexml/document'
REXML::Document.new(response.body).write(STDOUT, 2)
puts
else
puts response
end
end
exit false if opts[:response_code] && response.code >= 400

View File

@ -0,0 +1 @@
default: features --format progress

View File

@ -0,0 +1,32 @@
require 'rubygems'
require 'active_support'
dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
require File.join(dir, 'httparty')
require 'pp'
config = YAML::load(File.read(File.join(ENV['HOME'], '.aaws')))
module AAWS
class Book
include HTTParty
base_uri 'http://ecs.amazonaws.com'
default_params :Service => 'AWSECommerceService', :Operation => 'ItemSearch', :SearchIndex => 'Books'
def initialize(key)
self.class.default_params :AWSAccessKeyId => key
end
def search(options={})
raise ArgumentError, 'You must search for something' if options[:query].blank?
# amazon uses nasty camelized query params
options[:query] = options[:query].inject({}) { |h, q| h[q[0].to_s.camelize] = q[1]; h }
# make a request and return the items (NOTE: this doesn't handle errors at this point)
self.class.get('/onca/xml', options)['ItemSearchResponse']['Items']
end
end
end
aaws = AAWS::Book.new(config[:access_key])
pp aaws.search(:query => {:title => 'Ruby On Rails'})

View File

@ -0,0 +1,32 @@
dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
require File.join(dir, 'httparty')
require 'pp'
# You can also use post, put, delete, head, options in the same fashion
response = HTTParty.get('http://twitter.com/statuses/public_timeline.json')
puts response.body, response.code, response.message, response.headers.inspect
response.each do |item|
puts item['user']['screen_name']
end
# An example post to a minimal rails app in the development environment
# Note that "skip_before_filter :verify_authenticity_token" must be set in the
# "pears" controller for this example
class Partay
include HTTParty
base_uri 'http://localhost:3000'
end
options = {
:body => {
:pear => { # your resource
:foo => '123', # your columns/data
:bar => 'second',
:baz => 'last thing'
}
}
}
pp Partay.post('/pears.xml', options)

View File

@ -0,0 +1,19 @@
require 'rubygems'
require 'crack'
dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
require File.join(dir, 'httparty')
require 'pp'
class Rep
include HTTParty
parser(
Proc.new do |body, format|
Crack::XML.parse(body)
end
)
end
pp Rep.get('http://whoismyrepresentative.com/whoismyrep.php?zip=46544')
pp Rep.get('http://whoismyrepresentative.com/whoismyrep.php', :query => {:zip => 46544})

View File

@ -0,0 +1,67 @@
class ParseAtom
include HTTParty
# Support Atom along with the default parsers: xml, json, yaml, etc.
class Parser::Atom < HTTParty::Parser
SupportedFormats.merge!({"application/atom+xml" => :atom})
protected
# perform atom parsing on body
def atom
body.to_atom
end
end
parser Parser::Atom
end
class OnlyParseAtom
include HTTParty
# Only support Atom
class Parser::OnlyAtom < HTTParty::Parser
SupportedFormats = {"application/atom+xml" => :atom}
protected
# perform atom parsing on body
def atom
body.to_atom
end
end
parser Parser::OnlyAtom
end
class SkipParsing
include HTTParty
# Parse the response body however you like
class Parser::Simple < HTTParty::Parser
def parse
body
end
end
parser Parser::Simple
end
class AdHocParsing
include HTTParty
parser(
Proc.new do |body, format|
case format
when :json
body.to_json
when :xml
body.to_xml
else
body
end
end
)
end

View File

@ -0,0 +1,37 @@
dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
require File.join(dir, 'httparty')
require 'pp'
config = YAML::load(File.read(File.join(ENV['HOME'], '.delicious')))
class Delicious
include HTTParty
base_uri 'https://api.del.icio.us/v1'
def initialize(u, p)
@auth = {:username => u, :password => p}
end
# query params that filter the posts are:
# tag (optional). Filter by this tag.
# dt (optional). Filter by this date (CCYY-MM-DDThh:mm:ssZ).
# url (optional). Filter by this url.
# ie: posts(:query => {:tag => 'ruby'})
def posts(options={})
options.merge!({:basic_auth => @auth})
self.class.get('/posts/get', options)
end
# query params that filter the posts are:
# tag (optional). Filter by this tag.
# count (optional). Number of items to retrieve (Default:15, Maximum:100).
def recent(options={})
options.merge!({:basic_auth => @auth})
self.class.get('/posts/recent', options)
end
end
delicious = Delicious.new(config['username'], config['password'])
pp delicious.posts(:query => {:tag => 'ruby'})
pp delicious.recent
delicious.recent['posts']['post'].each { |post| puts post['href'] }

View File

@ -0,0 +1,16 @@
dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
require File.join(dir, 'httparty')
require 'pp'
class Google
include HTTParty
format :html
end
# google.com redirects to www.google.com so this is live test for redirection
pp Google.get('http://google.com')
puts '', '*'*70, ''
# check that ssl is requesting right
pp Google.get('https://www.google.com')

View File

@ -0,0 +1,6 @@
# To send custom user agents to identify your application to a web service (or mask as a specific browser for testing), send "User-Agent" as a hash to headers as shown below.
require 'httparty'
APPLICATION_NAME = "Httparty"
response = HTTParty.get('http://example.com', :headers => {"User-Agent" => APPLICATION_NAME})

View File

@ -0,0 +1,22 @@
require 'rubygems'
require 'nokogiri'
dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
require File.join(dir, 'httparty')
require 'pp'
class HtmlParserIncluded < HTTParty::Parser
SupportedFormats.merge!('text/html' => :html)
def html
Nokogiri::HTML(body)
end
end
class Page
include HTTParty
parser HtmlParserIncluded
end
pp Page.get('http://www.google.com')

View File

@ -0,0 +1,14 @@
dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
require File.join(dir, 'httparty')
require 'pp'
class Rubyurl
include HTTParty
base_uri 'rubyurl.com'
def self.shorten( website_url )
post( '/api/links.json', :query => { :link => { :website_url => website_url } } )
end
end
pp Rubyurl.shorten( 'http://istwitterdown.com/')

View File

@ -0,0 +1,33 @@
dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
require File.join(dir, 'httparty')
class TripIt
include HTTParty
base_uri 'http://www.tripit.com'
debug_output
def initialize(email, password)
@email = email
response = self.class.get('/account/login')
response = self.class.post(
'/account/login',
:body => {
:login_email_address => email,
:login_password => password
},
:headers => {'Cookie' => response.headers['Set-Cookie']}
)
@cookie = response.request.options[:headers]['Cookie']
end
def account_settings
self.class.get('/account/edit', :headers => {'Cookie' => @cookie})
end
def logged_in?
account_settings.include? "You're logged in as #{@email}"
end
end
tripit = TripIt.new('email', 'password')
puts "Logged in: #{tripit.logged_in?}"

View File

@ -0,0 +1,31 @@
dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
require File.join(dir, 'httparty')
require 'pp'
config = YAML::load(File.read(File.join(ENV['HOME'], '.twitter')))
class Twitter
include HTTParty
base_uri 'twitter.com'
def initialize(u, p)
@auth = {:username => u, :password => p}
end
# which can be :friends, :user or :public
# options[:query] can be things like since, since_id, count, etc.
def timeline(which=:friends, options={})
options.merge!({:basic_auth => @auth})
self.class.get("/statuses/#{which}_timeline.json", options)
end
def post(text)
options = { :query => {:status => text}, :basic_auth => @auth }
self.class.post('/statuses/update.json', options)
end
end
twitter = Twitter.new(config['email'], config['password'])
pp twitter.timeline
# pp twitter.timeline(:friends, :query => {:since_id => 868482746})
# pp twitter.timeline(:friends, :query => 'since_id=868482746')
# pp twitter.post('this is a test of 0.2.0')

View File

@ -0,0 +1,10 @@
dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
require File.join(dir, 'httparty')
require 'pp'
class Rep
include HTTParty
end
pp Rep.get('http://whoismyrepresentative.com/whoismyrep.php?zip=46544')
pp Rep.get('http://whoismyrepresentative.com/whoismyrep.php', :query => {:zip => 46544})

View File

@ -0,0 +1,20 @@
Feature: Basic Authentication
As a developer
I want to be able to use a service that requires Basic Authentication
Because that is not an uncommon requirement
Scenario: Passing no credentials to a page requiring Basic Authentication
Given a restricted page at '/basic_auth.html'
When I call HTTParty#get with '/basic_auth.html'
Then it should return a response with a 401 response code
Scenario: Passing proper credentials to a page requiring Basic Authentication
Given a remote service that returns 'Authenticated Page'
And that service is accessed at the path '/basic_auth.html'
And that service is protected by Basic Authentication
And that service requires the username 'jcash' with the password 'maninblack'
When I call HTTParty#get with '/basic_auth.html' and a basic_auth hash:
| username | password |
| jcash | maninblack |
Then the return value should match 'Authenticated Page'

View File

@ -0,0 +1,7 @@
Feature: Command Line
As a developer
I want to be able to harness the power of HTTParty from the command line
Because that would make quick testing and debugging easy
And 'easy' is my middle name
And I'm kidding it's actually 'Danger'!

View File

@ -0,0 +1,26 @@
Feature: Deals with HTTP error codes
As a developer
I want to be informed of non-successful responses
Because sometimes thing explode
And I should probably know what happened
Scenario: A response of '404 - Not Found'
Given a remote service that returns a 404 status code
And that service is accessed at the path '/404_service.html'
When I call HTTParty#get with '/404_service.html'
Then it should return a response with a 404 response code
Scenario: A response of '500 - Internal Server Error'
Given a remote service that returns a 500 status code
And that service is accessed at the path '/500_service.html'
When I call HTTParty#get with '/500_service.html'
Then it should return a response with a 500 response code
Scenario: A non-successful response where I need the body
Given a remote service that returns a 400 status code
And the response from the service has a body of 'Bad response'
And that service is accessed at the path '/400_service.html'
When I call HTTParty#get with '/400_service.html'
Then it should return a response with a 400 response code
And the return value should match 'Bad response'

View File

@ -0,0 +1,20 @@
Feature: Digest Authentication
As a developer
I want to be able to use a service that requires Digest Authentication
Because that is not an uncommon requirement
Scenario: Passing no credentials to a page requiring Digest Authentication
Given a restricted page at '/digest_auth.html'
When I call HTTParty#get with '/digest_auth.html'
Then it should return a response with a 401 response code
Scenario: Passing proper credentials to a page requiring Digest Authentication
Given a remote service that returns 'Digest Authenticated Page'
And that service is accessed at the path '/digest_auth.html'
And that service is protected by Digest Authentication
And that service requires the username 'jcash' with the password 'maninblack'
When I call HTTParty#get with '/digest_auth.html' and a digest_auth hash:
| username | password |
| jcash | maninblack |
Then the return value should match 'Digest Authenticated Page'

View File

@ -0,0 +1,19 @@
Feature: Handles Compressed Responses
In order to save bandwidth
As a developer
I want to uncompress compressed responses
Scenario: Supports deflate encoding
Given a remote deflate service
And the response from the service has a body of '<h1>Some HTML</h1>'
And that service is accessed at the path '/deflate_service.html'
When I call HTTParty#get with '/deflate_service.html'
Then the return value should match '<h1>Some HTML</h1>'
Scenario: Supports gzip encoding
Given a remote gzip service
And the response from the service has a body of '<h1>Some HTML</h1>'
And that service is accessed at the path '/gzip_service.html'
When I call HTTParty#get with '/gzip_service.html'
Then the return value should match '<h1>Some HTML</h1>'

View File

@ -0,0 +1,34 @@
Feature: Handles Multiple Formats
As a developer
I want to be able to consume remote services of many different formats
And I want those formats to be automatically detected and handled
Because web services take many forms
And I don't want to have to do any extra work
Scenario: An HTML service
Given a remote service that returns '<h1>Some HTML</h1>'
And that service is accessed at the path '/html_service.html'
And the response from the service has a Content-Type of 'text/html'
When I call HTTParty#get with '/html_service.html'
Then it should return a String
And the return value should match '<h1>Some HTML</h1>'
Scenario: A JSON service
Given a remote service that returns '{ "jennings": "waylon", "cash": "johnny" }'
And that service is accessed at the path '/service.json'
And the response from the service has a Content-Type of 'application/json'
When I call HTTParty#get with '/service.json'
Then it should return a Hash equaling:
| key | value |
| jennings | waylon |
| cash | johnny |
Scenario: An XML Service
Given a remote service that returns '<singer>waylon jennings</singer>'
And that service is accessed at the path '/service.xml'
And the response from the service has a Content-Type of 'text/xml'
When I call HTTParty#get with '/service.xml'
Then it should return a Hash equaling:
| key | value |
| singer | waylon jennings |

View File

@ -0,0 +1,22 @@
require 'mongrel'
require './lib/httparty'
require 'spec/expectations'
Before do
def new_port
server = TCPServer.new('0.0.0.0', nil)
port = server.addr[1]
ensure
server.close
end
port = ENV["HTTPARTY_PORT"] || new_port
@host_and_port = "0.0.0.0:#{port}"
@server = Mongrel::HttpServer.new("0.0.0.0", port)
@server.run
@request_options = {}
end
After do
@server.stop
end

View File

@ -0,0 +1,26 @@
Then /it should return an? (\w+)$/ do |class_string|
@response_from_httparty.should be_an_instance_of(class_string.class)
end
Then /the return value should match '(.*)'/ do |expected_text|
@response_from_httparty.should eql(expected_text)
end
Then /it should return a Hash equaling:/ do |hash_table|
@response_from_httparty.should be_an_instance_of(Hash)
@response_from_httparty.keys.length.should eql(hash_table.rows.length)
hash_table.hashes.each do |pair|
key, value = pair["key"], pair["value"]
@response_from_httparty.keys.should include(key)
@response_from_httparty[key].should eql(value)
end
end
Then /it should return a response with a (\d+) response code/ do |code|
@response_from_httparty.code.should eql(code.to_i)
end
Then /it should raise (?:an|a) ([\w:]+) exception/ do |exception|
@exception_from_httparty.should_not be_nil
@exception_from_httparty.class.name.should eql(exception)
end

View File

@ -0,0 +1,27 @@
When /^I set my HTTParty timeout option to (\d+)$/ do |timeout|
@request_options[:timeout] = timeout.to_i
end
When /I call HTTParty#get with '(.*)'$/ do |url|
begin
@response_from_httparty = HTTParty.get("http://#{@host_and_port}#{url}", @request_options)
rescue HTTParty::RedirectionTooDeep, Timeout::Error => e
@exception_from_httparty = e
end
end
When /I call HTTParty#get with '(.*)' and a basic_auth hash:/ do |url, auth_table|
h = auth_table.hashes.first
@response_from_httparty = HTTParty.get(
"http://#{@host_and_port}#{url}",
:basic_auth => { :username => h["username"], :password => h["password"] }
)
end
When /I call HTTParty#get with '(.*)' and a digest_auth hash:/ do |url, auth_table|
h = auth_table.hashes.first
@response_from_httparty = HTTParty.get(
"http://#{@host_and_port}#{url}",
:digest_auth => { :username => h["username"], :password => h["password"] }
)
end

View File

@ -0,0 +1,94 @@
require 'base64'
class BasicMongrelHandler < Mongrel::HttpHandler
attr_accessor :content_type, :custom_headers, :response_body, :response_code, :preprocessor, :username, :password
def initialize
@content_type = "text/html"
@response_body = ""
@response_code = 200
@custom_headers = {}
end
def process(request, response)
instance_eval &preprocessor if preprocessor
reply_with(response, response_code, response_body)
end
def reply_with(response, code, response_body)
response.start(code) do |head, body|
head["Content-Type"] = content_type
custom_headers.each { |k,v| head[k] = v }
body.write(response_body)
end
end
end
class DeflateHandler < BasicMongrelHandler
def process(request, response)
response.start do |head, body|
head['Content-Encoding'] = 'deflate'
body.write Zlib::Deflate.deflate(response_body)
end
end
end
class GzipHandler < BasicMongrelHandler
def process(request, response)
response.start do |head, body|
head['Content-Encoding'] = 'gzip'
body.write gzip(response_body)
end
end
protected
def gzip(string)
sio = StringIO.new('', 'r+')
gz = Zlib::GzipWriter.new sio
gz.write string
gz.finish
sio.rewind
sio.read
end
end
module BasicAuthentication
def self.extended(base)
base.custom_headers["WWW-Authenticate"] = 'Basic Realm="Super Secret Page"'
end
def process(request, response)
if authorized?(request)
super
else
reply_with(response, 401, "Incorrect. You have 20 seconds to comply.")
end
end
def authorized?(request)
request.params["HTTP_AUTHORIZATION"] == "Basic " + Base64.encode64("#{@username}:#{@password}").strip
end
end
module DigestAuthentication
def self.extended(base)
base.custom_headers["WWW-Authenticate"] = 'Digest realm="testrealm@host.com",qop="auth,auth-int",nonce="nonce",opaque="opaque"'
end
def process(request, response)
if authorized?(request)
super
else
reply_with(response, 401, "Incorrect. You have 20 seconds to comply.")
end
end
def authorized?(request)
request.params["HTTP_AUTHORIZATION"] =~ /Digest.*uri=/
end
end
def new_mongrel_redirector(target_url, relative_path = false)
target_url = "http://#{@host_and_port}#{target_url}" unless relative_path
Mongrel::RedirectHandler.new(target_url)
end

View File

@ -0,0 +1,69 @@
Given /a remote service that returns '(.*)'/ do |response_body|
@handler = BasicMongrelHandler.new
Given "the response from the service has a body of '#{response_body}'"
end
Given /a remote service that returns a (\d+) status code/ do |code|
@handler = BasicMongrelHandler.new
@handler.response_code = code
end
Given /that service is accessed at the path '(.*)'/ do |path|
@server.register(path, @handler)
end
Given /^that service takes (\d+) seconds to generate a response$/ do |time|
@server_response_time = time.to_i
@handler.preprocessor = Proc.new { sleep time.to_i }
end
Given /^a remote deflate service$/ do
@handler = DeflateHandler.new
end
Given /^a remote gzip service$/ do
@handler = GzipHandler.new
end
Given /the response from the service has a Content-Type of '(.*)'/ do |content_type|
@handler.content_type = content_type
end
Given /the response from the service has a body of '(.*)'/ do |response_body|
@handler.response_body = response_body
end
Given /the url '(.*)' redirects to '(.*)'/ do |redirection_url, target_url|
@server.register redirection_url, new_mongrel_redirector(target_url)
end
Given /that service is protected by Basic Authentication/ do
@handler.extend BasicAuthentication
end
Given /that service is protected by Digest Authentication/ do
@handler.extend DigestAuthentication
end
Given /that service requires the username '(.*)' with the password '(.*)'/ do |username, password|
@handler.username = username
@handler.password = password
end
Given /a restricted page at '(.*)'/ do |url|
Given "a remote service that returns 'A response I will never see'"
And "that service is accessed at the path '#{url}'"
And "that service is protected by Basic Authentication"
And "that service requires the username 'something' with the password 'secret'"
end
# This joins the server thread, and halts cucumber, so you can actually hit the
# server with a browser. Runs until you kill it with Ctrl-c
Given /I want to hit this in a browser/ do
@server.acceptor.join
end
Then /I wait for the server to recover/ do
timeout = @request_options[:timeout] || 0
sleep @server_response_time - timeout
end

View File

@ -0,0 +1,22 @@
Feature: Supports Redirection
As a developer
I want to work with services that may redirect me
And I want it to follow a reasonable number of redirects
Because sometimes web services do that
Scenario: A service that redirects once
Given a remote service that returns 'Service Response'
And that service is accessed at the path '/landing_service.html'
And the url '/redirector.html' redirects to '/landing_service.html'
When I call HTTParty#get with '/redirector.html'
Then the return value should match 'Service Response'
# TODO: Look in to why this actually fails...
Scenario: A service that redirects to a relative URL
Scenario: A service that redirects infinitely
Given the url '/first.html' redirects to '/second.html'
And the url '/second.html' redirects to '/first.html'
When I call HTTParty#get with '/first.html'
Then it should raise an HTTParty::RedirectionTooDeep exception

View File

@ -0,0 +1,13 @@
Feature: Supports the timeout option
In order to handle inappropriately slow response times
As a developer
I want my request to raise an exception after my specified timeout as elapsed
Scenario: A long running response
Given a remote service that returns '<h1>Some HTML</h1>'
And that service is accessed at the path '/long_running_service.html'
And that service takes 2 seconds to generate a response
When I set my HTTParty timeout option to 1
And I call HTTParty#get with '/long_running_service.html'
Then it should raise a Timeout::Error exception
And I wait for the server to recover

View File

@ -0,0 +1,24 @@
# -*- encoding: utf-8 -*-
$:.push File.expand_path("../lib", __FILE__)
require "httparty/version"
Gem::Specification.new do |s|
s.name = "httparty"
s.version = HTTParty::VERSION
s.platform = Gem::Platform::RUBY
s.authors = ["John Nunemaker", "Sandro Turriate"]
s.email = ["nunemaker@gmail.com"]
s.homepage = "http://jnunemaker.github.com/httparty"
s.summary = %q{Makes http fun! Also, makes consuming restful web services dead easy.}
s.description = %q{Makes http fun! Also, makes consuming restful web services dead easy.}
s.add_dependency 'multi_json', "~> 1.0"
s.add_dependency 'multi_xml'
s.post_install_message = "When you HTTParty, you must party hard!"
s.files = `git ls-files`.split("\n")
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
s.require_paths = ["lib"]
end

View File

@ -0,0 +1,503 @@
require 'pathname'
require 'net/http'
require 'net/https'
require 'uri'
require 'zlib'
require 'multi_xml'
require 'multi_json'
require 'httparty/module_inheritable_attributes'
require 'httparty/cookie_hash'
require 'httparty/net_digest_auth'
require 'httparty/version'
require 'httparty/connection_adapter'
# @see HTTParty::ClassMethods
module HTTParty
module AllowedFormatsDeprecation
def const_missing(const)
if const.to_s =~ /AllowedFormats$/
Kernel.warn("Deprecated: Use HTTParty::Parser::SupportedFormats")
HTTParty::Parser::SupportedFormats
else
super
end
end
end
extend AllowedFormatsDeprecation
def self.included(base)
base.extend ClassMethods
base.send :include, HTTParty::ModuleInheritableAttributes
base.send(:mattr_inheritable, :default_options)
base.send(:mattr_inheritable, :default_cookies)
base.instance_variable_set("@default_options", {})
base.instance_variable_set("@default_cookies", CookieHash.new)
end
# == Common Request Options
# Request methods (get, post, patch, put, delete, head, options) all take a common set of options. These are:
#
# [:+body+:] Body of the request. If passed a Hash, will try to normalize it first, by default passing it to ActiveSupport::to_params. Any other kind of object will get used as-is.
# [:+http_proxyaddr+:] Address of proxy server to use.
# [:+http_proxyport+:] Port of proxy server to use.
# [:+http_proxyuser+:] User for proxy server authentication.
# [:+http_proxypass+:] Password for proxy server authentication.
# [:+limit+:] Maximum number of redirects to follow. Takes precedences over :+no_follow+.
# [:+query+:] Query string, or a Hash representing it. Normalized according to the same rules as :+body+. If you specify this on a POST, you must use a Hash. See also HTTParty::ClassMethods.default_params.
# [:+timeout+:] Timeout for opening connection and reading data.
#
# There are also another set of options with names corresponding to various class methods. The methods in question are those that let you set a class-wide default, and the options override the defaults on a request-by-request basis. Those options are:
# * :+base_uri+: see HTTParty::ClassMethods.base_uri.
# * :+basic_auth+: see HTTParty::ClassMethods.basic_auth. Only one of :+basic_auth+ and :+digest_auth+ can be used at a time; if you try using both, you'll get an ArgumentError.
# * :+debug_output+: see HTTParty::ClassMethods.debug_output.
# * :+digest_auth+: see HTTParty::ClassMethods.digest_auth. Only one of :+basic_auth+ and :+digest_auth+ can be used at a time; if you try using both, you'll get an ArgumentError.
# * :+format+: see HTTParty::ClassMethods.format.
# * :+headers+: see HTTParty::ClassMethods.headers. Must be a Hash.
# * :+maintain_method_across_redirects+: see HTTParty::ClassMethods.maintain_method_across_redirects.
# * :+no_follow+: see HTTParty::ClassMethods.no_follow.
# * :+parser+: see HTTParty::ClassMethods.parser.
# * :+connection_adapter+: see HTTParty::ClassMethods.connection_adapter.
# * :+pem+: see HTTParty::ClassMethods.pem.
# * :+query_string_normalizer+: see HTTParty::ClassMethods.query_string_normalizer
# * :+ssl_ca_file+: see HTTParty::ClassMethods.ssl_ca_file.
# * :+ssl_ca_path+: see HTTParty::ClassMethods.ssl_ca_path.
module ClassMethods
extend AllowedFormatsDeprecation
# Allows setting http proxy information to be used
#
# class Foo
# include HTTParty
# http_proxy 'http://foo.com', 80, 'user', 'pass'
# end
def http_proxy(addr=nil, port=nil, user=nil, pass=nil)
default_options[:http_proxyaddr] = addr
default_options[:http_proxyport] = port
default_options[:http_proxyuser] = user
default_options[:http_proxypass] = pass
end
# Allows setting a base uri to be used for each request.
# Will normalize uri to include http, etc.
#
# class Foo
# include HTTParty
# base_uri 'twitter.com'
# end
def base_uri(uri=nil)
return default_options[:base_uri] unless uri
default_options[:base_uri] = HTTParty.normalize_base_uri(uri)
end
# Allows setting basic authentication username and password.
#
# class Foo
# include HTTParty
# basic_auth 'username', 'password'
# end
def basic_auth(u, p)
default_options[:basic_auth] = {:username => u, :password => p}
end
# Allows setting digest authentication username and password.
#
# class Foo
# include HTTParty
# digest_auth 'username', 'password'
# end
def digest_auth(u, p)
default_options[:digest_auth] = {:username => u, :password => p}
end
# Do not send rails style query strings.
# Specically, don't use bracket notation when sending an array
#
# For a query:
# get '/', :query => {:selected_ids => [1,2,3]}
#
# The default query string looks like this:
# /?selected_ids[]=1&selected_ids[]=2&selected_ids[]=3
#
# Call `disable_rails_query_string_format` to transform the query string
# into:
# /?selected_ids=1&selected_ids=2&selected_ids=3
#
# @example
# class Foo
# include HTTParty
# disable_rails_query_string_format
# end
def disable_rails_query_string_format
query_string_normalizer Request::NON_RAILS_QUERY_STRING_NORMALIZER
end
# Allows setting default parameters to be appended to each request.
# Great for api keys and such.
#
# class Foo
# include HTTParty
# default_params :api_key => 'secret', :another => 'foo'
# end
def default_params(h={})
raise ArgumentError, 'Default params must be a hash' unless h.is_a?(Hash)
default_options[:default_params] ||= {}
default_options[:default_params].merge!(h)
end
# Allows setting a default timeout for all HTTP calls
# Timeout is specified in seconds.
#
# class Foo
# include HTTParty
# default_timeout 10
# end
def default_timeout(t)
raise ArgumentError, 'Timeout must be an integer or float' unless t && (t.is_a?(Integer) || t.is_a?(Float))
default_options[:timeout] = t
end
# Set an output stream for debugging, defaults to $stderr.
# The output stream is passed on to Net::HTTP#set_debug_output.
#
# class Foo
# include HTTParty
# debug_output $stderr
# end
def debug_output(stream = $stderr)
default_options[:debug_output] = stream
end
# Allows setting HTTP headers to be used for each request.
#
# class Foo
# include HTTParty
# headers 'Accept' => 'text/html'
# end
def headers(h={})
raise ArgumentError, 'Headers must be a hash' unless h.is_a?(Hash)
default_options[:headers] ||= {}
default_options[:headers].merge!(h)
end
def cookies(h={})
raise ArgumentError, 'Cookies must be a hash' unless h.is_a?(Hash)
default_cookies.add_cookies(h)
end
# Proceed to the location header when an HTTP response dictates a redirect.
# Redirects are always followed by default.
#
# @example
# class Foo
# include HTTParty
# base_uri 'http://google.com'
# follow_redirects true
# end
def follow_redirects(value = true)
default_options[:follow_redirects] = value
end
# Allows setting the format with which to parse.
# Must be one of the allowed formats ie: json, xml
#
# class Foo
# include HTTParty
# format :json
# end
def format(f = nil)
if f.nil?
default_options[:format]
else
parser(Parser) if parser.nil?
default_options[:format] = f
validate_format
end
end
# Declare whether or not to follow redirects. When true, an
# {HTTParty::RedirectionTooDeep} error will raise upon encountering a
# redirect. You can then gain access to the response object via
# HTTParty::RedirectionTooDeep#response.
#
# @see HTTParty::ResponseError#response
#
# @example
# class Foo
# include HTTParty
# base_uri 'http://google.com'
# no_follow true
# end
#
# begin
# Foo.get('/')
# rescue HTTParty::RedirectionTooDeep => e
# puts e.response.body
# end
def no_follow(value = false)
default_options[:no_follow] = value
end
# Declare that you wish to maintain the chosen HTTP method across redirects.
# The default behavior is to follow redirects via the GET method.
# If you wish to maintain the original method, you can set this option to true.
#
# @example
# class Foo
# include HTTParty
# base_uri 'http://google.com'
# maintain_method_across_redirects true
# end
def maintain_method_across_redirects(value = true)
default_options[:maintain_method_across_redirects] = value
end
# Allows setting a PEM file to be used
#
# class Foo
# include HTTParty
# pem File.read('/home/user/my.pem'), "optional password"
# end
def pem(pem_contents, password=nil)
default_options[:pem] = pem_contents
default_options[:pem_password] = password
end
# Override the way query strings are normalized.
# Helpful for overriding the default rails normalization of Array queries.
#
# For a query:
# get '/', :query => {:selected_ids => [1,2,3]}
#
# The default query string normalizer returns:
# /?selected_ids[]=1&selected_ids[]=2&selected_ids[]=3
#
# Let's change it to this:
# /?selected_ids=1&selected_ids=2&selected_ids=3
#
# Pass a Proc to the query normalizer which accepts the yielded query.
#
# @example Modifying Array query strings
# class ServiceWrapper
# include HTTParty
#
# query_string_normalizer proc { |query|
# query.map do |key, value|
# value.map {|v| "#{key}=#{v}"}
# end.join('&')
# }
# end
#
# @param [Proc] normalizer custom query string normalizer.
# @yield [Hash, String] query string
# @yieldreturn [Array] an array that will later be joined with '&'
def query_string_normalizer(normalizer)
default_options[:query_string_normalizer] = normalizer
end
# Allows setting of SSL version to use. This only works in Ruby 1.9.
# You can get a list of valid versions from OpenSSL::SSL::SSLContext::METHODS.
#
# class Foo
# include HTTParty
# ssl_version :SSLv3
# end
def ssl_version(version)
default_options[:ssl_version] = version
end
# Allows setting an OpenSSL certificate authority file
#
# class Foo
# include HTTParty
# ssl_ca_file '/etc/ssl/certs/ca-certificates.crt'
# end
def ssl_ca_file(path)
default_options[:ssl_ca_file] = path
end
# Allows setting an OpenSSL certificate authority path (directory)
#
# class Foo
# include HTTParty
# ssl_ca_path '/etc/ssl/certs/'
# end
def ssl_ca_path(path)
default_options[:ssl_ca_path] = path
end
# Allows setting a custom parser for the response.
#
# class Foo
# include HTTParty
# parser Proc.new {|data| ...}
# end
def parser(custom_parser = nil)
if custom_parser.nil?
default_options[:parser]
else
default_options[:parser] = custom_parser
validate_format
end
end
# Allows setting a custom connection_adapter for the http connections
#
# @example
# class Foo
# include HTTParty
# connection_adapter Proc.new {|uri, options| ... }
# end
#
# @example provide optional configuration for your connection_adapter
# class Foo
# include HTTParty
# connection_adapter Proc.new {|uri, options| ... }, {:foo => :bar}
# end
#
# @see HTTParty::ConnectionAdapter
def connection_adapter(custom_adapter = nil, options = nil)
if custom_adapter.nil?
default_options[:connection_adapter]
else
default_options[:connection_adapter] = custom_adapter
default_options[:connection_adapter_options] = options
end
end
# Allows making a get request to a url.
#
# class Foo
# include HTTParty
# end
#
# # Simple get with full url
# Foo.get('http://foo.com/resource.json')
#
# # Simple get with full url and query parameters
# # ie: http://foo.com/resource.json?limit=10
# Foo.get('http://foo.com/resource.json', :query => {:limit => 10})
def get(path, options={}, &block)
perform_request Net::HTTP::Get, path, options, &block
end
# Allows making a post request to a url.
#
# class Foo
# include HTTParty
# end
#
# # Simple post with full url and setting the body
# Foo.post('http://foo.com/resources', :body => {:bar => 'baz'})
#
# # Simple post with full url using :query option,
# # which gets set as form data on the request.
# Foo.post('http://foo.com/resources', :query => {:bar => 'baz'})
def post(path, options={}, &block)
perform_request Net::HTTP::Post, path, options, &block
end
# Perform a PATCH request to a path
def patch(path, options={}, &block)
perform_request Net::HTTP::Patch, path, options, &block
end
# Perform a PUT request to a path
def put(path, options={}, &block)
perform_request Net::HTTP::Put, path, options, &block
end
# Perform a DELETE request to a path
def delete(path, options={}, &block)
perform_request Net::HTTP::Delete, path, options, &block
end
# Perform a HEAD request to a path
def head(path, options={}, &block)
perform_request Net::HTTP::Head, path, options, &block
end
# Perform an OPTIONS request to a path
def options(path, options={}, &block)
perform_request Net::HTTP::Options, path, options, &block
end
def default_options #:nodoc:
@default_options
end
private
def perform_request(http_method, path, options, &block) #:nodoc:
options = default_options.dup.merge(options)
process_cookies(options)
Request.new(http_method, path, options).perform(&block)
end
def process_cookies(options) #:nodoc:
return unless options[:cookies] || default_cookies.any?
options[:headers] ||= headers.dup
options[:headers]["cookie"] = cookies.merge(options.delete(:cookies) || {}).to_cookie_string
end
def validate_format
if format && parser.respond_to?(:supports_format?) && !parser.supports_format?(format)
raise UnsupportedFormat, "'#{format.inspect}' Must be one of: #{parser.supported_formats.map{|f| f.to_s}.sort.join(', ')}"
end
end
end
def self.normalize_base_uri(url) #:nodoc:
normalized_url = url.dup
use_ssl = (normalized_url =~ /^https/) || (normalized_url =~ /:443\b/)
ends_with_slash = normalized_url =~ /\/$/
normalized_url.chop! if ends_with_slash
normalized_url.gsub!(/^https?:\/\//i, '')
"http#{'s' if use_ssl}://#{normalized_url}"
end
class Basement #:nodoc:
include HTTParty
end
def self.get(*args, &block)
Basement.get(*args, &block)
end
def self.post(*args, &block)
Basement.post(*args, &block)
end
def self.patch(*args, &block)
Basement.patch(*args, &block)
end
def self.put(*args, &block)
Basement.put(*args, &block)
end
def self.delete(*args, &block)
Basement.delete(*args, &block)
end
def self.head(*args, &block)
Basement.head(*args, &block)
end
def self.options(*args, &block)
Basement.options(*args, &block)
end
end
require 'httparty/core_extensions'
require 'httparty/hash_conversions'
require 'httparty/exceptions'
require 'httparty/parser'
require 'httparty/request'
require 'httparty/response'

View File

@ -0,0 +1,116 @@
module HTTParty
# Default connection adapter that returns a new Net::HTTP each time
#
# == Custom Connection Factories
#
# If you like to implement your own connection adapter, subclassing
# HTTPParty::ConnectionAdapter will make it easier. Just override
# the #connection method. The uri and options attributes will have
# all the info you need to construct your http connection. Whatever
# you return from your connection method needs to adhere to the
# Net::HTTP interface as this is what HTTParty expects.
#
# @example log the uri and options
# class LoggingConnectionAdapter < HTTParty::ConnectionAdapter
# def connection
# puts uri
# puts options
# Net::HTTP.new(uri)
# end
# end
#
# @example count number of http calls
# class CountingConnectionAdapter < HTTParty::ConnectionAdapter
# @@count = 0
#
# self.count
# @@count
# end
#
# def connection
# self.count += 1
# super
# end
# end
#
# === Configuration
# There is lots of configuration data available for your connection adapter
# in the #options attribute. It is up to you to interpret them within your
# connection adapter. Take a look at the implementation of
# HTTParty::ConnectionAdapter#connection for examples of how they are used.
# Something are probably interesting are as follows:
# * :+timeout+: timeout in seconds
# * :+debug_output+: see HTTParty::ClassMethods.debug_output.
# * :+pem+: contains pem data. see HTTParty::ClassMethods.pem.
# * :+ssl_ca_file+: see HTTParty::ClassMethods.ssl_ca_file.
# * :+ssl_ca_path+: see HTTParty::ClassMethods.ssl_ca_path.
# * :+connection_adapter_options+: contains the hash your passed to HTTParty.connection_adapter when you configured your connection adapter
class ConnectionAdapter
def self.call(uri, options)
new(uri, options).connection
end
attr_reader :uri, :options
def initialize(uri, options={})
raise ArgumentError, "uri must be a URI, not a #{uri.class}" unless uri.kind_of? URI
@uri = uri
@options = options
end
def connection
http = Net::HTTP.new(uri.host, uri.port, options[:http_proxyaddr], options[:http_proxyport], options[:http_proxyuser], options[:http_proxypass])
http.use_ssl = ssl_implied?(uri)
attach_ssl_certificates(http, options)
if options[:timeout] && (options[:timeout].is_a?(Integer) || options[:timeout].is_a?(Float))
http.open_timeout = options[:timeout]
http.read_timeout = options[:timeout]
end
if options[:debug_output]
http.set_debug_output(options[:debug_output])
end
return http
end
private
def ssl_implied?(uri)
uri.port == 443 || uri.instance_of?(URI::HTTPS)
end
def attach_ssl_certificates(http, options)
if http.use_ssl?
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
# Client certificate authentication
if options[:pem]
http.cert = OpenSSL::X509::Certificate.new(options[:pem])
http.key = OpenSSL::PKey::RSA.new(options[:pem], options[:pem_password])
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
end
# SSL certificate authority file and/or directory
if options[:ssl_ca_file]
http.ca_file = options[:ssl_ca_file]
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
end
if options[:ssl_ca_path]
http.ca_path = options[:ssl_ca_path]
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
end
# This is only Ruby 1.9+
if options[:ssl_version] && http.respond_to?(:ssl_version=)
http.ssl_version = options[:ssl_version]
end
end
end
end
end

View File

@ -0,0 +1,22 @@
class HTTParty::CookieHash < Hash #:nodoc:
CLIENT_COOKIES = %w{path expires domain path secure HTTPOnly}
def add_cookies(value)
case value
when Hash
merge!(value)
when String
value.split('; ').each do |cookie|
array = cookie.split('=')
self[array[0].to_sym] = array[1]
end
else
raise "add_cookies only takes a Hash or a String"
end
end
def to_cookie_string
delete_if { |k, v| CLIENT_COOKIES.include?(k.to_s) }.collect { |k, v| "#{k}=#{v}" }.join("; ")
end
end

View File

@ -0,0 +1,32 @@
module HTTParty
if defined?(::BasicObject)
BasicObject = ::BasicObject #:nodoc:
else
class BasicObject #:nodoc:
instance_methods.each { |m| undef_method m unless m =~ /^__|instance_eval/ }
end
end
unless defined?(Net::HTTP::Patch)
class Net::HTTP
def patch(path, data, initheader = nil, dest = nil, &block) #:nodoc:
res = nil
request(Patch.new(path, initheader), data) {|r|
r.read_body dest, &block
res = r
}
unless @newimpl
res.value
return res, res.body
end
res
end
class Patch < Net::HTTPRequest
METHOD = 'PATCH'
REQUEST_HAS_BODY = true
RESPONSE_HAS_BODY = true
end
end
end
end

View File

@ -0,0 +1,26 @@
module HTTParty
# Exception raised when you attempt to set a non-existant format
class UnsupportedFormat < StandardError; end
# Exception raised when using a URI scheme other than HTTP or HTTPS
class UnsupportedURIScheme < StandardError; end
# @abstract Exceptions which inherit from ResponseError contain the Net::HTTP
# response object accessible via the {#response} method.
class ResponseError < StandardError
# Returns the response of the last request
# @return [Net::HTTPResponse] A subclass of Net::HTTPResponse, e.g.
# Net::HTTPOK
attr_reader :response
# Instantiate an instance of ResponseError with a Net::HTTPResponse object
# @param [Net::HTTPResponse]
def initialize(response)
@response = response
end
end
# Exception that is raised when request has redirected too many times.
# Calling {#response} returns the Net:HTTP response object.
class RedirectionTooDeep < ResponseError; end
end

View File

@ -0,0 +1,51 @@
module HTTParty
module HashConversions
# @return <String> This hash as a query string
#
# @example
# { :name => "Bob",
# :address => {
# :street => '111 Ruby Ave.',
# :city => 'Ruby Central',
# :phones => ['111-111-1111', '222-222-2222']
# }
# }.to_params
# #=> "name=Bob&address[city]=Ruby Central&address[phones][]=111-111-1111&address[phones][]=222-222-2222&address[street]=111 Ruby Ave."
def self.to_params(hash)
params = hash.map { |k,v| normalize_param(k,v) }.join
params.chop! # trailing &
params
end
# @param key<Object> The key for the param.
# @param value<Object> The value for the param.
#
# @return <String> This key value pair as a param
#
# @example normalize_param(:name, "Bob Jones") #=> "name=Bob%20Jones&"
def self.normalize_param(key, value)
param = ''
stack = []
if value.is_a?(Array)
param << value.map { |element| normalize_param("#{key}[]", element) }.join
elsif value.is_a?(Hash)
stack << [key,value]
else
param << "#{key}=#{URI.encode(value.to_s, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))}&"
end
stack.each do |parent, hash|
hash.each do |key, value|
if value.is_a?(Hash)
stack << ["#{parent}[#{key}]", value]
else
param << normalize_param("#{parent}[#{key}]", value)
end
end
end
param
end
end
end

View File

@ -0,0 +1,44 @@
module HTTParty
module ModuleInheritableAttributes #:nodoc:
def self.included(base)
base.extend(ClassMethods)
end
# borrowed from Rails 3.2 ActiveSupport
def self.hash_deep_dup(h)
duplicate = h.dup
duplicate.each_pair do |k,v|
tv = duplicate[k]
duplicate[k] = tv.is_a?(Hash) && v.is_a?(Hash) ? hash_deep_dup(tv) : v
end
duplicate
end
module ClassMethods #:nodoc:
def mattr_inheritable(*args)
@mattr_inheritable_attrs ||= [:mattr_inheritable_attrs]
@mattr_inheritable_attrs += args
args.each do |arg|
module_eval %(class << self; attr_accessor :#{arg} end)
end
@mattr_inheritable_attrs
end
def inherited(subclass)
super
@mattr_inheritable_attrs.each do |inheritable_attribute|
ivar = "@#{inheritable_attribute}"
subclass.instance_variable_set(ivar, instance_variable_get(ivar).clone)
if instance_variable_get(ivar).respond_to?(:merge)
method = <<-EOM
def self.#{inheritable_attribute}
#{ivar} = superclass.#{inheritable_attribute}.merge ModuleInheritableAttributes.hash_deep_dup(#{ivar})
end
EOM
subclass.class_eval method
end
end
end
end
end
end

View File

@ -0,0 +1,84 @@
require 'digest/md5'
require 'net/http'
module Net
module HTTPHeader
def digest_auth(username, password, response)
@header['Authorization'] = DigestAuthenticator.new(username, password,
@method, @path, response).authorization_header
end
class DigestAuthenticator
def initialize(username, password, method, path, response_header)
@username = username
@password = password
@method = method
@path = path
@response = parse(response_header)
end
def authorization_header
@cnonce = md5(random)
header = [
%Q(Digest username="#{@username}"),
%Q(realm="#{@response['realm']}"),
%Q(nonce="#{@response['nonce']}"),
%Q(uri="#{@path}"),
%Q(response="#{request_digest}"),
]
if qop_present?
fields = [
%Q(cnonce="#{@cnonce}"),
%Q(qop="#{@response['qop']}"),
%Q(nc="00000001")
]
fields.each { |field| header << field }
end
header << %Q(opaque="#{@response['opaque']}") if opaque_present?
header
end
private
def parse(response_header)
response_header['www-authenticate'] =~ /^(\w+) (.*)/
params = {}
$2.gsub(/(\w+)="(.*?)"/) { params[$1] = $2 }
params
end
def opaque_present?
@response.has_key?('opaque') and not @response['opaque'].empty?
end
def qop_present?
@response.has_key?('qop') and not @response['qop'].empty?
end
def random
"%x" % (Time.now.to_i + rand(65535))
end
def request_digest
a = [md5(a1), @response['nonce'], md5(a2)]
a.insert(2, "00000001", @cnonce, @response['qop']) if qop_present?
md5(a.join(":"))
end
def md5(str)
Digest::MD5.hexdigest(str)
end
def a1
[@username, @response['realm'], @password].join(":")
end
def a2
[@method, @path].join(":")
end
end
end
end

View File

@ -0,0 +1,145 @@
module HTTParty
# The default parser used by HTTParty, supports xml, json, html, yaml, and
# plain text.
#
# == Custom Parsers
#
# If you'd like to do your own custom parsing, subclassing HTTParty::Parser
# will make that process much easier. There are a few different ways you can
# utilize HTTParty::Parser as a superclass.
#
# @example Intercept the parsing for all formats
# class SimpleParser < HTTParty::Parser
# def parse
# perform_parsing
# end
# end
#
# @example Add the atom format and parsing method to the default parser
# class AtomParsingIncluded < HTTParty::Parser
# SupportedFormats.merge!(
# {"application/atom+xml" => :atom}
# )
#
# def atom
# perform_atom_parsing
# end
# end
#
# @example Only support the atom format
# class ParseOnlyAtom < HTTParty::Parser
# SupportedFormats = {"application/atom+xml" => :atom}
#
# def atom
# perform_atom_parsing
# end
# end
#
# @abstract Read the Custom Parsers section for more information.
class Parser
SupportedFormats = {
'text/xml' => :xml,
'application/xml' => :xml,
'application/json' => :json,
'text/json' => :json,
'application/javascript' => :json,
'text/javascript' => :json,
'text/html' => :html,
'application/x-yaml' => :yaml,
'text/yaml' => :yaml,
'text/plain' => :plain
}
# The response body of the request
# @return [String]
attr_reader :body
# The intended parsing format for the request
# @return [Symbol] e.g. :json
attr_reader :format
# Instantiate the parser and call {#parse}.
# @param [String] body the response body
# @param [Symbol] format the response format
# @return parsed response
def self.call(body, format)
new(body, format).parse
end
# @return [Hash] the SupportedFormats hash
def self.formats
const_get(:SupportedFormats)
end
# @param [String] mimetype response MIME type
# @return [Symbol]
# @return [nil] mime type not supported
def self.format_from_mimetype(mimetype)
formats[formats.keys.detect {|k| mimetype.include?(k)}]
end
# @return [Array<Symbol>] list of supported formats
def self.supported_formats
formats.values.uniq
end
# @param [Symbol] format e.g. :json, :xml
# @return [Boolean]
def self.supports_format?(format)
supported_formats.include?(format)
end
def initialize(body, format)
@body = body
@format = format
end
# @return [Object] the parsed body
# @return [nil] when the response body is nil, an empty string, spaces only or "null"
def parse
return nil if body.nil? || body.strip.empty? || body == "null"
if supports_format?
parse_supported_format
else
body
end
end
protected
def xml
MultiXml.parse(body)
end
def json
# https://github.com/sferik/rails/commit/5e62670131dfa1718eaf21ff8dd3371395a5f1cc
if MultiJson.respond_to?(:adapter)
MultiJson.load(body)
else
MultiJson.decode(body)
end
end
def yaml
YAML.load(body)
end
def html
body
end
def plain
body
end
def supports_format?
self.class.supports_format?(format)
end
def parse_supported_format
send(format)
rescue NoMethodError => e
raise NotImplementedError, "#{self.class.name} has not implemented a parsing method for the #{format.inspect} format.", e.backtrace
end
end
end

View File

@ -0,0 +1,239 @@
module HTTParty
class Request #:nodoc:
SupportedHTTPMethods = [
Net::HTTP::Get,
Net::HTTP::Post,
Net::HTTP::Patch,
Net::HTTP::Put,
Net::HTTP::Delete,
Net::HTTP::Head,
Net::HTTP::Options
]
SupportedURISchemes = [URI::HTTP, URI::HTTPS]
NON_RAILS_QUERY_STRING_NORMALIZER = Proc.new do |query|
Array(query).map do |key, value|
if value.nil?
key.to_s
elsif value.is_a?(Array)
value.map {|v| "#{key}=#{URI.encode(v.to_s, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))}"}
else
HashConversions.to_params(key => value)
end
end.flatten.sort.join('&')
end
attr_accessor :http_method, :path, :options, :last_response, :redirect, :last_uri
def initialize(http_method, path, o={})
self.http_method = http_method
self.path = path
self.options = {
:limit => o.delete(:no_follow) ? 1 : 5,
:default_params => {},
:follow_redirects => true,
:parser => Parser,
:connection_adapter => ConnectionAdapter
}.merge(o)
end
def path=(uri)
@path = URI.parse(uri)
end
def uri
new_uri = path.relative? ? URI.parse("#{base_uri}#{path}") : path
# avoid double query string on redirects [#12]
unless redirect
new_uri.query = query_string(new_uri)
end
unless SupportedURISchemes.include? new_uri.class
raise UnsupportedURIScheme, "'#{new_uri}' Must be HTTP or HTTPS"
end
@last_uri = new_uri
end
def base_uri
redirect ? "#{@last_uri.scheme}://#{@last_uri.host}" : options[:base_uri]
end
def format
options[:format] || (format_from_mimetype(last_response['content-type']) if last_response)
end
def parser
options[:parser]
end
def connection_adapter
options[:connection_adapter]
end
def perform(&block)
validate
setup_raw_request
chunked_body = nil
self.last_response = http.request(@raw_request) do |http_response|
if block
chunks = []
http_response.read_body do |fragment|
chunks << fragment
block.call(fragment)
end
chunked_body = chunks.join
end
end
handle_deflation
handle_response(chunked_body)
end
private
def http
connection_adapter.call(uri, options)
end
def body
options[:body].is_a?(Hash) ? normalize_query(options[:body]) : options[:body]
end
def credentials
options[:basic_auth] || options[:digest_auth]
end
def username
credentials[:username]
end
def password
credentials[:password]
end
def normalize_query(query)
if query_string_normalizer
query_string_normalizer.call(query)
else
HashConversions.to_params(query)
end
end
def query_string_normalizer
options[:query_string_normalizer]
end
def setup_raw_request
@raw_request = http_method.new(uri.request_uri)
@raw_request.body = body if body
@raw_request.initialize_http_header(options[:headers])
@raw_request.basic_auth(username, password) if options[:basic_auth]
setup_digest_auth if options[:digest_auth]
end
def setup_digest_auth
auth_request = http_method.new(uri.request_uri)
auth_request.initialize_http_header(options[:headers])
res = http.request(auth_request)
if res['www-authenticate'] != nil && res['www-authenticate'].length > 0
@raw_request.digest_auth(username, password, res)
end
end
def query_string(uri)
query_string_parts = []
query_string_parts << uri.query unless uri.query.nil?
if options[:query].is_a?(Hash)
query_string_parts << normalize_query(options[:default_params].merge(options[:query]))
else
query_string_parts << normalize_query(options[:default_params]) unless options[:default_params].empty?
query_string_parts << options[:query] unless options[:query].nil?
end
query_string_parts.size > 0 ? query_string_parts.join('&') : nil
end
def handle_response(body)
if response_redirects?
options[:limit] -= 1
self.path = last_response['location']
self.redirect = true
self.http_method = Net::HTTP::Get unless options[:maintain_method_across_redirects]
capture_cookies(last_response)
perform
else
body = body || last_response.body
Response.new(self, last_response, lambda { parse_response(body) }, :body => body)
end
end
# Inspired by Ruby 1.9
def handle_deflation
case last_response["content-encoding"]
when "gzip", "x-gzip"
body_io = StringIO.new(last_response.body)
last_response.body.replace Zlib::GzipReader.new(body_io).read
last_response.delete('content-encoding')
when "deflate"
last_response.body.replace Zlib::Inflate.inflate(last_response.body)
last_response.delete('content-encoding')
end
end
def response_redirects?
case last_response
when Net::HTTPMultipleChoice, # 300
Net::HTTPMovedPermanently, # 301
Net::HTTPFound, # 302
Net::HTTPSeeOther, # 303
Net::HTTPUseProxy, # 305
Net::HTTPTemporaryRedirect
options[:follow_redirects] && last_response.key?('location')
end
end
def parse_response(body)
parser.call(body, format)
end
def capture_cookies(response)
return unless response['Set-Cookie']
cookies_hash = HTTParty::CookieHash.new()
cookies_hash.add_cookies(options[:headers]['Cookie']) if options[:headers] && options[:headers]['Cookie']
cookies_hash.add_cookies(response['Set-Cookie'])
options[:headers] ||= {}
options[:headers]['Cookie'] = cookies_hash.to_cookie_string
end
# Uses the HTTP Content-Type header to determine the format of the
# response It compares the MIME type returned to the types stored in the
# SupportedFormats hash
def format_from_mimetype(mimetype)
if mimetype && parser.respond_to?(:format_from_mimetype)
parser.format_from_mimetype(mimetype)
end
end
def validate
raise HTTParty::RedirectionTooDeep.new(last_response), 'HTTP redirects too deep' if options[:limit].to_i <= 0
raise ArgumentError, 'only get, post, patch, put, delete, head, and options methods are supported' unless SupportedHTTPMethods.include?(http_method)
raise ArgumentError, ':headers must be a hash' if options[:headers] && !options[:headers].is_a?(Hash)
raise ArgumentError, 'only one authentication method, :basic_auth or :digest_auth may be used at a time' if options[:basic_auth] && options[:digest_auth]
raise ArgumentError, ':basic_auth must be a hash' if options[:basic_auth] && !options[:basic_auth].is_a?(Hash)
raise ArgumentError, ':digest_auth must be a hash' if options[:digest_auth] && !options[:digest_auth].is_a?(Hash)
raise ArgumentError, ':query must be hash if using HTTP Post' if post? && !options[:query].nil? && !options[:query].is_a?(Hash)
end
def post?
Net::HTTP::Post == http_method
end
end
end

View File

@ -0,0 +1,62 @@
module HTTParty
class Response < HTTParty::BasicObject #:nodoc:
def self.underscore(string)
string.gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').gsub(/([a-z])([A-Z])/,'\1_\2').downcase
end
attr_reader :request, :response, :body, :headers
def initialize(request, response, parsed_block, options={})
@request = request
@response = response
@body = response.body || options[:body]
@parsed_block = parsed_block
@headers = Headers.new(response.to_hash)
end
def parsed_response
@parsed_response ||= @parsed_block.call
end
def class
Response
end
def code
response.code.to_i
end
def inspect
inspect_id = "%x" % (object_id * 2)
%(#<#{self.class}:0x#{inspect_id} parsed_response=#{parsed_response.inspect}, @response=#{response.inspect}, @headers=#{headers.inspect}>)
end
CODES_TO_OBJ = ::Net::HTTPResponse::CODE_CLASS_TO_OBJ.merge ::Net::HTTPResponse::CODE_TO_OBJ
CODES_TO_OBJ.each do |response_code, klass|
name = klass.name.sub("Net::HTTP", '')
define_method("#{underscore(name)}?") do
klass === response
end
end
def respond_to?(name)
return true if [:request, :response, :parsed_response, :body, :headers].include?(name)
parsed_response.respond_to?(name) || response.respond_to?(name)
end
protected
def method_missing(name, *args, &block)
if parsed_response.respond_to?(name)
parsed_response.send(name, *args, &block)
elsif response.respond_to?(name)
response.send(name, *args, &block)
else
super
end
end
end
end
require 'httparty/response/headers'

View File

@ -0,0 +1,31 @@
module HTTParty
class Response #:nodoc:
class Headers
include ::Net::HTTPHeader
def initialize(header = {})
@header = header
end
def ==(other)
@header == other
end
def inspect
@header.inspect
end
def method_missing(name, *args, &block)
if @header.respond_to?(name)
@header.send(name, *args, &block)
else
super
end
end
def respond_to?(method)
super || @header.respond_to?(method)
end
end
end
end

View File

@ -0,0 +1,3 @@
module HTTParty
VERSION = "0.9.0"
end

View File

@ -0,0 +1,23 @@
<posts user="jnunemaker" tag="ruby">
<post href="http://roxml.rubyforge.org/" hash="19bba2ab667be03a19f67fb67dc56917" description="ROXML - Ruby Object to XML Mapping Library" tag="ruby xml gems mapping" time="2008-08-09T05:24:20Z" others="56" extended="ROXML is a Ruby library designed to make it easier for Ruby developers to work with XML. Using simple annotations, it enables Ruby classes to be custom-mapped to XML. ROXML takes care of the marshalling and unmarshalling of mapped attributes so that developers can focus on building first-class Ruby classes."/>
<post href="http://code.google.com/p/sparrow/" hash="1df8a7cb9e8960992556518c0ea0d146" description="sparrow - Google Code" tag="ruby sparrow memcache queue" time="2008-08-06T15:07:24Z" others="115" extended="Sparrow is a really fast lightweight queue written in Ruby that speaks memcache. That means you can use Sparrow with any memcached client library (Ruby or otherwise)."/>
<post href="http://code.google.com/p/query-reviewer/" hash="963187e8bf350ae42e21eee13a2bef07" description="query-reviewer - Google Code" tag="rails ruby railstips plugins database optimization" time="2008-08-04T21:50:14Z" others="180" extended="This rails plugin not only runs &quot;EXPLAIN&quot; before each of your select queries in development, but provides a small DIV in the rendered output of each page with the summary of query warnings that it analyzed."/>
<post href="http://dev.zeraweb.com/introducing-functor" hash="2cdd545934bd37ae6f4829c51b3041c5" description="dev.zeraweb.com: Introducing Functor" tag="ruby methods gems railstips" time="2008-08-04T21:46:47Z" others="61" extended="Really cool ruby lib for overloading method definitions. I can think of a few places this would be handy."/>
<post href="http://prawn.majesticseacreature.com/" hash="92764e019de7553b4cd38017e42e4aaa" description="prawn.majesticseacreature.com" tag="pdf ruby railstips" time="2008-08-04T21:26:20Z" others="237" extended="pure ruby pdf generation library."/>
<post href="http://meme-rocket.com/2006/09/28/ruby-moduleinclude-at-odds-with-duck-typing/" hash="4ce96c7c237161819e9625737c22b462" description="Bill Burchams memeRocket :: Ruby Module#include at Odds with Duck Typing." tag="ruby railstips enumerable comparable" time="2008-08-03T16:09:24Z" others="3" extended="How to build your own enumerable and comparable objects in ruby. This article is old but just came across it and found it handy."/>
<post href="http://bl.ogtastic.com/archives/2008/7" hash="6bebd138c037d7d7c88a7046ca03f671" description="The right way to do something you should never do" tag="juggernaut observers rails flash ruby" time="2008-08-03T03:50:58Z" others="0" extended="Example of how to use juggernaut with an observer."/>
<post href="http://ncavig.com/blog/?page_id=8" hash="55450d103d6e2dd609b203ad133d751f" description="Nics Notions » Juggernaut Tutorials" tag="juggernaut rails ruby flash plugins server chat" time="2008-08-03T02:26:39Z" others="30" extended="Several juggernaut tutorials."/>
<post href="http://blog.labnotes.org/2008/05/05/distributed-twitter-client-in-20-lines-of-code/" hash="7c2a36292db109b144036a02eb3f46b7" description="Labnotes » Distributed Twitter Client in 20 lines of code" tag="xmpp ruby jabber xmpp4r" time="2008-08-01T18:16:23Z" others="18" extended="Cool little snippet of xmpp goodness to check your buddies status messages."/>
<post href="http://labs.reevoo.com/plugins/beanstalk-messaging" hash="d100c10208acbf5e954320a5577838d9" description="reevoolabs - Beanstalk Messaging" tag="railstips messaging queue rails ruby" time="2008-07-28T02:57:00Z" others="33" extended="Good write up on beanstalk"/>
<post href="http://www.slideshare.net/guest807bb2/rubyfringe?src=embed" hash="c3dc3b940dbe25e39737240b4e1ab071" description="Rockstar Memcached" tag="memcached performance caching ruby rails railstips" time="2008-07-28T02:30:50Z" others="11" extended="Killer presentation on memcached by Tobi of Shopify."/>
<post href="http://www.igvita.com/2008/07/22/unix-signals-for-live-debugging/" hash="288054a38d870b15bdf060ed5c6b2a2e" description="Unix Signals for Live Debugging - igvita.com" tag="ruby signals unix debugging signal railstips" time="2008-07-27T04:53:00Z" others="86" extended="I've known how to kill processes and such but never quite understood kill. Ilya Grigorik explains not only how to send those signals but how to use them in your scripts to change the way they behave on the fly. Very cool."/>
<post href="http://www.rubyinside.com/redcloth-4-released-962.html" hash="b3db9b84940ce550e26a560b83eb2f66" description="RedCloth 4.0 Released: 40x Faster Textile Rendering" tag="textile ruby gems railstips" time="2008-07-27T04:42:29Z" others="20" extended="Redcloth gets some serious love. It's now much faster. Sweet!"/>
<post href="http://code.google.com/p/rubycas-server/" hash="b532ea5933e4eba76c44823e17fecd31" description="rubycas-server - Google Code" tag="sso authentication cas ruby" time="2008-07-22T17:04:09Z" others="132" extended="RubyCAS-Server provides a single sign-on solution for web applications, implementing the server-end of JA-SIG's CAS protocol."/>
<post href="http://reinh.com/blog/2008/07/14/a-thinking-mans-sphinx.html" hash="033d72ac54d8c722618383e0e2aa18ff" description="ReinH — A Thinking Man's Sphinx" tag="rails railstips sphinx search ruby" time="2008-07-17T19:34:59Z" others="142" extended="A guide to the two sphynx plugins: ultrasphynx and thinksphynx and why you should choose one or the other."/>
<post href="http://www.rubyinside.com/ruby-xml-crisis-over-libxml-0-8-0-released-955.html" hash="70490d9786f09db5ba5f7904f88d304c" description="libxml-ruby 0.8.0 Released: Ruby Gets Fast, Reliable XML Processing At Last" tag="libxml xml ruby gems" time="2008-07-17T18:22:23Z" others="55" extended="lib xml gets an update and now it's really fast."/>
<post href="http://github.com/RISCfuture/autumn/tree/master" hash="9b47db4bf59da2009642f4084e3113a2" description="autumn at master — GitHub" tag="irc ruby gems" time="2008-07-17T18:20:19Z" others="18" extended="Easy, fresh, feature-rich IRC bots in Ruby"/>
<post href="http://groups.google.com/group/datamapper/browse_thread/thread/d33fbb20e41fad04" hash="4403898c92b37788f002ad6d79a66b68" description="New Finder Syntax (before 1.0) -" tag="railstips ruby datamapper" time="2008-07-05T20:42:27Z" others="1" extended="really cool idea for conditions in datamapper. even if you don't use datamapper, read this as it's sweet."/>
<post href="http://codeclimber.blogspot.com/2008/06/using-ruby-for-imap-with-gmail.html" hash="33bbf2492beac5fbf1fc167014060067" description="CodeClimber: using Ruby for IMAP with Gmail" tag="email gems gmail google imap rails railstips ruby" time="2008-07-05T20:06:47Z" others="118" extended="how to check gmail using ruby's IMAP libraries. the key is to use the login method instead of the authenticate one."/>
<post href="http://xullicious.blogspot.com/2008/07/updated-curb-multi-interface-patch.html" hash="f95dcc012bdc13bc26bace3ceed10656" description="Xul for thought: Updated curb multi interface patch" tag="curl ruby http" time="2008-07-03T21:52:45Z" others="1" extended="Really cool multi curl stuff to rapidly hit urls."/>
</posts>
<!-- fe04.api.del.ac4.yahoo.net uncompressed/chunked Sat Aug 9 00:20:11 PDT 2008 -->

View File

@ -0,0 +1,3 @@
<html><head><meta http-equiv="content-type" content="text/html; charset=ISO-8859-1"><title>Google</title><style>body,td,a,p,.h{font-family:arial,sans-serif}.h{color:#36c;font-size:20px}.q{color:#00c}.ts td{padding:0}.ts{border-collapse:collapse}#gbar{height:22px;padding-left:2px}.gbh,.gbd{border-top:1px solid #c9d7f1;font-size:1px}.gbh{height:0;position:absolute;top:24px;width:100%}#gbi,#gbs{background:#fff;left:0;position:absolute;top:24px;visibility:hidden;z-index:1000}#gbi{border:1px solid;border-color:#c9d7f1 #36c #36c #a2bae7;z-index:1001}#guser{padding-bottom:7px !important}#gbar,#guser{font-size:13px;padding-top:1px !important}@media all{.gb1,.gb3{height:22px;margin-right:.73em;vertical-align:top}#gbar{float:left}}.gb2{display:block;padding:.2em .5em}a.gb1,a.gb2,a.gb3{color:#00c !important}.gb2,.gb3{text-decoration:none}a.gb2:hover{background:#36c;color:#fff !important}</style><script>window.google={kEI:"Zuk6ScOkLKHCMrrttckF",kEXPI:"17259,19124,19314",kHL:"en"};
google.y={};google.x=function(e,g){google.y[e.id]=[e,g];return false};function sf(){document.f.q.focus()}
window.gbar={};(function(){var b=window.gbar,f,h;b.qs=function(a){var c=window.encodeURIComponent&&(document.forms[0].q||"").value;if(c)a.href=a.href.replace(/([?&])q=[^&]*|$/,function(i,g){return(g||"&")+"q="+encodeURIComponent(c)})};function j(a,c){a.visibility=h?"hidden":"visible";a.left=c+"px"}b.tg=function(a){a=a||window.event;var c=0,i,g=window.navExtra,d=document.getElementById("gbi"),e=a.target||a.srcElement;a.cancelBubble=true;if(!f){f=document.createElement(Array.every||window.createPopup?"iframe":"div");f.frameBorder="0";f.src="#";d.parentNode.appendChild(f).id="gbs";if(g)for(i in g)d.insertBefore(g[i],d.firstChild).className="gb2";document.onclick=b.close}if(e.className!="gb3")e=e.parentNode;do c+=e.offsetLeft;while(e=e.offsetParent);j(d.style,c);f.style.width=d.offsetWidth+"px";f.style.height=d.offsetHeight+"px";j(f.style,c);h=!h};b.close=function(a){h&&b.tg(a)}})();</script></head><body bgcolor=#ffffff text=#000000 link=#0000cc vlink=#551a8b alink=#ff0000 onload="sf();if(document.images)new Image().src='/images/nav_logo3.png'" topmargin=3 marginheight=3><div id=gbar><nobr><b class=gb1>Web</b> <a href="http://images.google.com/imghp?hl=en&tab=wi" onclick=gbar.qs(this) class=gb1>Images</a> <a href="http://maps.google.com/maps?hl=en&tab=wl" onclick=gbar.qs(this) class=gb1>Maps</a> <a href="http://news.google.com/nwshp?hl=en&tab=wn" onclick=gbar.qs(this) class=gb1>News</a> <a href="http://www.google.com/prdhp?hl=en&tab=wf" onclick=gbar.qs(this) class=gb1>Shopping</a> <a href="http://mail.google.com/mail/?hl=en&tab=wm" class=gb1>Gmail</a> <a href="http://www.google.com/intl/en/options/" onclick="this.blur();gbar.tg(event);return !1" class=gb3><u>more</u> <small>&#9660;</small></a><div id=gbi> <a href="http://video.google.com/?hl=en&tab=wv" onclick=gbar.qs(this) class=gb2>Video</a> <a href="http://groups.google.com/grphp?hl=en&tab=wg" onclick=gbar.qs(this) class=gb2>Groups</a> <a href="http://books.google.com/bkshp?hl=en&tab=wp" onclick=gbar.qs(this) class=gb2>Books</a> <a href="http://scholar.google.com/schhp?hl=en&tab=ws" onclick=gbar.qs(this) class=gb2>Scholar</a> <a href="http://finance.google.com/finance?hl=en&tab=we" onclick=gbar.qs(this) class=gb2>Finance</a> <a href="http://blogsearch.google.com/?hl=en&tab=wb" onclick=gbar.qs(this) class=gb2>Blogs</a> <div class=gb2><div class=gbd></div></div> <a href="http://www.youtube.com/?hl=en&tab=w1" onclick=gbar.qs(this) class=gb2>YouTube</a> <a href="http://www.google.com/calendar/render?hl=en&tab=wc" class=gb2>Calendar</a> <a href="http://picasaweb.google.com/home?hl=en&tab=wq" onclick=gbar.qs(this) class=gb2>Photos</a> <a href="http://docs.google.com/?hl=en&tab=wo" class=gb2>Documents</a> <a href="http://www.google.com/reader/view/?hl=en&tab=wy" class=gb2>Reader</a> <a href="http://sites.google.com/?hl=en&tab=w3" class=gb2>Sites</a> <div class=gb2><div class=gbd></div></div> <a href="http://www.google.com/intl/en/options/" class=gb2>even more &raquo;</a></div> </nobr></div><div class=gbh style=left:0></div><div class=gbh style=right:0></div><div align=right id=guser style="font-size:84%;padding:0 0 4px" width=100%><nobr><a href="/url?sa=p&pref=ig&pval=3&q=http://www.google.com/ig%3Fhl%3Den%26source%3Diglk&usg=AFQjCNFA18XPfgb7dKnXfKz7x7g1GDH1tg">iGoogle</a> | <a href="https://www.google.com/accounts/Login?continue=http://www.google.com/&hl=en">Sign in</a></nobr></div><center><br clear=all id=lgpd><img alt="Google" height=110 src="/intl/en_ALL/images/logo.gif" width=276><br><br><form action="/search" name=f><table cellpadding=0 cellspacing=0><tr valign=top><td width=25%>&nbsp;</td><td align=center nowrap><input name=hl type=hidden value=en><input type=hidden name=ie value="ISO-8859-1"><input autocomplete="off" maxlength=2048 name=q size=55 title="Google Search" value=""><br><input name=btnG type=submit value="Google Search"><input name=btnI type=submit value="I'm Feeling Lucky"></td><td nowrap width=25%><font size=-2>&nbsp;&nbsp;<a href=/advanced_search?hl=en>Advanced Search</a><br>&nbsp;&nbsp;<a href=/preferences?hl=en>Preferences</a><br>&nbsp;&nbsp;<a href=/language_tools?hl=en>Language Tools</a></font></td></tr></table></form><br><br><font size=-1><a href="/intl/en/ads/">Advertising&nbsp;Programs</a> - <a href="/services/">Business Solutions</a> - <a href="/intl/en/about.html">About Google</a></font><p><font size=-2>&copy;2008 - <a href="/intl/en/privacy.html">Privacy</a></font></p></center></body><script>if(google.y)google.y.first=[];window.setTimeout(function(){var xjs=document.createElement('script');xjs.src='/extern_js/f/CgJlbhICdXMgACswCjgMLCswDjgCLCswGDgDLA/8MIofMT_4o8.js';document.getElementsByTagName('head')[0].appendChild(xjs)},0);google.y.first.push(function(){google.ac.i(document.f,document.f.q,'','')})</script></html>

View File

@ -0,0 +1,29 @@
#!/bin/sh
set -e
if [ -d "generated" ] ; then
echo >&2 "error: 'generated' directory already exists. Delete it first."
exit 1
fi
mkdir generated
# Generate the CA private key and certificate
openssl req -batch -subj '/CN=INSECURE Test Certificate Authority' -newkey rsa:1024 -new -x509 -days 999999 -keyout generated/ca.key -nodes -out generated/ca.crt
# Create symlinks for ssl_ca_path
c_rehash generated
# Generate the server private key and self-signed certificate
openssl req -batch -subj '/CN=localhost' -newkey rsa:1024 -new -x509 -days 999999 -keyout generated/server.key -nodes -out generated/selfsigned.crt
# Generate certificate signing request with bogus hostname
openssl req -batch -subj '/CN=bogo' -new -days 999999 -key generated/server.key -nodes -out generated/bogushost.csr
# Sign the certificate requests
openssl x509 -CA generated/ca.crt -CAkey generated/ca.key -set_serial 1 -in generated/selfsigned.crt -out generated/server.crt -clrext -extfile openssl-exts.cnf -extensions cert -days 999999
openssl x509 -req -CA generated/ca.crt -CAkey generated/ca.key -set_serial 1 -in generated/bogushost.csr -out generated/bogushost.crt -clrext -extfile openssl-exts.cnf -extensions cert -days 999999
# Remove certificate signing requests
rm -f generated/*.csr

View File

@ -0,0 +1,16 @@
-----BEGIN CERTIFICATE-----
MIICbTCCAdagAwIBAgIJAIAeO9TXtJ45MA0GCSqGSIb3DQEBBQUAMC4xLDAqBgNV
BAMTI0lOU0VDVVJFIFRlc3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MCAXDTEwMTAy
MDEzNDYyM1oYDzQ3NDgwOTE1MTM0NjIzWjAuMSwwKgYDVQQDEyNJTlNFQ1VSRSBU
ZXN0IENlcnRpZmljYXRlIEF1dGhvcml0eTCBnzANBgkqhkiG9w0BAQEFAAOBjQAw
gYkCgYEA3lkBcd352qiIIzqnyvvJj59cx1dnzMyjnuaK2cRH420rBfukLE2MbOVr
9nYq/7CdjqXpE8uFAF+UTSIK6MWZ/bidkr2xd/et/Ce2pVIVxH+rt3pJz3wZhC3H
Yz+HU4CD2iI9wAzsb6mMV7md1fjlYfir4SBGGPTkcqUJUp2/tQMCAwEAAaOBkDCB
jTAdBgNVHQ4EFgQUy0Lz6RgmtpywlBOXdPABQArp358wXgYDVR0jBFcwVYAUy0Lz
6RgmtpywlBOXdPABQArp35+hMqQwMC4xLDAqBgNVBAMTI0lOU0VDVVJFIFRlc3Qg
Q2VydGlmaWNhdGUgQXV0aG9yaXR5ggkAgB471Ne0njkwDAYDVR0TBAUwAwEB/zAN
BgkqhkiG9w0BAQUFAAOBgQCmi3JQm+EIWjkRlyz9sijkYS+Ps4opmd/weeaXwa4E
gVBWJGyiduB+kBnfv61+/tDjlrbjBDH5dP8suczHQL8gox4zGgjw64KH4o1ujZYR
cEPbhnUpwbXu7yItlajBZfpFefjF5P0Ao2iEzQldDy0D6nQ19h5QANvQxqweTPQp
pw==
-----END CERTIFICATE-----

View File

@ -0,0 +1,13 @@
-----BEGIN CERTIFICATE-----
MIICBTCCAW6gAwIBAgIBATANBgkqhkiG9w0BAQUFADAuMSwwKgYDVQQDEyNJTlNF
Q1VSRSBUZXN0IENlcnRpZmljYXRlIEF1dGhvcml0eTAgFw0xMDEwMjAxMzQ2MjNa
GA80NzQ4MDkxNTEzNDYyM1owDzENMAsGA1UEAxMEYm9nbzCBnzANBgkqhkiG9w0B
AQEFAAOBjQAwgYkCgYEAr6b0ZBrRrVvPmPbQv36Jnj5jv00ZkhimXrmbv9Z1AdIZ
WSsBpMd8TP7exE5OR5/DaxKmiZqVskgRyRkLm52/Dkt7Ncrzr5I3unHnMqsAv/28
5fGlYoRxnkCGMse/6NOFgCemRFw/bglxPNAGrFYKStameBRbCm0dCgtlvcwzdf8C
AwEAAaNQME4wDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUddLPFtGmb0aFWbTl2kAo
xD+fd6kwHwYDVR0jBBgwFoAUy0Lz6RgmtpywlBOXdPABQArp358wDQYJKoZIhvcN
AQEFBQADgYEAosqpPVsFu6cOIhGFT85Y1wwRUaihO0vWO7ghBU5ScuRU3tuvyJDZ
Z/HoAMXV6XZjVZzRosjtPjFbyWkZYjUqJJRMyEaRiGArWe6urKLzwnD6R9O3eNa5
7bgFhzZ5WBldJmtq4A3oNqBuvgZkYM6NVKvS4UoakkTliHB21/mDOSY=
-----END CERTIFICATE-----

View File

@ -0,0 +1,16 @@
-----BEGIN CERTIFICATE-----
MIICbTCCAdagAwIBAgIJAIAeO9TXtJ45MA0GCSqGSIb3DQEBBQUAMC4xLDAqBgNV
BAMTI0lOU0VDVVJFIFRlc3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MCAXDTEwMTAy
MDEzNDYyM1oYDzQ3NDgwOTE1MTM0NjIzWjAuMSwwKgYDVQQDEyNJTlNFQ1VSRSBU
ZXN0IENlcnRpZmljYXRlIEF1dGhvcml0eTCBnzANBgkqhkiG9w0BAQEFAAOBjQAw
gYkCgYEA3lkBcd352qiIIzqnyvvJj59cx1dnzMyjnuaK2cRH420rBfukLE2MbOVr
9nYq/7CdjqXpE8uFAF+UTSIK6MWZ/bidkr2xd/et/Ce2pVIVxH+rt3pJz3wZhC3H
Yz+HU4CD2iI9wAzsb6mMV7md1fjlYfir4SBGGPTkcqUJUp2/tQMCAwEAAaOBkDCB
jTAdBgNVHQ4EFgQUy0Lz6RgmtpywlBOXdPABQArp358wXgYDVR0jBFcwVYAUy0Lz
6RgmtpywlBOXdPABQArp35+hMqQwMC4xLDAqBgNVBAMTI0lOU0VDVVJFIFRlc3Qg
Q2VydGlmaWNhdGUgQXV0aG9yaXR5ggkAgB471Ne0njkwDAYDVR0TBAUwAwEB/zAN
BgkqhkiG9w0BAQUFAAOBgQCmi3JQm+EIWjkRlyz9sijkYS+Ps4opmd/weeaXwa4E
gVBWJGyiduB+kBnfv61+/tDjlrbjBDH5dP8suczHQL8gox4zGgjw64KH4o1ujZYR
cEPbhnUpwbXu7yItlajBZfpFefjF5P0Ao2iEzQldDy0D6nQ19h5QANvQxqweTPQp
pw==
-----END CERTIFICATE-----

View File

@ -0,0 +1,15 @@
-----BEGIN RSA PRIVATE KEY-----
MIICXgIBAAKBgQDeWQFx3fnaqIgjOqfK+8mPn1zHV2fMzKOe5orZxEfjbSsF+6Qs
TYxs5Wv2dir/sJ2OpekTy4UAX5RNIgroxZn9uJ2SvbF39638J7alUhXEf6u3eknP
fBmELcdjP4dTgIPaIj3ADOxvqYxXuZ3V+OVh+KvhIEYY9ORypQlSnb+1AwIDAQAB
AoGBAL147VFCDlM1gGU865V+wIFCFQbNxedwjxGuda4io/v6oEoF6R3Tq5F0Y27v
va6Lq4fOe/LhYGI0EKU2GEPJd3F2wA21r+81InPKAkqYI5CDQtKDDNLviur8ZVKF
i3UzutjeYoCqmWeHaKPD6w5DtqeBieem7LTWRyXlFtHZV/nBAkEA8nsMOSd1+JTm
ZT4HDsEFQrN8mIFUUioFSHPut2CwzvTEW+hTkLQiog3bua4n7uQOFImR63X9qMsh
IjZRJQNmowJBAOq+mQdnRWYKl0SYb++Eb3uW6L4h1zsW375+caKo9omtpeqDW/y0
BWyY0q4DPkm3yU26Yr+b2JijISrml9/8PiECQQDHuXyG8y7jktn3GFE94NURbL+6
6gPnLX9ufzdoSjc4MDowrbtvHEDOlHWgioeP6L6EQhA0DtrhlnbzNCRARX3bAkEA
jQOsF+dwqAjKr/lGnMKY2cxgyf64NZXbGKsKhmUrnK9E0SjR9G8MJx1yyffGzi/q
bJf/xAzRw3eTcBsPtwznIQJAHq5MOK7oaUuO+6cbsZYpOYOOkKIvDLiOtdSr7LTI
DziH/fpzB0VhCmFhhEQwHhlB4t3m66A9TelHmhrCDsIaLA==
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,14 @@
-----BEGIN CERTIFICATE-----
MIICHTCCAYagAwIBAgIJALT/G+ylQljIMA0GCSqGSIb3DQEBBQUAMBQxEjAQBgNV
BAMTCWxvY2FsaG9zdDAgFw0xMDEwMjAxMzQ2MjNaGA80NzQ4MDkxNTEzNDYyM1ow
FDESMBAGA1UEAxMJbG9jYWxob3N0MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB
gQCvpvRkGtGtW8+Y9tC/fomePmO/TRmSGKZeuZu/1nUB0hlZKwGkx3xM/t7ETk5H
n8NrEqaJmpWySBHJGQubnb8OS3s1yvOvkje6cecyqwC//bzl8aVihHGeQIYyx7/o
04WAJ6ZEXD9uCXE80AasVgpK1qZ4FFsKbR0KC2W9zDN1/wIDAQABo3UwczAdBgNV
HQ4EFgQUddLPFtGmb0aFWbTl2kAoxD+fd6kwRAYDVR0jBD0wO4AUddLPFtGmb0aF
WbTl2kAoxD+fd6mhGKQWMBQxEjAQBgNVBAMTCWxvY2FsaG9zdIIJALT/G+ylQljI
MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAlOCBO54S88mD3VYviER6
V+lkd7iWmdas2wUUDeMKA9CxnirWi7ne2U7wQH/5FJ1j3ImSfjb4h/98xiVJE84e
Ld7mb61g/M4g4b62kt0HK8/cGUxfuz5zwIfi28qJq3ow6AFEq1fywbJvUAnnamwU
cZF/qoVfJhus2mXjYc4hFWg=
-----END CERTIFICATE-----

View File

@ -0,0 +1,13 @@
-----BEGIN CERTIFICATE-----
MIICCjCCAXOgAwIBAgIBATANBgkqhkiG9w0BAQUFADAuMSwwKgYDVQQDEyNJTlNF
Q1VSRSBUZXN0IENlcnRpZmljYXRlIEF1dGhvcml0eTAgFw0xMDEwMjAxMzQ2MjNa
GA80NzQ4MDkxNTEzNDYyM1owFDESMBAGA1UEAxMJbG9jYWxob3N0MIGfMA0GCSqG
SIb3DQEBAQUAA4GNADCBiQKBgQCvpvRkGtGtW8+Y9tC/fomePmO/TRmSGKZeuZu/
1nUB0hlZKwGkx3xM/t7ETk5Hn8NrEqaJmpWySBHJGQubnb8OS3s1yvOvkje6cecy
qwC//bzl8aVihHGeQIYyx7/o04WAJ6ZEXD9uCXE80AasVgpK1qZ4FFsKbR0KC2W9
zDN1/wIDAQABo1AwTjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBR10s8W0aZvRoVZ
tOXaQCjEP593qTAfBgNVHSMEGDAWgBTLQvPpGCa2nLCUE5d08AFACunfnzANBgkq
hkiG9w0BAQUFAAOBgQCR4Oor0YAvK0tNFrOLtqmC6D0F5IYCyu7komk7JGn9L4nn
7VyVxd4MXdc1r1v+WP5JtnA9ZjMmEmH9gl4gwR/Cu+TMkArsq0Z8mREOLNL8pwpx
Zxgk0CwacYR9RQcpuJ9nSDzVoO5ecYkb5C9q7gwgqbmCzr7oz/rwTqRwiUZCVQ==
-----END CERTIFICATE-----

View File

@ -0,0 +1,15 @@
-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQCvpvRkGtGtW8+Y9tC/fomePmO/TRmSGKZeuZu/1nUB0hlZKwGk
x3xM/t7ETk5Hn8NrEqaJmpWySBHJGQubnb8OS3s1yvOvkje6cecyqwC//bzl8aVi
hHGeQIYyx7/o04WAJ6ZEXD9uCXE80AasVgpK1qZ4FFsKbR0KC2W9zDN1/wIDAQAB
AoGALIdgkTgTS6VovVhklwcXEBy04LxE7Tp+gqj/COTvCKUgc/BpHELOCh7ajl1j
jti7i5tQyLV9mZKXn6lPvgWBd0w+p6VhM4NFA97CoodEJm2ckFC9zUABCh9dOpbm
8KzF7hdpYWgJJchwwZ60tbcP7K1DkiNX6Kk9qKQEWvitMBECQQDpOSzzLldcEU9l
ze/nG2+rf6ecaPnKeafY8R2qVils8I7ZJAW3+0bNT5gQs7rT7aWo8vMvrXq++lWb
JkNV6hK9AkEAwM5wsmg7REmAaDwgUBq5mNt963/uG2ihAODFS70lYT23UYl5Y3rD
s3qU4ntG4DvWIQgPdwdstzDh9fMBVXa1awJBAID1WoOE5k1ETRDP1I2HwDGmPnng
Ge75YfQ1LuAXEITqZzJuFrNqv/Waw0zI9M9moqlO3WVJmYusRFWrzKPe8EkCQEwC
FlN+275z63csHOD3aCtmfCGW8VtEyBP8iErvagkHt3khZQVepD/hF0ihqLNFY4jq
EI6wEp+1WZ8ICYKTpbkCQQDhl5QLdy5Xo3k3agCnB9nktSzs2iqFvsGvfOAW4628
iThKTNua6bBvbdiG0Vh2Sv0XBYVJoHB3WnTVgFyPJaaF
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,9 @@
[ca]
basicConstraints=critical,CA:true
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid:always,issuer:always
[cert]
basicConstraints=critical,CA:false
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,403 @@
<statuses type="array">
<status>
<created_at>Sun Dec 07 00:36:16 +0000 2008</created_at>
<id>1042729116</id>
<text>@sebbo Outlook not so good</text>
<source>web</source>
<truncated>false</truncated>
<in_reply_to_status_id>1042716367</in_reply_to_status_id>
<in_reply_to_user_id>2989541</in_reply_to_user_id>
<favorited>false</favorited>
<user>
<id>17656026</id>
<name>Magic 8 Bot</name>
<screen_name>magic8bot</screen_name>
<description>ask me a question</description>
<location></location>
<profile_image_url>http://s3.amazonaws.com/twitter_production/profile_images/65565851/8ball_large_normal.jpg</profile_image_url>
<url></url>
<protected>false</protected>
<followers_count>90</followers_count>
</user>
</status><status>
<created_at>Sun Dec 07 00:36:14 +0000 2008</created_at>
<id>1042729115</id>
<text>Azdel Slade :friends from midian city http://bloghud.com/id/27312</text>
<source>web</source>
<truncated>false</truncated>
<in_reply_to_status_id />
<in_reply_to_user_id />
<favorited>false</favorited>
<user>
<id>801094</id>
<name>BlogHUD</name>
<screen_name>bloghud</screen_name>
<description />
<location />
<profile_image_url>http://s3.amazonaws.com/twitter_production/profile_images/25235272/bloghud_twitter_normal.jpg</profile_image_url>
<url />
<protected>false</protected>
<followers_count>313</followers_count>
</user>
</status><status>
<created_at>Sun Dec 07 00:36:14 +0000 2008</created_at>
<id>1042729114</id>
<text>Reading: &quot;The Reckoning - Debt Watchdogs - Tamed or Caught Napping? - Series - NYTimes.com&quot; ( http://tinyurl.com/5754s6 )</text>
<source>twitthat</source>
<truncated>false</truncated>
<in_reply_to_status_id />
<in_reply_to_user_id />
<favorited>false</favorited>
<user>
<id>3512101</id>
<name>bill</name>
<screen_name>niubi</screen_name>
<description>in beijing learning socialism 2 prepare 4 return 2 us</description>
<location>beijing</location>
<profile_image_url>http://s3.amazonaws.com/twitter_production/profile_images/53292616/realtwitterers_normal.jpg</profile_image_url>
<url></url>
<protected>false</protected>
<followers_count>710</followers_count>
</user>
</status><status>
<created_at>Sun Dec 07 00:36:14 +0000 2008</created_at>
<id>1042729113</id>
<text>Fianlly done and headed home!</text>
<source>sms</source>
<truncated>false</truncated>
<in_reply_to_status_id />
<in_reply_to_user_id />
<favorited>false</favorited>
<user>
<id>13186842</id>
<name>Okthirddayfan</name>
<screen_name>Okthirddayfan</screen_name>
<description></description>
<location>Oklahoma!</location>
<profile_image_url>http://s3.amazonaws.com/twitter_production/profile_images/61414367/mecropped_normal.jpg</profile_image_url>
<url>http://thirddaypix.blogspot.com/</url>
<protected>false</protected>
<followers_count>68</followers_count>
</user>
</status><status>
<created_at>Sun Dec 07 00:36:16 +0000 2008</created_at>
<id>1042729112</id>
<text>Adobe Flashplayer 10 and Debug versions: http://www.adobe.com/support/flashplayer/downloads.html</text>
<source>toro</source>
<truncated>false</truncated>
<in_reply_to_status_id />
<in_reply_to_user_id />
<favorited>false</favorited>
<user>
<id>15243380</id>
<name>cbrueggenolte</name>
<screen_name>cbrueggenolte</screen_name>
<description>27, Male, Mac Geek, Developer Java &amp;amp; Flex &amp;amp; Air</description>
<location>Aachen</location>
<profile_image_url>http://s3.amazonaws.com/twitter_production/profile_images/55929263/214508011845f026bfd407c_normal.jpg</profile_image_url>
<url>http://my.opera.com/carstenbrueggenolte</url>
<protected>false</protected>
<followers_count>16</followers_count>
</user>
</status><status>
<created_at>Sun Dec 07 00:36:14 +0000 2008</created_at>
<id>1042729111</id>
<text>Done and done.</text>
<source>twitterrific</source>
<truncated>false</truncated>
<in_reply_to_status_id />
<in_reply_to_user_id />
<favorited>false</favorited>
<user>
<id>10978752</id>
<name>Sergey Safonov</name>
<screen_name>iron_Lung</screen_name>
<description>I have my fingers in many pies.</description>
<location>Moscow</location>
<profile_image_url>http://s3.amazonaws.com/twitter_production/profile_images/57841057/eat38_normal.gif</profile_image_url>
<url>http://www.flickr.com/photos/iron_Lung</url>
<protected>false</protected>
<followers_count>11</followers_count>
</user>
</status><status>
<created_at>Sun Dec 07 00:36:14 +0000 2008</created_at>
<id>1042729110</id>
<text>Veja a tabela de preços do Acquaplay da Tecnisa aqui:http://tinyurl.com/acquaplaypreco</text>
<source>web</source>
<truncated>false</truncated>
<in_reply_to_status_id />
<in_reply_to_user_id />
<favorited>false</favorited>
<user>
<id>13735402</id>
<name>Tecnisa S.A</name>
<screen_name>Tecnisa</screen_name>
<description>Mais construtora por m2</description>
<location>Faria Lima, 3144 - SP</location>
<profile_image_url>http://s3.amazonaws.com/twitter_production/profile_images/56572222/logo_normal.jpg</profile_image_url>
<url>http://www.tecnisa.com.br</url>
<protected>false</protected>
<followers_count>77</followers_count>
</user>
</status><status>
<created_at>Sun Dec 07 00:36:16 +0000 2008</created_at>
<id>1042729108</id>
<text>devin harris has the flu. always have the memory of jordan scoring 50 on the knicks at the garden with the flu</text>
<source>web</source>
<truncated>false</truncated>
<in_reply_to_status_id />
<in_reply_to_user_id />
<favorited>false</favorited>
<user>
<id>17930773</id>
<name>24 Seconds to Shoot</name>
<screen_name>NBA24sts</screen_name>
<description>NBA handicapping insight and analysis.</description>
<location>las vegas</location>
<profile_image_url>http://s3.amazonaws.com/twitter_production/profile_images/66593862/fresno_normal.jpg</profile_image_url>
<url></url>
<protected>false</protected>
<followers_count>1</followers_count>
</user>
</status><status>
<created_at>Sun Dec 07 00:36:16 +0000 2008</created_at>
<id>1042729105</id>
<text>At Brandon and Shannon's holiday party..</text>
<source>twitterberry</source>
<truncated>false</truncated>
<in_reply_to_status_id />
<in_reply_to_user_id />
<favorited>false</favorited>
<user>
<id>5388602</id>
<name>Mike J. (Telligent)</name>
<screen_name>mjamrst</screen_name>
<description>Video Game Account Manager</description>
<location>Palo Alto, CA</location>
<profile_image_url>http://s3.amazonaws.com/twitter_production/profile_images/66375174/Thanksgiving_11272008-048_normal.jpg</profile_image_url>
<url>http://www.telligent.com</url>
<protected>false</protected>
<followers_count>225</followers_count>
</user>
</status><status>
<created_at>Sun Dec 07 00:36:13 +0000 2008</created_at>
<id>1042729104</id>
<text>Xinhua: Forty percent of Australian PM office' staff quit : CANBERRA, Dec. 7 (Xinhua) -- Only.. http://tinyurl.com/5gwotd</text>
<source>twitterfeed</source>
<truncated>false</truncated>
<in_reply_to_status_id />
<in_reply_to_user_id />
<favorited>false</favorited>
<user>
<id>11566502</id>
<name>Headline News</name>
<screen_name>headlinenews</screen_name>
<description></description>
<location></location>
<profile_image_url>http://s3.amazonaws.com/twitter_production/profile_images/41784602/1890_wires_normal.jpg</profile_image_url>
<url></url>
<protected>false</protected>
<followers_count>575</followers_count>
</user>
</status><status>
<created_at>Sun Dec 07 00:36:13 +0000 2008</created_at>
<id>1042729101</id>
<text>@hilarycassman soo funnny</text>
<source>sms</source>
<truncated>false</truncated>
<in_reply_to_status_id>1042725825</in_reply_to_status_id>
<in_reply_to_user_id>17644455</in_reply_to_user_id>
<favorited>false</favorited>
<user>
<id>17887548</id>
<name>katieballss</name>
<screen_name>katieballss</screen_name>
<description></description>
<location></location>
<profile_image_url>http://s3.amazonaws.com/twitter_production/profile_images/66523737/th_Photo87_normal.jpg</profile_image_url>
<url>http://www.myspace.com/xxpeachxx101</url>
<protected>false</protected>
<followers_count>23</followers_count>
</user>
</status><status>
<created_at>Sun Dec 07 00:36:13 +0000 2008</created_at>
<id>1042729100</id>
<text>d'ora in poi, oltre al ferragosto, odiera' anche il natale e tutto il mese che ci gira intorno.. =.=''</text>
<source>mobile</source>
<truncated>false</truncated>
<in_reply_to_status_id />
<in_reply_to_user_id />
<favorited>false</favorited>
<user>
<id>9719482</id>
<name>trotto</name>
<screen_name>trotto</screen_name>
<description>sociologo di formazione... uno dei tanti attivisti! :)</description>
<location>Fano - Italy</location>
<profile_image_url>http://s3.amazonaws.com/twitter_production/profile_images/55603933/mybob-calimetux-1526_normal.png</profile_image_url>
<url>http://trotto1308.netsons.org/wordpress/</url>
<protected>false</protected>
<followers_count>98</followers_count>
</user>
</status><status>
<created_at>Sun Dec 07 00:36:13 +0000 2008</created_at>
<id>1042729099</id>
<text>Came across an ad on another site with a redneck looking santa... said &quot;Bust Santa's zit and win a free ipod.&quot; Umm... disturbing much?</text>
<source>web</source>
<truncated>false</truncated>
<in_reply_to_status_id />
<in_reply_to_user_id />
<favorited>false</favorited>
<user>
<id>9937182</id>
<name>froggybluesock</name>
<screen_name>froggybluesock</screen_name>
<description></description>
<location>Indiana</location>
<profile_image_url>http://s3.amazonaws.com/twitter_production/profile_images/35393032/about_me_normal.jpg</profile_image_url>
<url>http://www.footprintsonthemoon.com</url>
<protected>false</protected>
<followers_count>82</followers_count>
</user>
</status><status>
<created_at>Sun Dec 07 00:36:17 +0000 2008</created_at>
<id>1042729098</id>
<text>nothing</text>
<source>web</source>
<truncated>false</truncated>
<in_reply_to_status_id />
<in_reply_to_user_id />
<favorited>false</favorited>
<user>
<id>17932339</id>
<name>treblehook</name>
<screen_name>treblehook</screen_name>
<description />
<location />
<profile_image_url>http://static.twitter.com/images/default_profile_normal.png</profile_image_url>
<url />
<protected>false</protected>
<followers_count>0</followers_count>
</user>
</status><status>
<created_at>Sun Dec 07 00:36:13 +0000 2008</created_at>
<id>1042729095</id>
<text>Setting up my new Windows Live profile</text>
<source>web</source>
<truncated>false</truncated>
<in_reply_to_status_id />
<in_reply_to_user_id />
<favorited>false</favorited>
<user>
<id>17906075</id>
<name>Mark_Layton</name>
<screen_name>Mark_Layton</screen_name>
<description />
<location />
<profile_image_url>http://static.twitter.com/images/default_profile_normal.png</profile_image_url>
<url />
<protected>false</protected>
<followers_count>2</followers_count>
</user>
</status><status>
<created_at>Sun Dec 07 00:36:13 +0000 2008</created_at>
<id>1042729092</id>
<text>me voy a sobar, a ver si mañana estoy mejor, wenas noches a tod@s</text>
<source>web</source>
<truncated>false</truncated>
<in_reply_to_status_id />
<in_reply_to_user_id />
<favorited>false</favorited>
<user>
<id>14062655</id>
<name>tmaniak</name>
<screen_name>tmaniak</screen_name>
<description></description>
<location></location>
<profile_image_url>http://s3.amazonaws.com/twitter_production/profile_images/51680481/BF-109_normal.jpg</profile_image_url>
<url>http://dhost.info/tmaniak</url>
<protected>false</protected>
<followers_count>10</followers_count>
</user>
</status><status>
<created_at>Sun Dec 07 00:36:14 +0000 2008</created_at>
<id>1042729090</id>
<text>バイト延長戦入りましたー 店長が遅刻ってどうなんだ。しかも何回も</text>
<source>natsuliphone</source>
<truncated>false</truncated>
<in_reply_to_status_id />
<in_reply_to_user_id />
<favorited>false</favorited>
<user>
<id>15618846</id>
<name>kabayaki</name>
<screen_name>kabayaki</screen_name>
<description>FPS(L4D、TF2、CS:S、BF)、TPS、RCG、マンガ、アニメ、デジモが好きなとある学生</description>
<location>tokyo</location>
<profile_image_url>http://s3.amazonaws.com/twitter_production/profile_images/57305902/184_normal.jpg</profile_image_url>
<url>http://kabayakiya.blog43.fc2.com/</url>
<protected>false</protected>
<followers_count>20</followers_count>
</user>
</status><status>
<created_at>Sun Dec 07 00:36:13 +0000 2008</created_at>
<id>1042729089</id>
<text>just drove to southern california and joy of children hugging grandparents made it all worthwhile. heading to imedia in la quinta tomorrow.</text>
<source>web</source>
<truncated>false</truncated>
<in_reply_to_status_id />
<in_reply_to_user_id />
<favorited>false</favorited>
<user>
<id>1630261</id>
<name>mark silva</name>
<screen_name>marksilva</screen_name>
<description>digital media http://realbranding.com Principal, Managing Director</description>
<location>often san francisco</location>
<profile_image_url>http://s3.amazonaws.com/twitter_production/profile_images/29807902/silvasimpson3th_normal.jpg</profile_image_url>
<url>http://marksilva.com</url>
<protected>false</protected>
<followers_count>497</followers_count>
</user>
</status><status>
<created_at>Sun Dec 07 00:36:14 +0000 2008</created_at>
<id>1042729088</id>
<text>@wholefoods would love to have a juicebar in one of your cambridge or boston locations</text>
<source>web</source>
<truncated>false</truncated>
<in_reply_to_status_id>1041125103</in_reply_to_status_id>
<in_reply_to_user_id>15131310</in_reply_to_user_id>
<favorited>false</favorited>
<user>
<id>15856582</id>
<name>Maura McGovern</name>
<screen_name>mmcgovern</screen_name>
<description>photographer with a day job in venture capital; obsessed with design blogs, long walks, yoga, social networking, people watching. tea and music are essential!</description>
<location>Boston</location>
<profile_image_url>http://s3.amazonaws.com/twitter_production/profile_images/66110383/DSCN1740_normal.JPG</profile_image_url>
<url>http://lefteyephotography.blogspot.com</url>
<protected>false</protected>
<followers_count>244</followers_count>
</user>
</status><status>
<created_at>Sun Dec 07 00:36:15 +0000 2008</created_at>
<id>1042729087</id>
<text>Going over the Advent lists.</text>
<source>web</source>
<truncated>false</truncated>
<in_reply_to_status_id />
<in_reply_to_user_id />
<favorited>false</favorited>
<user>
<id>17122107</id>
<name>gsusan</name>
<screen_name>gsusan</screen_name>
<description>US American writer/sister/daughter/aunt/woman from New England living in SoCal.</description>
<location>San Diego, CA USA</location>
<profile_image_url>http://s3.amazonaws.com/twitter_production/profile_images/63951854/susan_ocracoke_normal.jpg</profile_image_url>
<url></url>
<protected>false</protected>
<followers_count>6</followers_count>
</user>
</status>
</statuses>

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<Entities total="0" results="0" page="1" page-size="25" href="https://s3-sandbox.parature.com/api/v1/5578/5633/Account" />

View File

@ -0,0 +1,206 @@
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
describe HTTParty::ConnectionAdapter do
describe "initialization" do
let(:uri) { URI 'http://www.google.com' }
it "takes a URI as input" do
HTTParty::ConnectionAdapter.new(uri)
end
it "raises an ArgumentError if the uri is nil" do
expect { HTTParty::ConnectionAdapter.new(nil) }.to raise_error ArgumentError
end
it "raises an ArgumentError if the uri is a String" do
expect { HTTParty::ConnectionAdapter.new('http://www.google.com') }.to raise_error ArgumentError
end
it "sets the uri" do
adapter = HTTParty::ConnectionAdapter.new(uri)
adapter.uri.should be uri
end
it "also accepts an optional options hash" do
HTTParty::ConnectionAdapter.new(uri, {})
end
it "sets the options" do
options = {:foo => :bar}
adapter = HTTParty::ConnectionAdapter.new(uri, options)
adapter.options.should be options
end
end
describe ".call" do
it "generates an HTTParty::ConnectionAdapter instance with the given uri and options" do
HTTParty::ConnectionAdapter.should_receive(:new).with(@uri, @options).and_return(stub(:connection => nil))
HTTParty::ConnectionAdapter.call(@uri, @options)
end
it "calls #connection on the connection adapter" do
adapter = mock('Adapter')
connection = mock('Connection')
adapter.should_receive(:connection).and_return(connection)
HTTParty::ConnectionAdapter.stub(:new => adapter)
HTTParty::ConnectionAdapter.call(@uri, @options).should be connection
end
end
describe '#connection' do
let(:uri) { URI 'http://www.google.com' }
let(:options) { Hash.new }
let(:adapter) { HTTParty::ConnectionAdapter.new(uri, options) }
describe "the resulting connection" do
subject { adapter.connection }
it { should be_an_instance_of Net::HTTP }
context "using port 80" do
let(:uri) { URI 'http://foobar.com' }
it { should_not use_ssl }
end
context "when dealing with ssl" do
let(:uri) { URI 'https://foobar.com' }
context "using port 443 for ssl" do
let(:uri) { URI 'https://api.foo.com/v1:443' }
it { should use_ssl }
end
context "https scheme with default port" do
it { should use_ssl }
end
context "https scheme with non-standard port" do
let(:uri) { URI 'https://foobar.com:123456' }
it { should use_ssl }
end
context "when ssl version is set" do
let(:options) { {:ssl_version => :TLSv1} }
it "sets ssl version" do
subject.ssl_version.should == :TLSv1
end
end if RUBY_VERSION > '1.9'
end
context "when timeout is not set" do
it "doesn't set the timeout" do
http = mock("http", :null_object => true)
http.should_not_receive(:open_timeout=)
http.should_not_receive(:read_timeout=)
Net::HTTP.stub(:new => http)
adapter.connection
end
end
context "when setting timeout" do
context "to 5 seconds" do
let(:options) { {:timeout => 5} }
its(:open_timeout) { should == 5 }
its(:read_timeout) { should == 5 }
end
context "and timeout is a string" do
let(:options) { {:timeout => "five seconds"} }
it "doesn't set the timeout" do
http = mock("http", :null_object => true)
http.should_not_receive(:open_timeout=)
http.should_not_receive(:read_timeout=)
Net::HTTP.stub(:new => http)
adapter.connection
end
end
end
context "when debug_output" do
let(:http) { Net::HTTP.new(uri) }
before do
Net::HTTP.stub(:new => http)
end
context "is set to $stderr" do
let(:options) { {:debug_output => $stderr} }
it "has debug output set" do
http.should_receive(:set_debug_output).with($stderr)
adapter.connection
end
end
context "is not provided" do
it "does not set_debug_output" do
http.should_not_receive(:set_debug_output)
adapter.connection
end
end
end
context 'when providing proxy address and port' do
let(:options) { {:http_proxyaddr => '1.2.3.4', :http_proxyport => 8080} }
it { should be_a_proxy }
its(:proxy_address) { should == '1.2.3.4' }
its(:proxy_port) { should == 8080 }
context 'as well as proxy user and password' do
let(:options) do
{:http_proxyaddr => '1.2.3.4', :http_proxyport => 8080,
:http_proxyuser => 'user', :http_proxypass => 'pass'}
end
its(:proxy_user) { should == 'user' }
its(:proxy_pass) { should == 'pass' }
end
end
context "when providing PEM certificates" do
let(:pem) { :pem_contents }
let(:options) { {:pem => pem, :pem_password => "password"} }
context "when scheme is https" do
let(:uri) { URI 'https://google.com' }
let(:cert) { mock("OpenSSL::X509::Certificate") }
let(:key) { mock("OpenSSL::PKey::RSA") }
before do
OpenSSL::X509::Certificate.should_receive(:new).with(pem).and_return(cert)
OpenSSL::PKey::RSA.should_receive(:new).with(pem, "password").and_return(key)
end
it "uses the provided PEM certificate " do
subject.cert.should == cert
subject.key.should == key
end
it "will verify the certificate" do
subject.verify_mode.should == OpenSSL::SSL::VERIFY_PEER
end
end
context "when scheme is not https" do
let(:uri) { URI 'http://google.com' }
let(:http) { Net::HTTP.new(uri) }
before do
Net::HTTP.stub(:new => http)
OpenSSL::X509::Certificate.should_not_receive(:new).with(pem)
OpenSSL::PKey::RSA.should_not_receive(:new).with(pem, "password")
http.should_not_receive(:cert=)
http.should_not_receive(:key=)
end
it "has no PEM certificate " do
subject.cert.should be_nil
subject.key.should be_nil
end
end
end
end
end
end

View File

@ -0,0 +1,70 @@
require File.expand_path(File.join(File.dirname(__FILE__), '../spec_helper'))
describe HTTParty::CookieHash do
before(:each) do
@cookie_hash = HTTParty::CookieHash.new
end
describe "#add_cookies" do
describe "with a hash" do
it "should add new key/value pairs to the hash" do
@cookie_hash.add_cookies(:foo => "bar")
@cookie_hash.add_cookies(:rofl => "copter")
@cookie_hash.length.should eql(2)
end
it "should overwrite any existing key" do
@cookie_hash.add_cookies(:foo => "bar")
@cookie_hash.add_cookies(:foo => "copter")
@cookie_hash.length.should eql(1)
@cookie_hash[:foo].should eql("copter")
end
end
describe "with a string" do
it "should add new key/value pairs to the hash" do
@cookie_hash.add_cookies("first=one; second=two; third")
@cookie_hash[:first].should == 'one'
@cookie_hash[:second].should == 'two'
@cookie_hash[:third].should == nil
end
it "should overwrite any existing key" do
@cookie_hash[:foo] = 'bar'
@cookie_hash.add_cookies("foo=tar")
@cookie_hash.length.should eql(1)
@cookie_hash[:foo].should eql("tar")
end
end
describe 'with other class' do
it "should error" do
lambda {
@cookie_hash.add_cookies(Array.new)
}.should raise_error
end
end
end
# The regexen are required because Hashes aren't ordered, so a test against
# a hardcoded string was randomly failing.
describe "#to_cookie_string" do
before(:each) do
@cookie_hash.add_cookies(:foo => "bar")
@cookie_hash.add_cookies(:rofl => "copter")
@s = @cookie_hash.to_cookie_string
end
it "should format the key/value pairs, delimited by semi-colons" do
@s.should match(/foo=bar/)
@s.should match(/rofl=copter/)
@s.should match(/^\w+=\w+; \w+=\w+$/)
end
it "should not include client side only cookies" do
@cookie_hash.add_cookies(:path => "/")
@s = @cookie_hash.to_cookie_string
@s.should_not match(/path=\//)
end
end
end

View File

@ -0,0 +1,115 @@
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
describe Net::HTTPHeader::DigestAuthenticator do
def setup_digest(response)
digest = Net::HTTPHeader::DigestAuthenticator.new("Mufasa",
"Circle Of Life", "GET", "/dir/index.html", response)
digest.stub(:random).and_return("deadbeef")
Digest::MD5.stub(:hexdigest) { |str| "md5(#{str})" }
digest
end
def authorization_header
@digest.authorization_header.join(", ")
end
context "with an opaque value in the response header" do
before do
@digest = setup_digest({
'www-authenticate' => 'Digest realm="myhost@testrealm.com", opaque="solid"'
})
end
it "should set opaque" do
authorization_header.should include(%Q(opaque="solid"))
end
end
context "without an opaque valid in the response header" do
before do
@digest = setup_digest({
'www-authenticate' => 'Digest realm="myhost@testrealm.com"'
})
end
it "should not set opaque" do
authorization_header.should_not include(%Q(opaque=))
end
end
context "with specified quality of protection (qop)" do
before do
@digest = setup_digest({
'www-authenticate' => 'Digest realm="myhost@testrealm.com", nonce="NONCE", qop="auth"',
})
end
it "should set prefix" do
authorization_header.should =~ /^Digest /
end
it "should set username" do
authorization_header.should include(%Q(username="Mufasa"))
end
it "should set digest-uri" do
authorization_header.should include(%Q(uri="/dir/index.html"))
end
it "should set qop" do
authorization_header.should include(%Q(qop="auth"))
end
it "should set cnonce" do
authorization_header.should include(%Q(cnonce="md5(deadbeef)"))
end
it "should set nonce-count" do
authorization_header.should include(%Q(nc="00000001"))
end
it "should set response" do
request_digest = "md5(md5(Mufasa:myhost@testrealm.com:Circle Of Life):NONCE:00000001:md5(deadbeef):auth:md5(GET:/dir/index.html))"
authorization_header.should include(%Q(response="#{request_digest}"))
end
end
context "with unspecified quality of protection (qop)" do
before do
@digest = setup_digest({
'www-authenticate' => 'Digest realm="myhost@testrealm.com", nonce="NONCE"',
})
end
it "should set prefix" do
authorization_header.should =~ /^Digest /
end
it "should set username" do
authorization_header.should include(%Q(username="Mufasa"))
end
it "should set digest-uri" do
authorization_header.should include(%Q(uri="/dir/index.html"))
end
it "should not set qop" do
authorization_header.should_not include(%Q(qop=))
end
it "should not set cnonce" do
authorization_header.should_not include(%Q(cnonce=))
end
it "should not set nonce-count" do
authorization_header.should_not include(%Q(nc=))
end
it "should set response" do
request_digest = "md5(md5(Mufasa:myhost@testrealm.com:Circle Of Life):NONCE:md5(GET:/dir/index.html))"
authorization_header.should include(%Q(response="#{request_digest}"))
end
end
end

View File

@ -0,0 +1,171 @@
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
describe HTTParty::Parser do
describe ".SupportedFormats" do
it "returns a hash" do
HTTParty::Parser::SupportedFormats.should be_instance_of(Hash)
end
end
describe ".call" do
it "generates an HTTParty::Parser instance with the given body and format" do
HTTParty::Parser.should_receive(:new).with('body', :plain).and_return(stub(:parse => nil))
HTTParty::Parser.call('body', :plain)
end
it "calls #parse on the parser" do
parser = mock('Parser')
parser.should_receive(:parse)
HTTParty::Parser.stub(:new => parser)
parser = HTTParty::Parser.call('body', :plain)
end
end
describe ".formats" do
it "returns the SupportedFormats constant" do
HTTParty::Parser.formats.should == HTTParty::Parser::SupportedFormats
end
it "returns the SupportedFormats constant for subclasses" do
class MyParser < HTTParty::Parser
SupportedFormats = {"application/atom+xml" => :atom}
end
MyParser.formats.should == {"application/atom+xml" => :atom}
end
end
describe ".format_from_mimetype" do
it "returns a symbol representing the format mimetype" do
HTTParty::Parser.format_from_mimetype("text/plain").should == :plain
end
it "returns nil when the mimetype is not supported" do
HTTParty::Parser.format_from_mimetype("application/atom+xml").should be_nil
end
end
describe ".supported_formats" do
it "returns a unique set of supported formats represented by symbols" do
HTTParty::Parser.supported_formats.should == HTTParty::Parser::SupportedFormats.values.uniq
end
end
describe ".supports_format?" do
it "returns true for a supported format" do
HTTParty::Parser.stub(:supported_formats => [:json])
HTTParty::Parser.supports_format?(:json).should be_true
end
it "returns false for an unsupported format" do
HTTParty::Parser.stub(:supported_formats => [])
HTTParty::Parser.supports_format?(:json).should be_false
end
end
describe "#parse" do
before do
@parser = HTTParty::Parser.new('body', :json)
end
it "attempts to parse supported formats" do
@parser.stub(:supports_format? => true)
@parser.should_receive(:parse_supported_format)
@parser.parse
end
it "returns the unparsed body when the format is unsupported" do
@parser.stub(:supports_format? => false)
@parser.parse.should == @parser.body
end
it "returns nil for an empty body" do
@parser.stub(:body => '')
@parser.parse.should be_nil
end
it "returns nil for a nil body" do
@parser.stub(:body => nil)
@parser.parse.should be_nil
end
it "returns nil for a 'null' body" do
@parser.stub(:body => "null")
@parser.parse.should be_nil
end
it "returns nil for a body with spaces only" do
@parser.stub(:body => " ")
@parser.parse.should be_nil
end
end
describe "#supports_format?" do
it "utilizes the class method to determine if the format is supported" do
HTTParty::Parser.should_receive(:supports_format?).with(:json)
parser = HTTParty::Parser.new('body', :json)
parser.send(:supports_format?)
end
end
describe "#parse_supported_format" do
it "calls the parser for the given format" do
parser = HTTParty::Parser.new('body', :json)
parser.should_receive(:json)
parser.send(:parse_supported_format)
end
context "when a parsing method does not exist for the given format" do
it "raises an exception" do
parser = HTTParty::Parser.new('body', :atom)
expect do
parser.send(:parse_supported_format)
end.to raise_error(NotImplementedError, "HTTParty::Parser has not implemented a parsing method for the :atom format.")
end
it "raises a useful exception message for subclasses" do
atom_parser = Class.new(HTTParty::Parser) do
def self.name; 'AtomParser'; end
end
parser = atom_parser.new 'body', :atom
expect do
parser.send(:parse_supported_format)
end.to raise_error(NotImplementedError, "AtomParser has not implemented a parsing method for the :atom format.")
end
end
end
context "parsers" do
subject do
HTTParty::Parser.new('body', nil)
end
it "parses xml with MultiXml" do
MultiXml.should_receive(:parse).with('body')
subject.send(:xml)
end
it "parses json with MultiJson" do
MultiJson.should_receive(:load).with('body')
subject.send(:json)
end
it "uses MultiJson.decode if MultiJson does not respond to adapter" do
MultiJson.should_receive(:respond_to?).with(:adapter).and_return(false)
MultiJson.should_receive(:decode).with('body')
subject.send(:json)
end
it "parses yaml" do
YAML.should_receive(:load).with('body')
subject.send(:yaml)
end
it "parses html by simply returning the body" do
subject.send(:html).should == 'body'
end
it "parses plain text by simply returning the body" do
subject.send(:plain).should == 'body'
end
end
end

View File

@ -0,0 +1,495 @@
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
describe HTTParty::Request do
before do
@request = HTTParty::Request.new(Net::HTTP::Get, 'http://api.foo.com/v1', :format => :xml)
end
describe "::NON_RAILS_QUERY_STRING_NORMALIZER" do
let(:normalizer) { HTTParty::Request::NON_RAILS_QUERY_STRING_NORMALIZER }
it "doesn't modify strings" do
query_string = normalizer["foo=bar&foo=baz"]
URI.unescape(query_string).should == "foo=bar&foo=baz"
end
context "when the query is an array" do
it "doesn't include brackets" do
query_string = normalizer[{:page => 1, :foo => %w(bar baz)}]
URI.unescape(query_string).should == "foo=bar&foo=baz&page=1"
end
it "URI encodes array values" do
query_string = normalizer[{:people => ["Bob Marley", "Tim & Jon"]}]
query_string.should == "people=Bob%20Marley&people=Tim%20%26%20Jon"
end
end
context "when the query is a hash" do
it "correctly handles nil values" do
query_string = normalizer[{:page => 1, :per_page => nil}]
query_string.should == "page=1&per_page"
end
end
end
describe "initialization" do
it "sets parser to HTTParty::Parser" do
request = HTTParty::Request.new(Net::HTTP::Get, 'http://google.com')
request.parser.should == HTTParty::Parser
end
it "sets parser to the optional parser" do
my_parser = lambda {}
request = HTTParty::Request.new(Net::HTTP::Get, 'http://google.com', :parser => my_parser)
request.parser.should == my_parser
end
it "sets connection_adapter to HTTPParty::ConnectionAdapter" do
request = HTTParty::Request.new(Net::HTTP::Get, 'http://google.com')
request.connection_adapter.should == HTTParty::ConnectionAdapter
end
it "sets connection_adapter to the optional connection_adapter" do
my_adapter = lambda {}
request = HTTParty::Request.new(Net::HTTP::Get, 'http://google.com', :connection_adapter => my_adapter)
request.connection_adapter.should == my_adapter
end
end
describe "#format" do
context "request yet to be made" do
it "returns format option" do
request = HTTParty::Request.new 'get', '/', :format => :xml
request.format.should == :xml
end
it "returns nil format" do
request = HTTParty::Request.new 'get', '/'
request.format.should be_nil
end
end
context "request has been made" do
it "returns format option" do
request = HTTParty::Request.new 'get', '/', :format => :xml
request.last_response = stub
request.format.should == :xml
end
it "returns the content-type from the last response when the option is not set" do
request = HTTParty::Request.new 'get', '/'
response = stub
response.should_receive(:[]).with('content-type').and_return('text/json')
request.last_response = response
request.format.should == :json
end
end
end
context "options" do
it "should use basic auth when configured" do
@request.options[:basic_auth] = {:username => 'foobar', :password => 'secret'}
@request.send(:setup_raw_request)
@request.instance_variable_get(:@raw_request)['authorization'].should_not be_nil
end
it "should use digest auth when configured" do
FakeWeb.register_uri(:get, "http://api.foo.com/v1",
:www_authenticate => 'Digest realm="Log Viewer", qop="auth", nonce="2CA0EC6B0E126C4800E56BA0C0003D3C", opaque="5ccc069c403ebaf9f0171e9517f40e41", stale=false')
@request.options[:digest_auth] = {:username => 'foobar', :password => 'secret'}
@request.send(:setup_raw_request)
raw_request = @request.instance_variable_get(:@raw_request)
raw_request.instance_variable_get(:@header)['Authorization'].should_not be_nil
end
it "should use the right http method for digest authentication" do
@post_request = HTTParty::Request.new(Net::HTTP::Post, 'http://api.foo.com/v1', :format => :xml)
FakeWeb.register_uri(:post, "http://api.foo.com/v1", {})
http = @post_request.send(:http)
@post_request.should_receive(:http).and_return(http)
http.should_not_receive(:head).and_return({'www-authenticate' => nil})
@post_request.options[:digest_auth] = {:username => 'foobar', :password => 'secret'}
@post_request.send(:setup_raw_request)
end
end
describe "#uri" do
context "query strings" do
it "does not add an empty query string when default_params are blank" do
@request.options[:default_params] = {}
@request.uri.query.should be_nil
end
it "respects the query string normalization proc" do
empty_proc = lambda {|qs| ""}
@request.options[:query_string_normalizer] = empty_proc
@request.options[:query] = {:foo => :bar}
URI.unescape(@request.uri.query).should == ""
end
context "when representing an array" do
it "returns a Rails style query string" do
@request.options[:query] = {:foo => %w(bar baz)}
URI.unescape(@request.uri.query).should == "foo[]=bar&foo[]=baz"
end
end
end
end
describe "#setup_raw_request" do
context "when query_string_normalizer is set" do
it "sets the body to the return value of the proc" do
@request.options[:query_string_normalizer] = HTTParty::Request::NON_RAILS_QUERY_STRING_NORMALIZER
@request.options[:body] = {:page => 1, :foo => %w(bar baz)}
@request.send(:setup_raw_request)
body = @request.instance_variable_get(:@raw_request).body
URI.unescape(body).should == "foo=bar&foo=baz&page=1"
end
end
end
describe 'http' do
it "should get a connection from the connection_adapter" do
http = Net::HTTP.new('google.com')
adapter = mock('adapter')
request = HTTParty::Request.new(Net::HTTP::Get, 'https://api.foo.com/v1:443', :connection_adapter => adapter)
adapter.should_receive(:call).with(request.uri, request.options).and_return(http)
request.send(:http).should be http
end
end
describe '#format_from_mimetype' do
it 'should handle text/xml' do
["text/xml", "text/xml; charset=iso8859-1"].each do |ct|
@request.send(:format_from_mimetype, ct).should == :xml
end
end
it 'should handle application/xml' do
["application/xml", "application/xml; charset=iso8859-1"].each do |ct|
@request.send(:format_from_mimetype, ct).should == :xml
end
end
it 'should handle text/json' do
["text/json", "text/json; charset=iso8859-1"].each do |ct|
@request.send(:format_from_mimetype, ct).should == :json
end
end
it 'should handle application/json' do
["application/json", "application/json; charset=iso8859-1"].each do |ct|
@request.send(:format_from_mimetype, ct).should == :json
end
end
it 'should handle text/javascript' do
["text/javascript", "text/javascript; charset=iso8859-1"].each do |ct|
@request.send(:format_from_mimetype, ct).should == :json
end
end
it 'should handle application/javascript' do
["application/javascript", "application/javascript; charset=iso8859-1"].each do |ct|
@request.send(:format_from_mimetype, ct).should == :json
end
end
it "returns nil for an unrecognized mimetype" do
@request.send(:format_from_mimetype, "application/atom+xml").should be_nil
end
it "returns nil when using a default parser" do
@request.options[:parser] = lambda {}
@request.send(:format_from_mimetype, "text/json").should be_nil
end
end
describe 'parsing responses' do
it 'should handle xml automatically' do
xml = %q[<books><book><id>1234</id><name>Foo Bar!</name></book></books>]
@request.options[:format] = :xml
@request.send(:parse_response, xml).should == {'books' => {'book' => {'id' => '1234', 'name' => 'Foo Bar!'}}}
end
it 'should handle json automatically' do
json = %q[{"books": {"book": {"name": "Foo Bar!", "id": "1234"}}}]
@request.options[:format] = :json
@request.send(:parse_response, json).should == {'books' => {'book' => {'id' => '1234', 'name' => 'Foo Bar!'}}}
end
it 'should handle yaml automatically' do
yaml = "books: \n book: \n name: Foo Bar!\n id: \"1234\"\n"
@request.options[:format] = :yaml
@request.send(:parse_response, yaml).should == {'books' => {'book' => {'id' => '1234', 'name' => 'Foo Bar!'}}}
end
it "should include any HTTP headers in the returned response" do
@request.options[:format] = :html
response = stub_response "Content"
response.initialize_http_header("key" => "value")
@request.perform.headers.should == { "key" => ["value"] }
end
describe 'with non-200 responses' do
context "3xx responses" do
it 'returns a valid object for 304 not modified' do
stub_response '', 304
resp = @request.perform
resp.code.should == 304
resp.body.should == ''
resp.should be_nil
end
it "redirects if a 300 contains a location header" do
redirect = stub_response '', 300
redirect['location'] = 'http://foo.com/foo'
ok = stub_response('<hash><foo>bar</foo></hash>', 200)
@http.stub!(:request).and_return(redirect, ok)
response = @request.perform
response.request.base_uri.to_s.should == "http://foo.com"
response.request.path.to_s.should == "http://foo.com/foo"
response.request.uri.request_uri.should == "/foo"
response.request.uri.to_s.should == "http://foo.com/foo"
response.should == {"hash" => {"foo" => "bar"}}
end
it "redirects if a 300 contains a relative location header" do
redirect = stub_response '', 300
redirect['location'] = '/foo/bar'
ok = stub_response('<hash><foo>bar</foo></hash>', 200)
@http.stub!(:request).and_return(redirect, ok)
response = @request.perform
response.request.base_uri.to_s.should == "http://api.foo.com"
response.request.path.to_s.should == "/foo/bar"
response.request.uri.request_uri.should == "/foo/bar"
response.request.uri.to_s.should == "http://api.foo.com/foo/bar"
response.should == {"hash" => {"foo" => "bar"}}
end
it "handles multiple redirects and relative location headers on different hosts" do
@request = HTTParty::Request.new(Net::HTTP::Get, 'http://test.com/redirect', :format => :xml)
FakeWeb.register_uri(:get, "http://test.com/redirect", :status => [300, "REDIRECT"], :location => "http://api.foo.com/v2")
FakeWeb.register_uri(:get, "http://api.foo.com/v2", :status => [300, "REDIRECT"], :location => "/v3")
FakeWeb.register_uri(:get, "http://api.foo.com/v3", :body => "<hash><foo>bar</foo></hash>")
response = @request.perform
response.request.base_uri.to_s.should == "http://api.foo.com"
response.request.path.to_s.should == "/v3"
response.request.uri.request_uri.should == "/v3"
response.request.uri.to_s.should == "http://api.foo.com/v3"
response.should == {"hash" => {"foo" => "bar"}}
end
it "returns the HTTParty::Response when the 300 does not contain a location header" do
net_response = stub_response '', 300
HTTParty::Response.should === @request.perform
end
end
it 'should return a valid object for 4xx response' do
stub_response '<foo><bar>yes</bar></foo>', 401
resp = @request.perform
resp.code.should == 401
resp.body.should == "<foo><bar>yes</bar></foo>"
resp['foo']['bar'].should == "yes"
end
it 'should return a valid object for 5xx response' do
stub_response '<foo><bar>error</bar></foo>', 500
resp = @request.perform
resp.code.should == 500
resp.body.should == "<foo><bar>error</bar></foo>"
resp['foo']['bar'].should == "error"
end
it "parses response lazily so codes can be checked prior" do
stub_response 'not xml', 500
@request.options[:format] = :xml
lambda {
response = @request.perform
response.code.should == 500
response.body.should == 'not xml'
}.should_not raise_error
end
end
end
it "should not attempt to parse empty responses" do
[204, 304].each do |code|
stub_response "", code
@request.options[:format] = :xml
@request.perform.should be_nil
end
end
it "should not fail for missing mime type" do
stub_response "Content for you"
@request.options[:format] = :html
@request.perform.should == 'Content for you'
end
describe "a request that redirects" do
before(:each) do
@redirect = stub_response("", 302)
@redirect['location'] = '/foo'
@ok = stub_response('<hash><foo>bar</foo></hash>', 200)
end
describe "once" do
before(:each) do
@http.stub!(:request).and_return(@redirect, @ok)
end
it "should be handled by GET transparently" do
@request.perform.should == {"hash" => {"foo" => "bar"}}
end
it "should be handled by POST transparently" do
@request.http_method = Net::HTTP::Post
@request.perform.should == {"hash" => {"foo" => "bar"}}
end
it "should be handled by DELETE transparently" do
@request.http_method = Net::HTTP::Delete
@request.perform.should == {"hash" => {"foo" => "bar"}}
end
it "should be handled by PATCH transparently" do
@request.http_method = Net::HTTP::Patch
@request.perform.should == {"hash" => {"foo" => "bar"}}
end
it "should be handled by PUT transparently" do
@request.http_method = Net::HTTP::Put
@request.perform.should == {"hash" => {"foo" => "bar"}}
end
it "should be handled by HEAD transparently" do
@request.http_method = Net::HTTP::Head
@request.perform.should == {"hash" => {"foo" => "bar"}}
end
it "should be handled by OPTIONS transparently" do
@request.http_method = Net::HTTP::Options
@request.perform.should == {"hash" => {"foo" => "bar"}}
end
it "should keep track of cookies between redirects" do
@redirect['Set-Cookie'] = 'foo=bar; name=value; HTTPOnly'
@request.perform
@request.options[:headers]['Cookie'].should match(/foo=bar/)
@request.options[:headers]['Cookie'].should match(/name=value/)
end
it 'should update cookies with rediects' do
@request.options[:headers] = {'Cookie'=> 'foo=bar;'}
@redirect['Set-Cookie'] = 'foo=tar;'
@request.perform
@request.options[:headers]['Cookie'].should match(/foo=tar/)
end
it 'should keep cookies between rediects' do
@request.options[:headers] = {'Cookie'=> 'keep=me'}
@redirect['Set-Cookie'] = 'foo=tar;'
@request.perform
@request.options[:headers]['Cookie'].should match(/keep=me/)
end
it 'should make resulting request a get request if it not already' do
@request.http_method = Net::HTTP::Delete
@request.perform.should == {"hash" => {"foo" => "bar"}}
@request.http_method.should == Net::HTTP::Get
end
it 'should not make resulting request a get request if options[:maintain_method_across_redirects] is true' do
@request.options[:maintain_method_across_redirects] = true
@request.http_method = Net::HTTP::Delete
@request.perform.should == {"hash" => {"foo" => "bar"}}
@request.http_method.should == Net::HTTP::Delete
end
end
describe "infinitely" do
before(:each) do
@http.stub!(:request).and_return(@redirect)
end
it "should raise an exception" do
lambda { @request.perform }.should raise_error(HTTParty::RedirectionTooDeep)
end
end
end
describe "#handle_deflation" do
context "context-encoding" do
before do
@request.options[:format] = :html
@last_response = mock()
@last_response.stub!(:body).and_return('')
end
it "should inflate the gzipped body with content-encoding: gzip" do
@last_response.stub!(:[]).with("content-encoding").and_return("gzip")
@request.stub!(:last_response).and_return(@last_response)
Zlib::GzipReader.should_receive(:new).and_return(StringIO.new(''))
@request.last_response.should_receive(:delete).with('content-encoding')
@request.send(:handle_deflation)
end
it "should inflate the gzipped body with content-encoding: x-gzip" do
@last_response.stub!(:[]).with("content-encoding").and_return("x-gzip")
@request.stub!(:last_response).and_return(@last_response)
Zlib::GzipReader.should_receive(:new).and_return(StringIO.new(''))
@request.last_response.should_receive(:delete).with('content-encoding')
@request.send(:handle_deflation)
end
it "should inflate the deflated body" do
@last_response.stub!(:[]).with("content-encoding").and_return("deflate")
@request.stub!(:last_response).and_return(@last_response)
Zlib::Inflate.should_receive(:inflate).and_return('')
@request.last_response.should_receive(:delete).with('content-encoding')
@request.send(:handle_deflation)
end
end
end
context "with POST http method" do
it "should raise argument error if query is not a hash" do
lambda {
HTTParty::Request.new(Net::HTTP::Post, 'http://api.foo.com/v1', :format => :xml, :query => 'astring').perform
}.should raise_error(ArgumentError)
end
end
describe "argument validation" do
it "should raise argument error if basic_auth and digest_auth are both present" do
lambda {
HTTParty::Request.new(Net::HTTP::Post, 'http://api.foo.com/v1', :basic_auth => {}, :digest_auth => {}).perform
}.should raise_error(ArgumentError, "only one authentication method, :basic_auth or :digest_auth may be used at a time")
end
it "should raise argument error if basic_auth is not a hash" do
lambda {
HTTParty::Request.new(Net::HTTP::Post, 'http://api.foo.com/v1', :basic_auth => ["foo", "bar"]).perform
}.should raise_error(ArgumentError, ":basic_auth must be a hash")
end
it "should raise argument error if digest_auth is not a hash" do
lambda {
HTTParty::Request.new(Net::HTTP::Post, 'http://api.foo.com/v1', :digest_auth => ["foo", "bar"]).perform
}.should raise_error(ArgumentError, ":digest_auth must be a hash")
end
end
end

View File

@ -0,0 +1,214 @@
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
describe HTTParty::Response do
before do
@last_modified = Date.new(2010, 1, 15).to_s
@content_length = '1024'
@request_object = HTTParty::Request.new Net::HTTP::Get, '/'
@response_object = Net::HTTPOK.new('1.1', 200, 'OK')
@response_object.stub(:body => "{foo:'bar'}")
@response_object['last-modified'] = @last_modified
@response_object['content-length'] = @content_length
@parsed_response = lambda { {"foo" => "bar"} }
@response = HTTParty::Response.new(@request_object, @response_object, @parsed_response)
end
describe ".underscore" do
it "works with one capitalized word" do
HTTParty::Response.underscore("Accepted").should == "accepted"
end
it "works with titlecase" do
HTTParty::Response.underscore("BadGateway").should == "bad_gateway"
end
it "works with all caps" do
HTTParty::Response.underscore("OK").should == "ok"
end
end
describe "initialization" do
it "should set the Net::HTTP Response" do
@response.response.should == @response_object
end
it "should set body" do
@response.body.should == @response_object.body
end
it "should set code" do
@response.code.should.to_s == @response_object.code
end
it "should set code as a Fixnum" do
@response.code.should be_an_instance_of(Fixnum)
end
end
it "returns response headers" do
response = HTTParty::Response.new(@request_object, @response_object, @parsed_response)
response.headers.should == {'last-modified' => [@last_modified], 'content-length' => [@content_length]}
end
it "should send missing methods to delegate" do
response = HTTParty::Response.new(@request_object, @response_object, @parsed_response)
response['foo'].should == 'bar'
end
it "response to request" do
response = HTTParty::Response.new(@request_object, @response_object, @parsed_response)
response.respond_to?(:request).should be_true
end
it "responds to response" do
response = HTTParty::Response.new(@request_object, @response_object, @parsed_response)
response.respond_to?(:response).should be_true
end
it "responds to body" do
response = HTTParty::Response.new(@request_object, @response_object, @parsed_response)
response.respond_to?(:body).should be_true
end
it "responds to headers" do
response = HTTParty::Response.new(@request_object, @response_object, @parsed_response)
response.respond_to?(:headers).should be_true
end
it "responds to parsed_response" do
response = HTTParty::Response.new(@request_object, @response_object, @parsed_response)
response.respond_to?(:parsed_response).should be_true
end
it "responds to anything parsed_response responds to" do
response = HTTParty::Response.new(@request_object, @response_object, @parsed_response)
response.respond_to?(:[]).should be_true
end
it "should be able to iterate if it is array" do
response = HTTParty::Response.new(@request_object, @response_object, lambda { [{'foo' => 'bar'}, {'foo' => 'baz'}] })
response.size.should == 2
expect {
response.each { |item| }
}.to_not raise_error
end
it "allows headers to be accessed by mixed-case names in hash notation" do
response = HTTParty::Response.new(@request_object, @response_object, @parsed_response)
response.headers['Content-LENGTH'].should == @content_length
end
it "returns a comma-delimited value when multiple values exist" do
@response_object.add_field 'set-cookie', 'csrf_id=12345; path=/'
@response_object.add_field 'set-cookie', '_github_ses=A123CdE; path=/'
response = HTTParty::Response.new(@request_object, @response_object, @parsed_response)
response.headers['set-cookie'].should == "csrf_id=12345; path=/, _github_ses=A123CdE; path=/"
end
# Backwards-compatibility - previously, #headers returned a Hash
it "responds to hash methods" do
response = HTTParty::Response.new(@request_object, @response_object, @parsed_response)
hash_methods = {}.methods - response.headers.methods
hash_methods.each do |method_name|
response.headers.respond_to?(method_name).should be_true
end
end
describe "semantic methods for response codes" do
def response_mock(klass)
response = klass.new('', '', '')
response.stub(:body)
response
end
context "major codes" do
it "is information" do
net_response = response_mock(Net::HTTPInformation)
response = HTTParty::Response.new(@request_object, net_response, '')
response.information?.should be_true
end
it "is success" do
net_response = response_mock(Net::HTTPSuccess)
response = HTTParty::Response.new(@request_object, net_response, '')
response.success?.should be_true
end
it "is redirection" do
net_response = response_mock(Net::HTTPRedirection)
response = HTTParty::Response.new(@request_object, net_response, '')
response.redirection?.should be_true
end
it "is client error" do
net_response = response_mock(Net::HTTPClientError)
response = HTTParty::Response.new(@request_object, net_response, '')
response.client_error?.should be_true
end
it "is server error" do
net_response = response_mock(Net::HTTPServerError)
response = HTTParty::Response.new(@request_object, net_response, '')
response.server_error?.should be_true
end
end
context "for specific codes" do
SPECIFIC_CODES = {
:accepted? => Net::HTTPAccepted,
:bad_gateway? => Net::HTTPBadGateway,
:bad_request? => Net::HTTPBadRequest,
:conflict? => Net::HTTPConflict,
:continue? => Net::HTTPContinue,
:created? => Net::HTTPCreated,
:expectation_failed? => Net::HTTPExpectationFailed,
:forbidden? => Net::HTTPForbidden,
:found? => Net::HTTPFound,
:gateway_time_out? => Net::HTTPGatewayTimeOut,
:gone? => Net::HTTPGone,
:internal_server_error? => Net::HTTPInternalServerError,
:length_required? => Net::HTTPLengthRequired,
:method_not_allowed? => Net::HTTPMethodNotAllowed,
:moved_permanently? => Net::HTTPMovedPermanently,
:multiple_choice? => Net::HTTPMultipleChoice,
:no_content? => Net::HTTPNoContent,
:non_authoritative_information? => Net::HTTPNonAuthoritativeInformation,
:not_acceptable? => Net::HTTPNotAcceptable,
:not_found? => Net::HTTPNotFound,
:not_implemented? => Net::HTTPNotImplemented,
:not_modified? => Net::HTTPNotModified,
:ok? => Net::HTTPOK,
:partial_content? => Net::HTTPPartialContent,
:payment_required? => Net::HTTPPaymentRequired,
:precondition_failed? => Net::HTTPPreconditionFailed,
:proxy_authentication_required? => Net::HTTPProxyAuthenticationRequired,
:request_entity_too_large? => Net::HTTPRequestEntityTooLarge,
:request_time_out? => Net::HTTPRequestTimeOut,
:request_uri_too_long? => Net::HTTPRequestURITooLong,
:requested_range_not_satisfiable? => Net::HTTPRequestedRangeNotSatisfiable,
:reset_content? => Net::HTTPResetContent,
:see_other? => Net::HTTPSeeOther,
:service_unavailable? => Net::HTTPServiceUnavailable,
:switch_protocol? => Net::HTTPSwitchProtocol,
:temporary_redirect? => Net::HTTPTemporaryRedirect,
:unauthorized? => Net::HTTPUnauthorized,
:unsupported_media_type? => Net::HTTPUnsupportedMediaType,
:use_proxy? => Net::HTTPUseProxy,
:version_not_supported? => Net::HTTPVersionNotSupported
}.each do |method, klass|
it "responds to #{method}" do
net_response = response_mock(klass)
response = HTTParty::Response.new(@request_object, net_response, '')
response.__send__(method).should be_true
end
end
end
end
describe "headers" do
it "can initialize without headers" do
headers = HTTParty::Response::Headers.new
headers.should == {}
end
end
end

View File

@ -0,0 +1,62 @@
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
describe HTTParty::Request do
context "SSL certificate verification" do
before do
FakeWeb.allow_net_connect = true
end
after do
FakeWeb.allow_net_connect = false
end
it "should work when no trusted CA list is specified" do
ssl_verify_test(nil, nil, "selfsigned.crt").should == {'success' => true}
end
it "should work when no trusted CA list is specified, even with a bogus hostname" do
ssl_verify_test(nil, nil, "bogushost.crt").should == {'success' => true}
end
it "should work when using ssl_ca_file with a self-signed CA" do
ssl_verify_test(:ssl_ca_file, "selfsigned.crt", "selfsigned.crt").should == {'success' => true}
end
it "should work when using ssl_ca_file with a certificate authority" do
ssl_verify_test(:ssl_ca_file, "ca.crt", "server.crt").should == {'success' => true}
end
it "should work when using ssl_ca_path with a certificate authority" do
http = Net::HTTP.new('www.google.com', 443, nil, nil, nil, nil)
response = stub(Net::HTTPResponse, :[] => '', :body => '', :to_hash => {})
http.stub(:request).and_return(response)
Net::HTTP.should_receive(:new).with('www.google.com', 443, nil, nil, nil, nil).and_return(http)
http.should_receive(:ca_path=).with('/foo/bar')
HTTParty.get('https://www.google.com', :ssl_ca_path => '/foo/bar')
end
it "should fail when using ssl_ca_file and the server uses an unrecognized certificate authority" do
lambda do
ssl_verify_test(:ssl_ca_file, "ca.crt", "selfsigned.crt")
end.should raise_error(OpenSSL::SSL::SSLError)
end
it "should fail when using ssl_ca_path and the server uses an unrecognized certificate authority" do
lambda do
ssl_verify_test(:ssl_ca_path, ".", "selfsigned.crt")
end.should raise_error(OpenSSL::SSL::SSLError)
end
it "should fail when using ssl_ca_file and the server uses a bogus hostname" do
lambda do
ssl_verify_test(:ssl_ca_file, "ca.crt", "bogushost.crt")
end.should raise_error(OpenSSL::SSL::SSLError)
end
it "should fail when using ssl_ca_path and the server uses a bogus hostname" do
lambda do
ssl_verify_test(:ssl_ca_path, ".", "bogushost.crt")
end.should raise_error(OpenSSL::SSL::SSLError)
end
end
end

View File

@ -0,0 +1,703 @@
require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
describe HTTParty do
before(:each) do
@klass = Class.new
@klass.instance_eval { include HTTParty }
end
describe "AllowedFormats deprecated" do
before do
Kernel.stub(:warn)
end
it "warns with a deprecation message" do
Kernel.should_receive(:warn).with("Deprecated: Use HTTParty::Parser::SupportedFormats")
HTTParty::AllowedFormats
end
it "returns HTTPart::Parser::SupportedFormats" do
HTTParty::AllowedFormats.should == HTTParty::Parser::SupportedFormats
end
end
describe "pem" do
it 'should set the pem content' do
@klass.pem 'PEM-CONTENT'
@klass.default_options[:pem].should == 'PEM-CONTENT'
end
it "should set the password to nil if it's not provided" do
@klass.pem 'PEM-CONTENT'
@klass.default_options[:pem_password].should be_nil
end
it 'should set the password' do
@klass.pem 'PEM-CONTENT', 'PASSWORD'
@klass.default_options[:pem_password].should == 'PASSWORD'
end
end
describe 'ssl_version' do
it 'should set the ssl_version content' do
@klass.ssl_version :SSLv3
@klass.default_options[:ssl_version].should == :SSLv3
end
end
describe 'http_proxy' do
it 'should set the address' do
@klass.http_proxy 'proxy.foo.com', 80
options = @klass.default_options
options[:http_proxyaddr].should == 'proxy.foo.com'
options[:http_proxyport].should == 80
end
it 'should set the proxy user and pass when they are provided' do
@klass.http_proxy 'proxy.foo.com', 80, 'user', 'pass'
options = @klass.default_options
options[:http_proxyuser].should == 'user'
options[:http_proxypass].should == 'pass'
end
end
describe "base uri" do
before(:each) do
@klass.base_uri('api.foo.com/v1')
end
it "should have reader" do
@klass.base_uri.should == 'http://api.foo.com/v1'
end
it 'should have writer' do
@klass.base_uri('http://api.foobar.com')
@klass.base_uri.should == 'http://api.foobar.com'
end
it 'should not modify the parameter during assignment' do
uri = 'http://api.foobar.com'
@klass.base_uri(uri)
uri.should == 'http://api.foobar.com'
end
end
describe ".disable_rails_query_string_format" do
it "sets the query string normalizer to HTTParty::Request::NON_RAILS_QUERY_STRING_NORMALIZER" do
@klass.disable_rails_query_string_format
@klass.default_options[:query_string_normalizer].should == HTTParty::Request::NON_RAILS_QUERY_STRING_NORMALIZER
end
end
describe ".normalize_base_uri" do
it "should add http if not present for non ssl requests" do
uri = HTTParty.normalize_base_uri('api.foobar.com')
uri.should == 'http://api.foobar.com'
end
it "should add https if not present for ssl requests" do
uri = HTTParty.normalize_base_uri('api.foo.com/v1:443')
uri.should == 'https://api.foo.com/v1:443'
end
it "should not remove https for ssl requests" do
uri = HTTParty.normalize_base_uri('https://api.foo.com/v1:443')
uri.should == 'https://api.foo.com/v1:443'
end
it 'should not modify the parameter' do
uri = 'http://api.foobar.com'
HTTParty.normalize_base_uri(uri)
uri.should == 'http://api.foobar.com'
end
it "should not treat uri's with a port of 4430 as ssl" do
uri = HTTParty.normalize_base_uri('http://api.foo.com:4430/v1')
uri.should == 'http://api.foo.com:4430/v1'
end
end
describe "headers" do
def expect_headers(header={})
HTTParty::Request.should_receive(:new) \
.with(anything, anything, hash_including({ :headers => header })) \
.and_return(mock("mock response", :perform => nil))
end
it "should default to empty hash" do
@klass.headers.should == {}
end
it "should be able to be updated" do
init_headers = {:foo => 'bar', :baz => 'spax'}
@klass.headers init_headers
@klass.headers.should == init_headers
end
it "uses the class headers when sending a request" do
expect_headers(:foo => 'bar')
@klass.headers(:foo => 'bar')
@klass.get('')
end
it "overwrites class headers when passing in headers" do
expect_headers(:baz => 'spax')
@klass.headers(:foo => 'bar')
@klass.get('', :headers => {:baz => 'spax'})
end
context "with cookies" do
it 'utilizes the class-level cookies' do
expect_headers(:foo => 'bar', 'cookie' => 'type=snickerdoodle')
@klass.headers(:foo => 'bar')
@klass.cookies(:type => 'snickerdoodle')
@klass.get('')
end
it 'adds cookies to the headers' do
expect_headers(:foo => 'bar', 'cookie' => 'type=snickerdoodle')
@klass.headers(:foo => 'bar')
@klass.get('', :cookies => {:type => 'snickerdoodle'})
end
it 'adds optional cookies to the optional headers' do
expect_headers(:baz => 'spax', 'cookie' => 'type=snickerdoodle')
@klass.get('', :cookies => {:type => 'snickerdoodle'}, :headers => {:baz => 'spax'})
end
end
end
describe "cookies" do
def expect_cookie_header(s)
HTTParty::Request.should_receive(:new) \
.with(anything, anything, hash_including({ :headers => { "cookie" => s } })) \
.and_return(mock("mock response", :perform => nil))
end
it "should not be in the headers by default" do
HTTParty::Request.stub!(:new).and_return(stub(nil, :perform => nil))
@klass.get("")
@klass.headers.keys.should_not include("cookie")
end
it "should raise an ArgumentError if passed a non-Hash" do
lambda do
@klass.cookies("nonsense")
end.should raise_error(ArgumentError)
end
it "should allow a cookie to be specified with a one-off request" do
expect_cookie_header "type=snickerdoodle"
@klass.get("", :cookies => { :type => "snickerdoodle" })
end
describe "when a cookie is set at the class level" do
before(:each) do
@klass.cookies({ :type => "snickerdoodle" })
end
it "should include that cookie in the request" do
expect_cookie_header "type=snickerdoodle"
@klass.get("")
end
it "should pass the proper cookies when requested multiple times" do
2.times do
expect_cookie_header "type=snickerdoodle"
@klass.get("")
end
end
it "should allow the class defaults to be overridden" do
expect_cookie_header "type=chocolate_chip"
@klass.get("", :cookies => { :type => "chocolate_chip" })
end
end
describe "in a class with multiple methods that use different cookies" do
before(:each) do
@klass.instance_eval do
def first_method
get("first_method", :cookies => { :first_method_cookie => "foo" })
end
def second_method
get("second_method", :cookies => { :second_method_cookie => "foo" })
end
end
end
it "should not allow cookies used in one method to carry over into other methods" do
expect_cookie_header "first_method_cookie=foo"
@klass.first_method
expect_cookie_header "second_method_cookie=foo"
@klass.second_method
end
end
end
describe "default params" do
it "should default to empty hash" do
@klass.default_params.should == {}
end
it "should be able to be updated" do
new_defaults = {:foo => 'bar', :baz => 'spax'}
@klass.default_params new_defaults
@klass.default_params.should == new_defaults
end
end
describe "default timeout" do
it "should default to nil" do
@klass.default_options[:timeout].should == nil
end
it "should support updating" do
@klass.default_timeout 10
@klass.default_options[:timeout].should == 10
end
it "should support floats" do
@klass.default_timeout 0.5
@klass.default_options[:timeout].should == 0.5
end
end
describe "debug_output" do
it "stores the given stream as a default_option" do
@klass.debug_output $stdout
@klass.default_options[:debug_output].should == $stdout
end
it "stores the $stderr stream by default" do
@klass.debug_output
@klass.default_options[:debug_output].should == $stderr
end
end
describe "basic http authentication" do
it "should work" do
@klass.basic_auth 'foobar', 'secret'
@klass.default_options[:basic_auth].should == {:username => 'foobar', :password => 'secret'}
end
end
describe "digest http authentication" do
it "should work" do
@klass.digest_auth 'foobar', 'secret'
@klass.default_options[:digest_auth].should == {:username => 'foobar', :password => 'secret'}
end
end
describe "parser" do
class CustomParser
def self.parse(body)
return {:sexy => true}
end
end
let(:parser) do
Proc.new{ |data, format| CustomParser.parse(data) }
end
it "should set parser options" do
@klass.parser parser
@klass.default_options[:parser].should == parser
end
it "should be able parse response with custom parser" do
@klass.parser parser
FakeWeb.register_uri(:get, 'http://twitter.com/statuses/public_timeline.xml', :body => 'tweets')
custom_parsed_response = @klass.get('http://twitter.com/statuses/public_timeline.xml')
custom_parsed_response[:sexy].should == true
end
it "raises UnsupportedFormat when the parser cannot handle the format" do
@klass.format :json
class MyParser < HTTParty::Parser
SupportedFormats = {}
end unless defined?(MyParser)
expect do
@klass.parser MyParser
end.to raise_error(HTTParty::UnsupportedFormat)
end
it 'does not validate format whe custom parser is a proc' do
expect do
@klass.format :json
@klass.parser lambda {|body, format|}
end.to_not raise_error(HTTParty::UnsupportedFormat)
end
end
describe "connection_adapter" do
let(:uri) { 'http://google.com/api.json' }
let(:connection_adapter) { mock('CustomConnectionAdapter') }
it "should set the connection_adapter" do
@klass.connection_adapter connection_adapter
@klass.default_options[:connection_adapter].should be connection_adapter
end
it "should set the connection_adapter_options when provided" do
options = {:foo => :bar}
@klass.connection_adapter connection_adapter, options
@klass.default_options[:connection_adapter_options].should be options
end
it "should not set the connection_adapter_options when not provided" do
@klass.connection_adapter connection_adapter
@klass.default_options[:connection_adapter_options].should be_nil
end
it "should process a request with a connection from the adapter" do
connection_adapter_options = {:foo => :bar}
connection_adapter.should_receive(:call) do |u,o|
o[:connection_adapter_options].should == connection_adapter_options
HTTParty::ConnectionAdapter.call(u,o)
end.with(URI.parse(uri), kind_of(Hash))
FakeWeb.register_uri(:get, uri, :body => 'stuff')
@klass.connection_adapter connection_adapter, connection_adapter_options
@klass.get(uri).should == 'stuff'
end
end
describe "format" do
it "should allow xml" do
@klass.format :xml
@klass.default_options[:format].should == :xml
end
it "should allow json" do
@klass.format :json
@klass.default_options[:format].should == :json
end
it "should allow yaml" do
@klass.format :yaml
@klass.default_options[:format].should == :yaml
end
it "should allow plain" do
@klass.format :plain
@klass.default_options[:format].should == :plain
end
it 'should not allow funky format' do
lambda do
@klass.format :foobar
end.should raise_error(HTTParty::UnsupportedFormat)
end
it 'should only print each format once with an exception' do
lambda do
@klass.format :foobar
end.should raise_error(HTTParty::UnsupportedFormat, "':foobar' Must be one of: html, json, plain, xml, yaml")
end
it 'sets the default parser' do
@klass.default_options[:parser].should be_nil
@klass.format :json
@klass.default_options[:parser].should == HTTParty::Parser
end
it 'does not reset parser to the default parser' do
my_parser = lambda {}
@klass.parser my_parser
@klass.format :json
@klass.parser.should == my_parser
end
end
describe "#no_follow" do
it "sets no_follow to false by default" do
@klass.no_follow
@klass.default_options[:no_follow].should be_false
end
it "sets the no_follow option to true" do
@klass.no_follow true
@klass.default_options[:no_follow].should be_true
end
end
describe "#maintain_method_across_redirects" do
it "sets maintain_method_across_redirects to true by default" do
@klass.maintain_method_across_redirects
@klass.default_options[:maintain_method_across_redirects].should be_true
end
it "sets the maintain_method_across_redirects option to false" do
@klass.maintain_method_across_redirects false
@klass.default_options[:maintain_method_across_redirects].should be_false
end
end
describe ".follow_redirects" do
it "sets follow redirects to true by default" do
@klass.follow_redirects
@klass.default_options[:follow_redirects].should be_true
end
it "sets the follow_redirects option to false" do
@klass.follow_redirects false
@klass.default_options[:follow_redirects].should be_false
end
end
describe ".query_string_normalizer" do
it "sets the query_string_normalizer option" do
normalizer = proc {}
@klass.query_string_normalizer normalizer
@klass.default_options[:query_string_normalizer].should == normalizer
end
end
describe "with explicit override of automatic redirect handling" do
before do
@request = HTTParty::Request.new(Net::HTTP::Get, 'http://api.foo.com/v1', :format => :xml, :no_follow => true)
@redirect = stub_response 'first redirect', 302
@redirect['location'] = 'http://foo.com/bar'
HTTParty::Request.stub(:new => @request)
end
it "should fail with redirected GET" do
lambda do
@error = @klass.get('/foo', :no_follow => true)
end.should raise_error(HTTParty::RedirectionTooDeep) {|e| e.response.body.should == 'first redirect'}
end
it "should fail with redirected POST" do
lambda do
@klass.post('/foo', :no_follow => true)
end.should raise_error(HTTParty::RedirectionTooDeep) {|e| e.response.body.should == 'first redirect'}
end
it "should fail with redirected PATCH" do
lambda do
@klass.patch('/foo', :no_follow => true)
end.should raise_error(HTTParty::RedirectionTooDeep) {|e| e.response.body.should == 'first redirect'}
end
it "should fail with redirected DELETE" do
lambda do
@klass.delete('/foo', :no_follow => true)
end.should raise_error(HTTParty::RedirectionTooDeep) {|e| e.response.body.should == 'first redirect'}
end
it "should fail with redirected PUT" do
lambda do
@klass.put('/foo', :no_follow => true)
end.should raise_error(HTTParty::RedirectionTooDeep) {|e| e.response.body.should == 'first redirect'}
end
it "should fail with redirected HEAD" do
lambda do
@klass.head('/foo', :no_follow => true)
end.should raise_error(HTTParty::RedirectionTooDeep) {|e| e.response.body.should == 'first redirect'}
end
it "should fail with redirected OPTIONS" do
lambda do
@klass.options('/foo', :no_follow => true)
end.should raise_error(HTTParty::RedirectionTooDeep) {|e| e.response.body.should == 'first redirect'}
end
end
describe "with multiple class definitions" do
before(:each) do
@klass.instance_eval do
base_uri "http://first.com"
default_params :one => 1
end
@additional_klass = Class.new
@additional_klass.instance_eval do
include HTTParty
base_uri "http://second.com"
default_params :two => 2
end
end
it "should not run over each others options" do
@klass.default_options.should == { :base_uri => 'http://first.com', :default_params => { :one => 1 } }
@additional_klass.default_options.should == { :base_uri => 'http://second.com', :default_params => { :two => 2 } }
end
end
describe "two child classes inheriting from one parent" do
before(:each) do
@parent = Class.new do
include HTTParty
def self.name
"Parent"
end
end
@child1 = Class.new(@parent)
@child2 = Class.new(@parent)
end
it "does not modify each others inherited attributes" do
@child1.default_params :joe => "alive"
@child2.default_params :joe => "dead"
@child1.default_options.should == { :default_params => {:joe => "alive"} }
@child2.default_options.should == { :default_params => {:joe => "dead"} }
@parent.default_options.should == { }
end
it "inherits default_options from the superclass" do
@parent.basic_auth 'user', 'password'
@child1.default_options.should == {:basic_auth => {:username => 'user', :password => 'password'}}
@child1.basic_auth 'u', 'p' # modifying child1 has no effect on child2
@child2.default_options.should == {:basic_auth => {:username => 'user', :password => 'password'}}
end
it "doesn't modify the parent's default options" do
@parent.basic_auth 'user', 'password'
@child1.basic_auth 'u', 'p'
@child1.default_options.should == {:basic_auth => {:username => 'u', :password => 'p'}}
@child1.basic_auth 'email', 'token'
@child1.default_options.should == {:basic_auth => {:username => 'email', :password => 'token'}}
@parent.default_options.should == {:basic_auth => {:username => 'user', :password => 'password'}}
end
it "doesn't modify hashes in the parent's default options" do
@parent.headers 'Accept' => 'application/json'
@child1.headers 'Accept' => 'application/xml'
@parent.default_options[:headers].should == {'Accept' => 'application/json'}
@child1.default_options[:headers].should == {'Accept' => 'application/xml'}
end
it "inherits default_cookies from the parent class" do
@parent.cookies 'type' => 'chocolate_chip'
@child1.default_cookies.should == {"type" => "chocolate_chip"}
@child1.cookies 'type' => 'snickerdoodle'
@child1.default_cookies.should == {"type" => "snickerdoodle"}
@child2.default_cookies.should == {"type" => "chocolate_chip"}
end
it "doesn't modify the parent's default cookies" do
@parent.cookies 'type' => 'chocolate_chip'
@child1.cookies 'type' => 'snickerdoodle'
@child1.default_cookies.should == {"type" => "snickerdoodle"}
@parent.default_cookies.should == {"type" => "chocolate_chip"}
end
end
describe "grand parent with inherited callback" do
before do
@grand_parent = Class.new do
def self.inherited(subclass)
subclass.instance_variable_set(:@grand_parent, true)
end
end
@parent = Class.new(@grand_parent) do
include HTTParty
end
end
it "continues running the #inherited on the parent" do
child = Class.new(@parent)
child.instance_variable_get(:@grand_parent).should be_true
end
end
describe "#get" do
it "should be able to get html" do
stub_http_response_with('google.html')
HTTParty.get('http://www.google.com').should == file_fixture('google.html')
end
it "should be able to get chunked html" do
chunks = ["Chunk1", "Chunk2", "Chunk3", "Chunk4"]
stub_chunked_http_response_with(chunks)
HTTParty.get('http://www.google.com') do |fragment|
chunks.should include(fragment)
end.should == chunks.join
end
it "should be able parse response type json automatically" do
stub_http_response_with('twitter.json')
tweets = HTTParty.get('http://twitter.com/statuses/public_timeline.json')
tweets.size.should == 20
tweets.first['user'].should == {
"name" => "Pyk",
"url" => nil,
"id" => "7694602",
"description" => nil,
"protected" => false,
"screen_name" => "Pyk",
"followers_count" => 1,
"location" => "Opera Plaza, California",
"profile_image_url" => "http://static.twitter.com/images/default_profile_normal.png"
}
end
it "should be able parse response type xml automatically" do
stub_http_response_with('twitter.xml')
tweets = HTTParty.get('http://twitter.com/statuses/public_timeline.xml')
tweets['statuses'].size.should == 20
tweets['statuses'].first['user'].should == {
"name" => "Magic 8 Bot",
"url" => nil,
"id" => "17656026",
"description" => "ask me a question",
"protected" => "false",
"screen_name" => "magic8bot",
"followers_count" => "90",
"profile_image_url" => "http://s3.amazonaws.com/twitter_production/profile_images/65565851/8ball_large_normal.jpg",
"location" => nil
}
end
it "should not get undefined method add_node for nil class for the following xml" do
stub_http_response_with('undefined_method_add_node_for_nil.xml')
result = HTTParty.get('http://foobar.com')
result.should == {"Entities"=>{"href"=>"https://s3-sandbox.parature.com/api/v1/5578/5633/Account", "results"=>"0", "total"=>"0", "page_size"=>"25", "page"=>"1"}}
end
it "should parse empty response fine" do
stub_http_response_with('empty.xml')
result = HTTParty.get('http://foobar.com')
result.should be_nil
end
it "should accept http URIs" do
stub_http_response_with('google.html')
lambda do
HTTParty.get('http://google.com')
end.should_not raise_error(HTTParty::UnsupportedURIScheme)
end
it "should accept https URIs" do
stub_http_response_with('google.html')
lambda do
HTTParty.get('https://google.com')
end.should_not raise_error(HTTParty::UnsupportedURIScheme)
end
it "should raise an ArgumentError on URIs that are not http or https" do
lambda do
HTTParty.get("file:///there_is_no_party_on/my/filesystem")
end.should raise_error(HTTParty::UnsupportedURIScheme)
end
it "should raise an InvalidURIError on URIs that can't be parsed at all" do
lambda do
HTTParty.get("It's the one that says 'Bad URI'")
end.should raise_error(URI::InvalidURIError)
end
end
end

View File

@ -0,0 +1,2 @@
--colour
--backtrace

View File

@ -0,0 +1,30 @@
$:.push File.expand_path("../lib", __FILE__)
require "httparty"
require 'spec/autorun'
require 'fakeweb'
def file_fixture(filename)
open(File.join(File.dirname(__FILE__), 'fixtures', "#{filename.to_s}")).read
end
Dir[File.expand_path(File.join(File.dirname(__FILE__),'support','**','*.rb'))].each {|f| require f}
Spec::Runner.configure do |config|
config.include HTTParty::StubResponse
config.include HTTParty::SSLTestHelper
config.before(:suite) do
FakeWeb.allow_net_connect = false
end
config.after(:suite) do
FakeWeb.allow_net_connect = true
end
end
Spec::Matchers.define :use_ssl do
match do |connection|
connection.use_ssl?
end
end

View File

@ -0,0 +1,47 @@
require 'pathname'
module HTTParty
module SSLTestHelper
def ssl_verify_test(mode, ca_basename, server_cert_filename)
options = {
:format => :json,
:timeout => 30,
}
if mode
ca_path = File.expand_path("../../fixtures/ssl/generated/#{ca_basename}", __FILE__)
raise ArgumentError.new("#{ca_path} does not exist") unless File.exist?(ca_path)
options[mode] = ca_path
end
begin
test_server = SSLTestServer.new(
:rsa_key => File.read(File.expand_path("../../fixtures/ssl/generated/server.key", __FILE__)),
:cert => File.read(File.expand_path("../../fixtures/ssl/generated/#{server_cert_filename}", __FILE__)))
test_server.start
if mode
ca_path = File.expand_path("../../fixtures/ssl/generated/#{ca_basename}", __FILE__)
raise ArgumentError.new("#{ca_path} does not exist") unless File.exist?(ca_path)
return HTTParty.get("https://localhost:#{test_server.port}/", :format => :json, :timeout => 30, mode => ca_path)
else
return HTTParty.get("https://localhost:#{test_server.port}/", :format => :json, :timeout => 30)
end
ensure
test_server.stop if test_server
end
test_server = SSLTestServer.new({
:rsa_key => path.join('server.key').read,
:cert => path.join(server_cert_filename).read,
})
test_server.start
HTTParty.get("https://localhost:#{test_server.port}/", options)
ensure
test_server.stop if test_server
end
end
end

View File

@ -0,0 +1,80 @@
require 'openssl'
require 'socket'
require 'thread'
# NOTE: This code is garbage. It probably has deadlocks, it might leak
# threads, and otherwise cause problems in a real system. It's really only
# intended for testing HTTParty.
class SSLTestServer
attr_accessor :ctx # SSLContext object
attr_reader :port
def initialize(options={})
@ctx = OpenSSL::SSL::SSLContext.new
@ctx.cert = OpenSSL::X509::Certificate.new(options[:cert])
@ctx.key = OpenSSL::PKey::RSA.new(options[:rsa_key])
@ctx.verify_mode = OpenSSL::SSL::VERIFY_NONE # Don't verify client certificate
@port = options[:port] || 0
@thread = nil
@stopping_mutex = Mutex.new
@stopping = false
end
def start
@raw_server = TCPServer.new(@port)
if @port == 0
@port = Socket::getnameinfo(@raw_server.getsockname, Socket::NI_NUMERICHOST|Socket::NI_NUMERICSERV)[1].to_i
end
@ssl_server = OpenSSL::SSL::SSLServer.new(@raw_server, @ctx)
@stopping_mutex.synchronize{
return if @stopping
@thread = Thread.new{ thread_main }
}
nil
end
def stop
@stopping_mutex.synchronize{
return if @stopping
@stopping = true
}
@thread.join
end
private
def thread_main
until @stopping_mutex.synchronize{ @stopping }
(rr,ww,ee) = select([@ssl_server.to_io], nil, nil, 0.1)
next unless rr && rr.include?(@ssl_server.to_io)
socket = @ssl_server.accept
Thread.new{
header = []
until (line = socket.readline).rstrip.empty?
header << line
end
response =<<EOF
HTTP/1.1 200 OK
Connection: close
Content-Type: application/json; charset=UTF-8
{"success":true}
EOF
socket.write(response.gsub(/\r\n/n, "\n").gsub(/\n/n, "\r\n"))
socket.close
}
end
@ssl_server.close
end
end

View File

@ -0,0 +1,43 @@
module HTTParty
module StubResponse
def stub_http_response_with(filename)
format = filename.split('.').last.intern
data = file_fixture(filename)
response = Net::HTTPOK.new("1.1", 200, "Content for you")
response.stub!(:body).and_return(data)
http_request = HTTParty::Request.new(Net::HTTP::Get, 'http://localhost', :format => format)
http_request.stub_chain(:http, :request).and_return(response)
HTTParty::Request.should_receive(:new).and_return(http_request)
end
def stub_chunked_http_response_with(chunks)
response = Net::HTTPResponse.new("1.1", 200, nil)
response.stub(:chunked_data).and_return(chunks)
def response.read_body(&block)
@body || chunked_data.each(&block)
end
http_request = HTTParty::Request.new(Net::HTTP::Get, 'http://localhost', :format => "html")
http_request.stub_chain(:http, :request).and_yield(response).and_return(response)
HTTParty::Request.should_receive(:new).and_return(http_request)
end
def stub_response(body, code = 200)
@request.options[:base_uri] ||= 'http://localhost'
unless defined?(@http) && @http
@http = Net::HTTP.new('localhost', 80)
@request.stub!(:http).and_return(@http)
end
response = Net::HTTPResponse::CODE_TO_OBJ[code.to_s].new("1.1", code, body)
response.stub!(:body).and_return(body)
@http.stub!(:request).and_return(response)
response
end
end
end

View File

@ -0,0 +1,47 @@
@media screen, projection {
/*
Copyright (c) 2007, Yahoo! Inc. All rights reserved.
Code licensed under the BSD License:
http://developer.yahoo.net/yui/license.txt
version: 2.2.0
*/
body {font:13px arial,helvetica,clean,sans-serif;*font-size:small;*font:x-small;}table {font-size:inherit;font:100%;}select, input, textarea {font:99% arial,helvetica,clean,sans-serif;}pre, code {font:115% monospace;*font-size:100%;}body * {line-height:1.22em;}
body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,form,fieldset,input,textarea,p,blockquote,th,td{margin:0;padding:0;}table{border-collapse:collapse;border-spacing:0;}fieldset,img{border:0;}address,caption,cite,code,dfn,em,strong,th,var{font-style:normal;font-weight:normal;}/*ol,ul {list-style:none;}*/caption,th {text-align:left;}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal;}q:before,q:after{content:'';}abbr,acronym {border:0;}
/* end of yahoo reset and fonts */
body {color:#333; background:#4b1a1a; line-height:1.3;}
p {margin:0 0 20px;}
a {color:#4b1a1a;}
a:hover {text-decoration:none;}
strong {font-weight:bold;}
em {font-style:italics;}
h1,h2,h3,h4,h5,h6 {font-weight:bold;}
h1 {font-size:197%; margin:30px 0; color:#4b1a1a;}
h2 {font-size:174%; margin:20px 0; color:#b8111a;}
h3 {font-size:152%; margin:10px 0;}
h4 {font-size:129%; margin:10px 0;}
pre {background:#eee; margin:0 0 20px; padding:20px; border:1px solid #ccc; font-size:100%; overflow:auto;}
code {font-size:100%; margin:0; padding:0;}
ul, ol {margin:10px 0 10px 25px;}
ol li {margin:0 0 10px;}
div#wrapper {background:#fff; width:560px; margin:0 auto; padding:20px; border:10px solid #bc8c46; border-width:0 10px;}
div#header {position:relative; border-bottom:1px dotted; margin:0 0 10px; padding:0 0 10px;}
div#header p {margin:0; padding:0;}
div#header h1 {margin:0; padding:0;}
ul#nav {position:absolute; top:0; right:0; list-style:none; margin:0; padding:0;}
ul#nav li {display:inline; padding:0 0 0 5px;}
ul#nav li a {}
div#content {}
div#footer {margin:40px 0 0; border-top:1px dotted; padding:10px 0 0;}
}

View File

@ -0,0 +1,73 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>HTTParty by John Nunemaker</title>
<link rel="stylesheet" href="css/common.css" type="text/css" />
</head>
<body>
<div id="wrapper">
<div id="header">
<h1>HTTParty</h1>
<p>Tonight we're gonna HTTParty like it's 1999!</p>
<ul id="nav">
<li><a href="rdoc/">Docs</a></li>
<li><a href="http://github.com/jnunemaker/httparty">Github</a></li>
<li><a href="http://rubyforge.org/projects/httparty/">Rubyforge</a></li>
</ul>
</div>
<div id="content">
<h2>Install</h2>
<pre><code>$ sudo gem install httparty</code></pre>
<h2>Some Quick Examples</h2>
<p>The following is a simple example of wrapping Twitter's API for posting updates.</p>
<pre><code>class Twitter
include HTTParty
base_uri 'twitter.com'
basic_auth 'username', 'password'
end
Twitter.post('/statuses/update.json', :query => {:status => "It's an HTTParty and everyone is invited!"})</code></pre>
<p>That is really it! The object returned is a ruby hash that is decoded from Twitter's json response. JSON parsing is used because of the .json extension in the path of the request. You can also explicitly set a format (see the examples). </p>
<p>That works and all but what if you don't want to embed your username and password in the class? Below is an example to fix that:</p>
<pre><code>class Twitter
include HTTParty
base_uri 'twitter.com'
def initialize(u, p)
@auth = {:username => u, :password => p}
end
def post(text)
options = { :query => {:status => text}, :basic_auth => @auth }
self.class.post('/statuses/update.json', options)
end
end
Twitter.new('username', 'password').post("It's an HTTParty and everyone is invited!")</code></pre>
<p><strong>More Examples:</strong> There are <a href="http://github.com/jnunemaker/httparty/tree/master/examples/">several examples in the gem itself</a>.</p>
<h2>Support</h2>
<p>Conversations welcome in the <a href="http://groups.google.com/group/httparty-gem">google group</a> and bugs/features over at <a href="http://github.com/jnunemaker/httparty">Github</a>.</p>
</div>
<div id="footer">
<p>Created by <a href="http://addictedtonew.com/about/">John Nunemaker</a> |
<a href="http://orderedlist.com/">Hire Me at Ordered List</a></p>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,5 @@
LICENSE.md
README.md
bin/*
features/**/*.feature
lib/**/*.rb

View File

@ -0,0 +1,3 @@
--color
--fail-fast
--order random

View File

@ -0,0 +1,10 @@
language: ruby
rvm:
- rbx-18mode
- rbx-19mode
- jruby-18mode
- jruby-19mode
- 1.8.7
- 1.9.2
- 1.9.3
- ruby-head

View File

@ -0,0 +1,7 @@
source 'https://rubygems.org'
gem 'json', '~> 1.4', :require => nil
gem 'oj', '~> 1.0', :require => nil, :platforms => [:ruby, :mswin, :mingw]
gem 'yajl-ruby', '~> 1.0', :require => nil, :platforms => [:ruby, :mswin, :mingw]
gemspec

View File

@ -0,0 +1,20 @@
Copyright (c) 2010 Michael Bleigh, Josh Kalderimis, Erik Michaels-Ober, and Intridea, Inc.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,72 @@
# MultiJSON [![Build Status](https://secure.travis-ci.org/intridea/multi_json.png?branch=master)][travis] [![Dependency Status](https://gemnasium.com/intridea/multi_json.png?travis)][gemnasium]
[travis]: http://travis-ci.org/intridea/multi_json
[gemnasium]: https://gemnasium.com/intridea/multi_json
Lots of Ruby libraries parse JSON and everyone has their favorite JSON coder.
Instead of choosing a single JSON coder and forcing users of your library to be
stuck with it, you can use MultiJSON instead, which will simply choose the
fastest available JSON coder. Here's how to use it:
require 'multi_json'
MultiJson.load('{"abc":"def"}') #=> {"abc" => "def"}
MultiJson.load('{"abc":"def"}', :symbolize_keys => true) #=> {:abc => "def"}
MultiJson.dump({:abc => 'def'}) # convert Ruby back to JSON
MultiJson.dump({:abc => 'def'}, :pretty => true) # encoded in a pretty form (if supported by the coder)
The `use` method, which sets the MultiJson adapter, takes either a symbol or a
class (to allow for custom JSON parsers) that responds to both `.load` and `.dump`
at the class level.
MultiJSON tries to have intelligent defaulting. That is, if you have any of the
supported engines already loaded, it will utilize them before attempting to
load any. When loading, libraries are ordered by speed. First Oj, then Yajl,
then the JSON gem, then JSON pure. If no other JSON library is available,
MultiJSON falls back to [OkJson][], a simple, vendorable JSON parser.
[okjson]: https://github.com/kr/okjson
## Supported JSON Engines
* [Oj](https://github.com/ohler55/oj) Optimized JSON by Peter Ohler
* [Yajl](https://github.com/brianmario/yajl-ruby) Yet Another JSON Library by Brian Lopez
* [JSON](https://github.com/flori/json) The default JSON gem with C-extensions (ships with Ruby 1.9)
* [JSON Pure](https://github.com/flori/json) A Ruby variant of the JSON gem
* [NSJSONSerialization](https://developer.apple.com/library/ios/#documentation/Foundation/Reference/NSJSONSerialization_Class/Reference/Reference.html) Wrapper for Apple's NSJSONSerialization in the Cocoa Framework (MacRuby only)
* [OkJson][okjson] A simple, vendorable JSON parser
## Supported Ruby Versions
This library aims to support and is [tested against][travis] the following Ruby
implementations:
* Ruby 1.8.7
* Ruby 1.9.2
* Ruby 1.9.3
* [JRuby][]
* [Rubinius][]
* [MacRuby][] (not tested on Travis CI)
[jruby]: http://www.jruby.org/
[rubinius]: http://rubini.us/
[macruby]: http://www.macruby.org/
If something doesn't work on one of these interpreters, it should be considered
a bug.
This library may inadvertently work (or seem to work) on other Ruby
implementations, however support will only be provided for the versions listed
above.
If you would like this library to support another Ruby version, you may
volunteer to be a maintainer. Being a maintainer entails making sure all tests
run and pass on that implementation. When something breaks on your
implementation, you will be personally responsible for providing patches in a
timely fashion. If critical issues for a particular implementation exist at the
time of a major release, support for that Ruby version may be dropped.
## Copyright
Copyright (c) 2010 Michael Bleigh, Josh Kalderimis, Erik Michaels-Ober, and Intridea, Inc.
See [LICENSE][] for details.
[license]: https://github.com/intridea/multi_json/blob/master/LICENSE.md

View File

@ -0,0 +1,20 @@
require 'bundler'
Bundler::GemHelper.install_tasks
require 'rspec/core/rake_task'
desc "Run all examples"
RSpec::Core::RakeTask.new(:spec)
task :default => :spec
task :test => :spec
namespace :doc do
require 'rdoc/task'
require File.expand_path('../lib/multi_json/version', __FILE__)
RDoc::Task.new do |rdoc|
rdoc.rdoc_dir = 'rdoc'
rdoc.title = "multi_json #{MultiJson::VERSION}"
rdoc.main = 'README.md'
rdoc.rdoc_files.include('README.md', 'LICENSE.md', 'lib/**/*.rb')
end
end

View File

@ -0,0 +1,122 @@
module MultiJson
class DecodeError < StandardError
attr_reader :data
def initialize(message="", backtrace=[], data="")
super(message)
self.set_backtrace(backtrace)
@data = data
end
end
@adapter = nil
REQUIREMENT_MAP = [
["oj", :oj],
["yajl", :yajl],
["json", :json_gem],
["json/pure", :json_pure]
]
class << self
# The default adapter based on what you currently
# have loaded and installed. First checks to see
# if any adapters are already loaded, then checks
# to see which are installed if none are loaded.
def default_adapter
return :oj if defined?(::Oj)
return :yajl if defined?(::Yajl)
return :json_gem if defined?(::JSON)
REQUIREMENT_MAP.each do |(library, adapter)|
begin
require library
return adapter
rescue LoadError
next
end
end
Kernel.warn "[WARNING] MultiJson is using the default adapter (ok_json). We recommend loading a different JSON library to improve performance."
:ok_json
end
# :nodoc:
alias :default_engine :default_adapter
# Get the current adapter class.
def adapter
return @adapter if @adapter
self.use self.default_adapter
@adapter
end
# :nodoc:
alias :engine :adapter
# Set the JSON parser utilizing a symbol, string, or class.
# Supported by default are:
#
# * <tt>:oj</tt>
# * <tt>:json_gem</tt>
# * <tt>:json_pure</tt>
# * <tt>:ok_json</tt>
# * <tt>:yajl</tt>
# * <tt>:nsjsonserialization</tt> (MacRuby only)
def use(new_adapter)
@adapter = load_adapter(new_adapter)
end
alias :adapter= :use
# :nodoc:
alias :engine= :use
def load_adapter(new_adapter)
case new_adapter
when String, Symbol
require "multi_json/adapters/#{new_adapter}"
MultiJson::Adapters.const_get(:"#{new_adapter.to_s.split('_').map{|s| s.capitalize}.join('')}")
when NilClass, FalseClass
default_adapter = self.default_adapter
require "multi_json/adapters/#{default_adapter}"
MultiJson::Adapters.const_get(:"#{default_adapter.to_s.split('_').map{|s| s.capitalize}.join('')}")
when Class
new_adapter
else
raise "Did not recognize your adapter specification. Please specify either a symbol or a class."
end
end
# Decode a JSON string into Ruby.
#
# <b>Options</b>
#
# <tt>:symbolize_keys</tt> :: If true, will use symbols instead of strings for the keys.
# <tt>:adapter</tt> :: If set, the selected engine will be used just for the call.
def load(string, options={})
adapter = current_adapter(options)
begin
adapter.load(string, options)
rescue adapter::ParseError => exception
raise DecodeError.new(exception.message, exception.backtrace, string)
end
end
# :nodoc:
alias :decode :load
def current_adapter(options)
if new_adapter = (options || {}).delete(:adapter)
load_adapter(new_adapter)
else
adapter
end
end
# Encodes a Ruby object as JSON.
def dump(object, options={})
adapter = current_adapter(options)
adapter.dump(object, options)
end
# :nodoc:
alias :encode :dump
end
end

View File

@ -0,0 +1,25 @@
module MultiJson
module Adapters
module JsonCommon
def load(string, options={})
string = string.read if string.respond_to?(:read)
::JSON.parse(string, :symbolize_names => options[:symbolize_keys], :quirks_mode => true)
end
def dump(object, options={})
object.to_json(process_options(options))
end
protected
def process_options(options={})
return options if options.empty?
opts = {}
opts.merge!(JSON::PRETTY_STATE_PROTOTYPE.to_h) if options.delete(:pretty)
opts.merge!(options)
end
end
end
end

View File

@ -0,0 +1,12 @@
require 'json' unless defined?(::JSON)
require 'multi_json/adapters/json_common'
module MultiJson
module Adapters
# Use the JSON gem to dump/load.
class JsonGem
ParseError = ::JSON::ParserError
extend JsonCommon
end
end
end

Some files were not shown because too many files have changed in this diff Show More