t 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/t/collectable.rb CHANGED
@@ -4,7 +4,7 @@ module T
4
4
  def collect_with_cursor(collection=[], cursor=-1, &block)
5
5
  object = yield cursor
6
6
  collection += object.collection
7
- object.next_cursor.zero? ? collection : collect_with_cursor(collection, object.next_cursor)
7
+ object.last? ? collection : collect_with_cursor(collection, object.next_cursor, &block)
8
8
  end
9
9
 
10
10
  end
data/lib/t/delete.rb ADDED
@@ -0,0 +1,84 @@
1
+ require 't/core_ext/string'
2
+ require 't/rcfile'
3
+ require 't/requestable'
4
+ require 'thor'
5
+
6
+ module T
7
+ class Delete < Thor
8
+ include T::Requestable
9
+
10
+ check_unknown_options!
11
+
12
+ def initialize(*)
13
+ super
14
+ @rcfile = RCFile.instance
15
+ end
16
+
17
+ desc "block SCREEN_NAME [SCREEN_NAME...]", "Unblock users."
18
+ def block(screen_name, *screen_names)
19
+ screen_names.unshift(screen_name)
20
+ screen_names.threaded_each do |screen_name|
21
+ screen_name.strip_at
22
+ retryable(:tries => 3, :on => Twitter::Error::ServerError, :sleep => 0) do
23
+ client.unblock(screen_name, :include_entities => false)
24
+ end
25
+ end
26
+ say "@#{@rcfile.default_profile[0]} unblocked @#{screen_names.join(' ')}."
27
+ say
28
+ say "Run `#{File.basename($0)} block #{screen_names.join(' ')}` to block."
29
+ end
30
+
31
+ desc "dm", "Delete the last Direct Message sent."
32
+ def dm
33
+ direct_message = client.direct_messages_sent(:count => 1, :include_entities => false).first
34
+ if direct_message
35
+ unless parent_options['force']
36
+ return unless yes? "Are you sure you want to permanently delete the direct message to @#{direct_message.recipient.screen_name}: \"#{direct_message.text}\"? [y/N]"
37
+ end
38
+ direct_message = client.direct_message_destroy(direct_message.id, :include_entities => false)
39
+ say "@#{direct_message.sender.screen_name} deleted the direct message sent to @#{direct_message.recipient.screen_name}: \"#{direct_message.text}\""
40
+ else
41
+ raise Thor::Error, "Direct Message not found"
42
+ end
43
+ end
44
+ map %w(m) => :dm
45
+
46
+ desc "favorite STATUS_ID [STATUS_ID...]", "Delete favorites."
47
+ def favorite(status_id, *status_ids)
48
+ status_ids.unshift(status_id)
49
+ status_ids.each do |status_id|
50
+ unless parent_options['force']
51
+ status = client.status(status_id, :include_entities => false, :include_my_retweet => false, :trim_user => true)
52
+ return unless yes? "Are you sure you want to delete the favorite of @#{status.user.screen_name}'s status: \"#{status.text}\"? [y/N]"
53
+ end
54
+ status = client.unfavorite(status_id, :include_entities => false)
55
+ say "@#{@rcfile.default_profile[0]} unfavorited @#{status.user.screen_name}'s status: \"#{status.text}\""
56
+ end
57
+ end
58
+ map %w(post tweet update) => :status
59
+
60
+ desc "list LIST_NAME", "Delete a list."
61
+ def list(list_name)
62
+ unless parent_options['force']
63
+ return unless yes? "Are you sure you want to permanently delete the list \"#{list_name}\"? [y/N]"
64
+ end
65
+ status = client.list_destroy(list_name)
66
+ say "@#{@rcfile.default_profile[0]} deleted the list \"#{list_name}\"."
67
+ end
68
+
69
+ desc "status STATUS_ID [STATUS_ID...]", "Delete Tweets."
70
+ def status(status_id, *status_ids)
71
+ status_ids.unshift(status_id)
72
+ status_ids.each do |status_id|
73
+ unless parent_options['force']
74
+ status = client.status(status_id, :include_entities => false, :include_my_retweet => false, :trim_user => true)
75
+ return unless yes? "Are you sure you want to permanently delete @#{status.user.screen_name}'s status: \"#{status.text}\"? [y/N]"
76
+ end
77
+ status = client.status_destroy(status_id, :include_entities => false, :trim_user => true)
78
+ say "@#{@rcfile.default_profile[0]} deleted the status: \"#{status.text}\""
79
+ end
80
+ end
81
+ map %w(post tweet update) => :status
82
+
83
+ end
84
+ end
data/lib/t/list.rb ADDED
@@ -0,0 +1,103 @@
1
+ require 'action_view'
2
+ require 'active_support/core_ext/array/grouping'
3
+ require 'retryable'
4
+ require 't/core_ext/enumerable'
5
+ require 't/core_ext/string'
6
+ require 't/rcfile'
7
+ require 't/requestable'
8
+ require 'thor'
9
+
10
+ module T
11
+ class List < Thor
12
+ include ActionView::Helpers::DateHelper
13
+ include T::Requestable
14
+
15
+ DEFAULT_NUM_RESULTS = 20
16
+ MAX_SCREEN_NAME_SIZE = 20
17
+ MAX_USERS_PER_LIST = 500
18
+ MAX_USERS_PER_REQUEST = 100
19
+
20
+ check_unknown_options!
21
+
22
+ def initialize(*)
23
+ super
24
+ @rcfile = RCFile.instance
25
+ end
26
+
27
+ desc "add LIST_NAME SCREEN_NAME [SCREEN_NAME...]", "Add users to a list."
28
+ def add(list_name, screen_name, *screen_names)
29
+ screen_names.unshift(screen_name)
30
+ screen_names.map!(&:strip_at)
31
+ screen_names.in_groups_of(MAX_USERS_PER_REQUEST, false).threaded_each do |user_id_group|
32
+ retryable(:tries => 3, :on => Twitter::Error::ServerError, :sleep => 0) do
33
+ client.list_add_members(list_name, user_id_group)
34
+ end
35
+ end
36
+ number = screen_names.length
37
+ say "@#{@rcfile.default_profile[0]} added #{number} #{number == 1 ? 'user' : 'users'} to the list \"#{list_name}\"."
38
+ say
39
+ say "Run `#{File.basename($0)} list remove users #{list_name} #{screen_names.join(' ')}` to undo."
40
+ end
41
+
42
+ desc "create LIST_NAME [DESCRIPTION]", "Create a new list."
43
+ method_option :private, :aliases => "-p", :type => :boolean
44
+ def create(list_name, description="")
45
+ opts = description.blank? ? {} : {:description => description}
46
+ opts.merge!(:mode => 'private') if options['private']
47
+ client.list_create(list_name, opts)
48
+ say "@#{@rcfile.default_profile[0]} created the list \"#{list_name}\"."
49
+ end
50
+
51
+ # Remove
52
+ desc "remove LIST_NAME SCREEN_NAME [SCREEN_NAME...]", "Remove users from a list."
53
+ def remove(list_name, screen_name, *screen_names)
54
+ screen_names.unshift(screen_name)
55
+ screen_names.map!(&:strip_at)
56
+ screen_names.in_groups_of(MAX_USERS_PER_REQUEST, false).threaded_each do |user_id_group|
57
+ retryable(:tries => 3, :on => Twitter::Error::ServerError, :sleep => 0) do
58
+ client.list_remove_members(list_name, user_id_group)
59
+ end
60
+ end
61
+ number = screen_names.length
62
+ say "@#{@rcfile.default_profile[0]} removed #{number} #{number == 1 ? 'user' : 'users'} from the list \"#{list_name}\"."
63
+ say
64
+ say "Run `#{File.basename($0)} list add users #{list_name} #{screen_names.join(' ')}` to undo."
65
+ end
66
+
67
+ desc "timeline [SCREEN_NAME] LIST_NAME", "Show tweet timeline for members of the specified list."
68
+ method_option :created, :aliases => "-c", :type => :boolean, :default => false, :desc => "Sort by the time when Twitter acount was created."
69
+ method_option :friends, :aliases => "-d", :type => :boolean, :default => false, :desc => "Sort by total number of friends."
70
+ method_option :followers, :aliases => "-f", :type => :boolean, :default => false, :desc => "Sort by total number of followers."
71
+ method_option :listed, :aliases => "-i", :type => :boolean, :default => false, :desc => "Sort by number of list memberships."
72
+ method_option :long, :aliases => "-l", :type => :boolean, :default => false, :desc => "List in long format."
73
+ method_option :number, :aliases => "-n", :type => :numeric, :default => DEFAULT_NUM_RESULTS
74
+ method_option :reverse, :aliases => "-r", :type => :boolean, :default => false, :desc => "Reverse the order of the sort."
75
+ method_option :tweets, :aliases => "-t", :type => :boolean, :default => false, :desc => "Sort by total number of Tweets."
76
+ method_option :unsorted, :aliases => "-u", :type => :boolean, :default => false, :desc => "Output is not sorted."
77
+ method_option :favorites, :aliases => "-v", :type => :boolean, :default => false, :desc => "Sort by total number of favorites."
78
+ def timeline(*args)
79
+ list = args.pop
80
+ owner = args.pop || @rcfile.default_profile[0]
81
+ per_page = options['number'] || DEFAULT_NUM_RESULTS
82
+ statuses = client.list_timeline(owner, list, :include_entities => false, :per_page => per_page)
83
+ statuses.reverse! if options['reverse']
84
+ if options['long']
85
+ array = statuses.map do |status|
86
+ created_at = status.created_at > 6.months.ago ? status.created_at.strftime("%b %e %H:%M") : status.created_at.strftime("%b %e %Y")
87
+ [status.id.to_s, created_at, status.user.screen_name, status.text.gsub(/\n+/, ' ')]
88
+ end
89
+ if STDOUT.tty?
90
+ headings = ["ID", "Created at", "Screen name", "Text"]
91
+ array.unshift(headings)
92
+ end
93
+ print_table(array)
94
+ else
95
+ statuses.each do |status|
96
+ say "#{status.user.screen_name.rjust(MAX_SCREEN_NAME_SIZE)}: #{status.text.gsub(/\n+/, ' ')} (#{time_ago_in_words(status.created_at)} ago)"
97
+ end
98
+ end
99
+ end
100
+ map %w(tl) => :timeline
101
+
102
+ end
103
+ end
data/lib/t/search.rb ADDED
@@ -0,0 +1,133 @@
1
+ require 'action_view'
2
+ require 'retryable'
3
+ require 't/core_ext/enumerable'
4
+ require 't/rcfile'
5
+ require 't/requestable'
6
+ require 'thor'
7
+
8
+ module T
9
+ class Search < Thor
10
+ include ActionView::Helpers::DateHelper
11
+ include T::Requestable
12
+
13
+ DEFAULT_NUM_RESULTS = 20
14
+ MAX_PAGES = 16
15
+ MAX_NUM_RESULTS = 200
16
+ MAX_SCREEN_NAME_SIZE = 20
17
+
18
+ check_unknown_options!
19
+
20
+ def initialize(*)
21
+ super
22
+ @rcfile = RCFile.instance
23
+ end
24
+
25
+ desc "all QUERY", "Returns the #{DEFAULT_NUM_RESULTS} most recent Tweets that match a specified query."
26
+ method_option :number, :aliases => "-n", :type => :numeric, :default => DEFAULT_NUM_RESULTS
27
+ def all(query)
28
+ rpp = options['number'] || DEFAULT_NUM_RESULTS
29
+ statuses = client.search(query, :include_entities => false, :rpp => rpp)
30
+ if parent_options['long']
31
+ array = statuses.map do |status|
32
+ created_at = status.created_at > 6.months.ago ? status.created_at.strftime("%b %e %H:%M") : status.created_at.strftime("%b %e %Y")
33
+ [status.id.to_s, created_at, status.from_user, status.text.gsub(/\n+/, ' ')]
34
+ end
35
+ if STDOUT.tty?
36
+ headings = ["ID", "Created at", "Screen name", "Text"]
37
+ array.unshift(headings)
38
+ end
39
+ print_table(array)
40
+ else
41
+ statuses.each do |status|
42
+ say "#{status.from_user.rjust(MAX_SCREEN_NAME_SIZE)}: #{status.text.gsub(/\n+/, ' ')} (#{time_ago_in_words(status.created_at)} ago)"
43
+ end
44
+ end
45
+ end
46
+
47
+ desc "favorites QUERY", "Returns Tweets you've favorited that mach a specified query."
48
+ def favorites(query)
49
+ statuses = 1.upto(MAX_PAGES).threaded_map do |page|
50
+ retryable(:tries => 3, :on => Twitter::Error::ServerError, :sleep => 0) do
51
+ client.favorites(:page => page, :count => MAX_NUM_RESULTS).select do |status|
52
+ /#{query}/i.match(status.text)
53
+ end
54
+ end
55
+ end.flatten.compact
56
+ print_status_list(statuses)
57
+ end
58
+ map %w(faves) => :favorites
59
+
60
+ desc "mentions QUERY", "Returns Tweets mentioning you that mach a specified query."
61
+ def mentions(query)
62
+ statuses = 1.upto(MAX_PAGES).threaded_map do |page|
63
+ retryable(:tries => 3, :on => Twitter::Error::ServerError, :sleep => 0) do
64
+ client.mentions(:page => page, :count => MAX_NUM_RESULTS).select do |status|
65
+ /#{query}/i.match(status.text)
66
+ end
67
+ end
68
+ end.flatten.compact
69
+ print_status_list(statuses)
70
+ end
71
+ map %w(replies) => :mentions
72
+
73
+ desc "retweets QUERY", "Returns Tweets you've retweeted that mach a specified query."
74
+ def retweets(query)
75
+ statuses = 1.upto(MAX_PAGES).threaded_map do |page|
76
+ retryable(:tries => 3, :on => Twitter::Error::ServerError, :sleep => 0) do
77
+ client.retweeted_by(:page => page, :count => MAX_NUM_RESULTS).select do |status|
78
+ /#{query}/i.match(status.text)
79
+ end
80
+ end
81
+ end.flatten.compact
82
+ print_status_list(statuses)
83
+ end
84
+ map %w(rts) => :retweets
85
+
86
+ desc "timeline QUERY", "Returns Tweets in your timeline that match a specified query."
87
+ def timeline(query)
88
+ statuses = 1.upto(MAX_PAGES).threaded_map do |page|
89
+ retryable(:tries => 3, :on => Twitter::Error::ServerError, :sleep => 0) do
90
+ client.home_timeline(:page => page, :count => MAX_NUM_RESULTS).select do |status|
91
+ /#{query}/i.match(status.text)
92
+ end
93
+ end
94
+ end.flatten.compact
95
+ print_status_list(statuses)
96
+ end
97
+ map %w(tl) => :timeline
98
+
99
+ desc "user SCREEN_NAME QUERY", "Returns Tweets in a user's timeline that match a specified query."
100
+ def user(screen_name, query)
101
+ screen_name = screen_name.strip_at
102
+ statuses = 1.upto(MAX_PAGES).threaded_map do |page|
103
+ retryable(:tries => 3, :on => Twitter::Error::ServerError, :sleep => 0) do
104
+ client.user_timeline(screen_name, :page => page, :count => MAX_NUM_RESULTS).select do |status|
105
+ /#{query}/i.match(status.text)
106
+ end
107
+ end
108
+ end.flatten.compact
109
+ print_status_list(statuses)
110
+ end
111
+
112
+ private
113
+
114
+ def print_status_list(statuses)
115
+ if parent_options['long']
116
+ array = statuses.map do |status|
117
+ created_at = status.created_at > 6.months.ago ? status.created_at.strftime("%b %e %H:%M") : status.created_at.strftime("%b %e %Y")
118
+ [status.id.to_s, created_at, status.user.screen_name, status.text.gsub(/\n+/, ' ')]
119
+ end
120
+ if STDOUT.tty?
121
+ headings = ["ID", "Created at", "Screen name", "Text"]
122
+ array.unshift(headings)
123
+ end
124
+ print_table(array)
125
+ else
126
+ statuses.each do |status|
127
+ say "#{status.user.screen_name.rjust(MAX_SCREEN_NAME_SIZE)}: #{status.text.gsub(/\n+/, ' ')} (#{time_ago_in_words(status.created_at)} ago)"
128
+ end
129
+ end
130
+ end
131
+
132
+ end
133
+ end
data/lib/t/set.rb ADDED
@@ -0,0 +1,57 @@
1
+ require 't/core_ext/string'
2
+ require 't/rcfile'
3
+ require 't/requestable'
4
+ require 'thor'
5
+
6
+ module T
7
+ class Set < Thor
8
+ include T::Requestable
9
+
10
+ check_unknown_options!
11
+
12
+ def initialize(*)
13
+ super
14
+ @rcfile = RCFile.instance
15
+ end
16
+
17
+ desc "bio DESCRIPTION", "Edits your Bio information on your Twitter profile."
18
+ def bio(description)
19
+ client.update_profile(:description => description, :include_entities => false)
20
+ say "@#{@rcfile.default_profile[0]}'s bio has been updated."
21
+ end
22
+
23
+ desc "default SCREEN_NAME [CONSUMER_KEY]", "Set your default account."
24
+ def default(screen_name, consumer_key=nil)
25
+ screen_name = screen_name.strip_at
26
+ @rcfile.path = parent_options['profile'] if parent_options['profile']
27
+ consumer_key = @rcfile[screen_name].keys.last if consumer_key.nil?
28
+ @rcfile.default_profile = {'username' => screen_name, 'consumer_key' => consumer_key}
29
+ say "Default account has been updated."
30
+ end
31
+
32
+ desc "language LANGUAGE_NAME", "Selects the language you'd like to receive notifications in."
33
+ def language(language_name)
34
+ client.settings(:lang => language_name)
35
+ say "@#{@rcfile.default_profile[0]}'s language has been updated."
36
+ end
37
+
38
+ desc "location PLACE_NAME", "Updates the location field in your profile."
39
+ def location(place_name)
40
+ client.update_profile(:location => place_name, :include_entities => false)
41
+ say "@#{@rcfile.default_profile[0]}'s location has been updated."
42
+ end
43
+
44
+ desc "name NAME", "Sets the name field on your Twitter profile."
45
+ def name(name)
46
+ client.update_profile(:name => name, :include_entities => false)
47
+ say "@#{@rcfile.default_profile[0]}'s name has been updated."
48
+ end
49
+
50
+ desc "url URL", "Sets the URL field on your profile."
51
+ def url(url)
52
+ client.update_profile(:url => url, :include_entities => false)
53
+ say "@#{@rcfile.default_profile[0]}'s URL has been updated."
54
+ end
55
+
56
+ end
57
+ end
data/lib/t/version.rb CHANGED
@@ -8,7 +8,7 @@ module T
8
8
 
