twterm 1.0.9 → 1.0.10
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 +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
|