From 466a0d9942d1c0c0c35c6302951087076ddf4b82 Mon Sep 17 00:00:00 2001 From: Erik Michaels-Ober Date: Fri, 24 Sep 2010 18:15:49 -0700 Subject: [PATCH 01/12] Replace HTTParty with Faraday --- Gemfile.lock | 18 +++- examples/search.rb | 6 +- lib/faraday/raise_errors.rb | 27 +++++ lib/twitter.rb | 149 +++++++-------------------- lib/twitter/base.rb | 134 ++++++++++++------------ lib/twitter/geo.rb | 41 ++++++-- lib/twitter/local_trends.rb | 22 ---- lib/twitter/request.rb | 40 ++++--- lib/twitter/search.rb | 31 ++++-- lib/twitter/trends.rb | 79 +++++++------- lib/twitter/unauthenticated.rb | 68 ++++++++++++ test/twitter/base_test.rb | 14 +-- test/twitter/geo_test.rb | 14 +-- test/twitter/request_test.rb | 53 +++------- test/twitter/search_test.rb | 54 ++++------ test/twitter/trends_test.rb | 24 ++--- test/twitter/unauthenticated_test.rb | 108 +++++++++++++++++++ test/twitter_test.rb | 102 ------------------ twitter.gemspec | 4 +- website/index.html | 2 +- 20 files changed, 509 insertions(+), 481 deletions(-) create mode 100644 lib/faraday/raise_errors.rb delete mode 100644 lib/twitter/local_trends.rb create mode 100644 lib/twitter/unauthenticated.rb create mode 100644 test/twitter/unauthenticated_test.rb diff --git a/Gemfile.lock b/Gemfile.lock index df492d075..9fb1a9fd4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -2,23 +2,29 @@ PATH remote: . specs: twitter (0.9.11) + addressable (~> 2.2.1) + faraday (~> 0.4.6) + faraday-middleware (~> 0.0.1) hashie (~> 0.4.0) - httparty (~> 0.6.1) multi_json (~> 0.0.4) oauth (~> 0.4.3) GEM remote: http://rubygems.org/ specs: - crack (0.1.8) + addressable (2.2.1) fakeweb (1.3.0) + faraday (0.4.6) + addressable (>= 2.1.1) + rack (>= 1.0.1) + faraday-middleware (0.0.1) + faraday (~> 0.4.5) hashie (0.4.0) - httparty (0.6.1) - crack (= 0.1.8) mocha (0.9.8) rake multi_json (0.0.4) oauth (0.4.3) + rack (1.2.1) rake (0.8.7) shoulda (2.11.3) @@ -26,9 +32,11 @@ PLATFORMS ruby DEPENDENCIES + addressable (~> 2.2.1) fakeweb (~> 1.3.0) + faraday (~> 0.4.6) + faraday-middleware (~> 0.0.1) hashie (~> 0.4.0) - httparty (~> 0.6.1) mocha (~> 0.9.8) multi_json (~> 0.0.4) oauth (~> 0.4.3) diff --git a/examples/search.rb b/examples/search.rb index 16027cd6b..ed0e54c89 100644 --- a/examples/search.rb +++ b/examples/search.rb @@ -10,6 +10,6 @@ search.each { |result| pp result } puts '*'*50, 'Parameter Check', '*'*50 -pp Twitter::Search.new('#austineats').fetch().results.first -pp Twitter::Search.new('#austineats').page(2).fetch().results.first -pp Twitter::Search.new('#austineats').since(1412737343).fetch().results.first +pp Twitter::Search.new('#austineats').fetch.results.first +pp Twitter::Search.new('#austineats').page(2).fetch.results.first +pp Twitter::Search.new('#austineats').since(1412737343).fetch.results.first diff --git a/lib/faraday/raise_errors.rb b/lib/faraday/raise_errors.rb new file mode 100644 index 000000000..ec3e89e32 --- /dev/null +++ b/lib/faraday/raise_errors.rb @@ -0,0 +1,27 @@ +module Faraday + class Response::RaiseErrors < Response::Middleware + def self.register_on_complete(env) + env[:response].on_complete do |response| + case response[:status].to_i + when 400 + raise Twitter::RateLimitExceeded, "(#{response[:status]}): #{response[:body]}" + when 401 + raise Twitter::Unauthorized, "(#{response[:status]}): #{response[:body]}" + when 403 + raise Twitter::General, "(#{response[:status]}): #{response[:body]}" + when 404 + raise Twitter::NotFound, "(#{response[:status]}): #{response[:body]}" + when 500 + raise Twitter::InformTwitter, "Twitter had an internal error. Please let them know in the group. (#{response[:status]}): #{response[:body]}" + when 502..503 + raise Twitter::Unavailable, "(#{response[:status]}): #{response[:body]}" + end + end + end + + def initialize(app) + super + @parser = nil + end + end +end diff --git a/lib/twitter.rb b/lib/twitter.rb index c41c4f281..774d2c347 100644 --- a/lib/twitter.rb +++ b/lib/twitter.rb @@ -1,28 +1,20 @@ -require "forwardable" -require "oauth" -require "hashie" -require "httparty" -require "multi_json" +require 'addressable/uri' +require 'faraday' +require 'faraday_middleware' +require 'forwardable' +require 'hashie' +require 'multi_json' +require 'oauth' module Twitter - include HTTParty - class TwitterError < StandardError - attr_reader :data - - def initialize(data) - @data = data - super - end + def self.adapter + @adapter ||= Faraday.default_adapter end - class RateLimitExceeded < TwitterError; end - class Unauthorized < TwitterError; end - class General < TwitterError; end - - class Unavailable < StandardError; end - class InformTwitter < StandardError; end - class NotFound < StandardError; end + def self.adapter=(value) + @adapter = value + end def self.user_agent @user_agent ||= 'Ruby Twitter Gem' @@ -33,11 +25,13 @@ def self.user_agent=(value) end def self.api_endpoint - @api_endpoint ||= "api.twitter.com/#{self.api_version}" + api_endpoint = "api.twitter.com/#{Twitter.api_version}" + api_endpoint = Addressable::URI.heuristic_parse(api_endpoint).to_s + @api_endpoint ||= api_endpoint end def self.api_endpoint=(value) - @api_endpoint = value + @api_endpoint = Addressable::URI.heuristic_parse(value).to_s end def self.api_version @@ -48,54 +42,24 @@ def self.api_version=(value) @api_version = value end - def self.firehose(options = {}) - perform_get("/statuses/public_timeline.json") - end - - def self.user(id, options={}) - perform_get("/users/show/#{id}.json") - end - - def self.status(id, options={}) - perform_get("/statuses/show/#{id}.json") - end - - def self.friend_ids(id, options={}) - perform_get("/friends/ids/#{id}.json") - end - - def self.follower_ids(id, options={}) - perform_get("/followers/ids/#{id}.json") - end - - def self.timeline(id, options={}) - perform_get("/statuses/user_timeline/#{id}.json", :query => options) - end - - # :per_page = max number of statues to get at once - # :page = which page of tweets you wish to get - def self.list_timeline(list_owner_screen_name, slug, query = {}) - perform_get("/#{list_owner_screen_name}/lists/#{slug}/statuses.json", :query => query) - end - private - def self.perform_get(uri, options = {}) - base_uri self.api_endpoint - make_friendly(get(uri, options)) - end + class TwitterError < StandardError + attr_reader :data - def self.make_friendly(response) - raise_errors(response) - data = parse(response) - # Don't mash arrays of integers - if data && data.is_a?(Array) && data.first.is_a?(Integer) - data - else - mash(data) + def initialize(data) + @data = data + super end end + class RateLimitExceeded < TwitterError; end + class Unauthorized < TwitterError; end + class General < TwitterError; end + class Unavailable < StandardError; end + class InformTwitter < StandardError; end + class NotFound < StandardError; end + def self.raise_errors(response) case response.code.to_i when 400 @@ -116,54 +80,13 @@ def self.raise_errors(response) end end - def self.parse(response) - case response.body - when '' - nil - when 'true' - true - when 'false' - false - else - MultiJson.decode(response.body) - end - end - - def self.mash(obj) - if obj.is_a?(Array) - obj.map{|item| Hashie::Mash.new(item)} - elsif obj.is_a?(Hash) - Hashie::Mash.new(obj) - else - obj - end - end - end -module Hashie - class Mash - - # Converts all of the keys to strings, optionally formatting key name - def rubyify_keys! - keys.each{|k| - v = delete(k) - new_key = k.to_s.underscore - self[new_key] = v - v.rubyify_keys! if v.is_a?(Hash) - v.each{|p| p.rubyify_keys! if p.is_a?(Hash)} if v.is_a?(Array) - } - self - end - - end -end - -directory = File.expand_path(File.dirname(__FILE__)) - -require File.join(directory, "twitter", "oauth") -require File.join(directory, "twitter", "request") -require File.join(directory, "twitter", "base") -require File.join(directory, "twitter", "search") -require File.join(directory, "twitter", "trends") -require File.join(directory, "twitter", "geo") +require File.expand_path("../faraday/raise_errors", __FILE__) +require File.expand_path("../twitter/oauth", __FILE__) +require File.expand_path("../twitter/request", __FILE__) +require File.expand_path("../twitter/base", __FILE__) +require File.expand_path("../twitter/search", __FILE__) +require File.expand_path("../twitter/trends", __FILE__) +require File.expand_path("../twitter/geo", __FILE__) +require File.expand_path("../twitter/unauthenticated", __FILE__) diff --git a/lib/twitter/base.rb b/lib/twitter/base.rb index 11a083b04..8001f5646 100644 --- a/lib/twitter/base.rb +++ b/lib/twitter/base.rb @@ -12,31 +12,31 @@ def initialize(client) # Options: since_id, max_id, count, page def home_timeline(query={}) - perform_get("/#{Twitter.api_version}/statuses/home_timeline.json", :query => query) + perform_get("statuses/home_timeline.json", :query => query) end # Options: since_id, max_id, count, page, since def friends_timeline(query={}) - perform_get("/#{Twitter.api_version}/statuses/friends_timeline.json", :query => query) + perform_get("statuses/friends_timeline.json", :query => query) end # Options: id, user_id, screen_name, since_id, max_id, page, since, count def user_timeline(query={}) - perform_get("/#{Twitter.api_version}/statuses/user_timeline.json", :query => query) + perform_get("statuses/user_timeline.json", :query => query) end def status(id) - perform_get("/#{Twitter.api_version}/statuses/show/#{id}.json") + perform_get("statuses/show/#{id}.json") end # Options: count def retweets(id, query={}) - perform_get("/#{Twitter.api_version}/statuses/retweets/#{id}.json", :query => query) + perform_get("statuses/retweets/#{id}.json", :query => query) end # Options: in_reply_to_status_id def update(status, query={}) - perform_post("/#{Twitter.api_version}/statuses/update.json", :body => {:status => status}.merge(query)) + perform_post("statuses/update.json", :body => {:status => status}.merge(query)) end # DEPRECATED: Use #mentions instead @@ -44,51 +44,51 @@ def update(status, query={}) # Options: since_id, max_id, since, page def replies(query={}) warn("DEPRECATED: #replies is deprecated by Twitter; use #mentions instead") - perform_get("/#{Twitter.api_version}/statuses/replies.json", :query => query) + perform_get("statuses/replies.json", :query => query) end # Options: since_id, max_id, count, page def mentions(query={}) - perform_get("/#{Twitter.api_version}/statuses/mentions.json", :query => query) + perform_get("statuses/mentions.json", :query => query) end # Options: since_id, max_id, count, page def retweeted_by_me(query={}) - perform_get("/#{Twitter.api_version}/statuses/retweeted_by_me.json", :query => query) + perform_get("statuses/retweeted_by_me.json", :query => query) end # Options: since_id, max_id, count, page def retweeted_to_me(query={}) - perform_get("/#{Twitter.api_version}/statuses/retweeted_to_me.json", :query => query) + perform_get("statuses/retweeted_to_me.json", :query => query) end # Options: since_id, max_id, count, page def retweets_of_me(query={}) - perform_get("/#{Twitter.api_version}/statuses/retweets_of_me.json", :query => query) + perform_get("statuses/retweets_of_me.json", :query => query) end # options: count, page, ids_only def retweeters_of(id, options={}) ids_only = !!(options.delete(:ids_only)) - perform_get("/#{Twitter.api_version}/statuses/#{id}/retweeted_by#{"/ids" if ids_only}.json", :query => options) + perform_get("statuses/#{id}/retweeted_by#{"/ids" if ids_only}.json", :query => options) end def status_destroy(id) - perform_post("/#{Twitter.api_version}/statuses/destroy/#{id}.json") + perform_post("statuses/destroy/#{id}.json") end def retweet(id) - perform_post("/#{Twitter.api_version}/statuses/retweet/#{id}.json") + perform_post("statuses/retweet/#{id}.json") end # Options: id, user_id, screen_name, page def friends(query={}) - perform_get("/#{Twitter.api_version}/statuses/friends.json", :query => query) + perform_get("statuses/friends.json", :query => query) end # Options: id, user_id, screen_name, page def followers(query={}) - perform_get("/#{Twitter.api_version}/statuses/followers.json", :query => query) + perform_get("statuses/followers.json", :query => query) end def user(id_or_screen_name, query={}) @@ -97,7 +97,7 @@ def user(id_or_screen_name, query={}) elsif id_or_screen_name.is_a?(String) query.merge!({:screen_name => id_or_screen_name}) end - perform_get("/#{Twitter.api_version}/users/show.json", :query => query) + perform_get("users/show.json", :query => query) end def users(*ids_or_screen_names) @@ -112,226 +112,226 @@ def users(*ids_or_screen_names) query = {} query[:user_id] = ids.join(",") unless ids.empty? query[:screen_name] = screen_names.join(",") unless screen_names.empty? - perform_get("/#{Twitter.api_version}/users/lookup.json", :query => query) + perform_get("users/lookup.json", :query => query) end # Options: page, per_page def user_search(q, query={}) q = URI.escape(q) - perform_get("/#{Twitter.api_version}/users/search.json", :query => ({:q => q}.merge(query))) + perform_get("users/search.json", :query => ({:q => q}.merge(query))) end # Options: since, since_id, page def direct_messages(query={}) - perform_get("/#{Twitter.api_version}/direct_messages.json", :query => query) + perform_get("direct_messages.json", :query => query) end # Options: since, since_id, page def direct_messages_sent(query={}) - perform_get("/#{Twitter.api_version}/direct_messages/sent.json", :query => query) + perform_get("direct_messages/sent.json", :query => query) end def direct_message_create(user_id_or_screen_name, text) - perform_post("/#{Twitter.api_version}/direct_messages/new.json", :body => {:user => user_id_or_screen_name, :text => text}) + perform_post("direct_messages/new.json", :body => {:user => user_id_or_screen_name, :text => text}) end def direct_message_destroy(id) - perform_post("/#{Twitter.api_version}/direct_messages/destroy/#{id}.json") + perform_post("direct_messages/destroy/#{id}.json") end def friendship_create(id, follow=false) body = {} body.merge!(:follow => follow) if follow - perform_post("/#{Twitter.api_version}/friendships/create/#{id}.json", :body => body) + perform_post("friendships/create/#{id}.json", :body => body) end def friendship_destroy(id) - perform_post("/#{Twitter.api_version}/friendships/destroy/#{id}.json") + perform_post("friendships/destroy/#{id}.json") end def friendship_exists?(a, b) - perform_get("/#{Twitter.api_version}/friendships/exists.json", :query => {:user_a => a, :user_b => b}) + perform_get("friendships/exists.json", :query => {:user_a => a, :user_b => b}) end def friendship_show(query) - perform_get("/#{Twitter.api_version}/friendships/show.json", :query => query) + perform_get("friendships/show.json", :query => query) end # Options: id, user_id, screen_name def friend_ids(query={}) - perform_get("/#{Twitter.api_version}/friends/ids.json", :query => query) + perform_get("friends/ids.json", :query => query) end # Options: id, user_id, screen_name def follower_ids(query={}) - perform_get("/#{Twitter.api_version}/followers/ids.json", :query => query) + perform_get("followers/ids.json", :query => query) end def verify_credentials - perform_get("/#{Twitter.api_version}/account/verify_credentials.json") + perform_get("account/verify_credentials.json") end # Device must be sms, im or none def update_delivery_device(device) - perform_post("/#{Twitter.api_version}/account/update_delivery_device.json", :body => {:device => device}) + perform_post("account/update_delivery_device.json", :body => {:device => device}) end # One or more of the following must be present: # profile_background_color, profile_text_color, profile_link_color, # profile_sidebar_fill_color, profile_sidebar_border_color def update_profile_colors(colors={}) - perform_post("/#{Twitter.api_version}/account/update_profile_colors.json", :body => colors) + perform_post("account/update_profile_colors.json", :body => colors) end # file should respond to #read and #path def update_profile_image(file) - perform_post("/#{Twitter.api_version}/account/update_profile_image.json", build_multipart_bodies(:image => file)) + perform_post("account/update_profile_image.json", build_multipart_bodies(:image => file)) end # file should respond to #read and #path def update_profile_background(file, tile = false) - perform_post("/#{Twitter.api_version}/account/update_profile_background_image.json", build_multipart_bodies(:image => file).merge(:tile => tile)) + perform_post("account/update_profile_background_image.json", build_multipart_bodies(:image => file).merge(:tile => tile)) end def rate_limit_status - perform_get("/#{Twitter.api_version}/account/rate_limit_status.json") + perform_get("account/rate_limit_status.json") end # One or more of the following must be present: # name, email, url, location, description def update_profile(body={}) - perform_post("/#{Twitter.api_version}/account/update_profile.json", :body => body) + perform_post("account/update_profile.json", :body => body) end # Options: id, page def favorites(query={}) - perform_get("/#{Twitter.api_version}/favorites.json", :query => query) + perform_get("favorites.json", :query => query) end def favorite_create(id) - perform_post("/#{Twitter.api_version}/favorites/create/#{id}.json") + perform_post("favorites/create/#{id}.json") end def favorite_destroy(id) - perform_post("/#{Twitter.api_version}/favorites/destroy/#{id}.json") + perform_post("favorites/destroy/#{id}.json") end def enable_notifications(id) - perform_post("/#{Twitter.api_version}/notifications/follow/#{id}.json") + perform_post("notifications/follow/#{id}.json") end def disable_notifications(id) - perform_post("/#{Twitter.api_version}/notifications/leave/#{id}.json") + perform_post("notifications/leave/#{id}.json") end def block(id) - perform_post("/#{Twitter.api_version}/blocks/create/#{id}.json") + perform_post("blocks/create/#{id}.json") end def unblock(id) - perform_post("/#{Twitter.api_version}/blocks/destroy/#{id}.json") + perform_post("blocks/destroy/#{id}.json") end # When reporting a user for spam, specify one or more of id, screen_name, or user_id def report_spam(options) - perform_post("/#{Twitter.api_version}/report_spam.json", :body => options) + perform_post("report_spam.json", :body => options) end def help - perform_get("/#{Twitter.api_version}/help/test.json") + perform_get("help/test.json") end def list_create(list_owner_screen_name, options) - perform_post("/#{Twitter.api_version}/#{list_owner_screen_name}/lists.json", :body => {:user => list_owner_screen_name}.merge(options)) + perform_post("#{list_owner_screen_name}/lists.json", :body => {:user => list_owner_screen_name}.merge(options)) end def list_update(list_owner_screen_name, slug, options) - perform_put("/#{Twitter.api_version}/#{list_owner_screen_name}/lists/#{slug}.json", :body => options) + perform_put("#{list_owner_screen_name}/lists/#{slug}.json", :body => options) end def list_delete(list_owner_screen_name, slug) - perform_delete("/#{Twitter.api_version}/#{list_owner_screen_name}/lists/#{slug}.json") + perform_delete("#{list_owner_screen_name}/lists/#{slug}.json") end def lists(list_owner_screen_name = nil, query = {}) path = case list_owner_screen_name when nil, Hash query = list_owner_screen_name - "/#{Twitter.api_version}/lists.json" + "lists.json" else - "/#{Twitter.api_version}/#{list_owner_screen_name}/lists.json" + "#{list_owner_screen_name}/lists.json" end perform_get(path, :query => query) end def list(list_owner_screen_name, slug) - perform_get("/#{Twitter.api_version}/#{list_owner_screen_name}/lists/#{slug}.json") + perform_get("#{list_owner_screen_name}/lists/#{slug}.json") end # :per_page = max number of statues to get at once # :page = which page of tweets you wish to get def list_timeline(list_owner_screen_name, slug, query = {}) - perform_get("/#{Twitter.api_version}/#{list_owner_screen_name}/lists/#{slug}/statuses.json", :query => query) + perform_get("#{list_owner_screen_name}/lists/#{slug}/statuses.json", :query => query) end def memberships(list_owner_screen_name, query={}) - perform_get("/#{Twitter.api_version}/#{list_owner_screen_name}/lists/memberships.json", :query => query) + perform_get("#{list_owner_screen_name}/lists/memberships.json", :query => query) end def subscriptions(list_owner_screen_name, query = {}) - perform_get("/#{Twitter.api_version}/#{list_owner_screen_name}/lists/subscriptions.json", :query => query) + perform_get("#{list_owner_screen_name}/lists/subscriptions.json", :query => query) end def list_members(list_owner_screen_name, slug, query = {}) - perform_get("/#{Twitter.api_version}/#{list_owner_screen_name}/#{slug}/members.json", :query => query) + perform_get("#{list_owner_screen_name}/#{slug}/members.json", :query => query) end def list_add_member(list_owner_screen_name, slug, new_id) - perform_post("/#{Twitter.api_version}/#{list_owner_screen_name}/#{slug}/members.json", :body => {:id => new_id}) + perform_post("#{list_owner_screen_name}/#{slug}/members.json", :body => {:id => new_id}) end def list_remove_member(list_owner_screen_name, slug, id) - perform_delete("/#{Twitter.api_version}/#{list_owner_screen_name}/#{slug}/members.json", :query => {:id => id}) + perform_delete("#{list_owner_screen_name}/#{slug}/members.json", :query => {:id => id}) end def is_list_member?(list_owner_screen_name, slug, id) - perform_get("/#{Twitter.api_version}/#{list_owner_screen_name}/#{slug}/members/#{id}.json").error.nil? + perform_get("#{list_owner_screen_name}/#{slug}/members/#{id}.json").error.nil? end def list_subscribers(list_owner_screen_name, slug, query={}) - perform_get("/#{Twitter.api_version}/#{list_owner_screen_name}/#{slug}/subscribers.json", :body => {:query => query}) + perform_get("#{list_owner_screen_name}/#{slug}/subscribers.json", :body => {:query => query}) end def list_subscribe(list_owner_screen_name, slug) - perform_post("/#{Twitter.api_version}/#{list_owner_screen_name}/#{slug}/subscribers.json") + perform_post("#{list_owner_screen_name}/#{slug}/subscribers.json") end def list_unsubscribe(list_owner_screen_name, slug) - perform_delete("/#{Twitter.api_version}/#{list_owner_screen_name}/#{slug}/subscribers.json") + perform_delete("#{list_owner_screen_name}/#{slug}/subscribers.json") end def blocked_ids - perform_get("/#{Twitter.api_version}/blocks/blocking/ids.json", :mash => false) + perform_get("blocks/blocking/ids.json", :mash => false) end def blocking(options={}) - perform_get("/#{Twitter.api_version}/blocks/blocking.json", options) + perform_get("blocks/blocking.json", options) end def saved_searches - perform_get("/#{Twitter.api_version}/saved_searches.json") + perform_get("saved_searches.json") end def saved_search(id) - perform_get("/#{Twitter.api_version}/saved_searches/show/#{id}.json") + perform_get("saved_searches/show/#{id}.json") end def saved_search_create(query) - perform_post("/#{Twitter.api_version}/saved_searches/create.json", :body => {:query => query}) + perform_post("saved_searches/create.json", :body => {:query => query}) end def saved_search_destroy(id) - perform_delete("/#{Twitter.api_version}/saved_searches/destroy/#{id}.json") + perform_delete("saved_searches/destroy/#{id}.json") end protected diff --git a/lib/twitter/geo.rb b/lib/twitter/geo.rb index d9f101067..2eedc9374 100644 --- a/lib/twitter/geo.rb +++ b/lib/twitter/geo.rb @@ -1,24 +1,43 @@ module Twitter class Geo - include HTTParty - base_uri "api.twitter.com/#{Twitter.api_version}/geo" - def self.place(place_id, query={}) - Twitter.mash(Twitter.parse(get("/id/#{place_id}.json", :query => query))) + def initialize(options={}) + @adapter = options.delete(:adapter) + @api_endpoint = "api.twitter.com/#{Twitter.api_version}/geo" + @api_endpoint = Addressable::URI.heuristic_parse(@api_endpoint) + @api_endpoint = @api_endpoint.to_s end - def self.search(query={}) - mashup(get("/search.json", :query => query)) + def place(place_id, options={}) + results = connection.get do |req| + req.url "id/#{place_id}.json", options + end.body + results end - def self.reverse_geocode(query={}) - mashup(get("/reverse_geocode.json", :query => query)) + def search(options={}) + results = connection.get do |req| + req.url "search.json", options + end.body + results.result.values.flatten end - private + def reverse_geocode(options={}) + results = connection.get do |req| + req.url "reverse_geocode.json", options + end.body + results.result.values.flatten + end - def self.mashup(response) - Twitter.parse(response)["result"].values.flatten.map{|t| Twitter.mash(t)} + def connection + headers = { + :user_agent => Twitter.user_agent + } + @connection ||= Faraday::Connection.new(:url => @api_endpoint, :headers => headers) do |builder| + builder.adapter(@adapter || Faraday.default_adapter) + builder.use Faraday::Response::MultiJson + builder.use Faraday::Response::Mashify + end end end diff --git a/lib/twitter/local_trends.rb b/lib/twitter/local_trends.rb deleted file mode 100644 index b0f42cad8..000000000 --- a/lib/twitter/local_trends.rb +++ /dev/null @@ -1,22 +0,0 @@ -module Twitter - class LocalTrends - include HTTParty - base_uri "api.twitter.com/#{Twitter.api_version}/trends" - - def self.available(query={}) - query.delete(:api_endpoint) - mashup(get("/available.json", :query => query)) - end - - def self.for_location(woeid, options = {}) - mashup(get("/#{woeid}.json")) - end - - private - - def self.mashup(response) - Twitter.parse(response).map{|t| Twitter.mash(t)} - end - - end -end diff --git a/lib/twitter/request.rb b/lib/twitter/request.rb index 17e8b7fc9..5bbcb5e2b 100644 --- a/lib/twitter/request.rb +++ b/lib/twitter/request.rb @@ -3,19 +3,19 @@ class Request extend Forwardable def self.get(client, path, options={}) - new(client, :get, path, options).perform + new(client, :get, path, options).perform_get end def self.post(client, path, options={}) - new(client, :post, path, options).perform + new(client, :post, path, options).perform_post end def self.put(client, path, options={}) - new(client, :put, path, options).perform + new(client, :put, path, options).perform_put end def self.delete(client, path, options={}) - new(client, :delete, path, options).perform + new(client, :delete, path, options).perform_delete end attr_reader :client, :method, :path, :options @@ -38,28 +38,44 @@ def uri end end - def perform - Twitter.make_friendly(send("perform_#{method}")) + def connection + headers = { + :user_agent => Twitter.user_agent + } + @connection ||= Faraday::Connection.new(:url => Twitter.api_endpoint, :headers => headers) do |builder| + builder.adapter(@adapter || Faraday.default_adapter) + builder.use Faraday::Response::RaiseErrors + builder.use Faraday::Response::MultiJson + builder.use Faraday::Response::Mashify + end end - private - def perform_get - get(uri, options[:headers]) + results = connection.get do |req| + req.url uri + end.body end def perform_post - post(uri, options[:body], options[:headers]) + results = connection.post do |req| + req.url uri, options[:body] + end.body end def perform_put - put(uri, options[:body], options[:headers]) + results = connection.put do |req| + req.url uri, options[:body] + end.body end def perform_delete - delete(uri, options[:headers]) + results = connection.delete do |req| + req.url uri + end.body end + private + def to_query(options) options.inject([]) do |collection, opt| collection << "#{opt[0]}=#{opt[1]}" diff --git a/lib/twitter/search.rb b/lib/twitter/search.rb index 78169ef8b..1d5f1f9e2 100644 --- a/lib/twitter/search.rb +++ b/lib/twitter/search.rb @@ -1,18 +1,15 @@ module Twitter class Search - include HTTParty include Enumerable - base_uri "search.twitter.com/search" - attr_reader :result, :query def initialize(q=nil, options={}) + @adapter = options.delete(:adapter) @options = options clear containing(q) if q && q.strip != "" - endpoint_url = options[:api_endpoint] - endpoint_url = "#{endpoint_url}/search" if endpoint_url && !endpoint_url.include?("/search") - self.class.base_uri(endpoint_url) if endpoint_url + @api_endpoint = 'search.twitter.com/search.json' + @api_endpoint = Addressable::URI.heuristic_parse(@api_endpoint) end def user_agent @@ -156,28 +153,40 @@ def fetch(force=false) end def each - results = fetch()['results'] + results = fetch['results'] return if results.nil? results.each {|r| yield r} end def next_page? - !!fetch()["next_page"] + !!fetch["next_page"] end def fetch_next_page if next_page? s = Search.new(nil, :user_agent => user_agent) - s.perform_get(fetch()["next_page"][1..-1]) + s.perform_get(fetch["next_page"][1..-1]) s end end + def connection + headers = { + :user_agent => user_agent + } + @connection ||= Faraday::Connection.new(:url => @api_endpoint.omit(:path), :headers => headers) do |builder| + builder.adapter(@adapter || Faraday.default_adapter) + builder.use Faraday::Response::MultiJson + builder.use Faraday::Response::Mashify + end + end + protected def perform_get(query) - response = self.class.get("#{self.class.base_uri}.json", :query => query, :format => :json, :headers => {"User-Agent" => user_agent}) - @fetch = Twitter.mash(response) + @fetch = connection.get do |req| + req.url @api_endpoint.path, query + end.body end end diff --git a/lib/twitter/trends.rb b/lib/twitter/trends.rb index c5b7abbd2..5ab58a47b 100644 --- a/lib/twitter/trends.rb +++ b/lib/twitter/trends.rb @@ -1,53 +1,60 @@ -require File.join(File.expand_path(File.dirname(__FILE__)), "local_trends") - module Twitter class Trends - include HTTParty - - def self.api_endpoint - @api_endpoint ||= "api.twitter.com/#{Twitter.api_version}/trends" - end - def self.api_endpoint=(value) - @api_endpoint = value + def initialize(options={}) + @adapter = options.delete(:adapter) + @api_endpoint = "api.twitter.com/#{Twitter.api_version}/trends" + @api_endpoint = Addressable::URI.heuristic_parse(@api_endpoint) + @api_endpoint = @api_endpoint.to_s end # :exclude => 'hashtags' to exclude hashtags - def self.current(options={}) - get("/current.json", :query => options) + def current(options={}) + results = connection.get do |req| + req.url "current.json", options + end.body + results = results.trends.values.flatten end # :exclude => 'hashtags' to exclude hashtags # :date => yyyy-mm-dd for specific date - def self.daily(options={}) - get("/daily.json", :query => options) + def daily(options={}) + results = connection.get do |req| + req.url "daily.json", options + end.body + results = results.trends.values.flatten end # :exclude => 'hashtags' to exclude hashtags # :date => yyyy-mm-dd for specific date - def self.weekly(options={}) - get("/weekly.json", :query => options) - end - - def self.available(query={}) - # Checking for api_endpoint in local_trends - LocalTrends.available(query) - end - - def self.for_location(woeid, options={}) - # Checking for api_endpoint in local_trends - LocalTrends.for_location(woeid, options) - end - - private - - def self.get(*args) - base_uri api_endpoint - mashup(super) - end - - def self.mashup(response) - Twitter.parse(response)["trends"].values.flatten.map{|t| Twitter.mash(t)} + def weekly(options={}) + results = connection.get do |req| + req.url "weekly.json", options + end.body + results = results.trends.values.flatten + end + + def available(query={}) + connection.get do |req| + req.url "available.json", query + end.body + end + + def for_location(woeid,options = {}) + connection.get do |req| + req.url "#{woeid}.json", options + end.body + end + + def connection + headers = { + :user_agent => Twitter.user_agent + } + @connection ||= Faraday::Connection.new(:url => @api_endpoint, :headers => headers) do |builder| + builder.adapter(@adapter || Faraday.default_adapter) + builder.use Faraday::Response::MultiJson + builder.use Faraday::Response::Mashify + end end end diff --git a/lib/twitter/unauthenticated.rb b/lib/twitter/unauthenticated.rb new file mode 100644 index 000000000..8c3f5ed7c --- /dev/null +++ b/lib/twitter/unauthenticated.rb @@ -0,0 +1,68 @@ +module Twitter + class Unauthenticated + + def initialize(options={}) + @adapter = options.delete(:adapter) + @api_endpoint = "api.twitter.com/#{Twitter.api_version}" + @api_endpoint = Addressable::URI.heuristic_parse(@api_endpoint) + @api_endpoint = @api_endpoint.to_s + end + + def firehose(options = {}) + results = connection.get do |req| + req.url "statuses/public_timeline.json", options + end.body + end + + def user(id, options={}) + results = connection.get do |req| + req.url "users/show/#{id}.json", options + end.body + end + + def status(id, options={}) + results = connection.get do |req| + req.url "statuses/show/#{id}.json", options + end.body + end + + def friend_ids(id, options={}) + results = connection.get do |req| + req.url "friends/ids/#{id}.json", options + end.body + end + + def follower_ids(id, options={}) + results = connection.get do |req| + req.url "followers/ids/#{id}.json", options + end.body + end + + def timeline(id, options={}) + results = connection.get do |req| + req.url "statuses/user_timeline/#{id}.json", options + end.body + end + + # :per_page = max number of statues to get at once + # :page = which page of tweets you wish to get + def list_timeline(list_owner_screen_name, slug, options = {}) + results = connection.get do |req| + req.url "#{list_owner_screen_name}/lists/#{slug}/statuses.json", options + end.body + end + + def connection + headers = { + :user_agent => Twitter.user_agent + } + @connection ||= Faraday::Connection.new(:url => @api_endpoint, :headers => headers) do |builder| + builder.adapter(@adapter || Faraday.default_adapter) + builder.use Faraday::Response::RaiseErrors + builder.use Faraday::Response::MultiJson + builder.use Faraday::Response::Mashify + end + end + + end +end diff --git a/test/twitter/base_test.rb b/test/twitter/base_test.rb index 0861f767b..cf237574d 100644 --- a/test/twitter/base_test.rb +++ b/test/twitter/base_test.rb @@ -68,7 +68,7 @@ class BaseTest < Test::Unit::TestCase end should "be able to update status" do - stub_post("/1/statuses/update.json", "status.json") + stub_post("/1/statuses/update.json?status=Rob%20Dyrdek%20is%20the%20funniest%20man%20alive.%20That%20is%20all.", "status.json") status = @twitter.update("Rob Dyrdek is the funniest man alive. That is all.") assert_equal 'John Nunemaker', status.user.name assert_equal 'Rob Dyrdek is the funniest man alive. That is all.', status.text @@ -179,7 +179,7 @@ class BaseTest < Test::Unit::TestCase end should "be able to lookup users in bulk" do - stub_get("/1/users/lookup.json?screen_name=sferik&user_id=59593,774010", "users.json") + stub_get("/1/users/lookup.json?user_id=59593%2C774010&screen_name=sferik", "users.json") users = @twitter.users("sferik", 59593, 774010) assert_equal 3, users.count screen_names = users.map{|user| user["screen_name"]} @@ -211,7 +211,7 @@ class BaseTest < Test::Unit::TestCase end should "report a spammer" do - stub_post("/1/report_spam.json", "report_spam.json") + stub_post("/1/report_spam.json?screen_name=lucaasvaz00", "report_spam.json") spammer = @twitter.report_spam(:screen_name => 'lucaasvaz00') assert_equal 'lucaasvaz00', spammer.screen_name end @@ -244,7 +244,7 @@ class BaseTest < Test::Unit::TestCase end should "be able to create a saved search" do - stub_post("/1/saved_searches/create.json", "saved_search.json") + stub_post("/1/saved_searches/create.json?query=great%20danes", "saved_search.json") search = @twitter.saved_search_create("great danes") assert_equal 'great danes', search.query end @@ -259,7 +259,7 @@ class BaseTest < Test::Unit::TestCase context "when using lists" do should "be able to create a new list" do - stub_post("/1/pengwynn/lists.json", "list.json") + stub_post("/1/pengwynn/lists.json?name=Rubyists&user=pengwynn", "list.json") list = @twitter.list_create("pengwynn", {:name => "Rubyists"}) assert_equal 'Rubyists', list.name assert_equal 'rubyists', list.slug @@ -267,7 +267,7 @@ class BaseTest < Test::Unit::TestCase end should "be able to update a list" do - stub_put("/1/pengwynn/lists/rubyists.json", "list.json") + stub_put("/1/pengwynn/lists/rubyists.json?name=Rubyists", "list.json") list = @twitter.list_update("pengwynn", "rubyists", {:name => "Rubyists"}) assert_equal 'Rubyists', list.name assert_equal 'rubyists', list.slug @@ -367,7 +367,7 @@ class BaseTest < Test::Unit::TestCase end should "be able to add a member to a list" do - stub_post("/1/pengwynn/rubyists/members.json", "user.json") + stub_post("/1/pengwynn/rubyists/members.json?id=4243", "user.json") user = @twitter.list_add_member("pengwynn", "rubyists", 4243) assert_equal 'jnunemaker', user.screen_name end diff --git a/test/twitter/geo_test.rb b/test/twitter/geo_test.rb index 82896a95c..db9ee3c43 100644 --- a/test/twitter/geo_test.rb +++ b/test/twitter/geo_test.rb @@ -7,7 +7,7 @@ class GeoTest < Test::Unit::TestCase should "work" do stub_get 'http://api.twitter.com/1/geo/id/ea76a36c5bc2bdff.json', 'geo_place.json' - place = Geo.place('ea76a36c5bc2bdff') + place = Twitter::Geo.new.place('ea76a36c5bc2bdff') assert_equal 'The United States of America', place.country assert_equal 'Ballantyne West, Charlotte', place.full_name assert_kind_of Array, place.geometry.coordinates @@ -19,7 +19,7 @@ class GeoTest < Test::Unit::TestCase should "work" do stub_get 'http://api.twitter.com/1/geo/search.json?lat=35.061161&long=-80.854568', 'geo_search.json' - places = Geo.search(:lat => 35.061161, :long => -80.854568) + places = Twitter::Geo.new.search(:lat => 35.061161, :long => -80.854568) assert_equal 3, places.size assert_equal 'Ballantyne West, Charlotte', places[0].full_name assert_equal 'Ballantyne West', places[0].name @@ -27,7 +27,7 @@ class GeoTest < Test::Unit::TestCase should "be able to search with free form text" do stub_get 'http://api.twitter.com/1/geo/search.json?query=princeton%20record%20exchange', 'geo_search_query.json' - places = Geo.search(:query => 'princeton record exchange') + places = Twitter::Geo.new.search(:query => 'princeton record exchange') assert_equal 1, places.size assert_equal 'Princeton Record Exchange', places[0].name assert_equal 'poi', places[0].place_type @@ -36,7 +36,7 @@ class GeoTest < Test::Unit::TestCase should "be able to search by ip address" do stub_get 'http://api.twitter.com/1/geo/search.json?ip=74.125.19.104', 'geo_search_ip_address.json' - places = Geo.search(:ip => '74.125.19.104') + places = Twitter::Geo.new.search(:ip => '74.125.19.104') assert_equal 4, places.size assert_equal 'Mountain View, CA', places[0].full_name assert_equal 'Mountain View', places[0].name @@ -50,7 +50,7 @@ class GeoTest < Test::Unit::TestCase should "work" do stub_get 'http://api.twitter.com/1/geo/reverse_geocode.json?lat=35.061161&long=-80.854568', 'geo_reverse_geocode.json' - places = Geo.reverse_geocode(:lat => 35.061161, :long => -80.854568) + places = Twitter::Geo.new.reverse_geocode(:lat => 35.061161, :long => -80.854568) assert_equal 4, places.size assert_equal 'Ballantyne West, Charlotte', places[0].full_name assert_equal 'Ballantyne West', places[0].name @@ -58,7 +58,7 @@ class GeoTest < Test::Unit::TestCase should "be able to limit the number of results returned" do stub_get 'http://api.twitter.com/1/geo/reverse_geocode.json?lat=35.061161&max_results=2&long=-80.854568', 'geo_reverse_geocode_limit.json' - places = Geo.reverse_geocode(:lat => 35.061161, :long => -80.854568, :max_results => 2) + places = Twitter::Geo.new.reverse_geocode(:lat => 35.061161, :long => -80.854568, :max_results => 2) assert_equal 2, places.size assert_equal 'Ballantyne West, Charlotte', places[0].full_name assert_equal 'Ballantyne West', places[0].name @@ -66,7 +66,7 @@ class GeoTest < Test::Unit::TestCase should "be able to lookup with granularity" do stub_get 'http://api.twitter.com/1/geo/reverse_geocode.json?lat=35.061161&long=-80.854568&granularity=city', 'geo_reverse_geocode_granularity.json' - places = Geo.reverse_geocode(:lat => 35.061161, :long => -80.854568, :granularity => 'city') + places = Twitter::Geo.new.reverse_geocode(:lat => 35.061161, :long => -80.854568, :granularity => 'city') assert_equal 3, places.size assert_equal 'Charlotte, NC', places[0].full_name assert_equal 'Charlotte', places[0].name diff --git a/test/twitter/request_test.rb b/test/twitter/request_test.rb index 4c4beed9e..f59c3ee79 100644 --- a/test/twitter/request_test.rb +++ b/test/twitter/request_test.rb @@ -29,13 +29,8 @@ class RequestTest < Test::Unit::TestCase context "performing request for collection" do setup do - response = mock('response') do - stubs(:body).returns(fixture_file('user_timeline.json')) - stubs(:code).returns('200') - end - - @client.expects(:get).returns(response) - @object = @request.perform + stub_get("/1/statuses/user_timeline.json?since_id=1234", "user_timeline.json") + @object = @request.perform_get end should "return array of mashes" do @@ -47,13 +42,8 @@ class RequestTest < Test::Unit::TestCase context "performing a request for a single object" do setup do - response = mock('response') do - stubs(:body).returns(fixture_file('status.json')) - stubs(:code).returns('200') - end - - @client.expects(:get).returns(response) - @object = @request.perform + stub_get("/1/statuses/user_timeline.json?since_id=1234", "status.json") + @object = @request.perform_get end should "return a single mash" do @@ -77,18 +67,13 @@ class RequestTest < Test::Unit::TestCase end should "have get shortcut to initialize and perform all in one" do - Twitter::Request.any_instance.expects(:perform).returns(nil) + Twitter::Request.any_instance.expects(:perform_get).returns(nil) Twitter::Request.get(@client, '/foo') end - should "allow setting query string and headers" do - response = mock('response') do - stubs(:body).returns('') - stubs(:code).returns('200') - end - - @client.expects(:get).with('/1/statuses/friends_timeline.json?since_id=1234', {'Foo' => 'Bar'}).returns(response) - Twitter::Request.get(@client, '/1/statuses/friends_timeline.json?since_id=1234', :headers => {'Foo' => 'Bar'}) + should "allow setting query string" do + stub_get('/1/statuses/friends_timeline.json?since_id=1234', 'friends_timeline.json') + Twitter::Request.get(@client, '/1/statuses/friends_timeline.json?since_id=1234') end end @@ -98,25 +83,15 @@ class RequestTest < Test::Unit::TestCase @request = Twitter::Request.new(@client, :post, '/1/statuses/update.json', {:body => {:status => 'Woohoo!'}}) end - should "allow setting body and headers" do - response = mock('response') do - stubs(:body).returns('') - stubs(:code).returns('200') - end - - @client.expects(:post).with('/1/statuses/update.json', {:status => 'Woohoo!'}, {'Foo' => 'Bar'}).returns(response) - Twitter::Request.post(@client, '/1/statuses/update.json', :body => {:status => 'Woohoo!'}, :headers => {'Foo' => 'Bar'}) + should "allow setting body" do + stub_post('/1/statuses/update.json?status=Woohoo%21', 'status.json') + Twitter::Request.post(@client, '/1/statuses/update.json', :body => {:status => 'Woohoo!'}) end context "performing request" do setup do - response = mock('response') do - stubs(:body).returns(fixture_file('status.json')) - stubs(:code).returns('200') - end - - @client.expects(:post).returns(response) - @object = @request.perform + stub_post('/1/statuses/update.json?status=Woohoo%21', 'status.json') + @object = @request.perform_post end should "return a mash of the object" do @@ -125,7 +100,7 @@ class RequestTest < Test::Unit::TestCase end should "have post shortcut to initialize and perform all in one" do - Twitter::Request.any_instance.expects(:perform).returns(nil) + Twitter::Request.any_instance.expects(:perform_post).returns(nil) Twitter::Request.post(@client, '/foo') end end diff --git a/test/twitter/search_test.rb b/test/twitter/search_test.rb index bae02df02..bfd03d76f 100644 --- a/test/twitter/search_test.rb +++ b/test/twitter/search_test.rb @@ -21,8 +21,8 @@ class SearchTest < Test::Unit::TestCase end should "pass user agent along with headers when making request" do - Twitter::Search.expects(:get).with('http://search.twitter.com/search.json', {:format => :json, :query => {:q => 'foo'}, :headers => {'User-Agent' => 'Foobar'}}) - Twitter::Search.new('foo', :user_agent => 'Foobar').fetch() + s = Twitter::Search.new('foo', :user_agent => 'Foobar') + assert_equal 'Foobar', s.connection.headers['user_agent'] end should "be able to specify from" do @@ -86,69 +86,69 @@ class SearchTest < Test::Unit::TestCase end should "be able to specify the language" do + stub_get("http://search.twitter.com/search.json?q=&lang=en", "search.json") @search.lang('en') - @search.class.expects(:get).with('http://search.twitter.com/search.json', :query => {:lang => 'en', :q => ''}, :format => :json, :headers => {'User-Agent' => 'Ruby Twitter Gem'}).returns({'foo' => 'bar'}) - @search.fetch() + @search.fetch end should "be able to specify the locale" do stub_get("http://search.twitter.com/search.json?q=&locale=ja", "search.json") @search.locale('ja') - @search.fetch() + @search.fetch end should "be able to specify the number of results per page" do + stub_get("http://search.twitter.com/search.json?q=&rpp=25", "search.json") @search.per_page(25) - @search.class.expects(:get).with('http://search.twitter.com/search.json', :query => {:rpp => 25, :q => ''}, :format => :json, :headers => {'User-Agent' => 'Ruby Twitter Gem'}).returns({'foo' => 'bar'}) - @search.fetch() + @search.fetch end should "be able to specify the page number" do + stub_get("http://search.twitter.com/search.json?q=&page=20", "search.json") @search.page(20) - @search.class.expects(:get).with('http://search.twitter.com/search.json', :query => {:page => 20, :q => ''}, :format => :json, :headers => {'User-Agent' => 'Ruby Twitter Gem'}).returns({'foo' => 'bar'}) - @search.fetch() + @search.fetch end should "be able to specify only returning results greater than an id" do + stub_get("http://search.twitter.com/search.json?q=&since_id=1234", "search.json") @search.since(1234) - @search.class.expects(:get).with('http://search.twitter.com/search.json', :query => {:since_id => 1234, :q => ''}, :format => :json, :headers => {'User-Agent' => 'Ruby Twitter Gem'}).returns({'foo' => 'bar'}) - @search.fetch() + @search.fetch end should "be able to specify since a date" do + stub_get("http://search.twitter.com/search.json?q=&since=2009-04-14", "search.json") @search.since_date('2009-04-14') - @search.class.expects(:get).with('http://search.twitter.com/search.json', :query => { :since => '2009-04-14', :q => ''}, :format => :json, :headers => {'User-Agent' => 'Ruby Twitter Gem'}).returns({ 'foo' => 'bar'}) @search.fetch end should "be able to specify until a date" do + stub_get("http://search.twitter.com/search.json?q=&until=2009-04-14", "search.json") @search.until_date('2009-04-14') - @search.class.expects(:get).with('http://search.twitter.com/search.json', :query => { :until => '2009-04-14', :q => ''}, :format => :json, :headers => {'User-Agent' => 'Ruby Twitter Gem'}).returns({ 'foo' => 'bar'}) @search.fetch end should "be able to specify geo coordinates" do + stub_get("http://search.twitter.com/search.json?q=&geocode=40.757929%2C-73.985506%2C25mi", "search.json") @search.geocode('40.757929', '-73.985506', '25mi') - @search.class.expects(:get).with('http://search.twitter.com/search.json', :query => {:geocode => '40.757929,-73.985506,25mi', :q => ''}, :format => :json, :headers => {'User-Agent' => 'Ruby Twitter Gem'}).returns({'foo' => 'bar'}) - @search.fetch() + @search.fetch end should "be able to specify max id" do + stub_get("http://search.twitter.com/search.json?q=&max_id=1234", "search.json") @search.max(1234) - @search.class.expects(:get).with('http://search.twitter.com/search.json', :query => {:max_id => 1234, :q => ''}, :format => :json, :headers => {'User-Agent' => 'Ruby Twitter Gem'}).returns({'foo' => 'bar'}) - @search.fetch() + @search.fetch end should "be able to set the phrase" do + stub_get("http://search.twitter.com/search.json?q=&phrase=Who%20Dat", "search.json") @search.phrase("Who Dat") - @search.class.expects(:get).with('http://search.twitter.com/search.json', :query => {:phrase => "Who Dat", :q => ''}, :format => :json, :headers => {'User-Agent' => 'Ruby Twitter Gem'}).returns({'foo' => 'bar'}) - @search.fetch() + @search.fetch end should "be able to set the result type" do + stub_get("http://search.twitter.com/search.json?q=&result_type=popular", "search.json") @search.result_type("popular") - @search.class.expects(:get).with('http://search.twitter.com/search.json', :query => {:result_type => 'popular', :q => ''}, :format => :json, :headers => {'User-Agent' => 'Ruby Twitter Gem'}).returns({'foo' => 'bar'}) - @search.fetch() + @search.fetch end should "be able to clear the filters set" do @@ -190,22 +190,12 @@ class SearchTest < Test::Unit::TestCase assert_equal 'PatParslow', first.from_user end - should "cache fetched results so multiple fetches don't keep hitting API" do - Twitter::Search.expects(:get).never - @search.fetch - end - - should "rehit API if fetch is called with true" do - Twitter::Search.expects(:get).once - @search.fetch(true) - end - should "tell if another page is available" do assert @search.next_page? end should "be able to fetch the next page" do - Twitter::Search.expects(:get).with('http://search.twitter.com/search.json', :query => 'page=2&max_id=1446791544&q=%40jnunemaker', :format => :json, :headers => {'User-Agent' => 'Ruby Twitter Gem'}).returns({'foo' => 'bar'}) + stub_get("http://search.twitter.com/search.json?page%3D2%26max_id%3D1446791544%26q%3D%2540jnunemaker=", "search.json") @search.fetch_next_page end end diff --git a/test/twitter/trends_test.rb b/test/twitter/trends_test.rb index fcdcd187b..e1900dbce 100644 --- a/test/twitter/trends_test.rb +++ b/test/twitter/trends_test.rb @@ -6,7 +6,7 @@ class TrendsTest < Test::Unit::TestCase context "Getting current trends" do should "work" do stub_get 'http://api.twitter.com/1/trends/current.json', 'trends_current.json' - trends = Trends.current + trends = Twitter::Trends.new.current assert_equal 10, trends.size assert_equal '#musicmonday', trends[0].name assert_equal '#musicmonday', trends[0].query @@ -16,7 +16,7 @@ class TrendsTest < Test::Unit::TestCase should "be able to exclude hashtags" do stub_get 'http://api.twitter.com/1/trends/current.json?exclude=hashtags', 'trends_current_exclude.json' - trends = Trends.current(:exclude => 'hashtags') + trends = Twitter::Trends.new.current(:exclude => 'hashtags') assert_equal 10, trends.size assert_equal 'New Divide', trends[0].name assert_equal %Q(\"New Divide\"), trends[0].query @@ -28,7 +28,7 @@ class TrendsTest < Test::Unit::TestCase context "Getting daily trends" do should "work" do stub_get 'http://api.twitter.com/1/trends/daily.json?', 'trends_daily.json' - trends = Trends.daily + trends = Twitter::Trends.new.daily assert_equal 480, trends.size assert_equal '#3turnoffwords', trends[0].name assert_equal '#3turnoffwords', trends[0].query @@ -36,7 +36,7 @@ class TrendsTest < Test::Unit::TestCase should "be able to exclude hastags" do stub_get 'http://api.twitter.com/1/trends/daily.json?exclude=hashtags', 'trends_daily_exclude.json' - trends = Trends.daily(:exclude => 'hashtags') + trends = Twitter::Trends.new.daily(:exclude => 'hashtags') assert_equal 480, trends.size assert_equal 'Kobe', trends[0].name assert_equal %Q(Kobe), trends[0].query @@ -44,7 +44,7 @@ class TrendsTest < Test::Unit::TestCase should "be able to get for specific date (with date string)" do stub_get 'http://api.twitter.com/1/trends/daily.json?date=2009-05-01', 'trends_daily_date.json' - trends = Trends.daily(:date => '2009-05-01') + trends = Twitter::Trends.new.daily(:date => '2009-05-01') assert_equal 440, trends.size assert_equal 'Swine Flu', trends[0].name assert_equal %Q(\"Swine Flu\" OR Flu), trends[0].query @@ -52,7 +52,7 @@ class TrendsTest < Test::Unit::TestCase should "be able to get for specific date (with date object)" do stub_get 'http://api.twitter.com/1/trends/daily.json?date=2009-05-01', 'trends_daily_date.json' - trends = Trends.daily(:date => Date.new(2009, 5, 1)) + trends = Twitter::Trends.new.daily(:date => Date.new(2009, 5, 1)) assert_equal 440, trends.size assert_equal 'Swine Flu', trends[0].name assert_equal %Q(\"Swine Flu\" OR Flu), trends[0].query @@ -62,7 +62,7 @@ class TrendsTest < Test::Unit::TestCase context "Getting weekly trends" do should "work" do stub_get 'http://api.twitter.com/1/trends/weekly.json?', 'trends_weekly.json' - trends = Trends.weekly + trends = Twitter::Trends.new.weekly assert_equal 210, trends.size assert_equal "Grey's Anatomy", trends[0].name assert_equal %Q(\"Grey's Anatomy\"), trends[0].query @@ -70,7 +70,7 @@ class TrendsTest < Test::Unit::TestCase should "be able to exclude hastags" do stub_get 'http://api.twitter.com/1/trends/weekly.json?exclude=hashtags', 'trends_weekly_exclude.json' - trends = Trends.weekly(:exclude => 'hashtags') + trends = Twitter::Trends.new.weekly(:exclude => 'hashtags') assert_equal 210, trends.size assert_equal "Grey's Anatomy", trends[0].name assert_equal %Q(\"Grey's Anatomy\"), trends[0].query @@ -78,7 +78,7 @@ class TrendsTest < Test::Unit::TestCase should "be able to get for specific date (with date string)" do stub_get 'http://api.twitter.com/1/trends/weekly.json?date=2009-05-01', 'trends_weekly_date.json' - trends = Trends.weekly(:date => '2009-05-01') + trends = Twitter::Trends.new.weekly(:date => '2009-05-01') assert_equal 210, trends.size assert_equal 'Swine Flu', trends[0].name assert_equal %Q(\"Swine Flu\"), trends[0].query @@ -86,7 +86,7 @@ class TrendsTest < Test::Unit::TestCase should "be able to get for specific date (with date object)" do stub_get 'http://api.twitter.com/1/trends/weekly.json?date=2009-05-01', 'trends_weekly_date.json' - trends = Trends.weekly(:date => Date.new(2009, 5, 1)) + trends = Twitter::Trends.new.weekly(:date => Date.new(2009, 5, 1)) assert_equal 210, trends.size assert_equal 'Swine Flu', trends[0].name assert_equal %Q(\"Swine Flu\"), trends[0].query @@ -97,14 +97,14 @@ class TrendsTest < Test::Unit::TestCase should "return a list of available locations" do stub_get 'http://api.twitter.com/1/trends/available.json?lat=33.237593417&lng=-96.960559033', 'trends_available.json' - locations = Trends.available(:lat => 33.237593417, :lng => -96.960559033) + locations = Twitter::Trends.new.available(:lat => 33.237593417, :lng => -96.960559033) assert_equal 'Ireland', locations.first.country assert_equal 12, locations.first.placeType.code end should "return a list of trends for a given location" do stub_get 'http://api.twitter.com/1/trends/2487956.json', 'trends_location.json' - trends = Trends.for_location(2487956).first.trends + trends = Twitter::Trends.new.for_location(2487956).first.trends assert_equal 'Gmail', trends.last.name end end diff --git a/test/twitter/unauthenticated_test.rb b/test/twitter/unauthenticated_test.rb new file mode 100644 index 000000000..094ffcb5f --- /dev/null +++ b/test/twitter/unauthenticated_test.rb @@ -0,0 +1,108 @@ +require 'test_helper' + +class UnauthenticatedTest < Test::Unit::TestCase + include Twitter + + should "have firehose method for public timeline" do + stub_get('http://api.twitter.com/1/statuses/public_timeline.json', 'firehose.json') + hose = Twitter::Unauthenticated.new.firehose + assert_equal 20, hose.size + first = hose.first + assert_equal '#torrents Ultimativer Flirt Guide - In 10 Minuten jede Frau erobern: Ultimativer Flirt Guide - In 10 Mi.. http://tinyurl.com/d3okh4', first.text + assert_equal 'P2P Torrents', first.user.name + end + + should "have user method for unauthenticated calls to get a user's information" do + stub_get('http://api.twitter.com/1/users/show/jnunemaker.json', 'user.json') + user = Twitter::Unauthenticated.new.user('jnunemaker') + assert_equal 'John Nunemaker', user.name + assert_equal 'Loves his wife, ruby, notre dame football and iu basketball', user.description + end + + should "have status method for unauthenticated calls to get a status" do + stub_get('http://api.twitter.com/1/statuses/show/1533815199.json', 'status_show.json') + status = Twitter::Unauthenticated.new.status(1533815199) + assert_equal 1533815199, status.id + assert_equal 'Eating some oatmeal and butterscotch cookies with a cold glass of milk for breakfast. Tasty!', status.text + end + + should "raise NotFound for unauthenticated calls to get a deleted or nonexistent status" do + stub_get('http://api.twitter.com/1/statuses/show/1.json', 'not_found.json', 404) + assert_raise Twitter::NotFound do + Twitter::Unauthenticated.new.status(1) + end + end + + should "have a timeline method for unauthenticated calls to get a user's timeline" do + stub_get('http://api.twitter.com/1/statuses/user_timeline/jnunemaker.json', 'user_timeline.json') + statuses = Twitter::Unauthenticated.new.timeline('jnunemaker') + assert_equal 1445986256, statuses.first.id + assert_equal 'jnunemaker', statuses.first.user.screen_name + end + + should "raise Unauthorized for unauthenticated calls to get a protected user's timeline" do + stub_get('http://api.twitter.com/1/statuses/user_timeline/protected.json', 'unauthorized.json', 401) + assert_raise Twitter::Unauthorized do + Twitter::Unauthenticated.new.timeline('protected') + end + end + + should "have friend_ids method" do + stub_get('http://api.twitter.com/1/friends/ids/jnunemaker.json', 'friend_ids.json') + ids = Twitter::Unauthenticated.new.friend_ids('jnunemaker') + assert_equal 161, ids.size + end + + should "raise Unauthorized for unauthenticated calls to get a protected user's friend_ids" do + stub_get('http://api.twitter.com/1/friends/ids/protected.json', 'unauthorized.json', 401) + assert_raise Twitter::Unauthorized do + Twitter::Unauthenticated.new.friend_ids('protected') + end + end + + should "have follower_ids method" do + stub_get('http://api.twitter.com/1/followers/ids/jnunemaker.json', 'follower_ids.json') + ids = Twitter::Unauthenticated.new.follower_ids('jnunemaker') + assert_equal 1252, ids.size + end + + should "raise Unauthorized for unauthenticated calls to get a protected user's follower_ids" do + stub_get('http://api.twitter.com/1/followers/ids/protected.json', 'unauthorized.json', 401) + assert_raise Twitter::Unauthorized do + Twitter::Unauthenticated.new.follower_ids('protected') + end + end + + context "when using lists" do + + should "be able to view list timeline" do + stub_get('http://api.twitter.com/1/pengwynn/lists/rubyists/statuses.json', 'list_statuses.json') + tweets = Twitter::Unauthenticated.new.list_timeline('pengwynn', 'rubyists') + assert_equal 20, tweets.size + assert_equal 5272535583, tweets.first.id + assert_equal 'John Nunemaker', tweets.first.user.name + end + + should "be able to limit number of tweets in list timeline" do + stub_get('http://api.twitter.com/1/pengwynn/lists/rubyists/statuses.json?per_page=1', 'list_statuses_1_1.json') + tweets = Twitter::Unauthenticated.new.list_timeline('pengwynn', 'rubyists', :per_page => 1) + assert_equal 1, tweets.size + assert_equal 5272535583, tweets.first.id + assert_equal 'John Nunemaker', tweets.first.user.name + end + + should "be able to paginate through the timeline" do + stub_get('http://api.twitter.com/1/pengwynn/lists/rubyists/statuses.json?page=1&per_page=1', 'list_statuses_1_1.json') + stub_get('http://api.twitter.com/1/pengwynn/lists/rubyists/statuses.json?page=2&per_page=1', 'list_statuses_2_1.json') + tweets = Twitter::Unauthenticated.new.list_timeline('pengwynn', 'rubyists', { :page => 1, :per_page => 1 }) + assert_equal 1, tweets.size + assert_equal 5272535583, tweets.first.id + assert_equal 'John Nunemaker', tweets.first.user.name + tweets = Twitter::Unauthenticated.new.list_timeline('pengwynn', 'rubyists', { :page => 2, :per_page => 1 }) + assert_equal 1, tweets.size + assert_equal 5264324712, tweets.first.id + assert_equal 'John Nunemaker', tweets.first.user.name + end + + end +end diff --git a/test/twitter_test.rb b/test/twitter_test.rb index 0e9689031..ebaf866ec 100644 --- a/test/twitter_test.rb +++ b/test/twitter_test.rb @@ -13,106 +13,4 @@ class TwitterTest < Test::Unit::TestCase end end - should "have firehose method for public timeline" do - stub_get('http://api.twitter.com/1/statuses/public_timeline.json', 'firehose.json') - hose = Twitter.firehose - assert_equal 20, hose.size - first = hose.first - assert_equal '#torrents Ultimativer Flirt Guide - In 10 Minuten jede Frau erobern: Ultimativer Flirt Guide - In 10 Mi.. http://tinyurl.com/d3okh4', first.text - assert_equal 'P2P Torrents', first.user.name - end - - should "have user method for unauthenticated calls to get a user's information" do - stub_get('http://api.twitter.com/1/users/show/jnunemaker.json', 'user.json') - user = Twitter.user('jnunemaker') - assert_equal 'John Nunemaker', user.name - assert_equal 'Loves his wife, ruby, notre dame football and iu basketball', user.description - end - - should "have status method for unauthenticated calls to get a status" do - stub_get('http://api.twitter.com/1/statuses/show/1533815199.json', 'status_show.json') - status = Twitter.status(1533815199) - assert_equal 1533815199, status.id - assert_equal 'Eating some oatmeal and butterscotch cookies with a cold glass of milk for breakfast. Tasty!', status.text - end - - should "raise NotFound for unauthenticated calls to get a deleted or nonexistent status" do - stub_get('http://api.twitter.com/1/statuses/show/1.json', 'not_found.json', 404) - assert_raise Twitter::NotFound do - Twitter.status(1) - end - end - - should "have a timeline method for unauthenticated calls to get a user's timeline" do - stub_get('http://api.twitter.com/1/statuses/user_timeline/jnunemaker.json', 'user_timeline.json') - statuses = Twitter.timeline('jnunemaker') - assert_equal 1445986256, statuses.first.id - assert_equal 'jnunemaker', statuses.first.user.screen_name - end - - should "raise Unauthorized for unauthenticated calls to get a protected user's timeline" do - stub_get('http://api.twitter.com/1/statuses/user_timeline/protected.json', 'unauthorized.json', 401) - assert_raise Twitter::Unauthorized do - Twitter.timeline('protected') - end - end - - should "have friend_ids method" do - stub_get('http://api.twitter.com/1/friends/ids/jnunemaker.json', 'friend_ids.json') - ids = Twitter.friend_ids('jnunemaker') - assert_equal 161, ids.size - end - - should "raise Unauthorized for unauthenticated calls to get a protected user's friend_ids" do - stub_get('http://api.twitter.com/1/friends/ids/protected.json', 'unauthorized.json', 401) - assert_raise Twitter::Unauthorized do - Twitter.friend_ids('protected') - end - end - - should "have follower_ids method" do - stub_get('http://api.twitter.com/1/followers/ids/jnunemaker.json', 'follower_ids.json') - ids = Twitter.follower_ids('jnunemaker') - assert_equal 1252, ids.size - end - - should "raise Unauthorized for unauthenticated calls to get a protected user's follower_ids" do - stub_get('http://api.twitter.com/1/followers/ids/protected.json', 'unauthorized.json', 401) - assert_raise Twitter::Unauthorized do - Twitter.follower_ids('protected') - end - end - - context "when using lists" do - - should "be able to view list timeline" do - stub_get('http://api.twitter.com/1/pengwynn/lists/rubyists/statuses.json', 'list_statuses.json') - tweets = Twitter.list_timeline('pengwynn', 'rubyists') - assert_equal 20, tweets.size - assert_equal 5272535583, tweets.first.id - assert_equal 'John Nunemaker', tweets.first.user.name - end - - should "be able to limit number of tweets in list timeline" do - stub_get('http://api.twitter.com/1/pengwynn/lists/rubyists/statuses.json?per_page=1', 'list_statuses_1_1.json') - tweets = Twitter.list_timeline('pengwynn', 'rubyists', :per_page => 1) - assert_equal 1, tweets.size - assert_equal 5272535583, tweets.first.id - assert_equal 'John Nunemaker', tweets.first.user.name - end - - should "be able to paginate through the timeline" do - stub_get('http://api.twitter.com/1/pengwynn/lists/rubyists/statuses.json?page=1&per_page=1', 'list_statuses_1_1.json') - stub_get('http://api.twitter.com/1/pengwynn/lists/rubyists/statuses.json?page=2&per_page=1', 'list_statuses_2_1.json') - tweets = Twitter.list_timeline('pengwynn', 'rubyists', { :page => 1, :per_page => 1 }) - assert_equal 1, tweets.size - assert_equal 5272535583, tweets.first.id - assert_equal 'John Nunemaker', tweets.first.user.name - tweets = Twitter.list_timeline('pengwynn', 'rubyists', { :page => 2, :per_page => 1 }) - assert_equal 1, tweets.size - assert_equal 5264324712, tweets.first.id - assert_equal 'John Nunemaker', tweets.first.user.name - end - - end end diff --git a/twitter.gemspec b/twitter.gemspec index 0745843c8..aa86119a5 100644 --- a/twitter.gemspec +++ b/twitter.gemspec @@ -5,8 +5,10 @@ Gem::Specification.new do |s| s.add_development_dependency("fakeweb", ["~> 1.3.0"]) s.add_development_dependency("mocha", ["~> 0.9.8"]) s.add_development_dependency("shoulda", ["~> 2.11.3"]) + s.add_runtime_dependency("addressable", ["~> 2.2.1"]) + s.add_runtime_dependency("faraday", ["~> 0.4.6"]) + s.add_runtime_dependency("faraday-middleware", ["~> 0.0.1"]) s.add_runtime_dependency("hashie", ["~> 0.4.0"]) - s.add_runtime_dependency("httparty", ["~> 0.6.1"]) s.add_runtime_dependency("oauth", ["~> 0.4.3"]) s.add_runtime_dependency("multi_json", ["~> 0.0.4"]) s.authors = ["John Nunemaker", "Wynn Netherland", "Erik Michaels-Ober"] diff --git a/website/index.html b/website/index.html index d56fd61d3..bd8d379b6 100644 --- a/website/index.html +++ b/website/index.html @@ -61,7 +61,7 @@

