handcrafted-twitter 0.4.0 → 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. data/{History.txt → History} +16 -0
  2. data/{License.txt → License} +0 -0
  3. data/{Manifest.txt → Manifest} +14 -16
  4. data/{README.txt → README} +0 -0
  5. data/Rakefile +41 -3
  6. data/bin/twitter +1 -2
  7. data/examples/direct_messages.rb +1 -0
  8. data/examples/favorites.rb +4 -4
  9. data/examples/replies.rb +1 -0
  10. data/examples/search.rb +1 -0
  11. data/examples/sent_messages.rb +1 -0
  12. data/examples/timeline.rb +1 -0
  13. data/lib/twitter.rb +13 -2
  14. data/lib/twitter/base.rb +52 -19
  15. data/lib/twitter/cli.rb +13 -7
  16. data/lib/twitter/cli/helpers.rb +20 -8
  17. data/lib/twitter/direct_message.rb +7 -7
  18. data/lib/twitter/rate_limit_status.rb +4 -4
  19. data/lib/twitter/search.rb +10 -3
  20. data/lib/twitter/search_result.rb +83 -0
  21. data/lib/twitter/search_result_info.rb +82 -0
  22. data/lib/twitter/status.rb +8 -8
  23. data/lib/twitter/user.rb +18 -18
  24. data/lib/twitter/version.rb +1 -7
  25. data/spec/base_spec.rb +32 -2
  26. data/spec/cli/helper_spec.rb +21 -7
  27. data/spec/fixtures/follower_ids.xml +11 -0
  28. data/spec/fixtures/friend_ids.xml +12 -0
  29. data/spec/fixtures/friendship_already_exists.xml +5 -0
  30. data/spec/fixtures/friendship_created.xml +12 -0
  31. data/spec/fixtures/search_result_info.yml +147 -0
  32. data/spec/search_spec.rb +12 -1
  33. data/spec/spec_helper.rb +12 -1
  34. data/twitter.gemspec +17 -21
  35. data/website/index.html +4 -1
  36. metadata +49 -46
  37. data/config/hoe.rb +0 -74
  38. data/config/requirements.rb +0 -17
  39. data/script/destroy +0 -14
  40. data/script/generate +0 -14
  41. data/script/txt2html +0 -74
  42. data/setup.rb +0 -1585
  43. data/tasks/deployment.rake +0 -50
  44. data/tasks/environment.rake +0 -7
  45. data/tasks/website.rake +0 -17
@@ -5,25 +5,37 @@ module Twitter
5
5
  class NoAccounts < StandardError; end
6
6
 
7
7
  def output_tweets(collection, options={})
8
- options.reverse_merge!({
8
+ options = {
9
9
  :cache => false,
10
10
  :since_prefix => '',
11
- :empty_msg => 'Nothing new since your last check.'
12
- })
11
+ :empty_msg => 'Nothing new since your last check.',
12
+ :reverse => false
13
+ }.merge(options)
14
+
13
15
  if collection.size > 0
14
16
  justify = collection.collect { |s| s.user.screen_name }.max { |a,b| a.length <=> b.length }.length rescue 0
15
17
  indention = ' ' * (justify + 3)
16
18
  say("\n#{indention}#{collection.size} new tweet(s) found.\n\n")
19
+ collection.reverse! if options[:reverse]
17
20
  collection.each do |s|
18
21
  Tweet.create_from_tweet(current_account, s) if options[:cache]
22
+
19
23
  occurred_at = Time.parse(s.created_at).strftime('On %b %d at %l:%M%P')
20
24
  formatted_time = '-' * occurred_at.length + "\n#{indention}#{occurred_at}"
21
25
  formatted_name = s.user.screen_name.rjust(justify + 1)
22
26
  formatted_msg = ''
