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.
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