t 0.9.9 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/t/rcfile.rb CHANGED
@@ -1,95 +1,101 @@
1
1
  require 'singleton'
2
2
 
3
- class RCFile
4
- FILE_NAME = '.trc'
5
- attr_reader :path
3
+ module T
4
+ class RCFile
5
+ FILE_NAME = '.trc'
6
+ attr_reader :path
6
7
 
7
- include Singleton
8
+ include Singleton
8
9
 
9
- def initialize
10
- @path = File.join(File.expand_path("~"), FILE_NAME)
11
- @data = load
12
- end
10
+ def initialize
11
+ @path = File.join(File.expand_path("~"), FILE_NAME)
12
+ @data = load
13
+ end
13
14
 
14
- def [](username)
15
- profiles[username]
16
- end
15
+ def [](username)
16
+ profiles[username]
17
+ end
17
18
 
18
- def []=(username, profile)
19
- profiles[username] ||= {}
20
- profiles[username].merge!(profile)
21
- write
22
- end
19
+ def []=(username, profile)
20
+ profiles[username] ||= {}
21
+ profiles[username].merge!(profile)
22
+ write
23
+ end
23
24
 
24
- def configuration
25
- @data['configuration']
26
- end
25
+ def configuration
26
+ @data['configuration']
27
+ end
27
28
 
28
- def active_consumer_key
29
- profiles[active_profile[0]][active_profile[1]]['consumer_key'] if active_profile && profiles[active_profile[0]] && profiles[active_profile[0]][active_profile[1]]
30
- end
29
+ def active_consumer_key
30
+ profiles[active_profile[0]][active_profile[1]]['consumer_key'] if active_profile?
31
+ end
31
32
 
32
- def active_consumer_secret
33
- profiles[active_profile[0]][active_profile[1]]['consumer_secret'] if active_profile && profiles[active_profile[0]] && profiles[active_profile[0]][active_profile[1]]
34
- end
33
+ def active_consumer_secret
34
+ profiles[active_profile[0]][active_profile[1]]['consumer_secret'] if active_profile?
35
+ end
35
36
 
36
- def active_profile
37
- configuration['default_profile']
38
- end
37
+ def active_profile
38
+ configuration['default_profile']
39
+ end
39
40
 
40
- def active_profile=(profile)
41
- configuration['default_profile'] = [profile['username'], profile['consumer_key']]
42
- write
43
- end
41
+ def active_profile=(profile)
42
+ configuration['default_profile'] = [profile['username'], profile['consumer_key']]
43
+ write
44
+ end
44
45
 
45
- def active_secret
46
- profiles[active_profile[0]][active_profile[1]]['secret'] if active_profile && profiles[active_profile[0]] && profiles[active_profile[0]][active_profile[1]]
47
- end
46
+ def active_secret
47
+ profiles[active_profile[0]][active_profile[1]]['secret'] if active_profile?
48
+ end
48
49
 
49
- def active_token
50
- profiles[active_profile[0]][active_profile[1]]['token'] if active_profile && profiles[active_profile[0]] && profiles[active_profile[0]][active_profile[1]]
51
- end
50
+ def active_token
51
+ profiles[active_profile[0]][active_profile[1]]['token'] if active_profile?
52
+ end
52
53
 
53
- def delete
54
- File.delete(@path) if File.exist?(@path)
55
- end
54
+ def delete
55
+ File.delete(@path) if File.exist?(@path)
56
+ end
56
57
 
57
- def empty?
58
- @data == default_structure
59
- end
58
+ def empty?
59
+ @data == default_structure
60
+ end
60
61
 
61
- def load
62
- require 'yaml'
63
- YAML.load_file(@path)
64
- rescue Errno::ENOENT
65
- default_structure
66
- end
62
+ def load
63
+ require 'yaml'
64
+ YAML.load_file(@path)
65
+ rescue Errno::ENOENT
66
+ default_structure
67
+ end
67
68
 
