From e5cbceaf001315545a6fff92265f6ab4b3c92d72 Mon Sep 17 00:00:00 2001 From: John Nunemaker Date: Tue, 8 Sep 2009 23:34:21 -0400 Subject: [PATCH] Adding custom parsers --- lib/httparty/request.rb | 46 ++++++++------------ spec/httparty_spec.rb | 95 +++++++++++++++++++++++++---------------- 2 files changed, 77 insertions(+), 64 deletions(-) diff --git a/lib/httparty/request.rb b/lib/httparty/request.rb index 2352362..84eb5d7 100644 --- a/lib/httparty/request.rb +++ b/lib/httparty/request.rb @@ -3,14 +3,14 @@ require 'uri' module HTTParty class Request #:nodoc: SupportedHTTPMethods = [Net::HTTP::Get, Net::HTTP::Post, Net::HTTP::Put, Net::HTTP::Delete] - + attr_accessor :http_method, :path, :options - + def initialize(http_method, path, o={}) self.http_method = http_method self.path = path self.options = { - :limit => o.delete(:no_follow) ? 0 : 5, + :limit => o.delete(:no_follow) ? 0 : 5, :default_params => {}, }.merge(o) end @@ -18,22 +18,22 @@ module HTTParty def path=(uri) @path = URI.parse(uri) end - + def uri new_uri = path.relative? ? URI.parse("#{options[:base_uri]}#{path}") : path - + # avoid double query string on redirects [#12] unless @redirect new_uri.query = query_string(new_uri) end - + new_uri end - + def format options[:format] end - + def perform validate setup_raw_request @@ -56,11 +56,11 @@ module HTTParty def body options[:body].is_a?(Hash) ? options[:body].to_params : options[:body] end - + def username options[:basic_auth][:username] end - + def password options[:basic_auth][:password] end @@ -81,7 +81,7 @@ module HTTParty options[:format] ||= format_from_mimetype(response['content-type']) response end - + def query_string(uri) query_string_parts = [] query_string_parts << uri.query unless uri.query.nil? @@ -92,10 +92,10 @@ module HTTParty query_string_parts << options[:default_params].to_params unless options[:default_params].nil? query_string_parts << options[:query] unless options[:query].nil? end - + query_string_parts.size > 0 ? query_string_parts.join('&') : nil end - + # Raises exception Net::XXX (http error code) if an http error occured def handle_response(response) case response @@ -111,8 +111,7 @@ module HTTParty Response.new(parsed_response, response.body, response.code, response.message, response.to_hash) end end - - # HTTParty.const_get((self.format.to_s || 'text').capitalize) + def parse_response(body) return nil if body.nil? or body.empty? if options[:parser].blank? @@ -134,7 +133,7 @@ module HTTParty end end end - + def capture_cookies(response) return unless response['Set-Cookie'] cookies_hash = HTTParty::CookieHash.new() @@ -143,23 +142,14 @@ module HTTParty options[:headers] ||= {} options[:headers]['Cookie'] = cookies_hash.to_cookie_string 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 AllowedFormats hash def format_from_mimetype(mimetype) return nil if mimetype.nil? AllowedFormats.each { |k, v| return v if mimetype.include?(k) } end - + def validate raise HTTParty::RedirectionTooDeep, 'HTTP redirects too deep' if options[:limit].to_i <= 0 raise ArgumentError, 'only get, post, put and delete methods are supported' unless SupportedHTTPMethods.include?(http_method) @@ -167,7 +157,7 @@ module HTTParty raise ArgumentError, ':basic_auth must be a hash' if options[:basic_auth] && !options[:basic_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 diff --git a/spec/httparty_spec.rb b/spec/httparty_spec.rb index 65d1d22..0fa7f2b 100644 --- a/spec/httparty_spec.rb +++ b/spec/httparty_spec.rb @@ -1,5 +1,11 @@ require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper')) +class CustomParser + def self.parse(body) + return {:sexy => true} + end +end + describe HTTParty do before(:each) do @klass = Class.new @@ -14,7 +20,7 @@ describe HTTParty do 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' @@ -26,18 +32,18 @@ describe HTTParty do uri.should == 'http://api.foobar.com' 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' @@ -49,7 +55,7 @@ describe HTTParty do uri.should == 'http://api.foobar.com' end end - + describe "headers" do def expect_headers(header={}) HTTParty::Request.should_receive(:new) \ @@ -170,47 +176,64 @@ describe HTTParty do 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 "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 "parser" do + before(:each) do + @parser = Proc.new{ |data| CustomParser.parse(data) } + @klass.parser @parser + end + + it "should set parser options" do + @klass.default_options[:parser].should == @parser + end + + it "should be able parse response with custom parser" do + 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 + 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 @@ -251,7 +274,7 @@ describe HTTParty do end.should raise_error(HTTParty::RedirectionTooDeep) end end - + describe "with multiple class definitions" do before(:each) do @klass.instance_eval do @@ -272,57 +295,57 @@ describe HTTParty do @additional_klass.default_options.should == { :base_uri => 'http://second.com', :default_params => { :two => 2 } } 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 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", + "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", + "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 == nil end end -end \ No newline at end of file +end