23
- s.text.split(' ').in_groups_of(6, false) { |row| formatted_msg += row.join(' ') + "\n#{indention}" }
24
- say "#{CGI::unescapeHTML(formatted_name)}: #{CGI::unescapeHTML(formatted_msg)}#{formatted_time}\n\n"
27
+
28
+ s.text.split(' ').each_with_index do |word, idx|
29
+ formatted_msg += "#{word} "
30
+
31
+ sixth_word = idx != 0 && idx % 6 == 0
32
+ formatted_msg += "\n#{indention}" if sixth_word
33
+ end
34
+
35
+ say "#{CGI::unescapeHTML(formatted_name)}: #{CGI::unescapeHTML(formatted_msg)}\n#{indention}#{formatted_time}\n\n"
25
36
  end
26
- Configuration["#{options[:since_prefix]}_since_id"] = collection.first.id
37
+
38
+ Configuration["#{options[:since_prefix]}_since_id"] = options[:reverse] ? collection.last.id : collection.first.id
27
39
  else
28
40
  say(options[:empty_msg])
29
41
  end
@@ -35,7 +47,7 @@ module Twitter
35
47
 
36
48
  def current_account
37
49
  @current_account ||= Account.active
38
- raise Account.count == 0 ? NoAccounts : NoActiveAccount if @current_account.blank?
50
+ raise Account.count == 0 ? NoAccounts : NoActiveAccount if @current_account.nil?
39
51
  @current_account
40
52
  end
41
53
 
@@ -44,7 +56,7 @@ module Twitter
44
56
  if File.exists?(tweet_file)
45
57
  say '.twitter file found, attempting import...'
46
58
  config = YAML::load(File.read(tweet_file))
47
- if !config['email'].blank? && !config['password'].blank?
59
+ if !config['email'].nil? && !config['password'].nil?
48
60
  Account.add(:username => config['email'], :password => config['password'])
49
61
  say 'Account imported'
50
62
  block.call if block_given?
@@ -8,13 +8,13 @@ module Twitter
8
8
  # Creates a new status from a piece of xml
9
9
  def new_from_xml(xml)
10
10
  DirectMessage.new do |d|
11
- d.id = (xml).at('id').text
12
- d.text = (xml).get_elements_by_tag_name('text').text
13
- d.sender_id = (xml).at('sender_id').text
14
- d.recipient_id = (xml).at('recipient_id').text
15
- d.created_at = (xml).at('created_at').text
16
- d.sender_screen_name = (xml).at('sender_screen_name').text
17
- d.recipient_screen_name = (xml).at('recipient_screen_name').text
11
+ d.id = (xml).at('id').innerHTML
12
+ d.text = (xml).get_elements_by_tag_name('text').innerHTML
13
+ d.sender_id = (xml).at('sender_id').innerHTML
14
+ d.recipient_id = (xml).at('recipient_id').innerHTML
15
+ d.created_at = (xml).at('created_at').innerHTML
16
+ d.sender_screen_name = (xml).at('sender_screen_name').innerHTML
17
+ d.recipient_screen_name = (xml).at('recipient_screen_name').innerHTML
18
18
  end
19
19
  end
20
20
  end
@@ -8,10 +8,10 @@ module Twitter
8
8
  # Creates a new rate limi status from a piece of xml
9
9
  def new_from_xml(xml)
10
10
  RateLimitStatus.new do |s|
11
- s.reset_time_in_seconds = xml.at('reset-time-in-seconds').text.to_i
12
- s.reset_time = Time.parse xml.at('reset-time').text
13
- s.remaining_hits = xml.at('remaining-hits').text.to_i
14
- s.hourly_limit = xml.at('hourly-limit').text.to_i
11
+ s.reset_time_in_seconds = xml.at('reset-time-in-seconds').inner_html.to_i
12
+ s.reset_time = Time.parse xml.at('reset-time').inner_html
13
+ s.remaining_hits = xml.at('remaining-hits').inner_html.to_i
14
+ s.hourly_limit = xml.at('hourly-limit').inner_html.to_i
15
15
  end
16
16
  end
17
17
  end
