tuomas-tweetwine 0.2.2 → 0.2.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,10 @@
1
+ === 0.2.4 released 2009-09-16
2
+
3
+ * Retry connection upon connection reset, trying maximum of three times.
4
+ * Display proper version info on Ruby 1.8 when using option "-v".
5
+ * Minor code cleanups.
6
+ * Release 0.2.3 is skipped due to my error in tagging the wrong commit.
7
+
1
8
  === 0.2.2 released 2009-09-03
2
9
 
3
10
  * Highlight hashtags in statuses.
data/Rakefile CHANGED
@@ -69,6 +69,7 @@ Rake::TestTask.new(:test) do |t|
69
69
  t.test_files = FileList["test/**/*_test.rb"]
70
70
  t.verbose = true
71
71
  t.warning = true
72
+ t.ruby_opts << "-rrubygems"
72
73
  t.libs << "test"
73
74
  end
74
75
 
@@ -15,51 +15,63 @@ require "tweetwine"
15
15
 
16
16
  include Tweetwine
17
17
 
18
- begin
19
- config = StartupConfig.new(Client::COMMANDS)
20
- config.parse(ARGV, ENV["HOME"] + "/.tweetwine") do |args|
21
- options = {}
22
- begin
23
- OptionParser.new do |opt|
24
- opt.banner =<<-END
18
+ cmd_parser = lambda do |args|
19
+ options = {}
20
+ begin
21
+ OptionParser.new do |opt|
22
+ opt.banner =<<-END
25
23
  Usage: tweetwine [options...] [command]
26
24
 
27
25
  Commands: #{Client::COMMANDS.join(", ")}
28
26
 
29
27
  Options:
30
28
 
31
- END
32
- opt.on("-a", "--auth USERNAME:PASSWORD", "Authentication") do |arg|
33
- options[:username], options[:password] = arg.split(":", 2)
34
- end
35
- opt.on("-c", "--colorize", "Colorize output with ANSI escape codes") do
36
- options[:colorize] = true
37
- end
38
- opt.on("-n", "--num N", Integer, "The number of statuses to fetch, defaults to #{Client::DEFAULT_NUM_STATUSES}") do |arg|
39
- options[:num_statuses] = arg
40
- end
41
- opt.on("--no-url-shorten", "Do not shorten URLs for status update") do
42
- options[:shorten_urls] = { :enable => false }
43
- end
44
- opt.on("-p", "--page N", Integer, "The page number of the statuses to fetch, defaults to #{Client::DEFAULT_PAGE_NUM}") do |arg|
45
- options[:page_num] = arg
46
- end
47
- opt.on("-v", "--version", "Show version information and exit") do
48
- puts "#{File.basename($0)} #{VERSION}"
49
- exit(EXIT_VERSION)
50
- end
51
- opt.on_tail("-h", "--help", "Show this help message and exit") do
52
- puts opt
53
- exit(EXIT_HELP)
54
- end
55
- end.parse!(args)
56
- rescue OptionParser::ParseError => e
57
- raise ArgumentError, e.message
58
- end
59
- options
29
+ END
30
+ opt.on("-a", "--auth USERNAME:PASSWORD", "Authentication") do |arg|
31
+ options[:username], options[:password] = arg.split(":", 2)
32
+ end
33
+ opt.on("-c", "--colorize", "Colorize output with ANSI escape codes") do
34
+ options[:colorize] = true
35
+ end
36
+ opt.on("-n", "--num N", Integer, "The number of statuses to fetch, defaults to #{Client::DEFAULT_NUM_STATUSES}") do |arg|
37
+ options[:num_statuses] = arg
38
+ end
39
+ opt.on("--no-url-shorten", "Do not shorten URLs for status update") do
40
+ options[:shorten_urls] = { :enable => false }
41
+ end
42
+ opt.on("-p", "--page N", Integer, "The page number of the statuses to fetch, defaults to #{Client::DEFAULT_PAGE_NUM}") do |arg|
43
+ options[:page_num] = arg
44
+ end
45
+ opt.on("-v", "--version", "Show version information and exit") do
46
+ puts "#{File.basename($0)} #{Tweetwine::VERSION}"
47
+ exit(EXIT_VERSION)
48
+ end
49
+ opt.on_tail("-h", "--help", "Show this help message and exit") do
50
+ puts opt
51
+ exit(EXIT_HELP)
52
+ end
53
+ end.parse!(args)
54
+ rescue OptionParser::ParseError => e
55
+ raise ArgumentError, e.message
60
56
  end
61
- io = Tweetwine::IO.new(config.options)
62
- client = Client.new(io, config.options)
57
+ options
58
+ end
59
+
60
+ def create_dependencies(options)
61
+ io = Tweetwine::IO.new(options)
62
+ rest_client = RestClientWrapper.new(io)
63
+ url_shortener = lambda { |opts| UrlShortener.new(rest_client, opts) }
64
+ {
65
+ :io => io,
66
+ :rest_client => rest_client,
67
+ :url_shortener => url_shortener
68
+ }
69
+ end
70
+
71
+ begin
72
+ config = StartupConfig.new(Client::COMMANDS)
73
+ config.parse(ARGV, ENV["HOME"] + "/.tweetwine", &cmd_parser)
74
+ client = Client.new(create_dependencies(config.options), config.options)
63
75
  client.send(config.command, *config.args)
64
76
  rescue ArgumentError, ClientError => e
65
77
  puts "Error: #{e.message}"
@@ -11,15 +11,16 @@ module Tweetwine
11
11
  DEFAULT_PAGE_NUM = 1
12
12
  MAX_STATUS_LENGTH = 140
13
13
 
14
- def initialize(io, options)
15
- @io = io
14
+ def initialize(dependencies, options)
15
+ @io = dependencies[:io]
16
+ @rest_client = dependencies[:rest_client]
16
17
  @username = options[:username].to_s
