twterm 1.0.0
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.
- checksums.yaml +7 -0
- data/.gitignore +88 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +10 -0
- data/Rakefile +1 -0
- data/bin/twterm +5 -0
- data/lib/extentions.rb +51 -0
- data/lib/twterm/app.rb +45 -0
- data/lib/twterm/auth.rb +23 -0
- data/lib/twterm/client.rb +214 -0
- data/lib/twterm/color_manager.rb +50 -0
- data/lib/twterm/config.rb +37 -0
- data/lib/twterm/list.rb +20 -0
- data/lib/twterm/notification/base.rb +24 -0
- data/lib/twterm/notification/error.rb +19 -0
- data/lib/twterm/notification/message.rb +19 -0
- data/lib/twterm/notifier.rb +68 -0
- data/lib/twterm/screen.rb +53 -0
- data/lib/twterm/status.rb +102 -0
- data/lib/twterm/tab/auto_reloadable.rb +14 -0
- data/lib/twterm/tab/base.rb +41 -0
- data/lib/twterm/tab/conversation_tab.rb +31 -0
- data/lib/twterm/tab/exceptions.rb +6 -0
- data/lib/twterm/tab/favorites.rb +7 -0
- data/lib/twterm/tab/list_tab.rb +33 -0
- data/lib/twterm/tab/mentions_tab.rb +36 -0
- data/lib/twterm/tab/new/list.rb +88 -0
- data/lib/twterm/tab/new/search.rb +52 -0
- data/lib/twterm/tab/new/start.rb +67 -0
- data/lib/twterm/tab/scrollable.rb +130 -0
- data/lib/twterm/tab/search_tab.rb +29 -0
- data/lib/twterm/tab/statuses_tab.rb +235 -0
- data/lib/twterm/tab/timeline_tab.rb +33 -0
- data/lib/twterm/tab/user_tab.rb +29 -0
- data/lib/twterm/tab_manager.rb +111 -0
- data/lib/twterm/tweetbox.rb +53 -0
- data/lib/twterm/user.rb +40 -0
- data/lib/twterm/user_window.rb +71 -0
- data/lib/twterm/version.rb +3 -0
- data/lib/twterm.rb +50 -0
- data/twterm.gemspec +28 -0
- metadata +184 -0
@@ -0,0 +1,130 @@
|
|
1
|
+
module Twterm
|
2
|
+
module Tab
|
3
|
+
module Scrollable
|
4
|
+
include Base
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
super
|
8
|
+
|
9
|
+
@scrollable_index = 0
|
10
|
+
@scrollable_count = 0
|
11
|
+
@scrollable_offset = 0
|
12
|
+
@scrollable_last = 0
|
13
|
+
@scrollable_scrollbar_length = 0
|
14
|
+
end
|
15
|
+
|
16
|
+
def respond_to_key(key)
|
17
|
+
case key
|
18
|
+
when 'g'
|
19
|
+
move_to_top
|
20
|
+
when 'G'
|
21
|
+
move_to_bottom
|
22
|
+
when 'j', 14, Key::DOWN
|
23
|
+
move_down
|
24
|
+
when 'k', 16, Key::UP
|
25
|
+
move_up
|
26
|
+
when 'd', 4
|
27
|
+
10.times { move_down }
|
28
|
+
when 'u', 21
|
29
|
+
10.times { move_up }
|
30
|
+
else
|
31
|
+
return false
|
32
|
+
end
|
33
|
+
true
|
34
|
+
end
|
35
|
+
|
36
|
+
def index
|
37
|
+
@scrollable_index
|
38
|
+
end
|
39
|
+
|
40
|
+
def count
|
41
|
+
@scrollable_count
|
42
|
+
end
|
43
|
+
|
44
|
+
def offset
|
45
|
+
@scrollable_offset
|
46
|
+
end
|
47
|
+
|
48
|
+
def last
|
49
|
+
@scrollable_last
|
50
|
+
end
|
51
|
+
|
52
|
+
def item_prepended
|
53
|
+
@scrollable_index += 1
|
54
|
+
@scrollable_offset += 1
|
55
|
+
update_scrollbar_length
|
56
|
+
end
|
57
|
+
|
58
|
+
def item_appended
|
59
|
+
@scrollable_index -= 1
|
60
|
+
@scrollable_offset -= 1 if @scrollable_offset > 0
|
61
|
+
update_scrollbar_length
|
62
|
+
end
|
63
|
+
|
64
|
+
def move_up
|
65
|
+
return if count == 0 || index == 0
|
66
|
+
|
67
|
+
@scrollable_index = [index - 1, 0].max
|
68
|
+
@scrollable_offset = [offset - 1, 0].max if index - 4 < offset
|
69
|
+
refresh
|
70
|
+
end
|
71
|
+
|
72
|
+
def move_down
|
73
|
+
return if count == 0 || index == count - 1
|
74
|
+
|
75
|
+
@scrollable_index = [index + 1, count - 1].min
|
76
|
+
@scrollable_offset = [
|
77
|
+
offset + 1,
|
78
|
+
count - 1,
|
79
|
+
count - offset_from_bottom
|
80
|
+
].min if index > last - 4
|
81
|
+
|
82
|
+
refresh
|
83
|
+
end
|
84
|
+
|
85
|
+
def move_to_top
|
86
|
+
return if count == 0 || index == 0
|
87
|
+
|
88
|
+
@scrollable_index = 0
|
89
|
+
@scrollable_offset = 0
|
90
|
+
refresh
|
91
|
+
end
|
92
|
+
|
93
|
+
def move_to_bottom
|
94
|
+
return if count == 0 || index == count - 1
|
95
|
+
|
96
|
+
@scrollable_index = count - 1
|
97
|
+
@scrollable_offset = count - 1 - offset_from_bottom
|
98
|
+
refresh
|
99
|
+
end
|
100
|
+
|
101
|
+
def offset_from_bottom
|
102
|
+
0
|
103
|
+
end
|
104
|
+
|
105
|
+
def update_scrollbar_length
|
106
|
+
@scrollable_scrollbar_length =
|
107
|
+
if count == 0
|
108
|
+
0
|
109
|
+
else
|
110
|
+
height = @window.maxy
|
111
|
+
[height * (last - index + 1) / count, 1].max
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def draw_scroll_bar
|
116
|
+
return if count == 0
|
117
|
+
|
118
|
+
height = @window.maxy
|
119
|
+
top = height * index / count
|
120
|
+
|
121
|
+
@window.with_color(:black, :white) do
|
122
|
+
@scrollable_scrollbar_length.times do |i|
|
123
|
+
@window.setpos(top + i, @window.maxx - 1)
|
124
|
+
@window.addch(' ')
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Twterm
|
2
|
+
module Tab
|
3
|
+
class SearchTab
|
4
|
+
include StatusesTab
|
5
|
+
|
6
|
+
attr_reader :query
|
7
|
+
|
8
|
+
def initialize(query)
|
9
|
+
super()
|
10
|
+
|
11
|
+
@query = query
|
12
|
+
@title = "\"#{@query}\""
|
13
|
+
|
14
|
+
fetch { move_to_top }
|
15
|
+
end
|
16
|
+
|
17
|
+
def fetch
|
18
|
+
Client.current.search(@query) do |statuses|
|
19
|
+
statuses.reverse.each { |status| prepend(status) }
|
20
|
+
yield if block_given?
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def ==(other)
|
25
|
+
other.is_a?(self.class) && query == other.query
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,235 @@
|
|
1
|
+
module Twterm
|
2
|
+
module Tab
|
3
|
+
module StatusesTab
|
4
|
+
include Base
|
5
|
+
include Scrollable
|
6
|
+
include AutoReloadable
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
super
|
10
|
+
|
11
|
+
@statuses = []
|
12
|
+
end
|
13
|
+
|
14
|
+
def prepend(status)
|
15
|
+
fail unless status.is_a? Status
|
16
|
+
|
17
|
+
return if @statuses.any? { |s| s.id == status.id }
|
18
|
+
|
19
|
+
@statuses << status
|
20
|
+
status.split(@window.maxx - 4)
|
21
|
+
item_prepended
|
22
|
+
refresh
|
23
|
+
end
|
24
|
+
|
25
|
+
def append(status)
|
26
|
+
fail ArgumentError, 'argument must be an instance of Status class' unless status.is_a? Status
|
27
|
+
|
28
|
+
return if @statuses.any? { |s| s == status }
|
29
|
+
|
30
|
+
@statuses.unshift(status)
|
31
|
+
status.split(@window.maxx - 4)
|
32
|
+
item_appended
|
33
|
+
refresh
|
34
|
+
end
|
35
|
+
|
36
|
+
def reply
|
37
|
+
return if highlighted_status.nil?
|
38
|
+
Tweetbox.instance.compose(highlighted_status)
|
39
|
+
end
|
40
|
+
|
41
|
+
def favorite
|
42
|
+
return if highlighted_status.nil?
|
43
|
+
if highlighted_status.favorited?
|
44
|
+
Client.current.unfavorite(highlighted_status) do
|
45
|
+
refresh
|
46
|
+
end
|
47
|
+
else
|
48
|
+
Client.current.favorite(highlighted_status) do
|
49
|
+
refresh
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def retweet
|
55
|
+
return if highlighted_status.nil?
|
56
|
+
Client.current.retweet(highlighted_status) do
|
57
|
+
refresh
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def delete_status(status_id)
|
62
|
+
detector = -> (status) { status.id == status_id }
|
63
|
+
@statuses.delete_if(&detector)
|
64
|
+
refresh
|
65
|
+
end
|
66
|
+
|
67
|
+
def show_user
|
68
|
+
return if highlighted_status.nil?
|
69
|
+
user = highlighted_status.user
|
70
|
+
user_tab = Tab::UserTab.new(user)
|
71
|
+
TabManager.instance.add_and_show(user_tab)
|
72
|
+
end
|
73
|
+
|
74
|
+
def open_link
|
75
|
+
return if highlighted_status.nil?
|
76
|
+
status = highlighted_status
|
77
|
+
urls = status.urls.map(&:expanded_url) + status.media.map(&:expanded_url)
|
78
|
+
urls.each(&Launchy.method(:open))
|
79
|
+
end
|
80
|
+
|
81
|
+
def show_conversation
|
82
|
+
return if highlighted_status.nil?
|
83
|
+
tab = Tab::ConversationTab.new(highlighted_status)
|
84
|
+
TabManager.instance.add_and_show(tab)
|
85
|
+
end
|
86
|
+
|
87
|
+
def fetch
|
88
|
+
fail NotImplementedError, 'fetch method must be implemented'
|
89
|
+
end
|
90
|
+
|
91
|
+
def update
|
92
|
+
current_line = 0
|
93
|
+
|
94
|
+
@window.clear
|
95
|
+
|
96
|
+
@statuses.reverse.drop(offset).each.with_index(offset) do |status, i|
|
97
|
+
formatted_lines = status.split(@window.maxx - 4).count
|
98
|
+
if current_line + formatted_lines + 2 > @window.maxy
|
99
|
+
@scrollable_last = i
|
100
|
+
break
|
101
|
+
end
|
102
|
+
|
103
|
+
posy = current_line
|
104
|
+
|
105
|
+
if index == i
|
106
|
+
@window.with_color(:black, :magenta) do
|
107
|
+
(formatted_lines + 1).times do |j|
|
108
|
+
@window.setpos(posy + j, 0)
|
109
|
+
@window.addch(' ')
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
@window.setpos(current_line, 2)
|
115
|
+
|
116
|
+
@window.bold do
|
117
|
+
@window.with_color(status.user.color) do
|
118
|
+
@window.addstr(status.user.name)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
@window.addstr(" (@#{status.user.screen_name}) [#{status.date}] ")
|
123
|
+
|
124
|
+
unless status.retweeted_by.nil?
|
125
|
+
@window.addstr('(retweeted by ')
|
126
|
+
@window.bold do
|
127
|
+
@window.addstr("@#{status.retweeted_by.screen_name}")
|
128
|
+
end
|
129
|
+
@window.addstr(') ')
|
130
|
+
end
|
131
|
+
|
132
|
+
if status.favorited?
|
133
|
+
@window.with_color(:black, :yellow) do
|
134
|
+
@window.addch(' ')
|
135
|
+
end
|
136
|
+
|
137
|
+
@window.addch(' ')
|
138
|
+
end
|
139
|
+
|
140
|
+
if status.retweeted?
|
141
|
+
@window.with_color(:black, :green) do
|
142
|
+
@window.addch(' ')
|
143
|
+
end
|
144
|
+
@window.addch(' ')
|
145
|
+
end
|
146
|
+
|
147
|
+
if status.favorite_count > 0
|
148
|
+
@window.with_color(:yellow) do
|
149
|
+
@window.addstr("#{status.favorite_count}fav#{status.favorite_count > 1 ? 's' : ''}")
|
150
|
+
end
|
151
|
+
@window.addch(' ')
|
152
|
+
end
|
153
|
+
|
154
|
+
if status.retweet_count > 0
|
155
|
+
@window.with_color(:green) do
|
156
|
+
@window.addstr("#{status.retweet_count}RT#{status.retweet_count > 1 ? 's' : ''}")
|
157
|
+
end
|
158
|
+
@window.addch(' ')
|
159
|
+
end
|
160
|
+
|
161
|
+
status.split(@window.maxx - 4).each do |line|
|
162
|
+
current_line += 1
|
163
|
+
@window.setpos(current_line, 2)
|
164
|
+
@window.addstr(line)
|
165
|
+
end
|
166
|
+
|
167
|
+
current_line += 2
|
168
|
+
end
|
169
|
+
|
170
|
+
draw_scroll_bar
|
171
|
+
|
172
|
+
@window.refresh
|
173
|
+
|
174
|
+
UserWindow.instance.update(highlighted_status.user) unless highlighted_status.nil?
|
175
|
+
show_help
|
176
|
+
end
|
177
|
+
|
178
|
+
def respond_to_key(key)
|
179
|
+
return true if super
|
180
|
+
|
181
|
+
case key
|
182
|
+
when 'c'
|
183
|
+
show_conversation
|
184
|
+
when 'F'
|
185
|
+
favorite
|
186
|
+
when 'o'
|
187
|
+
open_link
|
188
|
+
when 'r'
|
189
|
+
reply
|
190
|
+
when 'R'
|
191
|
+
retweet
|
192
|
+
when 18
|
193
|
+
fetch
|
194
|
+
when 'U'
|
195
|
+
show_user
|
196
|
+
else
|
197
|
+
return false
|
198
|
+
end
|
199
|
+
true
|
200
|
+
end
|
201
|
+
|
202
|
+
private
|
203
|
+
|
204
|
+
def highlighted_status
|
205
|
+
@statuses[count - index - 1]
|
206
|
+
end
|
207
|
+
|
208
|
+
def count
|
209
|
+
@statuses.count
|
210
|
+
end
|
211
|
+
|
212
|
+
def offset_from_bottom
|
213
|
+
return @offset_from_bottom unless @offset_from_bottom.nil?
|
214
|
+
|
215
|
+
height = 0
|
216
|
+
@statuses.each.with_index(-1) do |status, i|
|
217
|
+
height += status.split(@window.maxx - 4).count + 2
|
218
|
+
if height >= @window.maxy
|
219
|
+
@offset_from_bottom = i
|
220
|
+
return i
|
221
|
+
end
|
222
|
+
end
|
223
|
+
count
|
224
|
+
end
|
225
|
+
|
226
|
+
def sort
|
227
|
+
@statuses.sort_by!(&:created_at_for_sort)
|
228
|
+
end
|
229
|
+
|
230
|
+
def show_help
|
231
|
+
Notifier.instance.show_help '[n] Compose [r] Reply [F] Favorite [R] Retweet [U] Show user [w] Close tab [Q] Quit'
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Twterm
|
2
|
+
module Tab
|
3
|
+
class TimelineTab
|
4
|
+
include StatusesTab
|
5
|
+
|
6
|
+
def initialize(client)
|
7
|
+
fail ArgumentError, 'argument must be an instance of Client class' unless client.is_a? Client
|
8
|
+
|
9
|
+
super()
|
10
|
+
@client = client
|
11
|
+
@client.on_timeline_status(&method(:prepend))
|
12
|
+
@title = 'Timeline'
|
13
|
+
|
14
|
+
fetch { move_to_top }
|
15
|
+
auto_reload(180) { fetch }
|
16
|
+
end
|
17
|
+
|
18
|
+
def fetch
|
19
|
+
Thread.new do
|
20
|
+
@client.home_timeline do |statuses|
|
21
|
+
statuses.each(&method(:prepend))
|
22
|
+
sort
|
23
|
+
yield if block_given?
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def close
|
29
|
+
fail NotClosableError
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Twterm
|
2
|
+
module Tab
|
3
|
+
class UserTab
|
4
|
+
include StatusesTab
|
5
|
+
|
6
|
+
attr_reader :user
|
7
|
+
|
8
|
+
def initialize(user)
|
9
|
+
fail ArgumentError, 'argument must be an instance of User class' unless user.is_a? User
|
10
|
+
|
11
|
+
super()
|
12
|
+
|
13
|
+
@user = user
|
14
|
+
@title = "@#{user.screen_name}"
|
15
|
+
|
16
|
+
fetch { move_to_top }
|
17
|
+
auto_reload(120) { fetch }
|
18
|
+
end
|
19
|
+
|
20
|
+
def fetch
|
21
|
+
Client.current.user_timeline(@user.id) do |statuses|
|
22
|
+
statuses.reverse.each(&method(:prepend))
|
23
|
+
sort
|
24
|
+
yield if block_given?
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
module Twterm
|
2
|
+
class TabManager
|
3
|
+
include Singleton
|
4
|
+
include Curses
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@tabs = []
|
8
|
+
@index = 0
|
9
|
+
@history = []
|
10
|
+
|
11
|
+
@window = stdscr.subwin(3, stdscr.maxx - 30, 0, 0)
|
12
|
+
end
|
13
|
+
|
14
|
+
def add(tab_to_add)
|
15
|
+
fail ArgumentError, 'argument must be an instance of Tab::Base' unless tab_to_add.is_a? Tab::Base
|
16
|
+
@tabs.each.with_index do |tab, i|
|
17
|
+
next unless tab == tab_to_add
|
18
|
+
@index = i
|
19
|
+
refresh_window
|
20
|
+
return false
|
21
|
+
end
|
22
|
+
@tabs << tab_to_add
|
23
|
+
@history.push(@tabs.count)
|
24
|
+
refresh_window
|
25
|
+
true
|
26
|
+
end
|
27
|
+
|
28
|
+
def add_and_show(tab)
|
29
|
+
result = add(tab)
|
30
|
+
@index = @tabs.count - 1 if result
|
31
|
+
current_tab.refresh
|
32
|
+
refresh_window
|
33
|
+
result
|
34
|
+
end
|
35
|
+
|
36
|
+
def current_tab
|
37
|
+
@history.unshift(@index).uniq!
|
38
|
+
@tabs[@index]
|
39
|
+
end
|
40
|
+
|
41
|
+
def show_next
|
42
|
+
@index = (@index + 1) % @tabs.count
|
43
|
+
current_tab.refresh
|
44
|
+
refresh_window
|
45
|
+
end
|
46
|
+
|
47
|
+
def show_previous
|
48
|
+
@index = (@index - 1) % @tabs.count
|
49
|
+
current_tab.refresh
|
50
|
+
refresh_window
|
51
|
+
end
|
52
|
+
|
53
|
+
def open_new
|
54
|
+
tab = Tab::New::Start.new
|
55
|
+
add_and_show(tab)
|
56
|
+
end
|
57
|
+
|
58
|
+
def close
|
59
|
+
current_tab.close
|
60
|
+
@tabs.delete_at(@index)
|
61
|
+
@history.delete_if { |n| n == @index }
|
62
|
+
@history = @history.map { |i| i > @index ? i - 1 : i }
|
63
|
+
@index = @history.first
|
64
|
+
current_tab.refresh
|
65
|
+
refresh_window
|
66
|
+
rescue Tab::NotClosableError
|
67
|
+
Notifier.instance.show_error 'This tab cannot be closed'
|
68
|
+
end
|
69
|
+
|
70
|
+
def switch(tab)
|
71
|
+
close
|
72
|
+
add_and_show(tab)
|
73
|
+
end
|
74
|
+
|
75
|
+
def refresh_window
|
76
|
+
@window.clear
|
77
|
+
current_tab_id = current_tab.object_id
|
78
|
+
|
79
|
+
@window.setpos(1, 1)
|
80
|
+
@window.addstr('| ')
|
81
|
+
@tabs.each do |tab|
|
82
|
+
if tab.object_id == current_tab_id
|
83
|
+
@window.bold do
|
84
|
+
@window.addstr(tab.title)
|
85
|
+
end
|
86
|
+
else
|
87
|
+
@window.addstr(tab.title)
|
88
|
+
end
|
89
|
+
@window.addstr(' | ')
|
90
|
+
end
|
91
|
+
|
92
|
+
@window.refresh
|
93
|
+
end
|
94
|
+
|
95
|
+
def respond_to_key(key)
|
96
|
+
case key
|
97
|
+
when 'h', 2, Key::LEFT
|
98
|
+
show_previous
|
99
|
+
when 'l', 6, Key::RIGHT
|
100
|
+
show_next
|
101
|
+
when 'N'
|
102
|
+
open_new
|
103
|
+
when 'w'
|
104
|
+
close
|
105
|
+
else
|
106
|
+
return false
|
107
|
+
end
|
108
|
+
true
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Twterm
|
2
|
+
class Tweetbox
|
3
|
+
include Singleton
|
4
|
+
include Readline
|
5
|
+
include Curses
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@status = ''
|
9
|
+
end
|
10
|
+
|
11
|
+
def compose(in_reply_to = nil)
|
12
|
+
if in_reply_to.is_a? Status
|
13
|
+
@in_reply_to = in_reply_to
|
14
|
+
else
|
15
|
+
@in_reply_to = nil
|
16
|
+
end
|
17
|
+
|
18
|
+
resetter = proc do
|
19
|
+
reset_prog_mode
|
20
|
+
sleep 0.1
|
21
|
+
Screen.instance.refresh
|
22
|
+
end
|
23
|
+
|
24
|
+
thread = Thread.new do
|
25
|
+
close_screen
|
26
|
+
puts "\ncompose new tweet:"
|
27
|
+
@status = readline(@in_reply_to.nil? ? '> ' : " @#{in_reply_to.user.screen_name} ", true)
|
28
|
+
resetter.call
|
29
|
+
post
|
30
|
+
end
|
31
|
+
|
32
|
+
App.instance.register_interruption_handler do
|
33
|
+
thread.kill
|
34
|
+
clear
|
35
|
+
puts "\ncanceled"
|
36
|
+
resetter.call
|
37
|
+
end
|
38
|
+
|
39
|
+
thread.join
|
40
|
+
end
|
41
|
+
|
42
|
+
def post
|
43
|
+
return if @status.nil?
|
44
|
+
|
45
|
+
Client.current.post(@status, @in_reply_to)
|
46
|
+
clear
|
47
|
+
end
|
48
|
+
|
49
|
+
def clear
|
50
|
+
@status = ''
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
data/lib/twterm/user.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
module Twterm
|
2
|
+
class User
|
3
|
+
attr_reader :id, :name, :screen_name, :description, :location, :website, :following, :protected, :statuses_count, :friends_count, :followers_count
|
4
|
+
attr_reader :color
|
5
|
+
alias_method :following?, :following
|
6
|
+
alias_method :protected?, :protected
|
7
|
+
|
8
|
+
COLORS = [:red, :blue, :green, :cyan, :yellow, :magenta]
|
9
|
+
|
10
|
+
@@instances = []
|
11
|
+
|
12
|
+
def self.new(user)
|
13
|
+
detector = -> (instance) { instance.id == user.id }
|
14
|
+
instance = @@instances.find(&detector)
|
15
|
+
instance.nil? ? super : instance.update!(user)
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize(user)
|
19
|
+
@id = user.id
|
20
|
+
update!(user)
|
21
|
+
@color = COLORS[@id % 6]
|
22
|
+
|
23
|
+
@@instances << self
|
24
|
+
end
|
25
|
+
|
26
|
+
def update!(user)
|
27
|
+
@name = user.name
|
28
|
+
@screen_name = user.screen_name
|
29
|
+
@description = user.description || ''
|
30
|
+
@location = user.location.is_a?(Twitter::NullObject) ? '' : user.location
|
31
|
+
@website = user.website
|
32
|
+
@following = user.following?
|
33
|
+
@protected = user.protected?
|
34
|
+
@statuses_count = user.statuses_count
|
35
|
+
@friends_count = user.friends_count
|
36
|
+
@followers_count = user.followers_count
|
37
|
+
self
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|