@@ -11,7 +11,7 @@ module Twitter
11
11
 
12
12
  def initialize(q=nil)
13
13
  clear
14
- containing(q) unless q.blank?
14
+ containing(q) if q && q.strip != ''
15
15
  end
16
16
 
17
17
  def from(user)
@@ -59,6 +59,12 @@ module Twitter
59
59
  self
60
60
  end
61
61
 
62
+ # Which page of results to fetch
63
+ def page(num)
64
+ @query[:page] = num
65
+ self
66
+ end
67
+
62
68
  # Only searches tweets since a given id.
63
69
  # Recommended to use this when possible.
64
70
  def since(since_id)
@@ -83,7 +89,7 @@ module Twitter
83
89
  # If you want to get results do something other than iterate over them.
84
90
  def fetch
85
91
  @query[:q] = @query[:q].join(' ')
86
- self.class.get('/search.json', {:query => @query})
92
+ SearchResultInfo.new_from_hash(self.class.get('/search.json', {:query => @query}))
87
93
  end
88
94
 
89
95
  def each
@@ -91,4 +97,5 @@ module Twitter
91
97
  @result['results'].each { |r| yield r }
92
98
  end
93
99
  end
94
- end
100
+ end
101
+
@@ -0,0 +1,83 @@
1
+ module Twitter
2
+ class SearchResult < Hash
3
+
4
+ # Creates an easier to work with hash from
5
+ # one with string-based keys
6
+ def self.new_from_hash(hash)
7
+ new.merge!(hash)
8
+ end
9
+
10
+ def created_at
11
+ self['created_at']
12
+ end
13
+
14
+ def created_at=(val)
15
+ self['created_at'] = val
16
+ end
17
+
18
+ def from_user
19
+ self['from_user']
20
+ end
21
+
22
+ def from_user=(val)
23
+ self['from_user'] = val
24
+ end
25
+
26
+ def from_user_id
27
+ self['from_user_id']
28
+ end
29
+
30
+ def from_user_id=(val)
31
+ self['from_user_id'] = val
32
+ end
33
+
34
+ def id
35
+ self['id']
36
+ end
37
+
38
+ def id=(val)
39
+ self['id'] = val
40
+ end
41
+
42
+ def iso_language_code
43
+ self['iso_language_code']
44
+ end
45
+
46
+ def iso_language_code=(val)
47
+ self['iso_language_code'] = val
48
+ end
49
+
50
+ def profile_image_url
51
+ self['profile_image_url']
52
+ end
53
+
54
+ def profile_image_url=(val)
55
+ self['profile_image_url'] = val
56
+ end
57
+
58
+ def text
59
+ self['text']
60
+ end
61
+
62
+ def text=(val)
63
+ self['text'] = val
64
+ end
65
+
66
+ def to_user
67
+ self['to_user']
68
+ end
69
+
70
+ def to_user=(val)
71
+ self['to_user'] = val
72
+ end
73
+
74
+ def to_user_id
75
+ self['to_user_id']
76
+ end
77
+
78
+ def to_user_id=(val)
79
+ self['to_user_id'] = val
80
+ end
81
+
82
+ end
83
+ end
@@ -0,0 +1,82 @@
1
+ module Twitter
2
+ class SearchResultInfo < Hash
3
+
4
+ # Creates an easier to work with hash from
5
+ # one with string-based keys
6
+ def self.new_from_hash(hash)
7
+ i = new
8
+ i.merge!(hash)
9
+ search_results = []
10
+ i.results.each do |r|
11
+ search_results << SearchResult.new_from_hash(r)
12
+ end
13
+ i.results = search_results
14
+ i
15
+ end
16
+
17
+ def completed_in
18
+ self['completed_in']
19
+ end
20
+
21
+ def completed_in=(val)
22
+ self['completed_in'] = val
23
+ end
24
+
25
+ def max_id
26
+ self['max_id']
27
+ end
28
+
29
+ def max_id=(val)
30
+ self['max_id'] = val
31
+ end
32
+
33
+ def next_page
34
+ self['next_page']
35
+ end
36
+
37
+ def next_page=(val)
38
+ self['next_page'] = val
39
+ end
40
+
41
+ def page
42
+ self['page']
43
+ end
44
+
45
+ def page=(val)
46
+ self['page'] = val
47
+ end
48
+
49
+ def refresh_url
50
+ self['refresh_url']
51
+ end
52
+
53
+ def refresh_url=(val)
54
+ self['refresh_url'] = val
55
+ end
56
+
57
+ def results_per_page
58
+ self['results_per_page']
59
+ end
60
+
61
+ def results_per_page=(val)
62
+ self['results_per_page'] = val
63
+ end
64
+
65
+ def since_id
66
+ self['since_id']
67
+ end
68
+
69
+ def since_id=(val)
70
+ self['since_id'] = val
71
+ end
72
+
73
+ def results
74
+ self['results']
75
+ end
76
+
77
+ def results=(val)
78
+ self['results'] = val
79
+ end
80
+
81
+ end
82
+ end
@@ -7,14 +7,14 @@ module Twitter
7
7
  # Creates a new status from a piece of xml