17
18
  raise ArgumentError, "No authentication data given" if @username.empty?
18
19
  @base_url = "https://#{@username}:#{options[:password]}@twitter.com/"
19
20
  @num_statuses = Util.parse_int_gt(options[:num_statuses], DEFAULT_NUM_STATUSES, 1, "number of statuses_to_show")
20
21
  @page_num = Util.parse_int_gt(options[:page_num], DEFAULT_PAGE_NUM, 1, "page number")
21
22
  @url_shortener = if options[:shorten_urls] && options[:shorten_urls][:enable]
22
- UrlShortener.new(options[:shorten_urls])
23
+ dependencies[:url_shortener].call(options[:shorten_urls])
23
24
  else
24
25
  nil
25
26
  end
@@ -110,11 +111,11 @@ module Tweetwine
110
111
  end
111
112
 
112
113
  def get(body_url)
113
- RestClientWrapper.get @base_url + body_url
114
+ @rest_client.get @base_url + body_url
114
115
  end
115
116
 
116
117
  def post(body_url, body)
117
- RestClientWrapper.post @base_url + body_url, body
118
+ @rest_client.post @base_url + body_url, body
118
119
  end
119
120
 
120
121
  class StatusUpdateFactory
@@ -114,4 +114,4 @@ module Tweetwine
114
114
  "\033[#{color_code}m#{str}\033[0m"
115
115
  end
116
116
  end
117
- end
117
+ end
@@ -1,3 +1,3 @@
1
1
  module Tweetwine
2
- VERSION = "0.2.2"
2
+ VERSION = "0.2.4"
3
3
  end
@@ -6,12 +6,32 @@ module Tweetwine
6
6
  class RestClientWrapper
7
7
  instance_methods.each { |m| undef_method m unless m =~ /(^__|^send$|^object_id$)/ }
8
8
 
9
+ MAX_RETRIES = 3
10
+ RETRY_BASE_WAIT_TIMEOUT = 4
11
+
12
+ def initialize(io)
13
+ @io = io
14
+ end
15
+
9
16
  protected
10
17
 
11
- def self.method_missing(name, *args, &block)
12
- RestClient.send(name, *args, &block)
13
- rescue RestClient::Exception, SocketError, SystemCallError => e
14
- raise ClientError, e.message
18
+ def method_missing(name, *args, &block)
19
+ tries = 0
20
+ begin
21
+ tries += 1
22
+ RestClient.send(name, *args, &block)
23
+ rescue Errno::ECONNRESET => e
24
+ if tries < MAX_RETRIES
25
+ timeout = RETRY_BASE_WAIT_TIMEOUT**tries
26
+ @io.warn("Could not connect -- retrying in #{timeout} seconds")
27
+ sleep timeout
28
+ retry
29
+ else
30
+ raise ClientError, e
31
+ end
32
+ rescue RestClient::Exception, SocketError, SystemCallError => e
33
+ raise ClientError, e
34
+ end
15
35
  end
16
36
  end
17
37
  end
@@ -1,6 +1,7 @@
1
1
  module Tweetwine
2
2
  class UrlShortener
3
- def initialize(options)
3
+ def initialize(rest_client, options)
4
+ @rest_client = rest_client
4
5
  options = Options.new(options, "URL shortening")
5
6
  @method = (options[:method] || :get).to_sym
6
7
  @service_url = options.require :service_url
@@ -29,7 +30,7 @@ module Tweetwine
29
30
  else
30
31
  raise "Unrecognized HTTP request method"
31
32
  end
32
- response = RestClientWrapper.send(@method, *rest)
33
+ response = @rest_client.send(@method, *rest)
33
34
  doc = Nokogiri::HTML(response)
34
35
  doc.xpath(@xpath_selector).first.to_s
35
36
  end
@@ -49,4 +49,4 @@ module Tweetwine
49
49
  unit
50
50
  end
51
51
  end
52
- end
52
+ end
@@ -6,323 +6,182 @@ Mocha::Configuration.allow(:stubbing_non_existent_method)
6
6
  module Tweetwine
7
7
 
8
8
  class ClientTest < Test::Unit::TestCase
9
- context "Upon initialization, a client" do
9
+ context "A client instance" do
10
10
  setup do
11
11
  @io = mock()
12
+ @rest_client = mock()
13
+ @url_shortener = mock()
14
+ url_shortener = lambda { |options| @url_shortener }
15
+ @deps = {
16
+ :io => @io,
17
+ :rest_client => @rest_client,
18
+ :url_shortener => url_shortener
19
+ }
12
20
  end
13
21
 
14
- should "raise exception when no authentication data is given" do
15
- assert_raise(ArgumentError) { Client.new(@io, {}) }
16
- assert_raise(ArgumentError) { Client.new(@io, { :password => "bar" }) }
17
- assert_raise(ArgumentError) { Client.new(@io, { :username => "", :password => "bar" }) }
18
- assert_nothing_raised { Client.new(@io, { :username => "foo", :password => "bar" }) }
19
- end
20
-
21
- should "use default number of statuses if not configured otherwise" do
22
- @client = Client.new(@io, { :username => "foo", :password => "bar" })
23
- assert_equal Client::DEFAULT_NUM_STATUSES, @client.num_statuses
24
- end
25
-
26
- should "use configured number of statuses if in allowed range" do
27
- @client = Client.new(@io, { :username => "foo", :password => "bar", :num_statuses => 12 })
28
- assert_equal 12, @client.num_statuses
29
- end
30
-
31
- should "raise an exception for configured number of statuses if not in allowed range" do
32
- assert_raise(ArgumentError) { Client.new(@io, { :username => "foo", :password => "bar", :num_statuses => 0 }) }
33
- end
34
-
35
- should "use default page number if not configured otherwise" do
36
- @client = Client.new(@io, { :username => "foo", :password => "bar" })
37
- assert_equal Client::DEFAULT_PAGE_NUM, @client.page_num
38
- end
22
+ context "upon initialization" do
23
+ should "raise exception when no authentication data is given" do
24
+ assert_raise(ArgumentError) { Client.new(@deps, {}) }
25
+ assert_raise(ArgumentError) { Client.new(@deps, { :password => "bar" }) }
26
+ assert_raise(ArgumentError) { Client.new(@deps, { :username => "", :password => "bar" }) }
27
+ assert_nothing_raised { Client.new(@deps, { :username => "foo", :password => "bar" }) }
28
+ end
39
29
 
