twterm 1.0.9 → 1.0.10
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rspec +2 -0
- data/{LICENSE.txt → LICENSE} +0 -0
- data/README.md +24 -2
- data/lib/twterm/app.rb +14 -4
- data/lib/twterm/auth.rb +5 -5
- data/lib/twterm/client.rb +185 -142
- data/lib/twterm/color_manager.rb +15 -11
- data/lib/twterm/config.rb +22 -17
- data/lib/twterm/list.rb +9 -9
- data/lib/twterm/notifier.rb +7 -22
- data/lib/twterm/scheduler.rb +5 -8
- data/lib/twterm/screen.rb +25 -20
- data/lib/twterm/status.rb +84 -79
- data/lib/twterm/tab/base.rb +26 -11
- data/lib/twterm/tab/conversation_tab.rb +16 -16
- data/lib/twterm/tab/key_assignments_cheatsheet.rb +113 -0
- data/lib/twterm/tab/list_tab.rb +1 -1
- data/lib/twterm/tab/mentions_tab.rb +13 -13
- data/lib/twterm/tab/new/list.rb +51 -40
- data/lib/twterm/tab/new/search.rb +8 -5
- data/lib/twterm/tab/new/start.rb +26 -35
- data/lib/twterm/tab/new/user.rb +8 -5
- data/lib/twterm/tab/scroll_manager.rb +84 -0
- data/lib/twterm/tab/search_tab.rb +16 -16
- data/lib/twterm/tab/statuses_tab.rb +177 -128
- data/lib/twterm/tab/timeline_tab.rb +13 -15
- data/lib/twterm/tab/user_tab.rb +18 -18
- data/lib/twterm/tab_manager.rb +49 -44
- data/lib/twterm/tweetbox.rb +51 -25
- data/lib/twterm/user.rb +5 -8
- data/lib/twterm/version.rb +1 -1
- data/lib/twterm.rb +4 -2
- data/spec/resources/config +4 -0
- data/spec/spec_helper.rb +7 -0
- data/spec/twterm/config_spec.rb +34 -0
- data/twterm.gemspec +2 -0
- metadata +25 -6
- data/lib/twterm/tab/scrollable.rb +0 -130
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8d067153f4c632aeba6108ea07201aff1db4790c
|
4
|
+
data.tar.gz: d6ddac0119221c40646bae8bd53bf19081ddab1b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dcd02ff35df547ca1383dfe70ac5a097c4ca0d91448ec0a79a4557084a84a14b24961c190697d30f428b1053db710fcd4030fcb9350a0a12111dd1c6eeec0dba
|
7
|
+
data.tar.gz: d37de57e07ad39327df4864b961a141e146a00d6956f43391c2e9cf4843e5d4d80a78d4666204edfeb32c4fbc7dd0ab73692a6e847a3d060bb4a57271629848c
|
data/.rspec
ADDED
data/{LICENSE.txt → LICENSE}
RENAMED
File without changes
|
data/README.md
CHANGED
@@ -8,7 +8,7 @@ A full-featured CLI Twitter client
|
|
8
8
|
|
9
9
|
## Requirements
|
10
10
|
|
11
|
-
- Ruby (compiled with C Curses and Readline)
|
11
|
+
- Ruby (>= 2.1, compiled with C Curses and Readline)
|
12
12
|
- C curses
|
13
13
|
- Readline
|
14
14
|
|
@@ -18,12 +18,34 @@ A full-featured CLI Twitter client
|
|
18
18
|
$ gem install twterm
|
19
19
|
```
|
20
20
|
|
21
|
-
##
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
To launch twterm, just type in your console:
|
22
24
|
|
23
25
|
```
|
24
26
|
$ twterm
|
25
27
|
```
|
26
28
|
|
29
|
+
### Basic key assignments
|
30
|
+
|
31
|
+
key | operation
|
32
|
+
--- | ---
|
33
|
+
`F` | add to favorite
|
34
|
+
`d` `C-d` | scroll down
|
35
|
+
`h` `C-b` `←` | show previous tab
|
36
|
+
`j` `C-n` `↓` | move down
|
37
|
+
`k` `C-p` `↑` | move up
|
38
|
+
`l` `C-f` `→` | show next tab
|
39
|
+
`n` | compose new tweet
|
40
|
+
`N` | open new tab
|
41
|
+
`r` | reply
|
42
|
+
`R` | retweet
|
43
|
+
`w` | close current tab
|
44
|
+
`Q` | quit
|
45
|
+
`?` | open key assignments cheatsheet
|
46
|
+
|
47
|
+
Type `?` key to see the full list of key assignments.
|
48
|
+
|
27
49
|
## License
|
28
50
|
|
29
51
|
See the LICENSE file for license rights and limitations (MIT).
|
data/lib/twterm/app.rb
CHANGED
@@ -7,13 +7,10 @@ module Twterm
|
|
7
7
|
def initialize
|
8
8
|
Dir.mkdir(DATA_DIR, 0700) unless File.directory?(DATA_DIR)
|
9
9
|
|
10
|
-
|
11
|
-
Auth.authenticate_user if Config[:screen_name].nil?
|
10
|
+
Auth.authenticate_user(config) if config[:screen_name].nil?
|
12
11
|
|
13
12
|
Screen.instance
|
14
13
|
|
15
|
-
client = Client.new(Config[:user_id], Config[:screen_name], Config[:access_token], Config[:access_token_secret])
|
16
|
-
|
17
14
|
timeline = Tab::TimelineTab.new(client)
|
18
15
|
TabManager.instance.add_and_show(timeline)
|
19
16
|
|
@@ -53,6 +50,19 @@ module Twterm
|
|
53
50
|
|
54
51
|
private
|
55
52
|
|
53
|
+
def client
|
54
|
+
@client ||= Client.new(
|
55
|
+
config[:user_id].to_i,
|
56
|
+
config[:screen_name],
|
57
|
+
config[:access_token],
|
58
|
+
config[:access_token_secret]
|
59
|
+
)
|
60
|
+
end
|
61
|
+
|
62
|
+
def config
|
63
|
+
@config ||= Config.new
|
64
|
+
end
|
65
|
+
|
56
66
|
def run_periodic_cleanup
|
57
67
|
Scheduler.new(300) do
|
58
68
|
Status.cleanup
|
data/lib/twterm/auth.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
module Twterm
|
2
2
|
module Auth
|
3
|
-
def authenticate_user
|
3
|
+
def authenticate_user(config)
|
4
4
|
consumer = OAuth::Consumer.new(
|
5
5
|
'vLNSVFgXclBJQJRZ7VLMxL9lA',
|
6
6
|
'OFLKzrepRG2p1hq0nUB9j2S9ndFQoNTPheTpmOY0GYw55jGgS5',
|
@@ -17,10 +17,10 @@ module Twterm
|
|
17
17
|
pin = (STDIN.gets || '').strip
|
18
18
|
access_token = request_token.get_access_token(oauth_verifier: pin)
|
19
19
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
20
|
+
config[:access_token] = access_token.token
|
21
|
+
config[:access_token_secret] = access_token.secret
|
22
|
+
config[:screen_name] = access_token.params[:screen_name]
|
23
|
+
config[:user_id] = access_token.params[:user_id]
|
24
24
|
end
|
25
25
|
|
26
26
|
module_function :authenticate_user
|
data/lib/twterm/client.rb
CHANGED
@@ -3,146 +3,191 @@ module Twterm
|
|
3
3
|
attr_reader :user_id, :screen_name
|
4
4
|
|
5
5
|
CREATE_STATUS_PROC = -> (s) { Status.new(s) }
|
6
|
+
CONSUMER_KEY = 'vLNSVFgXclBJQJRZ7VLMxL9lA'.freeze
|
7
|
+
CONSUMER_SECRET = 'OFLKzrepRG2p1hq0nUB9j2S9ndFQoNTPheTpmOY0GYw55jGgS5'.freeze
|
6
8
|
|
7
9
|
@@instances = []
|
8
10
|
|
9
|
-
def
|
10
|
-
|
11
|
-
@screen_name = screen_name
|
11
|
+
def connect_stream
|
12
|
+
stream_client.stop_stream
|
12
13
|
|
13
|
-
@
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
14
|
+
@streaming_thread = Thread.new do
|
15
|
+
begin
|
16
|
+
Notifier.instance.show_message 'Trying to connect to Twitter...'
|
17
|
+
stream_client.userstream
|
18
|
+
rescue EventMachine::ConnectionError
|
19
|
+
Notifier.instance.show_error 'Connection failed'
|
20
|
+
sleep 30
|
21
|
+
retry
|
22
|
+
end
|
18
23
|
end
|
24
|
+
end
|
19
25
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
+
def destroy_status(status)
|
27
|
+
send_request do
|
28
|
+
begin
|
29
|
+
rest_client.destroy_status(status.id)
|
30
|
+
yield if block_given?
|
31
|
+
rescue Twitter::Error::NotFound, Twitter::Error::Forbidden
|
32
|
+
Notifier.instance.show_error 'You cannot destroy that status'
|
33
|
+
end
|
26
34
|
end
|
35
|
+
end
|
27
36
|
|
28
|
-
|
29
|
-
|
30
|
-
@callbacks = {}
|
37
|
+
def favorite(status)
|
38
|
+
return false unless status.is_a? Status
|
31
39
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
!(status.retweeted_status.is_a?(Twitter::NullObject) &&
|
37
|
-
muted_user_ids.include?(status.retweeted_status.user.id))
|
38
|
-
end
|
40
|
+
send_request do
|
41
|
+
rest_client.favorite(status.id)
|
42
|
+
status.favorite!
|
43
|
+
yield status if block_given?
|
39
44
|
end
|
40
45
|
|
41
|
-
|
46
|
+
self
|
42
47
|
end
|
43
48
|
|
44
|
-
def
|
45
|
-
|
46
|
-
|
47
|
-
@
|
49
|
+
def fetch_muted_users
|
50
|
+
send_request do
|
51
|
+
@muted_user_ids = rest_client.muted_ids.to_a
|
52
|
+
yield @muted_user_ids if block_given?
|
48
53
|
end
|
54
|
+
end
|
49
55
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
56
|
+
def home_timeline
|
57
|
+
send_request do
|
58
|
+
statuses = rest_client
|
59
|
+
.home_timeline(count: 100)
|
60
|
+
.select(&@mute_filter)
|
61
|
+
.map(&CREATE_STATUS_PROC)
|
62
|
+
yield statuses
|
54
63
|
end
|
64
|
+
end
|
55
65
|
|
56
|
-
|
57
|
-
|
58
|
-
|
66
|
+
def initialize(user_id, screen_name, access_token, access_token_secret)
|
67
|
+
@user_id, @screen_name = user_id, screen_name
|
68
|
+
@access_token, @access_token_secret = access_token, access_token_secret
|
59
69
|
|
60
|
-
@
|
61
|
-
break if event[:source][:screen_name] == @screen_name
|
62
|
-
message = "@#{event[:source][:screen_name]} has favorited your tweet: #{event[:target_object][:text]}"
|
63
|
-
Notifier.instance.show_message(message)
|
64
|
-
end
|
70
|
+
@callbacks = {}
|
65
71
|
|
66
|
-
@
|
67
|
-
|
68
|
-
|
72
|
+
@mute_filter = -> _ { true }
|
73
|
+
fetch_muted_users do |muted_user_ids|
|
74
|
+
@mute_filter = lambda do |status|
|
75
|
+
!muted_user_ids.include?(status.user.id) &&
|
76
|
+
!(status.retweeted_status.is_a?(Twitter::NullObject) &&
|
77
|
+
muted_user_ids.include?(status.retweeted_status.user.id))
|
78
|
+
end
|
69
79
|
end
|
70
80
|
|
71
|
-
|
81
|
+
@@instances << self
|
72
82
|
end
|
73
83
|
|
74
|
-
def
|
75
|
-
|
76
|
-
|
77
|
-
@streaming_thread = Thread.new do
|
78
|
-
begin
|
79
|
-
Notifier.instance.show_message 'Trying to connect to Twitter...'
|
80
|
-
@stream_client.userstream
|
81
|
-
rescue EventMachine::ConnectionError
|
82
|
-
Notifier.instance.show_error 'Connection failed'
|
83
|
-
sleep 30
|
84
|
-
retry
|
85
|
-
end
|
84
|
+
def list(list_id)
|
85
|
+
send_request do
|
86
|
+
yield List.new(rest_client.list(list_id))
|
86
87
|
end
|
87
88
|
end
|
88
89
|
|
89
|
-
def
|
90
|
+
def list_timeline(list)
|
91
|
+
fail ArgumentError,
|
92
|
+
'argument must be an instance of List class' unless list.is_a? List
|
90
93
|
send_request do
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
end
|
94
|
+
statuses = rest_client
|
95
|
+
.list_timeline(list.id, count: 100)
|
96
|
+
.select(&@mute_filter)
|
97
|
+
.map(&CREATE_STATUS_PROC)
|
98
|
+
yield statuses
|
97
99
|
end
|
98
100
|
end
|
99
101
|
|
100
|
-
def
|
102
|
+
def lists
|
101
103
|
send_request do
|
102
|
-
yield
|
104
|
+
yield rest_client.lists.map { |list| List.new(list) }
|
103
105
|
end
|
104
106
|
end
|
105
107
|
|
106
108
|
def mentions
|
107
109
|
send_request do
|
108
|
-
|
110
|
+
statuses = rest_client
|
111
|
+
.mentions(count: 100)
|
112
|
+
.select(&@mute_filter)
|
113
|
+
.map(&CREATE_STATUS_PROC)
|
114
|
+
yield statuses
|
109
115
|
end
|
110
116
|
end
|
111
117
|
|
112
|
-
def
|
113
|
-
|
114
|
-
|
115
|
-
end
|
118
|
+
def on_mention(&block)
|
119
|
+
fail ArgumentError, 'no block given' unless block_given?
|
120
|
+
on(:mention, &block)
|
116
121
|
end
|
117
122
|
|
118
|
-
def
|
123
|
+
def on_timeline_status(&block)
|
124
|
+
fail ArgumentError, 'no block given' unless block_given?
|
125
|
+
on(:timeline_status, &block)
|
126
|
+
end
|
127
|
+
|
128
|
+
def post(text, in_reply_to = nil)
|
119
129
|
send_request do
|
120
|
-
|
130
|
+
if in_reply_to.is_a? Status
|
131
|
+
text = "@#{in_reply_to.user.screen_name} #{text}"
|
132
|
+
rest_client.update(text, in_reply_to_status_id: in_reply_to.id)
|
133
|
+
else
|
134
|
+
rest_client.update(text)
|
135
|
+
end
|
121
136
|
end
|
122
137
|
end
|
123
138
|
|
124
|
-
def
|
125
|
-
|
126
|
-
|
139
|
+
def rest_client
|
140
|
+
@rest_client ||= Twitter::REST::Client.new do |config|
|
141
|
+
config.consumer_key = CONSUMER_KEY
|
142
|
+
config.consumer_secret = CONSUMER_SECRET
|
143
|
+
config.access_token = @access_token
|
144
|
+
config.access_token_secret = @access_token_secret
|
127
145
|
end
|
128
146
|
end
|
129
147
|
|
130
|
-
def
|
131
|
-
fail ArgumentError,
|
148
|
+
def retweet(status)
|
149
|
+
fail ArgumentError,
|
150
|
+
'argument must be an instance of Status class' unless status.is_a? Status
|
151
|
+
|
132
152
|
send_request do
|
133
|
-
|
153
|
+
begin
|
154
|
+
rest_client.retweet!(status.id)
|
155
|
+
status.retweet!
|
156
|
+
yield status if block_given?
|
157
|
+
rescue => e
|
158
|
+
message =
|
159
|
+
case e
|
160
|
+
when Twitter::Error::AlreadyRetweeted
|
161
|
+
'The status is already retweeted'
|
162
|
+
when Twitter::Error::NotFound
|
163
|
+
'The status is not found'
|
164
|
+
when Twitter::Error::Forbidden
|
165
|
+
if status.user.id == user_id # when the status is mine
|
166
|
+
'You cannot retweet your own status'
|
167
|
+
else # when the status is not mine
|
168
|
+
'The status is protected'
|
169
|
+
end
|
170
|
+
else
|
171
|
+
raise e
|
172
|
+
end
|
173
|
+
Notifier.instance.show_error "Retweet attempt failed: #{message}"
|
174
|
+
end
|
134
175
|
end
|
135
176
|
end
|
136
177
|
|
137
178
|
def search(query)
|
138
179
|
send_request do
|
139
|
-
|
180
|
+
statuses = rest_client
|
181
|
+
.search(query, count: 100)
|
182
|
+
.select(&@mute_filter)
|
183
|
+
.map(&CREATE_STATUS_PROC)
|
184
|
+
yield statuses
|
140
185
|
end
|
141
186
|
end
|
142
187
|
|
143
188
|
def show_status(status_id)
|
144
189
|
send_request do
|
145
|
-
yield Status.new(
|
190
|
+
yield Status.new(rest_client.status(status_id))
|
146
191
|
end
|
147
192
|
end
|
148
193
|
|
@@ -150,7 +195,7 @@ module Twterm
|
|
150
195
|
send_request do
|
151
196
|
user =
|
152
197
|
begin
|
153
|
-
User.new(
|
198
|
+
User.new(rest_client.user(query))
|
154
199
|
rescue Twitter::Error::NotFound
|
155
200
|
nil
|
156
201
|
end
|
@@ -158,90 +203,82 @@ module Twterm
|
|
158
203
|
end
|
159
204
|
end
|
160
205
|
|
161
|
-
def
|
162
|
-
|
206
|
+
def stream
|
207
|
+
stream_client.on_friends do
|
208
|
+
Notifier.instance.show_message 'Connection established' unless @stream_connected
|
209
|
+
@stream_connected = true
|
210
|
+
end
|
163
211
|
|
164
|
-
|
165
|
-
|
166
|
-
status
|
167
|
-
|
212
|
+
stream_client.on_timeline_status do |tweet|
|
213
|
+
status = Status.new(tweet)
|
214
|
+
invoke_callbacks(:timeline_status, status)
|
215
|
+
invoke_callbacks(:mention, status) if status.text.include? "@#{@screen_name}"
|
168
216
|
end
|
169
217
|
|
170
|
-
|
171
|
-
|
218
|
+
stream_client.on_delete do |status_id|
|
219
|
+
timeline.delete_status(status_id)
|
220
|
+
end
|
172
221
|
|
173
|
-
|
174
|
-
|
222
|
+
stream_client.on_event(:favorite) do |event|
|
223
|
+
break if event[:source][:screen_name] == @screen_name
|
175
224
|
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
225
|
+
user = event[:source][:screen_name]
|
226
|
+
text = event[:target_object][:text]
|
227
|
+
message = "@#{user} has favorited your tweet: #{text}"
|
228
|
+
Notifier.instance.show_message(message)
|
180
229
|
end
|
181
|
-
end
|
182
230
|
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
send_request do
|
187
|
-
begin
|
188
|
-
@rest_client.retweet!(status.id)
|
189
|
-
status.retweet!
|
190
|
-
yield status if block_given?
|
191
|
-
rescue Twitter::Error::AlreadyRetweeted, Twitter::Error::NotFound, Twitter::Error::Forbidden
|
192
|
-
Notifier.instance.show_error 'Retweet attempt failed'
|
193
|
-
end
|
231
|
+
stream_client.on_no_data_received do
|
232
|
+
@stream_connected = false
|
233
|
+
connect_stream
|
194
234
|
end
|
235
|
+
|
236
|
+
connect_stream
|
195
237
|
end
|
196
238
|
|
197
|
-
def
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
end
|
239
|
+
def stream_client
|
240
|
+
@stream_client ||= TweetStream::Client.new(
|
241
|
+
consumer_key: CONSUMER_KEY,
|
242
|
+
consumer_secret: CONSUMER_SECRET,
|
243
|
+
oauth_token: @access_token,
|
244
|
+
oauth_token_secret: @access_token_secret,
|
245
|
+
auth_method: :oauth
|
246
|
+
)
|
206
247
|
end
|
207
248
|
|
208
|
-
def
|
249
|
+
def unfavorite(status)
|
250
|
+
fail ArgumentError,
|
251
|
+
'argument must be an instance of Status class' unless status.is_a? Status
|
252
|
+
|
209
253
|
send_request do
|
210
|
-
|
211
|
-
|
254
|
+
rest_client.unfavorite(status.id)
|
255
|
+
status.unfavorite!
|
256
|
+
yield status if block_given?
|
212
257
|
end
|
213
258
|
end
|
214
259
|
|
215
|
-
def
|
216
|
-
|
217
|
-
|
260
|
+
def user_timeline(user_id)
|
261
|
+
send_request do
|
262
|
+
statuses = rest_client
|
263
|
+
.user_timeline(user_id, count: 100)
|
264
|
+
.select(&@mute_filter)
|
265
|
+
.map(&CREATE_STATUS_PROC)
|
266
|
+
yield statuses
|
267
|
+
end
|
218
268
|
end
|
219
269
|
|
220
|
-
def
|
221
|
-
|
222
|
-
|
270
|
+
def self.new(user_id, screen_name, token, secret)
|
271
|
+
detector = -> (instance) { instance.user_id == user_id }
|
272
|
+
instance = @@instances.find(&detector)
|
273
|
+
instance.nil? ? super : instance
|
223
274
|
end
|
224
275
|
|
225
|
-
|
226
|
-
|
227
|
-
detector = -> (instance) { instance.user_id == user_id }
|
228
|
-
instance = @@instances.find(&detector)
|
229
|
-
instance.nil? ? super : instance
|
230
|
-
end
|
231
|
-
|
232
|
-
def current
|
233
|
-
@@instances[0]
|
234
|
-
end
|
276
|
+
def self.current
|
277
|
+
@@instances[0]
|
235
278
|
end
|
236
279
|
|
237
280
|
private
|
238
281
|
|
239
|
-
def on(event, &block)
|
240
|
-
@callbacks[event] ||= []
|
241
|
-
@callbacks[event] << block
|
242
|
-
self
|
243
|
-
end
|
244
|
-
|
245
282
|
def invoke_callbacks(event, data = nil)
|
246
283
|
return if @callbacks[event].nil?
|
247
284
|
|
@@ -249,6 +286,12 @@ module Twterm
|
|
249
286
|
self
|
250
287
|
end
|
251
288
|
|
289
|
+
def on(event, &block)
|
290
|
+
@callbacks[event] ||= []
|
291
|
+
@callbacks[event] << block
|
292
|
+
self
|
293
|
+
end
|
294
|
+
|
252
295
|
def send_request(&block)
|
253
296
|
Thread.new do
|
254
297
|
begin
|
data/lib/twterm/color_manager.rb
CHANGED
@@ -16,6 +16,17 @@ module Twterm
|
|
16
16
|
transparent: -1
|
17
17
|
}
|
18
18
|
|
19
|
+
def get_color_pair_index(fg, bg)
|
20
|
+
fail ArgumentError,
|
21
|
+
'invalid color name for foreground' unless COLORS.include? fg
|
22
|
+
fail ArgumentError,
|
23
|
+
'invalid color name for background' unless COLORS.include? bg
|
24
|
+
|
25
|
+
return @colors[bg][fg] unless @colors[bg][fg].nil?
|
26
|
+
|
27
|
+
add_color(fg, bg)
|
28
|
+
end
|
29
|
+
|
19
30
|
def initialize
|
20
31
|
@colors = {
|
21
32
|
black: {}, white: {}, red: {}, green: {},
|
@@ -25,20 +36,13 @@ module Twterm
|
|
25
36
|
@count = 0
|
26
37
|
end
|
27
38
|
|
28
|
-
def get_color_pair_index(fg, bg)
|
29
|
-
fail ArgumentError, 'invalid color name' unless COLORS.include? fg
|
30
|
-
fail ArgumentError, 'invalid color name' unless COLORS.include? bg
|
31
|
-
|
32
|
-
return @colors[bg][fg] unless @colors[bg][fg].nil?
|
33
|
-
|
34
|
-
add_color(fg, bg)
|
35
|
-
end
|
36
|
-
|
37
39
|
private
|
38
40
|
|
39
41
|
def add_color(fg, bg)
|
40
|
-
fail ArgumentError,
|
41
|
-
|
42
|
+
fail ArgumentError,
|
43
|
+
'invalid color name for foreground' unless COLORS.include? fg
|
44
|
+
fail ArgumentError,
|
45
|
+
'invalid color name for background' unless COLORS.include? bg
|
42
46
|
|
43
47
|
@count += 1
|
44
48
|
index = @count
|
data/lib/twterm/config.rb
CHANGED
@@ -1,32 +1,37 @@
|
|
1
1
|
module Twterm
|
2
|
-
|
3
|
-
CONFIG_FILE = "#{App::DATA_DIR}/config"
|
4
|
-
|
2
|
+
class Config
|
5
3
|
def [](key)
|
6
|
-
|
4
|
+
config[key]
|
7
5
|
end
|
8
6
|
|
9
7
|
def []=(key, value)
|
10
|
-
|
11
|
-
|
8
|
+
return if config[key] == value
|
9
|
+
save_config_to_file
|
10
|
+
config[key] = value
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def config
|
16
|
+
@config ||= exist_config_file? ? load_config_file : {}
|
12
17
|
end
|
13
18
|
|
14
|
-
def
|
15
|
-
|
16
|
-
|
17
|
-
return
|
19
|
+
def save_config_to_file
|
20
|
+
File.open(config_file_path, 'w', 0600) do |f|
|
21
|
+
f.write config.to_yaml
|
18
22
|
end
|
19
|
-
@config = YAML.load(File.read(CONFIG_FILE)) || {}
|
20
23
|
end
|
21
24
|
|
22
|
-
|
25
|
+
def load_config_file
|
26
|
+
YAML.load_file(config_file_path)
|
27
|
+
end
|
23
28
|
|
24
|
-
def
|
25
|
-
File.
|
26
|
-
f.write @config.to_yaml
|
27
|
-
end
|
29
|
+
def exist_config_file?
|
30
|
+
File.exist?(config_file_path)
|
28
31
|
end
|
29
32
|
|
30
|
-
|
33
|
+
def config_file_path
|
34
|
+
"#{App::DATA_DIR}/config".freeze
|
35
|
+
end
|
31
36
|
end
|
32
37
|
end
|