termtter 1.6.0 → 1.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (98) hide show
  1. data/.gitignore +5 -0
  2. data/README.rdoc +2 -2
  3. data/Rakefile +41 -75
  4. data/VERSION +1 -0
  5. data/bin/termtter +9 -2
  6. data/doc/Termtter-1.0-Release-Note-English.txt +37 -0
  7. data/doc/Termtter-1.0-Release-Note.txt +37 -0
  8. data/lib/plugins/another_prompt.rb +8 -8
  9. data/lib/plugins/ar.rb +102 -0
  10. data/lib/plugins/async.rb +1 -1
  11. data/lib/plugins/babelfish.rb +34 -0
  12. data/lib/plugins/confirm.rb +2 -0
  13. data/lib/plugins/crypt.rb +44 -0
  14. data/lib/plugins/defaults/auto_reload.rb +1 -1
  15. data/lib/plugins/defaults/command_line.rb +34 -17
  16. data/lib/plugins/defaults/confirm.rb +30 -0
  17. data/lib/plugins/defaults/hashtag.rb +1 -1
  18. data/lib/plugins/defaults/irb.rb +30 -0
  19. data/lib/plugins/defaults/keyword.rb +58 -0
  20. data/lib/plugins/defaults/list.rb +155 -0
  21. data/lib/plugins/defaults/plugin.rb +59 -0
  22. data/lib/plugins/defaults/retweet.rb +75 -23
  23. data/lib/plugins/defaults/standard_commands.rb +60 -87
  24. data/lib/plugins/defaults/standard_completion.rb +25 -15
  25. data/lib/plugins/defaults/stdout.rb +49 -10
  26. data/lib/plugins/defaults/switch.rb +1 -1
  27. data/lib/plugins/defaults/users.rb +63 -0
  28. data/lib/plugins/draft.rb +58 -0
  29. data/lib/plugins/expand-tinyurl.rb +5 -9
  30. data/lib/plugins/favotter.rb +1 -1
  31. data/lib/plugins/footer.rb +22 -0
  32. data/lib/plugins/friends.rb +5 -4
  33. data/lib/plugins/g.rb +9 -16
  34. data/lib/plugins/gem_install.rb +24 -0
  35. data/lib/plugins/gist.rb +20 -0
  36. data/lib/plugins/grass.rb +1 -1
  37. data/lib/plugins/gyazo.rb +78 -0
  38. data/lib/plugins/http_server.rb +1 -1
  39. data/lib/plugins/hugeurl.rb +6 -13
  40. data/lib/plugins/irc_gw.rb +15 -11
  41. data/lib/plugins/me.rb +1 -1
  42. data/lib/plugins/notify-send.rb +1 -1
  43. data/lib/plugins/notify-send3.rb +1 -1
  44. data/lib/plugins/open.rb +1 -1
  45. data/lib/plugins/open_url.rb +5 -1
  46. data/lib/plugins/pool.rb +1 -1
  47. data/lib/plugins/random.rb +1 -1
  48. data/lib/plugins/reply_retweet.rb +42 -0
  49. data/lib/plugins/screen-notify.rb +1 -1
  50. data/lib/plugins/sl.rb +3 -3
  51. data/lib/plugins/storage.rb +7 -10
  52. data/lib/plugins/storage/sqlite3.rb +155 -0
  53. data/lib/plugins/storage/status.rb +2 -0
  54. data/lib/plugins/stream.rb +1 -1
  55. data/lib/plugins/tinyurl.rb +3 -9
  56. data/lib/plugins/trends.rb +2 -2
  57. data/lib/plugins/truncate.rb +1 -1
  58. data/lib/plugins/w3mimg.rb +1 -1
  59. data/lib/termtter.rb +19 -20
  60. data/lib/termtter/active_rubytter.rb +4 -0
  61. data/lib/termtter/api.rb +22 -5
  62. data/lib/termtter/client.rb +55 -40
  63. data/lib/termtter/command.rb +3 -2
  64. data/lib/termtter/config_setup.rb +1 -1
  65. data/lib/termtter/config_template.erb +5 -0
  66. data/lib/termtter/default_config.rb +18 -0
  67. data/lib/termtter/hookable.rb +1 -0
  68. data/lib/termtter/httppool.rb +44 -0
  69. data/lib/termtter/memory_cache.rb +32 -0
  70. data/lib/termtter/optparse.rb +8 -15
  71. data/lib/termtter/rubytter_proxy.rb +65 -4
  72. data/lib/termtter/system_extensions.rb +40 -9
  73. data/lib/termtter/task.rb +2 -1
  74. data/spec/plugins/defaults/hashtag_spec.rb +8 -7
  75. data/spec/plugins/defaults/list_spec.rb +33 -0
  76. data/spec/plugins/defaults/plugin_spec.rb +17 -0
  77. data/spec/plugins/defaults/retweet_spec.rb +205 -0
  78. data/spec/plugins/draft_spec.rb +59 -0
  79. data/spec/plugins/expand-tinyurl_spec.rb +21 -0
  80. data/spec/plugins/footer_spec.rb +50 -0
  81. data/spec/plugins/storage/sqlite3_spec.rb +41 -0
  82. data/spec/termtter/api_spec.rb +1 -1
  83. data/spec/termtter/client_spec.rb +21 -21
  84. data/spec/termtter/command_spec.rb +8 -8
  85. data/spec/termtter/config_spec.rb +2 -2
  86. data/spec/termtter/memory_cache_spec.rb +20 -0
  87. data/spec/termtter/optparse_spec.rb +1 -1
  88. data/spec/termtter/rubytter_proxy_spec.rb +38 -0
  89. data/spec/termtter/system_extensions_spec.rb +25 -23
  90. data/spec/termtter/task_manager_spec.rb +1 -1
  91. data/spec/termtter_spec.rb +4 -2
  92. metadata +88 -19
  93. data/lib/plugins/defaults/lists.rb +0 -14
  94. data/lib/plugins/irb.rb +0 -6
  95. data/lib/plugins/storage/DB.rb +0 -37
  96. data/lib/termtter/version.rb +0 -4
  97. data/spec/plugins/defaults/lists_spec.rb +0 -34
  98. data/spec/plugins/storage/DB_spec_.rb +0 -12