68
- def path=(path)
69
- @path = path
70
- @data = load
71
- @path
72
- end
69
+ def path=(path)
70
+ @path = path
71
+ @data = load
72
+ @path
73
+ end
73
74
 
74
- def profiles
75
- @data['profiles']
76
- end
75
+ def profiles
76
+ @data['profiles']
77
+ end
77
78
 
78
- def reset
79
- self.send(:initialize)
80
- end
79
+ def reset
80
+ self.send(:initialize)
81
+ end
81
82
 
82
- private
83
+ private
83
84
 
84
- def default_structure
85
- {'configuration' => {}, 'profiles' => {}}
86
- end
85
+ def active_profile?
86
+ active_profile && profiles[active_profile[0]] && profiles[active_profile[0]][active_profile[1]]
87
+ end
87
88
 
88
- def write
89
- require 'yaml'
90
- File.open(@path, File::RDWR|File::TRUNC|File::CREAT, 0600) do |rcfile|
91
- rcfile.write @data.to_yaml
89
+ def default_structure
90
+ {'configuration' => {}, 'profiles' => {}}
91
+ end
92
+
93
+ def write
94
+ require 'yaml'
95
+ File.open(@path, File::RDWR|File::TRUNC|File::CREAT, 0600) do |rcfile|
96
+ rcfile.write @data.to_yaml
97
+ end
92
98
  end
93
- end
94
99
 
100
+ end
95
101
  end
data/lib/t/search.rb CHANGED
@@ -1,26 +1,26 @@
1
1
  require 'thor'
2
2
  require 'twitter'
3
+ require 't/collectable'
4
+ require 't/printable'
5
+ require 't/rcfile'
6
+ require 't/requestable'
7
+ require 't/utils'
3
8
 
4
9
  module T
5
- autoload :Collectable, 't/collectable'
6
- autoload :Printable, 't/printable'
7
- autoload :RCFile, 't/rcfile'
8
- autoload :Requestable, 't/requestable'
9
10
  class Search < Thor
10
11
  include T::Collectable
11
12
  include T::Printable
12
13
  include T::Requestable
14
+ include T::Utils
13
15
 
14
16
  DEFAULT_NUM_RESULTS = 20
15
17
  MAX_NUM_RESULTS = 200
16
- MAX_SCREEN_NAME_SIZE = 20
17
- MAX_USERS_PER_REQUEST = 20
18
18
 
19
19
  check_unknown_options!
20
20
 
21
21
  def initialize(*)
22
+ @rcfile = T::RCFile.instance
22
23
  super
23
- @rcfile = RCFile.instance
24
24
  end
25
25
 
26
26
  desc "all QUERY", "Returns the #{DEFAULT_NUM_RESULTS} most recent Tweets that match the specified query."
@@ -30,8 +30,9 @@ module T
30
30
  def all(query)
31
31
  rpp = options['number'] || DEFAULT_NUM_RESULTS
32
32
  statuses = collect_with_rpp(rpp) do |opts|
33
- client.search(query, opts)
33
+ client.search(query, opts).results
34
34
  end
35
+ statuses.reverse! if options['reverse']
35
36
  require 'htmlentities'
36
37
  if options['csv']
37
38
  require 'csv'
@@ -49,19 +50,35 @@ module T
49
50
  else
50
51
  say unless statuses.empty?
51
52
  statuses.each do |status|
52
- print_status(status)
53
+ print_message(status.from_user, status.full_text)
53
54
  end
54
55
  end
55
56
  end
56
57
 
57
- desc "favorites QUERY", "Returns Tweets you've favorited that match the specified query."
58
+ desc "favorites [USER] QUERY", "Returns Tweets you've favorited that match the specified query."
58
59
  method_option "csv", :aliases => "-c", :type => :boolean, :default => false, :desc => "Output in CSV format."
