dschn-twitter 0.3.7.2 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,19 @@
1
+ 0.4.1 - January 1, 2009
2
+ * 4 minor enhancements and 2 bug fixes:
3
+ * Added better exception handling (Billy Gray)
4
+ * Added page to search (Michael Ivey)
5
+ * Adding an option to display tweets on CLI in reverse order (oldest first). (Cameron Booth)
6
+ * Added in_reply_to_status_id option for replying to statuses (anthonycrumley)
7
+ * Fixed a bug where the @config was improperly set (K. Adam Christensen)
8
+ * Fix verify_credentials to include a format (breaks in laconica). (dustin)
9
+
10
+ 0.4.0 - December 23, 2008
11
+ * 3 major changes
12
+ * Removed active support as dependency
13
+ * Removed CLI dependencies from install dependency list
14
+ (they are now only installed by you manually)
15
+ * Switched to echoe for gem managment
16
+
1
17
  0.3.7 - August 26, 2008
2
18
  * Fixed source param not getting through
3
19
 
File without changes
@@ -1,11 +1,4 @@
1
- History.txt
2
- License.txt
3
- Manifest.txt
4
- README.txt
5
- Rakefile
6
1
  bin/twitter
7
- config/hoe.rb
8
- config/requirements.rb
9
2
  examples/blocks.rb
10
3
  examples/direct_messages.rb
11
4
  examples/favorites.rb
@@ -20,9 +13,8 @@ examples/sent_messages.rb
20
13
  examples/timeline.rb
21
14
  examples/twitter.rb
22
15
  examples/verify_credentials.rb
23
- lib/twitter.rb
16
+ History
24
17
  lib/twitter/base.rb
25
- lib/twitter/cli.rb
26
18
  lib/twitter/cli/config.rb
27
19
  lib/twitter/cli/helpers.rb
28
20
  lib/twitter/cli/migrations/20080722194500_create_accounts.rb
@@ -32,17 +24,21 @@ lib/twitter/cli/migrations/20080722214606_create_configurations.rb
32
24
  lib/twitter/cli/models/account.rb
33
25
  lib/twitter/cli/models/configuration.rb
34
26
  lib/twitter/cli/models/tweet.rb
27
+ lib/twitter/cli.rb
35
28
  lib/twitter/direct_message.rb
36
29
  lib/twitter/easy_class_maker.rb
37
30
  lib/twitter/rate_limit_status.rb
38
31
  lib/twitter/search.rb
32
+ lib/twitter/search_result.rb
33
+ lib/twitter/search_result_info.rb
39
34
  lib/twitter/status.rb
40
35
  lib/twitter/user.rb
41
36
  lib/twitter/version.rb
42
- script/destroy
43
- script/generate
44
- script/txt2html
45
- setup.rb
37
+ lib/twitter.rb
38
+ License
39
+ Manifest
40
+ Rakefile
41
+ README
46
42
  spec/base_spec.rb
47
43
  spec/cli/helper_spec.rb
48
44
  spec/direct_message_spec.rb
@@ -51,8 +47,11 @@ spec/fixtures/friends.xml
51
47
  spec/fixtures/friends_for.xml
52
48
  spec/fixtures/friends_lite.xml
53
49
  spec/fixtures/friends_timeline.xml
50
+ spec/fixtures/friendship_already_exists.xml
51
+ spec/fixtures/friendship_created.xml
54
52
  spec/fixtures/public_timeline.xml
55
53
  spec/fixtures/rate_limit_status.xml
54
+ spec/fixtures/search_result_info.yml
56
55
  spec/fixtures/search_results.json
57
56
  spec/fixtures/status.xml
58
57
  spec/fixtures/user.xml
@@ -62,9 +61,6 @@ spec/spec.opts
62
61
  spec/spec_helper.rb
63
62
  spec/status_spec.rb
64
63
  spec/user_spec.rb
65
- tasks/deployment.rake
66
- tasks/environment.rake
67
- tasks/website.rake
68
64
  twitter.gemspec
69
65
  website/css/common.css
70
66
  website/images/terminal_output.png