9
9
  # @return [Integer]
10
10
  def self.minor
11
- 4
11
+ 5
12
12
  end
13
13
 
14
14
  # @return [Integer]
data/spec/cli_spec.rb CHANGED
@@ -89,13 +89,13 @@ describe T::CLI do
89
89
  describe "#direct_messages" do
90
90
  before do
91
91
  stub_get("/1/direct_messages.json").
92
- with(:query => {:include_entities => "false"}).
92
+ with(:query => {:count => "20", :include_entities => "false"}).
93
93
  to_return(:body => fixture("direct_messages.json"), :headers => {:content_type => "application/json; charset=utf-8"})
94
94
  end
95
95
  it "should request the correct resource" do
96
96
  @t.direct_messages
97
97
  a_get("/1/direct_messages.json").
98
- with(:query => {:include_entities => "false"}).
98
+ with(:query => {:count => "20", :include_entities => "false"}).
99
99
  should have_been_made
100
100
  end
101
101
  it "should have the correct output" do
@@ -125,6 +125,45 @@ describe T::CLI do
125
125
  end
126
126
  end
127
127
 
128
+ describe "#direct_messages_sent" do
129
+ before do
130
+ stub_get("/1/direct_messages/sent.json").
131
+ with(:query => {:count => "20", :include_entities => "false"}).
132
+ to_return(:body => fixture("direct_messages.json"), :headers => {:content_type => "application/json; charset=utf-8"})
133
+ end
134
+ it "should request the correct resource" do
135
+ @t.direct_messages_sent
136
+ a_get("/1/direct_messages/sent.json").
137
+ with(:query => {:count => "20", :include_entities => "false"}).
138
+ should have_been_made
139
+ end
140
+ it "should have the correct output" do
141
+ @t.direct_messages_sent
142
+ $stdout.string.should == <<-eos.gsub(/^/, ' ' * 3)
143
+ hurrycane: Sounds good. Meeting Tuesday is fine. (about 1 year ago)
144
+ technoweenie: if you want to add me to your GroupMe group, my phone number is 415-312-2382 (about 1 year ago)
145
+ hurrycane: That's great news! Let's plan to chat around 8 AM tomorrow Pacific time. Does that work for you? (about 1 year ago)
146
+ hurrycane: I asked Yehuda about the stipend. I believe it has already been sent. Glad you're feeling better. (about 1 year ago)
147
+ hurrycane: Just checking in. How's everything going? (about 1 year ago)
148
+ hurrycane: Any luck completing graphs this weekend? There have been lots of commits to RailsAdmin since summer ended but none from you. How's it going? (about 1 year ago)
149
+ hurrycane: Not sure about the payment. Feel free to ask Leah or Yehuda directly. Think you'll be able to finish up your work on graphs this weekend? (about 1 year ago)
150
+ hurrycane: Looks good to me. I'm going to pull in the change now. My only concern is that we don't have any tests for auth. (about 1 year ago)
151
+ hurrycane: How are the graph enhancements coming? (about 1 year ago)
152
+ hurrycane: Changes pushed. You should pull and re-bundle when you have a minute. (about 1 year ago)
153
+ hurrycane: Glad to hear the new graphs are coming along. Can't wait to see them! (about 1 year ago)
154
+ hurrycane: I figured out what was wrong with the tests: I accidentally unbundled webrat. The problem had nothing to do with rspec-rails. (about 1 year ago)
155
+ hurrycane: After the upgrade 54/80 specs are failing. I'm working on fixing them now. (about 1 year ago)
156
+ hurrycane: a new version of rspec-rails just shipped with some nice features and fixes http://github.com/rspec/rspec-rails/blob/master/History.md (about 1 year ago)
157
+ hurrycane: How are the graphs coming? I'm really looking forward to seeing what you do with Raphaël. (about 1 year ago)
158
+ hurrycane: Awesome! Any luck duplicating the Gemfile.lock error with Ruby 1.9.2 final? (about 1 year ago)
159
+ hurrycane: I just committed a bunch of cleanup and fixes to RailsAdmin that touched many of files. Make sure you pull to avoid conflicts. (about 1 year ago)
160
+ hurrycane: Can you try upgrading to 1.9.2 final, re-installing Bundler 1.0.0.rc.6 (don't remove 1.0.0) and see if you can reproduce the problem? (about 1 year ago)
161
+ hurrycane: I'm trying to debug the issue you were having with the Bundler Gemfile.lock shortref. What version of Ruby and RubyGems are you running? (about 1 year ago)
162
+ hurrycane: Let's try to debug that problem during our session in 1.5 hours. In the mean time, try working on the graphs or internationalization. (about 1 year ago)
163
+ eos
164
+ end
165
+ end
166
+
128
167
  describe "#dm" do
129
168
  before do
130
169
  @t.options = @t.options.merge(:profile => fixture_path + "/.trc")
@@ -140,84 +179,37 @@ describe T::CLI do
140
179
  end
141
180
  it "should have the correct output" do
142
181
  @t.dm("pengwynn", "Creating a fixture for the Twitter gem")
143
- $stdout.string.chomp.should == "Direct Message sent from @testcli to @pengwynn (about 1 year ago)"
182
+ $stdout.string.chomp.should == "Direct Message sent from @testcli to @pengwynn (about 1 year ago)."
144
183
  end
145
184
  end
146
185
 
147
186
  describe "#favorite" do
148
187
  before do
149
188
  @t.options = @t.options.merge(:profile => fixture_path + "/.trc")
189
+ stub_post("/1/favorites/create/26755176471724032.json").
190
+ to_return(:body => fixture("status.json"), :headers => {:content_type => "application/json; charset=utf-8"})
150
191
  end
151
- context "not found" do
152
- before do
153
- stub_get("/1/users/show.json").
154
- with(:query => {:screen_name => "sferik", :include_entities => "false"}).
155
- to_return(:body => '{}', :headers => {:content_type => "application/json; charset=utf-8"})
156
- end
157
- it "should exit" do
158
- lambda do
159
- @t.favorite("sferik")
160
- end.should raise_error(Thor::Error, "Tweet not found")
161
- end
162
- end
163
- context "forbidden" do
164
- before do
165
- stub_get("/1/users/show.json").
166
- with(:query => {:screen_name => "sferik", :include_entities => "false"}).
167
- to_return(:body => '{"error":"Forbidden"}', :headers => {:content_type => "application/json; charset=utf-8"}, :status => 403)
168
- end
169
- it "should exit" do
170
- lambda do
171
- @t.favorite("sferik")
172
- end.should raise_error(Twitter::Error::Forbidden, "Forbidden")
173
- end
174
- end
175
- context "duplicate" do
176
- before do
177
- stub_get("/1/users/show.json").
178
- with(:query => {:screen_name => "sferik", :include_entities => "false"}).
179
- to_return(:body => fixture("sferik.json"), :headers => {:content_type => "application/json; charset=utf-8"})
180
- stub_post("/1/favorites/create/26755176471724032.json").
181
- to_return(:body => '{"error":"You have already favorited this status."}', :headers => {:content_type => "application/json; charset=utf-8"}, :status => 403)
182
- end
183
- it "should have the correct output" do
184
- @t.favorite("sferik")
185
- $stdout.string.should =~ /^@testcli favorited @sferik's latest status: "RT @tenderlove: \[ANN\] sqlite3-ruby =&gt; sqlite3"$/
186
- end
192
+ it "should request the correct resource" do
193
+ @t.favorite("26755176471724032")
194
+ a_post("/1/favorites/create/26755176471724032.json").
195
+ should have_been_made
187
196
  end
188
- context "found" do
189
- before do
190
- stub_get("/1/users/show.json").
191
- with(:query => {:screen_name => "sferik", :include_entities => "false"}).
192
- to_return(:body => fixture("sferik.json"), :headers => {:content_type => "application/json; charset=utf-8"})
193
- stub_post("/1/favorites/create/26755176471724032.json").
194
- to_return(:body => fixture("status.json"), :headers => {:content_type => "application/json; charset=utf-8"})
195
- end
196
- it "should request the correct resource" do
197
- @t.favorite("sferik")
198
- a_get("/1/users/show.json").
199
- with(:query => {:screen_name => "sferik", :include_entities => "false"}).
200
- should have_been_made
201
- a_post("/1/favorites/create/26755176471724032.json").
202
- should have_been_made
203
- end
204
- it "should have the correct output" do
205
- @t.favorite("sferik")
206
- $stdout.string.should =~ /^@testcli favorited @sferik's latest status: "RT @tenderlove: \[ANN\] sqlite3-ruby =&gt; sqlite3"$/
207
- end
197
+ it "should have the correct output" do
198
+ @t.favorite("26755176471724032")
199
+ $stdout.string.should =~ /^@testcli favorited @sferik's status: "@noradio working on implementing #NewTwitter API methods in the twitter gem\. Twurl is making it easy\. Thank you!"$/
208
200
  end
209
201
  end
210
202
 
211
203
  describe "#favorites" do
212
204
  before do
213
205
  stub_get("/1/favorites.json").
214
- with(:query => {:include_entities => "false"}).
206
+ with(:query => {:count => "20", :include_entities => "false"}).
215
207
  to_return(:body => fixture("statuses.json"), :headers => {:content_type => "application/json; charset=utf-8"})
216
208
  end
217
209
  it "should request the correct resource" do
218
210
  @t.favorites
219
211
  a_get("/1/favorites.json").
220
- with(:query => {:include_entities => "false"}).
212
+ with(:query => {:count => "20", :include_entities => "false"}).
221
213
  should have_been_made
222
214
  end
223
215
  it "should have the correct output" do
@@ -246,16 +238,180 @@ describe T::CLI do
246
238
  end
247
239
  end
248
240
 
241
+ describe "#follow" do
242
+ before do
243
+ @t.options = @t.options.merge(:profile => fixture_path + "/.trc")
244
+ end
245
+ context "no users" do
246
+ it "should exit" do
247
+ lambda do
248
+ @t.follow
249
+ end.should raise_error
250
+ end
251
+ end
252
+ context "one user" do
253
+ it "should request the correct resource" do
254
+ stub_post("/1/friendships/create.json").
255
+ with(:body => {:screen_name => "sferik", :include_entities => "false"}).
256
+ to_return(:body => fixture("sferik.json"), :headers => {:content_type => "application/json; charset=utf-8"})
257
+ @t.follow("sferik")
258
+ a_post("/1/friendships/create.json").
259
+ with(:body => {:screen_name => "sferik", :include_entities => "false"}).
260
+ should have_been_made
261
+ end
262
+ it "should have the correct output" do
263
+ stub_post("/1/friendships/create.json").
264
+ with(:body => {:screen_name => "sferik", :include_entities => "false"}).
265
+ to_return(:body => fixture("sferik.json"), :headers => {:content_type => "application/json; charset=utf-8"})
266
+ @t.follow("sferik")
267
+ $stdout.string.should =~ /^@testcli is now following 1 more user\.$/
268
+ end
269
+ context "Twitter is down" do
270
+ it "should retry 3 times and then raise an error" do
271
+ stub_post("/1/friendships/create.json").
272
+ with(:body => {:screen_name => "sferik", :include_entities => "false"}).
273
+ to_return(:status => 502)
274
+ lambda do
275
+ @t.follow("sferik")
276
+ end.should raise_error("Twitter is down or being upgraded.")
277
+ a_post("/1/friendships/create.json").
278
+ with(:body => {:screen_name => "sferik", :include_entities => "false"}).
279
+ should have_been_made.times(3)
280
+ end
281
+ end
282
+ end
283
+ end
284
+
285
+ describe "#followings" do
286
+ before do
287
+ stub_get("/1/friends/ids.json").
288
+ with(:query => {:cursor => "-1"}).
289
+ to_return(:body => fixture("friends_ids.json"), :headers => {:content_type => "application/json; charset=utf-8"})
290
+ stub_get("/1/users/lookup.json").
291
+ with(:query => {:user_id => "7505382", :include_entities => "false"}).
292
+ to_return(:body => fixture("users.json"), :headers => {:content_type => "application/json; charset=utf-8"})
293
+ end
294
+ it "should request the correct resource" do
295
+ @t.followings
296
+ a_get("/1/friends/ids.json").
297
+ with(:query => {:cursor => "-1"}).
298
+ should have_been_made
299
+ a_get("/1/users/lookup.json").
300
+ with(:query => {:user_id => "7505382", :include_entities => "false"}).
301
+ should have_been_made
302
+ end
303
+ it "should have the correct output" do
304
+ @t.followings
305
+ $stdout.string.chomp.rstrip.should == "pengwynn sferik"
306
+ end
307
+ end
308
+
309
+ describe "#followers" do
310
+ before do
311
+ stub_get("/1/followers/ids.json").
312
+ with(:query => {:cursor => "-1"}).
313
+ to_return(:body => fixture("friends_ids.json"), :headers => {:content_type => "application/json; charset=utf-8"})
314
+ stub_get("/1/users/lookup.json").
315
+ with(:query => {:user_id => "7505382", :include_entities => "false"}).
316
+ to_return(:body => fixture("users.json"), :headers => {:content_type => "application/json; charset=utf-8"})
317
+ end
318
+ it "should request the correct resource" do
319
+ @t.followers
320
+ a_get("/1/followers/ids.json").
321
+ with(:query => {:cursor => "-1"}).
322
+ should have_been_made
323
+ a_get("/1/users/lookup.json").
324
+ with(:query => {:user_id => "7505382", :include_entities => "false"}).
325
+ should have_been_made
326
+ end
327
+ it "should have the correct output" do
328
+ @t.followers
329
+ $stdout.string.chomp.rstrip.should == "pengwynn sferik"
330
+ end
331
+ end
332
+
333
+ describe "#friends" do
334
+ before do
335
+ stub_get("/1/friends/ids.json").
336
+ with(:query => {:cursor => "-1"}).
337
+ to_return(:body => fixture("friends_ids.json"), :headers => {:content_type => "application/json; charset=utf-8"})
338
+ stub_get("/1/followers/ids.json").
339
+ with(:query => {:cursor => "-1"}).
340
+ to_return(:body => fixture("friends_ids.json"), :headers => {:content_type => "application/json; charset=utf-8"})
341
+ stub_get("/1/users/lookup.json").
342
+ with(:query => {:user_id => "7505382", :include_entities => "false"}).
343
+ to_return(:body => fixture("users.json"), :headers => {:content_type => "application/json; charset=utf-8"})
344
+ end
345
+ it "should request the correct resource" do
346
+ @t.friends
347
+ a_get("/1/friends/ids.json").
348
+ with(:query => {:cursor => "-1"}).
349
+ should have_been_made
350
+ a_get("/1/followers/ids.json").
351
+ with(:query => {:cursor => "-1"}).
352
+ should have_been_made
353
+ a_get("/1/users/lookup.json").
354
+ with(:query => {:user_id => "7505382", :include_entities => "false"}).
355
+ should have_been_made
356
+ end
357
+ it "should have the correct output" do
358
+ @t.friends
359
+ $stdout.string.chomp.rstrip.should == "pengwynn sferik"
360
+ end
361
+ end
362
+
363
+ describe "#leaders" do
364
+ before do
365
+ stub_get("/1/friends/ids.json").
366
+ with(:query => {:cursor => "-1"}).
367
+ to_return(:body => fixture("friends_ids.json"), :headers => {:content_type => "application/json; charset=utf-8"})
368
+ stub_get("/1/followers/ids.json").
369
+ with(:query => {:cursor => "-1"}).
370
+ to_return(:body => fixture("friends_ids.json"), :headers => {:content_type => "application/json; charset=utf-8"})
371
+ end
372
+ it "should request the correct resource" do
373
+ @t.leaders
374
+ a_get("/1/friends/ids.json").
375
+ with(:query => {:cursor => "-1"}).
376
+ should have_been_made
377
+ a_get("/1/followers/ids.json").
378
+ with(:query => {:cursor => "-1"}).
379
+ should have_been_made
380
+ end
381
+ it "should have the correct output" do
382
+ @t.leaders
383
+ $stdout.string.chomp.rstrip.should == ""
384
+ end
385
+ end
386
+
387
+ describe "#members" do
388
+ before do
389
+ stub_get("/1/lists/members.json").
390
+ with(:query => {:cursor => "-1", :include_entities => "false", :owner_screen_name => "sferik", :skip_status => "true", :slug => "presidents"}).
391
+ to_return(:body => fixture("empty_cursor.json"), :headers => {:content_type => "application/json; charset=utf-8"})
392
+ end
393
+ it "should request the correct resource" do
394
+ @t.members("sferik", "presidents")
395
+ a_get("/1/lists/members.json").
396
+ with(:query => {:cursor => "-1", :include_entities => "false", :owner_screen_name => "sferik", :skip_status => "true", :slug => "presidents"}).
397
+ should have_been_made
398
+ end
399
+ it "should have the correct output" do
400
+ @t.members("sferik", "presidents")
401
+ $stdout.string.chomp.should == ""
402
+ end
403
+ end
404
+
249
405
  describe "#mentions" do
250
406
  before do
251
407
  stub_get("/1/statuses/mentions.json").
252
- with(:query => {:include_entities => "false"}).
408
+ with(:query => {:count => "20", :include_entities => "false"}).
253
409
  to_return(:body => fixture("statuses.json"), :headers => {:content_type => "application/json; charset=utf-8"})
254
410
  end
255
411
  it "should request the correct resource" do
256
412
  @t.mentions
257
413
  a_get("/1/statuses/mentions.json").
258
- with(:query => {:include_entities => "false"}).
414
+ with(:query => {:count => "20", :include_entities => "false"}).
259
415
  should have_been_made
260
416
  end
261
417
  it "should have the correct output" do
@@ -298,11 +454,11 @@ describe T::CLI do
298
454
  describe "#reply" do
299
455
  before do
300
456
  @t.options = @t.options.merge(:profile => fixture_path + "/.trc", :location => true)
301
- stub_get("/1/users/show.json").
302
- with(:query => {:screen_name => "sferik", :include_entities => "false"}).
303
- to_return(:body => fixture("sferik.json"), :headers => {:content_type => "application/json; charset=utf-8"})
457
+ stub_get("/1/statuses/show/25938088801.json").
458
+ with(:query => {:include_entities => "false", :include_my_retweet => "false", :trim_user => "true"}).
459
+ to_return(:body => fixture("status.json"), :headers => {:content_type => "application/json; charset=utf-8"})
304
460
  stub_post("/1/statuses/update.json").
305
- with(:body => {:in_reply_to_status_id => "26755176471724032", :status => "@sferik Testing", :lat => "37.76969909668", :long => "-122.39330291748", :include_entities => "false", :trim_user => "true"}).
461
+ with(:body => {:in_reply_to_status_id => "25938088801", :status => "@sferik Testing", :lat => "37.76969909668", :long => "-122.39330291748", :include_entities => "false", :trim_user => "true"}).
306
462
  to_return(:body => fixture("status.json"), :headers => {:content_type => "application/json; charset=utf-8"})
307
463
  stub_request(:get, "http://checkip.dyndns.org/").
308
464
  to_return(:body => fixture("checkip.html"), :headers => {:content_type => "text/html"})
@@ -310,12 +466,12 @@ describe T::CLI do
310
466
  to_return(:body => fixture("xml.gp"), :headers => {:content_type => "application/xml"})
311
467
  end
312
468
  it "should request the correct resource" do
313
- @t.reply("sferik", "Testing")
314
- a_get("/1/users/show.json").
315
- with(:query => {:screen_name => "sferik", :include_entities => "false"}).
469
+ @t.reply("25938088801", "Testing")
470
+ a_get("/1/statuses/show/25938088801.json").
471
+ with(:query => {:include_entities => "false", :include_my_retweet => "false", :trim_user => "true"}).
316
472
  should have_been_made
317
473
  a_post("/1/statuses/update.json").
318
- with(:body => {:in_reply_to_status_id => "26755176471724032", :status => "@sferik Testing", :lat => "37.76969909668", :long => "-122.39330291748", :include_entities => "false", :trim_user => "true"}).
474
+ with(:body => {:in_reply_to_status_id => "25938088801", :status => "@sferik Testing", :lat => "37.76969909668", :long => "-122.39330291748", :include_entities => "false", :trim_user => "true"}).
319
475
  should have_been_made
320
476
  a_request(:get, "http://checkip.dyndns.org/").
321
477
  should have_been_made
@@ -323,72 +479,44 @@ describe T::CLI do
323
479
  should have_been_made
324
480
  end
325
481
  it "should have the correct output" do
326
- @t.reply("sferik", "Testing")
327
- $stdout.string.should =~ /^Reply created by @testcli to @sferik \(about 1 year ago\)$/
482
+ @t.reply("25938088801", "Testing")
483
+ $stdout.string.should =~ /^Reply created by @testcli to @sferik \(about 1 year ago\)\.$/
328
484
  end
329
485
  end
330
486
 
331
- describe "#retweet" do
487
+ describe "#report_spam" do
332
488
  before do
333
489
  @t.options = @t.options.merge(:profile => fixture_path + "/.trc")
490
+ stub_post("/1/report_spam.json").
491
+ with(:body => {:screen_name => "sferik", :include_entities => "false"}).
492
+ to_return(:body => fixture("sferik.json"), :headers => {:content_type => "application/json; charset=utf-8"})
334
493
  end
335
- context "not found" do
336
- before do
337
- stub_get("/1/users/show.json").
338
- with(:query => {:screen_name => "sferik", :include_entities => "false"}).
339
- to_return(:body => '{}', :headers => {:content_type => "application/json; charset=utf-8"})
340
- end
341
- it "should exit" do
342
- lambda do
343
- @t.retweet("sferik")
344
- end.should raise_error(Thor::Error, "Tweet not found")
345
- end
494
+ it "should request the correct resource" do
495
+ @t.report_spam("sferik")
496
+ a_post("/1/report_spam.json").
497
+ with(:body => {:screen_name => "sferik", :include_entities => "false"}).
498
+ should have_been_made
346
499
  end
347
- context "forbidden" do
348
- before do
349
- stub_get("/1/users/show.json").
350
- with(:query => {:screen_name => "sferik", :include_entities => "false"}).
351
- to_return(:body => '{"error":"Forbidden"}', :headers => {:content_type => "application/json; charset=utf-8"}, :status => 403)
352
- end
353
- it "should exit" do
354
- lambda do
355
- @t.retweet("sferik")
356
- end.should raise_error(Twitter::Error::Forbidden, "Forbidden")
357
- end
500
+ it "should have the correct output" do
501
+ @t.report_spam("sferik")
502
+ $stdout.string.should =~ /^@testcli reported @sferik/
358
503
  end
359
- context "duplicate" do
360
- before do
361
- stub_get("/1/users/show.json").
362
- with(:query => {:screen_name => "sferik", :include_entities => "false"}).
363
- to_return(:body => fixture("sferik.json"), :headers => {:content_type => "application/json; charset=utf-8"})
364
- stub_post("/1/statuses/retweet/26755176471724032.json").
365
- to_return(:body => '{"error":"sharing is not permissable for this status (Share validations failed)"}', :headers => {:content_type => "application/json; charset=utf-8"}, :status => 403)
366
- end
367
- it "should have the correct output" do
368
- @t.retweet("sferik")
369
- $stdout.string.should =~ /^@testcli retweeted @sferik's latest status: "RT @tenderlove: \[ANN\] sqlite3-ruby =&gt; sqlite3"$/
370
- end
504
+ end
505
+
506
+ describe "#retweet" do
507
+ before do
508
+ @t.options = @t.options.merge(:profile => fixture_path + "/.trc")
509
+ stub_post("/1/statuses/retweet/26755176471724032.json").
510
+ to_return(:body => fixture("retweet.json"), :headers => {:content_type => "application/json; charset=utf-8"})
371
511
  end
372
- context "found" do
373
- before do
374
- stub_get("/1/users/show.json").
375
- with(:query => {:screen_name => "sferik", :include_entities => "false"}).
376
- to_return(:body => fixture("sferik.json"), :headers => {:content_type => "application/json; charset=utf-8"})
377
- stub_post("/1/statuses/retweet/26755176471724032.json").
378
- to_return(:body => fixture("retweet.json"), :headers => {:content_type => "application/json; charset=utf-8"})
379
- end
380
- it "should request the correct resource" do
381
- @t.retweet("sferik")
382
- a_get("/1/users/show.json").
383
- with(:query => {:screen_name => "sferik", :include_entities => "false"}).
384
- should have_been_made
385
- a_post("/1/statuses/retweet/26755176471724032.json").
386
- should have_been_made
387
- end
388
- it "should have the correct output" do
389
- @t.retweet("sferik")
390
- $stdout.string.should =~ /^@testcli retweeted @sferik's latest status: "RT @tenderlove: \[ANN\] sqlite3-ruby =&gt; sqlite3"$/
391
- end
512
+ it "should request the correct resource" do
513
+ @t.retweet("26755176471724032")
514
+ a_post("/1/statuses/retweet/26755176471724032.json").
515
+ should have_been_made
516
+ end
517
+ it "should have the correct output" do
518
+ @t.retweet("26755176471724032")
519
+ $stdout.string.should =~ /^@testcli retweeted @gruber's status: "As for the Series, I'm for the Giants\. Fuck Texas, fuck Nolan Ryan, fuck George Bush\."$/
392
520
  end
393
521
  end
394
522
 
@@ -396,13 +524,13 @@ describe T::CLI do
396
524
  context "without arguments" do
397
525
  before do
398
526
  stub_get("/1/statuses/retweeted_by_me.json").
399
- with(:query => {:include_entities => "false"}).
527
+ with(:query => {:count => "20", :include_entities => "false"}).
400
528
  to_return(:body => fixture("statuses.json"), :headers => {:content_type => "application/json; charset=utf-8"})
401
529
  end
402
530
  it "should request the correct resource" do
403
531
  @t.retweets
404
532
  a_get("/1/statuses/retweeted_by_me.json").
405
- with(:query => {:include_entities => "false"}).
533
+ with(:query => {:count => "20", :include_entities => "false"}).
406
534
  should have_been_made
407
535
  end
408
536
  it "should have the correct output" do
@@ -433,13 +561,13 @@ describe T::CLI do
433
561
  context "with a screen name passed" do
434
562
  before do
435
563
  stub_get("/1/statuses/retweeted_by_user.json").
436
- with(:query => {:screen_name => "sferik", :include_entities => "false"}).
564
+ with(:query => {:count => "20", :include_entities => "false", :screen_name => "sferik"}).
437
565
  to_return(:body => fixture("statuses.json"), :headers => {:content_type => "application/json; charset=utf-8"})
438
566
  end
439
567
  it "should request the correct resource" do
440
568
  @t.retweets("sferik")
441
569
  a_get("/1/statuses/retweeted_by_user.json").
442
- with(:query => {:screen_name => "sferik", :include_entities => "false"}).
570
+ with(:query => {:count => "20", :include_entities => "false", :screen_name => "sferik"}).
443
571
  should have_been_made
444
572
  end
445
573
  it "should have the correct output" do
@@ -469,67 +597,6 @@ describe T::CLI do
469
597
  end
470
598
  end
471
599
 
472
- describe "#sent_messages" do
473
- before do
474
- stub_get("/1/direct_messages/sent.json").
475
- with(:query => {:include_entities => "false"}).
476
- to_return(:body => fixture("direct_messages.json"), :headers => {:content_type => "application/json; charset=utf-8"})
477
- end
478
- it "should request the correct resource" do
479
- @t.sent_messages
480
- a_get("/1/direct_messages/sent.json").
481
- with(:query => {:include_entities => "false"}).
482
- should have_been_made
483
- end
484
- it "should have the correct output" do
485
- @t.sent_messages
486
- $stdout.string.should == <<-eos.gsub(/^/, ' ' * 3)
487
- hurrycane: Sounds good. Meeting Tuesday is fine. (about 1 year ago)
488
- technoweenie: if you want to add me to your GroupMe group, my phone number is 415-312-2382 (about 1 year ago)
489
- hurrycane: That's great news! Let's plan to chat around 8 AM tomorrow Pacific time. Does that work for you? (about 1 year ago)
490
- hurrycane: I asked Yehuda about the stipend. I believe it has already been sent. Glad you're feeling better. (about 1 year ago)
491
- hurrycane: Just checking in. How's everything going? (about 1 year ago)
492
- hurrycane: Any luck completing graphs this weekend? There have been lots of commits to RailsAdmin since summer ended but none from you. How's it going? (about 1 year ago)
493
- hurrycane: Not sure about the payment. Feel free to ask Leah or Yehuda directly. Think you'll be able to finish up your work on graphs this weekend? (about 1 year ago)
494
- hurrycane: Looks good to me. I'm going to pull in the change now. My only concern is that we don't have any tests for auth. (about 1 year ago)
495
- hurrycane: How are the graph enhancements coming? (about 1 year ago)
496
- hurrycane: Changes pushed. You should pull and re-bundle when you have a minute. (about 1 year ago)
497
- hurrycane: Glad to hear the new graphs are coming along. Can't wait to see them! (about 1 year ago)
498
- hurrycane: I figured out what was wrong with the tests: I accidentally unbundled webrat. The problem had nothing to do with rspec-rails. (about 1 year ago)
499
- hurrycane: After the upgrade 54/80 specs are failing. I'm working on fixing them now. (about 1 year ago)
500
- hurrycane: a new version of rspec-rails just shipped with some nice features and fixes http://github.com/rspec/rspec-rails/blob/master/History.md (about 1 year ago)
501
- hurrycane: How are the graphs coming? I'm really looking forward to seeing what you do with Raphaël. (about 1 year ago)
502
- hurrycane: Awesome! Any luck duplicating the Gemfile.lock error with Ruby 1.9.2 final? (about 1 year ago)
503
- hurrycane: I just committed a bunch of cleanup and fixes to RailsAdmin that touched many of files. Make sure you pull to avoid conflicts. (about 1 year ago)
504
- hurrycane: Can you try upgrading to 1.9.2 final, re-installing Bundler 1.0.0.rc.6 (don't remove 1.0.0) and see if you can reproduce the problem? (about 1 year ago)
505
- hurrycane: I'm trying to debug the issue you were having with the Bundler Gemfile.lock shortref. What version of Ruby and RubyGems are you running? (about 1 year ago)
506
- hurrycane: Let's try to debug that problem during our session in 1.5 hours. In the mean time, try working on the graphs or internationalization. (about 1 year ago)
507
- eos
508
- end
509
- end
510
-
511
- describe "#stats" do
512
- before do
513
- stub_get("/1/users/show.json").
514
- with(:query => {:screen_name => "sferik", :include_entities => "false"}).
515
- to_return(:body => fixture("sferik.json"), :headers => {:content_type => "application/json; charset=utf-8"})
516
- end
517
- it "should request the correct resource" do
518
- @t.stats("sferik")
519
- a_get("/1/users/show.json").
520
- with(:query => {:screen_name => "sferik", :include_entities => "false"}).
521
- should have_been_made
522
- end
523
- it "should have the correct output" do
524
- @t.stats("sferik")
525
- $stdout.string.should =~ /^Tweets: 3,479$/
526
- $stdout.string.should =~ /^Following: 197$/
527
- $stdout.string.should =~ /^Followers: 1,048$/
528
- $stdout.string.should =~ /^Favorites: 1,040$/
529
- $stdout.string.should =~ /^Listed: 41$/
530
- end
531
- end
532
-
533
600
  describe "#status" do
534
601
  before do
535
602
  @t.options = @t.options.merge(:profile => fixture_path + "/.trc", :location => true)
@@ -553,25 +620,25 @@ describe T::CLI do
553
620
  end
554
621
  it "should have the correct output" do
555
622
  @t.status("Testing")
556
- $stdout.string.should =~ /^Tweet created by @testcli \(about 1 year ago\)$/
623
+ $stdout.string.should =~ /^Tweet created by @testcli \(about 1 year ago\)\.$/
557
624
  end
558
625
  end
559
626
 
560
627
  describe "#suggest" do
561
628
  before do
562
629
  stub_get("/1/users/recommendations.json").
563
- with(:query => {:limit => "1", :include_entities => "false"}).
630
+ with(:query => {:limit => "20", :include_entities => "false"}).
564
631
  to_return(:body => fixture("recommendations.json"), :headers => {:content_type => "application/json; charset=utf-8"})
565
632
  end
566
633
  it "should request the correct resource" do
567
634
  @t.suggest
568
635
  a_get("/1/users/recommendations.json").
569
- with(:query => {:limit => "1", :include_entities => "false"}).
636
+ with(:query => {:limit => "20", :include_entities => "false"}).
570
637
  should have_been_made
571
638
  end
572
639
  it "should have the correct output" do
573
640
  @t.suggest
574
- $stdout.string.should =~ /^Try following @jtrupiano\.$/
641
+ $stdout.string.chomp.rstrip.should == "antpires jtrupiano maccman mlroach stuntmann82"
575
642
  end
576
643
  end
577
644
 
@@ -579,13 +646,13 @@ describe T::CLI do
579
646
  context "without user" do
580
647
  before do
581
648
  stub_get("/1/statuses/home_timeline.json").
582
- with(:query => {:include_entities => "false"}).
649
+ with(:query => {:count => "20", :include_entities => "false"}).
583
650
  to_return(:body => fixture("statuses.json"), :headers => {:content_type => "application/json; charset=utf-8"})
584
651
  end
585
652
  it "should request the correct resource" do
586
653
  @t.timeline
587
654
  a_get("/1/statuses/home_timeline.json").
588
- with(:query => {:include_entities => "false"}).
655
+ with(:query => {:count => "20", :include_entities => "false"}).
589
656
  should have_been_made
590
657
  end
591
658
  it "should have the correct output" do
@@ -616,13 +683,13 @@ describe T::CLI do
616
683
  context "with user" do
617
684
  before do
618
685
  stub_get("/1/statuses/user_timeline.json").
619
- with(:query => {:screen_name => "sferik", :include_entities => "false"}).
686
+ with(:query => {:count => "20", :include_entities => "false", :screen_name => "sferik"}).
620
687
  to_return(:body => fixture("statuses.json"), :headers => {:content_type => "application/json; charset=utf-8"})
621
688
  end
622
689
  it "should request the correct resource" do
623
690
  @t.timeline("sferik")
624
691
  a_get("/1/statuses/user_timeline.json").
625
- with(:query => {:screen_name => "sferik", :include_entities => "false"}).
692
+ with(:query => {:count => "20", :include_entities => "false", :screen_name => "sferik"}).
626
693
  should have_been_made
627
694
  end
628
695
  it "should have the correct output" do
@@ -652,6 +719,68 @@ describe T::CLI do
652
719
  end
653
720
  end
654
721
 
722
+ describe "#unfollow" do
723
+ before do
724
+ @t.options = @t.options.merge(:profile => fixture_path + "/.trc")
725
+ end
726
+ context "no users" do
727
+ it "should exit" do
728
+ lambda do
729
+ @t.unfollow
730
+ end.should raise_error
731
+ end
732
+ end
733
+ context "one user" do
734
+ it "should request the correct resource" do
735
+ stub_delete("/1/friendships/destroy.json").
736
+ with(:query => {:screen_name => "sferik", :include_entities => "false"}).
737
+ to_return(:body => fixture("sferik.json"), :headers => {:content_type => "application/json; charset=utf-8"})
738
+ @t.unfollow("sferik")
739
+ a_delete("/1/friendships/destroy.json").
740
+ with(:query => {:screen_name => "sferik", :include_entities => "false"}).
741
+ should have_been_made
742
+ end
743
+ it "should have the correct output" do
744
+ stub_delete("/1/friendships/destroy.json").
745
+ with(:query => {:screen_name => "sferik", :include_entities => "false"}).
746
+ to_return(:body => fixture("sferik.json"), :headers => {:content_type => "application/json; charset=utf-8"})
747
+ @t.unfollow("sferik")
748
+ $stdout.string.should =~ /^@testcli is no longer following 1 user\.$/
749
+ end
750
+ context "Twitter is down" do
751
+ it "should retry 3 times and then raise an error" do
752
+ stub_delete("/1/friendships/destroy.json").
753
+ with(:query => {:screen_name => "sferik", :include_entities => "false"}).
754
+ to_return(:status => 502)
755
+ lambda do
756
+ @t.unfollow("sferik")
757
+ end.should raise_error("Twitter is down or being upgraded.")
758
+ a_delete("/1/friendships/destroy.json").
759
+ with(:query => {:screen_name => "sferik", :include_entities => "false"}).
760
+ should have_been_made.times(3)
761
+ end
762
+ end
763
+ end
764
+ end
765
+
766
+ describe "#users" do
767
+ before do
768
+ stub_get("/1/users/lookup.json").
769
+ with(:query => {:screen_name => "sferik", :include_entities => "false"}).
770
+ to_return(:body => fixture("users.json"), :headers => {:content_type => "application/json; charset=utf-8"})
771
+ end
772
+ it "should request the correct resource" do
773
+ @t.users("sferik")
774
+ a_get("/1/users/lookup.json").
775
+ with(:query => {:screen_name => "sferik", :include_entities => "false"}).
776
+ should have_been_made
777
+ end
778
+ it "should have the correct output" do
779
+ @t.users("sferik")
780
+ $stdout.string.chomp.rstrip.should == "pengwynn sferik"
781
+ end
782
+ end
783
+
655
784
  describe "#version" do
656
785
  it "should have the correct output" do
657
786
  @t.version