8
8
  def self.new_from_xml(xml)
9
9
  s = new
10
- s.id = (xml).at('id').text
11
- s.created_at = (xml).at('created_at').text
12
- s.text = (xml).get_elements_by_tag_name('text').text
13
- s.source = (xml).at('source').text
14
- s.truncated = (xml).at('truncated').text == 'false' ? false : true
15
- s.favorited = (xml).at('favorited').text == 'false' ? false : true
16
- s.in_reply_to_status_id = (xml).at('in_reply_to_status_id').text
17
- s.in_reply_to_user_id = (xml).at('in_reply_to_user_id').text
10
+ s.id = (xml).at('id').innerHTML
11
+ s.created_at = (xml).at('created_at').innerHTML
12
+ s.text = (xml).get_elements_by_tag_name('text').innerHTML
13
+ s.source = (xml).at('source').innerHTML
14
+ s.truncated = (xml).at('truncated').innerHTML == 'false' ? false : true
15
+ s.favorited = (xml).at('favorited').innerHTML == 'false' ? false : true
16
+ s.in_reply_to_status_id = (xml).at('in_reply_to_status_id').innerHTML
17
+ s.in_reply_to_user_id = (xml).at('in_reply_to_user_id').innerHTML
18
18
  s.user = User.new_from_xml(xml.at('user')) if (xml).at('user')
19
19
  s
20
20
  end
@@ -10,26 +10,26 @@ module Twitter
10
10
  # Creates a new user from a piece of xml
11
11
  def self.new_from_xml(xml)
12
12
  u = new
13
- u.id = (xml).at('id').text
14
- u.name = (xml).at('name').text
15
- u.screen_name = (xml).at('screen_name').text
16
- u.location = (xml).at('location').text
17
- u.description = (xml).at('description').text
18
- u.url = (xml).at('url').text
19
- u.profile_image_url = (xml).at('profile_image_url').text
13
+ u.id = (xml).at('id').innerHTML
14
+ u.name = (xml).at('name').innerHTML
15
+ u.screen_name = (xml).at('screen_name').innerHTML
16
+ u.location = (xml).at('location').innerHTML
17
+ u.description = (xml).at('description').innerHTML
18
+ u.url = (xml).at('url').innerHTML
19
+ u.profile_image_url = (xml).at('profile_image_url').innerHTML
20
20
 
21
21
  # optional, not always present
