t 0.0.2 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ class String
2
+
3
+ def strip_at
4
+ self.tr('@', '')
5
+ end
6
+
7
+ end
data/lib/t/delete.rb ADDED
@@ -0,0 +1,101 @@
1
+ require 't/rcfile'
2
+ require 'thor'
3
+ require 'twitter'
4
+
5
+ module T
6
+ class Delete < Thor
7
+ DEFAULT_HOST = 'api.twitter.com'
8
+ DEFAULT_PROTOCOL = 'https'
9
+
10
+ check_unknown_options!
11
+
12
+ def initialize(*)
13
+ super
14
+ @rcfile = RCFile.instance
15
+ end
16
+
17
+ desc "block USERNAME", "Unblock a user."
18
+ def block(username)
19
+ username = username.strip_at
20
+ user = client.unblock(username)
21
+ say "@#{@rcfile.default_profile[0]} unblocked @#{user.screen_name}"
22
+ say
23
+ say "Run `#{$0} block #{user.screen_name}` to block."
24
+ end
25
+
26
+ desc "dm", "Delete the last Direct Message sent."
27
+ def dm
28
+ direct_message = client.direct_messages_sent(:count => 1).first
29
+ if direct_message
30
+ unless parent_options['force']
31
+ exit unless yes?("Are you sure you want to permanently delete the direct message to @#{direct_message.recipient.screen_name}: #{direct_message.text}?")
32
+ end
33
+ direct_message = client.direct_message_destroy(direct_message.id)
34
+ say "@#{direct_message.sender.screen_name} deleted the direct message sent to @#{direct_message.recipient.screen_name}: #{direct_message.text}"
35
+ else
36
+ raise Thor::Error, "Direct Message not found"
37
+ end
38
+ end
39
+ map %w(m) => :dm
40
+
41
+ desc "favorite", "Deletes the last favorite."
42
+ def favorite
43
+ status = client.favorites(:count => 1).first
44
+ if status
45
+ unless parent_options['force']
46
+ exit unless yes?("Are you sure you want to delete the favorite of @#{status.user.screen_name}: #{status.text}?")
47
+ end
48
+ client.unfavorite(status.id)
49
+ say "@#{@rcfile.default_profile[0]} unfavorited @#{status.user.screen_name}'s latest status: #{status.text}"
50
+ say
51
+ say "Run `#{$0} favorite #{status.user.screen_name}` to favorite."
52
+ else
53
+ raise Thor::Error, "Tweet not found"
54
+ end
55
+ end
56
+ map %w(fave) => :favorite
57
+
58
+ desc "status", "Delete a Tweet."
59
+ def status
60
+ user = client.user
61
+ if user.status
62
+ unless parent_options['force']
63
+ exit unless yes?("Are you sure you want to permanently delete the status: #{user.status.text}?")
64
+ end
65
+ status = client.status_destroy(user.status.id)
66
+ say "@#{@rcfile.default_profile[0]} deleted the status: #{status.text}"
67
+ else
68
+ raise Thor::Error, "Tweet not found"
69
+ end
70
+ end
71
+ map %w(post tweet update) => :status
72
+
73
+ no_tasks do
74
+
75
+ def base_url
76
+ "#{protocol}://#{host}"
77
+ end
78
+
79
+ def client
80
+ return @client if @client
81
+ @rcfile.path = parent_options['profile'] if parent_options['profile']
82
+ @client = Twitter::Client.new(
83
+ :endpoint => base_url,
84
+ :consumer_key => @rcfile.default_consumer_key,
85
+ :consumer_secret => @rcfile.default_consumer_secret,
86
+ :oauth_token => @rcfile.default_token,
87
+ :oauth_token_secret => @rcfile.default_secret
88
+ )
89
+ end
90
+
91
+ def host
92
+ parent_options['host'] || DEFAULT_HOST
93
+ end
94
+
95
+ def protocol
96
+ parent_options['no_ssl'] ? 'http' : DEFAULT_PROTOCOL
97
+ end
98
+
99
+ end
100
+ end
101
+ end
data/lib/t/set.rb CHANGED
@@ -7,42 +7,49 @@ module T
7
7
  DEFAULT_HOST = 'api.twitter.com'
8
8
  DEFAULT_PROTOCOL = 'https'
9
9
 
10
+ check_unknown_options!
11
+
12
+ def initialize(*)
13
+ super
14
+ @rcfile = RCFile.instance
15
+ end
16
+
10
17
  desc "bio DESCRIPTION", "Edits your Bio information on your Twitter profile."
11
18
  def bio(description)
12
19
  client.update_profile(:description => description)
13
- say "Bio has been changed."
20
+ say "@#{@rcfile.default_profile[0]}'s bio has been updated."
14
21
  end
15
22
 
16
- desc "default USERNAME, CONSUMER_KEY", "Set your default account."
23
+ desc "default USERNAME [CONSUMER_KEY]", "Set your default account."
17
24
  def default(username, consumer_key=nil)