File without changes
data/Rakefile CHANGED
@@ -1,4 +1,42 @@
1
- require 'config/requirements'
2
- require 'config/hoe' # setup Hoe + all gem configuration
1
+ ProjectName = 'twitter'
2
+ WebsitePath = "jnunemaker@rubyforge.org:/var/www/gforge-projects/#{ProjectName}"
3
3
 
4
- Dir['tasks/**/*.rake'].each { |rake| load rake }
4
+ require 'rubygems'
5
+ require 'rake'
6
+ require 'echoe'
7
+ require 'spec/rake/spectask'
8
+ require "lib/#{ProjectName}/version"
9
+
10
+ Echoe.new(ProjectName, Twitter::Version) do |p|
11
+ p.description = "a command line interface for twitter, also a library which wraps the twitter api"
12
+ p.url = "http://#{ProjectName}.rubyforge.org"
13
+ p.author = "John Nunemaker"
14
+ p.email = "nunemaker@gmail.com"
15
+ p.extra_deps = [['hpricot', '>= 0.6'], ['activesupport', '>= 2.1'], ['httparty', '>= 0.2.4']]
16
+ p.need_tar_gz = false
17
+ p.docs_host = WebsitePath
18
+ end
19
+
20
+ desc 'Upload website files to rubyforge'
21
+ task :website do
22
+ sh %{rsync -av website/ #{WebsitePath}}
23
+ Rake::Task['website_docs'].invoke
24
+ end
25
+
26
+ task :website_docs do
27
+ Rake::Task['redocs'].invoke
28
+ sh %{rsync -av doc/ #{WebsitePath}/docs}
29
+ end
30
+
31
+ desc 'Preps the gem for a new release'
32
+ task :prepare do
33
+ %w[manifest build_gemspec].each do |task|
34
+ Rake::Task[task].invoke
35
+ end
36
+ end
37
+
38
+ Rake::Task[:default].prerequisites.clear
39
+ task :default => :spec
40
+ Spec::Rake::SpecTask.new do |t|
41
+ t.spec_files = FileList["spec/**/*_spec.rb"]
42
+ end
data/bin/twitter CHANGED
@@ -10,6 +10,5 @@ if ARGV[0] && ARGV[0] == 'd' && !STDIN.tty?
10
10
  ARGV[2] = "#{STDIN.read}#{ARGV[2]}"
11
11
  end
12
12
 
13
- $:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
14
- require 'twitter'
13
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'twitter'))
15
14
  require 'twitter/cli'
@@ -1,4 +1,5 @@
1
1
  require 'rubygems'
2
+ require 'activesupport'
2
3
  require File.join(File.dirname(__FILE__), '..', 'lib', 'twitter')
3
4
  config = YAML::load(open(ENV['HOME'] + '/.twitter'))
4
5
 
@@ -4,13 +4,13 @@ config = YAML::load(open(ENV['HOME'] + '/.twitter'))
4
4
 
5
5
  twitter = Twitter::Base.new(config['email'], config['password'])
6
6
 
7
- puts 'FAVORITES'
8
- twitter.favorites.each { |f| puts f.text }
7
+ puts 'CREATE'
8
+ puts twitter.create_favorite(865416114).text
9
9
  puts
10
10
  puts
11
11
 
12
- puts 'CREATE'
13
- puts twitter.create_favorite(865416114).text
12
+ puts 'FAVORITES'
13
+ twitter.favorites.each { |f| puts f.text }
14
14
  puts
15
15
  puts
16
16
 
data/examples/replies.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'rubygems'
2
+ require 'activesupport'
2
3
  require File.join(File.dirname(__FILE__), '..', 'lib', 'twitter')
3
4
  config = YAML::load(open(ENV['HOME'] + '/.twitter'))
4
5
 
data/examples/search.rb CHANGED
@@ -2,6 +2,7 @@ require 'rubygems'
2
2
  require File.join(File.dirname(__FILE__), '..', 'lib', 'twitter')
3
3
 
4
4
  Twitter::Search.new('httparty').each { |r| puts r.inspect,'' }
5
+ Twitter::Search.new('httparty').page(2).each { |r| puts r.inspect, '' }
5
6
 
