assistly 0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gemtest +0 -0
- data/.gitignore +11 -0
- data/.rspec +3 -0
- data/.travis.yml +7 -0
- data/.yardopts +9 -0
- data/Gemfile +12 -0
- data/HISTORY.mkd +4 -0
- data/LICENSE.mkd +20 -0
- data/README.mkd +121 -0
- data/Rakefile +23 -0
- data/assistly.gemspec +43 -0
- data/lib/assistly.rb +26 -0
- data/lib/assistly/api.rb +23 -0
- data/lib/assistly/authentication.rb +25 -0
- data/lib/assistly/client.rb +25 -0
- data/lib/assistly/client/case.rb +51 -0
- data/lib/assistly/client/customer.rb +108 -0
- data/lib/assistly/client/interaction.rb +41 -0
- data/lib/assistly/client/user.rb +38 -0
- data/lib/assistly/client/utils.rb +83 -0
- data/lib/assistly/configuration.rb +104 -0
- data/lib/assistly/connection.rb +39 -0
- data/lib/assistly/error.rb +62 -0
- data/lib/assistly/request.rb +44 -0
- data/lib/assistly/search.rb +473 -0
- data/lib/assistly/version.rb +4 -0
- data/lib/faraday/request/multipart_with_file.rb +30 -0
- data/lib/faraday/response/raise_http_4xx.rb +45 -0
- data/lib/faraday/response/raise_http_5xx.rb +24 -0
- data/spec/assistly/api_spec.rb +70 -0
- data/spec/assistly/client/case_spec.rb +88 -0
- data/spec/assistly/client/customer_spec.rb +158 -0
- data/spec/assistly/client/interaction_spec.rb +58 -0
- data/spec/assistly/client/user_spec.rb +58 -0
- data/spec/assistly/client_spec.rb +10 -0
- data/spec/assistly_spec.rb +99 -0
- data/spec/faraday/response_spec.rb +34 -0
- data/spec/fixtures/case.json +59 -0
- data/spec/fixtures/case_update.json +59 -0
- data/spec/fixtures/cases.json +182 -0
- data/spec/fixtures/customer.json +58 -0
- data/spec/fixtures/customer_create.json +56 -0
- data/spec/fixtures/customer_create_email.json +15 -0
- data/spec/fixtures/customer_update.json +47 -0
- data/spec/fixtures/customer_update_email.json +15 -0
- data/spec/fixtures/customers.json +98 -0
- data/spec/fixtures/interaction_create.json +126 -0
- data/spec/fixtures/interactions.json +139 -0
- data/spec/fixtures/user.json +15 -0
- data/spec/fixtures/users.json +24 -0
- data/spec/helper.rb +53 -0
- metadata +391 -0
@@ -0,0 +1,41 @@
|
|
1
|
+
module Assistly
|
2
|
+
class Client
|
3
|
+
# Defines methods related to interactions
|
4
|
+
module Interaction
|
5
|
+
|
6
|
+
# Returns extended information of up to 100 interactions
|
7
|
+
#
|
8
|
+
# @option options [Boolean, String, Integer]
|
9
|
+
# @example Return extended information for 12345
|
10
|
+
# Assistly.interactions(:since_id => 12345)
|
11
|
+
# Assistly.interactions(:since_id => 12345, :count => 5)
|
12
|
+
# @format :json
|
13
|
+
# @authenticated true
|
14
|
+
# @see http://dev.assistly.com/docs/api/interactions
|
15
|
+
def interactions(*args)
|
16
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
17
|
+
response = get("interactions",options)
|
18
|
+
response['results']
|
19
|
+
end
|
20
|
+
|
21
|
+
# Creates an interaction
|
22
|
+
#
|
23
|
+
# @format :json
|
24
|
+
# @authenticated true
|
25
|
+
# @rate_limited true
|
26
|
+
# @return [Array] The requested users.
|
27
|
+
# @see http://dev.assistly.com/docs/api/interactions/create
|
28
|
+
# @example Create a new interaction
|
29
|
+
# Assistly.create_interaction(:interaction_subject => "this is an api test", :customer_email => "foo@example.com")
|
30
|
+
def create_interaction(*args)
|
31
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
32
|
+
response = post('interactions', options)
|
33
|
+
if response['success']
|
34
|
+
return response['results']
|
35
|
+
else
|
36
|
+
return response['errors']
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Assistly
|
2
|
+
class Client
|
3
|
+
# Defines methods related to users
|
4
|
+
module User
|
5
|
+
# Returns extended information of a given user
|
6
|
+
#
|
7
|
+
# @overload user(user, options={})
|
8
|
+
# @param user [Integer] An Assitely user ID
|
9
|
+
# @option options [Boolean, String, Integer] :include_entities Include {http://dev.twitter.com/pages/tweet_entities Tweet Entities} when set to true, 't' or 1.
|
10
|
+
# @return [Hashie::Mash] The requested user.
|
11
|
+
# @example Return extended information for 12345
|
12
|
+
# Assistly.user(12345)
|
13
|
+
# @format :json, :xml
|
14
|
+
# @authenticated true
|
15
|
+
# @see http://dev.assistly.com/docs/api/users/show
|
16
|
+
def user(id,*args)
|
17
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
18
|
+
response = get("users/#{id}",options)
|
19
|
+
response.user
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns extended information for up to 100 users
|
23
|
+
#
|
24
|
+
# @format :json, :xml
|
25
|
+
# @authenticated true
|
26
|
+
# @rate_limited true
|
27
|
+
# @return [Array] The requested users.
|
28
|
+
# @see http://dev.assistly.com/docs/api/users
|
29
|
+
# @example Return extended information account users
|
30
|
+
# Assistly.users
|
31
|
+
def users(*args)
|
32
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
33
|
+
response = get('users', options)
|
34
|
+
response['results'].collect{|result| result.user}
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
module Assistly
|
3
|
+
class Client
|
4
|
+
# @private
|
5
|
+
module Utils
|
6
|
+
private
|
7
|
+
|
8
|
+
# Returns the configured screen name or the screen name of the authenticated user
|
9
|
+
#
|
10
|
+
# @return [String]
|
11
|
+
def get_screen_name
|
12
|
+
@screen_name ||= self.verify_credentials.screen_name
|
13
|
+
end
|
14
|
+
|
15
|
+
# Take a single user ID or screen name and merge it into an options hash with the correct key
|
16
|
+
#
|
17
|
+
# @param user_id_or_screen_name [Integer, String] A Twitter user ID or screen_name.
|
18
|
+
# @param options [Hash] A customizable set of options.
|
19
|
+
# @return [Hash]
|
20
|
+
def merge_user_into_options!(user_id_or_screen_name, options={})
|
21
|
+
case user_id_or_screen_name
|
22
|
+
when Fixnum
|
23
|
+
options[:user_id] = user_id_or_screen_name
|
24
|
+
when String
|
25
|
+
options[:screen_name] = user_id_or_screen_name
|
26
|
+
end
|
27
|
+
options
|
28
|
+
end
|
29
|
+
|
30
|
+
# Take a multiple user IDs and screen names and merge them into an options hash with the correct keys
|
31
|
+
#
|
32
|
+
# @param users_id_or_screen_names [Array] An array of Twitter user IDs or screen_names.
|
33
|
+
# @param options [Hash] A customizable set of options.
|
34
|
+
# @return [Hash]
|
35
|
+
def merge_users_into_options!(user_ids_or_screen_names, options={})
|
36
|
+
user_ids, screen_names = [], []
|
37
|
+
user_ids_or_screen_names.flatten.each do |user_id_or_screen_name|
|
38
|
+
case user_id_or_screen_name
|
39
|
+
when Fixnum
|
40
|
+
user_ids << user_id_or_screen_name
|
41
|
+
when String
|
42
|
+
screen_names << user_id_or_screen_name
|
43
|
+
end
|
44
|
+
end
|
45
|
+
options[:user_id] = user_ids.join(',') unless user_ids.empty?
|
46
|
+
options[:screen_name] = screen_names.join(',') unless screen_names.empty?
|
47
|
+
options
|
48
|
+
end
|
49
|
+
|
50
|
+
# Take a single owner ID or owner screen name and merge it into an options hash with the correct key
|
51
|
+
# (for Twitter API endpoints that want :owner_id and :owner_screen_name)
|
52
|
+
#
|
53
|
+
# @param owner_id_or_owner_screen_name [Integer, String] A Twitter user ID or screen_name.
|
54
|
+
# @param options [Hash] A customizable set of options.
|
55
|
+
# @return [Hash]
|
56
|
+
def merge_owner_into_options!(owner_id_or_owner_screen_name, options={})
|
57
|
+
case owner_id_or_owner_screen_name
|
58
|
+
when Fixnum
|
59
|
+
options[:owner_id] = owner_id_or_owner_screen_name
|
60
|
+
when String
|
61
|
+
options[:owner_screen_name] = owner_id_or_owner_screen_name
|
62
|
+
end
|
63
|
+
options
|
64
|
+
end
|
65
|
+
|
66
|
+
# Take a single list ID or slug and merge it into an options hash with the correct key
|
67
|
+
#
|
68
|
+
# @param list_id_or_slug [Integer, String] A Twitter list ID or slug.
|
69
|
+
# @param options [Hash] A customizable set of options.
|
70
|
+
# @return [Hash]
|
71
|
+
def merge_list_into_options!(list_id_or_screen_name, options={})
|
72
|
+
case list_id_or_screen_name
|
73
|
+
when Fixnum
|
74
|
+
options[:list_id] = list_id_or_screen_name
|
75
|
+
when String
|
76
|
+
options[:slug] = list_id_or_screen_name
|
77
|
+
end
|
78
|
+
options
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
require 'assistly/version'
|
3
|
+
|
4
|
+
module Assistly
|
5
|
+
# Defines constants and methods related to configuration
|
6
|
+
module Configuration
|
7
|
+
# An array of valid keys in the options hash when configuring a {Twitter::API}
|
8
|
+
VALID_OPTIONS_KEYS = [
|
9
|
+
:adapter,
|
10
|
+
:consumer_key,
|
11
|
+
:consumer_secret,
|
12
|
+
:endpoint,
|
13
|
+
:format,
|
14
|
+
:oauth_token,
|
15
|
+
:oauth_token_secret,
|
16
|
+
:proxy,
|
17
|
+
:subdomain,
|
18
|
+
:user_agent,
|
19
|
+
:version].freeze
|
20
|
+
|
21
|
+
# An array of valid request/response formats
|
22
|
+
#
|
23
|
+
# @note Not all methods support the XML format.
|
24
|
+
VALID_FORMATS = [
|
25
|
+
:json].freeze
|
26
|
+
|
27
|
+
# The adapter that will be used to connect if none is set
|
28
|
+
#
|
29
|
+
# @note The default faraday adapter is Net::HTTP.
|
30
|
+
DEFAULT_ADAPTER = Faraday.default_adapter
|
31
|
+
|
32
|
+
# By default, don't set an application key
|
33
|
+
DEFAULT_CONSUMER_KEY = nil
|
34
|
+
|
35
|
+
# By default, don't set an application secret
|
36
|
+
DEFAULT_CONSUMER_SECRET = nil
|
37
|
+
|
38
|
+
# The response format appended to the path and sent in the 'Accept' header if none is set
|
39
|
+
#
|
40
|
+
# @note JSON is preferred over XML because it is more concise and faster to parse.
|
41
|
+
DEFAULT_FORMAT = :json
|
42
|
+
|
43
|
+
# By default, don't set a user oauth token
|
44
|
+
DEFAULT_OAUTH_TOKEN = nil
|
45
|
+
|
46
|
+
# By default, don't set a user oauth secret
|
47
|
+
DEFAULT_OAUTH_TOKEN_SECRET = nil
|
48
|
+
|
49
|
+
# By default, don't use a proxy server
|
50
|
+
DEFAULT_PROXY = nil
|
51
|
+
|
52
|
+
# By default, don't set a subdomain
|
53
|
+
DEFAULT_SUBDOMAIN = "zencoder"
|
54
|
+
|
55
|
+
# The user agent that will be sent to the API endpoint if none is set
|
56
|
+
DEFAULT_USER_AGENT = "Assistly Ruby Gem #{Assistly::VERSION}".freeze
|
57
|
+
|
58
|
+
# The user agent that will be sent to the API endpoint if none is set
|
59
|
+
DEFAULT_VERSION = "v1".freeze
|
60
|
+
|
61
|
+
# The endpoint that will be used to connect if none is set
|
62
|
+
#
|
63
|
+
# @note This is configurable in case you want to use HTTP instead of HTTPS, specify a different API version, or use a Twitter-compatible endpoint.
|
64
|
+
# @see http://status.net/wiki/Twitter-compatible_API
|
65
|
+
# @see http://en.blog.wordpress.com/2009/12/12/twitter-api/
|
66
|
+
# @see http://staff.tumblr.com/post/287703110/api
|
67
|
+
# @see http://developer.typepad.com/typepad-twitter-api/twitter-api.html
|
68
|
+
DEFAULT_ENDPOINT = "https://#{DEFAULT_SUBDOMAIN}.assistly.com/api/#{DEFAULT_VERSION}/".freeze
|
69
|
+
|
70
|
+
# @private
|
71
|
+
attr_accessor *VALID_OPTIONS_KEYS
|
72
|
+
|
73
|
+
# When this module is extended, set all configuration options to their default values
|
74
|
+
def self.extended(base)
|
75
|
+
base.reset
|
76
|
+
end
|
77
|
+
|
78
|
+
# Convenience method to allow configuration options to be set in a block
|
79
|
+
def configure
|
80
|
+
yield self
|
81
|
+
end
|
82
|
+
|
83
|
+
# Create a hash of options and their values
|
84
|
+
def options
|
85
|
+
Hash[VALID_OPTIONS_KEYS.map {|key| [key, send(key)] }]
|
86
|
+
end
|
87
|
+
|
88
|
+
# Reset all configuration options to defaults
|
89
|
+
def reset
|
90
|
+
self.adapter = DEFAULT_ADAPTER
|
91
|
+
self.consumer_key = DEFAULT_CONSUMER_KEY
|
92
|
+
self.consumer_secret = DEFAULT_CONSUMER_SECRET
|
93
|
+
self.endpoint = DEFAULT_ENDPOINT
|
94
|
+
self.format = DEFAULT_FORMAT
|
95
|
+
self.oauth_token = DEFAULT_OAUTH_TOKEN
|
96
|
+
self.oauth_token_secret = DEFAULT_OAUTH_TOKEN_SECRET
|
97
|
+
self.proxy = DEFAULT_PROXY
|
98
|
+
self.subdomain = DEFAULT_SUBDOMAIN
|
99
|
+
self.user_agent = DEFAULT_USER_AGENT
|
100
|
+
self.version = DEFAULT_VERSION
|
101
|
+
self
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'faraday_middleware'
|
2
|
+
require 'faraday/request/multipart_with_file'
|
3
|
+
require 'faraday/response/raise_http_4xx'
|
4
|
+
require 'faraday/response/raise_http_5xx'
|
5
|
+
|
6
|
+
module Assistly
|
7
|
+
# @private
|
8
|
+
module Connection
|
9
|
+
private
|
10
|
+
|
11
|
+
def connection(raw=false)
|
12
|
+
options = {
|
13
|
+
:headers => {'Accept' => "application/#{format}", 'User-Agent' => user_agent},
|
14
|
+
:proxy => proxy,
|
15
|
+
:ssl => {:verify => false},
|
16
|
+
:url => api_endpoint,
|
17
|
+
}
|
18
|
+
|
19
|
+
Faraday.new(options) do |builder|
|
20
|
+
builder.use Faraday::Request::MultipartWithFile
|
21
|
+
builder.use Faraday::Request::OAuth, authentication if authenticated?
|
22
|
+
builder.use Faraday::Request::Multipart
|
23
|
+
builder.use Faraday::Request::UrlEncoded
|
24
|
+
builder.use Faraday::Response::RaiseHttp4xx
|
25
|
+
builder.use Faraday::Response::Rashify unless raw
|
26
|
+
unless raw
|
27
|
+
case format.to_s.downcase
|
28
|
+
when 'json'
|
29
|
+
builder.use Faraday::Response::ParseJson
|
30
|
+
when 'xml'
|
31
|
+
builder.use Faraday::Response::ParseXml
|
32
|
+
end
|
33
|
+
end
|
34
|
+
builder.use Faraday::Response::RaiseHttp5xx
|
35
|
+
builder.adapter(adapter)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Assistly
|
2
|
+
# Custom error class for rescuing from all Assistly errors
|
3
|
+
class Error < StandardError
|
4
|
+
attr_reader :http_headers
|
5
|
+
|
6
|
+
def initialize(message, http_headers)
|
7
|
+
http_headers ||= {}
|
8
|
+
@http_headers = Hash[http_headers]
|
9
|
+
super message
|
10
|
+
end
|
11
|
+
|
12
|
+
def ratelimit_reset
|
13
|
+
Time.at(@http_headers.values_at('x-ratelimit-reset', 'X-RateLimit-Reset').detect {|value| value }.to_i)
|
14
|
+
end
|
15
|
+
|
16
|
+
def ratelimit_limit
|
17
|
+
@http_headers.values_at('x-ratelimit-limit', 'X-RateLimit-Limit').detect {|value| value }.to_i
|
18
|
+
end
|
19
|
+
|
20
|
+
def ratelimit_remaining
|
21
|
+
@http_headers.values_at('x-ratelimit-limit', 'X-RateLimit-Limit').detect {|value| value }.to_i
|
22
|
+
end
|
23
|
+
|
24
|
+
def retry_after
|
25
|
+
[(ratelimit_reset - Time.now).ceil, 0].max
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Raised when Assistly returns the HTTP status code 400
|
30
|
+
class BadRequest < Error; end
|
31
|
+
|
32
|
+
# Raised when Assistly returns the HTTP status code 401
|
33
|
+
class Unauthorized < Error; end
|
34
|
+
|
35
|
+
# Raised when Assistly returns the HTTP status code 403
|
36
|
+
class Forbidden < Error; end
|
37
|
+
|
38
|
+
# Raised when Assistly returns the HTTP status code 404
|
39
|
+
class NotFound < Error; end
|
40
|
+
|
41
|
+
# Raised when Assistly returns the HTTP status code 406
|
42
|
+
class NotAcceptable < Error; end
|
43
|
+
|
44
|
+
# Raised when Assistly returns the HTTP status code 420
|
45
|
+
class EnhanceYourCalm < Error
|
46
|
+
# The number of seconds your application should wait before requesting date from the Search API again
|
47
|
+
#
|
48
|
+
# @see http://dev.Assistly.com/pages/rate-limiting
|
49
|
+
def retry_after
|
50
|
+
@http_headers.values_at('retry-after', 'Retry-After').detect {|value| value }.to_i
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Raised when Assistly returns the HTTP status code 500
|
55
|
+
class InternalServerError < Error; end
|
56
|
+
|
57
|
+
# Raised when Assistly returns the HTTP status code 502
|
58
|
+
class BadGateway < Error; end
|
59
|
+
|
60
|
+
# Raised when Assistly returns the HTTP status code 503
|
61
|
+
class ServiceUnavailable < Error; end
|
62
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Assistly
|
2
|
+
# Defines HTTP request methods
|
3
|
+
module Request
|
4
|
+
# Perform an HTTP GET request
|
5
|
+
def get(path, options={}, raw=false)
|
6
|
+
request(:get, path, options, raw)
|
7
|
+
end
|
8
|
+
|
9
|
+
# Perform an HTTP POST request
|
10
|
+
def post(path, options={}, raw=false)
|
11
|
+
request(:post, path, options, raw)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Perform an HTTP PUT request
|
15
|
+
def put(path, options={}, raw=false)
|
16
|
+
request(:put, path, options, raw)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Perform an HTTP DELETE request
|
20
|
+
def delete(path, options={}, raw=false)
|
21
|
+
request(:delete, path, options, raw)
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
# Perform an HTTP request
|
27
|
+
def request(method, path, options, raw=false)
|
28
|
+
response = connection(raw).send(method) do |request|
|
29
|
+
case method
|
30
|
+
when :get, :delete
|
31
|
+
request.url(formatted_path(path), options)
|
32
|
+
when :post, :put
|
33
|
+
request.path = formatted_path(path)
|
34
|
+
request.body = options unless options.empty?
|
35
|
+
end
|
36
|
+
end
|
37
|
+
raw ? response : response.body
|
38
|
+
end
|
39
|
+
|
40
|
+
def formatted_path(path)
|
41
|
+
[path, format].compact.join('.')
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,473 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
|
3
|
+
module Twitter
|
4
|
+
# Wrapper for the Twitter Search API
|
5
|
+
#
|
6
|
+
# @note As of April 1st 2010, the Search API provides an option to retrieve
|
7
|
+
# "popular tweets" in addition to real-time search results. In an upcoming release,
|
8
|
+
# this will become the default and clients that don't want to receive popular tweets
|
9
|
+
# in their search results will have to explicitly opt-out. See {Twitter::Search#result_type}
|
10
|
+
# for more information.
|
11
|
+
# @note The user ids in the Search API are different from those in the REST API
|
12
|
+
# ({http://dev.twitter.com/pages/api_overview about the two APIs}). This defect is
|
13
|
+
# being tracked by {http://code.google.com/p/twitter-api/issues/detail?id=214 Issue 214}.
|
14
|
+
# This means that the to_user_id and from_user_id field vary from the actualy user id on
|
15
|
+
# Twitter.com. Applications will have to perform a screen name-based lookup with
|
16
|
+
# {Twitter::Client::User#user} to get the correct user id if necessary.
|
17
|
+
# @see http://dev.twitter.com/doc/get/search Twitter Search API Documentation
|
18
|
+
class Search < API
|
19
|
+
include Enumerable
|
20
|
+
|
21
|
+
# @private
|
22
|
+
attr_reader :query
|
23
|
+
|
24
|
+
# Creates a new search
|
25
|
+
#
|
26
|
+
# @example Initialize a Twitter search
|
27
|
+
# search = Twitter::Search.new
|
28
|
+
def initialize(*)
|
29
|
+
clear
|
30
|
+
super
|
31
|
+
end
|
32
|
+
|
33
|
+
alias :api_endpoint :search_endpoint
|
34
|
+
|
35
|
+
# Clears all query filters and cached results
|
36
|
+
#
|
37
|
+
# @return [Twitter::Search] self
|
38
|
+
# @example Clear a search for "twitter"
|
39
|
+
# search = Twitter::Search.new
|
40
|
+
# search.containing("twitter").fetch
|
41
|
+
# search.clear
|
42
|
+
# search.fetch_next_page #=> 403 Forbidden: You must enter a query.
|
43
|
+
def clear
|
44
|
+
@cache = nil
|
45
|
+
@query = {}
|
46
|
+
@query[:q] = []
|
47
|
+
@query[:tude] = []
|
48
|
+
self
|
49
|
+
end
|
50
|
+
|
51
|
+
# @group Generic filters
|
52
|
+
|
53
|
+
# Search query
|
54
|
+
#
|
55
|
+
# @param query [String] The search query.
|
56
|
+
# @return [Twitter::Search] self
|
57
|
+
# @example Return an array of tweets containing "twitter"
|
58
|
+
# Twitter::Search.new.containing("twitter").fetch
|
59
|
+
# Twitter::Search.new.contains("twitter").fetch # Shortcut for the above
|
60
|
+
# Twitter::Search.new.q("twitter").fetch # Even shorter-cut
|
61
|
+
def containing(query)
|
62
|
+
@query[:q] << query
|
63
|
+
self
|
64
|
+
end
|
65
|
+
alias :contains :containing
|
66
|
+
alias :q :containing
|
67
|
+
|
68
|
+
# Negative search query
|
69
|
+
#
|
70
|
+
# @param query [String] The negative search query.
|
71
|
+
# @return [Twitter::Search] self
|
72
|
+
# @example Return an array of tweets containing "beer" but not "root"
|
73
|
+
# Twitter::Search.new.containing("beer").not_containing("root").fetch
|
74
|
+
# Twitter::Search.new.containing("beer").does_not_contain("root").fetch # Same as above
|
75
|
+
# Twitter::Search.new.containing("beer").excluding("root").fetch # Shortcut for the above
|
76
|
+
# Twitter::Search.new.contains("beer").excludes("root").fetch # Even shorter
|
77
|
+
# Twitter::Search.new.q("beer").exclude("root").fetch # Shorter still
|
78
|
+
def not_containing(query)
|
79
|
+
@query[:q] << "-#{query}"
|
80
|
+
self
|
81
|
+
end
|
82
|
+
alias :does_not_contain :not_containing
|
83
|
+
alias :excluding :not_containing
|
84
|
+
alias :excludes :not_containing
|
85
|
+
alias :exclude :not_containing
|
86
|
+
|
87
|
+
# Search for a specific phrase instead of a group of words
|
88
|
+
#
|
89
|
+
# @param phrase [String] The search phrase.
|
90
|
+
# @return [Twitter::Search] self
|
91
|
+
# @example Return an array of tweets containing the phrase "happy hour"
|
92
|
+
# Twitter::Search.new.phrase("happy hour").fetch
|
93
|
+
def phrase(phrase)
|
94
|
+
@query[:phrase] = phrase
|
95
|
+
self
|
96
|
+
end
|
97
|
+
|
98
|
+
# Only include tweets that contain URLs
|
99
|
+
#
|
100
|
+
# @param filter [String] A type of search filter. Only 'links' is currently effective.
|
101
|
+
# @return [Twitter::Search] self
|
102
|
+
# @example Return an array of tweets containing "twitter" and URLs
|
103
|
+
# Twitter::Search.new.containing("twitter").filter.fetch
|
104
|
+
def filter(filter='links')
|
105
|
+
@query[:q] << "filter:#{filter}"
|
106
|
+
self
|
107
|
+
end
|
108
|
+
|
109
|
+
# Only include tweets from after a given date
|
110
|
+
#
|
111
|
+
# @param date [String] A date in the format YYYY-MM-DD.
|
112
|
+
# @return [Twitter::Search] self
|
113
|
+
# @example Return an array of tweets containing "twitter" since October 1, 2010
|
114
|
+
# Twitter::Search.new.containing("twitter").since_date("2010-10-01").fetch
|
115
|
+
def since_date(date)
|
116
|
+
@query[:since] = date
|
117
|
+
self
|
118
|
+
end
|
119
|
+
alias :since :since_date
|
120
|
+
|
121
|
+
# Only include tweets from before a given date
|
122
|
+
#
|
123
|
+
# @param date [String] A date in the format YYYY-MM-DD.
|
124
|
+
# @return [Twitter::Search] self
|
125
|
+
# @example Return an array of tweets containing "twitter" up until October 1, 2010
|
126
|
+
# Twitter::Search.new.containing("twitter").since_date("2010-10-01").fetch
|
127
|
+
def until_date(date)
|
128
|
+
@query[:until] = date
|
129
|
+
self
|
130
|
+
end
|
131
|
+
alias :until :until_date
|
132
|
+
|
133
|
+
# Only include tweets with a positive attitude
|
134
|
+
#
|
135
|
+
# @return [Twitter::Search] self
|
136
|
+
# @example Return an array of tweets containing happy emoticons
|
137
|
+
# Twitter::Search.new.positive.fetch
|
138
|
+
def positive
|
139
|
+
@query[:tude] << ":)"
|
140
|
+
self
|
141
|
+
end
|
142
|
+
|
143
|
+
# Only include tweets with a negative attitude
|
144
|
+
#
|
145
|
+
# @return [Twitter::Search] self
|
146
|
+
# @example Return an array of tweets containing sad emoticons
|
147
|
+
# Twitter::Search.new.negative.fetch
|
148
|
+
def negative
|
149
|
+
@query[:tude] << ":("
|
150
|
+
self
|
151
|
+
end
|
152
|
+
|
153
|
+
# Only include tweets that are asking a question
|
154
|
+
#
|
155
|
+
# @return [Twitter::Search] self
|
156
|
+
# @example Return an array of tweets containing question marks
|
157
|
+
# Twitter::Search.new.question.fetch
|
158
|
+
def question
|
159
|
+
@query[:tude] << "?"
|
160
|
+
self
|
161
|
+
end
|
162
|
+
|
163
|
+
# @group Demographic filters
|
164
|
+
|
165
|
+
# Only include tweets in a given language, specified by an ISO 639-1 code
|
166
|
+
#
|
167
|
+
# @param code [String] An ISO 639-1 code.
|
168
|
+
# @return [Twitter::Search] self
|
169
|
+
# @example Return an array of French-language tweets containing "twitter"
|
170
|
+
# Twitter::Search.new.containing("twitter").language("fr").fetch
|
171
|
+
# Twitter::Search.new.containing("twitter").lang("fr").fetch # Shortcut for the above
|
172
|
+
# @see http://en.wikipedia.org/wiki/ISO_639-1
|
173
|
+
def language(code)
|
174
|
+
@query[:lang] = code
|
175
|
+
self
|
176
|
+
end
|
177
|
+
alias :lang :language
|
178
|
+
|
179
|
+
# Specify the locale of the query you are sending
|
180
|
+
#
|
181
|
+
# @param code [String] An ISO 639-1 code (only 'ja' is currently effective).
|
182
|
+
# @return [Twitter::Search] self
|
183
|
+
# @example Return an array of tweets from Japan containing "twitter"
|
184
|
+
# Twitter::Search.new.containing("twitter").locale("ja").fetch
|
185
|
+
# @see http://en.wikipedia.org/wiki/ISO_639-1
|
186
|
+
def locale(code)
|
187
|
+
@query[:locale] = code
|
188
|
+
self
|
189
|
+
end
|
190
|
+
|
191
|
+
# Only include tweets from users in a given radius of a given location
|
192
|
+
#
|
193
|
+
# @param lat [Float] A latitude.
|
194
|
+
# @param long [Float] A longitude.
|
195
|
+
# @param radius [String] A search radius, specified in either 'mi' (miles) or 'km' (kilometers).
|
196
|
+
# @return [Twitter::Search] self
|
197
|
+
# @example Return an array of tweets within a 1-mile radius of Twitter HQ
|
198
|
+
# Twitter::Search.new.containing("twitter").geocode(37.781157, -122.398720, "1mi").fetch
|
199
|
+
def geocode(lat, long, radius)
|
200
|
+
@query[:geocode] = [lat, long, radius].join(",")
|
201
|
+
self
|
202
|
+
end
|
203
|
+
|
204
|
+
# Only include tweets from users in a given place, specified by a place ID
|
205
|
+
#
|
206
|
+
# @param place_id [String] A place ID.
|
207
|
+
# @return [Twitter::Search] self
|
208
|
+
# @example Return an array of tweets from San Francisco
|
209
|
+
# Twitter::Search.new.place("5a110d312052166f").fetch
|
210
|
+
def place(place_id)
|
211
|
+
@query[:q] << "place:#{place_id}"
|
212
|
+
self
|
213
|
+
end
|
214
|
+
|
215
|
+
# Only include tweets from users near a given location
|
216
|
+
#
|
217
|
+
# @param location [String] A location name.
|
218
|
+
# @return [Twitter::Search] self
|
219
|
+
# @example Return an array of tweets near San Francisco
|
220
|
+
# Twitter::Search.new.near("San Francisco").fetch
|
221
|
+
def near(location)
|
222
|
+
@query[:q] << "near:#{location.inspect}"
|
223
|
+
self
|
224
|
+
end
|
225
|
+
|
226
|
+
# @group User filters
|
227
|
+
|
228
|
+
# Only include tweets from a given user, specified by screen_name
|
229
|
+
#
|
230
|
+
# @param screen_name [String] A Twitter user name.
|
231
|
+
# @return [Twitter::Search] self
|
232
|
+
# @example Return an array of tweets containing "twitter" from @sferik
|
233
|
+
# Twitter::Search.new.containing("twitter").from("sferik").fetch
|
234
|
+
def from(screen_name)
|
235
|
+
@query[:q] << "from:#{screen_name}"
|
236
|
+
self
|
237
|
+
end
|
238
|
+
|
239
|
+
# Exclude tweets from a given user, specified by screen_name
|
240
|
+
#
|
241
|
+
# @param screen_name [String] A Twitter user name.
|
242
|
+
# @return [Twitter::Search] self
|
243
|
+
# @example Return an array of tweets containing "twitter" from everyone except @sferik
|
244
|
+
# Twitter::Search.new.containing("twitter").not_from("sferik").fetch
|
245
|
+
def not_from(screen_name)
|
246
|
+
@query[:q] << "-from:#{screen_name}"
|
247
|
+
self
|
248
|
+
end
|
249
|
+
|
250
|
+
# Only include tweets to a given user, specified by screen_name
|
251
|
+
#
|
252
|
+
# @param screen_name [String] A Twitter user name.
|
253
|
+
# @return [Twitter::Search] self
|
254
|
+
# @example Return an array of tweets containing "twitter" to @sferik
|
255
|
+
# Twitter::Search.new.containing("twitter").to("sferik").fetch
|
256
|
+
def to(screen_name)
|
257
|
+
@query[:q] << "to:#{screen_name}"
|
258
|
+
self
|
259
|
+
end
|
260
|
+
|
261
|
+
# Exclude tweets to a given user, specified by screen_name
|
262
|
+
#
|
263
|
+
# @param screen_name [String] A Twitter user name.
|
264
|
+
# @return [Twitter::Search] self
|
265
|
+
# @example Return an array of tweets containing "twitter" to everyone except @sferik
|
266
|
+
# Twitter::Search.new.containing("twitter").not_to("sferik").fetch
|
267
|
+
def not_to(screen_name)
|
268
|
+
@query[:q] << "-to:#{screen_name}"
|
269
|
+
self
|
270
|
+
end
|
271
|
+
|
272
|
+
# Only include tweets mentioning a given user, specified by screen_name
|
273
|
+
#
|
274
|
+
# @param screen_name [String] A Twitter user name.
|
275
|
+
# @return [Twitter::Search] self
|
276
|
+
# @example Return an array of tweets containing "twitter" and mentioning @sferik
|
277
|
+
# Twitter::Search.new.containing("twitter").mentioning("sferik").fetch
|
278
|
+
def mentioning(screen_name)
|
279
|
+
@query[:q] << "@#{screen_name.gsub('@', '')}"
|
280
|
+
self
|
281
|
+
end
|
282
|
+
alias :referencing :mentioning
|
283
|
+
alias :mentions :mentioning
|
284
|
+
alias :references :mentioning
|
285
|
+
|
286
|
+
# Exclude tweets mentioning a given user, specified by screen_name
|
287
|
+
#
|
288
|
+
# @param screen_name [String] A Twitter user name.
|
289
|
+
# @return [Twitter::Search] self
|
290
|
+
# @example Return an array of tweets containing "twitter" but not mentioning @sferik
|
291
|
+
# Twitter::Search.new.containing("twitter").not_mentioning("sferik").fetch
|
292
|
+
def not_mentioning(screen_name)
|
293
|
+
@query[:q] << "-@#{screen_name.gsub('@', '')}"
|
294
|
+
self
|
295
|
+
end
|
296
|
+
alias :not_referencing :not_mentioning
|
297
|
+
alias :does_not_mention :not_mentioning
|
298
|
+
alias :does_not_reference :not_mentioning
|
299
|
+
|
300
|
+
# @group Twitter filters
|
301
|
+
|
302
|
+
# Only include retweets
|
303
|
+
#
|
304
|
+
# @return [Twitter::Search] self
|
305
|
+
# @example Return an array of retweets containing "twitter"
|
306
|
+
# Twitter::Search.new.containing("twitter").retweets.fetch
|
307
|
+
def retweets
|
308
|
+
@query[:q] << "rt"
|
309
|
+
self
|
310
|
+
end
|
311
|
+
|
312
|
+
# Only include original status updates (i.e., not retweets)
|
313
|
+
#
|
314
|
+
# @return [Twitter::Search] self
|
315
|
+
# @example Return an array of tweets containing "twitter", excluding retweets
|
316
|
+
# Twitter::Search.new.containing("twitter").no_retweets.fetch
|
317
|
+
def no_retweets
|
318
|
+
@query[:q] << "-rt"
|
319
|
+
self
|
320
|
+
end
|
321
|
+
|
322
|
+
# Only include tweets containing a given hashtag
|
323
|
+
#
|
324
|
+
# @param tag [String] A Twitter hashtag.
|
325
|
+
# @return [Twitter::Search] self
|
326
|
+
# @example Return an array of tweets containing the hashtag #FollowFriday
|
327
|
+
# Twitter::Search.new.hashtag("FollowFriday").fetch
|
328
|
+
def hashtag(tag)
|
329
|
+
@query[:q] << "\##{tag.gsub('#', '')}"
|
330
|
+
self
|
331
|
+
end
|
332
|
+
|
333
|
+
# Exclude tweets containing a given hashtag
|
334
|
+
#
|
335
|
+
# @param tag [String] A Twitter hashtag.
|
336
|
+
# @return [Twitter::Search] self
|
337
|
+
# @example Return an array of tweets containing the hashtag #FollowFriday but not #FF
|
338
|
+
# Twitter::Search.new.hashtag("FollowFriday").excluding_hashtag("FF").fetch
|
339
|
+
# Twitter::Search.new.hashtag("FollowFriday").excludes_hashtag("FF").fetch # Shortcut for the above
|
340
|
+
# Twitter::Search.new.hashtag("FollowFriday").exclude_hashtag("FF").fetch # Even shorter
|
341
|
+
def excluding_hashtag(tag)
|
342
|
+
@query[:q] << "-\##{tag.gsub('#', '')}"
|
343
|
+
self
|
344
|
+
end
|
345
|
+
alias :excludes_hashtag :excluding_hashtag
|
346
|
+
alias :exclude_hashtag :excluding_hashtag
|
347
|
+
|
348
|
+
# Only include tweets with an ID greater than the specified ID
|
349
|
+
#
|
350
|
+
# @param id [Integer] A Twitter status ID.
|
351
|
+
# @return [Twitter::Search] self
|
352
|
+
# @example Return an array of tweets containing "twitter" with an ID greater than 123456789
|
353
|
+
# Twitter::Search.new.containing("twitter").since_id(123456789).fetch
|
354
|
+
def since_id(id)
|
355
|
+
@query[:since_id] = id
|
356
|
+
self
|
357
|
+
end
|
358
|
+
|
359
|
+
# Only include tweets with an ID less than or equal to the specified ID
|
360
|
+
#
|
361
|
+
# @param id [Integer] A Twitter status ID.
|
362
|
+
# @return [Twitter::Search] self
|
363
|
+
# @example Return an array of tweets containing "twitter" with an ID less than or equal to 123456789
|
364
|
+
# Twitter::Search.new.containing("twitter").max_id(123456789).fetch
|
365
|
+
#
|
366
|
+
def max_id(id)
|
367
|
+
@query[:max_id] = id
|
368
|
+
self
|
369
|
+
end
|
370
|
+
alias :max :max_id
|
371
|
+
|
372
|
+
# Specify what type of search results you want to receive
|
373
|
+
#
|
374
|
+
# @param result_type [String] The type of results you want to receive ('recent', 'popular', or 'mixed').
|
375
|
+
# @return [Twitter::Search] self
|
376
|
+
# @example Return an array of recent tweets containing "twitter"
|
377
|
+
# Twitter::Search.new.containing("twitter").result_type("recent").fetch
|
378
|
+
def result_type(result_type="mixed")
|
379
|
+
@query[:result_type] = result_type
|
380
|
+
self
|
381
|
+
end
|
382
|
+
|
383
|
+
# Only include tweets from a given source
|
384
|
+
#
|
385
|
+
# @param source [String] A Twitter source.
|
386
|
+
# @return [Twitter::Search] self
|
387
|
+
# @example Return an array of tweets containing "twitter", posted from Hibari
|
388
|
+
# Twitter::Search.new.containing("twitter").source("Hibari").fetch
|
389
|
+
def source(source)
|
390
|
+
@query[:q] << "source:#{source}"
|
391
|
+
self
|
392
|
+
end
|
393
|
+
|
394
|
+
# @group Paging
|
395
|
+
|
396
|
+
# Specify the number of tweets to return per page
|
397
|
+
#
|
398
|
+
# @param number [Integer] The number of tweets to return per page, up to a max of 100.
|
399
|
+
# @return [Twitter::Search] self
|
400
|
+
# @example Return an array of 100 tweets containing "twitter"
|
401
|
+
# Twitter::Search.new.containing("twitter").per_page(100).fetch
|
402
|
+
def per_page(number=15)
|
403
|
+
@query[:rpp] = number
|
404
|
+
self
|
405
|
+
end
|
406
|
+
alias :rpp :per_page
|
407
|
+
|
408
|
+
# Specify the page number to return, up to a maximum of roughly 1500 results
|
409
|
+
#
|
410
|
+
# @param number [Integer] The page number (starting at 1) to return, up to a max of roughly 1500 results (based on {Twitter::Client::Search#per_page} * {Twitter::Client::Search#page}).
|
411
|
+
# @return [Twitter::Search] self
|
412
|
+
# @example Return the second page of tweets containing "twitter"
|
413
|
+
# Twitter::Search.new.containing("twitter").page(2).fetch
|
414
|
+
def page(number)
|
415
|
+
@query[:page] = number
|
416
|
+
self
|
417
|
+
end
|
418
|
+
|
419
|
+
# Indicates if there are additional results to be fetched
|
420
|
+
#
|
421
|
+
# @return [Boolean]
|
422
|
+
# @example
|
423
|
+
# search = Twitter::Search.new.containing("twitter").fetch
|
424
|
+
# search.next_page? #=> true
|
425
|
+
def next_page?
|
426
|
+
fetch if @cache.nil?
|
427
|
+
!!@cache["next_page"]
|
428
|
+
end
|
429
|
+
|
430
|
+
# @group Fetching
|
431
|
+
|
432
|
+
# Fetch the next page of results of the query
|
433
|
+
#
|
434
|
+
# @return [Array] Tweets that match specified query.
|
435
|
+
# @example Return the first two pages of results
|
436
|
+
# search = Twitter::Search.new.containing("twitter").fetch
|
437
|
+
# search.fetch_next_page
|
438
|
+
def fetch_next_page
|
439
|
+
if next_page?
|
440
|
+
@cache = get("search", CGI.parse(@cache["next_page"][1..-1]))
|
441
|
+
@cache.results
|
442
|
+
end
|
443
|
+
end
|
444
|
+
|
445
|
+
# Fetch the results of the query
|
446
|
+
#
|
447
|
+
# @param force [Boolean] Ignore the cache and hit the API again.
|
448
|
+
# @return [Array] Tweets that match specified query.
|
449
|
+
# @example Return an array of tweets containing "twitter"
|
450
|
+
# search = Twitter::Search.new.containing("twitter").fetch
|
451
|
+
def fetch(force=false)
|
452
|
+
if @cache.nil? || force
|
453
|
+
options = query.dup
|
454
|
+
options[:q] = options[:q].join(" ")
|
455
|
+
@cache = get("search", options)
|
456
|
+
end
|
457
|
+
@cache.results
|
458
|
+
end
|
459
|
+
|
460
|
+
# Calls block once for each element in self, passing that element as a parameter
|
461
|
+
#
|
462
|
+
# @yieldparam [Hashie::Mash] result Tweet that matches specified query.
|
463
|
+
# @return [Array] Tweets that match specified query.
|
464
|
+
# @example
|
465
|
+
# Twitter::Search.new.containing('marry me').to('justinbieber').each do |result|
|
466
|
+
# puts "#{result.from_user}: #{result.text}"
|
467
|
+
# end
|
468
|
+
def each
|
469
|
+
fetch.each{|result| yield result}
|
470
|
+
end
|
471
|
+
|
472
|
+
end
|
473
|
+
end
|