Search API Examples

end # you can also use fetch to actually just get the parsed response -Twitter::Search.new.from('jnunemaker').to('oaknd1').fetch() +Twitter::Search.new.from('jnunemaker').to('oaknd1').fetch From 3c4b0aa0b17c661c9f1a74e74a7058c7a27f9320 Mon Sep 17 00:00:00 2001 From: Wynn Netherland Date: Sat, 25 Sep 2010 08:55:34 -0500 Subject: [PATCH 02/12] Bumped faraday-middleware dependency --- Gemfile.lock | 6 +++--- twitter.gemspec | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index c2fe29702..b166099d3 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -4,7 +4,7 @@ PATH twitter (0.9.12) addressable (~> 2.2.1) faraday (~> 0.4.6) - faraday-middleware (~> 0.0.1) + faraday-middleware (~> 0.0.2) hashie (~> 0.4.0) multi_json (~> 0.0.4) oauth (~> 0.4.3) @@ -17,7 +17,7 @@ GEM faraday (0.4.6) addressable (>= 2.1.1) rack (>= 1.0.1) - faraday-middleware (0.0.1) + faraday-middleware (0.0.2) faraday (~> 0.4.5) hashie (0.4.0) mocha (0.9.8) @@ -35,7 +35,7 @@ DEPENDENCIES addressable (~> 2.2.1) fakeweb (~> 1.3.0) faraday (~> 0.4.6) - faraday-middleware (~> 0.0.1) + faraday-middleware (~> 0.0.2) hashie (~> 0.4.0) mocha (~> 0.9.8) multi_json (~> 0.0.4) diff --git a/twitter.gemspec b/twitter.gemspec index aa86119a5..cf17c22ba 100644 --- a/twitter.gemspec +++ b/twitter.gemspec @@ -7,7 +7,7 @@ Gem::Specification.new do |s| s.add_development_dependency("shoulda", ["~> 2.11.3"]) s.add_runtime_dependency("addressable", ["~> 2.2.1"]) s.add_runtime_dependency("faraday", ["~> 0.4.6"]) - s.add_runtime_dependency("faraday-middleware", ["~> 0.0.1"]) + s.add_runtime_dependency("faraday-middleware", ["~> 0.0.2"]) s.add_runtime_dependency("hashie", ["~> 0.4.0"]) s.add_runtime_dependency("oauth", ["~> 0.4.3"]) s.add_runtime_dependency("multi_json", ["~> 0.0.4"]) From 074787e83657ee8c2ec1d749ae9799b5a8395929 Mon Sep 17 00:00:00 2001 From: Wynn Netherland Date: Sat, 25 Sep 2010 09:18:27 -0500 Subject: [PATCH 03/12] added empty body params to make Faraday happy --- lib/twitter/base.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/twitter/base.rb b/lib/twitter/base.rb index 8001f5646..983f8c8e2 100644 --- a/lib/twitter/base.rb +++ b/lib/twitter/base.rb @@ -78,7 +78,7 @@ def status_destroy(id) end def retweet(id) - perform_post("statuses/retweet/#{id}.json") + perform_post("statuses/retweet/#{id}.json", :body => {}) end # Options: id, user_id, screen_name, page @@ -303,7 +303,7 @@ def list_subscribers(list_owner_screen_name, slug, query={}) end def list_subscribe(list_owner_screen_name, slug) - perform_post("#{list_owner_screen_name}/#{slug}/subscribers.json") + perform_post("#{list_owner_screen_name}/#{slug}/subscribers.json", :body => {}) end def list_unsubscribe(list_owner_screen_name, slug) From c02489d148e086ff21e52ef353854f36533a6b30 Mon Sep 17 00:00:00 2001 From: Erik Michaels-Ober Date: Sun, 26 Sep 2010 08:55:43 -0700 Subject: [PATCH 04/12] Update faraday-middleware dependency --- Gemfile.lock | 6 +++--- twitter.gemspec | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index b166099d3..688259e48 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -4,7 +4,7 @@ PATH twitter (0.9.12) addressable (~> 2.2.1) faraday (~> 0.4.6) - faraday-middleware (~> 0.0.2) + faraday-middleware (~> 0.0.3) hashie (~> 0.4.0) multi_json (~> 0.0.4) oauth (~> 0.4.3) @@ -17,7 +17,7 @@ GEM faraday (0.4.6) addressable (>= 2.1.1) rack (>= 1.0.1) - faraday-middleware (0.0.2) + faraday-middleware (0.0.3) faraday (~> 0.4.5) hashie (0.4.0) mocha (0.9.8) @@ -35,7 +35,7 @@ DEPENDENCIES addressable (~> 2.2.1) fakeweb (~> 1.3.0) faraday (~> 0.4.6) - faraday-middleware (~> 0.0.2) + faraday-middleware (~> 0.0.3) hashie (~> 0.4.0) mocha (~> 0.9.8) multi_json (~> 0.0.4) diff --git a/twitter.gemspec b/twitter.gemspec index cf17c22ba..37acdddb5 100644 --- a/twitter.gemspec +++ b/twitter.gemspec @@ -7,7 +7,7 @@ Gem::Specification.new do |s| s.add_development_dependency("shoulda", ["~> 2.11.3"]) s.add_runtime_dependency("addressable", ["~> 2.2.1"]) s.add_runtime_dependency("faraday", ["~> 0.4.6"]) - s.add_runtime_dependency("faraday-middleware", ["~> 0.0.2"]) + s.add_runtime_dependency("faraday-middleware", ["~> 0.0.3"]) s.add_runtime_dependency("hashie", ["~> 0.4.0"]) s.add_runtime_dependency("oauth", ["~> 0.4.3"]) s.add_runtime_dependency("multi_json", ["~> 0.0.4"]) From 032c344824634bc8e4bc1f1f41e6db59a89956dc Mon Sep 17 00:00:00 2001 From: Erik Michaels-Ober Date: Sun, 26 Sep 2010 08:57:27 -0700 Subject: [PATCH 05/12] Remove multi_json dependency (JSON parsing is now handled entirely by faraday-middleware) --- Gemfile.lock | 3 --- lib/twitter.rb | 1 - twitter.gemspec | 1 - 3 files changed, 5 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 688259e48..87ca0ecc5 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -6,7 +6,6 @@ PATH faraday (~> 0.4.6) faraday-middleware (~> 0.0.3) hashie (~> 0.4.0) - multi_json (~> 0.0.4) oauth (~> 0.4.3) GEM @@ -22,7 +21,6 @@ GEM hashie (0.4.0) mocha (0.9.8) rake - multi_json (0.0.4) oauth (0.4.3) rack (1.2.1) rake (0.8.7) @@ -38,7 +36,6 @@ DEPENDENCIES faraday-middleware (~> 0.0.3) hashie (~> 0.4.0) mocha (~> 0.9.8) - multi_json (~> 0.0.4) oauth (~> 0.4.3) shoulda (~> 2.11.3) twitter! diff --git a/lib/twitter.rb b/lib/twitter.rb index 774d2c347..3df0b5e91 100644 --- a/lib/twitter.rb +++ b/lib/twitter.rb @@ -3,7 +3,6 @@ require 'faraday_middleware' require 'forwardable' require 'hashie' -require 'multi_json' require 'oauth' module Twitter diff --git a/twitter.gemspec b/twitter.gemspec index 37acdddb5..211577e1a 100644 --- a/twitter.gemspec +++ b/twitter.gemspec @@ -10,7 +10,6 @@ Gem::Specification.new do |s| s.add_runtime_dependency("faraday-middleware", ["~> 0.0.3"]) s.add_runtime_dependency("hashie", ["~> 0.4.0"]) s.add_runtime_dependency("oauth", ["~> 0.4.3"]) - s.add_runtime_dependency("multi_json", ["~> 0.0.4"]) s.authors = ["John Nunemaker", "Wynn Netherland", "Erik Michaels-Ober"] s.description = %q{Ruby wrapper for the Twitter API} s.email = ["nunemaker@gmail.com"] From 1f99e46f602268bc82daf39b329d81e54abfb292 Mon Sep 17 00:00:00 2001 From: Erik Michaels-Ober Date: Sun, 26 Sep 2010 08:58:03 -0700 Subject: [PATCH 06/12] Remove raise_error method (error handling is not handled by middleware) --- lib/twitter.rb | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/lib/twitter.rb b/lib/twitter.rb index 3df0b5e91..83173eaf9 100644 --- a/lib/twitter.rb +++ b/lib/twitter.rb @@ -58,27 +58,6 @@ class General < TwitterError; end class Unavailable < StandardError; end class InformTwitter < StandardError; end class NotFound < StandardError; end - - def self.raise_errors(response) - case response.code.to_i - when 400 - data = parse(response) - raise RateLimitExceeded.new(data), "(#{response.code}): #{response.message} - #{data['error'] if data}" - when 401 - data = parse(response) - raise Unauthorized.new(data), "(#{response.code}): #{response.message} - #{data['error'] if data}" - when 403 - data = parse(response) - raise General.new(data), "(#{response.code}): #{response.message} - #{data['error'] if data}" - when 404 - raise NotFound, "(#{response.code}): #{response.message}" - when 500 - raise InformTwitter, "Twitter had an internal error. Please let them know in the group. (#{response.code}): #{response.message}" - when 502..503 - raise Unavailable, "(#{response.code}): #{response.message}" - end - end - end require File.expand_path("../faraday/raise_errors", __FILE__) From 2e768fc2590164ddc4e98d82c3ec89cef45cbfe5 Mon Sep 17 00:00:00 2001 From: Erik Michaels-Ober Date: Sun, 26 Sep 2010 09:05:56 -0700 Subject: [PATCH 07/12] Remove test for ability to pass custom user agent --- test/twitter/search_test.rb | 5 ----- 1 file changed, 5 deletions(-) diff --git a/test/twitter/search_test.rb b/test/twitter/search_test.rb index bfd03d76f..7d9300059 100644 --- a/test/twitter/search_test.rb +++ b/test/twitter/search_test.rb @@ -20,11 +20,6 @@ class SearchTest < Test::Unit::TestCase assert_equal 'Foobar', search.user_agent end - should "pass user agent along with headers when making request" do - s = Twitter::Search.new('foo', :user_agent => 'Foobar') - assert_equal 'Foobar', s.connection.headers['user_agent'] - end - should "be able to specify from" do assert @search.from('jnunemaker').query[:q].include? 'from:jnunemaker' end From 7b5674542af8e20ba8b7200b58e85f3564ad0e48 Mon Sep 17 00:00:00 2001 From: Erik Michaels-Ober Date: Mon, 27 Sep 2010 12:16:29 -0700 Subject: [PATCH 08/12] Use new faraday_middleware gem --- Gemfile.lock | 6 +++--- twitter.gemspec | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 87ca0ecc5..77e5b71cb 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -4,7 +4,7 @@ PATH twitter (0.9.12) addressable (~> 2.2.1) faraday (~> 0.4.6) - faraday-middleware (~> 0.0.3) + faraday_middleware (~> 0.0.4) hashie (~> 0.4.0) oauth (~> 0.4.3) @@ -16,7 +16,7 @@ GEM faraday (0.4.6) addressable (>= 2.1.1) rack (>= 1.0.1) - faraday-middleware (0.0.3) + faraday_middleware (0.0.4) faraday (~> 0.4.5) hashie (0.4.0) mocha (0.9.8) @@ -33,7 +33,7 @@ DEPENDENCIES addressable (~> 2.2.1) fakeweb (~> 1.3.0) faraday (~> 0.4.6) - faraday-middleware (~> 0.0.3) + faraday_middleware (~> 0.0.4) hashie (~> 0.4.0) mocha (~> 0.9.8) oauth (~> 0.4.3) diff --git a/twitter.gemspec b/twitter.gemspec index 211577e1a..c9e7173e5 100644 --- a/twitter.gemspec +++ b/twitter.gemspec @@ -7,7 +7,7 @@ Gem::Specification.new do |s| s.add_development_dependency("shoulda", ["~> 2.11.3"]) s.add_runtime_dependency("addressable", ["~> 2.2.1"]) s.add_runtime_dependency("faraday", ["~> 0.4.6"]) - s.add_runtime_dependency("faraday-middleware", ["~> 0.0.3"]) + s.add_runtime_dependency("faraday_middleware", ["~> 0.0.4"]) s.add_runtime_dependency("hashie", ["~> 0.4.0"]) s.add_runtime_dependency("oauth", ["~> 0.4.3"]) s.authors = ["John Nunemaker", "Wynn Netherland", "Erik Michaels-Ober"] From a0f2642d63fe1fc49f321ebf1675dd4440095a18 Mon Sep 17 00:00:00 2001 From: Erik Michaels-Ober Date: Mon, 27 Sep 2010 23:41:00 -0700 Subject: [PATCH 09/12] Capitalize API --- test/twitter/base_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/twitter/base_test.rb b/test/twitter/base_test.rb index cf237574d..0ea0562c0 100644 --- a/test/twitter/base_test.rb +++ b/test/twitter/base_test.rb @@ -26,7 +26,7 @@ class BaseTest < Test::Unit::TestCase @twitter.post("/foo", {:bar => "baz"}) end - context "hitting the api" do + context "hitting the API" do should "be able to get home timeline" do stub_get("/1/statuses/home_timeline.json", "home_timeline.json") timeline = @twitter.home_timeline From 41ddc8c1a048a501f8f32d2bc6730032c905e481 Mon Sep 17 00:00:00 2001 From: Erik Michaels-Ober Date: Mon, 27 Sep 2010 23:43:10 -0700 Subject: [PATCH 10/12] Bump version of faraday_middleware --- Gemfile.lock | 13 ++++++++++--- twitter.gemspec | 2 +- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 77e5b71cb..42e6a7b80 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -4,7 +4,7 @@ PATH twitter (0.9.12) addressable (~> 2.2.1) faraday (~> 0.4.6) - faraday_middleware (~> 0.0.4) + faraday_middleware (~> 0.0.5) hashie (~> 0.4.0) oauth (~> 0.4.3) @@ -16,12 +16,19 @@ GEM faraday (0.4.6) addressable (>= 2.1.1) rack (>= 1.0.1) - faraday_middleware (0.0.4) + faraday_middleware (0.0.5) faraday (~> 0.4.5) + hashie (~> 0.4.0) + multi_json (~> 0.0.4) + oauth2 (~> 0.0.13) hashie (0.4.0) mocha (0.9.8) rake + multi_json (0.0.4) oauth (0.4.3) + oauth2 (0.0.13) + faraday (~> 0.4.1) + multi_json (>= 0.0.4) rack (1.2.1) rake (0.8.7) shoulda (2.11.3) @@ -33,7 +40,7 @@ DEPENDENCIES addressable (~> 2.2.1) fakeweb (~> 1.3.0) faraday (~> 0.4.6) - faraday_middleware (~> 0.0.4) + faraday_middleware (~> 0.0.5) hashie (~> 0.4.0) mocha (~> 0.9.8) oauth (~> 0.4.3) diff --git a/twitter.gemspec b/twitter.gemspec index c9e7173e5..928fd2632 100644 --- a/twitter.gemspec +++ b/twitter.gemspec @@ -7,7 +7,7 @@ Gem::Specification.new do |s| s.add_development_dependency("shoulda", ["~> 2.11.3"]) s.add_runtime_dependency("addressable", ["~> 2.2.1"]) s.add_runtime_dependency("faraday", ["~> 0.4.6"]) - s.add_runtime_dependency("faraday_middleware", ["~> 0.0.4"]) + s.add_runtime_dependency("faraday_middleware", ["~> 0.0.5"]) s.add_runtime_dependency("hashie", ["~> 0.4.0"]) s.add_runtime_dependency("oauth", ["~> 0.4.3"]) s.authors = ["John Nunemaker", "Wynn Netherland", "Erik Michaels-Ober"] From 3545d921d374db5293ca95f9e0f4eff8584e3d08 Mon Sep 17 00:00:00 2001 From: Erik Michaels-Ober Date: Mon, 27 Sep 2010 23:50:27 -0700 Subject: [PATCH 11/12] Add json as development dependency --- Gemfile.lock | 2 ++ twitter.gemspec | 1 + 2 files changed, 3 insertions(+) diff --git a/Gemfile.lock b/Gemfile.lock index 42e6a7b80..da010b889 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -22,6 +22,7 @@ GEM multi_json (~> 0.0.4) oauth2 (~> 0.0.13) hashie (0.4.0) + json (1.4.6) mocha (0.9.8) rake multi_json (0.0.4) @@ -42,6 +43,7 @@ DEPENDENCIES faraday (~> 0.4.6) faraday_middleware (~> 0.0.5) hashie (~> 0.4.0) + json (~> 1.4.6) mocha (~> 0.9.8) oauth (~> 0.4.3) shoulda (~> 2.11.3) diff --git a/twitter.gemspec b/twitter.gemspec index 928fd2632..c50aba011 100644 --- a/twitter.gemspec +++ b/twitter.gemspec @@ -3,6 +3,7 @@ require File.expand_path("../lib/twitter/version", __FILE__) Gem::Specification.new do |s| s.add_development_dependency("fakeweb", ["~> 1.3.0"]) + s.add_development_dependency("json", ["~> 1.4.6"]) s.add_development_dependency("mocha", ["~> 0.9.8"]) s.add_development_dependency("shoulda", ["~> 2.11.3"]) s.add_runtime_dependency("addressable", ["~> 2.2.1"]) From 8f4c5ffc125e51c1e146877e256f1edfacd32cbb Mon Sep 17 00:00:00 2001 From: Erik Michaels-Ober Date: Tue, 28 Sep 2010 06:18:37 -0700 Subject: [PATCH 12/12] Don't send POST body in URL --- lib/twitter/geo.rb | 12 ++++++------ lib/twitter/request.rb | 28 +++++++++++++++------------- lib/twitter/search.rb | 4 ++-- lib/twitter/trends.rb | 24 ++++++++++++------------ lib/twitter/unauthenticated.rb | 28 ++++++++++++++-------------- test/twitter/base_test.rb | 12 ++++++------ test/twitter/request_test.rb | 12 ++++++------ 7 files changed, 61 insertions(+), 59 deletions(-) diff --git a/lib/twitter/geo.rb b/lib/twitter/geo.rb index 2eedc9374..0ca9977a9 100644 --- a/lib/twitter/geo.rb +++ b/lib/twitter/geo.rb @@ -9,22 +9,22 @@ def initialize(options={}) end def place(place_id, options={}) - results = connection.get do |req| - req.url "id/#{place_id}.json", options + results = connection.get do |request| + request.url "id/#{place_id}.json", options end.body results end def search(options={}) - results = connection.get do |req| - req.url "search.json", options + results = connection.get do |request| + request.url "search.json", options end.body results.result.values.flatten end def reverse_geocode(options={}) - results = connection.get do |req| - req.url "reverse_geocode.json", options + results = connection.get do |request| + request.url "reverse_geocode.json", options end.body results.result.values.flatten end diff --git a/lib/twitter/request.rb b/lib/twitter/request.rb index 5bbcb5e2b..c1df8d428 100644 --- a/lib/twitter/request.rb +++ b/lib/twitter/request.rb @@ -26,15 +26,15 @@ def initialize(client, method, path, options={}) @client, @method, @path, @options = client, method, path, options end - def uri - @uri ||= begin - uri = URI.parse(path) + def url + @url ||= begin + url = URI.parse(path) if options[:query] && options[:query] != {} - uri.query = to_query(options[:query]) + url.query = to_query(options[:query]) end - uri.to_s + url.to_s end end @@ -51,26 +51,28 @@ def connection end def perform_get - results = connection.get do |req| - req.url uri + results = connection.get do |request| + request.url url end.body end def perform_post - results = connection.post do |req| - req.url uri, options[:body] + results = connection.post do |request| + request.path = url + request.body = options[:body] end.body end def perform_put - results = connection.put do |req| - req.url uri, options[:body] + results = connection.put do |request| + request.path = url + request.body = options end.body end def perform_delete - results = connection.delete do |req| - req.url uri + results = connection.delete do |request| + request.url url end.body end diff --git a/lib/twitter/search.rb b/lib/twitter/search.rb index 1d5f1f9e2..a3987aa80 100644 --- a/lib/twitter/search.rb +++ b/lib/twitter/search.rb @@ -184,8 +184,8 @@ def connection protected def perform_get(query) - @fetch = connection.get do |req| - req.url @api_endpoint.path, query + @fetch = connection.get do |request| + request.url @api_endpoint.path, query end.body end diff --git a/lib/twitter/trends.rb b/lib/twitter/trends.rb index 5ab58a47b..363badc90 100644 --- a/lib/twitter/trends.rb +++ b/lib/twitter/trends.rb @@ -10,8 +10,8 @@ def initialize(options={}) # :exclude => 'hashtags' to exclude hashtags def current(options={}) - results = connection.get do |req| - req.url "current.json", options + results = connection.get do |request| + request.url "current.json", options end.body results = results.trends.values.flatten end @@ -19,8 +19,8 @@ def current(options={}) # :exclude => 'hashtags' to exclude hashtags # :date => yyyy-mm-dd for specific date def daily(options={}) - results = connection.get do |req| - req.url "daily.json", options + results = connection.get do |request| + request.url "daily.json", options end.body results = results.trends.values.flatten end @@ -28,21 +28,21 @@ def daily(options={}) # :exclude => 'hashtags' to exclude hashtags # :date => yyyy-mm-dd for specific date def weekly(options={}) - results = connection.get do |req| - req.url "weekly.json", options + results = connection.get do |request| + request.url "weekly.json", options end.body results = results.trends.values.flatten end - def available(query={}) - connection.get do |req| - req.url "available.json", query + def available(options={}) + connection.get do |request| + request.url "available.json", options end.body end - def for_location(woeid,options = {}) - connection.get do |req| - req.url "#{woeid}.json", options + def for_location(woeid, options = {}) + connection.get do |request| + request.url "#{woeid}.json", options end.body end diff --git a/lib/twitter/unauthenticated.rb b/lib/twitter/unauthenticated.rb index 8c3f5ed7c..6544723c3 100644 --- a/lib/twitter/unauthenticated.rb +++ b/lib/twitter/unauthenticated.rb @@ -9,46 +9,46 @@ def initialize(options={}) end def firehose(options = {}) - results = connection.get do |req| - req.url "statuses/public_timeline.json", options + results = connection.get do |request| + request.url "statuses/public_timeline.json", options end.body end def user(id, options={}) - results = connection.get do |req| - req.url "users/show/#{id}.json", options + results = connection.get do |request| + request.url "users/show/#{id}.json", options end.body end def status(id, options={}) - results = connection.get do |req| - req.url "statuses/show/#{id}.json", options + results = connection.get do |request| + request.url "statuses/show/#{id}.json", options end.body end def friend_ids(id, options={}) - results = connection.get do |req| - req.url "friends/ids/#{id}.json", options + results = connection.get do |request| + request.url "friends/ids/#{id}.json", options end.body end def follower_ids(id, options={}) - results = connection.get do |req| - req.url "followers/ids/#{id}.json", options + results = connection.get do |request| + request.url "followers/ids/#{id}.json", options end.body end def timeline(id, options={}) - results = connection.get do |req| - req.url "statuses/user_timeline/#{id}.json", options + results = connection.get do |request| + request.url "statuses/user_timeline/#{id}.json", options end.body end # :per_page = max number of statues to get at once # :page = which page of tweets you wish to get def list_timeline(list_owner_screen_name, slug, options = {}) - results = connection.get do |req| - req.url "#{list_owner_screen_name}/lists/#{slug}/statuses.json", options + results = connection.get do |request| + request.url "#{list_owner_screen_name}/lists/#{slug}/statuses.json", options end.body end diff --git a/test/twitter/base_test.rb b/test/twitter/base_test.rb index 0ea0562c0..dec7cd965 100644 --- a/test/twitter/base_test.rb +++ b/test/twitter/base_test.rb @@ -68,7 +68,7 @@ class BaseTest < Test::Unit::TestCase end should "be able to update status" do - stub_post("/1/statuses/update.json?status=Rob%20Dyrdek%20is%20the%20funniest%20man%20alive.%20That%20is%20all.", "status.json") + stub_post("/1/statuses/update.json", "status.json") status = @twitter.update("Rob Dyrdek is the funniest man alive. That is all.") assert_equal 'John Nunemaker', status.user.name assert_equal 'Rob Dyrdek is the funniest man alive. That is all.', status.text @@ -211,7 +211,7 @@ class BaseTest < Test::Unit::TestCase end should "report a spammer" do - stub_post("/1/report_spam.json?screen_name=lucaasvaz00", "report_spam.json") + stub_post("/1/report_spam.json", "report_spam.json") spammer = @twitter.report_spam(:screen_name => 'lucaasvaz00') assert_equal 'lucaasvaz00', spammer.screen_name end @@ -244,7 +244,7 @@ class BaseTest < Test::Unit::TestCase end should "be able to create a saved search" do - stub_post("/1/saved_searches/create.json?query=great%20danes", "saved_search.json") + stub_post("/1/saved_searches/create.json", "saved_search.json") search = @twitter.saved_search_create("great danes") assert_equal 'great danes', search.query end @@ -259,7 +259,7 @@ class BaseTest < Test::Unit::TestCase context "when using lists" do should "be able to create a new list" do - stub_post("/1/pengwynn/lists.json?name=Rubyists&user=pengwynn", "list.json") + stub_post("/1/pengwynn/lists.json", "list.json") list = @twitter.list_create("pengwynn", {:name => "Rubyists"}) assert_equal 'Rubyists', list.name assert_equal 'rubyists', list.slug @@ -267,7 +267,7 @@ class BaseTest < Test::Unit::TestCase end should "be able to update a list" do - stub_put("/1/pengwynn/lists/rubyists.json?name=Rubyists", "list.json") + stub_put("/1/pengwynn/lists/rubyists.json", "list.json") list = @twitter.list_update("pengwynn", "rubyists", {:name => "Rubyists"}) assert_equal 'Rubyists', list.name assert_equal 'rubyists', list.slug @@ -367,7 +367,7 @@ class BaseTest < Test::Unit::TestCase end should "be able to add a member to a list" do - stub_post("/1/pengwynn/rubyists/members.json?id=4243", "user.json") + stub_post("/1/pengwynn/rubyists/members.json", "user.json") user = @twitter.list_add_member("pengwynn", "rubyists", 4243) assert_equal 'jnunemaker', user.screen_name end diff --git a/test/twitter/request_test.rb b/test/twitter/request_test.rb index f59c3ee79..f6576c9ef 100644 --- a/test/twitter/request_test.rb +++ b/test/twitter/request_test.rb @@ -23,8 +23,8 @@ class RequestTest < Test::Unit::TestCase assert_equal 1234, @request.options[:query][:since_id] end - should "have uri" do - assert_equal '/1/statuses/user_timeline.json?since_id=1234', @request.uri + should "have URL" do + assert_equal '/1/statuses/user_timeline.json?since_id=1234', @request.url end context "performing request for collection" do @@ -55,14 +55,14 @@ class RequestTest < Test::Unit::TestCase context "with no query string" do should "not have any query string" do request = Twitter::Request.new(@client, :get, '/1/statuses/user_timeline.json') - assert_equal '/1/statuses/user_timeline.json', request.uri + assert_equal '/1/statuses/user_timeline.json', request.url end end context "with blank query string" do should "not have any query string" do request = Twitter::Request.new(@client, :get, '/1/statuses/user_timeline.json', :query => {}) - assert_equal '/1/statuses/user_timeline.json', request.uri + assert_equal '/1/statuses/user_timeline.json', request.url end end @@ -84,13 +84,13 @@ class RequestTest < Test::Unit::TestCase end should "allow setting body" do - stub_post('/1/statuses/update.json?status=Woohoo%21', 'status.json') + stub_post('/1/statuses/update.json', 'status.json') Twitter::Request.post(@client, '/1/statuses/update.json', :body => {:status => 'Woohoo!'}) end context "performing request" do setup do - stub_post('/1/statuses/update.json?status=Woohoo%21', 'status.json') + stub_post('/1/statuses/update.json', 'status.json') @object = @request.perform_post end