t 0.9.5 → 0.9.6
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +5 -3
- data/Rakefile +0 -2
- data/lib/t.rb +15 -0
- data/lib/t/cli.rb +47 -59
- data/lib/t/collectable.rb +12 -8
- data/lib/t/delete.rb +4 -3
- data/lib/t/list.rb +3 -5
- data/lib/t/printable.rb +50 -81
- data/lib/t/search.rb +51 -62
- data/lib/t/stream.rb +15 -22
- data/lib/t/version.rb +1 -1
- data/spec/cli_spec.rb +198 -65
- data/spec/fixtures/rate_limit_status.json +1 -0
- data/spec/fixtures/status.json +1 -1
- data/spec/fixtures/status_no_attributes.json +1 -0
- data/spec/fixtures/status_no_country.json +1 -0
- data/spec/fixtures/status_no_full_name.json +1 -0
- data/spec/fixtures/status_no_locality.json +1 -0
- data/spec/fixtures/status_no_street_address.json +1 -0
- data/spec/helper.rb +1 -0
- data/spec/list_spec.rb +9 -9
- data/spec/rcfile_spec.rb +1 -1
- data/spec/search_spec.rb +319 -49
- data/spec/t_spec.rb +31 -0
- data/t.gemspec +2 -3
- metadata +22 -24
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Twitter CLI [![Build Status](https://secure.travis-ci.org/sferik/t.png?branch=master)][travis] [![Dependency Status](https://gemnasium.com/sferik/t.png?travis)][gemnasium]
|
1
|
+
# Twitter CLI [![Build Status](https://secure.travis-ci.org/sferik/t.png?branch=master)][travis] [![Dependency Status](https://gemnasium.com/sferik/t.png?travis)][gemnasium] [![Click here to make a donation to T](http://www.pledgie.com/campaigns/17330.png)][pledgie]
|
2
2
|
### A command-line power tool for Twitter.
|
3
3
|
|
4
4
|
The CLI takes syntactic cues from the [Twitter SMS commands][sms], however it
|
@@ -6,6 +6,7 @@ offers vastly more commands and capabilities than are available via SMS.
|
|
6
6
|
|
7
7
|
[travis]: http://travis-ci.org/sferik/t
|
8
8
|
[gemnasium]: https://gemnasium.com/sferik/t
|
9
|
+
[pledgie]: http://www.pledgie.com/campaigns/17330
|
9
10
|
[gem]: https://rubygems.org/gems/twitter
|
10
11
|
[sms]: https://support.twitter.com/articles/14020-twitter-sms-command
|
11
12
|
|
@@ -198,10 +199,10 @@ the original code.
|
|
198
199
|
![History](https://github.com/sferik/t/raw/master/screenshots/history.png)
|
199
200
|
|
200
201
|
## Contributing
|
201
|
-
In the spirit of [free software][
|
202
|
+
In the spirit of [free software][free-sw], **everyone** is encouraged to help
|
202
203
|
improve this project.
|
203
204
|
|
204
|
-
[
|
205
|
+
[free-sw]: http://www.fsf.org/licensing/essays/free-sw.html
|
205
206
|
|
206
207
|
Here are some ways *you* can contribute:
|
207
208
|
|
@@ -215,6 +216,7 @@ Here are some ways *you* can contribute:
|
|
215
216
|
* by refactoring code
|
216
217
|
* by fixing [issues][]
|
217
218
|
* by reviewing patches
|
219
|
+
* [financially][pledgie]
|
218
220
|
|
219
221
|
[issues]: https://github.com/sferik/t/issues
|
220
222
|
|
data/Rakefile
CHANGED
data/lib/t.rb
CHANGED
data/lib/t/cli.rb
CHANGED
@@ -7,6 +7,7 @@ require 'csv'
|
|
7
7
|
# 'fastercsv' required on Ruby versions < 1.9
|
8
8
|
require 'fastercsv' unless Array.new.respond_to?(:to_csv)
|
9
9
|
require 'open-uri'
|
10
|
+
require 'retryable'
|
10
11
|
require 't/authorizable'
|
11
12
|
require 't/collectable'
|
12
13
|
require 't/core_ext/string'
|
@@ -41,7 +42,7 @@ module T
|
|
41
42
|
check_unknown_options!
|
42
43
|
|
43
44
|
option "host", :aliases => "-H", :type => :string, :default => DEFAULT_HOST, :desc => "Twitter API server"
|
44
|
-
option "no-color", :aliases => "-N", :type => :boolean, :
|
45
|
+
option "no-color", :aliases => "-N", :type => :boolean, :desc => "Disable colorization in output"
|
45
46
|
option "no-ssl", :aliases => "-U", :type => :boolean, :default => false, :desc => "Disable SSL"
|
46
47
|
option "profile", :aliases => "-P", :type => :string, :default => File.join(File.expand_path("~"), RCFile::FILE_NAME), :desc => "Path to RC file", :banner => "FILE"
|
47
48
|
|
@@ -62,8 +63,8 @@ module T
|
|
62
63
|
end
|
63
64
|
|
64
65
|
desc "authorize", "Allows an application to request user authorization"
|
65
|
-
method_option "consumer-key", :aliases => "-c", :required => true, :desc => "This can be found at https://dev.twitter.com/apps"
|
66
|
-
method_option "consumer-secret", :aliases => "-s", :required => true, :desc => "This can be found at https://dev.twitter.com/apps"
|
66
|
+
method_option "consumer-key", :aliases => "-c", :required => true, :desc => "This can be found at https://dev.twitter.com/apps", :banner => "KEY"
|
67
|
+
method_option "consumer-secret", :aliases => "-s", :required => true, :desc => "This can be found at https://dev.twitter.com/apps", :banner => "SECRET"
|
67
68
|
method_option "display-url", :aliases => "-d", :type => :boolean, :default => false, :desc => "Display the authorization URL instead of attempting to open it."
|
68
69
|
method_option "prompt", :aliases => "-p", :type => :boolean, :default => true
|
69
70
|
def authorize
|
@@ -132,22 +133,15 @@ module T
|
|
132
133
|
end
|
133
134
|
direct_messages.reverse! if options['reverse']
|
134
135
|
if options['csv']
|
135
|
-
say
|
136
|
+
say DIRECT_MESSAGE_HEADINGS.to_csv unless direct_messages.empty?
|
136
137
|
direct_messages.each do |direct_message|
|
137
|
-
say [direct_message.id, direct_message
|
138
|
+
say [direct_message.id, csv_formatted_time(direct_message), direct_message.sender.screen_name, direct_message.text].to_csv
|
138
139
|
end
|
139
140
|
elsif options['long']
|
140
141
|
array = direct_messages.map do |direct_message|
|
141
|
-
|
142
|
-
[direct_message.id, created_at, "@#{direct_message.sender.screen_name}", HTMLEntities.new.decode(direct_message.text).gsub(/\n+/, ' ')]
|
143
|
-
end
|
144
|
-
if STDOUT.tty?
|
145
|
-
headings = ["ID", "Posted at", "Screen name", "Text"]
|
146
|
-
array.unshift(headings) unless direct_messages.empty?
|
147
|
-
print_table(array, :truncate => true)
|
148
|
-
else
|
149
|
-
print_table(array)
|
142
|
+
[direct_message.id, ls_formatted_time(direct_message), "@#{direct_message.sender.screen_name}", HTMLEntities.new.decode(direct_message.text).gsub(/\n+/, ' ')]
|
150
143
|
end
|
144
|
+
print_table_with_headings(array, DIRECT_MESSAGE_HEADINGS)
|
151
145
|
else
|
152
146
|
direct_messages.each do |direct_message|
|
153
147
|
say "#{direct_message.sender.screen_name.rjust(MAX_SCREEN_NAME_SIZE)}: #{direct_message.text.gsub(/\n+/, ' ')} (#{time_ago_in_words(direct_message.created_at)} ago)"
|
@@ -168,22 +162,15 @@ module T
|
|
168
162
|
end
|
169
163
|
direct_messages.reverse! if options['reverse']
|
170
164
|
if options['csv']
|
171
|
-
say
|
165
|
+
say DIRECT_MESSAGE_HEADINGS.to_csv unless direct_messages.empty?
|
172
166
|
direct_messages.each do |direct_message|
|
173
|
-
say [direct_message.id, direct_message
|
167
|
+
say [direct_message.id, csv_formatted_time(direct_message), direct_message.recipient.screen_name, direct_message.text].to_csv
|
174
168
|
end
|
175
169
|
elsif options['long']
|
176
170
|
array = direct_messages.map do |direct_message|
|
177
|
-
|
178
|
-
[direct_message.id, created_at, "@#{direct_message.recipient.screen_name}", HTMLEntities.new.decode(direct_message.text).gsub(/\n+/, ' ')]
|
179
|
-
end
|
180
|
-
if STDOUT.tty?
|
181
|
-
headings = ["ID", "Posted at", "Screen name", "Text"]
|
182
|
-
array.unshift(headings) unless direct_messages.empty?
|
183
|
-
print_table(array, :truncate => true)
|
184
|
-
else
|
185
|
-
print_table(array)
|
171
|
+
[direct_message.id, ls_formatted_time(direct_message), "@#{direct_message.recipient.screen_name}", HTMLEntities.new.decode(direct_message.text).gsub(/\n+/, ' ')]
|
186
172
|
end
|
173
|
+
print_table_with_headings(array, DIRECT_MESSAGE_HEADINGS)
|
187
174
|
else
|
188
175
|
direct_messages.each do |direct_message|
|
189
176
|
say "#{direct_message.recipient.screen_name.rjust(MAX_SCREEN_NAME_SIZE)}: #{direct_message.text.gsub(/\n+/, ' ')} (#{time_ago_in_words(direct_message.created_at)} ago)"
|
@@ -512,6 +499,11 @@ module T
|
|
512
499
|
print_lists(lists)
|
513
500
|
end
|
514
501
|
|
502
|
+
desc "matrix", "Unfortunately, no one can be told what the Matrix is. You have to see it for yourself."
|
503
|
+
def matrix
|
504
|
+
T::Stream.new.matrix
|
505
|
+
end
|
506
|
+
|
515
507
|
desc "mentions", "Returns the #{DEFAULT_NUM_RESULTS} most recent Tweets mentioning you."
|
516
508
|
method_option "csv", :aliases => "-c", :type => :boolean, :default => false, :desc => "Output in CSV format."
|
517
509
|
method_option "long", :aliases => "-l", :type => :boolean, :default => false, :desc => "Output in long format."
|
@@ -537,19 +529,36 @@ module T
|
|
537
529
|
Launchy.open("https://twitter.com/#{user.screen_name}", :dry_run => options['display-url'])
|
538
530
|
elsif options['status']
|
539
531
|
status = client.status(user.to_i, :include_my_retweet => false)
|
540
|
-
Launchy.open("https://twitter.com/#{status.
|
532
|
+
Launchy.open("https://twitter.com/#{status.from_user}/status/#{status.id}", :dry_run => options['display-url'])
|
541
533
|
else
|
542
534
|
Launchy.open("https://twitter.com/#{user.strip_ats}", :dry_run => options['display-url'])
|
543
535
|
end
|
544
536
|
end
|
545
537
|
|
538
|
+
desc "rate_limit", "Returns information related to Twitter API rate limiting."
|
539
|
+
method_option "csv", :aliases => "-c", :type => :boolean, :default => false, :desc => "Output in CSV format."
|
540
|
+
def rate_limit
|
541
|
+
rate_limit_status = client.rate_limit_status
|
542
|
+
if options['csv']
|
543
|
+
say ["Hourly limit", "Remaining hits", "Reset time"].to_csv
|
544
|
+
say [rate_limit_status.hourly_limit, rate_limit_status.remaining_hits, csv_formatted_time(rate_limit_status, :reset_time)].to_csv
|
545
|
+
else
|
546
|
+
array = []
|
547
|
+
array << ["Hourly limit", number_with_delimiter(rate_limit_status.hourly_limit)]
|
548
|
+
array << ["Remaining hits", number_with_delimiter(rate_limit_status.remaining_hits)]
|
549
|
+
array << ["Reset time", ls_formatted_time(rate_limit_status, :reset_time)]
|
550
|
+
print_table(array)
|
551
|
+
end
|
552
|
+
end
|
553
|
+
map %w(ratelimit rl) => :rate_limit
|
554
|
+
|
546
555
|
desc "reply STATUS_ID MESSAGE", "Post your Tweet as a reply directed at another person."
|
547
556
|
method_option "all", :aliases => "-a", :type => "boolean", :default => false, :desc => "Reply to all users mentioned in the Tweet."
|
548
557
|
method_option "location", :aliases => "-l", :type => :boolean, :default => false
|
549
558
|
def reply(status_id, message)
|
550
559
|
status_id = status_id.strip_commas
|
551
560
|
status = client.status(status_id.to_i, :include_my_retweet => false)
|
552
|
-
users = Array(status.
|
561
|
+
users = Array(status.from_user)
|
553
562
|
if options['all']
|
554
563
|
# twitter-text requires $KCODE to be set to UTF8 on Ruby versions < 1.8
|
555
564
|
major, minor, patch = RUBY_VERSION.split('.')
|
@@ -653,18 +662,18 @@ module T
|
|
653
662
|
end
|
654
663
|
if options['csv']
|
655
664
|
say ["ID", "Text", "Screen name", "Posted at", "Location", "Retweets", "Source", "URL"].to_csv
|
656
|
-
say [status.id, HTMLEntities.new.decode(status.text), status.
|
665
|
+
say [status.id, HTMLEntities.new.decode(status.text), status.from_user, csv_formatted_time(status), location, status.retweet_count, strip_tags(status.source), "https://twitter.com/#{status.from_user}/status/#{status.id}"].to_csv
|
657
666
|
else
|
658
667
|
array = []
|
659
668
|
array << ["ID", status.id.to_s]
|
660
669
|
array << ["Text", HTMLEntities.new.decode(status.text).gsub(/\n+/, ' ')]
|
661
|
-
array << ["Screen name", "@#{status.
|
670
|
+
array << ["Screen name", "@#{status.from_user}"]
|
662
671
|
posted_at = status.created_at > 6.months.ago ? status.created_at.strftime("%b %e %H:%M") : status.created_at.strftime("%b %e %Y")
|
663
672
|
array << ["Posted at", posted_at]
|
664
673
|
array << ["Location", location] unless location.nil?
|
665
674
|
array << ["Retweets", number_with_delimiter(status.retweet_count)]
|
666
675
|
array << ["Source", strip_tags(status.source)]
|
667
|
-
array << ["URL", "https://twitter.com/#{status.
|
676
|
+
array << ["URL", "https://twitter.com/#{status.from_user}/status/#{status.id}"]
|
668
677
|
print_table(array)
|
669
678
|
end
|
670
679
|
end
|
@@ -727,13 +736,7 @@ module T
|
|
727
736
|
opts = {}
|
728
737
|
opts.merge!(:exclude => "hashtags") if options['exclude-hashtags']
|
729
738
|
trends = client.trends(woe_id, opts)
|
730
|
-
|
731
|
-
print_in_columns(trends.map(&:name))
|
732
|
-
else
|
733
|
-
trends.each do |trend|
|
734
|
-
say trend.name
|
735
|
-
end
|
736
|
-
end
|
739
|
+
print_attribute(trends, :name)
|
737
740
|
end
|
738
741
|
|
739
742
|
desc "trends_locations", "Returns the locations for which Twitter has trending topic information."
|
@@ -746,7 +749,7 @@ module T
|
|
746
749
|
places = places.sort_by{|places| places.name.downcase} unless options['unsorted']
|
747
750
|
places.reverse! if options['reverse']
|
748
751
|
if options['csv']
|
749
|
-
say
|
752
|
+
say TREND_HEADINGS.to_csv unless places.empty?
|
750
753
|
places.each do |place|
|
751
754
|
say [place.woeid, place.parent_id, place.place_type, place.name, place.country].to_csv
|
752
755
|
end
|
@@ -754,21 +757,9 @@ module T
|
|
754
757
|
array = places.map do |place|
|
755
758
|
[place.woeid, place.parent_id, place.place_type, place.name, place.country]
|
756
759
|
end
|
757
|
-
|
758
|
-
headings = ["WOEID", "Parent ID", "Type", "Name", "Country"]
|
759
|
-
array.unshift(headings) unless places.empty?
|
760
|
-
print_table(array, :truncate => true)
|
761
|
-
else
|
762
|
-
print_table(array)
|
763
|
-
end
|
760
|
+
print_table_with_headings(array, TREND_HEADINGS)
|
764
761
|
else
|
765
|
-
|
766
|
-
print_in_columns(places.map(&:name))
|
767
|
-
else
|
768
|
-
places.each do |place|
|
769
|
-
say place.name
|
770
|
-
end
|
771
|
-
end
|
762
|
+
print_attribute(places, :name)
|
772
763
|
end
|
773
764
|
end
|
774
765
|
map %w(locations trendlocations) => :trend_locations
|
@@ -847,19 +838,16 @@ module T
|
|
847
838
|
user = client.user(user)
|
848
839
|
if options['csv']
|
849
840
|
say ["ID", "Verified", "Name", "Screen name", "Bio", "Location", "Following", "Last update", "Lasted updated at", "Since", "Tweets", "Favorites", "Listed", "Following", "Followers", "URL"].to_csv
|
850
|
-
say [user.id, user.verified?, user.name, user.screen_name, user.description, user.location, user.following?, HTMLEntities.new.decode(user.status.text), user.status
|
841
|
+
say [user.id, user.verified?, user.name, user.screen_name, user.description, user.location, user.following?, HTMLEntities.new.decode(user.status.text), csv_formatted_time(user.status), csv_formatted_time(user), user.statuses_count, user.favourites_count, user.listed_count, user.friends_count, user.followers_count, user.url].to_csv
|
851
842
|
else
|
852
843
|
array = []
|
853
|
-
name_label = user.verified ? "Name (Verified)" : "Name"
|
854
844
|
array << ["ID", user.id.to_s]
|
855
|
-
array << [
|
845
|
+
array << [user.verified ? "Name (Verified)" : "Name", user.name] unless user.name.nil?
|
856
846
|
array << ["Bio", user.description.gsub(/\n+/, ' ')] unless user.description.nil?
|
857
847
|
array << ["Location", user.location] unless user.location.nil?
|
858
|
-
|
859
|
-
array << ["Status", status]
|
848
|
+
array << ["Status", user.following ? "Following" : "Not following"]
|
860
849
|
array << ["Last update", "#{HTMLEntities.new.decode(user.status.text).gsub(/\n+/, ' ')} (#{time_ago_in_words(user.status.created_at)} ago)"] unless user.status.nil?
|
861
|
-
|
862
|
-
array << ["Since", created_at]
|
850
|
+
array << ["Since", ls_formatted_time(user)]
|
863
851
|
array << ["Tweets", number_with_delimiter(user.statuses_count)]
|
864
852
|
array << ["Favorites", number_with_delimiter(user.favourites_count)]
|
865
853
|
array << ["Listed", number_with_delimiter(user.listed_count)]
|
data/lib/t/collectable.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'retryable'
|
2
|
+
|
1
3
|
module T
|
2
4
|
module Collectable
|
3
5
|
|
@@ -8,16 +10,20 @@ module T
|
|
8
10
|
end
|
9
11
|
|
10
12
|
def collect_with_cursor(collection=[], cursor=-1, &block)
|
11
|
-
object =
|
13
|
+
object = retryable(:tries => 3, :on => Twitter::Error::ServerError, :sleep => 0) do
|
14
|
+
yield cursor
|
15
|
+
end
|
12
16
|
collection += object.collection
|
13
|
-
object.last? ? collection : collect_with_cursor(collection, object.next_cursor, &block)
|
17
|
+
object.last? ? collection.flatten : collect_with_cursor(collection, object.next_cursor, &block)
|
14
18
|
end
|
15
19
|
|
16
20
|
def collect_with_max_id(collection=[], max_id=nil, &block)
|
17
|
-
array =
|
18
|
-
|
21
|
+
array = retryable(:tries => 3, :on => Twitter::Error::ServerError, :sleep => 0) do
|
22
|
+
yield max_id
|
23
|
+
end
|
24
|
+
return collection if array.nil?
|
19
25
|
collection += array
|
20
|
-
array.empty? ? collection : collect_with_max_id(collection, array.last.id - 1, &block)
|
26
|
+
array.empty? ? collection.flatten : collect_with_max_id(collection, array.last.id - 1, &block)
|
21
27
|
end
|
22
28
|
|
23
29
|
def collect_with_number(number, key, &block)
|
@@ -28,9 +34,7 @@ module T
|
|
28
34
|
opts[key] = number unless number >= MAX_NUM_RESULTS
|
29
35
|
if number > 0
|
30
36
|
number -= MAX_NUM_RESULTS
|
31
|
-
|
32
|
-
yield opts
|
33
|
-
end
|
37
|
+
yield opts
|
34
38
|
end
|
35
39
|
end.flatten.compact
|
36
40
|
end
|
data/lib/t/delete.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'retryable'
|
1
2
|
require 't/core_ext/enumerable'
|
2
3
|
require 't/core_ext/string'
|
3
4
|
require 't/rcfile'
|
@@ -60,10 +61,10 @@ module T
|
|
60
61
|
status_ids.each do |status_id|
|
61
62
|
unless options['force']
|
62
63
|
status = client.status(status_id.to_i, :include_my_retweet => false, :trim_user => true)
|
63
|
-
return unless yes? "Are you sure you want to remove @#{status.
|
64
|
+
return unless yes? "Are you sure you want to remove @#{status.from_user}'s status: \"#{status.text}\" from your favorites? [y/N]"
|
64
65
|
end
|
65
66
|
status = client.unfavorite(status_id.to_i)
|
66
|
-
say "@#{@rcfile.active_profile[0]} unfavorited @#{status.
|
67
|
+
say "@#{@rcfile.active_profile[0]} unfavorited @#{status.from_user}'s status: \"#{status.text}\""
|
67
68
|
end
|
68
69
|
end
|
69
70
|
map %w(fave favourite) => :favorite
|
@@ -89,7 +90,7 @@ module T
|
|
89
90
|
status_ids.each do |status_id|
|
90
91
|
unless options['force']
|
91
92
|
status = client.status(status_id.to_i, :include_my_retweet => false, :trim_user => true)
|
92
|
-
return unless yes? "Are you sure you want to permanently delete @#{status.
|
93
|
+
return unless yes? "Are you sure you want to permanently delete @#{status.from_user}'s status: \"#{status.text}\"? [y/N]"
|
93
94
|
end
|
94
95
|
status = client.status_destroy(status_id.to_i, :trim_user => true)
|
95
96
|
say "@#{@rcfile.active_profile[0]} deleted the status: \"#{status.text}\""
|
data/lib/t/list.rb
CHANGED
@@ -81,19 +81,17 @@ module T
|
|
81
81
|
list = client.list(owner, list)
|
82
82
|
if options['csv']
|
83
83
|
say ["ID", "Description", "Slug", "Screen name", "Created at", "Members", "Subscribers", "Following", "Mode", "URL"].to_csv
|
84
|
-
say [list.id, list.description, list.slug, list.user.screen_name, list
|
84
|
+
say [list.id, list.description, list.slug, list.user.screen_name, csv_formatted_time(list), list.member_count, list.subscriber_count, list.following?, list.mode, "https://twitter.com#{list.uri}"].to_csv
|
85
85
|
else
|
86
86
|
array = []
|
87
87
|
array << ["ID", list.id.to_s]
|
88
88
|
array << ["Description", list.description] unless list.description.nil?
|
89
89
|
array << ["Slug", list.slug]
|
90
90
|
array << ["Screen name", "@#{list.user.screen_name}"]
|
91
|
-
|
92
|
-
array << ["Created at", created_at]
|
91
|
+
array << ["Created at", ls_formatted_time(list)]
|
93
92
|
array << ["Members", number_with_delimiter(list.member_count)]
|
94
93
|
array << ["Subscribers", number_with_delimiter(list.subscriber_count)]
|
95
|
-
|
96
|
-
array << ["Status", status]
|
94
|
+
array << ["Status", list.following ? "Following" : "Not following"]
|
97
95
|
array << ["Mode", list.mode]
|
98
96
|
array << ["URL", "https://twitter.com#{list.uri}"]
|
99
97
|
print_table(array)
|
data/lib/t/printable.rb
CHANGED
@@ -2,7 +2,6 @@ require 'action_view'
|
|
2
2
|
require 'csv'
|
3
3
|
# 'fastercsv' required on Ruby versions < 1.9
|
4
4
|
require 'fastercsv' unless Array.new.respond_to?(:to_csv)
|
5
|
-
require 'highline'
|
6
5
|
require 'htmlentities'
|
7
6
|
require 'thor/shell/color'
|
8
7
|
require 'time'
|
@@ -10,6 +9,12 @@ require 'time'
|
|
10
9
|
module T
|
11
10
|
module Printable
|
12
11
|
MAX_SCREEN_NAME_SIZE = 20
|
12
|
+
DIRECT_MESSAGE_HEADINGS = ["ID", "Posted at", "Screen name", "Text"]
|
13
|
+
LIST_HEADINGS =["ID", "Created at", "Screen name", "Slug", "Members", "Subscribers", "Mode", "Description"]
|
14
|
+
STATUS_HEADINGS = ["ID", "Posted at", "Screen name", "Text"]
|
15
|
+
TREND_HEADINGS = ["WOEID", "Parent ID", "Type", "Name", "Country"]
|
16
|
+
USER_HEADINGS = ["ID", "Since", "Tweets", "Favorites", "Listed", "Following", "Followers", "Screen name", "Name"]
|
17
|
+
|
13
18
|
include ActionView::Helpers::NumberHelper
|
14
19
|
|
15
20
|
def self.included(base)
|
@@ -17,63 +22,39 @@ module T
|
|
17
22
|
private
|
18
23
|
|
19
24
|
def build_long_list(list)
|
20
|
-
|
21
|
-
list.created_at.strftime("%b %e %H:%M")
|
22
|
-
else
|
23
|
-
list.created_at.strftime("%b %e %Y")
|
24
|
-
end
|
25
|
-
[list.id, created_at, "@#{list.user.screen_name}", list.slug, number_with_delimiter(list.member_count), number_with_delimiter(list.subscriber_count), list.mode, list.description]
|
25
|
+
[list.id, ls_formatted_time(list), "@#{list.user.screen_name}", list.slug, number_with_delimiter(list.member_count), number_with_delimiter(list.subscriber_count), list.mode, list.description]
|
26
26
|
end
|
27
27
|
|
28
28
|
def build_long_status(status)
|
29
|
-
|
30
|
-
Time.parse(status.created_at.to_s).strftime("%b %e %H:%M")
|
31
|
-
else
|
32
|
-
Time.parse(status.created_at.to_s).strftime("%b %e %Y")
|
33
|
-
end
|
34
|
-
[status.id, created_at, "@#{status.user.screen_name}", HTMLEntities.new.decode(status.text).gsub(/\n+/, ' ')]
|
29
|
+
[status.id, ls_formatted_time(status), "@#{status.from_user}", HTMLEntities.new.decode(status.text).gsub(/\n+/, ' ')]
|
35
30
|
end
|
36
31
|
|
37
32
|
def build_long_user(user)
|
38
|
-
|
39
|
-
|
33
|
+
[user.id, ls_formatted_time(user), number_with_delimiter(user.statuses_count), number_with_delimiter(user.favourites_count), number_with_delimiter(user.listed_count), number_with_delimiter(user.friends_count), number_with_delimiter(user.followers_count), "@#{user.screen_name}", user.name]
|
34
|
+
end
|
35
|
+
|
36
|
+
def csv_formatted_time(object, key=:created_at)
|
37
|
+
Time.parse(object.send(key.to_sym).to_s).utc.strftime("%Y-%m-%d %H:%M:%S %z")
|
38
|
+
end
|
39
|
+
|
40
|
+
def ls_formatted_time(object, key=:created_at)
|
41
|
+
if object.send(key.to_sym) > 6.months.ago
|
42
|
+
Time.parse(object.send(key.to_sym).to_s).strftime("%b %e %H:%M")
|
40
43
|
else
|
41
|
-
|
44
|
+
Time.parse(object.send(key.to_sym).to_s).strftime("%b %e %Y")
|
42
45
|
end
|
43
|
-
[user.id, created_at, number_with_delimiter(user.statuses_count), number_with_delimiter(user.favourites_count), number_with_delimiter(user.listed_count), number_with_delimiter(user.friends_count), number_with_delimiter(user.followers_count), "@#{user.screen_name}", user.name]
|
44
46
|
end
|
45
47
|
|
46
48
|
def print_csv_list(list)
|
47
|
-
|
48
|
-
say [list.id, created_at, list.user.screen_name, list.slug, list.member_count, list.subscriber_count, list.mode, list.description].to_csv
|
49
|
+
say [list.id, csv_formatted_time(list), list.user.screen_name, list.slug, list.member_count, list.subscriber_count, list.mode, list.description].to_csv
|
49
50
|
end
|
50
51
|
|
51
52
|
def print_csv_status(status)
|
52
|
-
|
53
|
-
say [status.id, created_at, status.user.screen_name, HTMLEntities.new.decode(status.text)].to_csv
|
53
|
+
say [status.id, csv_formatted_time(status), status.from_user, HTMLEntities.new.decode(status.text)].to_csv
|
54
54
|
end
|
55
55
|
|
56
56
|
def print_csv_user(user)
|
57
|
-
|
58
|
-
say [user.id, created_at, user.statuses_count, user.favourites_count, user.listed_count, user.friends_count, user.followers_count, user.screen_name, user.name].to_csv
|
59
|
-
end
|
60
|
-
|
61
|
-
def print_in_columns(array)
|
62
|
-
cols = HighLine::SystemExtensions.terminal_size[0]
|
63
|
-
width = (array.map{|el| el.to_s.size}.max || 0) + 2
|
64
|
-
array.each_with_index do |value, index|
|
65
|
-
puts if (((index) % (cols / width))).zero? && !index.zero?
|
66
|
-
printf("%-#{width}s", value)
|
67
|
-
end
|
68
|
-
puts
|
69
|
-
end
|
70
|
-
|
71
|
-
def list_headings
|
72
|
-
["ID", "Created at", "Screen name", "Slug", "Members", "Subscribers", "Mode", "Description"]
|
73
|
-
end
|
74
|
-
|
75
|
-
def status_headings
|
76
|
-
["ID", "Posted at", "Screen name", "Text"]
|
57
|
+
say [user.id, csv_formatted_time(user), user.statuses_count, user.favourites_count, user.listed_count, user.friends_count, user.followers_count, user.screen_name, user.name].to_csv
|
77
58
|
end
|
78
59
|
|
79
60
|
def print_lists(lists)
|
@@ -89,7 +70,7 @@ module T
|
|
89
70
|
end
|
90
71
|
lists.reverse! if options['reverse']
|
91
72
|
if options['csv']
|
92
|
-
say
|
73
|
+
say LIST_HEADINGS.to_csv unless lists.empty?
|
93
74
|
lists.each do |list|
|
94
75
|
print_csv_list(list)
|
95
76
|
end
|
@@ -97,28 +78,36 @@ module T
|
|
97
78
|
array = lists.map do |list|
|
98
79
|
build_long_list(list)
|
99
80
|
end
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
81
|
+
print_table_with_headings(array, LIST_HEADINGS)
|
82
|
+
else
|
83
|
+
print_attribute(lists, :full_name)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def print_attribute(array, attribute)
|
88
|
+
if STDOUT.tty?
|
89
|
+
print_in_columns(array.map(&attribute.to_sym))
|
106
90
|
else
|
107
|
-
|
108
|
-
|
109
|
-
else
|
110
|
-
lists.each do |list|
|
111
|
-
say list.full_name
|
112
|
-
end
|
91
|
+
array.each do |element|
|
92
|
+
say element.send(attribute.to_sym)
|
113
93
|
end
|
114
94
|
end
|
115
95
|
end
|
116
96
|
|
97
|
+
def print_table_with_headings(array, headings)
|
98
|
+
if STDOUT.tty?
|
99
|
+
array.unshift(headings) unless array.flatten.empty?
|
100
|
+
print_table(array, :truncate => true)
|
101
|
+
else
|
102
|
+
print_table(array)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
117
106
|
def print_status(status)
|
118
107
|
if STDOUT.tty? && !options['no-color']
|
119
|
-
say(" @#{status.
|
108
|
+
say(" @#{status.from_user}", [:bold, :yellow])
|
120
109
|
else
|
121
|
-
say(" @#{status.
|
110
|
+
say(" @#{status.from_user}")
|
122
111
|
end
|
123
112
|
print_wrapped(HTMLEntities.new.decode(status.text), :indent => 3)
|
124
113
|
say
|
@@ -127,7 +116,7 @@ module T
|
|
127
116
|
def print_statuses(statuses)
|
128
117
|
statuses.reverse! if options['reverse'] || options['stream']
|
129
118
|
if options['csv']
|
130
|
-
say
|
119
|
+
say STATUS_HEADINGS.to_csv unless statuses.empty?
|
131
120
|
statuses.each do |status|
|
132
121
|
print_csv_status(status)
|
133
122
|
end
|
@@ -135,12 +124,7 @@ module T
|
|
135
124
|
array = statuses.map do |status|
|
136
125
|
build_long_status(status)
|
137
126
|
end
|
138
|
-
|
139
|
-
array.unshift(status_headings) unless statuses.empty?
|
140
|
-
print_table(array, :truncate => true)
|
141
|
-
else
|
142
|
-
print_table(array)
|
143
|
-
end
|
127
|
+
print_table_with_headings(array, STATUS_HEADINGS)
|
144
128
|
else
|
145
129
|
statuses.each do |status|
|
146
130
|
print_status(status)
|
@@ -148,10 +132,6 @@ module T
|
|
148
132
|
end
|
149
133
|
end
|
150
134
|
|
151
|
-
def user_headings
|
152
|
-
["ID", "Since", "Tweets", "Favorites", "Listed", "Following", "Followers", "Screen name", "Name"]
|
153
|
-
end
|
154
|
-
|
155
135
|
def print_users(users)
|
156
136
|
users = users.sort_by{|user| user.screen_name.downcase} unless options['unsorted']
|
157
137
|
if options['posted']
|
@@ -169,7 +149,7 @@ module T
|
|
169
149
|
end
|
170
150
|
users.reverse! if options['reverse']
|
171
151
|
if options['csv']
|
172
|
-
say
|
152
|
+
say USER_HEADINGS.to_csv unless users.empty?
|
173
153
|
users.each do |user|
|
174
154
|
print_csv_user(user)
|
175
155
|
end
|
@@ -177,20 +157,9 @@ module T
|
|
177
157
|
array = users.map do |user|
|
178
158
|
build_long_user(user)
|
179
159
|
end
|
180
|
-
|
181
|
-
array.unshift(user_headings) unless users.empty?
|
182
|
-
print_table(array, :truncate => true)
|
183
|
-
else
|
184
|
-
print_table(array)
|
185
|
-
end
|
160
|
+
print_table_with_headings(array, USER_HEADINGS)
|
186
161
|
else
|
187
|
-
|
188
|
-
print_in_columns(users.map{|user| "@#{user.screen_name}"})
|
189
|
-
else
|
190
|
-
users.each do |user|
|
191
|
-
say "@#{user.screen_name}"
|
192
|
-
end
|
193
|
-
end
|
162
|
+
print_attribute(users, :screen_name)
|
194
163
|
end
|
195
164
|
end
|
196
165
|
|