assistly 0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. data/.gemtest +0 -0
  2. data/.gitignore +11 -0
  3. data/.rspec +3 -0
  4. data/.travis.yml +7 -0
  5. data/.yardopts +9 -0
  6. data/Gemfile +12 -0
  7. data/HISTORY.mkd +4 -0
  8. data/LICENSE.mkd +20 -0
  9. data/README.mkd +121 -0
  10. data/Rakefile +23 -0
  11. data/assistly.gemspec +43 -0
  12. data/lib/assistly.rb +26 -0
  13. data/lib/assistly/api.rb +23 -0
  14. data/lib/assistly/authentication.rb +25 -0
  15. data/lib/assistly/client.rb +25 -0
  16. data/lib/assistly/client/case.rb +51 -0
  17. data/lib/assistly/client/customer.rb +108 -0
  18. data/lib/assistly/client/interaction.rb +41 -0
  19. data/lib/assistly/client/user.rb +38 -0
  20. data/lib/assistly/client/utils.rb +83 -0
  21. data/lib/assistly/configuration.rb +104 -0
  22. data/lib/assistly/connection.rb +39 -0
  23. data/lib/assistly/error.rb +62 -0
  24. data/lib/assistly/request.rb +44 -0
  25. data/lib/assistly/search.rb +473 -0
  26. data/lib/assistly/version.rb +4 -0
  27. data/lib/faraday/request/multipart_with_file.rb +30 -0
  28. data/lib/faraday/response/raise_http_4xx.rb +45 -0
  29. data/lib/faraday/response/raise_http_5xx.rb +24 -0
  30. data/spec/assistly/api_spec.rb +70 -0
  31. data/spec/assistly/client/case_spec.rb +88 -0
  32. data/spec/assistly/client/customer_spec.rb +158 -0
  33. data/spec/assistly/client/interaction_spec.rb +58 -0
  34. data/spec/assistly/client/user_spec.rb +58 -0
  35. data/spec/assistly/client_spec.rb +10 -0
  36. data/spec/assistly_spec.rb +99 -0
  37. data/spec/faraday/response_spec.rb +34 -0
  38. data/spec/fixtures/case.json +59 -0
  39. data/spec/fixtures/case_update.json +59 -0
  40. data/spec/fixtures/cases.json +182 -0
  41. data/spec/fixtures/customer.json +58 -0
  42. data/spec/fixtures/customer_create.json +56 -0
  43. data/spec/fixtures/customer_create_email.json +15 -0
  44. data/spec/fixtures/customer_update.json +47 -0
  45. data/spec/fixtures/customer_update_email.json +15 -0
  46. data/spec/fixtures/customers.json +98 -0
  47. data/spec/fixtures/interaction_create.json +126 -0
  48. data/spec/fixtures/interactions.json +139 -0
  49. data/spec/fixtures/user.json +15 -0
  50. data/spec/fixtures/users.json +24 -0
  51. data/spec/helper.rb +53 -0
  52. 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