60
+ method_option "id", :aliases => "-i", :type => "boolean", :default => false, :desc => "Specify user via ID instead of screen name."
59
61
  method_option "long", :aliases => "-l", :type => :boolean, :default => false, :desc => "Output in long format."
60
- def favorites(query)
62
+ def favorites(*args)
61
63
  opts = {:count => MAX_NUM_RESULTS}
62
- statuses = collect_with_max_id do |max_id|
63
- opts[:max_id] = max_id unless max_id.nil?
64
- client.favorites(opts)
64
+ query = args.pop
65
+ user = args.pop
66
+ if user
67
+ require 't/core_ext/string'
68
+ user = if options['id']
69
+ user.to_i
70
+ else
71
+ user.strip_ats
72
+ end
73
+ statuses = collect_with_max_id do |max_id|
74
+ opts[:max_id] = max_id unless max_id.nil?
75
+ client.favorites(user, opts)
76
+ end
77
+ else
78
+ statuses = collect_with_max_id do |max_id|
79
+ opts[:max_id] = max_id unless max_id.nil?
80
+ client.favorites(opts)
81
+ end
65
82
  end
66
83
  statuses = statuses.select do |status|
67
84
  /#{query}/i.match(status.full_text)
@@ -75,18 +92,7 @@ module T
75
92
  method_option "id", :aliases => "-i", :type => "boolean", :default => false, :desc => "Specify user via ID instead of screen name."
76
93
  method_option "long", :aliases => "-l", :type => :boolean, :default => false, :desc => "Output in long format."
77
94
  def list(list, query)
78
- owner, list = list.split('/')
79
- if list.nil?
80
- list = owner
81
- owner = @rcfile.active_profile[0]
82
- else
83
- require 't/core_ext/string'
84
- owner = if options['id']
85
- owner.to_i
86
- else
87
- owner.strip_ats
88
- end
89
- end
95
+ owner, list = extract_owner(list, options)
90
96
  opts = {:count => MAX_NUM_RESULTS}
91
97
  statuses = collect_with_max_id do |max_id|
92
98
  opts[:max_id] = max_id unless max_id.nil?
@@ -114,14 +120,30 @@ module T
114
120
  end
115
121
  map %w(replies) => :mentions
116
122
 
117
- desc "retweets QUERY", "Returns Tweets you've retweeted that match the specified query."
123
+ desc "retweets [USER] QUERY", "Returns Tweets you've retweeted that match the specified query."
118
124
  method_option "csv", :aliases => "-c", :type => :boolean, :default => false, :desc => "Output in CSV format."
125
+ method_option "id", :aliases => "-i", :type => "boolean", :default => false, :desc => "Specify user via ID instead of screen name."
119
126
  method_option "long", :aliases => "-l", :type => :boolean, :default => false, :desc => "Output in long format."
120
- def retweets(query)
127
+ def retweets(*args)
121
128
  opts = {:count => MAX_NUM_RESULTS}
122
- statuses = collect_with_max_id do |max_id|
123
- opts[:max_id] = max_id unless max_id.nil?
124
- client.retweeted_by(opts)
129
+ query = args.pop
130
+ user = args.pop
131
+ if user
132
+ require 't/core_ext/string'
133
+ user = if options['id']
134
+ user.to_i
135
+ else
136
+ user.strip_ats
137
+ end
138
+ statuses = collect_with_max_id do |max_id|
139
+ opts[:max_id] = max_id unless max_id.nil?
140
+ client.retweeted_by_user(user, opts)
141
+ end
142
+ else
143
+ statuses = collect_with_max_id do |max_id|
144
+ opts[:max_id] = max_id unless max_id.nil?
145
+ client.retweeted_by_me(opts)
146
+ end
125
147
  end
126
148
  statuses = statuses.select do |status|
127
149
  /#{query}/i.match(status.full_text)
@@ -164,23 +186,14 @@ module T
164
186
 
