look-twitter-search 0.5.4 → 0.5.6

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.textile ADDED
@@ -0,0 +1,16 @@
1
+ h2. 0.5.5 (May 18, 2009)
2
+
3
+ * raise error when query contains :near or :within:. (Dan Croak)
4
+
5
+ h2. 0.5.4 (May 16, 2009)
6
+
7
+ * Fixed bug in Twitter trends. (Dan Croak)
8
+ * Refactored test suite. Now organized by function, uses redgreen, began moving toward using JSON instead of YAML for fixtures. (Dan Croak)
9
+ * Exposed Shoulda Macros to apps that want to use them. (Dan Croak)
10
+
11
+ h2. 0.5.3 (May 15, 2009)
12
+
13
+ * Added Twitter trends. (Matt Sanford)
14
+ * Added overdue attribution for Luke Francl, Matt Sanford, Alejandro Crosa, Danny Burkes, Don Brown, & HotFusionMan.
15
+ * Added CHANGELOG. (Dan Croak)
16
+
data/README.markdown CHANGED
@@ -14,8 +14,10 @@ Require the gem.
14
14
 
15
15
  Set up a TwitterSearch::Client. Name your client (a.k.a. 'user agent') to something meaningful, such as your app's name. This helps Twitter Search answer any questions about your use of the API.
16
16
 
17
- @client = TwitterSearch::Client.new 'politweets'
18
-
17
+ @client = TwitterSearch::Client.new 'politweets'
18
+
19
+ ### Search
20
+
19
21
  Request tweets by calling the query method of your client. It takes either a String or a Hash of arguments.
20
22
 
21
23
  @tweets = @client.query 'twitter search'
@@ -26,6 +28,16 @@ The String form uses the default Twitter Search behavior, which in this example
26
28
 
27
29
  Use the Twitter Search API's query operators with the :q key to access a variety of behavior.
28
30
 
31
+ ### Trends
32
+
33
+ Request the current trending topics by calling the trends method of your client. It takes an optional Hash of arguments.
34
+
35
+ @trends = @client.trends
36
+
37
+ The only supported option currently is exclude_hashtags to return trends that are not hashtags only.
38
+
39
+ @trends = @client.trends :exclude_hashtags => true
40
+
29
41
  ## Search Operators
30
42
 
31
43
  The following operator examples find tweets...
@@ -66,14 +78,14 @@ Alter the number of Tweets returned per page with the :rpp key. Stick with 10, 1
66
78
 
67
79
  * Searching for a positive attitude :) returns tweets containing the text :), =), :D, and :-)
68
80
 
69
- ## Authors
81
+ ## Contributors
70
82
 
71
- Written by Dustin Sallings (dustin@spy.net), forked by Dan Croak (dcroak@thoughtbot.com).
83
+ Dustin Sallings, Dan Croak, Luke Francl, Matt Sanford, Alejandro Crosa, Danny Burkes, Don Brown, & HotFusionMan.
72
84
 
73
85
  ## Resources
74
86
 