6
7
  # search = Twitter::Search.new
7
8
  # search.from('jnunemaker').to('oaknd1').each { |r| puts r.inspect, '' }
@@ -1,4 +1,5 @@
1
1
  require 'rubygems'
2
+ require 'activesupport'
2
3
  require File.join(File.dirname(__FILE__), '..', 'lib', 'twitter')
3
4
  config = YAML::load(open(ENV['HOME'] + '/.twitter'))
4
5
 
data/examples/timeline.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'rubygems'
2
+ require 'activesupport'
2
3
  require File.join(File.dirname(__FILE__), '..', 'lib', 'twitter')
3
4
  config = YAML::load(open(ENV['HOME'] + '/.twitter'))
4
5
 
data/lib/twitter/base.rb CHANGED
@@ -12,16 +12,10 @@ module Twitter
12
12
  # Identi.ca example:
13
13
  # Twitter.new('email/username', 'password', :api_host => 'identi.ca/api')
14
14
  def initialize(email, password, options={})
15
- @config, @config[:email], @config[:password] = {}, email, password
16
15
  @api_host = options.delete(:api_host) || 'twitter.com'
17
- end
18
-
19
- def timeout=(time)
20
- @config[:timeout] = time
21
- end
22
-
23
- def timeout
24
- @config[:timeout]
16
+ @config, @config[:email], @config[:password] = options, email, password
17
+ @proxy_host = options[:proxy_host]
18
+ @proxy_port = options[:proxy_port]
25
19
  end
26
20
 
27
21
  # Returns an array of statuses for a timeline; Defaults to your friends timeline.
@@ -167,6 +161,7 @@ module Twitter
167
161
  def post(status, options={})
168
162
  form_data = {'status' => status}
169
163
  form_data.merge!({'source' => options[:source]}) if options[:source]
164
+ form_data.merge!({'in_reply_to_status_id' => options[:in_reply_to_status_id]}) if options[:in_reply_to_status_id]
170
165
  Status.new_from_xml(request('statuses/update.xml', :auth => true, :method => :post, :form_data => form_data))
171
166
  end
172
167
  alias :update :post
@@ -174,7 +169,7 @@ module Twitter
174
169
  # Verifies the credentials for the auth user.
175
170
  # raises Twitter::CantConnect on failure.
176
171
  def verify_credentials
177
- request('account/verify_credentials', :auth => true)
172
+ request('account/verify_credentials.xml', :auth => true)
178
173
  end
179
174
 
180
175
  private
@@ -192,41 +187,47 @@ module Twitter
192
187
  #
193
188
  # ie: call(:public_timeline, :auth => false)
194
189
  def call(method, options={})
195
- options.reverse_merge!({ :auth => true, :args => {} })
190
+ options = { :auth => true, :args => {} }.merge(options)
196
191
  # Following line needed as lite=false doesn't work in the API: http://tinyurl.com/yo3h5d
197
192
  options[:args].delete(:lite) unless options[:args][:lite]
198
193
  args = options.delete(:args)
199
194
  request(build_path("statuses/#{method.to_s}.xml", args), options)
200
195
  end
201
196
 
202
- # Makes a request to twitter.
203
- def request(path, options={})
204
- options.reverse_merge!({
205
- :headers => { "User-Agent" => @config[:email] },
206
- :method => :get
207
- })
208
- unless options[:since].blank?
209
- since = options[:since].kind_of?(Date) ? options[:since].strftime('%a, %d-%b-%y %T GMT') : options[:since].to_s
210
- options[:headers]["If-Modified-Since"] = since
211
- end
212
-
197
+ def response(path, options={})
213
198
  uri = URI.parse("http://#{@api_host}")
214
199
 
215
200
  begin
216
- response = Net::HTTP.start(uri.host, 80) do |http|
201
+ response = Net::HTTP::Proxy(@proxy_host, @proxy_port).start(uri.host, uri.port) do |http|
217
202
  klass = Net::HTTP.const_get options[:method].to_s.downcase.capitalize
218
203
  req = klass.new("#{uri.path}/#{path}", options[:headers])