165
187
  desc "users QUERY", "Returns users that match the specified query."
166
188
  method_option "csv", :aliases => "-c", :type => :boolean, :default => false, :desc => "Output in CSV format."
167
- method_option "favorites", :aliases => "-v", :type => :boolean, :default => false, :desc => "Sort by number of favorites."
168
- method_option "followers", :aliases => "-f", :type => :boolean, :default => false, :desc => "Sort by number of followers."
169
- method_option "friends", :aliases => "-e", :type => :boolean, :default => false, :desc => "Sort by number of friends."
170
- method_option "listed", :aliases => "-d", :type => :boolean, :default => false, :desc => "Sort by number of list memberships."
171
189
  method_option "long", :aliases => "-l", :type => :boolean, :default => false, :desc => "Output in long format."
172
- method_option "posted", :aliases => "-p", :type => :boolean, :default => false, :desc => "Sort by the time when Twitter account was posted."
173
190
  method_option "reverse", :aliases => "-r", :type => :boolean, :default => false, :desc => "Reverse the order of the sort."
174
- method_option "tweets", :aliases => "-t", :type => :boolean, :default => false, :desc => "Sort by number of Tweets."
191
+ method_option "sort", :aliases => "-s", :type => :string, :enum => %w(favorites followers friends listed screen_name since tweets tweeted), :default => "screen_name", :desc => "Specify the order of the results.", :banner => "ORDER"
175
192
  method_option "unsorted", :aliases => "-u", :type => :boolean, :default => false, :desc => "Output is not sorted."
176
193
  def users(query)
177
- require 't/core_ext/enumerable'
178
- require 'retryable'
179
- users = 1.upto(50).threaded_map do |page|
180
- retryable(:tries => 3, :on => Twitter::Error::ServerError, :sleep => 0) do
181
- client.user_search(query, :page => page, :per_page => MAX_USERS_PER_REQUEST)
182
- end
183
- end.flatten
194
+ users = collect_with_page do |page|
195
+ client.user_search(query, :page => page)
196
+ end
184
197
  print_users(users)
185
198
  end
186
199
 
data/lib/t/set.rb CHANGED
@@ -1,16 +1,16 @@
1
1
  require 'thor'
2
+ require 't/rcfile'
3
+ require 't/requestable'
2
4
 
3
5
  module T
4
- autoload :RCFile, 't/rcfile'
5
- autoload :Requestable, 't/requestable'
6
6
  class Set < Thor
7
7
  include T::Requestable
8
8
 
9
9
  check_unknown_options!
10
10
 
11
11
  def initialize(*)
12
+ @rcfile = T::RCFile.instance
12
13
  super
13
- @rcfile = RCFile.instance
14
14
  end
15
15
 
16
16
  desc "active SCREEN_NAME [CONSUMER_KEY]", "Set your active account."
data/lib/t/stream.rb CHANGED
@@ -1,10 +1,8 @@
1
1
  require 'thor'
2
+ require 't/printable'
3
+ require 't/rcfile'
2
4
 
3
5
  module T
4
- autoload :CLI, 't/cli'
5
- autoload :Printable, 't/printable'
6
- autoload :RCFile, 't/rcfile'
7
- autoload :Search, 't/search'
8
6
  class Stream < Thor
9
7
  include T::Printable
10
8
 
@@ -16,8 +14,8 @@ module T
16
14
  ]
17
15
 
18
16
  def initialize(*)
17
+ @rcfile = T::RCFile.instance
19
18
  super
20
- @rcfile = RCFile.instance
21
19
  end
22
20
 
23
21
  desc "all", "Stream a random sample of all Tweets (Control-C to stop)"
@@ -46,7 +44,7 @@ module T
46
44
  end
47
45
  print_table([array], :truncate => STDOUT.tty?)
48
46
  else
49
- print_status(status)
47
+ print_message(status.user.screen_name, status.text)
50
48
  end
51
49
  end