@@ -1,13 +1,17 @@
1
+ config.set_default(:memory_cache_size, 10000)
2
+
1
3
  module Termtter
2
4
  class RubytterProxy
3
5
  include Hookable
4
6
 
7
+ attr_reader :rubytter
8
+
5
9
  def initialize(*args)
6
10
  @rubytter = Rubytter.new(*args)
7
11
  end
8
12
 
9
13
  def method_missing(method, *args, &block)
10
- if @rubytter.methods.include?(method.to_s)
14
+ if @rubytter.respond_to?(method)
11
15
  result = nil
12
16
  begin
13
17
  modified_args = args
@@ -16,9 +20,9 @@ module Termtter
16
20
  modified_args = hook.call(*modified_args)
17
21
  end
18
22
 
19
- timeout(config.timeout) do
20
- result = @rubytter.__send__(method, *modified_args)
21
- end
23
+ from = Time.now
24
+ result = call_rubytter_or_use_cache(method, *modified_args, &block)
25
+ Termtter::Client.logger.debug "rubytter_proxy: #{method}(#{modified_args.inspect[1...-1]}), %.2fsec" % (Time.now - from)
22
26
 
23
27
  self.class.call_hooks("post_#{method}", *args)
24
28
  rescue HookCanceled
@@ -28,5 +32,62 @@ module Termtter
28
32
  super
29
33
  end
30
34
  end
35
+
36
+ def status_cache_store
37
+ # TODO: DB store とかにうまいこと切り替えられるようにしたい
38
+ @status_cache_store ||= MemoryCache.new(config.memory_cache_size)
39
+ end
40
+
41
+ def users_cache_store
42
+ @users_cache_store ||= MemoryCache.new(config.memory_cache_size)
43
+ end
44
+
45
+ def cached_user(screen_name)
46
+ users_cache_store[screen_name]
47
+ end
48
+
49
+ def call_rubytter_or_use_cache(method, *args, &block)
50
+ case method
51
+ when :show
52
+ if status_cache_store.key?(args[0].to_i)
53
+ status_cache_store[args[0].to_i]
54
+ else
55
+ status = call_rubytter(method, *args, &block)
56
+ store_status_cache(status)
57
+ status
58
+ end
59
+ when :home_timeline, :user_timeline, :friends_timeline, :search
60
+ statuses = call_rubytter(method, *args, &block)
61
+ statuses.each do |status|
62
+ store_status_cache(status)
63
+ end
64
+ statuses
65
+ else
66
+ call_rubytter(method, *args, &block)
67
+ end
68
+ end
69
+
70
+ def store_status_cache(status)
71
+ return if status_cache_store.key?(status.id)
72
+ status_cache_store[status.id] = status
73
+ store_user_cache(status.user)
74
+ end
75
+
76
+ def store_user_cache(user)
77
+ return if users_cache_store.key?(user.screen_name)
78
+ users_cache_store[user.screen_name] = user
79
+ end
80
+
81
+ def call_rubytter(method, *args, &block)
82
+ config.retry.times do
83
+ begin
84
+ timeout(config.timeout) do
85
+ return @rubytter.__send__(method, *args, &block)
86
+ end
87
+ rescue TimeoutError
88
+ end
89
+ end
90
+ raise TimeoutError, 'execution expired'
91
+ end
31
92
  end