219
204
  req.basic_auth(@config[:email], @config[:password]) if options[:auth]
220
205
  if options[:method].to_s == 'post' && options[:form_data]
221
206
  req.set_form_data(options[:form_data])
222
207
  end
223
- http.read_timeout = @config[:timeout] if @config[:timeout]
224
208
  http.request(req)
225
209
  end
226
210
  rescue => error
227
211
  raise CantConnect, error.message
228
212
  end
213
+ end
214
+
215
+ # Makes a request to twitter.
216
+ def request(path, options={})
217
+ options = {
218
+ :headers => { "User-Agent" => @config[:email] },
219
+ :method => :get,
220
+ }.merge(options)
221
+
222
+ unless options[:since].nil?
223
+ since = options[:since].kind_of?(Date) ? options[:since].strftime('%a, %d-%b-%y %T GMT') : options[:since].to_s
224
+ options[:headers]["If-Modified-Since"] = since
225
+ end
229
226
 
227
+ handle_response!(response(path, options))
228
+ end
229
+
230
+ def handle_response!(response)
230
231
  if %w[200 304].include?(response.code)
231
232
  response = parse(response.body)
232
233
  raise RateExceeded if (response/:hash/:error).text =~ /Rate limit exceeded/
@@ -237,14 +238,23 @@ module Twitter
237
238
  raise Unavailable, response.message
238
239
  elsif response.code == '401'
239
240
  raise CantConnect, 'Authentication failed. Check your username and password'
241
+ elsif response.code == '403'
242
+ error_message = (parse(response.body)/:hash/:error).text
243
+ raise CantFindUsers, error_message if error_message =~ /Could not find both specified users/
244
+ raise AlreadyFollowing, error_message if error_message =~ /already on your list/
245
+ raise CantFollowUser, "Response code #{response.code}: #{response.message} #{error_message}"
240
246
  else
241
247
  raise CantConnect, "Twitter is returning a #{response.code}: #{response.message}"
242
248
  end
243
- end
249
+ end
244
250
 
245
251
  # Given a path and a hash, build a full path with the hash turned into a query string
246
252
  def build_path(path, options)
247
- path += "?#{options.to_query}" unless options.blank?
253
+ unless options.nil?
254
+ query = options.inject('') { |str, h| str += "#{CGI.escape(h[0].to_s)}=#{CGI.escape(h[1].to_s)}&"; str }
255
+ path += "?#{query}"
256
+ end
257
+
248
258
  path
249
259
  end
250
260
 
@@ -259,4 +269,4 @@ module Twitter
259
269
  Hpricot.XML(response || '')
260
270
  end
261
271
  end
262
- end
272
+ end
@@ -5,25 +5,37 @@ module Twitter
5
5
  class NoAccounts < StandardError; end
6
6
 
7
7
  def output_tweets(collection, options={})
8
- options.reverse_merge!({
8
+ options = {
9
9
  :cache => false,
10
10
  :since_prefix => '',
11
- :empty_msg => 'Nothing new since your last check.'
12
- })
11
+ :empty_msg => 'Nothing new since your last check.',
12
+ :reverse => false
13
+ }.merge(options)
14
+
13
15
  if collection.size > 0
14
16
  justify = collection.collect { |s| s.user.screen_name }.max { |a,b| a.length <=> b.length }.length rescue 0
15
17
  indention = ' ' * (justify + 3)
16
18
  say("\n#{indention}#{collection.size} new tweet(s) found.\n\n")
19
+ collection.reverse! if options[:reverse]
17
20
  collection.each do |s|
18
21
  Tweet.create_from_tweet(current_account, s) if options[:cache]
22
+
19
23
  occurred_at = Time.parse(s.created_at).strftime('On %b %d at %l:%M%P')
20
24
  formatted_time = '-' * occurred_at.length + "\n#{indention}#{occurred_at}"
21
25
  formatted_name = s.user.screen_name.rjust(justify + 1)
22
26
  formatted_msg = ''