18
- rcfile = RCFile.instance
25
+ @rcfile.path = parent_options['profile'] if parent_options['profile']
19
26
  consumer_key = rcfile[username].keys.last if consumer_key.nil?
20
- rcfile.default_profile = {'username' => username, 'consumer_key' => consumer_key}
21
- say "Default account has been changed."
27
+ @rcfile.default_profile = {'username' => username, 'consumer_key' => consumer_key}
28
+ say "Default account has been updated."
22
29
  end
23
30
 
24
31
  desc "language LANGUAGE_NAME", "Selects the language you'd like to receive notifications in."
25
32
  def language(language_name)
26
- client.settings(:language => language_name)
27
- say "Language has been changed."
33
+ client.settings(:lang => language_name)
34
+ say "@#{@rcfile.default_profile[0]}'s language has been updated."
28
35
  end
29
36
 
30
37
  desc "location PLACE_NAME", "Updates the location field in your profile."
31
38
  def location(place_name)
32
39
  client.update_profile(:location => place_name)
33
- say "Location has been changed."
40
+ say "@#{@rcfile.default_profile[0]}'s location has been updated."
34
41
  end
35
42
 
36
43
  desc "name NAME", "Sets the name field on your Twitter profile."
37
44
  def name(name)
38
45
  client.update_profile(:name => name)
39
- say "Name has been changed."
46
+ say "@#{@rcfile.default_profile[0]}'s name has been updated."
40
47
  end
41
48
 
42
49
  desc "url URL", "Sets the URL field on your profile."
43
50
  def url(url)
44
51
  client.update_profile(:url => url)
45
- say "URL has been changed."
52
+ say "@#{@rcfile.default_profile[0]}'s URL has been updated."
46
53
  end
47
54
 
48
55
  no_tasks do
@@ -52,13 +59,14 @@ module T
52
59
  end
53
60
 
54
61
  def client
