handcrafted-twitter 0.4.0 → 0.4.2

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