23
- s.text.split(' ').in_groups_of(6, false) { |row| formatted_msg += row.join(' ') + "\n#{indention}" }
24
- say "#{CGI::unescapeHTML(formatted_name)}: #{CGI::unescapeHTML(formatted_msg)}#{formatted_time}\n\n"
27
+
28
+ s.text.split(' ').each_with_index do |word, idx|
29
+ formatted_msg += "#{word} "
30
+
31
+ sixth_word = idx != 0 && idx % 6 == 0
32
+ formatted_msg += "\n#{indention}" if sixth_word
33
+ end
34
+
35
+ say "#{CGI::unescapeHTML(formatted_name)}: #{CGI::unescapeHTML(formatted_msg)}\n#{indention}#{formatted_time}\n\n"
25
36
  end
26
- Configuration["#{options[:since_prefix]}_since_id"] = collection.first.id
37
+
38
+ Configuration["#{options[:since_prefix]}_since_id"] = options[:reverse] ? collection.last.id : collection.first.id
27
39
  else
28
40
  say(options[:empty_msg])
29
41
  end
@@ -35,7 +47,7 @@ module Twitter
35
47
 
36
48
  def current_account
37
49
  @current_account ||= Account.active
38
- raise Account.count == 0 ? NoAccounts : NoActiveAccount if @current_account.blank?
50
+ raise Account.count == 0 ? NoAccounts : NoActiveAccount if @current_account.nil?
39
51
  @current_account
40
52
  end
41
53
 
@@ -44,7 +56,7 @@ module Twitter
44
56
  if File.exists?(tweet_file)
45
57
  say '.twitter file found, attempting import...'
46
58
  config = YAML::load(File.read(tweet_file))
47
- if !config['email'].blank? && !config['password'].blank?
59
+ if !config['email'].nil? && !config['password'].nil?
48
60
  Account.add(:username => config['email'], :password => config['password'])
49
61
  say 'Account imported'
50
62
  block.call if block_given?
data/lib/twitter/cli.rb CHANGED
@@ -1,8 +1,10 @@
1
1
  require 'rubygems'
2
+
2
3
  gem 'main', '>= 2.8.2'
3
4
  gem 'highline', '>= 1.4.0'
4
- gem 'activerecord', '>= 2.1'
5
+ gem 'activerecord', '= 2.2.2'
5
6
  gem 'sqlite3-ruby', '>= 1.2.1'
7
+
6
8
  require 'main'
7
9
  require 'highline/import'
8
10
  require 'activerecord'