55
- rcfile = RCFile.instance
56
- Twitter::Client.new(
62
+ return @client if @client
63
+ @rcfile.path = parent_options['profile'] if parent_options['profile']
64
+ @client = Twitter::Client.new(
57
65
  :endpoint => base_url,
58
- :consumer_key => rcfile.default_consumer_key,
59
- :consumer_secret => rcfile.default_consumer_secret,
60
- :oauth_token => rcfile.default_token,
61
- :oauth_token_secret => rcfile.default_secret
66
+ :consumer_key => @rcfile.default_consumer_key,
67
+ :consumer_secret => @rcfile.default_consumer_secret,
68
+ :oauth_token => @rcfile.default_token,
69
+ :oauth_token_secret => @rcfile.default_secret
62
70
  )
63
71
  end
64
72
 
@@ -67,7 +75,7 @@ module T
67
75
  end
68
76
 
69
77
  def protocol
70
- parent_options['no-ssl'] ? 'http' : DEFAULT_PROTOCOL
78
+ parent_options['no_ssl'] ? 'http' : DEFAULT_PROTOCOL
71
79
  end
72
80
 
73
81
  end
data/lib/t/version.rb CHANGED
@@ -8,12 +8,12 @@ module T
8
8
 
9
9
  # @return [Integer]
10
10
  def self.minor
11
- 0
11
+ 1
12
12
  end
13
13
 
14
14
  # @return [Integer]
15
15
  def self.patch
16
- 2
16
+ 0
17
17
  end
18
18
 
19
19
  # @return [String, NilClass]
data/spec/cli_spec.rb ADDED
@@ -0,0 +1,587 @@
1
+ # encoding: utf-8
2
+ require 'helper'
3
+
4
+ describe T::CLI do
5
+
6
+ before do
7
+ @t = T::CLI.new
8
+ Timecop.freeze(Time.local(2011, 11, 24, 16, 20, 0))
9
+ @old_stderr = $stderr
10
+ $stderr = StringIO.new
11
+ @old_stdout = $stdout
12
+ $stdout = StringIO.new
13
+ end
14
+
15
+ after do
16
+ $stderr = @old_stderr
17
+ $stdout = @old_stdout
18
+ end
19
+
20
+ describe "#account" do
21
+ before do
22
+ @t.options = @t.options.merge(:profile => File.expand_path('../fixtures/.trc', __FILE__))
23
+ end
24
+ it "should have the correct output" do
25
+ @t.accounts
26
+ $stdout.string.should == <<-eos.gsub(/^ {8}/, '')
27
+ testcli
28
+ abc123 (default)
29
+ eos
30
+ end
31
+ end
32
+
33
+ describe "#authorize" do
34
+ before do
35
+ @t.options = @t.options.merge(:profile => File.expand_path('/tmp/trc', __FILE__), :consumer_key => "abc123", :consumer_secret => "asdfasd223sd2", :prompt => true, :dry_run => true)
36
+ stub_post("/oauth/request_token").
37
+ to_return(:body => fixture("request_token"))
38
+ stub_post("/oauth/access_token").
39
+ to_return(:body => fixture("access_token"))
40
+ stub_get("/1/account/verify_credentials.json").
41
+ to_return(:body => fixture("sferik.json"), :headers => {:content_type => "application/json; charset=utf-8"})
42
+ end
43
+ it "should request the correct resource" do
44
+ $stdout.should_receive(:print).with("Press [Enter] to open the Twitter app authorization page. ")
45
+ $stdin.should_receive(:gets).and_return("\n")
46
+ $stdout.should_receive(:print).with("Paste in the supplied PIN: ")
47
+ $stdin.should_receive(:gets).and_return("1234567890")
48
+ @t.authorize
49
+ a_post("/oauth/request_token").
50
+ should have_been_made
51
+ a_post("/oauth/access_token").
52
+ should have_been_made
53
+ a_get("/1/account/verify_credentials.json").
54
+ should have_been_made
55
+ end
56
+ it "should not raise error" do
57
+ lambda do
58
+ $stdout.should_receive(:print).with("Press [Enter] to open the Twitter app authorization page. ")
59
+ $stdin.should_receive(:gets).and_return("\n")
60
+ $stdout.should_receive(:print).with("Paste in the supplied PIN: ")
61
+ $stdin.should_receive(:gets).and_return("1234567890")
62
+ @t.authorize
63
+ end.should_not raise_error
64
+ end
65
+ end
66
+
67
+ describe "#block" do
68
+ before do
69
+ @t.options = @t.options.merge(:profile => File.expand_path('../fixtures/.trc', __FILE__))
70
+ stub_post("/1/blocks/create.json").
71
+ with(:body => {:screen_name => "sferik"}).
72
+ to_return(:body => fixture("sferik.json"), :headers => {:content_type => "application/json; charset=utf-8"})
73
+ end
74
+ it "should request the correct resource" do
75
+ @t.block("sferik")
76
+ a_post("/1/blocks/create.json").
77
+ with(:body => {:screen_name => "sferik"}).
78
+ should have_been_made
79
+ end
80
+ it "should have the correct output" do
81
+ @t.block("sferik")
82
+ $stdout.string.should =~ /^@testcli blocked @sferik/
83
+ end
84
+ end
85
+
86
+ describe "#direct_messages" do
87
+ before do
88
+ stub_get("/1/direct_messages.json").
89
+ to_return(:body => fixture("direct_messages.json"), :headers => {:content_type => "application/json; charset=utf-8"})
90
+ end
91
+ it "should request the correct resource" do
92
+ @t.direct_messages
93
+ a_get("/1/direct_messages.json").
94
+ should have_been_made
95
+ end
96
+ it "should have the correct output" do
97
+ @t.direct_messages
98
+ $stdout.string.should == <<-eos.gsub(/^/, ' ' * 6)
99
+ sferik: Sounds good. Meeting Tuesday is fine. (about 1 year ago)
100
+ sferik: if you want to add me to your GroupMe group, my phone number is 415-312-2382 (about 1 year ago)
101
+ sferik: That's great news! Let's plan to chat around 8 AM tomorrow Pacific time. Does that work for you? (about 1 year ago)
102
+ sferik: I asked Yehuda about the stipend. I believe it has already been sent. Glad you're feeling better. (about 1 year ago)
103
+ sferik: Just checking in. How's everything going? (about 1 year ago)
104
+ sferik: 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)
105
+ sferik: 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)
106
+ sferik: 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)
107
+ sferik: How are the graph enhancements coming? (about 1 year ago)
108
+ sferik: Changes pushed. You should pull and re-bundle when you have a minute. (about 1 year ago)
109
+ sferik: Glad to hear the new graphs are coming along. Can't wait to see them! (about 1 year ago)
110
+ sferik: 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)
111
+ sferik: After the upgrade 54/80 specs are failing. I'm working on fixing them now. (about 1 year ago)
112
+ sferik: 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)
113
+ sferik: How are the graphs coming? I'm really looking forward to seeing what you do with Raphaël. (about 1 year ago)
114
+ sferik: Awesome! Any luck duplicating the Gemfile.lock error with Ruby 1.9.2 final? (about 1 year ago)
115
+ sferik: 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)
116
+ sferik: 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)
117
+ sferik: 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)
118
+ sferik: 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)
119
+ eos
120
+ end
121
+ end
122
+
123
+ describe "#dm" do
124
+ before do
125
+ @t.options = @t.options.merge(:profile => File.expand_path('../fixtures/.trc', __FILE__))
126
+ stub_post("/1/direct_messages/new.json").
127
+ with(:body => {:screen_name => "pengwynn", :text => "Creating a fixture for the Twitter gem"}).
128
+ to_return(:body => fixture("direct_message.json"), :headers => {:content_type => "application/json; charset=utf-8"})
129
+ end
130
+ it "should request the correct resource" do
131
+ @t.dm("pengwynn", "Creating a fixture for the Twitter gem")
132
+ a_post("/1/direct_messages/new.json").
133
+ with(:body => {:screen_name => "pengwynn", :text => "Creating a fixture for the Twitter gem"}).
134
+ should have_been_made
135
+ end
136
+ it "should have the correct output" do
137
+ @t.dm("pengwynn", "Creating a fixture for the Twitter gem")
138
+ $stdout.string.chomp.should == "Direct Message sent from @testcli to @pengwynn (about 1 year ago)"
139
+ end
140
+ end
141
+
142
+ describe "#favorite" do
143
+ before do
144
+ @t.options = @t.options.merge(:profile => File.expand_path('../fixtures/.trc', __FILE__))
145
+ end
146
+ context "not found" do
147
+ before do
148
+ stub_get("/1/users/show.json").
149
+ with(:query => {:screen_name => "sferik"}).
150
+ to_return(:body => '{}', :headers => {:content_type => "application/json; charset=utf-8"})
151
+ end
152
+ it "should exit" do
153
+ lambda do
154
+ @t.favorite("sferik")
155
+ end.should raise_error(Thor::Error, "Tweet not found")
156
+ end
157
+ end
158
+ context "forbidden" do
159
+ before do
160
+ stub_get("/1/users/show.json").
161
+ with(:query => {:screen_name => "sferik"}).
162
+ to_return(:body => '{"error":"Forbidden"}', :headers => {:content_type => "application/json; charset=utf-8"}, :status => 403)
163
+ end
164
+ it "should exit" do
165
+ lambda do
166
+ @t.favorite("sferik")
167
+ end.should raise_error(Twitter::Error::Forbidden, "Forbidden")
168
+ end
169
+ end
170
+ context "duplicate" do
171
+ before do
172
+ stub_get("/1/users/show.json").
173
+ with(:query => {:screen_name => "sferik"}).
174
+ to_return(:body => fixture("sferik.json"), :headers => {:content_type => "application/json; charset=utf-8"})
175
+ stub_post("/1/favorites/create/26755176471724032.json").
176
+ to_return(:body => '{"error":"You have already favorited this status."}', :headers => {:content_type => "application/json; charset=utf-8"}, :status => 403)
177
+ end
178
+ it "should have the correct output" do
179
+ @t.favorite("sferik")
180
+ $stdout.string.should =~ /^@testcli favorited @sferik's latest status: RT @tenderlove: \[ANN\] sqlite3-ruby =&gt; sqlite3$/
181
+ end
182
+ end
183
+ context "found" do
184
+ before do
185
+ stub_get("/1/users/show.json").
186
+ with(:query => {:screen_name => "sferik"}).
187
+ to_return(:body => fixture("sferik.json"), :headers => {:content_type => "application/json; charset=utf-8"})
188
+ stub_post("/1/favorites/create/26755176471724032.json").
189
+ to_return(:body => fixture("status.json"), :headers => {:content_type => "application/json; charset=utf-8"})
190
+ end
191
+ it "should request the correct resource" do
192
+ @t.favorite("sferik")
193
+ a_get("/1/users/show.json").
194
+ with(:query => {:screen_name => "sferik"}).
195
+ should have_been_made
196
+ a_post("/1/favorites/create/26755176471724032.json").
197
+ should have_been_made
198
+ end
199
+ it "should have the correct output" do
200
+ @t.favorite("sferik")
201
+ $stdout.string.should =~ /^@testcli favorited @sferik's latest status: RT @tenderlove: \[ANN\] sqlite3-ruby =&gt; sqlite3$/
202
+ end
203
+ end
204
+ end
205
+
206
+ describe "#follow" do
207
+ before do
208
+ @t.options = @t.options.merge(:profile => File.expand_path('../fixtures/.trc', __FILE__))
209
+ stub_post("/1/friendships/create.json").
210
+ with(:body => {:screen_name => "sferik"}).
211
+ to_return(:body => fixture("sferik.json"), :headers => {:content_type => "application/json; charset=utf-8"})
212
+ end
213
+ it "should request the correct resource" do
214
+ @t.follow("sferik")
215
+ a_post("/1/friendships/create.json").
216
+ with(:body => {:screen_name => "sferik"}).
217
+ should have_been_made
218
+ end
219
+ it "should have the correct output" do
220
+ @t.follow("sferik")
221
+ $stdout.string.should =~ /^@testcli is now following @sferik\.$/
222
+ end
223
+ end
224
+
225
+ describe "#get" do
226
+ context "not found" do
227
+ before do
228
+ stub_get("/1/users/show.json").
229
+ with(:query => {:screen_name => "sferik"}).
230
+ to_return(:body => '{}', :headers => {:content_type => "application/json; charset=utf-8"})
231
+ end
232
+ it "should exit" do
233
+ lambda do
234
+ @t.get("sferik")
235
+ end.should raise_error(Thor::Error, "Tweet not found")
236
+ end
237
+ end
238
+ context "found" do
239
+ before do
240
+ stub_get("/1/users/show.json").
241
+ with(:query => {:screen_name => "sferik"}).
242
+ to_return(:body => fixture("sferik.json"), :headers => {:content_type => "application/json; charset=utf-8"})
243
+ end
244
+ it "should request the correct resource" do
245
+ @t.get("sferik")
246
+ a_get("/1/users/show.json").
247
+ with(:query => {:screen_name => "sferik"}).
248
+ should have_been_made
249
+ end
250
+ it "should have the correct output" do
251
+ @t.get("sferik")
252
+ $stdout.string.chomp.should == "RT @tenderlove: [ANN] sqlite3-ruby =&gt; sqlite3 (10 months ago)"
253
+ end
254
+ end
255
+ end
256
+
257
+ describe "#mentions" do
258
+ before do
259
+ stub_get("/1/statuses/mentions.json").
260
+ to_return(:body => fixture("statuses.json"), :headers => {:content_type => "application/json; charset=utf-8"})
261
+ end
262
+ it "should request the correct resource" do
263
+ @t.mentions
264
+ a_get("/1/statuses/mentions.json").
265
+ should have_been_made
266
+ end
267
+ it "should have the correct output" do
268
+ @t.mentions
269
+ $stdout.string.should == <<-eos.gsub(/^/, ' ' * 6)
270
+ sferik: Ruby is the best programming language for hiding the ugly bits. (about 1 year ago)
271
+ sferik: There are 1.3 billion people in China; when people say there are 1 billion they are rounding off the entire population of the United States. (about 1 year ago)
272
+ sferik: The new Windows Phone campaign is the best advertising from Microsoft since "Start Me Up" (1995). Great work by CP+B. http://t.co/tIzxopI (about 1 year ago)
273
+ sferik: Fear not to sow seeds because of the birds. http://twitpic.com/2wg621 (about 1 year ago)
274
+ sferik: Speaking of things that are maddening: the interview with the Wall Street guys on the most recent This American Life http://bit.ly/af9pSD (about 1 year ago)
275
+ sferik: Holy cow! RailsAdmin is up to 200 watchers (from 100 yesterday). http://github.com/sferik/rails_admin (about 1 year ago)
276
+ sferik: Kind of cool that Facebook acts as a mirror for open-source projects that they use or like http://mirror.facebook.net/ (about 1 year ago)
277
+ sferik: RailsAdmin already has 100 watchers, 12 forks, and 6 contributors in less than 2 months. Let's keep the momentum going! http://bit.ly/cCMMqD (about 1 year ago)
278
+ sferik: This week's This American Life is amazing. @JoeLipari is an American hero. http://bit.ly/d9RbnB (about 1 year ago)
279
+ sferik: RT @polyseme: OH: shofars should be called jewvuzelas. (about 1 year ago)
280
+ sferik: Spent this morning fixing broken windows in RailsAdmin http://github.com/sferik/rails_admin/compare/ab6c598...0e3770f (about 1 year ago)
281
+ sferik: I'm a big believer that the broken windows theory applies to software development http://en.wikipedia.org/wiki/Broken_windows_theory (about 1 year ago)
282
+ sferik: I hope you idiots are happy with your piece of shit Android phones. http://www.apple.com/pr/library/2010/09/09statement.html (about 1 year ago)
283
+ sferik: Ping: kills MySpace dead. (about 1 year ago)
284
+ sferik: Crazy that iTunes Ping didn't leak a drop. (about 1 year ago)
285
+ sferik: The plot thickens http://twitpic.com/2k5lt2 (about 1 year ago)
286
+ sferik: 140 Proof Provides A Piece Of The Twitter Advertising Puzzle http://t.co/R2cUSDe via @techcrunch (about 1 year ago)
287
+ sferik: Try as you may http://www.thedoghousediaries.com/?p=1940 (about 1 year ago)
288
+ sferik: I know @SarahPalinUSA has a right to use Twitter, but should she? (over 1 year ago)
289
+ eos
290
+ end
291
+ end
292
+
293
+ describe "#open" do
294
+ before do
295
+ @t.options = @t.options.merge(:dry_run => true)
296
+ end
297
+ it "should not raise error" do
298
+ lambda do
299
+ @t.open("sferik")
300
+ end.should_not raise_error
301
+ end
302
+ end
303
+
304
+ describe "#reply" do
305
+ before do
306
+ @t.options = @t.options.merge(:profile => File.expand_path('../fixtures/.trc', __FILE__), :location => true)
307
+ stub_get("/1/users/show.json").
308
+ with(:query => {:screen_name => "sferik"}).
309
+ to_return(:body => fixture("sferik.json"), :headers => {:content_type => "application/json; charset=utf-8"})
310
+ stub_post("/1/statuses/update.json").
311
+ with(:body => {:in_reply_to_status_id => "26755176471724032", :status => "@sferik Testing", :lat => "37.76969909668", :long => "-122.39330291748"}).
312
+ to_return(:body => fixture("status.json"), :headers => {:content_type => "application/json; charset=utf-8"})
313
+ stub_request(:get, "http://checkip.dyndns.org/").
314
+ to_return(:body => fixture("checkip.html"), :headers => {:content_type => "text/html"})
315
+ stub_request(:get, "http://www.geoplugin.net/xml.gp?ip=50.131.22.169").
316
+ to_return(:body => fixture("xml.gp"), :headers => {:content_type => "application/xml"})
317
+ end
318
+ it "should request the correct resource" do
319
+ @t.reply("sferik", "Testing")
320
+ a_get("/1/users/show.json").
321
+ with(:query => {:screen_name => "sferik"}).
322
+ should have_been_made
323
+ a_post("/1/statuses/update.json").
324
+ with(:body => {:in_reply_to_status_id => "26755176471724032", :status => "@sferik Testing", :lat => "37.76969909668", :long => "-122.39330291748"}).
325
+ should have_been_made
326
+ a_request(:get, "http://checkip.dyndns.org/").
327
+ should have_been_made
328
+ a_request(:get, "http://www.geoplugin.net/xml.gp?ip=50.131.22.169").
329
+ should have_been_made
330
+ end
331
+ it "should have the correct output" do
332
+ @t.reply("sferik", "Testing")
333
+ $stdout.string.should =~ /^Reply created by @testcli to @sferik \(about 1 year ago\)$/
334
+ end
335
+ end
336
+
337
+ describe "#retweet" do
338
+ before do
339
+ @t.options = @t.options.merge(:profile => File.expand_path('../fixtures/.trc', __FILE__))
340
+ end
341
+ context "not found" do
342
+ before do
343
+ stub_get("/1/users/show.json").
344
+ with(:query => {:screen_name => "sferik"}).
345
+ to_return(:body => '{}', :headers => {:content_type => "application/json; charset=utf-8"})
346
+ end
347
+ it "should exit" do
348
+ lambda do
349
+ @t.retweet("sferik")
350
+ end.should raise_error(Thor::Error, "Tweet not found")
351
+ end
352
+ end
353
+ context "forbidden" do
354
+ before do
355
+ stub_get("/1/users/show.json").
356
+ with(:query => {:screen_name => "sferik"}).
357
+ to_return(:body => '{"error":"Forbidden"}', :headers => {:content_type => "application/json; charset=utf-8"}, :status => 403)
358
+ end
359
+ it "should exit" do
360
+ lambda do
361
+ @t.retweet("sferik")
362
+ end.should raise_error(Twitter::Error::Forbidden, "Forbidden")
363
+ end
364
+ end
365
+ context "duplicate" do
366
+ before do
367
+ stub_get("/1/users/show.json").
368
+ with(:query => {:screen_name => "sferik"}).
369
+ to_return(:body => fixture("sferik.json"), :headers => {:content_type => "application/json; charset=utf-8"})
370
+ stub_post("/1/statuses/retweet/26755176471724032.json").
371
+ to_return(:body => '{"error":"sharing is not permissable for this status (Share validations failed)"}', :headers => {:content_type => "application/json; charset=utf-8"}, :status => 403)
372
+ end
373
+ it "should have the correct output" do
374
+ @t.retweet("sferik")
375
+ $stdout.string.should =~ /^@testcli retweeted @sferik's latest status: RT @tenderlove: \[ANN\] sqlite3-ruby =&gt; sqlite3$/
376
+ end
377
+ end
378
+ context "found" do
379
+ before do
380
+ stub_get("/1/users/show.json").
381
+ with(:query => {:screen_name => "sferik"}).
382
+ to_return(:body => fixture("sferik.json"), :headers => {:content_type => "application/json; charset=utf-8"})
383
+ stub_post("/1/statuses/retweet/26755176471724032.json").
384
+ to_return(:body => fixture("retweet.json"), :headers => {:content_type => "application/json; charset=utf-8"})
385
+ end
386
+ it "should request the correct resource" do
387
+ @t.retweet("sferik")
388
+ a_get("/1/users/show.json").
389
+ with(:query => {:screen_name => "sferik"}).
390
+ should have_been_made
391
+ a_post("/1/statuses/retweet/26755176471724032.json").
392
+ should have_been_made
393
+ end
394
+ it "should have the correct output" do
395
+ @t.retweet("sferik")
396
+ $stdout.string.should =~ /^@testcli retweeted @sferik's latest status: RT @tenderlove: \[ANN\] sqlite3-ruby =&gt; sqlite3$/
397
+ end
398
+ end
399
+ end
400
+
401
+ describe "#sent_messages" do
402
+ before do
403
+ stub_get("/1/direct_messages/sent.json").
404
+ to_return(:body => fixture("direct_messages.json"), :headers => {:content_type => "application/json; charset=utf-8"})
405
+ end
406
+ it "should request the correct resource" do
407
+ @t.sent_messages
408
+ a_get("/1/direct_messages/sent.json").
409
+ should have_been_made
410
+ end
411
+ it "should have the correct output" do
412
+ @t.sent_messages
413
+ $stdout.string.should == <<-eos.gsub(/^/, ' ' * 3)
414
+ hurrycane: Sounds good. Meeting Tuesday is fine. (about 1 year ago)
415
+ technoweenie: if you want to add me to your GroupMe group, my phone number is 415-312-2382 (about 1 year ago)
416
+ 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)
417
+ hurrycane: I asked Yehuda about the stipend. I believe it has already been sent. Glad you're feeling better. (about 1 year ago)
418
+ hurrycane: Just checking in. How's everything going? (about 1 year ago)
419
+ 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)
420
+ 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)
421
+ 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)
422
+ hurrycane: How are the graph enhancements coming? (about 1 year ago)
423
+ hurrycane: Changes pushed. You should pull and re-bundle when you have a minute. (about 1 year ago)
424
+ hurrycane: Glad to hear the new graphs are coming along. Can't wait to see them! (about 1 year ago)
425
+ 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)
426
+ hurrycane: After the upgrade 54/80 specs are failing. I'm working on fixing them now. (about 1 year ago)
427
+ 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)
428
+ hurrycane: How are the graphs coming? I'm really looking forward to seeing what you do with Raphaël. (about 1 year ago)
429
+ hurrycane: Awesome! Any luck duplicating the Gemfile.lock error with Ruby 1.9.2 final? (about 1 year ago)
430
+ 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)
431
+ 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)
432
+ 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)
433
+ 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)
434
+ eos
435
+ end
436
+ end
437
+
438
+ describe "#stats" do
439
+ before do
440
+ stub_get("/1/users/show.json").
441
+ with(:query => {:screen_name => "sferik"}).
442
+ to_return(:body => fixture("sferik.json"), :headers => {:content_type => "application/json; charset=utf-8"})
443
+ end
444
+ it "should request the correct resource" do
445
+ @t.stats("sferik")
446
+ a_get("/1/users/show.json").
447
+ with(:query => {:screen_name => "sferik"}).
448
+ should have_been_made
449
+ end
450
+ it "should have the correct output" do
451
+ @t.stats("sferik")
452
+ $stdout.string.should =~ /^Followers: 1,048$/
453
+ $stdout.string.should =~ /^Following: 197$/
454
+ end
455
+ end
456
+
457
+ describe "#status" do
458
+ before do
459
+ @t.options = @t.options.merge(:profile => File.expand_path('../fixtures/.trc', __FILE__), :location => true)
460
+ stub_post("/1/statuses/update.json").
461
+ with(:body => {:status => "Testing", :lat => "37.76969909668", :long => "-122.39330291748"}).
462
+ to_return(:body => fixture("status.json"), :headers => {:content_type => "application/json; charset=utf-8"})
463
+ stub_request(:get, "http://checkip.dyndns.org/").
464
+ to_return(:body => fixture("checkip.html"), :headers => {:content_type => "text/html"})
465
+ stub_request(:get, "http://www.geoplugin.net/xml.gp?ip=50.131.22.169").
466
+ to_return(:body => fixture("xml.gp"), :headers => {:content_type => "application/xml"})
467
+ end
468
+ it "should request the correct resource" do
469
+ @t.status("Testing")
470
+ a_post("/1/statuses/update.json").
471
+ with(:body => {:status => "Testing", :lat => "37.76969909668", :long => "-122.39330291748"}).
472
+ should have_been_made
473
+ a_request(:get, "http://checkip.dyndns.org/").
474
+ should have_been_made
475
+ a_request(:get, "http://www.geoplugin.net/xml.gp?ip=50.131.22.169").
476
+ should have_been_made
477
+ end
478
+ it "should have the correct output" do
479
+ @t.status("Testing")
480
+ $stdout.string.should =~ /^Tweet created by @testcli \(about 1 year ago\)$/
481
+ end
482
+ end
483
+
484
+ describe "#suggest" do
485
+ before do
486
+ stub_get("/1/users/recommendations.json").
487
+ with(:query => {:limit => "1"}).
488
+ to_return(:body => fixture("recommendations.json"), :headers => {:content_type => "application/json; charset=utf-8"})
489
+ end
490
+ it "should request the correct resource" do
491
+ @t.suggest
492
+ a_get("/1/users/recommendations.json").
493
+ with(:query => {:limit => "1"}).
494
+ should have_been_made
495
+ end
496
+ it "should have the correct output" do
497
+ @t.suggest
498
+ $stdout.string.should =~ /^Try following @jtrupiano\.$/
499
+ end
500
+ end
501
+
502
+ describe "#timeline" do
503
+ before do
504
+ stub_get("/1/statuses/home_timeline.json").
505
+ to_return(:body => fixture("statuses.json"), :headers => {:content_type => "application/json; charset=utf-8"})
506
+ end
507
+ it "should request the correct resource" do
508
+ @t.timeline
509
+ a_get("/1/statuses/home_timeline.json").
510
+ should have_been_made
511
+ end
512
+ it "should have the correct output" do
513
+ @t.timeline
514
+ $stdout.string.should == <<-eos.gsub(/^/, ' ' * 6)
515
+ sferik: Ruby is the best programming language for hiding the ugly bits. (about 1 year ago)
516
+ sferik: There are 1.3 billion people in China; when people say there are 1 billion they are rounding off the entire population of the United States. (about 1 year ago)
517
+ sferik: The new Windows Phone campaign is the best advertising from Microsoft since "Start Me Up" (1995). Great work by CP+B. http://t.co/tIzxopI (about 1 year ago)
518
+ sferik: Fear not to sow seeds because of the birds. http://twitpic.com/2wg621 (about 1 year ago)
519
+ sferik: Speaking of things that are maddening: the interview with the Wall Street guys on the most recent This American Life http://bit.ly/af9pSD (about 1 year ago)
520
+ sferik: Holy cow! RailsAdmin is up to 200 watchers (from 100 yesterday). http://github.com/sferik/rails_admin (about 1 year ago)
521
+ sferik: Kind of cool that Facebook acts as a mirror for open-source projects that they use or like http://mirror.facebook.net/ (about 1 year ago)
522
+ sferik: RailsAdmin already has 100 watchers, 12 forks, and 6 contributors in less than 2 months. Let's keep the momentum going! http://bit.ly/cCMMqD (about 1 year ago)
523
+ sferik: This week's This American Life is amazing. @JoeLipari is an American hero. http://bit.ly/d9RbnB (about 1 year ago)
524
+ sferik: RT @polyseme: OH: shofars should be called jewvuzelas. (about 1 year ago)
525
+ sferik: Spent this morning fixing broken windows in RailsAdmin http://github.com/sferik/rails_admin/compare/ab6c598...0e3770f (about 1 year ago)
526
+ sferik: I'm a big believer that the broken windows theory applies to software development http://en.wikipedia.org/wiki/Broken_windows_theory (about 1 year ago)
527
+ sferik: I hope you idiots are happy with your piece of shit Android phones. http://www.apple.com/pr/library/2010/09/09statement.html (about 1 year ago)
528
+ sferik: Ping: kills MySpace dead. (about 1 year ago)
529
+ sferik: Crazy that iTunes Ping didn't leak a drop. (about 1 year ago)
530
+ sferik: The plot thickens http://twitpic.com/2k5lt2 (about 1 year ago)
531
+ sferik: 140 Proof Provides A Piece Of The Twitter Advertising Puzzle http://t.co/R2cUSDe via @techcrunch (about 1 year ago)
532
+ sferik: Try as you may http://www.thedoghousediaries.com/?p=1940 (about 1 year ago)
533
+ sferik: I know @SarahPalinUSA has a right to use Twitter, but should she? (over 1 year ago)
534
+ eos
535
+ end
536
+ end
537
+
538
+ describe "#unfollow" do
539
+ before do
540
+ @t.options = @t.options.merge(:profile => File.expand_path('../fixtures/.trc', __FILE__))
541
+ stub_delete("/1/friendships/destroy.json").
542
+ with(:query => {:screen_name => "sferik"}).
543
+ to_return(:body => fixture("sferik.json"), :headers => {:content_type => "application/json; charset=utf-8"})
544
+ end
545
+ it "should request the correct resource" do
546
+ @t.unfollow("sferik")
547
+ a_delete("/1/friendships/destroy.json").
548
+ with(:query => {:screen_name => "sferik"}).
549
+ should have_been_made
550
+ end
551
+ it "should have the correct output" do
552
+ @t.unfollow("sferik")
553
+ $stdout.string.should =~ /^@testcli is no longer following @sferik\.$/
554
+ end
555
+ end
556
+
557
+ describe "#version" do
558
+ it "should have the correct output" do
559
+ @t.version
560
+ $stdout.string.chomp.should == T::Version.to_s
561
+ end
562
+ end
563
+
564
+ describe "#whois" do
565
+ before do
566
+ stub_get("/1/users/show.json").
567
+ with(:query => {:screen_name => "sferik"}).
568
+ to_return(:body => fixture("sferik.json"), :headers => {:content_type => "application/json; charset=utf-8"})
569
+ end
570
+ it "should request the correct resource" do
571
+ @t.whois("sferik")
572
+ a_get("/1/users/show.json").
573
+ with(:query => {:screen_name => "sferik"}).
574
+ should have_been_made
575
+ end
576
+ it "should have the correct output" do
577
+ @t.whois("sferik")
578
+ $stdout.string.should == <<-eos.gsub(/^ {8}/, '')
579
+ Erik Michaels-Ober, since Jul 2007.
580
+ bio: A mind forever voyaging through strange seas of thought, alone.
581
+ location: San Francisco
582
+ web: https://github.com/sferik
583
+ eos
584
+ end
585
+ end
586
+
587
+ end