32
93
  end
@@ -8,6 +8,11 @@ require 'termtter/system_extensions/windows' if win?
8
8
  require 'termtter/system_extensions/core_compatibles'
9
9
  require 'termtter/system_extensions/termtter_compatibles'
10
10
 
11
+ unless Readline.const_defined?(:NATIVE_REFRESH_LINE_METHOD)
12
+ # Latest 'readline.so' has native 'refresh_line' method.
13
+ Readline::NATIVE_REFRESH_LINE_METHOD = Readline.respond_to?(:refresh_line)
14
+ end
15
+
11
16
  require 'dl/import'
12
17
  module Readline
13
18
  begin
@@ -18,18 +23,29 @@ module Readline
18
23
  extend DL::Importer
19
24
  end
20
25
  pathes = Array(ENV['TERMTTER_EXT_LIB'] || [
26
+ '/usr/lib64/libreadline.so',
27
+ '/usr/local/lib64/libreadline.so',
21
28
  '/opt/local/lib/libreadline.dylib',
22
29
  '/usr/lib/libreadline.so',
23
30
  '/usr/local/lib/libreadline.so',
24
31
  File.join(Gem.bindir, 'readline.dll')
25
32
  ])
26
33
  dlload(pathes.find { |path| File.exist?(path)})
27
- extern 'int rl_refresh_line(int, int)'
34
+ extern 'int rl_parse_and_bind (char *)'
28
35
  end
29
- def self.refresh_line
30
- LIBREADLINE.rl_refresh_line(0, 0)
36
+ def self.rl_parse_and_bind(str)
37
+ LIBREADLINE.rl_parse_and_bind(str.to_s)
38
+ end
39
+ unless Readline::NATIVE_REFRESH_LINE_METHOD
40
+ module LIBREADLINE
41
+ extern 'int rl_refresh_line(int, int)'
42
+ end
43
+ def self.refresh_line
44
+ LIBREADLINE.rl_refresh_line(0, 0)
45
+ end
31
46
  end
32
47
  rescue Exception
48
+ def self.rl_parse_and_bind(str);end
33
49
  def self.refresh_line;end
34
50
  end
35
51
  end
@@ -45,13 +61,28 @@ def create_highline
45
61
  end
46
62
 
47
63
  def open_browser(url)
48
- case RUBY_PLATFORM
49
- when /linux/
50
- system 'firefox', url
51
- when /mswin(?!ce)|mingw|bccwin/
52
- system 'explorer', url
64
+ if ENV['KDE_FULL_SESSION'] == 'true'
65
+ system 'kfmclient', 'exec', url
66
+ elsif ENV['GNOME_DESKTOP_SESSION_ID']
67
+ system 'gnome-open', url
68
+ elsif !(/not found/ =~ `which exo-open`)
69
+ # FIXME: is fungible system('exo-open').nil? for lambda {...}
70
+ system 'exo-open', url
53
71
  else
54
- system 'open', url
72
+ case RUBY_PLATFORM.downcase
73
+ when /linux/
74
+ system 'firefox', url
75
+ when /darwin/
76
+ system 'open', url
77
+ when /mswin(?!ce)|mingw|bccwin/
78
+ system 'start', url
79
+ else
80
+ system 'firefox', url
81
+ end
55
82
  end
56
83
  end
57
84
 
85
+ if Readline.respond_to?(:input=)
86
+ # temporary measure for Readline stops other threads problem.
87
+ Readline.input = STDIN
88
+ end
data/lib/termtter/task.rb CHANGED
@@ -11,7 +11,8 @@ module Termtter
11
11
  @work = true
12
12
  end
13
13
  def execute
14
- exec_proc.call(self) if work
14
+ args = if exec_proc.arity.zero? then [] else [self] end
15
+ exec_proc.call(*args) if work
15
16
  end
16
17
  end
17
18
  end
@@ -2,6 +2,7 @@ require File.dirname(__FILE__) + '/../../spec_helper'
2
2
 
3
3
  describe 'plugin hashtag' do
4
4
  before do
5
+ Termtter::Client.clear_hooks
5
6
  Termtter::Client.setup_task_manager
6
7
  Termtter::Client.plug 'defaults'
7
8
  end
@@ -12,30 +13,30 @@ describe 'plugin hashtag' do
12
13
  end
13
14
 