@@ -177,7 +179,7 @@ Main {
177
179
  end
178
180
  post_thread.join
179
181
  progress_thread.join
180
- say "Got it! New twitter created at: #{status.created_at}\n"
182
+ say "Got it! New tweet created at: #{status.created_at}\n"
181
183
  end
182
184
  end
183
185
  end
@@ -215,7 +217,7 @@ Main {
215
217
  end
216
218
 
217
219
  mode 'follow' do
218
- description "Allows you to turn on notifications for a user"
220
+ description "Allows you to add notifications for a user (aka Follow Them)"
219
221
  argument('username') {
220
222
  required
221
223
  description 'username or id of twitterrer to follow'
@@ -275,21 +277,25 @@ Main {
275
277
  option('force', 'f') {
276
278
  description "Ignore since_id and show first page of results even if there aren't new ones"
277
279
  }
280
+ option('reverse', 'r') {
281
+ description 'Reverse the output so the oldest tweets are at the top'
282
+ }
278
283
 
279
284
  def run
280
285
  do_work do
281
286
  timeline = params['timeline'].value == 'me' ? 'user' : params['timeline'].value
282
287
  options, since_id = {}, Configuration["#{timeline}_since_id"]
283
- options[:since_id] = since_id if !since_id.blank? && !params['force'].given?
288
+ options[:since_id] = since_id if !since_id.nil? && !params['force'].given?
289
+ reverse = params['reverse'].given? ? true : false
284
290
  cache = [:friends, :user].include?(timeline)
285
291
  collection = base.timeline(timeline.to_sym, options)
286
- output_tweets(collection, {:cache => cache, :since_prefix => timeline})
292
+ output_tweets(collection, {:cache => cache, :since_prefix => timeline, :reverse => reverse})
287
293
  end
288
294
  end
289
295
  end
290
296
 
291
297
  mode 'replies' do
292
- description 'Allows you to view all @replies at you'
298
+ description 'Allows you to view all @replies sent to you'
293
299
  option('force', 'f') {
294
300
  description "Ignore since_id and show first page of replies even if there aren't new ones"
295
301
  }
@@ -297,7 +303,7 @@ Main {
297
303
  def run
298
304
  do_work do
299
305
  options, since_id = {}, Configuration["replies_since_id"]
300
- options[:since_id] = since_id if !since_id.blank? && !params['force'].given?
306
+ options[:since_id] = since_id if !since_id.nil? && !params['force'].given?
301
307
  collection = base.replies(options)
302
308
  output_tweets(collection, {:since_prefix => 'replies'})
303
309
  end
@@ -11,7 +11,7 @@ module Twitter
11
11
 
12
12
  def initialize(q=nil)
13
13
  clear
14
- containing(q) unless q.blank?
14
+ containing(q) if q && q.strip != ''
15
15
  end
16
16
 
17
17
  def from(user)
@@ -59,6 +59,12 @@ module Twitter
59
59
  self
60
60
  end
61
61
 
62
+ # Which page of results to fetch
63
+ def page(num)
64
+ @query[:page] = num
65
+ self
66
+ end
67
+
62
68
  # Only searches tweets since a given id.
63
69
  # Recommended to use this when possible.
64
70
  def since(since_id)
@@ -83,7 +89,7 @@ module Twitter
83
89
  # If you want to get results do something other than iterate over them.
84
90
  def fetch
85
91
  @query[:q] = @query[:q].join(' ')
86
- self.class.get('/search.json', {:query => @query})
92
+ SearchResultInfo.new_from_hash(self.class.get('/search.json', {:query => @query}))
87
93
  end
88
94
 
89
95
  def each
@@ -91,4 +97,5 @@ module Twitter
91
97
  @result['results'].each { |r| yield r }
92
98
  end
93
99
  end
94
- end
100
+ end
101
+
@@ -1,9 +1,3 @@
1
1
  module Twitter #:nodoc:
2
- module VERSION #:nodoc:
3
- MAJOR = 0
4
- MINOR = 3
5
- TINY = 7
6
-
7
- STRING = [MAJOR, MINOR, TINY].join('.')
8
- end
2
+ Version = '0.4.1'
9
3
  end
data/lib/twitter.rb CHANGED
@@ -1,6 +1,12 @@
1
- %w(uri cgi net/http yaml rubygems hpricot active_support).each { |f| require f }
1
+ require 'uri'
2
+ require 'cgi'
3
+ require 'net/http'
4
+ require 'yaml'
5
+ require 'time'
6
+ require 'rubygems'
7
+ require 'hpricot'
2
8
 
3
- $:.unshift(File.join(File.dirname(__FILE__)))
9
+ $:.unshift(File.dirname(__FILE__))
4
10
  require 'twitter/version'
5
11
  require 'twitter/easy_class_maker'
6
12
  require 'twitter/base'
@@ -9,6 +15,8 @@ require 'twitter/search'
9
15
  require 'twitter/status'
10
16
  require 'twitter/direct_message'
11
17
  require 'twitter/rate_limit_status'
18
+ require 'twitter/search_result_info'
19
+ require 'twitter/search_result'
12
20
 
13
21
  module Twitter
14
22
  class Unavailable < StandardError; end
@@ -16,6 +24,9 @@ module Twitter
16
24
  class BadResponse < StandardError; end
17
25
  class UnknownTimeline < ArgumentError; end
18
26
  class RateExceeded < StandardError; end
27
+ class CantFindUsers < ArgumentError; end
28
+ class AlreadyFollowing < StandardError; end
29
+ class CantFollowUser < StandardError; end
19
30
 
20
31
  SourceName = 'twittergem'
21
32
  end
data/spec/base_spec.rb CHANGED
@@ -16,7 +16,11 @@ describe "Twitter::Base" do
16
16
  lambda { @base.timeline(:fakeyoutey) }.should raise_error(Twitter::UnknownTimeline)
17
17
  end
18
18
 
19
- it "should default to friends timeline"
19
+ it "should default to friends timeline" do
20
+ @base.should_receive(:call).with("friends_timeline", {:auth=>true, :args=>{}, :since=>nil})
21
+ @base.should_receive(:statuses)
22
+ @base.timeline
23
+ end
20
24
 
21
25
  it "should be able to retrieve friends timeline" do
22
26
  data = open(File.dirname(__FILE__) + '/fixtures/friends_timeline.xml').read
@@ -65,6 +69,20 @@ describe "Twitter::Base" do
65
69
  timeline.size.should == 29
66
70
  timeline.first.name.should == 'Blaine Cook'
67
71
  end
72
+
73
+ it "should be able to create a friendship" do
74
+ data = open(File.dirname(__FILE__) + '/fixtures/friendship_created.xml').read
75
+ @base.should_receive(:request).and_return(Hpricot::XML(data))
76
+ user = @base.create_friendship('jnunemaker')
77
+ end
78
+
79
+ it "should bomb if friendship already exists" do
80
+ data = open(File.dirname(__FILE__) + '/fixtures/friendship_already_exists.xml').read
81
+ response = Net::HTTPForbidden.new("1.1", '403', '')
82
+ response.stub!(:body).and_return(data)
83
+ @base.should_receive(:response).and_return(response)
84
+ lambda { @base.create_friendship('billymeltdown') }.should raise_error(Twitter::AlreadyFollowing)
85
+ end
68
86
  end
69
87
 
70
88
  it "should be able to get single status" do
@@ -106,4 +124,4 @@ describe "Twitter::Base" do
106
124
  @base.rate_limit_status.remaining_hits.should == 5
107
125
  end
108
126
  end
109
- end
127
+ end
@@ -8,6 +8,10 @@ def say(str)
8
8
  puts str
9
9
  end
10
10
 
11
+ class Tweet < OpenStruct
12
+ attr_accessor :id
13
+ end
14
+
11
15
  describe Twitter::CLI::Helpers do
12
16
  include Twitter::CLI::Helpers
13
17
 
@@ -15,21 +19,31 @@ describe Twitter::CLI::Helpers do
15
19
  before do
16
20
  Configuration.stub!(:[]=).and_return(true)
17
21
  @collection = [
18
- OpenStruct.new(
19
- :text => 'This is my long message that I want to see formatted ooooh so pretty with a few words on each line so it is easy to scan.',
22
+ Tweet.new(
23
+ :id => 1,
24
+ :text => 'This is my long message that I want to see formatted ooooh so pretty with a few words on each line so it is easy to scan.',
20
25
  :created_at => Time.mktime(2008, 5, 1, 10, 15, 00).strftime('%Y-%m-%d %H:%M:%S'),
21
- :user => OpenStruct.new(:screen_name => 'jnunemaker')
26
+ :user => OpenStruct.new(:screen_name => 'jnunemaker')
22
27
  ),
23
- OpenStruct.new(
24
- :text => 'This is my long message that I want to see formatted ooooh so pretty with a.',
28
+ Tweet.new(
29
+ :id => 2,
30
+ :text => 'This is my long message that I want to see formatted ooooh so pretty with a.',
25
31
  :created_at => Time.mktime(2008, 4, 1, 10, 15, 00).strftime('%Y-%m-%d %H:%M:%S'),
26
- :user => OpenStruct.new(:screen_name => 'danielmorrison')
32
+ :user => OpenStruct.new(:screen_name => 'danielmorrison')
27
33
  )
28
34
  ]
29
35
  end
30
36
 
31
37
  specify "should properly format" do
32
- output_tweets(@collection)
38
+ stdout_for {
39
+ output_tweets(@collection)
40
+ }.should match(/with a few words[\w\W]*with a\./)
41
+ end
42
+
43
+ specify 'should format in reverse' do
44
+ stdout_for {
45
+ output_tweets(@collection, :reverse => true)
46
+ }.should match(/with a\.[\w\W]*with a few words/)
33
47
  end
34
48
  end
35
49
  end