22
- u.profile_background_color = (xml).at('profile_background_color').text if (xml).at('profile_background_color')
23
- u.profile_text_color = (xml).at('profile_text_color').text if (xml).at('profile_text_color')
24
- u.profile_link_color = (xml).at('profile_link_color').text if (xml).at('profile_link_color')
25
- u.profile_sidebar_fill_color = (xml).at('profile_sidebar_fill_color').text if (xml).at('profile_sidebar_fill_color')
26
- u.profile_sidebar_border_color = (xml).at('profile_sidebar_border_color').text if (xml).at('profile_sidebar_border_color')
27
- u.friends_count = (xml).at('friends_count').text if (xml).at('friends_count')
28
- u.followers_count = (xml).at('followers_count').text if (xml).at('followers_count')
29
- u.favourites_count = (xml).at('favourites_count').text if (xml).at('favourites_count')
30
- u.statuses_count = (xml).at('statuses_count').text if (xml).at('statuses_count')
31
- u.utc_offset = (xml).at('utc_offset').text if (xml).at('utc_offset')
32
- u.protected = (xml).at('protected').text == 'false' ? false : true if (xml).at('protected')
22
+ u.profile_background_color = (xml).at('profile_background_color').innerHTML if (xml).at('profile_background_color')
23
+ u.profile_text_color = (xml).at('profile_text_color').innerHTML if (xml).at('profile_text_color')
24
+ u.profile_link_color = (xml).at('profile_link_color').innerHTML if (xml).at('profile_link_color')
25
+ u.profile_sidebar_fill_color = (xml).at('profile_sidebar_fill_color').innerHTML if (xml).at('profile_sidebar_fill_color')
26
+ u.profile_sidebar_border_color = (xml).at('profile_sidebar_border_color').innerHTML if (xml).at('profile_sidebar_border_color')
27
+ u.friends_count = (xml).at('friends_count').innerHTML if (xml).at('friends_count')
28
+ u.followers_count = (xml).at('followers_count').innerHTML if (xml).at('followers_count')
29
+ u.favourites_count = (xml).at('favourites_count').innerHTML if (xml).at('favourites_count')
30
+ u.statuses_count = (xml).at('statuses_count').innerHTML if (xml).at('statuses_count')
31
+ u.utc_offset = (xml).at('utc_offset').innerHTML if (xml).at('utc_offset')
32
+ u.protected = (xml).at('protected').innerHTML == 'false' ? false : true if (xml).at('protected')
33
33
  u.status = Status.new_from_xml(xml.at('status')) if (xml).at('status')
34
34
  u
35
35
  end
@@ -1,9 +1,3 @@
1
1
  module Twitter #:nodoc:
2
- module VERSION #:nodoc:
3
- MAJOR = 0
4
- MINOR = 3
5
- TINY = 7
6
-
7
- STRING = [MAJOR, MINOR, TINY].join('.')
8
- end
2
+ Version = '0.4.2'
9
3
  end
@@ -16,7 +16,11 @@ describe "Twitter::Base" do
16
16
  lambda { @base.timeline(:fakeyoutey) }.should raise_error(Twitter::UnknownTimeline)
17
17
  end
18
18
 
19
- it "should default to friends timeline"
19
+ it "should default to friends timeline" do
20
+ @base.should_receive(:call).with("friends_timeline", {:auth=>true, :args=>{}, :since=>nil})
21
+ @base.should_receive(:statuses)
22
+ @base.timeline
23
+ end
20
24
 
21
25
  it "should be able to retrieve friends timeline" do
22
26
  data = open(File.dirname(__FILE__) + '/fixtures/friends_timeline.xml').read
@@ -50,6 +54,12 @@ describe "Twitter::Base" do
50
54
  @base.friends(:lite => true).size.should == 15
51
55
  end
52
56
 
57
+ it "should be able to get friend ids" do
58
+ data = open(File.dirname(__FILE__) + '/fixtures/friend_ids.xml').read
59
+ @base.should_receive(:request).and_return(Hpricot::XML(data))
60
+ @base.friend_ids.size.should == 8
61
+ end
62
+
53
63
  it "should be able to get friends for another user" do
54
64
  data = open(File.dirname(__FILE__) + '/fixtures/friends_for.xml').read