14
15
  it 'should add hashtag "test"' do
15
- Termtter::Client.call_commands('hashtag add test')
16
- Termtter::Client.public_storage[:hashtags].should == Set.new('#test')
16
+ Termtter::Client.execute('hashtag add test')
17
+ Termtter::Client.public_storage[:hashtags].should == Set.new(['#test'])
17
18
  end
18
19
 
19
20
  it 'should add hashtag "#test"' do
20
- Termtter::Client.call_commands('hashtag add #test')
21
- Termtter::Client.public_storage[:hashtags].should == Set.new('#test')
21
+ Termtter::Client.execute('hashtag add #test')
22
+ Termtter::Client.public_storage[:hashtags].should == Set.new(['#test'])
22
23
  end
23
24
 
24
25
  it 'should add hashtags "foo", "bar"' do
25
- Termtter::Client.call_commands('hashtag add foo bar')
26
+ Termtter::Client.execute('hashtag add foo bar')
26
27
  Termtter::Client.public_storage[:hashtags].should == Set.new(["#foo", "#bar"])
27
28
  end
28
29
  end
29
30
 
30
31
  describe 'spec for hook of hashtag' do
31
32
  before do
32
- Termtter::Client.call_commands('hashtag add foo bar')
33
+ Termtter::Client.execute('hashtag add foo bar')
33
34
  end
34
35
 
35
36
  it 'add hashtags as args of command' do
36
37
  @update_command = Termtter::Client.commands[:update]
37
38
  @update_command.exec_proc.should_receive(:call).with("test #foo #bar")
38
- Termtter::Client.call_commands('update test')
39
+ Termtter::Client.execute('update test')
39
40
  end
40
41
  end
41
42
  end