40
- should "use configured page number if in allowed range" do
41
- @client = Client.new(@io, { :username => "foo", :password => "bar", :page_num => 12 })
42
- assert_equal 12, @client.page_num
43
- end
30
+ should "use default number of statuses if not configured otherwise" do
31
+ @client = Client.new(@deps, { :username => "foo", :password => "bar" })
32
+ assert_equal Client::DEFAULT_NUM_STATUSES, @client.num_statuses
33
+ end
44
34
 
45
- should "raise an exception for configured page number if not in allowed range" do
46
- assert_raise(ArgumentError) { Client.new(@io, { :username => "foo", :password => "bar", :page_num => 0 }) }
47
- end
48
- end
35
+ should "use configured number of statuses if in allowed range" do
36
+ @client = Client.new(@deps, { :username => "foo", :password => "bar", :num_statuses => 12 })
37
+ assert_equal 12, @client.num_statuses
38
+ end
49
39
 
50
- context "At runtime, a client" do
51
- setup do
52
- @io = mock()
53
- @username = "spiky"
54
- @password = "lullaby"
55
- @client = Client.new(@io, { :username => @username, :password => @password })
56
- @base_url = "https://#{@username}:#{@password}@twitter.com"
57
- @statuses_query_params = "count=#{Client::DEFAULT_NUM_STATUSES}&page=#{Client::DEFAULT_PAGE_NUM}"
58
- @users_query_params = "page=#{Client::DEFAULT_PAGE_NUM}"
59
- end
40
+ should "raise an exception for configured number of statuses if not in allowed range" do
41
+ assert_raise(ArgumentError) { Client.new(@deps, { :username => "foo", :password => "bar", :num_statuses => 0 }) }
42
+ end
60
43
 
61
- should "fetch friends' statuses (home view)" do
62
- status_records, gen_records = create_test_statuses(
63
- {
64
- :user => "zanzibar",
65
- :status => {
66
- :created_at => Time.at(1).to_s,
67
- :text => "wassup?",
68
- :in_reply_to => nil
69
- }
70
- },
71
- {
72
- :user => "lulzwoo",
73
- :status => {
74
- :created_at => Time.at(1).to_s,
75
- :text => "nuttin'",
76
- :in_reply_to => nil
77
- }
78
- }
79
- )
80
- RestClientWrapper.expects(:get) \
81
- .with("#{@base_url}/statuses/friends_timeline.json?#{@statuses_query_params}") \
82
- .returns(status_records.to_json)
83
- @io.expects(:show_record).with(gen_records[0])
84
- @io.expects(:show_record).with(gen_records[1])
85
- @client.home
86
- end
44
+ should "use default page number if not configured otherwise" do
45
+ @client = Client.new(@deps, { :username => "foo", :password => "bar" })
46
+ assert_equal Client::DEFAULT_PAGE_NUM, @client.page_num
47
+ end
87
48
 
88
- should "fetch mentions" do
89
- status_records, gen_records = create_test_statuses(
90
- {
91
- :user => "zanzibar",
92
- :status => {
93
- :created_at => Time.at(1).to_s,
94
- :text => "wassup, @#{@username}?",
95
- :in_reply_to => @username
96
- }
97
- },
98
- {
99
- :user => "lulzwoo",
100
- :status => {
101
- :created_at => Time.at(1).to_s,
102
- :text => "@#{@username}, doing nuttin'",
103
- :in_reply_to => @username
104
- }
105
- }
106
- )
107
- RestClientWrapper.expects(:get) \
108
- .with("#{@base_url}/statuses/mentions.json?#{@statuses_query_params}") \
109
- .returns(status_records.to_json)
110
- @io.expects(:show_record).with(gen_records[0])
111
- @io.expects(:show_record).with(gen_records[1])
112
- @client.mentions
113
- end
49
+ should "use configured page number if in allowed range" do
50
+ @client = Client.new(@deps, { :username => "foo", :password => "bar", :page_num => 12 })
51
+ assert_equal 12, @client.page_num
52
+ end
114
53
 
115
- should "fetch a specific user's statuses, when the user identified by given argument" do
116
- user = "spoonman"
117
- status_records, gen_records = create_test_statuses(
118
- {
119
- :user => user,
120
- :status => {
121
- :created_at => Time.at(1).to_s,
122
- :text => "wassup?",
123
- :in_reply_to => nil
124
- }
125
- }
126
- )
127
- RestClientWrapper.expects(:get) \
128
- .with("#{@base_url}/statuses/user_timeline/#{user}.json?#{@statuses_query_params}") \
129
- .returns(status_records.to_json)
130
- @io.expects(:show_record).with(gen_records[0])
131
- @client.user(user)
54
+ should "raise an exception for configured page number if not in allowed range" do
55
+ assert_raise(ArgumentError) { Client.new(@deps, { :username => "foo", :password => "bar", :page_num => 0 }) }
56
+ end
132
57
  end
133
58
 
