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
data/lib/twterm/tweetbox.rb
CHANGED
@@ -1,14 +1,16 @@
|
|
1
1
|
module Twterm
|
2
2
|
class Tweetbox
|
3
|
+
class EmptyTextError < StandardError; end
|
4
|
+
class InvalidCharactersError < StandardError; end
|
5
|
+
class TextTooLongError < StandardError; end
|
6
|
+
|
3
7
|
include Singleton
|
4
8
|
include Readline
|
5
9
|
include Curses
|
6
10
|
|
7
|
-
def initialize
|
8
|
-
@status = ''
|
9
|
-
end
|
10
|
-
|
11
11
|
def compose(in_reply_to = nil)
|
12
|
+
@text = ''
|
13
|
+
|
12
14
|
if in_reply_to.is_a? Status
|
13
15
|
@in_reply_to = in_reply_to
|
14
16
|
else
|
@@ -24,37 +26,39 @@ module Twterm
|
|
24
26
|
thread = Thread.new do
|
25
27
|
close_screen
|
26
28
|
|
27
|
-
if
|
29
|
+
if in_reply_to.nil?
|
28
30
|
puts "\nCompose new tweet:"
|
29
31
|
else
|
30
|
-
puts "\nReply to @#{
|
32
|
+
puts "\nReply to @#{in_reply_to.user.screen_name}'s tweet: \"#{in_reply_to.text}\""
|
31
33
|
end
|
32
34
|
|
33
35
|
CompletionManager.instance.set_default_mode!
|
34
36
|
|
35
37
|
loop do
|
36
38
|
loop do
|
37
|
-
msg =
|
39
|
+
msg = in_reply_to.nil? || !text.empty? ? '> ' : "> @#{in_reply_to.user.screen_name} "
|
38
40
|
line = (readline(msg, true) || '').strip
|
39
41
|
break if line.empty?
|
40
42
|
|
41
43
|
if line.end_with?('\\')
|
42
|
-
@
|
44
|
+
@text << line.chop.rstrip + "\n"
|
43
45
|
else
|
44
|
-
@
|
46
|
+
@text << line
|
45
47
|
break
|
46
48
|
end
|
47
49
|
end
|
48
50
|
|
49
51
|
puts "\n"
|
50
52
|
|
51
|
-
|
52
|
-
|
53
|
-
puts "Status is too long (#{length} / 140 characters)"
|
54
|
-
when :invalid_characters
|
55
|
-
puts 'Status contains invalid characters'
|
56
|
-
else
|
53
|
+
begin
|
54
|
+
validate_text!
|
57
55
|
break
|
56
|
+
rescue EmptyTextError
|
57
|
+
break
|
58
|
+
rescue InvalidCharactersError
|
59
|
+
puts 'Text contains invalid characters'
|
60
|
+
rescue TextTooLongError
|
61
|
+
puts "Text is too long (#{text_length} / 140 characters)"
|
58
62
|
end
|
59
63
|
|
60
64
|
puts "\n"
|
@@ -68,30 +72,52 @@ module Twterm
|
|
68
72
|
App.instance.register_interruption_handler do
|
69
73
|
thread.kill
|
70
74
|
clear
|
71
|
-
puts "\
|
75
|
+
puts "\nCanceled"
|
72
76
|
resetter.call
|
73
77
|
end
|
74
78
|
|
75
79
|
thread.join
|
76
80
|
end
|
77
81
|
|
78
|
-
|
79
|
-
|
82
|
+
private
|
83
|
+
|
84
|
+
attr_reader :in_reply_to
|
80
85
|
|
81
|
-
|
86
|
+
def clear
|
87
|
+
@text = ''
|
88
|
+
@in_reply_to = nil
|
89
|
+
end
|
90
|
+
|
91
|
+
def post
|
92
|
+
validate_text!
|
93
|
+
Client.current.post(text, in_reply_to)
|
94
|
+
rescue EmptyTextError
|
95
|
+
# do nothing
|
96
|
+
rescue InvalidCharactersError
|
97
|
+
Notifier.instance.show_error 'Text contains invalid characters'
|
98
|
+
rescue TextTooLongError
|
99
|
+
Notifier.instance.show_error "Text is too long (#{text_length} / 140 characters)"
|
100
|
+
ensure
|
82
101
|
clear
|
83
102
|
end
|
84
103
|
|
85
|
-
def
|
86
|
-
|
104
|
+
def text
|
105
|
+
@text || ''
|
87
106
|
end
|
88
107
|
|
89
|
-
def
|
90
|
-
Twitter::Validation.tweet_length(
|
108
|
+
def text_length
|
109
|
+
Twitter::Validation.tweet_length(text)
|
91
110
|
end
|
92
111
|
|
93
|
-
def
|
94
|
-
|
112
|
+
def validate_text!
|
113
|
+
case Twitter::Validation.tweet_invalid?(text)
|
114
|
+
when :empty
|
115
|
+
fail EmptyTextError
|
116
|
+
when :invalid_characters
|
117
|
+
fail InvalidCharactersError
|
118
|
+
when :too_long
|
119
|
+
fail TextTooLongError
|
120
|
+
end
|
95
121
|
end
|
96
122
|
end
|
97
123
|
end
|
data/lib/twterm/user.rb
CHANGED
@@ -5,20 +5,12 @@ module Twterm
|
|
5
5
|
:followers_count, :touched_at, :color
|
6
6
|
alias_method :following?, :following
|
7
7
|
alias_method :protected?, :protected
|
8
|
-
class << self
|
9
|
-
alias_method :create, :new
|
10
|
-
end
|
11
8
|
|
12
9
|
MAX_CACHED_TIME = 3600
|
13
10
|
COLORS = [:red, :blue, :green, :cyan, :yellow, :magenta]
|
14
11
|
|
15
12
|
@@instances = {}
|
16
13
|
|
17
|
-
def self.new(user)
|
18
|
-
instance = find(user.id)
|
19
|
-
instance.nil? ? super : instance.update!(user)
|
20
|
-
end
|
21
|
-
|
22
14
|
def initialize(user)
|
23
15
|
@id = user.id
|
24
16
|
update!(user)
|
@@ -73,5 +65,10 @@ module Twterm
|
|
73
65
|
user_ids = users.map(&:id)
|
74
66
|
@@instances = user_ids.zip(users).to_h
|
75
67
|
end
|
68
|
+
|
69
|
+
def self.new(user)
|
70
|
+
instance = find(user.id)
|
71
|
+
instance.nil? ? super : instance.update!(user)
|
72
|
+
end
|
76
73
|
end
|
77
74
|
end
|
data/lib/twterm/version.rb
CHANGED
data/lib/twterm.rb
CHANGED
@@ -3,6 +3,7 @@ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname
|
|
3
3
|
|
4
4
|
require 'cgi'
|
5
5
|
require 'curses'
|
6
|
+
require 'forwardable'
|
6
7
|
require 'launchy'
|
7
8
|
require 'oauth'
|
8
9
|
require 'readline'
|
@@ -36,9 +37,10 @@ require 'twterm/tab_manager'
|
|
36
37
|
require 'twterm/tab/base'
|
37
38
|
require 'twterm/tab/dumpable'
|
38
39
|
require 'twterm/tab/exceptions'
|
39
|
-
require 'twterm/tab/
|
40
|
+
require 'twterm/tab/scroll_manager'
|
40
41
|
require 'twterm/tab/statuses_tab'
|
41
42
|
require 'twterm/tab/conversation_tab'
|
43
|
+
require 'twterm/tab/key_assignments_cheatsheet'
|
42
44
|
require 'twterm/tab/list_tab'
|
43
45
|
require 'twterm/tab/mentions_tab'
|
44
46
|
require 'twterm/tab/new/start'
|
@@ -55,6 +57,6 @@ require 'twterm/version'
|
|
55
57
|
|
56
58
|
module Twterm
|
57
59
|
class Conf
|
58
|
-
REQUIRE_VERSION = '1.0.
|
60
|
+
REQUIRE_VERSION = '1.0.10'
|
59
61
|
end
|
60
62
|
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
describe Twterm::Config do
|
2
|
+
MOCK_CONFIG_PATH = 'spec/resources/config'.freeze
|
3
|
+
|
4
|
+
let(:config) { described_class.new }
|
5
|
+
|
6
|
+
describe '#[]' do
|
7
|
+
it 'returns config value from config_file_path' do
|
8
|
+
allow(config).to receive(:config_file_path).and_return(MOCK_CONFIG_PATH)
|
9
|
+
|
10
|
+
expect(config[:access_token]).to eq 'token'
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe '#[]=' do
|
15
|
+
context 'when fail to load config file' do
|
16
|
+
it 'set new value to config file' do
|
17
|
+
allow(config).to receive(:config_file_path).and_return('invalid path')
|
18
|
+
allow(config).to receive(:save_config_to_file).and_return(nil)
|
19
|
+
|
20
|
+
expect(config[:access_token]).to be_nil
|
21
|
+
config[:access_token] = 'new_token'
|
22
|
+
expect(config[:access_token]).to eq 'new_token'
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'can set some value' do
|
27
|
+
allow(config).to receive(:config_file_path).and_return(MOCK_CONFIG_PATH)
|
28
|
+
allow(config).to receive(:save_config_to_file).and_return(nil)
|
29
|
+
|
30
|
+
config[:access_token] = 'new_token'
|
31
|
+
expect(config[:access_token]).to eq 'new_token'
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/twterm.gemspec
CHANGED
@@ -16,6 +16,7 @@ Gem::Specification.new do |spec|
|
|
16
16
|
spec.files = `git ls-files -z`.split("\x0").reject { |i| i == 'Gemfile.lock' }
|
17
17
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
18
|
spec.require_paths = ['lib']
|
19
|
+
spec.required_ruby_version = '>= 2.1.0'
|
19
20
|
|
20
21
|
spec.add_dependency 'curses', '>= 1.0.1'
|
21
22
|
spec.add_dependency 'launchy', '>= 2.4.3'
|
@@ -26,4 +27,5 @@ Gem::Specification.new do |spec|
|
|
26
27
|
|
27
28
|
spec.add_development_dependency 'bundler', '~> 1.8'
|
28
29
|
spec.add_development_dependency 'rake', '~> 10.0'
|
30
|
+
spec.add_development_dependency 'rspec', '~> 3.2.0'
|
29
31
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: twterm
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.10
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ryota Kameoka
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-07-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: curses
|
@@ -122,6 +122,20 @@ dependencies:
|
|
122
122
|
- - "~>"
|
123
123
|
- !ruby/object:Gem::Version
|
124
124
|
version: '10.0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: rspec
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - "~>"
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: 3.2.0
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - "~>"
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: 3.2.0
|
125
139
|
description: A full-featured CLI Twitter client
|
126
140
|
email:
|
127
141
|
- kameoka.ryota@gmail.com
|
@@ -131,8 +145,9 @@ extensions: []
|
|
131
145
|
extra_rdoc_files: []
|
132
146
|
files:
|
133
147
|
- ".gitignore"
|
148
|
+
- ".rspec"
|
134
149
|
- Gemfile
|
135
|
-
- LICENSE
|
150
|
+
- LICENSE
|
136
151
|
- README.md
|
137
152
|
- Rakefile
|
138
153
|
- bin/twterm
|
@@ -162,13 +177,14 @@ files:
|
|
162
177
|
- lib/twterm/tab/dumpable.rb
|
163
178
|
- lib/twterm/tab/exceptions.rb
|
164
179
|
- lib/twterm/tab/favorites.rb
|
180
|
+
- lib/twterm/tab/key_assignments_cheatsheet.rb
|
165
181
|
- lib/twterm/tab/list_tab.rb
|
166
182
|
- lib/twterm/tab/mentions_tab.rb
|
167
183
|
- lib/twterm/tab/new/list.rb
|
168
184
|
- lib/twterm/tab/new/search.rb
|
169
185
|
- lib/twterm/tab/new/start.rb
|
170
186
|
- lib/twterm/tab/new/user.rb
|
171
|
-
- lib/twterm/tab/
|
187
|
+
- lib/twterm/tab/scroll_manager.rb
|
172
188
|
- lib/twterm/tab/search_tab.rb
|
173
189
|
- lib/twterm/tab/statuses_tab.rb
|
174
190
|
- lib/twterm/tab/timeline_tab.rb
|
@@ -178,6 +194,9 @@ files:
|
|
178
194
|
- lib/twterm/user.rb
|
179
195
|
- lib/twterm/user_window.rb
|
180
196
|
- lib/twterm/version.rb
|
197
|
+
- spec/resources/config
|
198
|
+
- spec/spec_helper.rb
|
199
|
+
- spec/twterm/config_spec.rb
|
181
200
|
- twterm.gemspec
|
182
201
|
homepage: http://twterm.ryota-ka.me/
|
183
202
|
licenses:
|
@@ -191,7 +210,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
191
210
|
requirements:
|
192
211
|
- - ">="
|
193
212
|
- !ruby/object:Gem::Version
|
194
|
-
version:
|
213
|
+
version: 2.1.0
|
195
214
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
196
215
|
requirements:
|
197
216
|
- - ">="
|
@@ -199,7 +218,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
199
218
|
version: '0'
|
200
219
|
requirements: []
|
201
220
|
rubyforge_project:
|
202
|
-
rubygems_version: 2.4.
|
221
|
+
rubygems_version: 2.4.7
|
203
222
|
signing_key:
|
204
223
|
specification_version: 4
|
205
224
|
summary: A full-featured CLI Twitter client
|
@@ -1,130 +0,0 @@
|
|
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 - offset + 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
|