Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,6 @@ marketing/samples/twitter4rails.post-0_2_4/log
marketing/samples/twitter4rails.post-0_2_4/tmp
twitter*.gem
coverage.data
coverage
gemfile.lock
*.DS_Store
2 changes: 1 addition & 1 deletion Gemfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
source :gemcutter

gem "oauth", ">=0.4.1"
gem "oauth", ">=0.4.5"
gem "rake"

if RUBY_VERSION < "1.9.0"
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ Code:
* Filipe Giusti <filipegiusti at gmail dot com> - fixed users/show issue that Twitter.com changed from under us, also inspired the v0.5.2 bugfix release by submitting great issue example code.
* Seth Cousins <seth.cousins at gmail dot com> - added HTTP timeout option and provided a patch that inspired the OAuth support for Twitter4R
* John McKerrell <@mcknut on twitter> - added geo attribute to Twitter::Message.
* domrout on GitHub - added Tweet Entities.

Design Suggestions:

Expand Down
8 changes: 4 additions & 4 deletions bin/t4rsh
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,19 @@

require("irb")
require("irb/completion")
require("rubygems")

begin
gem('twitter4r', '>0.3.0')
gem('twitter4r', '>=0.5.0')
require("twitter")
require("twitter/console")
rescue Gem::LoadError
begin
gem("mbbx6spp-twitter4r", '>=0.3.1')
require("rubygems")
gem("twitter4r", '>=0.5.0')
require("twitter")
require("twitter/console")
rescue Gem::LoadError
abort("Error: You must install either twitter4r gem from Rubyforge with version 0.3.1 or greater or the mbbx6spp-twitter4r gem from GitHub's servers with version 0.3.1 or greater (and make sure it is a recent version of the gem).")
abort("Error: You must install a twitter4r gem of version 0.5.0 or greater.")
end
end