134
- should "fetch a specific user's statuses, with the user being the authenticated user itself when given no argument" do
135
- status_records, gen_records = create_test_statuses(
136
- {
137
- :user => @username,
138
- :status => {
139
- :created_at => Time.at(1).to_s,
140
- :text => "wassup?",
141
- :in_reply_to => nil
142
- }
143
- }
144
- )
145
- RestClientWrapper.expects(:get) \
146
- .with("#{@base_url}/statuses/user_timeline/#{@username}.json?#{@statuses_query_params}") \
147
- .returns(status_records.to_json)
148
- @io.expects(:show_record).with(gen_records[0])
149
- @client.user
150
- end
59
+ context "at runtime" do
60
+ setup do
61
+ @username = "spiky"
62
+ @password = "lullaby"
63
+ @client = Client.new(@deps, { :username => @username, :password => @password })
64
+ @base_url = "https://#{@username}:#{@password}@twitter.com"
65
+ @statuses_query_params = "count=#{Client::DEFAULT_NUM_STATUSES}&page=#{Client::DEFAULT_PAGE_NUM}"
66
+ @users_query_params = "page=#{Client::DEFAULT_PAGE_NUM}"
67
+ end
151
68
 
152
- context "for posting status updates" do
153
- should "post a status update via argument, when positive confirmation" do
154
- status = "wondering around"
69
+ should "fetch friends' statuses (home view)" do
155
70
  status_records, gen_records = create_test_statuses(
156
71
  {
157
- :user => @username,
72
+ :user => "zanzibar",
158
73
  :status => {
159
74
  :created_at => Time.at(1).to_s,
160
- :text => status,
75
+ :text => "wassup?",
76
+ :in_reply_to => nil
77
+ }
78
+ },
79
+ {
80
+ :user => "lulzwoo",
81
+ :status => {
82
+ :created_at => Time.at(1).to_s,
83
+ :text => "nuttin'",
161
84
  :in_reply_to => nil
162
85
  }
163
86
  }
164
87
  )
165
- RestClientWrapper.expects(:post) \
166
- .with("#{@base_url}/statuses/update.json", {:status => status}) \
167
- .returns(status_records[0].to_json)
168
- @io.expects(:confirm).with("Really send?").returns(true)
169
- @io.expects(:show_status_preview).with(status)
170
- @io.expects(:info).with("Sent status update.\n\n")
88
+ @rest_client.expects(:get) \
89
+ .with("#{@base_url}/statuses/friends_timeline.json?#{@statuses_query_params}") \
90
+ .returns(status_records.to_json)
171
91
  @io.expects(:show_record).with(gen_records[0])
172
- @client.update(status)
92
+ @io.expects(:show_record).with(gen_records[1])
93
+ @client.home
173
94
  end
174
95
 
