diff --git a/Rakefile b/Rakefile index ecb8834..a87c11d 100644 --- a/Rakefile +++ b/Rakefile @@ -54,7 +54,6 @@ GEM_SPEC = Gem::Specification.new do |s| s.add_dependency 'rake', '>= 0.7.3' s.add_dependency 'xmpp4r', '>= 0.3.1' s.add_dependency 'gmailer', '>= 0.1.7' - s.add_dependency 'twitter4r', '>= 0.3.0' s.files = Dir.glob("{bin,lib,test}/**/*").delete_if { |item| item.include?('__workdir') } s.files += %w(License.txt Readme.markdown Changelog.txt Rakefile) diff --git a/lib/cerberus/publisher/twitter.rb b/lib/cerberus/publisher/twitter.rb index 69f03d6..03a0cfc 100644 --- a/lib/cerberus/publisher/twitter.rb +++ b/lib/cerberus/publisher/twitter.rb @@ -13,7 +13,6 @@ def Time.parse(args) class Cerberus::Publisher::Twitter < Cerberus::Publisher::Base def self.publish(state, manager, options) begin - gem 'twitter4r', '>=0.3.0' require 'twitter' twitter_options = options[:publisher, :twitter] diff --git a/lib/vendor/twitter/CHANGES b/lib/vendor/twitter/CHANGES new file mode 100644 index 0000000..7dca0f0 --- /dev/null +++ b/lib/vendor/twitter/CHANGES @@ -0,0 +1,129 @@ += CHANGES + +Catalog(ue) of changes for Twitter4R 0.1.x releases including Retrospectiva ticket cross-reference numbers. Refer to http://retro.tautology.net/projects/twitter4r/tickets for more information. + +== 0.3.1 Changes +* +* Added specs for Twitter::Client#profile (:info, :colors and :device cases) +* Added Twitter4R shell +* Improved code coverage for twitter/ext/stdlib code and removed hard coded extension in GET request path for retrieving account information +* Changed Twitter::RESTError super class to be RuntimeError instead of Exception +* Added URI.encode => CGI.escape fix +* Added block methods +* Added Twitter::Client#inspect method to XXXX out passwords +* Minor refactoring of spec/spec_helper.rb +* Added support for :page, :lite and :since options for Twitter::User#followers, Twitter::Client#my([:friends|:followers]...) and Twitter::Client#user([:friends|:followers]...)calls +* Added Twitter::Client.status(:replies) support + + +== 0.3.0 Changes +* Bunch of changes, but forgot to track them and too lazy to look through the SVN/Git logs. Sorry. + +== 0.2.5 Changes + +=== 2007-09-23 +* Added paging support as per Sergio Santos' request (#36). + +== 0.2.4 Changes + +=== 2007-07-24 +* Fixed ActiveSupport +Time#to_s+ conflict such that integration with Rails is much less painful (#34) + +== 0.2.3 Changes + +=== 2007-07-22 +* Fixed defect #31 such that passing string screen name as for user argument is handled correctly. +* Fixed #30 typo: respond_to -> respond_to? +* Added relevant exception handling for #message(:post, ...) case (#32) +* Add ability to pass in Twitter::User object to Twitter::Client#user(...) #33 +* Added stats Rake task +* Updated RDoc for Twitter::Client#user to warn against using it to get followers of authenticated user and updated ArgumentError raising logic as per #29. + +== 0.2.2 Changes + +=== 2007-07-18 +* Fixed URI paths for user, messaging and friendship APIs (#25) +* Added action checks for Twitter::Client methods: #user, #my, #message, #messages, #status, #timeline, #friend (#26) +* Added 'source' configuration documentation. +* Added missing attributes for Twitter::User (#28) + +== 0.2.1 Changes + +=== 2007-07-17 +* Added 'source' feature and configurability. + +== 0.2.0 Changes + +=== 2007-07-08 +* Added featured users API as an "extra" (#19). +* Productionized website for publishing. +* Published Ruby Gem on Rubyforge. + +=== 2007-07-07 +* Refactored Twitter4R API to be more consistent, by grouping APIs (#6): + - Messaging APIs: direct_messages, new, destroy, replies + - Friendship APIs: create, destroy +* Added following features (#7): + - Retrieving direct messages + - User APIs: friends, followers, show +* Updated documentation and example code. + +=== 2007-07-06 +* Refactored Twitter4R API to be more consistent, by grouping APIs (#6): + - Status APIs: show, update, destroy + - User APIs: friends, followers, show +* Added X-Twitter-Client HTTP headers and Twitter::Config options (#16) +* Removed redundant feature (#8): + - Followers timeline +* Refactored HTTP request/response code to DRY up code. +* Fix REST error handling to use #is_a?(HTTPSuccess) instead of code in ['200', '201'] to determine REST error (#15). + +=== 2007-06-25 +* Updated example documentation (#14) +* Refactored marshaling unmarshaling code (#13) + +=== 2007-06-20 +* Added proxy user/pass support. Tested only via endo-testing. (No system/integration testing behind real proxy as I do not have that environment). + + +=== 2007-06-17 +* Refactored Twitter4R API to be more consistent, by grouping APIs (#6): + - Timeline APIs: public, friends, user + +=== 2007-06-13 +* Added RSpec Autotest integration +* Fixed Twitter::Meta generation of spec for hash values +* Added HTTP header to each request including generated User-Agent header +* Added RCovMorpher and template to restyle RCov output upon release +* Added Gemspec dependencies and requirements +* Added default tidy YAML configuration file for RCovMorpher +* Added Contributors list and updated external dependencies list to README +* Removed shebang from examples + +=== 2007-06-12 +* Added proxy support as per Kaiichi Matsunaga submitted patch (#11). +* Added SSL support (#12) + +=== 2007-05-19 +* Translated RSpec specifications from 0.8.2 compliant to 1.0.0 (#10) + +== 0.1.1 Changes + +=== 2007-06-25 +* Added SSL support (#12) +* Added Proxy support (#11) + +== 0.1.0 Changes + +=== 2007-05-08 +* Added Google Analytics Javascript code to website pages (#5) + +=== 2007-05-07 +* Fixed errors in online sample code documentation and redeployed website (#2 and #3) +* Created more consistent RDoc theme to go more with website home page (#4) + +=== 2007-05-06 +* Initial revision of codebase commited; includes: + - Achieved 80% Twitter API feature-completeness + - Attained 100% RSpec C0 code coverage + - Rake tasks for: RSpec, RCov, RDoc, Gem, Rubyforge Publishing, etc. diff --git a/lib/vendor/twitter/MIT-LICENSE b/lib/vendor/twitter/MIT-LICENSE new file mode 100644 index 0000000..682e0bd --- /dev/null +++ b/lib/vendor/twitter/MIT-LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2006-2007 Susan Potter . + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/lib/vendor/twitter/README b/lib/vendor/twitter/README new file mode 100644 index 0000000..c36971b --- /dev/null +++ b/lib/vendor/twitter/README @@ -0,0 +1,37 @@ += Twitter4R + +* Project Website - http://twitter4r.rubyforge.org +* Mailing List - http://groups.google.com/group/twitter4r-users + +== Developers +* {Susan Potter}[http://SusanPotter.NET] + +== Contributors +Code: +* Kaiichi Matsunaga - proxy code suggestion +* Sergio Santos <> - message paging code suggestion +* Adam Stiles - URI.encode => CGI.escape fix +* Carl Crawley - Friendship get => post fix +* Christian Johansen - in_reply_to attributes in Twitter::Status + +Design Suggestions: +* Bosco So - making Twitter::Error a RuntimeError instead of an Exception to prevent irb from crashing out. + +== Description +Twitter4R provides an object based API to query or update your Twitter account via pure Ruby. It hides the ugly HTTP/REST code from your code. + +== External Dependencies +* Ruby 1.8 (tested with 1.8.6) +* RSpec gem 1.0.0+ (tested with 1.1.3) +* JSON gem 0.4.3+ (tested with versions: 1.1.1 and 1.1.2) +* jcode (for unicode support) + +== Usage Examples +Twitter4R starting with version 0.1.1 and above is organized into seven parts: +* {Configuration API}[link:files/examples/configure_rb.html] +* {Friendship API}[link:files/examples/friendship_rb.html] +* {Messaging API}[link:files/examples/messaging_rb.html] +* {Model API}[link:files/examples/model_rb.html] +* {Status API}[link:files/examples/status_rb.html] +* {Timeline API}[link:files/examples/timeline_rb.html] +* {User API}[link:files/examples/user_rb.html] diff --git a/lib/vendor/twitter/TODO b/lib/vendor/twitter/TODO new file mode 100644 index 0000000..9e34bd3 --- /dev/null +++ b/lib/vendor/twitter/TODO @@ -0,0 +1,7 @@ + +0.3.1 TODO: +* Add specs for :page, :lite and :since options support in Twitter::Client#my(...), Twitter::Client#user(....) and Twitter::User#followers calls +* Add specs for :replies support in Twitter::Client#status(...) and Twitter::Client#timeline_for(...) +* Add RDoc for :replies support +* Add better RDoc for Twitter::Client.account_info(:rate_limit_status) +* Add specs for Twitter::Client.account_info(:rate_limit_status) diff --git a/lib/vendor/twitter/lib/twitter.rb b/lib/vendor/twitter/lib/twitter.rb new file mode 100644 index 0000000..500f87c --- /dev/null +++ b/lib/vendor/twitter/lib/twitter.rb @@ -0,0 +1,34 @@ +# +require('rubygems') + +module Twitter; end + +def require_local(suffix) + require(File.expand_path(File.join(File.dirname(__FILE__), suffix))) +end + +# For better unicode support in 1.8 +if RUBY_VERSION < '1.9' + $KCODE = 'u' + require 'jcode' +end + +# External requires +require('yaml') +require('date') +require('time') +require('net/https') +require('uri') +require('cgi') +require('json') +require('yaml') + +# Ordering matters...pay attention here! +require_local('twitter/ext') +require_local('twitter/version') +require_local('twitter/meta') +require_local('twitter/core') +require_local('twitter/model') +require_local('twitter/config') +require_local('twitter/client') +require_local('twitter/console') diff --git a/lib/vendor/twitter/lib/twitter/client.rb b/lib/vendor/twitter/lib/twitter/client.rb new file mode 100644 index 0000000..bb2d83e --- /dev/null +++ b/lib/vendor/twitter/lib/twitter/client.rb @@ -0,0 +1,24 @@ +# client.rb contains the classes, methods and extends Twitter4R +# features to define client calls to the Twitter REST API. +# +# See: +# * Twitter::Client + +# Used to query or post to the Twitter REST API to simplify code. +class Twitter::Client + include Twitter::ClassUtilMixin +end + +require('twitter/client/base') +require('twitter/client/timeline') +require('twitter/client/status') +require('twitter/client/friendship') +require('twitter/client/messaging') +require('twitter/client/user') +require('twitter/client/auth') +require('twitter/client/favorites') +require('twitter/client/blocks') +require('twitter/client/account') +require('twitter/client/graph') +require('twitter/client/profile') +require('twitter/client/search') diff --git a/lib/vendor/twitter/lib/twitter/client/account.rb b/lib/vendor/twitter/lib/twitter/client/account.rb new file mode 100644 index 0000000..62ded84 --- /dev/null +++ b/lib/vendor/twitter/lib/twitter/client/account.rb @@ -0,0 +1,24 @@ +class Twitter::Client + @@ACCOUNT_URIS = { + :rate_limit_status => '/account/rate_limit_status', + } + + # Provides access to the Twitter rate limit status API. + # + # You can find out information about your account status. Currently the only + # supported type of account status is the :rate_limit_status which + # returns a Twitter::RateLimitStatus object. + # + # Example: + # account_status = client.account_info + # puts account_status.remaining_hits + def account_info(type = :rate_limit_status) + connection = create_http_connection + connection.start do |connection| + response = http_connect do |conn| + create_http_get_request(@@ACCOUNT_URIS[type]) + end + bless_models(Twitter::RateLimitStatus.unmarshal(response.body)) + end + end +end diff --git a/lib/vendor/twitter/lib/twitter/client/auth.rb b/lib/vendor/twitter/lib/twitter/client/auth.rb new file mode 100644 index 0000000..44a215a --- /dev/null +++ b/lib/vendor/twitter/lib/twitter/client/auth.rb @@ -0,0 +1,27 @@ +class Twitter::Client + @@AUTHENTICATION_URIS = { + :verify => '/account/verify_credentials', + } + + # Provides access to the Twitter verify credentials API. + # + # You can verify Twitter user credentials with minimal overhead using this method. + # + # Example: + # client.authenticate?("osxisforlightweights", "l30p@rd_s^cks!") + def authenticate?(login, password) + verify_credentials(login, password) + end + +private + def verify_credentials(username, passwd) + connection = create_http_connection + connection.start do |connection| + request = create_http_get_request("#{@@AUTHENTICATION_URIS[:verify]}.json") + request.basic_auth(username, passwd) + response = connection.request(request) + response.is_a?(Net::HTTPSuccess) ? true : false + end + end +end + diff --git a/lib/vendor/twitter/lib/twitter/client/base.rb b/lib/vendor/twitter/lib/twitter/client/base.rb new file mode 100644 index 0000000..eca96ae --- /dev/null +++ b/lib/vendor/twitter/lib/twitter/client/base.rb @@ -0,0 +1,93 @@ +class Twitter::Client + alias :old_inspect :inspect + def inspect + s = old_inspect + s.gsub!(/@password=".*?"/, '@password="XXXX"') + end + protected + attr_accessor :login, :password + + # Returns the response of the HTTP connection. + def http_connect(body = nil, require_auth = true, service = :rest, &block) + require_block(block_given?) + connection = create_http_connection(service) + connection.start do |connection| + request = yield connection if block_given? + request.basic_auth(@login, @password) if require_auth + response = connection.request(request, body) + handle_rest_response(response) + response + end + end + + # "Blesses" model object with client information + def bless_model(model) + model.bless(self) if model + end + + def bless_models(list) + return bless_model(list) if list.respond_to?(:client=) + list.collect { |model| bless_model(model) } if list.respond_to?(:collect) + end + + private + @@http_header = nil + + def raise_rest_error(response, uri = nil) + raise Twitter::RESTError.new(:code => response.code, + :message => response.message, + :uri => uri) + end + + def handle_rest_response(response, uri = nil) + unless response.is_a?(Net::HTTPSuccess) + raise_rest_error(response, uri) + end + end + + def create_http_connection(service = :rest) + case service + when :rest + protocol, host, port = @@config.protocol, @@config.host, @@config.port + when :search + protocol, host, port = @@config.search_protocol, @@config.search_host, @@config.search_port + end + conn = Net::HTTP.new(host, port, + @@config.proxy_host, @@config.proxy_port, + @@config.proxy_user, @@config.proxy_pass) + if protocol == :ssl + conn.use_ssl = true + conn.verify_mode = OpenSSL::SSL::VERIFY_NONE + end + conn + end + + def http_header + # can cache this in class variable since all "variables" used to + # create the contents of the HTTP header are determined by other + # class variables that are not designed to change after instantiation. + @@http_header ||= { + 'User-Agent' => "Twitter4R v#{Twitter::Version.to_version} [#{@@config.user_agent}]", + 'Accept' => 'text/x-json', + 'X-Twitter-Client' => @@config.application_name, + 'X-Twitter-Client-Version' => @@config.application_version, + 'X-Twitter-Client-URL' => @@config.application_url, + } + @@http_header + end + + def create_http_get_request(uri, params = {}) + path = (params.size > 0) ? "#{uri}?#{params.to_http_str}" : uri + Net::HTTP::Get.new(path, http_header) + end + + def create_http_post_request(uri) + Net::HTTP::Post.new(uri, http_header) + end + + def create_http_delete_request(uri, params = {}) + path = (params.size > 0) ? "#{uri}?#{params.to_http_str}" : uri + Net::HTTP::Delete.new(path, http_header) + end +end + diff --git a/lib/vendor/twitter/lib/twitter/client/blocks.rb b/lib/vendor/twitter/lib/twitter/client/blocks.rb new file mode 100644 index 0000000..6541f8f --- /dev/null +++ b/lib/vendor/twitter/lib/twitter/client/blocks.rb @@ -0,0 +1,35 @@ +class Twitter::Client + @@BLOCK_URIS = { + :add => '/blocks/create', + :remove => '/blocks/destroy', + } + + # Provides access to the Twitter Block API. + # + # You can add and remove blocks to users using this method. + # + # action can be any of the following values: + # * :add - to add a block, you would use this action value + # * :remove - to remove a block use this. + # + # The value must be either the user screen name, integer unique user ID or Twitter::User + # object representation. + # + # Examples: + # screen_name = 'dictionary' + # client.block(:add, 'dictionary') + # client.block(:remove, 'dictionary') + # id = 1260061 + # client.block(:add, id) + # client.block(:remove, id) + # user = Twitter::User.find(id, client) + # client.block(:add, user) + # client.block(:remove, user) + def block(action, value) + raise ArgumentError, "Invalid friend action provided: #{action}" unless @@BLOCK_URIS.keys.member?(action) + value = value.to_i unless value.is_a?(String) + uri = "#{@@BLOCK_URIS[action]}/#{value}.json" + response = http_connect {|conn| create_http_get_request(uri) } + bless_model(Twitter::User.unmarshal(response.body)) + end +end diff --git a/lib/vendor/twitter/lib/twitter/client/favorites.rb b/lib/vendor/twitter/lib/twitter/client/favorites.rb new file mode 100644 index 0000000..5b72015 --- /dev/null +++ b/lib/vendor/twitter/lib/twitter/client/favorites.rb @@ -0,0 +1,53 @@ +class Twitter::Client + # Why Twitter.com developers can't correctly document their API, I do not know! + @@FAVORITES_URIS = { + :add => '/favourings/create', + :remove => '/favourings/destroy', + } + + # Provides access to the Twitter list favorites API. + # + # You can access the authenticated [Twitter] user's favorites list using this method. + # + # By default you will receive the last twenty statuses added to your favorites list. + # To get a previous page you can provide options to this method. For example, + # statuses = client.favorites(:page => 2) + # The above one-liner will get the second page of favorites for the authenticated user. + def favorites(options = nil) + def uri_suffix(opts); opts && opts[:page] ? "?page=#{opts[:page]}" : ""; end + uri = '/favorites.json' + uri_suffix(options) + response = http_connect {|conn| create_http_get_request(uri) } + bless_models(Twitter::Status.unmarshal(response.body)) + end + + # Provides access to the Twitter add/remove favorite API. + # + # You can add and remove favorite status using this method. + # + # action can be any of the following values: + # * :add - to add a status to your favorites, you would use this action value + # * :remove - to remove an status from your existing favorites list use this. + # + # The value must be either the status object to add or remove or + # the integer unique status ID. + # + # Examples: + # id = 126006103423 + # client.favorite(:add, id) + # client.favorite(:remove, id) + # status = Twitter::Status.find(id, client) + # client.favorite(:add, status) + # client.favorite(:remove, status) + def favorite(action, value) + raise ArgumentError, "Invalid favorite action provided: #{action}" unless @@FAVORITES_URIS.keys.member?(action) + value = value.to_i.to_s unless value.is_a?(String) + uri = "#{@@FAVORITES_URIS[action]}/#{value}.json" + case action + when :add + response = http_connect {|conn| create_http_post_request(uri) } + when :remove + response = http_connect {|conn| create_http_delete_request(uri) } + end + bless_model(Twitter::Status.unmarshal(response.body)) + end +end diff --git a/lib/vendor/twitter/lib/twitter/client/friendship.rb b/lib/vendor/twitter/lib/twitter/client/friendship.rb new file mode 100644 index 0000000..6fb05c6 --- /dev/null +++ b/lib/vendor/twitter/lib/twitter/client/friendship.rb @@ -0,0 +1,35 @@ +class Twitter::Client + @@FRIENDSHIP_URIS = { + :add => '/friendships/create', + :remove => '/friendships/destroy', + } + + # Provides access to the Twitter Friendship API. + # + # You can add and remove friends using this method. + # + # action can be any of the following values: + # * :add - to add a friend, you would use this action value + # * :remove - to remove an existing friend from your friends list use this. + # + # The value must be either the user to befriend or defriend's + # screen name, integer unique user ID or Twitter::User object representation. + # + # Examples: + # screen_name = 'dictionary' + # client.friend(:add, 'dictionary') + # client.friend(:remove, 'dictionary') + # id = 1260061 + # client.friend(:add, id) + # client.friend(:remove, id) + # user = Twitter::User.find(id, client) + # client.friend(:add, user) + # client.friend(:remove, user) + def friend(action, value) + raise ArgumentError, "Invalid friend action provided: #{action}" unless @@FRIENDSHIP_URIS.keys.member?(action) + value = value.to_i unless value.is_a?(String) + uri = "#{@@FRIENDSHIP_URIS[action]}/#{value}.json" + response = http_connect {|conn| create_http_post_request(uri) } + bless_model(Twitter::User.unmarshal(response.body)) + end +end diff --git a/lib/vendor/twitter/lib/twitter/client/graph.rb b/lib/vendor/twitter/lib/twitter/client/graph.rb new file mode 100644 index 0000000..8646407 --- /dev/null +++ b/lib/vendor/twitter/lib/twitter/client/graph.rb @@ -0,0 +1,37 @@ +class Twitter::Client + @@GRAPH_URIS = { + :friends => '/friends/ids', + :followers => '/followers/ids', + } + + # Provides access to the Twitter Social Graphing API. + # + # You can retrieve the full graph of a user's friends or followers in one method call. + # + # action can be any of the following values: + # * :friends - retrieves ids of all friends of a given user. + # * :followers - retrieves ids of all followers of a given user. + # + # The value must be either the user screen name, integer unique user ID or Twitter::User + # object representation. + # + # Examples: + # screen_name = 'dictionary' + # client.graph(:friends, 'dictionary') + # client.graph(:followers, 'dictionary') + # id = 1260061 + # client.graph(:friends, id) + # client.graph(:followers, id) + # user = Twitter::User.find(id, client) + # client.graph(:friends, user) + # client.graph(:followers, user) + def graph(action, value = nil) + raise ArgumentError, "Invalid friend action provided: #{action}" unless @@GRAPH_URIS.keys.member?(action) + id = value.to_i unless value.nil? || value.is_a?(String) + id ||= value + id ||= @login + uri = "#{@@GRAPH_URIS[action]}.json" + response = http_connect {|conn| create_http_get_request(uri, :id => id) } + JSON.parse(response.body) + end +end diff --git a/lib/vendor/twitter/lib/twitter/client/messaging.rb b/lib/vendor/twitter/lib/twitter/client/messaging.rb new file mode 100644 index 0000000..631f4ed --- /dev/null +++ b/lib/vendor/twitter/lib/twitter/client/messaging.rb @@ -0,0 +1,79 @@ +class Twitter::Client + + @@MESSAGING_URIS = { + :received => '/direct_messages.json', + :sent => '/direct_messages/sent.json', + :post => '/direct_messages/new.json', + :delete => '/direct_messages/destroy', + } + + # Provides access to Twitter's Messaging API for received and + # sent direct messages. + # + # Example: + # received_messages = @twitter.messages(:received) + # + # An ArgumentError will be raised if an invalid action + # is given. Valid actions are: + # * +:received+ + # * +:sent+ + def messages(action, options = {}) + raise ArgumentError, "Invalid messaging action: #{action}" unless [:sent, :received].member?(action) + uri = @@MESSAGING_URIS[action] + response = http_connect {|conn| create_http_get_request(uri, options) } + bless_models(Twitter::Message.unmarshal(response.body)) + end + + # Provides access to Twitter's Messaging API for sending and deleting + # direct messages to other users. + # + # action can be: + # * :post - to send a new direct message, value, to user given. + # * :delete - to delete direct message with message ID value. + # + # value should be: + # * String when action is :post. Will be the message text sent to given user. + # * Integer or Twitter::Message object when action is :delete. Will refer to the unique message ID to delete. When passing in an instance of Twitter::Message that Status will be + # + # user should be: + # * Twitter::User, Integer or String object when action is :post. The Integer must be the unique ID of the Twitter user you wish to send the direct message to and any Strings passed in must be the screen name of the user you wish to send the direct message to. + # * totally ignore when action is :delete. It has no purpose in this use case scenario. + # + # Examples: + # The example below sends the message text 'Are you coming over at 6pm for the BBQ tonight?' to user with screen name 'myfriendslogin'... + # @twitter.message(:post, 'Are you coming over at 6pm for the BBQ tonight?', 'myfriendslogin') + # The example below sends the same message text as above to user with unique integer ID of 1234567890... + # the example below sends the same message text as above to user represented by user object instance of Twitter::User... + # @twitter.message(:post, 'Are you coming over at 6pm for the BBQ tonight?', user) + # message = @twitter.message(:post, 'Are you coming over at 6pm for the BBQ tonight?', 1234567890) + # the example below delete's the message send directly above to user with unique ID 1234567890... + # @twitter.message(:delete, message) + # Or the following can also be done... + # @twitter.message(:delete, message.id) + # + # In both scenarios (action is :post or + # :delete) a blessed Twitter::Message object is + # returned that represents the newly posted or newly deleted message. + # + # An ArgumentError will be raised if an invalid action + # is given. Valid actions are: + # * +:post+ + # * +:delete+ + # + # An ArgumentError is also raised when no user argument is + # supplied when action is +:post+. + def message(action, value, user = nil) + raise ArgumentError, "Invalid messaging action: #{action}" unless [:post, :delete].member?(action) + raise ArgumentError, "User argument must be supplied for :post case" if action.eql?(:post) and user.nil? + uri = @@MESSAGING_URIS[action] + user = user.to_i if user and user.is_a?(Twitter::User) + case action + when :post + response = http_connect({:text => value, :user => user, :source => @@config.source}.to_http_str) {|conn| create_http_post_request(uri) } + when :delete + response = http_connect {|conn| create_http_delete_request(uri, :id => value.to_i) } + end + message = Twitter::Message.unmarshal(response.body) + bless_model(message) + end +end diff --git a/lib/vendor/twitter/lib/twitter/client/profile.rb b/lib/vendor/twitter/lib/twitter/client/profile.rb new file mode 100644 index 0000000..bc47a88 --- /dev/null +++ b/lib/vendor/twitter/lib/twitter/client/profile.rb @@ -0,0 +1,29 @@ +class Twitter::Client + @@PROFILE_URIS = { + :info => '/account/update_profile', + :colors => '/account/update_profile_colors', + :device => '/account/update_delivery_device', + } + + # Provides access to the Twitter Profile API. + # + # You can update profile information. You can update the types of profile + # information: + # * :info (name, email, url, location, description) + # * :colors (background_color, text_color, link_color, sidebar_fill_color, + # sidebar_border_color) + # * :device (set device to either "sms", "im" or "none") + # + # Example: + # user = client.profile(:info, :location => "University Library") + # puts user.inspect + def profile(action, attributes) + connection = create_http_connection + connection.start do |connection| + response = http_connect(attributes.to_http_str) do |conn| + create_http_post_request(@@PROFILE_URIS[action]) + end + bless_models(Twitter::User.unmarshal(response.body)) + end + end +end diff --git a/lib/vendor/twitter/lib/twitter/client/search.rb b/lib/vendor/twitter/lib/twitter/client/search.rb new file mode 100644 index 0000000..59ab5c9 --- /dev/null +++ b/lib/vendor/twitter/lib/twitter/client/search.rb @@ -0,0 +1,27 @@ +class Twitter::Client + + @@SEARCH_URIS = { + :basic => "/search.json", + } + + # Provides access to Twitter's Search API. + # + # Example: + # # For keyword search + # iterator = @twitter.search(:q => "coworking") + # while (tweet = iterator.next) + # puts tweet.text + # end + # + # An ArgumentError will be raised if an invalid action + # is given. Valid actions are: + # * +:received+ + # * +:sent+ + def search(options = {}) +# raise ArgumentError, "Invalid messaging action: #{action}" + uri = @@SEARCH_URIS[:basic] + response = http_connect(nil, false, :search) {|conn| create_http_get_request(uri, options) } + json = JSON.parse(response.body) + bless_models(Twitter::Status.unmarshal(JSON.dump(json["results"]))) + end +end diff --git a/lib/vendor/twitter/lib/twitter/client/status.rb b/lib/vendor/twitter/lib/twitter/client/status.rb new file mode 100644 index 0000000..93227f7 --- /dev/null +++ b/lib/vendor/twitter/lib/twitter/client/status.rb @@ -0,0 +1,51 @@ +class Twitter::Client + @@STATUS_URIS = { + :get => '/statuses/show.json', + :post => '/statuses/update.json', + :delete => '/statuses/destroy.json', + :reply => '/statuses/update.json', + } + + # Provides access to individual statuses via Twitter's Status APIs + # + # action can be of the following values: + # * :get to retrieve status content. Assumes value given responds to :to_i message in meaningful way to yield intended status id. + # * :post to publish a new status + # * :delete to remove an existing status. Assumes value given responds to :to_i message in meaningful way to yield intended status id. + # * :reply to reply to an existing status. Assumes value given is Hash which contains :in_reply_to_status_id and :status + # + # value should be set to: + # * the status identifier for :get case + # * the status text message for :post case + # * none necessary for :delete case + # + # Examples: + # twitter.status(:get, 107786772) + # twitter.status(:post, "New Ruby open source project Twitter4R version 0.2.0 released.") + # twitter.status(:delete, 107790712) + # + # An ArgumentError will be raised if an invalid action + # is given. Valid actions are: + # * +:get+ + # * +:post+ + # * +:delete+ + def status(action, value = nil) + return self.timeline_for(action, value || {}) if :replies == action + raise ArgumentError, "Invalid status action: #{action}" unless @@STATUS_URIS.keys.member?(action) + return nil unless value + uri = @@STATUS_URIS[action] + response = nil + case action + when :get + response = http_connect {|conn| create_http_get_request(uri, :id => value.to_i) } + when :post + response = http_connect({:status => value, :source => @@config.source}.to_http_str) {|conn| create_http_post_request(uri) } + when :delete + response = http_connect {|conn| create_http_delete_request(uri, :id => value.to_i) } + when :reply + return nil if (!value.is_a?(Hash) || !value[:status] || !value[:in_reply_to_status_id]) + response = http_connect(value.merge(:source => @@config.source).to_http_str) {|conn| create_http_post_request(uri) } + end + bless_model(Twitter::Status.unmarshal(response.body)) + end +end diff --git a/lib/vendor/twitter/lib/twitter/client/timeline.rb b/lib/vendor/twitter/lib/twitter/client/timeline.rb new file mode 100644 index 0000000..354bb0c --- /dev/null +++ b/lib/vendor/twitter/lib/twitter/client/timeline.rb @@ -0,0 +1,72 @@ +class Twitter::Client + @@TIMELINE_URIS = { + :public => '/statuses/public_timeline.json', + :friends => '/statuses/friends_timeline.json', + :friend => '/statuses/friends_timeline.json', + :user => '/statuses/user_timeline.json', + :me => '/statuses/user_timeline.json', + :replies => '/statuses/replies.json', + } + + # Provides access to Twitter's Timeline APIs + # + # Returns timeline for given type. + # + # type can take the following values: + # * public + # * friends or friend + # * user or me + # + # :id is on key applicable to be defined in options: + # * the id or screen name (aka login) for :friends + # * the id or screen name (aka login) for :user + # * meaningless for the :me case, since twitter.timeline_for(:user, 'mylogin') and twitter.timeline_for(:me) are the same assuming 'mylogin' is the authenticated user's screen name (aka login). + # + # Examples: + # # returns the public statuses since status with id of 6543210 + # twitter.timeline_for(:public, id => 6543210) + # # returns the statuses for friend with user id 43210 + # twitter.timeline_for(:friend, :id => 43210) + # # returns the statuses for friend with screen name (aka login) of 'otherlogin' + # twitter.timeline_for(:friend, :id => 'otherlogin') + # # returns the statuses for user with screen name (aka login) of 'otherlogin' + # twitter.timeline_for(:user, :id => 'otherlogin') + # + # options can also include the following keys: + # * :id is the user ID, screen name of Twitter::User representation of a Twitter user. + # * :since is a Time object specifying the date-time from which to return results for. Applicable for the :friend, :friends, :user and :me cases. + # * :count specifies the number of statuses to retrieve. Only applicable for the :user case. + # * since_id is the status id of the public timeline from which to retrieve statuses for :public. Only applicable for the :public case. + # + # You can also pass this method a block, which will iterate through the results + # of the requested timeline and apply the block logic for each status returned. + # + # Example: + # twitter.timeline_for(:public) do |status| + # puts status.user.screen_name, status.text + # end + # + # twitter.timeline_for(:friend, :id => 'myfriend', :since => 30.minutes.ago) do |status| + # puts status.user.screen_name, status.text + # end + # + # timeline = twitter.timeline_for(:me) do |status| + # puts status.text + # end + # + # An ArgumentError will be raised if an invalid type + # is given. Valid types are: + # * +:public+ + # * +:friends+ + # * +:friend+ + # * +:user+ + # * +:me+ + def timeline_for(type, options = {}, &block) + raise ArgumentError, "Invalid timeline type: #{type}" unless @@TIMELINE_URIS.keys.member?(type) + uri = @@TIMELINE_URIS[type] + response = http_connect {|conn| create_http_get_request(uri, options) } + timeline = Twitter::Status.unmarshal(response.body) + timeline.each {|status| bless_model(status); yield status if block_given? } + timeline + end +end diff --git a/lib/vendor/twitter/lib/twitter/client/user.rb b/lib/vendor/twitter/lib/twitter/client/user.rb new file mode 100644 index 0000000..469e458 --- /dev/null +++ b/lib/vendor/twitter/lib/twitter/client/user.rb @@ -0,0 +1,65 @@ +class Twitter::Client + @@USER_URIS = { + :info => '/users/show', + :friends => '/statuses/friends.json', + :followers => '/statuses/followers.json', + } + + # Provides access to Twitter's User APIs + # + # Returns user instance for the id given. The id + # can either refer to the numeric user ID or the user's screen name. + # + # For example, + # @twitter.user(234943) #=> Twitter::User object instance for user with numeric id of 234943 + # @twitter.user('mylogin') #=> Twitter::User object instance for user with screen name 'mylogin' + # + # Where options is a +Hash+ of options that can include: + # * :page - optional. Retrieves the next set of friends. There are 100 friends per page. Default: 1. + # * :lite - optional. Prevents the inline inclusion of current status. Default: false. + # * :since - optional. Only relevant for :friends action. Narrows the results to just those friends added after the date given as value of this option. Must be HTTP-formatted date. + # + # An ArgumentError will be raised if an invalid action + # is given. Valid actions are: + # * +:info+ + # * +:friends+ + # + # +Note:+ You should not use this method to attempt to retrieve the + # authenticated user's followers. Please use any of the following + # ways of accessing this list: + # followers = client.my(:followers) + # OR + # followers = client.my(:info).followers + def user(id, action = :info, options = {}) + raise ArgumentError, "Invalid user action: #{action}" unless @@USER_URIS.keys.member?(action) + id = id.to_i if id.is_a?(Twitter::User) + params = options.merge(:id => id) + response = http_connect {|conn| create_http_get_request(@@USER_URIS[action], params) } + bless_models(Twitter::User.unmarshal(response.body)) + end + + # Syntactic sugar for queries relating to authenticated user in Twitter's User API + # + # Where action is one of the following: + # * :info - Returns user instance for the authenticated user. + # * :friends - Returns Array of users that are authenticated user's friends + # * :followers - Returns Array of users that are authenticated user's followers + # + # Where options is a +Hash+ of options that can include: + # * :page - optional. Retrieves the next set of friends. There are 100 friends per page. Default: 1. + # * :lite - optional. Prevents the inline inclusion of current status. Default: false. + # * :since - optional. Only relevant for :friends action. Narrows the results to just those friends added after the date given as value of this option. Must be HTTP-formatted date. + # + # An ArgumentError will be raised if an invalid action + # is given. Valid actions are: + # * +:info+ + # * +:friends+ + # * +:followers+ + def my(action, options = {}) + raise ArgumentError, "Invalid user action: #{action}" unless @@USER_URIS.keys.member?(action) + params = options.merge(:id => @login) + response = http_connect {|conn| create_http_get_request(@@USER_URIS[action], params) } + users = Twitter::User.unmarshal(response.body) + bless_models(users) + end +end diff --git a/lib/vendor/twitter/lib/twitter/config.rb b/lib/vendor/twitter/lib/twitter/config.rb new file mode 100644 index 0000000..034926b --- /dev/null +++ b/lib/vendor/twitter/lib/twitter/config.rb @@ -0,0 +1,77 @@ +# config.rb contains classes, methods and extends existing Twitter4R classes +# to provide easy configuration facilities. + +module Twitter + # Represents global configuration for Twitter::Client. + # Can override the following configuration options: + # * protocol - :http, :https or :ssl supported. :ssl is an alias for :https. Defaults to :ssl + # * host - hostname to connect to for the Twitter service. Defaults to 'twitter.com'. + # * port - port to connect to for the Twitter service. Defaults to 443. + # * proxy_host - proxy host to use. Defaults to nil. + # * proxy_port - proxy host to use. Defaults to nil. + # * proxy_user - proxy username to use. Defaults to nil. + # * proxy_pass - proxy password to use. Defaults to nil. + # * user_agent - user agent string to use for each request of the HTTP header. + # * application_name - name of your client application. Defaults to 'Twitter4R' + # * application_version - version of your client application. Defaults to current Twitter::Version.to_version. + # * application_url - URL of your client application. Defaults to http://twitter4r.rubyforge.org. + # * source - the source id given to you by Twitter to identify your application in their web interface. Note: you must contact Twitter.com developer directly so they can configure their servers appropriately. + class Config + include ClassUtilMixin + @@ATTRIBUTES = [ + :protocol, + :host, + :port, + :search_protocol, + :search_host, + :search_port, + :proxy_host, + :proxy_port, + :proxy_user, + :proxy_pass, + :user_agent, + :application_name, + :application_version, + :application_url, + :source, + ] + attr_accessor *@@ATTRIBUTES + + # Override of Object#eql? to ensure RSpec specifications run + # correctly. Also done to follow Ruby best practices. + def eql?(other) + return true if self == other + @@ATTRIBUTES.each do |att| + return false unless self.send(att).eql?(other.send(att)) + end + true + end + end + + class Client + @@defaults = { :host => 'twitter.com', + :port => 443, + :protocol => :ssl, + :search_host => 'search.twitter.com', + :search_port => 80, + :search_protocol => :http, + :proxy_host => nil, + :proxy_port => nil, + :user_agent => "default", + :application_name => 'Twitter4R', + :application_version => Twitter::Version.to_version, + :application_url => 'http://twitter4r.rubyforge.org', + :source => 'twitter4r', + } + @@config = Twitter::Config.new(@@defaults) + + # Twitter::Client class methods + class << self + # Yields to given block to configure the Twitter4R API. + def configure(&block) + raise ArgumentError, "Block must be provided to configure" unless block_given? + yield @@config + end # configure + end # class << self + end # Client class +end # Twitter module diff --git a/lib/vendor/twitter/lib/twitter/console.rb b/lib/vendor/twitter/lib/twitter/console.rb new file mode 100644 index 0000000..a95b159 --- /dev/null +++ b/lib/vendor/twitter/lib/twitter/console.rb @@ -0,0 +1,31 @@ +# Contains hooks for the twitter console + +require('optparse') + +module Twitter + class Client + class << self + # Helper method mostly for irb shell prototyping. + # + # Reads in login/password Twitter credentials from YAML file + # found at the location given by config_file that has + # the following format: + # envname: + # login: mytwitterlogin + # password: mytwitterpassword + # + # Where envname is the name of the environment like 'test', + # 'dev' or 'prod'. The env argument defaults to 'test'. + # + # To use this in the shell you would do something like the following + # examples: + # twitter = Twitter::Client.from_config('config/twitter.yml', 'dev') + # twitter = Twitter::Client.from_config('config/twitter.yml') + def from_config(config_file, env = 'test') + yaml_hash = YAML.load(File.read(config_file)) + self.new yaml_hash[env] + end + end # class << self + end +end + diff --git a/lib/vendor/twitter/lib/twitter/core.rb b/lib/vendor/twitter/lib/twitter/core.rb new file mode 100644 index 0000000..530f724 --- /dev/null +++ b/lib/vendor/twitter/lib/twitter/core.rb @@ -0,0 +1,137 @@ +# The Twitter4R API provides a nicer Ruby object API to work with +# instead of coding around the REST API. + +# Module to encapsule the Twitter4R API. +module Twitter + # Mixin module for classes that need to have a constructor similar to + # Rails' models, where a Hash is provided to set attributes + # appropriately. + # + # To define a class that uses this mixin, use the following code: + # class FilmActor + # include ClassUtilMixin + # end + module ClassUtilMixin #:nodoc: + def self.included(base) #:nodoc: + base.send(:include, InstanceMethods) + end + + # Instance methods defined for Twitter::ModelMixin module. + module InstanceMethods #:nodoc: + # Constructor/initializer that takes a hash of parameters that + # will initialize *members* or instance attributes to the + # values given. For example, + # + # class FilmActor + # include Twitter::ClassUtilMixin + # attr_accessor :name + # end + # + # class Production + # include Twitter::ClassUtilMixin + # attr_accessor :title, :year, :actors + # end + # + # # Favorite actress... + # jodhi = FilmActor.new(:name => "Jodhi May") + # jodhi.name # => "Jodhi May" + # + # # Favorite actor... + # robert = FilmActor.new(:name => "Robert Lindsay") + # robert.name # => "Robert Lindsay" + # + # # Jane is also an excellent pick...gotta love her accent! + # jane = FilmActor.new(name => "Jane Horrocks") + # jane.name # => "Jane Horrocks" + # + # # Witty BBC series... + # mrs_pritchard = Production.new(:title => "The Amazing Mrs. Pritchard", + # :year => 2005, + # :actors => [jodhi, jane]) + # mrs_pritchard.title # => "The Amazing Mrs. Pritchard" + # mrs_pritchard.year # => 2005 + # mrs_pritchard.actors # => [#, + # ] + # # Any Ros Pritchard's out there to save us from the Tony Blair + # # and Gordon Brown *New Labour* debacle? You've got my vote! + # + # jericho = Production.new(:title => "Jericho", + # :year => 2005, + # :actors => [robert]) + # jericho.title # => "Jericho" + # jericho.year # => 2005 + # jericho.actors # => [#] + # + # Assuming class FilmActor includes + # Twitter::ClassUtilMixin in the class definition + # and has an attribute of name, then that instance + # attribute will be set to "Jodhi May" for the actress + # object during object initialization (aka construction for + # you Java heads). + def initialize(params = {}) + params.each do |key,val| + self.send("#{key}=", val) if self.respond_to? key + end + self.send(:init) if self.respond_to? :init + end + + protected + # Helper method to provide an easy and terse way to require + # a block is provided to a method. + def require_block(block_given) + raise ArgumentError, "Must provide a block" unless block_given + end + end + end # ClassUtilMixin + + # Exception subclass raised when there is an error encountered upon + # querying or posting to the remote Twitter REST API. + # + # To consume and query any RESTError raised by Twitter4R: + # begin + # # Do something with your instance of Twitter::Client. + # # Maybe something like: + # timeline = twitter.timeline_for(:public) + # rescue RESTError => re + # puts re.code, re.message, re.uri + # end + # Which on the code raising a RESTError will output something like: + # 404 + # Resource Not Found + # /i_am_crap.json + class RESTError < RuntimeError + include ClassUtilMixin + @@ATTRIBUTES = [:code, :message, :uri] + attr_accessor :code, :message, :uri + + # Returns string in following format: + # "HTTP #{@code}: #{@message} at #{@uri}" + # For example, + # "HTTP 404: Resource Not Found at /i_am_crap.json" + def to_s + "HTTP #{@code}: #{@message} at #{@uri}" + end + end # RESTError + + # Remote REST API interface representation + # + class RESTInterfaceSpec + include ClassUtilMixin + + end + + # Remote REST API method representation + # + class RESTMethodSpec + include ClassUtilMixin + attr_accessor :uri, :method, :parameters + end + + # Remote REST API method parameter representation + # + class RESTParameterSpec + include ClassUtilMixin + attr_accessor :name, :type, :required + def required?; @required; end + end +end diff --git a/lib/vendor/twitter/lib/twitter/ext.rb b/lib/vendor/twitter/lib/twitter/ext.rb new file mode 100644 index 0000000..92edbd1 --- /dev/null +++ b/lib/vendor/twitter/lib/twitter/ext.rb @@ -0,0 +1,2 @@ + +require_local('twitter/ext/stdlib') diff --git a/lib/vendor/twitter/lib/twitter/ext/stdlib.rb b/lib/vendor/twitter/lib/twitter/ext/stdlib.rb new file mode 100644 index 0000000..9b1fd1e --- /dev/null +++ b/lib/vendor/twitter/lib/twitter/ext/stdlib.rb @@ -0,0 +1,52 @@ +# Contains Ruby standard library extensions specific to Twitter4R library. + +# Extension to Hash to create URL encoded string from key-values +class Hash + # Returns string formatted for HTTP URL encoded name-value pairs. + # For example, + # {:id => 'thomas_hardy'}.to_http_str + # # => "id=thomas_hardy" + # {:id => 23423, :since => Time.now}.to_http_str + # # => "since=Thu,%2021%20Jun%202007%2012:10:05%20-0500&id=23423" + def to_http_str + result = '' + return result if self.empty? + self.each do |key, val| + result << "#{key}=#{CGI.escape(val.to_s)}&" + end + result.chop # remove the last '&' character, since it can be discarded + end +end + +# Extension to Time that outputs RFC2822 compliant string on #to_s +class Time + alias :old_to_s :to_s + + # Returns RFC2822 compliant string for Time object. + # For example, + # # Tony Blair's last day in office (hopefully) + # best_day_ever = Time.local(2007, 6, 27) + # best_day_ever.to_s # => "Wed, 27 Jun 2007 00:00:00 +0100" + # You can also pass in an option format argument that + # corresponds to acceptable values according to ActiveSupport's + # +Time#to_formatted_s+ method. + def to_s(format = nil) + format ? self.to_formatted_s(format) : self.rfc2822 + end +end + +# Extension to Kernel to add #gem_present? without any exceptions raised +module Kernel + + # Returns whether or not a gem exists without raising a Gem::LoadError exception + def gem_present?(gem_name, version = nil) + present = false + begin + present = !!(version ? gem(gem_name, version) : gem(gem_name)) + rescue Gem::LoadError => le + present = false + warn("Gem load error: Couldn't load #{gem_name} #{version ? "with version requirement #{version}: #{le.to_s}": ""}") + end + present + end +end diff --git a/lib/vendor/twitter/lib/twitter/extras.rb b/lib/vendor/twitter/lib/twitter/extras.rb new file mode 100644 index 0000000..f38e2e8 --- /dev/null +++ b/lib/vendor/twitter/lib/twitter/extras.rb @@ -0,0 +1,39 @@ +# extra.rb contains features that are not considered part of the core library. +# This file is not imported by doing require('twitter'), so you will +# need to import this file separately like: +# require('twitter') +# require('twitter/extras') + +require('twitter') + +class Twitter::Client + @@FEATURED_URIS = { + :users => 'http://twitter.com/statuses/featured.json' + } + + # Provides access to the Featured Twitter API. + # + # Currently the only value for type accepted is :users, + # which will return an Array of blessed Twitter::User objects that + # represent Twitter's featured users. + def featured(type) + uri = @@FEATURED_URIS[type] + response = http_connect {|conn| create_http_get_request(uri) } + bless_models(Twitter::User.unmarshal(response.body)) + end +end + +class Twitter::User + class << self + # Provides access to the Featured Twitter API via the Twitter4R Model + # interface. + # + # The following lines of code are equivalent to each other: + # users1 = Twitter::User.features(client) + # users2 = client.featured(:users) + # where users1 and users2 would be logically equivalent. + def featured(client) + client.featured(:users) + end + end +end diff --git a/lib/vendor/twitter/lib/twitter/meta.rb b/lib/vendor/twitter/lib/twitter/meta.rb new file mode 100644 index 0000000..69dfa87 --- /dev/null +++ b/lib/vendor/twitter/lib/twitter/meta.rb @@ -0,0 +1,56 @@ +# meta.rb contains Twitter::Meta and related classes that +# help define the metadata of the Twitter4R project. + +require('rubygems') +require('erb') + +class Twitter::Meta #:nodoc: + attr_accessor :root_dir + attr_reader :gem_spec, :project_files, :spec_files + + # Initializer for Twitter::Meta class. Takes root_dir as parameter. + def initialize(root_dir) + @root_dir = root_dir + end + + # Returns package information defined in root_dir/pkg-info.yml + def pkg_info + yaml_file = File.join(@root_dir, 'pkg-info.yml') + ryaml = ERB.new(File.read(yaml_file), 0) + s = ryaml.result(binding) + YAML.load(s) + end + + # Returns RubyGems spec information + def spec_info + self.pkg_info['spec'] if self.pkg_info + end + + # Returns list of project files + def project_files + @project_files ||= Dir.glob(File.join(@root_dir, 'lib/**/*.rb')) + @project_files + end + + # Returns list of specification files + def spec_files + @spec_files ||= Dir.glob(File.join(@root_dir, 'spec/**/*_spec.rb')) + @spec_files + end + + # Returns RubyGem specification for Twitter4R project + def gem_spec + @gem_spec ||= Gem::Specification.new do |spec| + self.spec_info.each do |key, val| + if val.is_a?(Hash) + val.each do |k, v| + spec.send(key, k, v) + end + else + spec.send("#{key}=", val) + end + end + end + @gem_spec + end +end diff --git a/lib/vendor/twitter/lib/twitter/model.rb b/lib/vendor/twitter/lib/twitter/model.rb new file mode 100644 index 0000000..b8f0d7d --- /dev/null +++ b/lib/vendor/twitter/lib/twitter/model.rb @@ -0,0 +1,360 @@ +# Contains Twitter4R Model API. + +module Twitter + # Mixin module for model classes. Includes generic class methods like + # unmarshal. + # + # To create a new model that includes this mixin's features simply: + # class NewModel + # include Twitter::ModelMixin + # end + # + # This mixin module automatically includes Twitter::ClassUtilMixin + # features. + # + # The contract for models to use this mixin correctly is that the class + # including this mixin must provide an class method named attributes + # that will return an Array of attribute symbols that will be checked + # in #eql? override method. The following would be sufficient: + # def self.attributes; @@ATTRIBUTES; end + module ModelMixin #:nodoc: + def self.included(base) #:nodoc: + base.send(:include, Twitter::ClassUtilMixin) + base.send(:include, InstanceMethods) + base.extend(ClassMethods) + end + + # Class methods defined for Twitter::ModelMixin module. + module ClassMethods #:nodoc: + # Unmarshal object singular or plural array of model objects + # from JSON serialization. Currently JSON is only supported + # since this is all Twitter4R needs. + def unmarshal(raw) + input = JSON.parse(raw) + def unmarshal_model(hash) + self.new(hash) + end + return unmarshal_model(input) if input.is_a?(Hash) # singular case + result = [] + input.each do |hash| + model = unmarshal_model(hash) if hash.is_a?(Hash) + result << model + end if input.is_a?(Array) + result # plural case + end + end + + # Instance methods defined for Twitter::ModelMixin module. + module InstanceMethods #:nodoc: + attr_accessor :client + # Equality method override of Object#eql? default. + # + # Relies on the class using this mixin to provide a attributes + # class method that will return an Array of attributes to check are + # equivalent in this #eql? override. + # + # It is by design that the #eql? method will raise a NoMethodError + # if no attributes class method exists, to alert you that + # you must provide it for a meaningful result from this #eql? override. + # Otherwise this will return a meaningless result. + def eql?(other) + attrs = self.class.attributes + attrs.each do |att| + return false unless self.send(att).eql?(other.send(att)) + end + true + end + + # Returns integer representation of model object instance. + # + # For example, + # status = Twitter::Status.new(:id => 234343) + # status.to_i #=> 234343 + def to_i + @id + end + + # Returns string representation of model object instance. + # + # For example, + # status = Twitter::Status.new(:text => 'my status message') + # status.to_s #=> 'my status message' + # + # If a model class doesn't have a @text attribute defined + # the default Object#to_s will be returned as the result. + def to_s + self.respond_to?(:text) ? @text : super.to_s + end + + # Returns hash representation of model object instance. + # + # For example, + # u = Twitter::User.new(:id => 2342342, :screen_name => 'tony_blair_is_the_devil') + # u.to_hash #=> {:id => 2342342, :screen_name => 'tony_blair_is_the_devil'} + # + # This method also requires that the class method attributes be + # defined to return an Array of attributes for the class. + def to_hash + attrs = self.class.attributes + result = {} + attrs.each do |att| + value = self.send(att) + value = value.to_hash if value.respond_to?(:to_hash) + result[att] = value if value + end + result + end + + # "Blesses" model object. + # + # Should be overridden by model class if special behavior is expected + # + # Expected to return blessed object (usually self) + def bless(client) + self.basic_bless(client) + end + + protected + # Basic "blessing" of model object + def basic_bless(client) + self.client = client + self + end + end + end + + module AuthenticatedUserMixin + def self.included(base) + base.send(:include, InstanceMethods) + end + + module InstanceMethods + # Returns an Array of user objects that represents the authenticated + # user's friends on Twitter. + def followers(options = {}) + @client.my(:followers, options) + end + + # Adds given user as a friend. Returns user object as given by + # Twitter REST server response. + # + # For user argument you may pass in the unique integer + # user ID, screen name or Twitter::User object representation. + def befriend(user) + @client.friend(:add, user) + end + + # Removes given user as a friend. Returns user object as given by + # Twitter REST server response. + # + # For user argument you may pass in the unique integer + # user ID, screen name or Twitter::User object representation. + def defriend(user) + @client.friend(:remove, user) + end + end + end + + # Represents a Twitter user + class User + include ModelMixin + @@ATTRIBUTES = [:id, :name, :description, :location, :screen_name, :url, + :protected, :profile_image_url, :profile_background_color, + :profile_text_color, :profile_link_color, :profile_sidebar_fill_color, + :profile_sidebar_border_color, :profile_background_image_url, + :profile_background_tile, :utc_offset, :time_zone, + :following, :notifications, :favourites_count, :followers_count, + :friends_count, :statuses_count, :created_at, ] + attr_accessor *@@ATTRIBUTES + + class << self + # Used as factory method callback + def attributes; @@ATTRIBUTES; end + + # Returns user model object with given id using the configuration + # and credentials of the client object passed in. + # + # You can pass in either the user's unique integer ID or the user's + # screen name. + def find(id, client) + client.user(id) + end + end + + # Override of ModelMixin#bless method. + # + # Adds #followers instance method when user object represents + # authenticated user. Otherwise just do basic bless. + # + # This permits applications using Twitter4R to write + # Rubyish code like this: + # followers = user.followers if user.is_me? + # Or: + # followers = user.followers if user.respond_to?(:followers) + def bless(client) + basic_bless(client) + self.instance_eval(%{ + self.class.send(:include, Twitter::AuthenticatedUserMixin) + }) if self.is_me? and not self.respond_to?(:followers) + self + end + + # Returns whether this Twitter::User model object + # represents the authenticated user of the client + # that blessed it. + def is_me? + # TODO: Determine whether we should cache this or not? + # Might be dangerous to do so, but do we want to support + # the edge case where this would cause a problem? i.e. + # changing authenticated user after initial use of + # authenticated API. + # TBD: To cache or not to cache. That is the question! + # Since this is an implementation detail we can leave this for + # subsequent 0.2.x releases. It doesn't have to be decided before + # the 0.2.0 launch. + @screen_name == @client.instance_eval("@login") + end + + # Returns an Array of user objects that represents the authenticated + # user's friends on Twitter. + def friends + @client.user(@id, :friends) + end + end # User + + # Represents a status posted to Twitter by a Twitter user. + class Status + include ModelMixin + @@ATTRIBUTES = [:id, :text, :source, :truncated, :created_at, :user, + :favorited, :in_reply_to_status_id, :in_reply_to_user_id, + :in_reply_to_screen_name] + attr_accessor *@@ATTRIBUTES + + class << self + # Used as factory method callback + def attributes; @@ATTRIBUTES; end + + # Returns status model object with given status using the + # configuration and credentials of the client object passed in. + def find(id, client) + client.status(:get, id) + end + + # Creates a new status for the authenticated user of the given + # client context. + # + # You MUST include a valid/authenticated client context + # in the given params argument. + # + # For example: + # status = Twitter::Status.create( + # :text => 'I am shopping for flip flops', + # :client => client) + # + # An ArgumentError will be raised if no valid client context + # is given in the params Hash. For example, + # status = Twitter::Status.create(:text => 'I am shopping for flip flops') + # The above line of code will raise an ArgumentError. + # + # The same is true when you do not provide a :text key-value + # pair in the params argument given. + # + # The Twitter::Status object returned after the status successfully + # updates on the Twitter server side is returned from this method. + def create(params) + client, text = params[:client], params[:text] + raise ArgumentError, 'Valid client context must be provided' unless client.is_a?(Twitter::Client) + raise ArgumentError, 'Must provide text for the status to update' unless text.is_a?(String) + client.status(:post, text) + end + end + + def reply? + !!@in_reply_to_status_id + end + + def reply(status) + client.status(:reply, :status => status, :in_reply_to_status_id => @id) + end + + protected + # Constructor callback + def init + @user = User.new(@user) if @user.is_a?(Hash) + @created_at = Time.parse(@created_at) if @created_at.is_a?(String) + end + end # Status + + # Represents a direct message on Twitter between Twitter users. + class Message + include ModelMixin + @@ATTRIBUTES = [:id, :recipient, :sender, :text, :created_at] + attr_accessor *@@ATTRIBUTES + + class << self + # Used as factory method callback + def attributes; @@ATTRIBUTES; end + + # Raises NotImplementedError because currently + # Twitter doesn't provide a facility to retrieve + # one message by unique ID. + def find(id, client) + raise NotImplementedError, 'Twitter has yet to implement a REST API for this. This is not a Twitter4R library limitation.' + end + + # Creates a new direct message from the authenticated user of the + # given client context. + # + # You MUST include a valid/authenticated client context + # in the given params argument. + # + # For example: + # status = Twitter::Message.create( + # :text => 'I am shopping for flip flops', + # :receipient => 'anotherlogin', + # :client => client) + # + # An ArgumentError will be raised if no valid client context + # is given in the params Hash. For example, + # status = Twitter::Status.create(:text => 'I am shopping for flip flops') + # The above line of code will raise an ArgumentError. + # + # The same is true when you do not provide any of the following + # key-value pairs in the params argument given: + # * text - the String that will be the message text to send to user + # * recipient - the user ID, screen_name or Twitter::User object representation of the recipient of the direct message + # + # The Twitter::Message object returned after the direct message is + # successfully sent on the Twitter server side is returned from + # this method. + def create(params) + client, text, recipient = params[:client], params[:text], params[:recipient] + raise ArgumentError, 'Valid client context must be given' unless client.is_a?(Twitter::Client) + raise ArgumentError, 'Message text must be supplied to send direct message' unless text.is_a?(String) + raise ArgumentError, 'Recipient user must be specified to send direct message' unless [Twitter::User, Integer, String].member?(recipient.class) + client.message(:post, text, recipient) + end + end + + protected + # Constructor callback + def init + @sender = User.new(@sender) if @sender.is_a?(Hash) + @recipient = User.new(@recipient) if @recipient.is_a?(Hash) + @created_at = Time.parse(@created_at) if @created_at.is_a?(String) + end + end # Message + + # RateLimitStatus provides information about how many requests you have left + # and when you can resume more requests if your remaining_hits count is zero. + class RateLimitStatus + include ModelMixin + @@ATTRIBUTES = [:remaining_hits, :hourly_limit, :reset_time_in_seconds, :reset_time] + attr_accessor *@@ATTRIBUTES + + class << self + # Used as factory method callback + def attributes; @@ATTRIBUTES; end + end + end +end # Twitter diff --git a/lib/vendor/twitter/lib/twitter/version.rb b/lib/vendor/twitter/lib/twitter/version.rb new file mode 100644 index 0000000..fa97832 --- /dev/null +++ b/lib/vendor/twitter/lib/twitter/version.rb @@ -0,0 +1,19 @@ +# version.rb contains Twitter::Version that provides helper +# methods related to versioning of the Twitter4R project. + +module Twitter::Version #:nodoc: + MAJOR = 0 + MINOR = 3 + REVISION = 1 + class << self + # Returns X.Y.Z formatted version string + def to_version + "#{MAJOR}.#{MINOR}.#{REVISION}" + end + + # Returns X-Y-Z formatted version name + def to_name + "#{MAJOR}_#{MINOR}_#{REVISION}" + end + end +end diff --git a/lib/vendor/twitter/spec/twitter/client/account_spec.rb b/lib/vendor/twitter/spec/twitter/client/account_spec.rb new file mode 100644 index 0000000..bc8686d --- /dev/null +++ b/lib/vendor/twitter/spec/twitter/client/account_spec.rb @@ -0,0 +1,28 @@ +require File.join(File.dirname(__FILE__), '..', '..', 'spec_helper') + +describe Twitter::Client, "#account_info" do + before(:each) do + @uri = Twitter::Client.class_eval("@@ACCOUNT_URIS[:rate_limit_status]") + @request = mas_net_http_get(:basic_auth => nil) + @twitter = client_context + @default_header = @twitter.send(:http_header) + @response = mas_net_http_response(:success) + @connection = mas_net_http(@response) + @response.stub!(:body).and_return("{}") + Net::HTTP.stub!(:new).and_return(@connection) + @rate_limit_status = mock(Twitter::RateLimitStatus) + @twitter.stub!(:bless_models).and_return({}) + end + + it "should create expected HTTP GET request" do + @twitter.should_receive(:create_http_get_request).with(@uri).and_return(@request) + @twitter.account_info + end + + it "should raise Twitter::RESTError when 500 HTTP response received when giving page options" do + @connection = mas_net_http(mas_net_http_response(:server_error)) + lambda { + @twitter.account_info + }.should raise_error(Twitter::RESTError) + end +end diff --git a/lib/vendor/twitter/spec/twitter/client/auth_spec.rb b/lib/vendor/twitter/spec/twitter/client/auth_spec.rb new file mode 100644 index 0000000..da42b6d --- /dev/null +++ b/lib/vendor/twitter/spec/twitter/client/auth_spec.rb @@ -0,0 +1,34 @@ +require File.join(File.dirname(__FILE__), '..', '..', 'spec_helper') + +describe Twitter::Client, "#authenticate?" do + before(:each) do + @uri = '/account/verify_credentials.json' + @request = mas_net_http_get(:basic_auth => nil) + @twitter = client_context + @default_header = @twitter.send(:http_header) + @response = mas_net_http_response(:success) + @error_response = mas_net_http_response(404, "Resource Not Found") + @connection = mas_net_http(@response) + Net::HTTP.stub!(:new).and_return(@connection) + @login = "applestillsucks" + @password = "linuxstillrocks" + end + + it "creates expected HTTP GET request" do + @twitter.should_receive(:create_http_get_request).with(@uri).and_return(@request) + @twitter.authenticate?(@login, @password) + end + + it "should return true if HTTP response is 20X" do + @twitter.authenticate?(@login, @password).should be(true) + end + + it "should return false if HTTP response is not 20X" do + Net::HTTP.stub!(:new).and_return(mas_net_http(@error_response)) + @twitter.authenticate?(@login, @password).should be(false) + end + + after(:each) do + nilize(@uri, @request, @twitter, @default_header, @response, @error_response, @connection, @login, @password) + end +end diff --git a/lib/vendor/twitter/spec/twitter/client/base_spec.rb b/lib/vendor/twitter/spec/twitter/client/base_spec.rb new file mode 100644 index 0000000..c36315f --- /dev/null +++ b/lib/vendor/twitter/spec/twitter/client/base_spec.rb @@ -0,0 +1,242 @@ +require File.join(File.dirname(__FILE__), '..', '..', 'spec_helper') + +describe "Twitter::Client" do + before(:each) do + @init_hash = { :login => 'user', :password => 'pass' } + end + + it ".new should accept login and password as initializer hash keys and set the values to instance values" do + client = nil + lambda do + client = Twitter::Client.new(@init_hash) + end.should_not raise_error + client.send(:login).should eql(@init_hash[:login]) + client.send(:password).should eql(@init_hash[:password]) + end +end + +describe Twitter::Client, "#inspect" do + before(:each) do + @client = Twitter::Client.new(:login => "NippleEquality", :password => "3rdnipple") + end + + it "should block out password attribute values" do + @client.inspect.should_not match(/@password="3rdnipple"/) + end +end + +describe Twitter::Client, "#http_header" do + before(:each) do + @user_agent = 'myapp' + @application_name = @user_agent + @application_version = '1.2.3' + @application_url = 'http://myapp.url' + Twitter::Client.configure do |conf| + conf.user_agent = @user_agent + conf.application_name = @application_name + conf.application_version = @application_version + conf.application_url = @application_url + end + @expected_headers = { + 'Accept' => 'text/x-json', + 'X-Twitter-Client' => @application_name, + 'X-Twitter-Client-Version' => @application_version, + 'X-Twitter-Client-URL' => @application_url, + 'User-Agent' => "Twitter4R v#{Twitter::Version.to_version} [#{@user_agent}]", + } + @twitter = client_context + # reset @@http_header class variable in Twitter::Client class + Twitter::Client.class_eval("@@http_header = nil") + end + + it "should always return expected HTTP headers" do + headers = @twitter.send(:http_header) + headers.should === @expected_headers + end + + it "should cache HTTP headers Hash in class variable after first invocation" do + cache = Twitter::Client.class_eval("@@http_header") + cache.should be_nil + @twitter.send(:http_header) + cache = Twitter::Client.class_eval("@@http_header") + cache.should_not be_nil + cache.should === @expected_headers + end + + after(:each) do + nilize(@user_agent, @application_name, @application_version, @application_url, @twitter, @expected_headers) + end +end + +describe Twitter::Client, "#create_http_get_request" do + before(:each) do + @uri = '/some/path' + @expected_get_request = mock(Net::HTTP::Get) + @twitter = client_context + @default_header = @twitter.send(:http_header) + end + + it "should create new Net::HTTP::Get object with expected initialization arguments" do + Net::HTTP::Get.should_receive(:new).with(@uri, @default_header).and_return(@expected_get_request) + @twitter.send(:create_http_get_request, @uri) + end + + after(:each) do + nilize(@twitter, @uri, @expected_get_request, @default_header) + end +end + +describe Twitter::Client, "#create_http_post_request" do + before(:each) do + @uri = '/some/path' + @expected_post_request = mock(Net::HTTP::Post) + @twitter = client_context + @default_header = @twitter.send(:http_header) + end + + it "should create new Net::HTTP::Post object with expected initialization arguments" do + Net::HTTP::Post.should_receive(:new).with(@uri, @default_header).and_return(@expected_post_request) + @twitter.send(:create_http_post_request, @uri) + end + + after(:each) do + nilize(@twitter, @uri, @expected_post_request, @default_header) + end +end + +describe Twitter::Client, "#create_http_delete_request" do + before(:each) do + @uri = '/a/stupid/path/that/is/not/restful/since/twitter.com/cannot/do/consistent/restful/apis' + @expected_delete_request = mock(Net::HTTP::Delete) + @twitter = client_context + @default_header = @twitter.send(:http_header) + end + + it "should create new Net::HTTP::Delete object with expected initialization arguments" do + Net::HTTP::Delete.should_receive(:new).with(@uri, @default_header).and_return(@expected_delete_request) + @twitter.send(:create_http_delete_request, @uri) + end + + after(:each) do + nilize(@twitter, @uri, @expected_delete_request, @default_header) + end +end + +describe Twitter::Client, "#http_connect" do + before(:each) do + @request = mas_net_http_get(:basic_auth => nil) + @good_response = mas_net_http_response(:success) + @bad_response = mas_net_http_response(:server_error) + @http_stubs = {:is_a? => true} + @block = Proc.new do |conn| + conn.is_a?(Net::HTTP).should be(true) + @has_yielded = true + @request + end + @twitter = client_context + @has_yielded = false + end + + def generate_bad_response + @http = mas_net_http(@bad_response, @http_stubs) + Net::HTTP.stub!(:new).and_return(@http) + end + + def generate_good_response + @http = mas_net_http(@good_response, @http_stubs) + Net::HTTP.stub!(:new).and_return(@http) + end + + it "should yield HTTP connection when response is good" do + generate_good_response + @http.should_receive(:is_a?).with(Net::HTTP).and_return(true) + lambda do + @twitter.send(:http_connect, &@block) + end.should_not raise_error + @has_yielded.should be(true) + end + + it "should yield HTTP connection when response is bad" do + generate_bad_response + @http.should_receive(:is_a?).with(Net::HTTP).and_return(true) + lambda { + @twitter.send(:http_connect, &@block) + }.should raise_error(Twitter::RESTError) + @has_yielded.should be(true) + end + + after(:each) do + nilize(@good_response, @bad_response, @http) + end +end + +describe Twitter::Client, "#bless_model" do + before(:each) do + @twitter = client_context + @model = Twitter::User.new + end + + it "should recieve #client= message on given model to self" do + @model.should_receive(:client=).with(@twitter) + model = @twitter.send(:bless_model, @model) + end + + it "should set client attribute on given model to self" do + model = @twitter.send(:bless_model, @model) + model.client.should eql(@twitter) + end + + # if model is nil, it doesn't not necessarily signify an exceptional case for this method's usage. + it "should return nil when receiving nil and not raise any exceptions" do + model = @twitter.send(:bless_model, nil) + model.should be_nil + end + + # needed to alert developer that the model needs to respond to #client= messages appropriately. + it "should raise an error if passing in a non-nil object that doesn't not respond to the :client= message" do + lambda { + @twitter.send(:bless_model, Object.new) + }.should raise_error(NoMethodError) + end + + after(:each) do + nilize(@twitter) + end +end + +describe Twitter::Client, "#bless_models" do + before(:each) do + @twitter = client_context + @models = [ + Twitter::Status.new(:text => 'message #1'), + Twitter::Status.new(:text => 'message #2'), + ] + end + + it "should set client attributes for each model in given Array to self" do + models = @twitter.send(:bless_models, @models) + models.each {|model| model.client.should eql(@twitter) } + end + + it "should set client attribute for singular model given to self" do + model = @twitter.send(:bless_models, @models[0]) + model.client.should eql(@twitter) + end + + it "should delegate to bless_model for singular model case" do + model = @models[0] + @twitter.should_receive(:bless_model).with(model).and_return(model) + @twitter.send(:bless_models, model) + end + + it "should return nil when receiving nil and not raise any exceptions" do + lambda { + value = @twitter.send(:bless_models, nil) + value.should be_nil + }.should_not raise_error + end + + after(:each) do + nilize(@twitter, @models) + end +end diff --git a/lib/vendor/twitter/spec/twitter/client/blocks_spec.rb b/lib/vendor/twitter/spec/twitter/client/blocks_spec.rb new file mode 100644 index 0000000..87c81d5 --- /dev/null +++ b/lib/vendor/twitter/spec/twitter/client/blocks_spec.rb @@ -0,0 +1,76 @@ +require File.join(File.dirname(__FILE__), '..', '..', 'spec_helper') + +describe Twitter::Client, "#block" do + before(:each) do + @twitter = client_context + @id = 1234567 + @screen_name = 'dummylogin' + @friend = Twitter::User.new(:id => @id, :screen_name => @screen_name) + @uris = Twitter::Client.class_eval("@@BLOCK_URIS") + @request = mas_net_http_get(:basic_auth => nil) + @response = mas_net_http_response(:success) + @connection = mas_net_http(@response) + Net::HTTP.stub!(:new).and_return(@connection) + Twitter::User.stub!(:unmarshal).and_return(@friend) + end + + def create_uri(action, id) + "#{@uris[action]}/#{id}.json" + end + + it "should create expected HTTP GET request for :add case using integer user ID" do + # the integer user ID scenario... + @twitter.should_receive(:create_http_get_request).with(create_uri(:add, @id)).and_return(@request) + @twitter.block(:add, @id) + end + + it "should create expected HTTP GET request for :add case using screen name" do + # the screen name scenario... + @twitter.should_receive(:create_http_get_request).with(create_uri(:add, @screen_name)).and_return(@request) + @twitter.block(:add, @screen_name) + end + + it "should create expected HTTP GET request for :add case using Twitter::User object" do + # the Twitter::User object scenario... + @twitter.should_receive(:create_http_get_request).with(create_uri(:add, @friend.to_i)).and_return(@request) + @twitter.block(:add, @friend) + end + + it "should create expected HTTP GET request for :remove case using integer user ID" do + # the integer user ID scenario... + @twitter.should_receive(:create_http_get_request).with(create_uri(:remove, @id)).and_return(@request) + @twitter.block(:remove, @id) + end + + it "should create expected HTTP GET request for :remove case using screen name" do + # the screen name scenario... + @twitter.should_receive(:create_http_get_request).with(create_uri(:remove, @screen_name)).and_return(@request) + @twitter.block(:remove, @screen_name) + end + + it "should create expected HTTP GET request for :remove case using Twitter::User object" do + # the Twitter::User object scenario... + @twitter.should_receive(:create_http_get_request).with(create_uri(:remove, @friend.to_i)).and_return(@request) + @twitter.block(:remove, @friend) + end + + it "should bless user model returned for :add case" do + @twitter.should_receive(:bless_model).with(@friend) + @twitter.block(:add, @friend) + end + + it "should bless user model returned for :remove case" do + @twitter.should_receive(:bless_model).with(@friend) + @twitter.block(:remove, @friend) + end + + it "should raise ArgumentError if action given is not valid" do + lambda { + @twitter.block(:crap, @friend) + }.should raise_error(ArgumentError) + end + + after(:each) do + nilize(@twitter, @id, @uris, @request, @response, @connection) + end +end diff --git a/lib/vendor/twitter/spec/twitter/client/favorites_spec.rb b/lib/vendor/twitter/spec/twitter/client/favorites_spec.rb new file mode 100644 index 0000000..c76efd9 --- /dev/null +++ b/lib/vendor/twitter/spec/twitter/client/favorites_spec.rb @@ -0,0 +1,183 @@ +require File.join(File.dirname(__FILE__), '..', '..', 'spec_helper') + +describe Twitter::Client, "#favorites" do + before(:each) do + @uri = '/favorites.json' + @request = mas_net_http_get(:basic_auth => nil) + @twitter = client_context + @default_header = @twitter.send(:http_header) + @response = mas_net_http_response(:success) + @connection = mas_net_http(@response) + @options = { :page => 4 } + Net::HTTP.stub!(:new).and_return(@connection) + @favorites = [] + Twitter::Status.stub!(:unmarshal).and_return(@favorites) + end + + it "should create expected HTTP GET request when not giving options" do + @twitter.should_receive(:create_http_get_request).with(@uri).and_return(@request) + @twitter.favorites + end + + it "should create expected HTTP GET request when giving :page options" do + @twitter.should_receive(:create_http_get_request).with("#{@uri}?#{@options.to_http_str}").and_return(@request) + @twitter.favorites(@options) + end + + it "should raise Twitter::RESTError when 401 HTTP response received without giving options" do + @connection = mas_net_http(mas_net_http_response(:not_authorized)) + lambda { + @twitter.favorites + }.should raise_error(Twitter::RESTError) + end + + it "should raise Twitter::RESTError when 401 HTTP response received when giving page options" do + @connection = mas_net_http(mas_net_http_response(:not_authorized)) + lambda { + @twitter.favorites(@options) + }.should raise_error(Twitter::RESTError) + end + + it "should raise Twitter::RESTError when 403 HTTP response received without giving options" do + @connection = mas_net_http(mas_net_http_response(:forbidden)) + lambda { + @twitter.favorites + }.should raise_error(Twitter::RESTError) + end + + it "should raise Twitter::RESTError when 403 HTTP response received when giving page options" do + @connection = mas_net_http(mas_net_http_response(:forbidden)) + lambda { + @twitter.favorites(@options) + }.should raise_error(Twitter::RESTError) + end + + it "should raise Twitter::RESTError when 500 HTTP response received without giving options" do + @connection = mas_net_http(mas_net_http_response(:server_error)) + lambda { + @twitter.favorites + }.should raise_error(Twitter::RESTError) + end + + it "should raise Twitter::RESTError when 500 HTTP response received when giving page options" do + @connection = mas_net_http(mas_net_http_response(:server_error)) + lambda { + @twitter.favorites(@options) + }.should raise_error(Twitter::RESTError) + end + + after(:each) do + nilize(@uri, @request, @twitter, @default_header, @response, @error_response, @connection) + end +end + +module FavoriteSpecMixin + def init + @base_uri = '/favourings' + @request = mas_net_http_get(:basic_auth => nil) + @twitter = client_context + @default_header = @twitter.send(:http_header) + @response = mas_net_http_response(:success) + @connection = mas_net_http(@response) + Net::HTTP.stub!(:new).and_return(@connection) + @id = 234923423 + @status = mas_twitter_status(:id => @id, :to_i => @id) + Twitter::Status.stub!(:unmarshal).and_return(@status) + end + + def create_uri(method, id) + "#{@base_uri}/#{method.to_s}/#{id.to_i.to_s}.json" + end + + def connection=(connection) + @connection = connection + end + + def finalize + nilize(@uri, @request, @twitter, @default_header, @response, @error_response, @connection) + end +end + +describe "Twitter::Client#favorite error handling", :shared => true do + it "should raise a Twitter::RESTError exception when a 401 HTTP response is received" do + connection = mas_net_http(mas_net_http_response(:not_authorized)) + lambda { + execute_method + }.should raise_error(Twitter::RESTError) + end + + it "should raise a Twitter::RESTError exception when a 403 HTTP response is received" do + connection = mas_net_http(mas_net_http_response(:forbidden)) + lambda { + execute_method + }.should raise_error(Twitter::RESTError) + end + + it "should raise a Twitter::RESTError exception when a 404 HTTP response is received" do + connection = mas_net_http(mas_net_http_response(:file_not_found)) + lambda { + execute_method + }.should raise_error(Twitter::RESTError) + end + + it "should raise a Twitter::RESTError exception when a 500 HTTP response is received" do + connection = mas_net_http(mas_net_http_response(:server_error)) + lambda { + execute_method + }.should raise_error(Twitter::RESTError) + end +end + +describe Twitter::Client, "#favorite(:add, status)" do + include FavoriteSpecMixin + it_should_behave_like "Twitter::Client#favorite error handling" + + before(:each) do + init + end + + def execute_method + @twitter.favorite(:add, @id) + end + + it "should create expected POST request for :add action supplying integer id of status" do + @twitter.should_receive(:create_http_post_request).with(create_uri(:create, @id)).and_return(@request) + @twitter.favorite(:add, @id) + end + + it "should create expected POST request for :add action supplying status object" do + @twitter.should_receive(:create_http_post_request).with(create_uri(:create, @id)).and_return(@request) + @twitter.favorite(:add, @status) + end + + after(:each) do + finalize + end +end + +describe Twitter::Client, "#favorite(:remove, status)" do + include FavoriteSpecMixin + it_should_behave_like "Twitter::Client#favorite error handling" + + before(:each) do + init + end + + def execute_method + @twitter.favorite(:remove, @id) + end + + it "should create expected DELETE request for :remove action supplying integer id of status" do + @twitter.should_receive(:create_http_delete_request).with(create_uri(:destroy, @id)).and_return(@request) + @twitter.favorite(:remove, @id) + end + + it "should create expected DELETE request for :remove action supplying status object" do + @twitter.should_receive(:create_http_delete_request).with(create_uri(:destroy, @id)).and_return(@request) + @twitter.favorite(:remove, @status) + end + + after(:each) do + finalize + end +end diff --git a/lib/vendor/twitter/spec/twitter/client/friendship_spec.rb b/lib/vendor/twitter/spec/twitter/client/friendship_spec.rb new file mode 100644 index 0000000..4701cd7 --- /dev/null +++ b/lib/vendor/twitter/spec/twitter/client/friendship_spec.rb @@ -0,0 +1,76 @@ +require File.join(File.dirname(__FILE__), '..', '..', 'spec_helper') + +describe Twitter::Client, "#friend" do + before(:each) do + @twitter = client_context + @id = 1234567 + @screen_name = 'dummylogin' + @friend = Twitter::User.new(:id => @id, :screen_name => @screen_name) + @uris = Twitter::Client.class_eval("@@FRIENDSHIP_URIS") + @request = mas_net_http_post(:basic_auth => nil) + @response = mas_net_http_response(:success) + @connection = mas_net_http(@response) + Net::HTTP.stub!(:new).and_return(@connection) + Twitter::User.stub!(:unmarshal).and_return(@friend) + end + + def create_uri(action, id) + "#{@uris[action]}/#{id}.json" + end + + it "should create expected HTTP GET request for :add case using integer user ID" do + # the integer user ID scenario... + @twitter.should_receive(:create_http_post_request).with(create_uri(:add, @id)).and_return(@request) + @twitter.friend(:add, @id) + end + + it "should create expected HTTP GET request for :add case using screen name" do + # the screen name scenario... + @twitter.should_receive(:create_http_post_request).with(create_uri(:add, @screen_name)).and_return(@request) + @twitter.friend(:add, @screen_name) + end + + it "should create expected HTTP GET request for :add case using Twitter::User object" do + # the Twitter::User object scenario... + @twitter.should_receive(:create_http_post_request).with(create_uri(:add, @friend.to_i)).and_return(@request) + @twitter.friend(:add, @friend) + end + + it "should create expected HTTP GET request for :remove case using integer user ID" do + # the integer user ID scenario... + @twitter.should_receive(:create_http_post_request).with(create_uri(:remove, @id)).and_return(@request) + @twitter.friend(:remove, @id) + end + + it "should create expected HTTP GET request for :remove case using screen name" do + # the screen name scenario... + @twitter.should_receive(:create_http_post_request).with(create_uri(:remove, @screen_name)).and_return(@request) + @twitter.friend(:remove, @screen_name) + end + + it "should create expected HTTP GET request for :remove case using Twitter::User object" do + # the Twitter::User object scenario... + @twitter.should_receive(:create_http_post_request).with(create_uri(:remove, @friend.to_i)).and_return(@request) + @twitter.friend(:remove, @friend) + end + + it "should bless user model returned for :add case" do + @twitter.should_receive(:bless_model).with(@friend) + @twitter.friend(:add, @friend) + end + + it "should bless user model returned for :remove case" do + @twitter.should_receive(:bless_model).with(@friend) + @twitter.friend(:remove, @friend) + end + + it "should raise ArgumentError if action given is not valid" do + lambda { + @twitter.friend(:crap, @friend) + }.should raise_error(ArgumentError) + end + + after(:each) do + nilize(@twitter, @id, @uris, @request, @response, @connection) + end +end diff --git a/lib/vendor/twitter/spec/twitter/client/graph_spec.rb b/lib/vendor/twitter/spec/twitter/client/graph_spec.rb new file mode 100644 index 0000000..c4d5602 --- /dev/null +++ b/lib/vendor/twitter/spec/twitter/client/graph_spec.rb @@ -0,0 +1,67 @@ +require File.join(File.dirname(__FILE__), '..', '..', 'spec_helper') + +describe Twitter::Client, "#graph(:friends...)" do + before(:each) do + @twitter = client_context + @id = 1234567 + @screen_name = 'dummylogin' + @friend = Twitter::User.new(:id => @id, :screen_name => @screen_name) + @uris = Twitter::Client.class_eval("@@GRAPH_URIS") + @request = mas_net_http_get(:basic_auth => nil) + @response = mas_net_http_response(:success) + @response.stub!(:body).and_return("[1, 2, 3, 4, 5, 6]") + @connection = mas_net_http(@response) + Net::HTTP.stub!(:new).and_return(@connection) + Twitter::User.stub!(:unmarshal).and_return(@friend) + end + + def create_uri(action) + "#{@uris[action]}.json" + end + + it "should create expected HTTP GET request for :friends case using integer user ID" do + # the integer user ID scenario... + @twitter.should_receive(:create_http_get_request).with(create_uri(:friends), :id => @id).and_return(@request) + @twitter.graph(:friends, @id) + end + + it "should create expected HTTP GET request for :friends case using screen name" do + # the screen name scenario... + @twitter.should_receive(:create_http_get_request).with(create_uri(:friends), :id => @screen_name).and_return(@request) + @twitter.graph(:friends, @screen_name) + end + + it "should create expected HTTP GET request for :friends case using Twitter::User object" do + # the Twitter::User object scenario... + @twitter.should_receive(:create_http_get_request).with(create_uri(:friends), :id => @friend.to_i).and_return(@request) + @twitter.graph(:friends, @friend) + end + + it "should create expected HTTP GET request for :followers case using integer user ID" do + # the integer user ID scenario... + @twitter.should_receive(:create_http_get_request).with(create_uri(:followers), :id => @id).and_return(@request) + @twitter.graph(:followers, @id) + end + + it "should create expected HTTP GET request for :followers case using screen name" do + # the screen name scenario... + @twitter.should_receive(:create_http_get_request).with(create_uri(:followers), :id => @screen_name).and_return(@request) + @twitter.graph(:followers, @screen_name) + end + + it "should create expected HTTP GET request for :followers case using Twitter::User object" do + # the Twitter::User object scenario... + @twitter.should_receive(:create_http_get_request).with(create_uri(:followers), :id => @friend.to_i).and_return(@request) + @twitter.graph(:followers, @friend) + end + + it "should raise ArgumentError if action given is not valid" do + lambda { + @twitter.graph(:crap, @friend) + }.should raise_error(ArgumentError) + end + + after(:each) do + nilize(@twitter, @id, @uris, @request, @response, @connection) + end +end diff --git a/lib/vendor/twitter/spec/twitter/client/messaging_spec.rb b/lib/vendor/twitter/spec/twitter/client/messaging_spec.rb new file mode 100644 index 0000000..83e228d --- /dev/null +++ b/lib/vendor/twitter/spec/twitter/client/messaging_spec.rb @@ -0,0 +1,135 @@ +require File.join(File.dirname(__FILE__), '..', '..', 'spec_helper') + +describe Twitter::Client, "#messages" do + before(:each) do + @twitter = client_context + @uris = Twitter::Client.class_eval("@@MESSAGING_URIS") + @request = mas_net_http_get(:basic_auth => nil) + @response = mas_net_http_response(:success, "[]") + @connection = mas_net_http(@response) + Net::HTTP.stub!(:new).and_return(@connection) + @messages = [] + Twitter::Message.stub!(:unmarshal).and_return(@messages) + @page = 2 + end + + it "should create expected HTTP GET request for :received case" do + @twitter.should_receive(:create_http_get_request).with(@uris[:received], {}).and_return(@request) + @twitter.messages(:received) + end + + it "should bless the Array returned from Twitter for :received case" do + @twitter.should_receive(:bless_models).with(@messages).and_return(@messages) + @twitter.messages(:received) + end + + it "should create expected HTTP GET request for :sent case" do + @twitter.should_receive(:create_http_get_request).with(@uris[:sent], {}).and_return(@request) + @twitter.messages(:sent) + end + + it "should bless the Array returned from Twitter for :sent case" do + @twitter.should_receive(:bless_models).with(@messages).and_return(@messages) + @twitter.messages(:sent) + end + + it "should raise an ArgumentError when giving an invalid messaging action" do + lambda { + @twitter.messages(:crap) + }.should raise_error(ArgumentError) + end + + it "should accept an options Hash for paging" do + lambda { + @twitter.messages(:sent, :page => @page) + }.should_not raise_error(Exception) + end + + it "should generate expected GET HTTP request for paging case" do + @twitter.should_receive(:create_http_get_request).with(@uris[:received], {:page => @page}).and_return(@request) + @twitter.messages(:received, :page => @page) + end + + it "should bless models for paging case" do + @twitter.should_receive(:bless_models).with(@messages).and_return(@messages) + @twitter.messages(:sent, :page => @page) + end + + after(:each) do + nilize(@twitter, @uris, @request, @response, @connection, @messages) + end +end + +describe Twitter::Client, "#message" do + before(:each) do + @twitter = client_context + @attributes = { + :id => 34324, + :text => 'Randy, are you coming over later?', + :sender => {:id => 123, :screen_name => 'mylogin'}, + :recipient => {:id => 1234, :screen_name => 'randy'}, + } + @message = Twitter::Message.new(@attributes) + @uris = Twitter::Client.class_eval("@@MESSAGING_URIS") + @request = mas_net_http_get(:basic_auth => nil) + @json = JSON.unparse(@attributes) + @response = mas_net_http_response(:success, @json) + @connection = mas_net_http(@response) + @source = Twitter::Client.class_eval("@@defaults[:source]") + + Net::HTTP.stub!(:new).and_return(@connection) + Twitter::Message.stub!(:unmarshal).and_return(@message) + end + + it "should invoke #http_connect with expected arguments for :post case" do + @twitter.should_receive(:http_connect).with({:text => @message.text, :user => @message.recipient.to_i, :source => @source}.to_http_str).and_return(@response) + @twitter.message(:post, @message.text, @message.recipient) + end + + it "should create expected HTTP POST request for :post case" do + @twitter.should_receive(:create_http_post_request).with(@uris[:post]).and_return(@request) + @twitter.message(:post, @message.text, @message.recipient) + end + + it "should bless returned Twitter::Message object for :post case" do + @twitter.should_receive(:bless_model).with(@message) + @twitter.message(:post, @message.text, @message.recipient) + end + + it "should create expected HTTP DELETE request for :delete case" do + @twitter.should_receive(:create_http_delete_request).with(@uris[:delete], {:id => @message.to_i}).and_return(@request) + @twitter.message(:delete, @message) + end + + it "should bless returned Twitter::Message object for :delete case" do + @twitter.should_receive(:bless_model).with(@message) + @twitter.message(:delete, @message) + end + + it "should invoke #to_i on message object passed in for :delete case" do + @message.should_receive(:to_i).and_return(@message.id) + @twitter.message(:delete, @message) + end + + it "should raise an ArgumentError when giving an invalid messaging action" do + lambda { + @twitter.message(:crap, @message) + }.should raise_error(ArgumentError) + end + + it "should raise an ArgumentError for :post case if user argument is not supplied" do + lambda { + @twitter.message(:post, @message) + }.should raise_error(ArgumentError) + end + + it "should raise an ArgumentError for :post case if user argument is nil" do + lambda { + @twitter.message(:post, @message, nil) + }.should raise_error(ArgumentError) + end + + after(:each) do + nilize(@twitter, @uris, @request, @response, @connection, @sender, @recipient, @message, @attributes) + end +end diff --git a/lib/vendor/twitter/spec/twitter/client/profile_spec.rb b/lib/vendor/twitter/spec/twitter/client/profile_spec.rb new file mode 100644 index 0000000..0148653 --- /dev/null +++ b/lib/vendor/twitter/spec/twitter/client/profile_spec.rb @@ -0,0 +1,91 @@ +require File.join(File.dirname(__FILE__), '..', '..', 'spec_helper') + +describe Twitter::Client, "#profile" do + before(:each) do + @twitter = client_context + @user_attrs = { + :id => "JaneEyre", + :login => "Jane Eyre", + :url => "http://janeeyrerocks.co.uk", + :location => "Thornfield Manor", + } + # name, email, url, location, description + @info_attrs = { + :name => "Jane Eyre", + :email => "jane.eyre@gmail.co.uk", + :url => "http://janeeyrerocks.co.uk", + :location => "Thornfield Manor", + :description => "Governess who falls for slave-trade aristocrat with French lovechild he doesn't acknowledge & wife locked in damp attic with keeper.", + } + # background_color, text_color, link_color, sidebar_fill_color, sidebar_border_color + @colors_attrs = { + :background_color => "#ffffff", + :text_color => "#101010", + :link_color => "#990000", + } + # value + @device_attrs = { + :value => "sms", + } + @user = Twitter::User.new + @uris = Twitter::Client.class_eval("@@PROFILE_URIS") + @request = mas_net_http_get(:basic_auth => nil) + @json = JSON.unparse(@user_attrs) + @response = mas_net_http_response(:success, @json) + @connection = mas_net_http(@response) + + Net::HTTP.stub!(:new).and_return(@connection) + Twitter::User.stub!(:unmarshal).and_return(@user) + end + + it "should invoke #http_connect with expected arguments for :info case" do + @twitter.should_receive(:http_connect).with(@info_attrs.to_http_str).and_return(@response) + @twitter.profile(:info, @info_attrs) + end + + it "should invoke #http_connect with expected arguments for :colors case" do + @twitter.should_receive(:http_connect).with(@colors_attrs.to_http_str).and_return(@response) + @twitter.profile(:colors, @colors_attrs) + end + + it "should invoke #http_connect with expected arguments for :device case" do + @twitter.should_receive(:http_connect).with(@device_attrs.to_http_str).and_return(@response) + @twitter.profile(:info, @device_attrs) + end + + it "should create expected HTTP POST request for :info case" do + @twitter.should_receive(:create_http_post_request).with(@uris[:info]).and_return(@request) + @twitter.profile(:info, @info_attrs) + end + + it "should create expected HTTP POST request for :colors case" do + @twitter.should_receive(:create_http_post_request).with(@uris[:colors]).and_return(@request) + @twitter.profile(:colors, @colors_attrs) + end + + it "should create expected HTTP POST request for :device case" do + @twitter.should_receive(:create_http_post_request).with(@uris[:device]).and_return(@request) + @twitter.profile(:device, @device_attrs) + end + + it "should bless returned Twitter::User object for :info case" do + @twitter.should_receive(:bless_model).with(@user) + @twitter.profile(:info, @info_attrs) + end + + it "should bless returned Twitter::User object for :colors case" do + @twitter.should_receive(:bless_model).with(@user) + @twitter.profile(:colors, @colors_attrs) + end + + it "should bless returned Twitter::User object for :device case" do + @twitter.should_receive(:bless_model).with(@user) + @twitter.profile(:device, @device_attrs) + end + + it "should raise an ArgumentError when giving an invalid profile action" + + after(:each) do + nilize(@twitter, @uris, @request, @response, @connection, @sender, @recipient, @user, @attributes) + end +end diff --git a/lib/vendor/twitter/spec/twitter/client/search_spec.rb b/lib/vendor/twitter/spec/twitter/client/search_spec.rb new file mode 100644 index 0000000..5081102 --- /dev/null +++ b/lib/vendor/twitter/spec/twitter/client/search_spec.rb @@ -0,0 +1,68 @@ +require File.join(File.dirname(__FILE__), '..', '..', 'spec_helper') + +describe Twitter::Client, "#search" do + before(:each) do + @twitter = client_context + @uris = Twitter::Client.class_eval("@@SEARCH_URIS") + @request = mas_net_http_get(:basic_auth => nil) + @response = mas_net_http_response(:success, "{\"results\": [], \"refresh_url\":\"?since_id=1768746401&q=blabla\"}") + @connection = mas_net_http(@response) + Net::HTTP.stub!(:new).and_return(@connection) + @statuses = [] + Twitter::Status.stub!(:unmarshal).and_return(@statuses) + @page = 2 + @keywords = "twitter4r" + @to = "SusanPotter" + @from = "twitter4r" + end + + it "should create expected HTTP GET request using :to" do + @twitter.should_receive(:create_http_get_request).with(@uris[:basic], {:to => @to}).and_return(@request) + @twitter.search(:to => @to) + end + + it "should bless the Array returned from Twitter for :to case" do + @twitter.should_receive(:bless_models).with(@statuses).and_return(@statuses) + @twitter.search(:to => @to) + end + + it "should create expected HTTP GET request using :from" do + @twitter.should_receive(:create_http_get_request).with(@uris[:basic], {:from => @from}).and_return(@request) + @twitter.search(:from => @from) + end + + it "should bless the Array returned from Twitter for :to case" do + @twitter.should_receive(:bless_models).with(@statuses).and_return(@statuses) + @twitter.search(:from => @from) + end + + it "should create expected HTTP GET request using :keywords" do + @twitter.should_receive(:create_http_get_request).with(@uris[:basic], {:keywords => @keywords}).and_return(@request) + @twitter.search(:keywords => @keywords) + end + + it "should bless the Array returned from Twitter for :keywords case" do + @twitter.should_receive(:bless_models).with(@statuses).and_return(@statuses) + @twitter.search(:keywords => @keywords) + end + + it "should accept paging option" do + lambda { + @twitter.search(:keywords => @keywords, :page => @page) + }.should_not raise_error(Exception) + end + + it "should generate expected GET HTTP request for paging case" do + @twitter.should_receive(:create_http_get_request).with(@uris[:basic], {:page => @page}).and_return(@request) + @twitter.search(:page => @page) + end + + it "should bless models for paging case" do + @twitter.should_receive(:bless_models).with(@statuses).and_return(@statuses) + @twitter.search(:page => @page) + end + + after(:each) do + nilize(@twitter, @uris, @request, @response, @connection, @statuses) + end +end diff --git a/lib/vendor/twitter/spec/twitter/client/status_spec.rb b/lib/vendor/twitter/spec/twitter/client/status_spec.rb new file mode 100644 index 0000000..84aad8f --- /dev/null +++ b/lib/vendor/twitter/spec/twitter/client/status_spec.rb @@ -0,0 +1,119 @@ +require File.join(File.dirname(__FILE__), '..', '..', 'spec_helper') + +describe Twitter::Client, "#status" do + before(:each) do + @twitter = client_context + @message = 'This is my unique message' + @uris = Twitter::Client.class_eval("@@STATUS_URIS") + @options = {:id => 666666} + @request = mas_net_http_get(:basic_auth => nil) + @response = mas_net_http_response(:success, '{}') + @connection = mas_net_http(@response) + @float = 43.3434 + @status = Twitter::Status.new(:id => 2349343) + @reply_to_status_id = 3495293 + @source = Twitter::Client.class_eval("@@defaults[:source]") + end + + it "should return nil if nil is passed as value argument for :get case" do + status = @twitter.status(:get, nil) + status.should be_nil + end + + it "should not call @twitter#http_connect when passing nil for value argument in :get case" do + @twitter.should_not_receive(:http_connect) + @twitter.status(:get, nil) + end + + it "should create expected HTTP GET request for :get case" do + @twitter.should_receive(:create_http_get_request).with(@uris[:get], @options).and_return(@request) + @twitter.status(:get, @options[:id]) + end + + it "should invoke @twitter#create_http_get_request with given parameters equivalent to {:id => value.to_i} for :get case" do + # Float case + @twitter.should_receive(:create_http_get_request).with(@uris[:get], {:id => @float.to_i}).and_return(@request) + @twitter.status(:get, @float) + + # Twitter::Status object case + @twitter.should_receive(:create_http_get_request).with(@uris[:get], {:id => @status.to_i}).and_return(@request) + @twitter.status(:get, @status) + end + + it "should return nil if nil is passed as value argument for :post case" do + status = @twitter.status(:post, nil) + status.should be_nil + end + + it "should not call @twitter#http_connect when passing nil for value argument in :post case" do + @twitter.should_not_receive(:http_connect) + @twitter.status(:post, nil) + end + + it "should create expected HTTP POST request for :post case" do + @twitter.should_receive(:create_http_post_request).with(@uris[:post]).and_return(@request) + @connection.should_receive(:request).with(@request, {:status => @message, :source => @source}.to_http_str).and_return(@response) + @twitter.status(:post, @message) + end + + it "should return nil if no :status key-value given in the value argument for :reply case" do + status = @twitter.status(:reply, {}) + status.should be_nil + end + + it "should return nil if nil is passed as value argument for :reply case" do + status = @twitter.status(:reply, nil) + status.should be_nil + end + + it "should not call @twitter#http_connect when passing a value Hash argument that has no :status key-value in :reply case" do + @twitter.should_not_receive(:http_connect) + @twitter.status(:reply, {}) + end + + it "should not call @twitter#http_connect when passing nil for value argument in :reply case" do + @twitter.should_not_receive(:http_connect) + @twitter.status(:reply, nil) + end + + it "should create expected HTTP POST request for :reply case" do + @twitter.should_receive(:create_http_post_request).with(@uris[:reply]).and_return(@request) + @connection.should_receive(:request).with(@request, {:status => @message, :source => @source, :in_reply_to_status_id => @reply_to_status_id}.to_http_str).and_return(@response) + @twitter.status(:reply, :status => @message, :in_reply_to_status_id => @reply_to_status_id) + end + + it "should return nil if nil is passed as value argument for :delete case" do + status = @twitter.status(:delete, nil) + status.should be_nil + end + + it "should not call @twitter#http_connect when passing nil for value argument in :delete case" do + @twitter.should_not_receive(:http_connect) + @twitter.status(:delete, nil) + end + + it "should create expected HTTP DELETE request for :delete case" do + @twitter.should_receive(:create_http_delete_request).with(@uris[:delete], @options).and_return(@request) + @twitter.status(:delete, @options[:id]) + end + + it "should invoke @twitter#create_http_get_request with given parameters equivalent to {:id => value.to_i} for :delete case" do + # Float case + @twitter.should_receive(:create_http_delete_request).with(@uris[:delete], {:id => @float.to_i}).and_return(@request) + @twitter.status(:delete, @float) + + # Twitter::Status object case + @twitter.should_receive(:create_http_delete_request).with(@uris[:delete], {:id => @status.to_i}).and_return(@request) + @twitter.status(:delete, @status) + end + + it "should raise an ArgumentError when given an invalid status action" do + lambda { + @twitter.status(:crap, nil) + }.should raise_error(ArgumentError) + end + + after(:each) do + nilize(@twitter) + end +end diff --git a/lib/vendor/twitter/spec/twitter/client/timeline_spec.rb b/lib/vendor/twitter/spec/twitter/client/timeline_spec.rb new file mode 100644 index 0000000..b49255c --- /dev/null +++ b/lib/vendor/twitter/spec/twitter/client/timeline_spec.rb @@ -0,0 +1,79 @@ +require File.join(File.dirname(__FILE__), '..', '..', 'spec_helper') + +describe Twitter::Client, "Timeline API" do + before(:each) do + @client = client_context + @uris = Twitter::Client.class_eval("@@TIMELINE_URIS") + @user = Twitter::User.new(:screen_name => 'mylogin') + @status = Twitter::Status.new(:id => 23343443, :text => 'I love Lucy!', :user => @user) + @timeline = [@status] + @json = JSON.unparse([@status.to_hash]) + @request = mas_net_http_get(:basic_auth => nil) + @response = mas_net_http_response(:success, @json) + @connection = mas_net_http(@response) + @params = { + :public => {:since_id => 3249328}, + :friends => {:id => 'myfriend'}, + :user => {:id => 'auser'}, + :me => {}, + } + end + + it "should respond to instance method #timeline_for" do + @client.should respond_to(:timeline_for) + end + + it "should call #http_get with expected parameters for :public case" do + @client.should_receive(:http_connect).and_return(mas_net_http_response(:success, @json)) + @client.timeline_for(:public) + end + + it "should yield to block for each status in timeline" do + @client.should_receive(:http_connect).and_return(mas_net_http_response(:success, @json)) + Twitter::Status.should_receive(:unmarshal).and_return(@timeline) + count = 0 + @client.timeline_for(:public) do |status| + status.should eql(@status) + count += 1 + end + count.should eql(@timeline.size) + end + + it "should generate expected HTTP GET request for generic :public case" do + @client.should_receive(:create_http_get_request).with(@uris[:public], {}).and_return(@request) + timeline = @client.timeline_for(:public) + timeline.should eql(@timeline) + end + + it "should generate expected HTTP GET request for :public case with expected parameters" do + @client.should_receive(:create_http_get_request).with(@uris[:public], @params[:public]).and_return(@request) + timeline = @client.timeline_for(:public, @params[:public]) + timeline.should eql(@timeline) + end + + it "should generate expected HTTP GET request for generic :friends case" do + @client.should_receive(:create_http_get_request).with(@uris[:friends], {}).and_return(@request) + timeline = @client.timeline_for(:friends) + timeline.should eql(@timeline) + end + + it "should generate expected HTTP GET request for :friends case with expected parameters" do + @client.should_receive(:create_http_get_request).with(@uris[:friends], @params[:friends]).and_return(@request) + timeline = @client.timeline_for(:friends, @params[:friends]) + timeline.should eql(@timeline) + end + + it "should raise an ArgumentError if type given is not valid" do + lambda { + @client.timeline_for(:crap) + }.should raise_error(ArgumentError) + + lambda { + @client.timeline_for(:crap, @params[:friends]) + }.should raise_error(ArgumentError) + end + + after(:each) do + nilize(@client) + end +end diff --git a/lib/vendor/twitter/spec/twitter/client/user_spec.rb b/lib/vendor/twitter/spec/twitter/client/user_spec.rb new file mode 100644 index 0000000..c983ed9 --- /dev/null +++ b/lib/vendor/twitter/spec/twitter/client/user_spec.rb @@ -0,0 +1,203 @@ +require File.join(File.dirname(__FILE__), '..', '..', 'spec_helper') + +describe Twitter::Client, "#user(id, :info)" do + before(:each) do + @twitter = client_context + @id = 395783 + @screen_name = 'boris_johnson_is_funny_as_hell' + @user = Twitter::User.new( + :id => @id, + :screen_name => @screen_name, + :location => 'London' + ) + @json = JSON.unparse(@user.to_hash) + @request = mas_net_http_get(:basic_auth => nil) + @response = mas_net_http_response(:success, @json) + @connection = mas_net_http(@response) + @uris = Twitter::Client.class_eval("@@USER_URIS") + @twitter.stub!(:create_http_get_request).and_return(@request) + Twitter::User.stub!(:unmarshal).and_return(@user) + Net::HTTP.stub!(:new).and_return(@connection) + end + + it "should create expected HTTP GET request when giving numeric user id" do + @twitter.should_receive(:create_http_get_request).with(@uris[:info], {:id => @id}).and_return(@request) + @twitter.user(@id) + end + + it "should create expected HTTP GET request when giving screen name" do + @twitter.should_receive(:create_http_get_request).with(@uris[:info], {:id => @screen_name}).and_return(@request) + @twitter.user(@screen_name) + end + + it "should bless model returned when giving numeric user id" do + @twitter.should_receive(:bless_model).with(@user).and_return(@user) + @twitter.user(@id) + end + + it "should bless model returned when giving screen name" do + @twitter.should_receive(:bless_model).with(@user).and_return(@user) + @twitter.user(@screen_name) + end + + after(:each) do + nilize(@request, @response, @connection, @twitter, @id, @screen_name, @user) + end +end + +# TODO: Add specs for new Twitter::Client#user(id, :friends) and +# Twitter::Client#user(id, :followers) use cases. +describe Twitter::Client, "#user(id, :friends)" do + before(:each) do + @twitter = client_context + @id = 395784 + @screen_name = 'cafe_paradiso' + @user = Twitter::User.new( + :id => @id, + :screen_name => @screen_name, + :location => 'Urbana, IL' + ) + @json = JSON.unparse(@user.to_hash) + @request = mas_net_http_get(:basic_auth => nil) + @response = mas_net_http_response(:success, @json) + @connection = mas_net_http(@response) + @uris = Twitter::Client.class_eval("@@USER_URIS") + @twitter.stub!(:create_http_get_request).and_return(@request) + Twitter::User.stub!(:unmarshal).and_return(@user) + Net::HTTP.stub!(:new).and_return(@connection) + end + + it "should create expected HTTP GET request when giving numeric user id" do + @twitter.should_receive(:create_http_get_request).with(@uris[:friends], {:id => @id}).and_return(@request) + @twitter.user(@id, :friends) + end + + it "should invoke #to_i on Twitter::User objecct given" do + @user.should_receive(:to_i).and_return(@id) + @twitter.user(@user, :friends) + end + + it "should create expected HTTP GET request when giving Twitter::User object" do + @twitter.should_receive(:create_http_get_request).with(@uris[:friends], {:id => @user.to_i}).and_return(@request) + @twitter.user(@user, :friends) + end + + it "should create expected HTTP GET request when giving screen name" do + @twitter.should_receive(:create_http_get_request).with(@uris[:friends], {:id => @screen_name}).and_return(@request) + @twitter.user(@screen_name, :friends) + end + + it "should bless model returned when giving numeric id" do + @twitter.should_receive(:bless_model).with(@user).and_return(@user) + @twitter.user(@id, :friends) + end + + it "should bless model returned when giving Twitter::User object" do + @twitter.should_receive(:bless_model).with(@user).and_return(@user) + @twitter.user(@user, :friends) + end + + it "should bless model returned when giving screen name" do + @twitter.should_receive(:bless_model).with(@user).and_return(@user) + @twitter.user(@screen_name, :friends) + end + + after(:each) do + nilize(@request, @response, @connection, @twitter, @id, @screen_name, @user) + end +end + +describe Twitter::Client, "#my(:info)" do + before(:each) do + @twitter = client_context + @screen_name = @twitter.instance_eval("@login") + @user = Twitter::User.new( + :id => 2394393, + :screen_name => @screen_name, + :location => 'Glamorous Urbana' + ) + @json = JSON.unparse(@user.to_hash) + @request = mas_net_http_get(:basic_auth => nil) + @response = mas_net_http_response(:success, @json) + @connection = mas_net_http(@response) + @uris = Twitter::Client.class_eval("@@USER_URIS") + @twitter.stub!(:create_http_get_request).and_return(@request) + Net::HTTP.stub!(:new).and_return(@connection) + Twitter::User.stub!(:unmarshal).and_return(@user) + end + + it "should create expected HTTP GET request" do + @twitter.should_receive(:create_http_get_request).with(@uris[:info], :id => @screen_name).and_return(@request) + @twitter.my(:info) + end + + it "should bless the model object returned" do + @twitter.should_receive(:bless_models).with(@user).and_return(@user) + @twitter.my(:info) + end + + it "should return expected user object" do + user = @twitter.my(:info) + user.should eql(@user) + end + + after(:each) do + nilize(@request, @response, @connection, @twitter, @user, @screen_name) + end +end + +describe Twitter::Client, "#my(:friends)" do + before(:each) do + @twitter = client_context + @screen_name = @twitter.instance_eval("@login") + @friends = [ + Twitter::User.new(:screen_name => 'lucy_snowe'), + Twitter::User.new(:screen_name => 'jane_eyre'), + Twitter::User.new(:screen_name => 'tess_derbyfield'), + Twitter::User.new(:screen_name => 'elizabeth_jane_newson'), + ] + @json = JSON.unparse(@friends.collect {|f| f.to_hash }) + @request = mas_net_http_get(:basic_auth => nil) + @response = mas_net_http_response(:success, @json) + @connection = mas_net_http(@response) + @uris = Twitter::Client.class_eval("@@USER_URIS") + @twitter.stub!(:create_http_get_request).and_return(@request) + Twitter::User.stub!(:unmarshal).and_return(@friends) + Net::HTTP.stub!(:new).and_return(@connection) + end + + it "should create expected HTTP GET request" do + @twitter.should_receive(:create_http_get_request).with(@uris[:friends], :id => @screen_name).and_return(@request) + @twitter.my(:friends) + end + + it "should bless models returned" do + @twitter.should_receive(:bless_models).with(@friends).and_return(@friends) + @twitter.my(:friends) + end + + it "should return expected Array of friends" do + friends = @twitter.my(:friends) + friends.should eql(@friends) + end + + after(:each) do + nilize(@request, @response, @connection, @twitter, @friends, @screen_name) + end +end + +describe Twitter::Client, "#my(:invalid_action)" do + before(:each) do + @twitter = client_context + end + + it "should raise ArgumentError for invalid user action" do + lambda { + @twitter.my(:crap) + }.should raise_error(ArgumentError) + end + + after(:each) do + nilize(@twitter) + end +end diff --git a/lib/vendor/twitter/spec/twitter/client_spec.rb b/lib/vendor/twitter/spec/twitter/client_spec.rb new file mode 100644 index 0000000..28723b5 --- /dev/null +++ b/lib/vendor/twitter/spec/twitter/client_spec.rb @@ -0,0 +1,2 @@ +require File.join(File.dirname(__FILE__), '..', 'spec_helper') + diff --git a/lib/vendor/twitter/spec/twitter/config_spec.rb b/lib/vendor/twitter/spec/twitter/config_spec.rb new file mode 100644 index 0000000..19d65e5 --- /dev/null +++ b/lib/vendor/twitter/spec/twitter/config_spec.rb @@ -0,0 +1,86 @@ +require File.join(File.dirname(__FILE__), '..', 'spec_helper') + +describe Twitter::Client, ".configure" do + it "should respond to :configure class method" do + Twitter::Client.respond_to?(:configure).should be(true) + end + + it "should not accept calls that do not specify blocks" do + lambda { + Twitter::Client.configure() + }.should raise_error(ArgumentError) + end +end + +describe Twitter::Client, ".configure with mocked @config" do + before(:each) do + @block_invoked = false + @conf_yielded = false + @conf = mock(Twitter::Config) + @block = Proc.new do |conf| + @block_invoked = true + @conf_yielded = true if conf.is_a?(Twitter::Config) + end + Twitter::Config.stub!(:new).and_return(@conf) + end + + it "should not raise an error when passing block" do + lambda { + Twitter::Client.configure(&@block) + }.should_not raise_error + end + + it "should yield a Twitter::Client object to block" do + Twitter::Client.configure(&@block) + @block_invoked.should be(true) + @conf_yielded.should be(true) + end + + after(:each) do + nilize(@block, @block_invoked, @conf, @conf_yielded) + end +end + +describe Twitter::Config, "#eql?" do + before(:each) do + @protocol = :ssl + @host = 'twitter.com' + @port = 443 + @proxy_host = 'myproxy.host' + @proxy_port = 8080 + attrs = { + :protocol => @protocol, + :host => @host, + :port => @port, + :proxy_host => @proxy_host, + :proxy_port => @proxy_port, + } + @obj = Twitter::Config.new(attrs) + @other = Twitter::Config.new(attrs) + + @different = stubbed_twitter_config(Twitter::Config.new, attrs.merge(:proxy_host => 'different.proxy')) + @same = @obj + end + + it "should return true for two logically equivalent objects" do + @obj.should be_eql(@other) + @other.should be_eql(@obj) + end + + it "should return false for two logically different objects" do + @obj.should_not be_eql(@different) + @different.should_not be_eql(@obj) + @other.should_not be_eql(@different) + @different.should_not be_eql(@other) + end + + it "should return true for references to the same object in memory" do + @obj.should eql(@same) + @same.should eql(@obj) + @other.should eql(@other) + end + + after(:each) do + nilize(@protocol, @host, @port, @proxy_host, @proxy_port, @obj, @other, @different, @same) + end +end diff --git a/lib/vendor/twitter/spec/twitter/console_spec.rb b/lib/vendor/twitter/spec/twitter/console_spec.rb new file mode 100644 index 0000000..b296469 --- /dev/null +++ b/lib/vendor/twitter/spec/twitter/console_spec.rb @@ -0,0 +1,15 @@ +require File.join(File.dirname(__FILE__), '..', 'spec_helper') + +describe Twitter::Client, ".from_config" do + before(:each) do + + end + + it "should load YAML file for instance configuration" do + + end + + after(:each) do + + end +end diff --git a/lib/vendor/twitter/spec/twitter/core_spec.rb b/lib/vendor/twitter/spec/twitter/core_spec.rb new file mode 100644 index 0000000..7cd390e --- /dev/null +++ b/lib/vendor/twitter/spec/twitter/core_spec.rb @@ -0,0 +1,127 @@ +require File.join(File.dirname(__FILE__), '..', 'spec_helper') + +describe "Twitter::ClassUtilMixin mixed-in class" do + before(:each) do + class TestClass + include Twitter::ClassUtilMixin + attr_accessor :var1, :var2, :var3 + end + @init_hash = { :var1 => 'val1', :var2 => 'val2', :var3 => 'val3' } + end + + it "should have Twitter::ClassUtilMixin as an included module" do + TestClass.included_modules.member?(Twitter::ClassUtilMixin).should be(true) + end + + it "should set attributes passed in the hash to TestClass.new" do + test = TestClass.new(@init_hash) + @init_hash.each do |key, val| + test.send(key).should eql(val) + end + end + + it "should not set attributes passed in the hash that are not attributes in TestClass.new" do + test = nil + lambda { test = TestClass.new(@init_hash.merge(:var4 => 'val4')) }.should_not raise_error + test.respond_to?(:var4).should be(false) + end +end + +describe "Twitter::RESTError#to_s" do + before(:each) do + @hash = { :code => 200, :message => 'OK', :uri => 'http://test.host/bla' } + @error = Twitter::RESTError.new(@hash) + @expected_message = "HTTP #{@hash[:code]}: #{@hash[:message]} at #{@hash[:uri]}" + end + + it "should return @expected_message" do + @error.to_s.should eql(@expected_message) + end +end + +describe "Twitter::Status#eql?" do + before(:each) do + @id = 34329594003 + @attr_hash = { :text => 'Status', :id => @id, + :user => { :name => 'Tess', + :description => "Unfortunate D'Urberville", + :location => 'Dorset', + :url => nil, + :id => 34320304, + :screen_name => 'maiden_no_more' }, + :created_at => 'Wed May 02 03:04:54 +0000 2007'} + @obj = Twitter::Status.new @attr_hash + @other = Twitter::Status.new @attr_hash + end + + it "should return true when non-transient object attributes are eql?" do + @obj.should eql(@other) + end + + it "should return false when not all non-transient object attributes are eql?" do + @other.created_at = Time.now.to_s + @obj.should_not eql(@other) + end + + it "should return true when comparing same object to itself" do + @obj.should eql(@obj) + @other.should eql(@other) + end +end + +describe "Twitter::User#eql?" do + before(:each) do + @attr_hash = { :name => 'Elizabeth Jane Newson-Henshard', + :description => "Wronged 'Daughter'", + :location => 'Casterbridge', + :url => nil, + :id => 6748302, + :screen_name => 'mayors_daughter_or_was_she?' } + @obj = Twitter::User.new @attr_hash + @other = Twitter::User.new @attr_hash + end + + it "should return true when non-transient object attributes are eql?" do + @obj.should eql(@other) + end + + it "should return false when not all non-transient object attributes are eql?" do + @other.id = 1 + @obj.should_not eql(@other) + @obj.eql?(@other).should be(false) + end + + it "should return true when comparing same object to itself" do + @obj.should eql(@obj) + @other.should eql(@other) + end +end + +describe "Twitter::ClassUtilMixin#require_block" do + before(:each) do + class TestClass + include Twitter::ClassUtilMixin + end + @test_subject = TestClass.new + end + + it "should respond to :require_block" do + @test_subject.should respond_to(:require_block) + end + + it "should raise ArgumentError when block not given" do + lambda { + @test_subject.send(:require_block, false) + }.should raise_error(ArgumentError) + end + + it "should not raise ArgumentError when block is given" do + lambda { + @test_subject.send(:require_block, true) + }.should_not raise_error(ArgumentError) + end + + after(:each) do + @test_subject = nil + end +end diff --git a/lib/vendor/twitter/spec/twitter/ext/stdlib_spec.rb b/lib/vendor/twitter/spec/twitter/ext/stdlib_spec.rb new file mode 100644 index 0000000..f11e9b9 --- /dev/null +++ b/lib/vendor/twitter/spec/twitter/ext/stdlib_spec.rb @@ -0,0 +1,59 @@ +require File.join(File.dirname(__FILE__), '..', '..', 'spec_helper') + +describe Hash, "#to_http_str" do + before(:each) do + @http_params = {:id => 'otherlogin', :since_id => 3953743, :full_name => 'Lucy Cross'} + @id_regexp = Regexp.new("id=#{CGI.escape(@http_params[:id].to_s)}") + @since_id_regexp = Regexp.new("since_id=#{CGI.escape(@http_params[:since_id].to_s)}") + @full_name_regexp = Regexp.new("full_name=Lucy\\+Cross") + end + + it "should generate expected URL encoded string" do + http_str = @http_params.to_http_str + http_str.should match(@id_regexp) + http_str.should match(@since_id_regexp) + http_str.should match(@full_name_regexp) + end + + after(:each) do + @http_params = nil + @id_kv_str, @since_id_kv_str, @full_name_kv_str = nil + end +end + +describe Time, "#to_s" do + before(:each) do + @time = Time.now + @expected_string = @time.rfc2822 + end + + it "should output RFC2822 compliant string" do + time_string = @time.to_s + time_string.should eql(@expected_string) + end + + it "should respond to #old_to_s" do + @time.should respond_to(:old_to_s) + end + + after(:each) do + nilize(@time, @expected_string) + end +end + +# TODO: figure out how to stub the gem method to do what we want rather than this monstrousity. It is dependent on the system installation, which is always a bad thing. For now it will do so we can ship with 100% C0 coverage. +describe Kernel, "#gem_present?" do + before(:each) do + @present_gem = "rake" + @uninstalled_gem = "uninstalled-gem-crap" + end + + it "should return true when a gem isn't present on system" do + gem_present?(@present_gem).should eql(true) + end + + it "should return false when a gem isn't present on system" do + gem_present?(@uninstalled_gem).should eql(false) + end +end + diff --git a/lib/vendor/twitter/spec/twitter/extras_spec.rb b/lib/vendor/twitter/spec/twitter/extras_spec.rb new file mode 100644 index 0000000..b893f57 --- /dev/null +++ b/lib/vendor/twitter/spec/twitter/extras_spec.rb @@ -0,0 +1,46 @@ +require File.join(File.dirname(__FILE__), '..', 'spec_helper') + +describe Twitter::Client, "#featured(:users)" do + before(:each) do + @twitter = client_context + @uris = Twitter::Client.class_eval("@@FEATURED_URIS") + @request = mas_net_http_get(:basic_auth => nil) + @response = mas_net_http_response(:success) + @connection = mas_net_http(@response) + Net::HTTP.stub!(:new).and_return(@connection) + @users = [ + Twitter::User.new(:screen_name => 'twitter4r'), + Twitter::User.new(:screen_name => 'dictionary'), + ] + Twitter::User.stub!(:unmarshal).and_return(@users) + end + + it "should create expected HTTP GET request" do + @twitter.should_receive(:create_http_get_request).with(@uris[:users]).and_return(@request) + @twitter.featured(:users) + end + + it "should bless Twitter::User models returned" do + @twitter.should_receive(:bless_models).with(@users).and_return(@users) + @twitter.featured(:users) + end + + after(:each) do + nilize(@twitter, @uris, @request, @response, @connection) + end +end + +describe Twitter::User, ".featured" do + before(:each) do + @twitter = client_context + end + + it "should delegate #featured(:users) message to given client context" do + @twitter.should_receive(:featured).with(:users).and_return([]) + Twitter::User.featured(@twitter) + end + + after(:each) do + nilize(@twitter) + end +end diff --git a/lib/vendor/twitter/spec/twitter/meta_spec.rb b/lib/vendor/twitter/spec/twitter/meta_spec.rb new file mode 100644 index 0000000..773f61f --- /dev/null +++ b/lib/vendor/twitter/spec/twitter/meta_spec.rb @@ -0,0 +1,90 @@ +require File.join(File.dirname(__FILE__), '..', 'spec_helper') + +def glob_files(*path_elements) + Dir.glob(File.join(*path_elements)) +end + +def load_erb_yaml(path, context) + ryaml = ERB.new(File.read(path), 0) + YAML.load(ryaml.result(context)) +end + +module ERBMetaMixin + # Needed to make the YAML load work... + def project_files + glob_files(@root_dir, 'lib', '**/*.rb') + end + + # Needed to make the YAML load work... + def spec_files + glob_files(@root_dir, 'spec', '**/*_spec.rb') + end +end + +describe "Twitter::Meta cache policy" do + include ERBMetaMixin + before(:each) do + @root_dir = project_root_dir + @meta = Twitter::Meta.new(@root_dir) + @expected_pkg_info = load_erb_yaml(File.join(@root_dir, 'pkg-info.yml'), binding) + @expected_project_files = project_files + @expected_spec_files = spec_files + end + + it "should store value returned from project_files in @project_files after first glob" do + @meta.instance_eval("@project_files").should eql(nil) + @meta.project_files + @meta.instance_eval("@project_files").should eql(@expected_project_files) + @meta.project_files + @meta.instance_eval("@project_files").should eql(@expected_project_files) + end + + it "should store value returned from spec_files in @spec_files after first glob" do + @meta.instance_eval("@spec_files").should eql(nil) + @meta.spec_files + @meta.instance_eval("@spec_files").should eql(@expected_spec_files) + @meta.spec_files + @meta.instance_eval("@spec_files").should eql(@expected_spec_files) + end +end + +describe "Twitter::Meta" do + include ERBMetaMixin + before(:each) do + @root_dir = project_root_dir + @meta = Twitter::Meta.new(@root_dir) + @expected_yaml_hash = load_erb_yaml(File.join(@root_dir, 'pkg-info.yml'), binding) + @expected_project_files = project_files + @expected_spec_files = spec_files + end + + it "should load and return YAML file into Hash object upon #pkg_info call" do + yaml_hash = @meta.pkg_info + yaml_hash.should.eql? @expected_yaml_hash + end + + it "should return the embedded hash responding to key 'spec' of #pkg_info call upon #spec_info call" do + yaml_hash = @meta.spec_info + yaml_hash.should.eql? @expected_yaml_hash['spec'] + end + + it "should return list of files matching ROOT_DIR/lib/**/*.rb upon #project_files call" do + project_files = @meta.project_files + project_files.should.eql? @expected_project_files + end + + it "should return list of files matching ROOT_DIR/spec/**/*.rb upon #spec_files call" do + spec_files = @meta.spec_files + spec_files.should.eql? @expected_spec_files + end + + it "should return Gem specification based on YAML file contents and #project_files and #spec_files return values" do + spec = @meta.gem_spec + expected_spec_hash = @expected_yaml_hash['spec'] + expected_spec_hash.each do |key, val| + unless val.is_a?(Hash) + spec.send(key).should.eql? expected_spec_hash[key] + end + end + end +end diff --git a/lib/vendor/twitter/spec/twitter/model_spec.rb b/lib/vendor/twitter/spec/twitter/model_spec.rb new file mode 100644 index 0000000..6a8849b --- /dev/null +++ b/lib/vendor/twitter/spec/twitter/model_spec.rb @@ -0,0 +1,508 @@ +require File.join(File.dirname(__FILE__), '..', 'spec_helper') + +module Test + class Model + include Twitter::ModelMixin + end +end + +describe Twitter::Status, "unmarshaling" do + before(:each) do + @json_hash = { "text" => "Thinking Zipcar is lame...", + "id" => 46672912, + "user" => {"name" => "Angie", + "description" => "TV junkie...", + "location" => "NoVA", + "profile_image_url" => "http:\/\/assets0.twitter.com\/system\/user\/profile_image\/5483072\/normal\/eye.jpg?1177462492", + "url" => nil, + "id" => 5483072, + "protected" => false, + "screen_name" => "ang_410"}, + "created_at" => "Wed May 02 03:04:54 +0000 2007"} + @user = Twitter::User.new @json_hash["user"] + @status = Twitter::Status.new @json_hash + @status.user = @user + end + + it "should respond to unmarshal class method" do + Twitter::Status.should respond_to(:unmarshal) + end + + it "should return expected Twitter::Status object for singular case" do + status = Twitter::Status.unmarshal(JSON.unparse(@json_hash)) + status.should_not be(nil) + status.should eql(@status) + end + + it "should return expected array of Twitter::Status objects for plural case" do + statuses = Twitter::Status.unmarshal(JSON.unparse([@json_hash])) + statuses.should_not be(nil) + statuses.should have(1).entries + statuses.first.should eql(@status) + end +end + +describe Twitter::User, "unmarshaling" do + before(:each) do + @json_hash = { "name" => "Lucy Snowe", + "description" => "School Mistress Entrepreneur", + "location" => "Villette", + "url" => "http://villetteschoolforgirls.com", + "id" => 859303, + "protected" => true, + "screen_name" => "LucyDominatrix", } + @user = Twitter::User.new @json_hash + end + + it "should respond to unmarshal class method" do + Twitter::User.should respond_to(:unmarshal) + end + + it "should return expected arry of Twitter::User objects for plural case" do + users = Twitter::User.unmarshal(JSON.unparse([@json_hash])) + users.should have(1).entries + users.first.should eql(@user) + end + + it "should return expected Twitter::User object for singular case" do + user = Twitter::User.unmarshal(JSON.unparse(@json_hash)) + user.should_not be(nil) + user.should eql(@user) + end +end + +describe "Twitter::ModelMixin#to_hash" do + before(:all) do + class Model + include Twitter::ModelMixin + @@ATTRIBUTES = [:id, :name, :value, :unused_attr] + attr_accessor *@@ATTRIBUTES + def self.attributes; @@ATTRIBUTES; end + end + + class Hash + def eql?(other) + return false unless other # trivial nil case. + return false unless self.keys.eql?(other.keys) + self.each do |key,val| + return false unless self[key].eql?(other[key]) + end + true + end + end + end + + before(:each) do + @attributes = {:id => 14, :name => 'State', :value => 'Illinois'} + @model = Model.new(@attributes) + end + + it "should return expected hash representation of given model object" do + @model.to_hash.should eql(@attributes) + end + + after(:each) do + nilize(@attributes, @model) + end +end + +describe Twitter::User, ".find" do + before(:each) do + @twitter = Twitter::Client.from_config 'config/twitter.yml' + @id = 2423423 + @screen_name = 'ascreenname' + @expected_user = Twitter::User.new(:id => @id, :screen_name => @screen_name) + end + + it "should invoke given Twitter::Client's #user method with expected arguments" do + # case where id => @id + @twitter.should_receive(:user).with(@id).and_return(@expected_user) + user = Twitter::User.find(@id, @twitter) + user.should eql(@expected_user) + + # case where id => @screen_name, which is also valid + @twitter.should_receive(:user).with(@screen_name).and_return(@expected_user) + user = Twitter::User.find(@screen_name, @twitter) + user.should eql(@expected_user) + end + + after(:each) do + nilize(@twitter, @id, @screen_name, @expected_user) + end +end + +describe Twitter::Status, ".find" do + before(:each) do + @twitter = Twitter::Client.from_config 'config/twitter.yml' + @id = 9439843 + @text = 'My crummy status message' + @user = Twitter::User.new(:id => @id, :screen_name => @screen_name) + @expected_status = Twitter::Status.new(:id => @id, :text => @text, :user => @user) + end + + it "should invoke given Twitter::Client's #status method with expected arguments" do + @twitter.should_receive(:status).with(:get, @id).and_return(@expected_status) + status = Twitter::Status.find(@id, @twitter) + status.should eql(@expected_status) + end + + after(:each) do + nilize(@twitter, @id, @text, @user, @expected_status) + end +end + +describe Test::Model, "#bless" do + before(:each) do + @twitter = Twitter::Client.from_config('config/twitter.yml') + @model = Test::Model.new + end + + it "should delegate to #basic_bless" do + @model.should_receive(:basic_bless).and_return(@twitter) + @model.bless(@twitter) + end + + it "should set client attribute of self" do + @model.should_receive(:client=).once + @model.bless(@twitter) + end + + after(:each) do + nilize(@model, @twitter) + end +end + +describe Twitter::User, "#is_me?" do + before(:each) do + @twitter = Twitter::Client.from_config('config/twitter.yml') + @user_not_me = Twitter::User.new(:screen_name => 'notmylogin') + @user_me = Twitter::User.new(:screen_name => @twitter.instance_eval("@login")) + @user_not_me.bless(@twitter) + @user_me.bless(@twitter) + end + + it "should return true when Twitter::User object represents authenticated user of client context" do + @user_me.is_me?.should be_true + end + + it "should return false when Twitter::User object does not represent authenticated user of client context" do + @user_not_me.is_me?.should be_false + end + + after(:each) do + nilize(@twitter, @user_not_me, @user_me) + end +end + +describe Twitter::User, "#friends" do + before(:each) do + @twitter = Twitter::Client.from_config('config/twitter.yml') + @id = 5701682 + @user = Twitter::User.new(:id => @id, :screen_name => 'twitter4r') + @user.bless(@twitter) + end + + it "should delegate to @client.user(@id, :friends)" do + @twitter.should_receive(:user).with(@id, :friends) + @user.friends + end + + after(:each) do + nilize(@twitter, @id, @user) + end +end + +describe Twitter::User, "#followers" do + before(:each) do + @twitter = Twitter::Client.from_config('config/twitter.yml') + @id = 5701682 + @user = Twitter::User.new(:id => @id, :screen_name => 'twitter4r') + @user.bless(@twitter) + end + + it "should delegate to @client.my(:followers)" do + @twitter.should_receive(:my).with(:followers, {}) + @user.followers + end + + after(:each) do + nilize(@twitter, @id, @user) + end +end + +describe Test::Model, "#to_i" do + before(:each) do + @id = 234324285 + class Test::Model + attr_accessor :id + end + @model = Test::Model.new(:id => @id) + end + + it "should return @id attribute" do + @model.to_i.should eql(@id) + end + + after(:each) do + nilize(@model, @id) + end +end + +describe Test::Model, "#to_s" do + before(:each) do + class Test::Model + attr_accessor :text + end + @text = 'Some text for the message body here' + @model = Test::Model.new(:text => @text) + end + + it "should return expected text when a @text attribute exists for the model" do + @model.to_s.should eql(@text) + end + + after(:each) do + nilize(@model) + end +end + +describe Twitter::Message, ".find" do + it "should raise NotImplementedError due to Twitter (as opposed to Twitter4R) API limitation" do + lambda { + Twitter::Message.find(123, nil) + }.should raise_error(NotImplementedError) + end +end + +describe Twitter::Status, ".create" do + before(:each) do + @twitter = client_context + @text = 'My status update' + @status = Twitter::Status.new(:text => @text, :client => @twitter) + end + + it "should invoke #status(:post, text) on client context given" do + @twitter.should_receive(:status).with(:post, @text).and_return(@status) + Twitter::Status.create(:text => @text, :client => @twitter) + end + + it "should raise an ArgumentError when no client is given in params" do + lambda { + Twitter::Status.create(:text => @text) + }.should raise_error(ArgumentError) + end + + it "should raise an ArgumentError when no text is given in params" do + @twitter.should_receive(:is_a?).with(Twitter::Client) + lambda { + Twitter::Status.create(:client => @twitter) + }.should raise_error(ArgumentError) + end + + it "should raise an ArgumentError when text given in params is not a String" do + lambda { + Twitter::Status.create(:client => @twitter, :text => 234493) + }.should raise_error(ArgumentError) + end + + it "should raise an ArgumentError when client context given in params is not a Twitter::Client object" do + lambda { + Twitter::Status.create(:client => 'a string instead of a Twitter::Client', :text => @text) + }.should raise_error(ArgumentError) + end + + after(:each) do + nilize(@twitter, @text, @status) + end +end + +describe Twitter::Message, ".create" do + before(:each) do + @twitter = client_context + @text = 'Just between you and I, Lantana and Gosford Park are two of my favorite movies' + @recipient = Twitter::User.new(:id => 234958) + @message = Twitter::Message.new(:text => @text, :recipient => @recipient) + end + + it "should invoke #message(:post, text, recipient) on client context given" do + @twitter.should_receive(:message).with(:post, @text, @recipient).and_return(@message) + Twitter::Message.create(:client => @twitter, :text => @text, :recipient => @recipient) + end + + it "should raise an ArgumentError if no client context is given in params" do + lambda { + Twitter::Message.create(:text => @text, :recipient => @recipient) + }.should raise_error(ArgumentError) + end + + it "should raise an ArgumentError if client conext given in params is not a Twitter::Client object" do + lambda { + Twitter::Message.create( + :client => 3.14159, + :text => @text, + :recipient => @recipient) + }.should raise_error(ArgumentError) + end + + it "should raise an ArgumentError if no text is given in params" do + @twitter.should_receive(:is_a?).with(Twitter::Client) + lambda { + Twitter::Message.create( + :client => @twitter, + :recipient => @recipient) + }.should raise_error(ArgumentError) + end + + it "should raise an ArgumentError if text given in params is not a String" do + @twitter.should_receive(:is_a?).with(Twitter::Client) + lambda { + Twitter::Message.create( + :client => @twitter, + :text => Object.new, + :recipient => @recipient) + }.should raise_error(ArgumentError) + end + + it "should raise an ArgumentError if no recipient is given in params" do + @text.should_receive(:is_a?).with(String) + lambda { + Twitter::Message.create( + :client => @twitter, + :text => @text) + }.should raise_error(ArgumentError) + end + + it "should raise an ArgumentError if recipient given in params is not a Twitter::User, Integer or String object" do + @text.should_receive(:is_a?).with(String) + lambda { + Twitter::Message.create( + :client => @twitter, + :text => @text, + :recipient => 3.14159) + }.should raise_error(ArgumentError) + end + + after(:each) do + nilize(@twitter, @text, @recipient, @message) + end +end + +describe Twitter::User, "#befriend" do + before(:each) do + @twitter = client_context + @user = Twitter::User.new( + :id => 1234, + :screen_name => 'mylogin', + :client => @twitter) + @friend = Twitter::User.new( + :id => 5678, + :screen_name => 'friend', + :client => @twitter) + end + + it "should invoke #friend(:add, user) on client context" do + @twitter.should_receive(:friend).with(:add, @friend).and_return(@friend) + @user.befriend(@friend) + end + + after(:each) do + nilize(@twitter, @user, @friend) + end +end + +describe Twitter::User, "#defriend" do + before(:each) do + @twitter = client_context + @user = Twitter::User.new( + :id => 1234, + :screen_name => 'mylogin', + :client => @twitter) + @friend = Twitter::User.new( + :id => 5678, + :screen_name => 'friend', + :client => @twitter) + end + + it "should invoke #friend(:remove, user) on client context" do + @twitter.should_receive(:friend).with(:remove, @friend).and_return(@friend) + @user.defriend(@friend) + end + + after(:each) do + nilize(@twitter, @user, @friend) + end +end + +describe Twitter::Status, "#reply?" do + before(:each) do + @status = Twitter::Status.new( + :id => 123456789, + :text => "Wazzup?", + :user => mock(Twitter::User) + ) + @reply = Twitter::Status.new( + :text => "No much bro. You?", + :user => mock(Twitter::User), + :in_reply_to_status_id => @status.id + ) + end + + it "should return false for the original status" do + @status.reply?.should be(false) + end + + it "should return true for the reply to the original status" do + @reply.reply?.should be(true) + end +end + +describe Twitter::Status, "#reply(status_text)" do + before(:each) do + @twitter = client_context + @status = Twitter::Status.new( + :id => 1234, + :text => "The status text", + :client => @twitter) + @reply_text = "Reply text goes here" + @reply_status = Twitter::Status.new() + end + + it "should invoke #status(:reply, :status => ..., :in_reply_to_status_id => ...) on client context" do + @twitter.should_receive(:status).with(:reply, :status => @reply_text, :in_reply_to_status_id => @status.id).and_return(@reply_status) + @status.reply(@reply_text) + end + + after(:each) do + nilize(@twitter, @status) + end +end + +describe Twitter::Status, "#to_s" do + before(:each) do + @text = 'Aloha' + @status = Twitter::Status.new(:text => @text) + end + + it "should render text attribute" do + @status.to_s.should be(@text) + end + + after(:each) do + nilize(@text, @status) + end +end + +describe Twitter::Message, "#to_s" do + before(:each) do + @text = 'Aloha' + @message = Twitter::Message.new(:text => @text) + end + + it "should render text attribute" do + @message.to_s.should be(@text) + end + + after(:each) do + nilize(@text, @message) + end +end diff --git a/lib/vendor/twitter/spec/twitter/version_spec.rb b/lib/vendor/twitter/spec/twitter/version_spec.rb new file mode 100644 index 0000000..075c792 --- /dev/null +++ b/lib/vendor/twitter/spec/twitter/version_spec.rb @@ -0,0 +1,19 @@ +require File.join(File.dirname(__FILE__), '..', 'spec_helper') + +VERSION_LIST = [Twitter::Version::MAJOR, Twitter::Version::MINOR, Twitter::Version::REVISION] + +EXPECTED_VERSION = VERSION_LIST.join('.') +EXPECTED_NAME = VERSION_LIST.join('_') + +describe Twitter::Version, ".to_version" do + it "should return #{EXPECTED_VERSION}" do + Twitter::Version.to_version.should eql(EXPECTED_VERSION) + end +end + +describe Twitter::Version, ".to_name" do + it "should return #{EXPECTED_NAME}" do + Twitter::Version.to_name.should eql(EXPECTED_NAME) + end +end + diff --git a/test/mock/twitter.rb b/test/mock/twitter.rb index ad69c14..1f63adf 100644 --- a/test/mock/twitter.rb +++ b/test/mock/twitter.rb @@ -1,4 +1,3 @@ -gem 'twitter4r', '>=0.3.0' require 'twitter' class Twitter::Client