55
65
  @base.should_receive(:request).and_return(Hpricot::XML(data))
@@ -65,6 +75,26 @@ describe "Twitter::Base" do
65
75
  timeline.size.should == 29
66
76
  timeline.first.name.should == 'Blaine Cook'
67
77
  end
78
+
79
+ it "should be able to get follower ids" do
80
+ data = open(File.dirname(__FILE__) + '/fixtures/follower_ids.xml').read
81
+ @base.should_receive(:request).and_return(Hpricot::XML(data))
82
+ @base.follower_ids.size.should == 8
83
+ end
84
+
85
+ it "should be able to create a friendship" do
86
+ data = open(File.dirname(__FILE__) + '/fixtures/friendship_created.xml').read
87
+ @base.should_receive(:request).and_return(Hpricot::XML(data))
88
+ user = @base.create_friendship('jnunemaker')
89
+ end
90
+
91
+ it "should bomb if friendship already exists" do
92
+ data = open(File.dirname(__FILE__) + '/fixtures/friendship_already_exists.xml').read
93
+ response = Net::HTTPForbidden.new("1.1", '403', '')
94
+ response.stub!(:body).and_return(data)
95
+ @base.should_receive(:response).and_return(response)
96
+ lambda { @base.create_friendship('billymeltdown') }.should raise_error(Twitter::AlreadyFollowing)
97
+ end
68
98
  end
69
99
 
70
100
  it "should be able to get single status" do
@@ -106,4 +136,4 @@ describe "Twitter::Base" do
106
136
  @base.rate_limit_status.remaining_hits.should == 5
107
137
  end
108
138
  end
109
- end
139
+ end
@@ -8,6 +8,10 @@ def say(str)
8
8
  puts str
9
9
  end
10
10
 
11
+ class Tweet < OpenStruct
12
+ attr_accessor :id
13
+ end
14
+
11
15
  describe Twitter::CLI::Helpers do
12
16
  include Twitter::CLI::Helpers
13
17
 
@@ -15,21 +19,31 @@ describe Twitter::CLI::Helpers do
15
19
  before do
16
20
  Configuration.stub!(:[]=).and_return(true)
17
21
  @collection = [
18
- OpenStruct.new(
19
- :text => 'This is my long message that I want to see formatted ooooh so pretty with a few words on each line so it is easy to scan.',
22
+ Tweet.new(
23
+ :id => 1,
24
+ :text => 'This is my long message that I want to see formatted ooooh so pretty with a few words on each line so it is easy to scan.',
20
25
  :created_at => Time.mktime(2008, 5, 1, 10, 15, 00).strftime('%Y-%m-%d %H:%M:%S'),
21
- :user => OpenStruct.new(:screen_name => 'jnunemaker')
26
+ :user => OpenStruct.new(:screen_name => 'jnunemaker')
22
27
  ),
23
- OpenStruct.new(
24
- :text => 'This is my long message that I want to see formatted ooooh so pretty with a.',
28
+ Tweet.new(
29
+ :id => 2,
30
+ :text => 'This is my long message that I want to see formatted ooooh so pretty with a.',
25
31
  :created_at => Time.mktime(2008, 4, 1, 10, 15, 00).strftime('%Y-%m-%d %H:%M:%S'),
26
- :user => OpenStruct.new(:screen_name => 'danielmorrison')
32
+ :user => OpenStruct.new(:screen_name => 'danielmorrison')
27
33
  )
28
34
  ]
29
35
  end
30
36
 
31
37
  specify "should properly format" do
32
- output_tweets(@collection)
38
+ stdout_for {
39
+ output_tweets(@collection)
40
+ }.should match(/with a few words[\w\W]*with a\./)
41
+ end
42
+
43
+ specify 'should format in reverse' do
44
+ stdout_for {
45
+ output_tweets(@collection, :reverse => true)
46
+ }.should match(/with a\.[\w\W]*with a few words/)
33
47
  end
34
48
  end
35
49
  end