52
50
  client.sample
@@ -67,6 +65,7 @@ module T
67
65
  def search(keyword, *keywords)
68
66
  keywords.unshift(keyword)
69
67
  require 'tweetstream'
68
+ require 't/search'
70
69
  client.on_inited do
71
70
  search = T::Search.new
72
71
  search.options = search.options.merge(options)
@@ -83,7 +82,7 @@ module T
83
82
  end
84
83
  print_table([array], :truncate => STDOUT.tty?)
85
84
  else
86
- print_status(status)
85
+ print_message(status.user.screen_name, status.text)
87
86
  end
88
87
  end
89
88
  client.track(keywords)
@@ -94,6 +93,7 @@ module T
94
93
  method_option "long", :aliases => "-l", :type => :boolean, :default => false, :desc => "Output in long format."
95
94
  def timeline
96
95
  require 'tweetstream'
96
+ require 't/cli'
97
97
  client.on_inited do
98
98
  cli = T::CLI.new
99
99
  cli.options = cli.options.merge(options)
@@ -110,7 +110,7 @@ module T
110
110
  end
111
111
  print_table([array], :truncate => STDOUT.tty?)
112
112
  else
113
- print_status(status)
113
+ print_message(status.user.screen_name, status.text)
114
114
  end
115
115
  end
116
116
  client.userstream
@@ -144,7 +144,7 @@ module T
144
144
  end
145
145
  print_table([array], :truncate => STDOUT.tty?)
146
146
  else
147
- print_status(status)
147
+ print_message(status.user.screen_name, status.text)
148
148
  end
149
149
  end
150
150
  client.follow(user_ids)
@@ -1,5 +1,5 @@
1
1
  module T
2
- module FormatHelpers
2
+ module Utils
3
3
  private
4
4
 
5
5
  # https://github.com/rails/rails/blob/bd8a970/actionpack/lib/action_view/helpers/date_helper.rb
@@ -47,16 +47,53 @@ module T
47
47
  alias :time_ago_in_words :distance_of_time_in_words
48
48
  alias :time_from_now_in_words :distance_of_time_in_words
49
49
 
50
+ def fetch_users(users, options, &block)
51
+ format_users!(users, options)
52
+ require 'retryable'
53
+ users = retryable(:tries => 3, :on => Twitter::Error::ServerError, :sleep => 0) do
54
+ yield users
55
+ end
56
+ [users, users.length]
57
+ end
58
+
59
+ def format_users!(users, options)
60
+ require 't/core_ext/string'
61
+ if options['id']
62
+ users.map!(&:to_i)
63
+ else
64
+ users.map!(&:strip_ats)
65
+ end
66
+ end
67
+
68
+ def extract_owner(list, options)
69
+ owner, list = list.split('/')
70
+ if list.nil?
71
+ list = owner
72
+ owner = @rcfile.active_profile[0]
73
+ else
74
+ require 't/core_ext/string'
75
+ owner = if options['id']
76
+ owner.to_i
77
+ else
78
+ owner.strip_ats
79
+ end
80
+ end
81
+ [owner, list]
82
+ end
83
+
50
84
  def strip_tags(html)
51
85
  html.gsub(/<.+?>/, '')
52
86
  end
53
87
 
54
88
  def number_with_delimiter(number, delimiter=",")
55
89
  digits = number.to_s.split(//)
56
- require 'active_support/core_ext/array/grouping'
57
- groups = digits.reverse.in_groups_of(3).map{|g| g.join('')}
90
+ groups = digits.reverse.each_slice(3).map{|g| g.join('')}
58
91
  groups.join(delimiter).reverse
59
92
  end
60
93
 
94
+ def pluralize(count, singular, plural=nil)
95
+ "#{count || 0} " + ((count == 1 || count =~ /^1(\.0+)?$/) ? singular : (plural || "#{singular}s"))
96
+ end
97
+
61
98
  end
62
99
  end