Expand Down
115 changes: 83 additions & 32 deletions lib/twitter/client/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,31 @@ def search_oauth_connect(method, path, params = {}, headers = {}, require_auth =
response
end

# Returns the response of the OAuth/HTTP(s) request for Media/Upload API requests
def media_oauth_connect(method, path, params = {}, headers = {}, require_auth = true)
atoken = media_access_token
uri = media_request_uri(path, params)
puts uri
body = ::Twitter::MultiPartBody.new(params.delete(:parts))
headers['Accept'] = "application/json"
headers['Content-Type'] = "multipart/form-data; boundary=#{body.send(:boundary)}"
headers['Content-Length'] = body.to_s.bytesize.to_s
puts body
response = atoken.send(method, uri, body.to_s, http_header.merge(headers))
handle_rest_response(response)
response
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

Expand All @@ -58,7 +73,7 @@ def rest_consumer
cfg = self.class.config
key ||= cfg.oauth_consumer_token
secret ||= cfg.oauth_consumer_secret
@rest_consumer = OAuth::Consumer.new(key, secret,
@rest_consumer = OAuth::Consumer.new(key, secret,
:site => construct_site_url,
:proxy => construct_proxy_url)
http = @rest_consumer.http
Expand All @@ -67,32 +82,18 @@ def rest_consumer
@rest_consumer
end

def rest_access_token
unless @rest_access_token
access = @oauth_access
if access
key = access[:key] || access["key"]
secret = access[:secret] || access["secret"]
else
raise Error, "No access tokens are set"
end
@rest_access_token = OAuth::AccessToken.new(rest_consumer, key, secret)
end
@rest_access_token
end

def search_consumer
unless @search_consumer
cfg = self.class.config
consumer = @oauth_consumer
if consumer
key = consumer[:key] || consumer["key"]
key = consumer[:key] || consumer["key"]
secret = consumer[:secret] || consumer["secret"]
end
cfg = self.class.config
key ||= cfg.oauth_consumer_token
secret ||= cfg.oauth_consumer_secret
@search_consumer = OAuth::Consumer.new(key, secret,
@search_consumer = OAuth::Consumer.new(key, secret,
:site => construct_site_url(:search),
:proxy => construct_proxy_url)
http = @search_consumer.http
Expand All @@ -101,6 +102,40 @@ def search_consumer
@search_consumer
end

def media_consumer
unless @media_consumer
cfg = self.class.config
consumer = @oauth_consumer
if consumer
key = consumer[:key] || consumer["key"]
secret = consumer[:secret] || consumer["secret"]
end
cfg = self.class.config
key ||= cfg.oauth_consumer_token
secret ||= cfg.oauth_consumer_secret
@media_consumer = OAuth::Consumer.new(key, secret,
:site => construct_site_url(:media),
:proxy => construct_proxy_url)
http = @media_consumer.http
http.read_timeout = cfg.timeout
end
@media_consumer
end

def rest_access_token
unless @rest_access_token
access = @oauth_access
if access
key = access[:key] || access["key"]
secret = access[:secret] || access["secret"]
@rest_access_token = OAuth::AccessToken.new(rest_consumer, key, secret)
else
raise ::Twitter::Twitter4rError.new(:message => "No access tokens are set")
end
end
@rest_access_token
end

def search_access_token
unless @search_access_token
key = @oauth_access[:key] || @oauth_access["key"]
Expand All @@ -110,28 +145,37 @@ def search_access_token
@search_access_token
end

def media_access_token
unless @media_access_token
key = @oauth_access[:key] || @oauth_access["key"]
secret = @oauth_access[:secret] || @oauth_access["secret"]
@media_access_token = OAuth::AccessToken.new(media_consumer, key, secret)
end
@media_access_token
end

def raise_rest_error(response, uri = nil)
map = JSON.parse(response.body)
error = Twitter::RESTError.registry[response.code]
raise error.new(:code => response.code,
raise error.new(:code => response.code,
:message => response.message,
:error => map["error"],
:uri => uri)
:uri => uri)
end

def handle_rest_response(response, uri = nil)
unless response.is_a?(Net::HTTPSuccess)
raise_rest_error(response, uri)
end
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
# 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 ||= {
@@http_header ||= {
'User-Agent' => "Twitter4R v#{Twitter::Version.to_version} [#{self.class.config.user_agent}]",
'Accept' => 'text/x-json',
'Accept' => 'application/json',
'X-Twitter-Client' => self.class.config.application_name,
'X-Twitter-Client-Version' => self.class.config.application_version,
'X-Twitter-Client-URL' => self.class.config.application_url,
Expand All @@ -144,21 +188,28 @@ def rest_request_uri(path, params = nil)
uri << "?#{params.to_http_str}" if params
uri
end

def search_request_uri(path, params = nil)
uri = "#{self.class.config.search_path_prefix}#{path}"
uri << "?#{params.to_http_str}" if params
uri
end


def media_request_uri(path, params = nil)
"#{self.class.config.media_path_prefix}#{path}"
end

def uri_components(service = :rest)
case service
when :rest
return self.class.config.protocol, self.class.config.host, self.class.config.port,
self.class.config.path_prefix
return self.class.config.protocol, self.class.config.host,
self.class.config.port, self.class.config.path_prefix
when :search
return self.class.config.search_protocol, self.class.config.search_host,
return self.class.config.search_protocol, self.class.config.search_host,
self.class.config.search_port, self.class.config.search_path_prefix
when :media
return self.class.config.media_protocol, self.class.config.media_host,
self.class.config.media_port, self.class.config.media_path_prefix
end
end

Expand Down
38 changes: 29 additions & 9 deletions lib/twitter/client/status.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,41 +2,44 @@ class Twitter::Client
@@STATUS_URIS = {
:get => '/statuses/show.json',
:post => '/statuses/update.json',
:post_multipart => '/statuses/update_with_media.json',
:delete => '/statuses/destroy.json',
:reply => '/statuses/update.json'
}

# Provides access to individual statuses via Twitter's Status APIs
#
#
# <tt>action</tt> can be of the following values:
# * <tt>:get</tt> to retrieve status content. Assumes <tt>value</tt> given responds to :to_i message in meaningful way to yield intended status id.
# * <tt>:post</tt> to publish a new status
# * <tt>:delete</tt> to remove an existing status. Assumes <tt>value</tt> given responds to :to_i message in meaningful way to yield intended status id.
# * <tt>:reply</tt> to reply to an existing status. Assumes <tt>value</tt> given is <tt>Hash</tt> which contains <tt>:in_reply_to_status_id</tt> and <tt>:status</tt>
#
#
# <tt>value</tt> should be set to:
# * the status identifier for <tt>:get</tt> case
# * the status text message for <tt>:post</tt> case
# * none necessary for <tt>:delete</tt> case
#
#
# Examples:
# twitter.status(:get, 107786772)
# twitter.status(:post, "New Ruby open source project Twitter4R version 0.2.0 released.")
# twitter.status(:delete, 107790712)
# twitter.status(:reply, :in_reply_to_status_id => 1390482942342, :status => "@t4ruby This new v0.7.0 release is da bomb! #ruby #twitterapi #twitter4r")
# twitter.status(:post, "My brand new status in all its glory here tweeted from Greenwich (the real one). #withawesomehashtag #booyah", :lat => 0, :long => 0)
#
# An <tt>ArgumentError</tt> will be raised if an invalid <tt>action</tt>
# twitter.status(:post, :media => {:filename => "awesome_photo.png", :content_type => "image/png"}, :status => "My brand new status in all its glory here tweeted from Greenwich (the real one). #withawesomehashtag #booyah", :lat => 0, :long => 0)
#
# An <tt>ArgumentError</tt> will be raised if an invalid <tt>action</tt>
# is given. Valid actions are:
# * +:get+
# * +:post+
# * +:delete+
#
# The third argument +options+ sends on a Hash to the Twitter API with the following keys allowed:
# The options argument sends on a Hash to the Twitter API with the following keys allowed:
# * +:lat+ - latitude (for posting geolocation)
# * +:long+ - longitude (for posting geolocation)
# * +:place_id+ - using a place ID give by geo/reverse_geocode
# * +:display_coordinates+ - whether or not to put a pin in the exact coordinates
# * +:media+ - media enclosures to upload and embed in status
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)
Expand All @@ -47,14 +50,31 @@ def status(action, value = nil)
when :get
response = rest_oauth_connect(:get, uri, {:id => value.to_i})
when :post
if value.is_a?(Hash)
if value.is_a?(Hash) && value.key?(:media)
params = {
:parts => [
Twitter::MediaPart.new(value[:media].merge(:name => "media[]")),
Twitter::MediaPart.new(
:body => value[:status],
:content_type => "text/plain",
:name => "status")
]
}
action = :post_multipart
response = media_oauth_connect(:post, @@STATUS_URIS[action], params)
elsif value.is_a?(Hash)
params = value.delete_if { |k, v|
![:status, :lat, :long, :place_id, :display_coordinates].member?(k)
}
action = :post
response = rest_oauth_connect(action, uri,
params.merge(:source => self.class.config.source))
else
params = {:status => value}
action = :post
response = rest_oauth_connect(action, uri,
params.merge(:source => self.class.config.source))
end
response = rest_oauth_connect(:post, uri, params.merge(:source => self.class.config.source))
when :delete
response = rest_oauth_connect(:delete, uri, {:id => value.to_i})
when :reply
Expand Down
Loading