termtter 1.6.0 → 1.7.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.
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