75
- * [Official Twitter Search API](http://search.twitter.com/api)
87
+ * [Official Twitter Search API](http://apiwiki.twitter.com/Twitter-API-Documentation)
76
88
 
77
89
  ## License
78
90
 
79
- MIT License, same terms as Ruby.
91
+ MIT License, same terms as Ruby.
data/Rakefile CHANGED
@@ -1,25 +1,25 @@
1
1
  require 'rake'
2
2
  require 'rake/testtask'
3
-
4
- test_files_pattern = 'test/twitter_search_test.rb'
3
+
4
+ test_files_pattern = 'test/*_test.rb'
5
5
  Rake::TestTask.new do |t|
6
6
  t.libs << 'lib'
7
7
  t.pattern = test_files_pattern
8
8
  t.verbose = false
9
9
  end
10
-
10
+
11
11
  desc "Run the test suite"
12
12
  task :default => :test
13
-
13
+
14
14
  gem_spec = Gem::Specification.new do |gem_spec|
15
15
  gem_spec.name = "twitter-search"
16
- gem_spec.version = "0.5.2"
17
- gem_spec.summary = "Ruby client for Twitter Search."
16
+ gem_spec.version = "0.5.6"
17
+ gem_spec.summary = "Ruby client for Twitter Search. Includes trends."
18
18
  gem_spec.email = "dcroak@thoughtbot.com"
19
19
  gem_spec.homepage = "http://github.com/dancroak/twitter-search"
20
20
  gem_spec.description = "Ruby client for Twitter Search."
21
- gem_spec.authors = ["Dustin Sallings", "Dan Croak"]
22
- gem_spec.files = FileList["[A-Z]*", "{generators,lib,shoulda_macros,rails}/**/*"]
21
+ gem_spec.authors = ["Dustin Sallings", "Dan Croak", "Luke Francl", "Matt Sanford", "Alejandro Crosa", "Danny Burkes", "Don Brown", "HotFusionMan"]
22
+ gem_spec.files = FileList["[A-Z]*", "{lib,shoulda_macros}/**/*"]
23
23
  gem_spec.add_dependency('json', '>= 1.1.2')
24
24
  end
25
25
 
@@ -36,7 +36,7 @@ require 'yaml'
36
36
 
37
37
  namespace :yaml do
38
38
  desc "Write Twitter Search results to yaml file so API is not hit every test."
39
- task :write do
39
+ task :write do
40
40
  write_yaml :tweets => 'Obama', :file => 'obama'
41
41
  write_yaml :tweets => 'twitter search', :file => 'twitter_search'
42
42
  write_yaml :tweets => {:q => 'twitter search'}, :file => 'twitter_search_and'
@@ -64,7 +64,7 @@ end
64
64
  def write_yaml(opts = {})
65
65
  @client = TwitterSearch::Client.new 'twitter-search'
66
66
  tweets = @client.query(opts[:tweets])
67
- File.open(File.join(File.dirname(__FILE__), 'test', 'yaml', "#{opts[:file]}.yaml"), 'w+') do |file|
67
+ File.open(File.join(File.dirname(__FILE__), 'test', 'yaml', "#{opts[:file]}.yaml"), 'w+') do |file|
68
68
  file.puts tweets.to_yaml
69
69
  end
70
- end
70
+ end
data/lib/trends.rb ADDED
@@ -0,0 +1,36 @@
1
+ module TwitterSearch
2
+ class Trend
3
+ VARS = [ :query, :name ]
4
+ attr_reader *VARS
5
+ attr_reader :exclude_hashtags
6
+
7
+ def initialize(opts)
8
+ @exclude_hashtags = !!opts['exclude_hashtags']
9
+ VARS.each { |each| instance_variable_set "@#{each}", opts[each.to_s] }
10
+ end
11
+ end
12
+
13
+ class Trends
14
+ VARS = [:date]
15
+ attr_reader *VARS
16
+
17
+ include Enumerable
18
+
19
+ def initialize(opts)
20
+ @trends = opts['trends'].values.first.collect { |each| Trend.new(each) }
21
+ VARS.each { |each| instance_variable_set "@#{each}", opts[each.to_s] }
22
+ end
23
+
24
+ def each(&block)
25
+ @trends.each(&block)
26
+ end
27
+
28
+ def size
29
+ @trends.size
30
+ end
31
+
32
+ def [](index)
33
+ @trends[index]
34
+ end
35
+ end
36
+ end
data/lib/tweets.rb ADDED
@@ -0,0 +1,45 @@
1
+ module TwitterSearch
2
+ class Tweet
3
+ VARS = [:text, :from_user, :to_user, :to_user_id, :id, :iso_language_code, :from_user_id, :created_at, :profile_image_url, :source ]
4
+ attr_reader *VARS
5
+ attr_reader :language
6
+
7
+ def initialize(opts)
8
+ @language = opts['iso_language_code']
9
+ VARS.each { |each| instance_variable_set "@#{each}", opts[each.to_s] }
10
+ end
11
+ end
12
+
13
+ class Tweets
14
+ VARS = [:since_id, :max_id, :results_per_page, :page, :query, :next_page]
15
+ attr_reader *VARS
16
+
17
+ include Enumerable
18
+
19
+ def initialize(opts)
20
+ @results = opts['results'].collect { |each| Tweet.new(each) }
21
+ VARS.each { |each| instance_variable_set "@#{each}", opts[each.to_s] }
22
+ end
23
+
24
+ def each(&block)
25
+ @results.each(&block)
26
+ end
27
+
28
+ def size
29
+ @results.size
30
+ end
31
+
32
+ def [](index)
33
+ @results[index]
34
+ end
35
+
36
+ def has_next_page?
37
+ ! @next_page.nil?
38
+ end
39
+
40
+ def get_next_page
41
+ client = Client.new
42
+ return client.query( CGI.parse( @next_page[1..-1] ) )
43
+ end
44
+ end
45
+ end
@@ -3,99 +3,96 @@ require 'net/http'
3
3
  require 'json'
4
4
  require 'cgi'
5
5
 
6
- module TwitterSearch
6
+ require File.join(File.dirname(__FILE__), 'tweets')
7
+ require File.join(File.dirname(__FILE__), 'trends')
7
8
 
8
- class Tweet
9
- VARS = [:text, :from_user, :to_user, :to_user_id, :id, :iso_language_code, :from_user_id, :created_at, :profile_image_url, :source ]
10
- attr_reader *VARS
11
- attr_reader :language
12
-
13
- def initialize(opts)
14
- @language = opts['iso_language_code']
15
- VARS.each { |each| instance_variable_set "@#{each}", opts[each.to_s] }
16
- end
9
+ module TwitterSearch
10
+ class SearchOperatorError < ArgumentError
17
11
  end
18
-
19
- class Tweets
20
- VARS = [:since_id, :max_id, :results_per_page, :page, :query, :next_page]
21
- attr_reader *VARS
22
-
23
- include Enumerable
24
-
25
- def initialize(opts)
26
- @results = opts['results'].collect { |each| Tweet.new(each) }
27
- VARS.each { |each| instance_variable_set "@#{each}", opts[each.to_s] }
28
- end
29
-
30
- def each(&block)
31
- @results.each(&block)
32
- end
33
-
34
- def size
35
- @results.size
36
- end
37
-
38
- def [](index)
39
- @results[index]
40
- end
41
-
42
- def has_next_page?
43
- ! @next_page.nil?
44
- end
45
-
46
- def get_next_page
47
- client = Client.new
48
- return client.query( CGI.parse( @next_page[1..-1] ) )
49
- end
12
+ class SearchServerError < RuntimeError
50
13
  end
51
14
 
52
15
  class Client
53
- TWITTER_API_URL = 'http://search.twitter.com/search.json'
54
- TWITTER_API_DEFAULT_TIMEOUT = 5
55
-
16
+ TWITTER_SEARCH_API_URL = 'http://search.twitter.com/search.json'
17
+ TWITTER_TRENDS_API_URL = 'http://search.twitter.com/trends/current.json'
18
+ DEFAULT_TIMEOUT = 5
19
+
56
20
  attr_accessor :agent
57
21
  attr_accessor :timeout
58
-
59
- def initialize(agent = 'twitter-search', timeout = TWITTER_API_DEFAULT_TIMEOUT)
22
+
23
+ def initialize(agent = 'twitter-search', timeout = DEFAULT_TIMEOUT)
60
24
  @agent = agent
61
25
  @timeout = timeout
62
26
  end
63
-
27
+
64
28
  def headers
65
29
  { "Content-Type" => 'application/json',
66
30
  "User-Agent" => @agent }
67
31
  end
68
-
32
+
69
33
  def query(opts = {})
70
- url = URI.parse(TWITTER_API_URL)
34
+ url = URI.parse(TWITTER_SEARCH_API_URL)
71
35
  url.query = sanitize_query(opts)
72
36
 
37
+ ensure_no_location_operators(url.query)
38
+
73
39
  req = Net::HTTP::Get.new(url.path)
74
40
  http = Net::HTTP.new(url.host, url.port)
75
41
  http.read_timeout = timeout
42
+
43
+ res = http.start { |http|
44
+ http.get("#{url.path}?#{url.query}", headers)
45
+ }
46
+
47
+ if res.code == '404'
48
+ raise TwitterSearch::SearchServerError, "Twitter responded with a 404 for your query. It is likely too complicated to process."
49
+ end
50
+
51
+ json = res.body
76
52
 
53
+ Tweets.new JSON.parse(json)
54
+ end
55
+
56
+ def trends(opts = {})
57
+ url = URI.parse(TWITTER_TRENDS_API_URL)
58
+ if opts['exclude_hashtags']
59
+ url.query = sanitize_query_hash({ :exclude_hashtags => opts['exclude_hashtags'] })
60
+ end
61
+
62
+ req = Net::HTTP::Get.new(url.path)
63
+ http = Net::HTTP.new(url.host, url.port)
64
+ http.read_timeout = timeout
65
+
77
66
  json = http.start { |http|
78
67
  http.get("#{url.path}?#{url.query}", headers)
79
68
  }.body
80
- Tweets.new JSON.parse(json)
69
+
70
+ Trends.new JSON.parse(json)
81
71
  end
82
72
 
83
73
  private
84
74
 
85
75
  def sanitize_query(opts)
86
76
  if opts.is_a? String
87
- "q=#{CGI.escape(opts)}"
77
+ "q=#{CGI.escape(opts)}"
88
78
  elsif opts.is_a? Hash
89
79
  "#{sanitize_query_hash(opts)}"
90
80
  end
91
81
  end
92
82
 
93
83
  def sanitize_query_hash(query_hash)
94
- query_hash.collect { |key, value|
95
- "#{CGI.escape(key.to_s)}=#{CGI.escape(value.to_s)}"
84
+ query_hash.collect { |key, value|
85
+ "#{CGI.escape(key.to_s)}=#{CGI.escape(value.to_s)}"
96
86
  }.join('&')
97
87
  end
98
-
99
- end
100
88
 
89
+ def ensure_no_location_operators(query_string)
90
+ if query_string.include?("near%3A") ||
91
+ query_string.include?("within%3A")
92
+ raise TwitterSearch::SearchOperatorError,
93
+ "near: and within: are available from the Twitter Search web interface, but not the API. The API requires the geocode parameter. See dancroak/twitter-search README."
94
+ end
95
+ end
96
+
97
+ end
101
98
  end
@@ -0,0 +1,36 @@
1
+ module TwitterSearch
2
+ module Shoulda
3
+ def should_have_default_search_behaviors
4
+ should_find_tweets
5
+ should_have_text_for_all_tweets
6
+ should_return_page 1
7
+ should_return_tweets_in_sets_of 15
8
+ end
9
+
10
+ def should_find_tweets
11
+ should 'find tweets' do
12
+ assert @tweets.any?
13
+ end
14
+ end
15
+
16
+ def should_have_text_for_all_tweets
17
+ should 'have text for all tweets' do
18
+ assert @tweets.all? { |tweet| tweet.text.size > 0 }
19
+ end
20
+ end
21
+
22
+ def should_return_page(number)
23
+ should "return page #{number}" do
24
+ assert_equal number, @tweets.page
25
+ end
26
+ end
27
+
28
+ def should_return_tweets_in_sets_of(number)
29
+ should "return tweets in sets of #{number}" do
30
+ assert_equal number, @tweets.results_per_page
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+ Test::Unit::TestCase.extend(TwitterSearch::Shoulda)
metadata CHANGED
@@ -1,16 +1,22 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: look-twitter-search
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.4
4
+ version: 0.5.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dustin Sallings
8
8
  - Dan Croak
9
+ - Luke Francl
10
+ - Matt Sanford
11
+ - Alejandro Crosa
12
+ - Danny Burkes
13
+ - Don Brown
14
+ - HotFusionMan
9
15
  autorequire:
10
16
  bindir: bin
11
17
  cert_chain: []
12
18
 
13
- date: 2009-02-03 21:00:00 -08:00
19
+ date: 2009-05-17 21:00:00 -07:00
14
20
  default_executable:
15
21
  dependencies:
16
22
  - !ruby/object:Gem::Dependency
@@ -32,11 +38,15 @@ extensions: []
32
38
  extra_rdoc_files: []
33
39
 
34
40
  files:
41
+ - CHANGELOG.textile
35
42
  - Rakefile
36
43
  - README.markdown
37
44
  - TODO.markdown
45
+ - lib/trends.rb
46
+ - lib/tweets.rb
38
47
  - lib/twitter_search.rb
39
- has_rdoc: false
48
+ - shoulda_macros/twitter_search.rb
49
+ has_rdoc: true
40
50
  homepage: http://github.com/dancroak/twitter-search
41
51
  post_install_message:
42
52
  rdoc_options: []
@@ -60,7 +70,7 @@ requirements: []
60
70
  rubyforge_project:
61
71
  rubygems_version: 1.2.0
62
72
  signing_key:
63
- specification_version: 2
64
- summary: Ruby client for Twitter Search.
73
+ specification_version: 3
74
+ summary: Ruby client for Twitter Search. Includes trends.
65
75
  test_files: []
66
76