@@ -0,0 +1,33 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+
3
+ describe 'plugin lists' do
4
+ before do
5
+ Termtter::Client.setup_task_manager
6
+ config.user_name = 'jugyo'
7
+ @twitter_stub = Object.new
8
+ Termtter::API.stub!(:twitter).and_return(@twitter_stub)
9
+ end
10
+
11
+ describe 'command list' do
12
+ before do
13
+ Termtter::Client.plug 'defaults'
14
+ @command = Termtter::Client.commands[:list]
15
+ end
16
+
17
+ it 'command name is :lists' do
18
+ @command.name.should == :list
19
+ end
20
+
21
+ it 'should call with no user_name' do
22
+ response = []
23
+ @twitter_stub.should_receive(:home_timeline).and_return(response)
24
+ Termtter::Client.execute('list')
25
+ end
26
+
27
+ it 'should call with user_name' do
28
+ response = []
29
+ @twitter_stub.should_receive(:user_timeline).with('termtter', {}).and_return(response)
30
+ Termtter::Client.execute('list termtter')
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,17 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+
3
+ describe 'plugin hashtag' do
4
+ before do
5
+ Termtter::Client.clear_hooks
6
+ Termtter::Client.setup_task_manager
7
+ Termtter::Client.plug 'defaults'
8
+ end
9
+
10
+ it 'should search plugin file' do
11
+ Termtter::Client.search_plugin_file('plugin').should ==
12
+ File.expand_path(File.join(File.dirname(__FILE__), '../../..//lib/plugins/defaults/plugin.rb'))
13
+ end
14
+
15
+ # TODO: more specs...
16
+ end
17
+
@@ -0,0 +1,205 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ require File.dirname(__FILE__) + '/../../spec_helper'
4
+
5
+ describe 'Termtter::Client.post_retweet' do
6
+ describe 'posts a retweet based on the given post by someone,' do
7
+ completely_same_as_the_following = lambda {
8
+ Termtter::Client.plug 'defaults/retweet'
9
+
10
+ mock = Object.new
11
+ def mock.user
12
+ mock2 = Object.new
13
+ def mock2.protected
14
+ true
15
+ end
16
+
17
+ def mock2.screen_name
18
+ 'ujihisa'
19
+ end
20
+ mock2
21
+ end
22
+
23
+ def mock.text
24
+ 'hi'
25
+ end
26
+
27
+ mock3 = Object.new
28
+ def mock3.update(text)
29
+ text.should == 'my comment RT @ujihisa: hi'
30
+ end
31
+ def mock3.retweet(id)
32
+ id
33
+ end
34
+
35
+ Termtter::API.should_receive(:twitter).and_return(mock3)
36
+ Termtter::Client.
37
+ should_receive(:confirm).
38
+ with('ujihisa is protected! Are you sure?', false).
39
+ and_return true
40
+ be_quiet do
41
+ Termtter::Client.post_retweet(mock, 'my comment')
42
+ end
43
+ }
44
+
45
+ describe 'with your own comment,' do
46
+ it 'and without confirming in the original post being not protected' do
47
+ Termtter::Client.plug 'defaults/retweet'
48
+
49
+ mock = Object.new
50
+ def mock.user
51
+ mock2 = Object.new
52
+ def mock2.protected
53
+ false
54
+ end
55
+
56
+ def mock2.screen_name
57
+ 'ujihisa'
58
+ end
59
+ mock2
60
+ end
61
+
62
+ def mock.text
63
+ 'hi'
64
+ end
65
+
66
+ mock3 = Object.new
67
+ def mock3.update(text)
68
+ text.should == 'my comment RT @ujihisa: hi'
69
+ end
70
+
71
+ Termtter::API.should_receive(:twitter).and_return(mock3)
72
+ be_quiet do
73
+ Termtter::Client.post_retweet(mock, 'my comment')
74
+ end
75
+ end
76
+
77
+ it 'and use QT if config.plugins.retweet.quotetweet is true' do
78
+ config.plugins.retweet.quotetweet = true
79
+ Termtter::Client.plug 'defaults/retweet'
80
+
81
+ mock = Object.new
82
+ def mock.user
83
+ mock2 = Object.new
84
+ def mock2.protected
85
+ false
86
+ end
87
+
88
+ def mock2.screen_name
89
+ 'ujihisa'
90
+ end
91
+ mock2
92
+ end
93
+
94
+ def mock.text
95
+ 'hi'
96
+ end
97
+
98
+ mock3 = Object.new
99
+ def mock3.update(text)
100
+ text.should == 'my comment QT @ujihisa: hi'
101
+ end
102
+
103
+ Termtter::API.should_receive(:twitter).and_return(mock3)
104
+ be_quiet do
105
+ Termtter::Client.post_retweet(mock, 'my comment')
106
+ end
107
+ config.plugins.retweet.quotetweet = false
108
+ end
109
+
110
+
111
+ it 'and with confirming in the original post being protected' do
112
+ completely_same_as_the_following.call
113
+ end
114
+ end
115
+
116
+ describe 'without your own comment,' do
117
+ it 'and without confirming in the original post being not protected' do
118
+ Termtter::Client.plug 'defaults/retweet'
119
+
120
+ mock = Object.new
121
+ def mock.user
122
+ mock2 = Object.new
123
+ def mock2.protected
124
+ false
125
+ end
126
+
127
+ def mock2.screen_name
128
+ 'ujihisa'
129
+ end
130
+ mock2
131
+ end
132
+
133
+ def mock.text
134
+ 'hi'
135
+ end
136
+
137
+ def mock.id
138
+ 123
139
+ end
140
+
141
+ mock3 = Object.new
142
+ def mock3.retweet(id)
143
+ id.should == 123
144
+ end
145
+
146
+ Termtter::API.should_receive(:twitter).and_return(mock3)
147
+ be_quiet do
148
+ Termtter::Client.post_retweet(mock)
149
+ end
150
+ end
151
+
152
+ it 'and don\'t use QT if config.plugins.retweet.quotetweet is true' do
153
+ config.plugins.retweet.quotetweet = true
154
+ Termtter::Client.plug 'defaults/retweet'
155
+
156
+ mock = Object.new
157
+ def mock.user
158
+ mock2 = Object.new
159
+ def mock2.protected
160
+ false
161
+ end
162
+
163
+ def mock2.screen_name
164
+ 'ujihisa'
165
+ end
166
+ mock2
167
+ end
168
+
169
+ def mock.text
170
+ 'hi'
171
+ end
172
+
173
+ def mock.id
174
+ 123
175
+ end
176
+
177
+ mock3 = Object.new
178
+ def mock3.retweet(id)
179
+ id.should == 123
180
+ end
181
+
182
+ def mock3.update(text)
183
+ text.should == 'RT @ujihisa: hi'
184
+ end
185
+
186
+ Termtter::API.should_receive(:twitter).and_return(mock3)
187
+ be_quiet do
188
+ Termtter::Client.post_retweet(mock)
189
+ end
190
+ config.plugins.retweet.quotetweet = false
191
+ end
192
+
193
+ it 'and with confirming in the original post being protected' do
194
+ completely_same_as_the_following.call
195
+ end
196
+ end
197
+ end
198
+ end
199
+
200
+ describe 'Plugin `retweet`' do
201
+ it 'registers a commond when it is loaded' do
202
+ Termtter::Client.should_receive(:register_command).at_least(5).times
203
+ Termtter::Client.plug 'defaults/retweet'
204
+ end
205
+ end