175
- should "post a status update via prompt, when positive confirmation" do
176
- status = "wondering around"
96
+ should "fetch mentions" do
177
97
  status_records, gen_records = create_test_statuses(
178
- { :user => @username,
98
+ {
99
+ :user => "zanzibar",
179
100
  :status => {
180
101
  :created_at => Time.at(1).to_s,
181
- :text => status,
182
- :in_reply_to => nil
102
+ :text => "wassup, @#{@username}?",
103
+ :in_reply_to => @username
104
+ }
105
+ },
106
+ {
107
+ :user => "lulzwoo",
108
+ :status => {
109
+ :created_at => Time.at(1).to_s,
110
+ :text => "@#{@username}, doing nuttin'",
111
+ :in_reply_to => @username
183
112
  }
184
113
  }
185
114
  )
186
- RestClientWrapper.expects(:post) \
187
- .with("#{@base_url}/statuses/update.json", {:status => status}) \
188
- .returns(status_records[0].to_json)
189
- @io.expects(:prompt).with("Status update").returns(status)
190
- @io.expects(:show_status_preview).with(status)
191
- @io.expects(:confirm).with("Really send?").returns(true)
192
- @io.expects(:info).with("Sent status update.\n\n")
115
+ @rest_client.expects(:get) \
116
+ .with("#{@base_url}/statuses/mentions.json?#{@statuses_query_params}") \
117
+ .returns(status_records.to_json)
193
118
  @io.expects(:show_record).with(gen_records[0])
194
- @client.update
195
- end
196
-
197
- should "cancel a status update via argument, when negative confirmation" do
198
- status = "wondering around"
199
- RestClientWrapper.expects(:post).never
200
- @io.expects(:show_status_preview).with(status)
201
- @io.expects(:confirm).with("Really send?").returns(false)
202
- @io.expects(:info).with("Cancelled.")
203
- @io.expects(:show_record).never
204
- @client.update(status)
205
- end
206
-
207
- should "cancel a status update via prompt, when negative confirmation" do
208
- status = "wondering around"
209
- RestClientWrapper.expects(:post).never
210
- @io.expects(:prompt).with("Status update").returns(status)
211
- @io.expects(:show_status_preview).with(status)
212
- @io.expects(:confirm).with("Really send?").returns(false)
213
- @io.expects(:info).with("Cancelled.")
214
- @io.expects(:show_record).never
215
- @client.update
216
- end
217
-
218
- should "cancel a status update via argument, when empty status" do
219
- RestClientWrapper.expects(:post).never
220
- @io.expects(:confirm).never
221
- @io.expects(:info).with("Cancelled.")
222
- @io.expects(:show_record).never
223
- @client.update("")
119
+ @io.expects(:show_record).with(gen_records[1])
120
+ @client.mentions
224
121
  end
225
122
 
226
- should "cancel a status update via prompt, when empty status" do
227
- RestClientWrapper.expects(:post).never
228
- @io.expects(:prompt).with("Status update").returns("")
229
- @io.expects(:confirm).never
230
- @io.expects(:info).with("Cancelled.")
231
- @io.expects(:show_record).never
232
- @client.update
233
- end
234
-
235
- should "remove excess whitespace around a status update" do
236
- whitespaced_status = " oh, i was sloppy \t "
237
- stripped_status = "oh, i was sloppy"
123
+ should "fetch a specific user's statuses, when the user identified by given argument" do
124
+ user = "spoonman"
238
125
  status_records, gen_records = create_test_statuses(
239
- { :user => @username,
126
+ {
127
+ :user => user,
240
128
  :status => {
241
129
  :created_at => Time.at(1).to_s,
242
- :text => stripped_status,
130
+ :text => "wassup?",
243
131
  :in_reply_to => nil
244
132
  }
245
133
  }
246
134
  )
247
- RestClientWrapper.expects(:post) \
248
- .with("#{@base_url}/statuses/update.json", {:status => stripped_status}) \
249
- .returns(status_records[0].to_json)
250
- @io.expects(:show_status_preview).with(stripped_status)
251
- @io.expects(:confirm).with("Really send?").returns(true)
252
- @io.expects(:info).with("Sent status update.\n\n")
135
+ @rest_client.expects(:get) \
136
+ .with("#{@base_url}/statuses/user_timeline/#{user}.json?#{@statuses_query_params}") \
137
+ .returns(status_records.to_json)
253
138
  @io.expects(:show_record).with(gen_records[0])
254
- @client.update(whitespaced_status)
139
+ @client.user(user)
255
140
  end
256
141
 
257
- should "truncate a status update with too long argument and warn the user" do
258
- long_status = "x aaa bbb ccc ddd eee fff ggg hhh iii jjj kkk lll mmm nnn ooo ppp qqq rrr sss ttt uuu vvv www xxx yyy zzz 111 222 333 444 555 666 777 888 999 000"
259
- truncated_status = "x aaa bbb ccc ddd eee fff ggg hhh iii jjj kkk lll mmm nnn ooo ppp qqq rrr sss ttt uuu vvv www xxx yyy zzz 111 222 333 444 555 666 777 888 99"
142
+ should "fetch a specific user's statuses, with the user being the authenticated user itself when given no argument" do
260
143
  status_records, gen_records = create_test_statuses(
261
- { :user => @username,
144
+ {
145
+ :user => @username,
262
146
  :status => {
263
147
  :created_at => Time.at(1).to_s,
264
- :text => truncated_status,
148
+ :text => "wassup?",
265
149
  :in_reply_to => nil
266
150
  }
267
151
  }
268
152
  )
269
- RestClientWrapper.expects(:post) \
270
- .with("#{@base_url}/statuses/update.json", {:status => truncated_status}) \
271
- .returns(status_records[0].to_json)
272
- @io.expects(:warn).with("Status will be truncated.")
273
- @io.expects(:show_status_preview).with(truncated_status)
274
- @io.expects(:confirm).with("Really send?").returns(true)
275
- @io.expects(:info).with("Sent status update.\n\n")
153
+ @rest_client.expects(:get) \
154
+ .with("#{@base_url}/statuses/user_timeline/#{@username}.json?#{@statuses_query_params}") \
155
+ .returns(status_records.to_json)
276
156
  @io.expects(:show_record).with(gen_records[0])
277
- @client.update(long_status)
157
+ @client.user
278
158
  end
279
159
 
280
- context "with URL shortening enabled" do
281
- setup do
282
- @client = Client.new(@io, {
283
- :username => @username,
284
- :password => @password,
285
- :shorten_urls => {
286
- :enable => true,
287
- :service_url => "http://shorten.it/create",
288
- :method => "post",
289
- :url_param_name => "url",
290
- :xpath_selector => "//input[@id='short_url']/@value"
291
- }
292
- })
293
- @url_shortener = @client.instance_variable_get(:@url_shortener)
294
- end
295
-
296
- should "shorten URLs, avoiding truncation with long URLs" do
297
- long_urls = ["http://www.google.fi/search?q=ruby+nokogiri&ie=utf-8&oe=utf-8&aq=t&rls=org.mozilla:en-US:official&client=firefox-a", "http://www.w3.org/TR/1999/REC-xpath-19991116"]
298
- long_status = long_urls.join(" and ")
299
- short_urls = ["http://shorten.it/2k7i8", "http://shorten.it/2k7mk"]
300
- shortened_status = short_urls.join(" and ")
160
+ context "for posting status updates" do
161
+ should "post a status update via argument, when positive confirmation" do
162
+ status = "wondering around"
301
163
  status_records, gen_records = create_test_statuses(
302
- { :user => @username,
164
+ {
165
+ :user => @username,
303
166
  :status => {
304
167
  :created_at => Time.at(1).to_s,
305
- :text => shortened_status,
168
+ :text => status,
306
169
  :in_reply_to => nil
307
170
  }
308
171
  }
309
172
  )
310
- RestClientWrapper.expects(:post) \
311
- .with("#{@base_url}/statuses/update.json", {:status => shortened_status}) \
173
+ @rest_client.expects(:post) \
174
+ .with("#{@base_url}/statuses/update.json", {:status => status}) \
312
175
  .returns(status_records[0].to_json)
313
- @url_shortener.expects(:shorten).with(long_urls.first).returns(short_urls.first)
314
- @url_shortener.expects(:shorten).with(long_urls.last).returns(short_urls.last)
315
- @io.expects(:show_status_preview).with(shortened_status)
316
176
  @io.expects(:confirm).with("Really send?").returns(true)
177
+ @io.expects(:show_status_preview).with(status)
317
178
  @io.expects(:info).with("Sent status update.\n\n")
318
179
  @io.expects(:show_record).with(gen_records[0])
319
- @client.update(long_status)
180
+ @client.update(status)
320
181
  end
321
182
 
322
- should "discard obviously invalid shortened URLs, using originals instead" do
323
- long_urls = ["http://www.google.fi/", "http://www.w3.org/TR/1999/REC-xpath-19991116"]
324
- status = long_urls.join(" and ")
325
- short_urls = [nil, ""]
183
+ should "post a status update via prompt, when positive confirmation" do
184
+ status = "wondering around"
326
185
  status_records, gen_records = create_test_statuses(
327
186
  { :user => @username,
328
187
  :status => {
@@ -332,134 +191,283 @@ class ClientTest < Test::Unit::TestCase
332
191
  }
333
192
  }
334
193
  )
335
- RestClientWrapper.expects(:post) \
194
+ @rest_client.expects(:post) \
336
195
  .with("#{@base_url}/statuses/update.json", {:status => status}) \
337
196
  .returns(status_records[0].to_json)
338
- @url_shortener.expects(:shorten).with(long_urls.first).returns(short_urls.first)
339
- @url_shortener.expects(:shorten).with(long_urls.last).returns(short_urls.last)
197
+ @io.expects(:prompt).with("Status update").returns(status)
340
198
  @io.expects(:show_status_preview).with(status)
341
199
  @io.expects(:confirm).with("Really send?").returns(true)
342
200
  @io.expects(:info).with("Sent status update.\n\n")
343
201
  @io.expects(:show_record).with(gen_records[0])
202
+ @client.update
203
+ end
204
+
205
+ should "cancel a status update via argument, when negative confirmation" do
206
+ status = "wondering around"
207
+ @rest_client.expects(:post).never
208
+ @io.expects(:show_status_preview).with(status)
209
+ @io.expects(:confirm).with("Really send?").returns(false)
210
+ @io.expects(:info).with("Cancelled.")
211
+ @io.expects(:show_record).never
344
212
  @client.update(status)
345
213
  end
346
214
 
347
- should "reuse a shortened URL for duplicate long URLs" do
348
- long_urls = ["http://www.w3.org/TR/1999/REC-xpath-19991116"] * 2
349
- long_status = long_urls.join(" and ")
350
- short_url = "http://shorten.it/2k7mk"
351
- short_status = ([short_url] * 2).join(" and ")
215
+ should "cancel a status update via prompt, when negative confirmation" do
216
+ status = "wondering around"
217
+ @rest_client.expects(:post).never
218
+ @io.expects(:prompt).with("Status update").returns(status)
219
+ @io.expects(:show_status_preview).with(status)
220
+ @io.expects(:confirm).with("Really send?").returns(false)
221
+ @io.expects(:info).with("Cancelled.")
222
+ @io.expects(:show_record).never
223
+ @client.update
224
+ end
225
+
226
+ should "cancel a status update via argument, when empty status" do
227
+ @rest_client.expects(:post).never
228
+ @io.expects(:confirm).never
229
+ @io.expects(:info).with("Cancelled.")
230
+ @io.expects(:show_record).never
231
+ @client.update("")
232
+ end
233
+
234
+ should "cancel a status update via prompt, when empty status" do
235
+ @rest_client.expects(:post).never
236
+ @io.expects(:prompt).with("Status update").returns("")
237
+ @io.expects(:confirm).never
238
+ @io.expects(:info).with("Cancelled.")
239
+ @io.expects(:show_record).never
240
+ @client.update
241
+ end
242
+
243
+ should "remove excess whitespace around a status update" do
244
+ whitespaced_status = " oh, i was sloppy \t "
245
+ stripped_status = "oh, i was sloppy"
352
246
  status_records, gen_records = create_test_statuses(
353
247
  { :user => @username,
354
248
  :status => {
355
249
  :created_at => Time.at(1).to_s,
356
- :text => short_status,
250
+ :text => stripped_status,
357
251
  :in_reply_to => nil
358
252
  }
359
253
  }
360
254
  )
361
- RestClientWrapper.expects(:post) \
362
- .with("#{@base_url}/statuses/update.json", {:status => short_status}) \
255
+ @rest_client.expects(:post) \
256
+ .with("#{@base_url}/statuses/update.json", {:status => stripped_status}) \
363
257
  .returns(status_records[0].to_json)
364
- @url_shortener.expects(:shorten).with(long_urls.first).returns(short_url)
365
- @io.expects(:show_status_preview).with(short_status)
258
+ @io.expects(:show_status_preview).with(stripped_status)
259
+ @io.expects(:confirm).with("Really send?").returns(true)
260
+ @io.expects(:info).with("Sent status update.\n\n")
261
+ @io.expects(:show_record).with(gen_records[0])
262
+ @client.update(whitespaced_status)
263
+ end
264
+
265
+ should "truncate a status update with too long argument and warn the user" do
266
+ long_status = "x aaa bbb ccc ddd eee fff ggg hhh iii jjj kkk lll mmm nnn ooo ppp qqq rrr sss ttt uuu vvv www xxx yyy zzz 111 222 333 444 555 666 777 888 999 000"
267
+ truncated_status = "x aaa bbb ccc ddd eee fff ggg hhh iii jjj kkk lll mmm nnn ooo ppp qqq rrr sss ttt uuu vvv www xxx yyy zzz 111 222 333 444 555 666 777 888 99"
268
+ status_records, gen_records = create_test_statuses(
269
+ { :user => @username,
270
+ :status => {
271
+ :created_at => Time.at(1).to_s,
272
+ :text => truncated_status,
273
+ :in_reply_to => nil
274
+ }
275
+ }
276
+ )
277
+ @rest_client.expects(:post) \
278
+ .with("#{@base_url}/statuses/update.json", {:status => truncated_status}) \
279
+ .returns(status_records[0].to_json)
280
+ @io.expects(:warn).with("Status will be truncated.")
281
+ @io.expects(:show_status_preview).with(truncated_status)
366
282
  @io.expects(:confirm).with("Really send?").returns(true)
367
283
  @io.expects(:info).with("Sent status update.\n\n")
368
284
  @io.expects(:show_record).with(gen_records[0])
369
285
  @client.update(long_status)
370
286
  end
371
287
 
372
- context "in erroneous situations" do
288
+ context "with URL shortening enabled" do
373
289
  setup do
374
- @url = "http://www.w3.org/TR/1999/REC-xpath-19991116"
375
- @status = "skimming through #{@url}"
376
- @status_records, @gen_records = create_test_statuses(
290
+ @client = Client.new(@deps, {
291
+ :username => @username,
292
+ :password => @password,
293
+ :shorten_urls => {
294
+ :enable => true,
295
+ :service_url => "http://shorten.it/create",
296
+ :method => "post",
297
+ :url_param_name => "url",
298
+ :xpath_selector => "//input[@id='short_url']/@value"
299
+ }
300
+ })
301
+ end
302
+
303
+ should "shorten URLs, avoiding truncation with long URLs" do
304
+ long_urls = ["http://www.google.fi/search?q=ruby+nokogiri&ie=utf-8&oe=utf-8&aq=t&rls=org.mozilla:en-US:official&client=firefox-a", "http://www.w3.org/TR/1999/REC-xpath-19991116"]
305
+ long_status = long_urls.join(" and ")
306
+ short_urls = ["http://shorten.it/2k7i8", "http://shorten.it/2k7mk"]
307
+ shortened_status = short_urls.join(" and ")
308
+ status_records, gen_records = create_test_statuses(
377
309
  { :user => @username,
378
310
  :status => {
379
311
  :created_at => Time.at(1).to_s,
380
- :text => @status,
312
+ :text => shortened_status,
381
313
  :in_reply_to => nil
382
314
  }
383
315
  }
384
316
  )
317
+ @rest_client.expects(:post) \
318
+ .with("#{@base_url}/statuses/update.json", {:status => shortened_status}) \
319
+ .returns(status_records[0].to_json)
320
+ @url_shortener.expects(:shorten).with(long_urls.first).returns(short_urls.first)
321
+ @url_shortener.expects(:shorten).with(long_urls.last).returns(short_urls.last)
322
+ @io.expects(:show_status_preview).with(shortened_status)
323
+ @io.expects(:confirm).with("Really send?").returns(true)
324
+ @io.expects(:info).with("Sent status update.\n\n")
325
+ @io.expects(:show_record).with(gen_records[0])
326
+ @client.update(long_status)
385
327
  end
386
328
 
387
- should "skip shortening URLs if required libraries are not found" do
388
- RestClientWrapper.expects(:post) \
389
- .with("#{@base_url}/statuses/update.json", {:status => @status}) \
390
- .returns(@status_records[0].to_json)
391
- @url_shortener.expects(:shorten).with(@url).raises(LoadError, "gem not found")
392
- @io.expects(:warn)
393
- @io.expects(:show_status_preview).with(@status)
329
+ should "discard obviously invalid shortened URLs, using originals instead" do
330
+ long_urls = ["http://www.google.fi/", "http://www.w3.org/TR/1999/REC-xpath-19991116"]
331
+ status = long_urls.join(" and ")
332
+ short_urls = [nil, ""]
333
+ status_records, gen_records = create_test_statuses(
334
+ { :user => @username,
335
+ :status => {
336
+ :created_at => Time.at(1).to_s,
337
+ :text => status,
338
+ :in_reply_to => nil
339
+ }
340
+ }
341
+ )
342
+ @rest_client.expects(:post) \
343
+ .with("#{@base_url}/statuses/update.json", {:status => status}) \
344
+ .returns(status_records[0].to_json)
345
+ @url_shortener.expects(:shorten).with(long_urls.first).returns(short_urls.first)
346
+ @url_shortener.expects(:shorten).with(long_urls.last).returns(short_urls.last)
347
+ @io.expects(:show_status_preview).with(status)
394
348
  @io.expects(:confirm).with("Really send?").returns(true)
395
349
  @io.expects(:info).with("Sent status update.\n\n")
396
- @io.expects(:show_record).with(@gen_records[0])
397
- @client.update(@status)
350
+ @io.expects(:show_record).with(gen_records[0])
351
+ @client.update(status)
398
352
  end
399
353
 
400
- should "skip shortening URLs upon connection error to the URL shortening service" do
401
- RestClientWrapper.expects(:post) \
402
- .with("#{@base_url}/statuses/update.json", {:status => @status}) \
403
- .returns(@status_records[0].to_json)
404
- @url_shortener.expects(:shorten).with(@url).raises(ClientError, "connection error")
405
- @io.expects(:warn)
406
- @io.expects(:show_status_preview).with(@status)
354
+ should "reuse a shortened URL for duplicate long URLs" do
355
+ long_urls = ["http://www.w3.org/TR/1999/REC-xpath-19991116"] * 2
356
+ long_status = long_urls.join(" and ")
357
+ short_url = "http://shorten.it/2k7mk"
358
+ short_status = ([short_url] * 2).join(" and ")
359
+ status_records, gen_records = create_test_statuses(
360
+ { :user => @username,
361
+ :status => {
362
+ :created_at => Time.at(1).to_s,
363
+ :text => short_status,
364
+ :in_reply_to => nil
365
+ }
366
+ }
367
+ )
368
+ @rest_client.expects(:post) \
369
+ .with("#{@base_url}/statuses/update.json", {:status => short_status}) \
370
+ .returns(status_records[0].to_json)
371
+ @url_shortener.expects(:shorten).with(long_urls.first).returns(short_url)
372
+ @io.expects(:show_status_preview).with(short_status)
407
373
  @io.expects(:confirm).with("Really send?").returns(true)
408
374
  @io.expects(:info).with("Sent status update.\n\n")
409
- @io.expects(:show_record).with(@gen_records[0])
410
- @client.update(@status)
375
+ @io.expects(:show_record).with(gen_records[0])
376
+ @client.update(long_status)
377
+ end
378
+
379
+ context "in erroneous situations" do
380
+ setup do
381
+ @url = "http://www.w3.org/TR/1999/REC-xpath-19991116"
382
+ @status = "skimming through #{@url}"
383
+ @status_records, @gen_records = create_test_statuses(
384
+ { :user => @username,
385
+ :status => {
386
+ :created_at => Time.at(1).to_s,
387
+ :text => @status,
388
+ :in_reply_to => nil
389
+ }
390
+ }
391
+ )
392
+ end
393
+
394
+ should "skip shortening URLs if required libraries are not found" do
395
+ @rest_client.expects(:post) \
396
+ .with("#{@base_url}/statuses/update.json", {:status => @status}) \
397
+ .returns(@status_records[0].to_json)
398
+ @url_shortener.expects(:shorten).with(@url).raises(LoadError, "gem not found")
399
+ @io.expects(:warn)
400
+ @io.expects(:show_status_preview).with(@status)
401
+ @io.expects(:confirm).with("Really send?").returns(true)
402
+ @io.expects(:info).with("Sent status update.\n\n")
403
+ @io.expects(:show_record).with(@gen_records[0])
404
+ @client.update(@status)
405
+ end
406
+
407
+ should "skip shortening URLs upon connection error to the URL shortening service" do
408
+ @rest_client.expects(:post) \
409
+ .with("#{@base_url}/statuses/update.json", {:status => @status}) \
410
+ .returns(@status_records[0].to_json)
411
+ @url_shortener.expects(:shorten).with(@url).raises(ClientError, "connection error")
412
+ @io.expects(:warn)
413
+ @io.expects(:show_status_preview).with(@status)
414
+ @io.expects(:confirm).with("Really send?").returns(true)
415
+ @io.expects(:info).with("Sent status update.\n\n")
416
+ @io.expects(:show_record).with(@gen_records[0])
417
+ @client.update(@status)
418
+ end
411
419
  end
412
420
  end
413
421
  end
414
- end
415
422
 
416
- should "fetch friends" do
417
- user_records, gen_records = create_test_users(
418
- {
419
- :user => "zanzibar",
420
- :status => {
421
- :created_at => Time.at(1).to_s,
422
- :text => "wassup, @foo?",
423
- :in_reply_to => "foo"
424
- }
425
- },
426
- {
427
- :user => "lulzwoo",
428
- :status => {
429
- :created_at => Time.at(1).to_s,
430
- :text => "@foo, doing nuttin'",
431
- :in_reply_to => "foo"
423
+ should "fetch friends" do
424
+ user_records, gen_records = create_test_users(
425
+ {
426
+ :user => "zanzibar",
427
+ :status => {
428
+ :created_at => Time.at(1).to_s,
429
+ :text => "wassup, @foo?",
430
+ :in_reply_to => "foo"
431
+ }
432
+ },
433
+ {
434
+ :user => "lulzwoo",
435
+ :status => {
436
+ :created_at => Time.at(1).to_s,
437
+ :text => "@foo, doing nuttin'",
438
+ :in_reply_to => "foo"
439
+ }
432
440
  }
433
- }
434
- )
435
- RestClientWrapper.expects(:get) \
436
- .with("#{@base_url}/statuses/friends/#{@username}.json?#{@users_query_params}") \
437
- .returns(user_records.to_json)
438
- @io.expects(:show_record).with(gen_records[0])
439
- @io.expects(:show_record).with(gen_records[1])
440
- @client.friends
441
- end
441
+ )
442
+ @rest_client.expects(:get) \
443
+ .with("#{@base_url}/statuses/friends/#{@username}.json?#{@users_query_params}") \
444
+ .returns(user_records.to_json)
445
+ @io.expects(:show_record).with(gen_records[0])
446
+ @io.expects(:show_record).with(gen_records[1])
447
+ @client.friends
448
+ end
442
449
 
443
- should "fetch followers" do
444
- user_records, gen_records = create_test_users(
445
- {
446
- :user => "zanzibar",
447
- :status => {
448
- :created_at => Time.at(1).to_s,
449
- :text => "wassup, @foo?",
450
- :in_reply_to => "foo"
450
+ should "fetch followers" do
451
+ user_records, gen_records = create_test_users(
452
+ {
453
+ :user => "zanzibar",
454
+ :status => {
455
+ :created_at => Time.at(1).to_s,
456
+ :text => "wassup, @foo?",
457
+ :in_reply_to => "foo"
458
+ }
459
+ },
460
+ {
461
+ :user => "lulzwoo"
451
462
  }
452
- },
453
- {
454
- :user => "lulzwoo"
455
- }
456
- )
457
- RestClientWrapper.expects(:get) \
458
- .with("#{@base_url}/statuses/followers/#{@username}.json?#{@users_query_params}") \
459
- .returns(user_records.to_json)
460
- @io.expects(:show_record).with(gen_records[0])
461
- @io.expects(:show_record).with(gen_records[1])
462
- @client.followers
463
+ )
464
+ @rest_client.expects(:get) \
465
+ .with("#{@base_url}/statuses/followers/#{@username}.json?#{@users_query_params}") \
466
+ .returns(user_records.to_json)
467
+ @io.expects(:show_record).with(gen_records[0])
468
+ @io.expects(:show_record).with(gen_records[1])
469
+ @client.followers
470
+ end
463
471
  end
464
472
  end
465
473
  end