termtter 1.9.0 → 1.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (116) hide show
  1. data/Rakefile +15 -15
  2. data/VERSION +1 -1
  3. data/lib/plugins/appendtitle.rb +39 -19
  4. data/lib/plugins/basic.rb +25 -0
  5. data/lib/plugins/capture.rb +22 -0
  6. data/lib/plugins/channel.rb +4 -5
  7. data/lib/plugins/copy.rb +32 -0
  8. data/lib/plugins/defaults/auto_reload.rb +4 -0
  9. data/lib/plugins/defaults/cache.rb +2 -2
  10. data/lib/plugins/defaults/command_line.rb +21 -0
  11. data/lib/plugins/defaults/keyword.rb +58 -8
  12. data/lib/plugins/defaults/list.rb +6 -4
  13. data/lib/plugins/defaults/retweet.rb +6 -1
  14. data/lib/plugins/defaults/standard_commands.rb +54 -27
  15. data/lib/plugins/defaults/stdout.rb +8 -4
  16. data/lib/plugins/defaults/switch.rb +21 -9
  17. data/lib/plugins/encoding.rb +46 -0
  18. data/lib/plugins/english.rb +2 -1
  19. data/lib/plugins/friends.rb +5 -8
  20. data/lib/plugins/geo.rb +21 -0
  21. data/lib/plugins/http_server.rb +10 -3
  22. data/lib/plugins/http_server/{index.html → skin/default/index.html} +0 -0
  23. data/lib/plugins/http_server/skin/miku/cui/chara/miku/images/001.png +0 -0
  24. data/lib/plugins/http_server/skin/miku/cui/chara/miku/images/002.png +0 -0
  25. data/lib/plugins/http_server/skin/miku/cui/chara/miku/images/003.png +0 -0
  26. data/lib/plugins/http_server/skin/miku/cui/chara/miku/images/004.png +0 -0
  27. data/lib/plugins/http_server/skin/miku/cui/chara/miku/script.js +66 -0
  28. data/lib/plugins/http_server/skin/miku/cui/core.js +198 -0
  29. data/lib/plugins/http_server/skin/miku/cui/util.js +65 -0
  30. data/lib/plugins/http_server/skin/miku/index.html +142 -0
  31. data/lib/plugins/http_server/skin/miku/jquery-1.4.2.min.js +154 -0
  32. data/lib/plugins/http_server/skin/sample1/index.html +165 -0
  33. data/lib/plugins/im_kayac.rb +33 -0
  34. data/lib/plugins/irc_gw.rb +53 -17
  35. data/lib/plugins/mongo.rb +112 -0
  36. data/lib/plugins/reply_sound.rb +1 -1
  37. data/lib/plugins/say.rb +2 -1
  38. data/lib/plugins/searchline.rb +1 -1
  39. data/lib/plugins/short_logger.rb +15 -0
  40. data/lib/plugins/storage.rb +1 -1
  41. data/lib/plugins/storage/status.rb +1 -1
  42. data/lib/plugins/stream.rb +1 -3
  43. data/lib/plugins/suspend.rb +9 -0
  44. data/lib/plugins/time_signal.rb +1 -1
  45. data/lib/plugins/tinyurl.rb +17 -8
  46. data/lib/plugins/truncate.rb +13 -3
  47. data/lib/plugins/update_editor.rb +1 -1
  48. data/lib/plugins/url.rb +11 -0
  49. data/lib/plugins/user_stream.rb +76 -51
  50. data/lib/termtter.rb +1 -0
  51. data/lib/termtter/active_rubytter.rb +3 -6
  52. data/lib/termtter/api.rb +18 -8
  53. data/lib/termtter/client.rb +30 -18
  54. data/lib/termtter/command.rb +22 -13
  55. data/lib/termtter/config.rb +15 -4
  56. data/lib/termtter/config_setup.rb +17 -2
  57. data/lib/termtter/config_template.erb +1 -0
  58. data/lib/termtter/default_config.rb +3 -1
  59. data/lib/termtter/hookable.rb +3 -3
  60. data/lib/termtter/httppool.rb +10 -4
  61. data/lib/termtter/memory_cache.rb +1 -1
  62. data/lib/termtter/optparse.rb +0 -4
  63. data/lib/termtter/rubytter_proxy.rb +31 -11
  64. data/lib/termtter/system_extensions.rb +9 -1
  65. data/lib/termtter/system_extensions/core_compatibles.rb +9 -2
  66. data/lib/termtter/task_manager.rb +2 -0
  67. data/spec/plugins/capital_update_spec.rb +1 -1
  68. data/spec/plugins/cool_spec.rb +1 -1
  69. data/spec/plugins/curry_spec.rb +1 -1
  70. data/spec/plugins/db_spec.rb +1 -1
  71. data/spec/plugins/defaults/hashtag_spec.rb +1 -1
  72. data/spec/plugins/defaults/list_spec.rb +1 -1
  73. data/spec/plugins/defaults/plugin_spec.rb +1 -1
  74. data/spec/plugins/defaults/retweet_spec.rb +1 -1
  75. data/spec/plugins/draft_spec.rb +1 -1
  76. data/spec/plugins/english_spec_.rb +1 -1
  77. data/spec/plugins/expand-tinyurl_spec.rb +1 -1
  78. data/spec/plugins/fib_spec.rb +1 -1
  79. data/spec/plugins/filter_spec_.rb +1 -1
  80. data/spec/plugins/footer_spec.rb +1 -1
  81. data/spec/plugins/gsub_spec.rb +1 -1
  82. data/spec/plugins/haml_spec.rb +1 -1
  83. data/spec/plugins/hi_spec.rb +1 -1
  84. data/spec/plugins/md5pass_spec.rb +1 -1
  85. data/spec/plugins/pause_spec.rb +1 -1
  86. data/spec/plugins/primes_spec_.rb +1 -1
  87. data/spec/plugins/shell_spec.rb +1 -1
  88. data/spec/plugins/sl_spec_.rb +1 -1
  89. data/spec/plugins/spam_spec.rb +1 -1
  90. data/spec/plugins/standard_commands_spec.rb +1 -1
  91. data/spec/plugins/storage/sqlite3_spec.rb +2 -2
  92. data/spec/plugins/storage/status_spec_.rb +2 -2
  93. data/spec/plugins/tinyurl_spec.rb +1 -1
  94. data/spec/plugins/truncate_spec.rb +36 -1
  95. data/spec/plugins/whois_spec_.rb +1 -1
  96. data/spec/termtter/active_rubytter_spec.rb +1 -1
  97. data/spec/termtter/api_spec.rb +1 -1
  98. data/spec/termtter/client_spec.rb +1 -1
  99. data/spec/termtter/command_spec.rb +1 -1
  100. data/spec/termtter/config_setup_spec.rb +1 -1
  101. data/spec/termtter/config_spec.rb +9 -1
  102. data/spec/termtter/crypt_spec.rb +1 -1
  103. data/spec/termtter/event_spec.rb +1 -1
  104. data/spec/termtter/hook_spec.rb +1 -1
  105. data/spec/termtter/hookable_spec.rb +1 -1
  106. data/spec/termtter/memory_cache_spec.rb +1 -1
  107. data/spec/termtter/optparse_spec.rb +1 -1
  108. data/spec/termtter/rubytter_proxy_spec.rb +1 -1
  109. data/spec/termtter/system_extensions/windows_spec.rb +1 -1
  110. data/spec/termtter/system_extensions_spec.rb +1 -1
  111. data/spec/termtter/task_manager_spec.rb +1 -1
  112. data/spec/termtter/task_spec.rb +1 -1
  113. data/spec/termtter_spec.rb +1 -1
  114. data/termtter.gemspec +326 -0
  115. metadata +81 -149
  116. data/.gitignore +0 -5
@@ -5,10 +5,20 @@ def multibyte_string(text)
5
5
  end
6
6
 
7
7
  def truncate(text, length = 140, omission = "...")
8
- o = multibyte_string(omission)
9
- l = length - o.length
10
8
  chars = multibyte_string(text)
11
- chars.length > length ? (chars[0...l] + o).pack('U*') : text
9
+ if chars.length > length
10
+ _, text_base, urls =
11
+ text.match(/\A(.*?)((?:\s+#{URI.regexp(%w(http https ftp))}\S*)*)\z/).to_a
12
+ chars_base = multibyte_string(text_base)
13
+ o = multibyte_string(omission + urls)
14
+ if o.length >= length
15
+ o = multibyte_string(omission)
16
+ chars_base = chars
17
+ end
18
+ (chars_base[0...(length - o.length)] + o).pack('U*')
19
+ else
20
+ text
21
+ end
12
22
  end
13
23
 
14
24
  Termtter::RubytterProxy.register_hook(
@@ -34,7 +34,7 @@ module Termtter::Client
34
34
  text.split("\n").each do |post|
35
35
  break if post =~ /^__END__$/
36
36
  unless post.empty?
37
- Termtter::API.twitter.update(post)
37
+ execute("update #{post}")
38
38
  puts "=> #{post}"
39
39
  end
40
40
  end
@@ -0,0 +1,11 @@
1
+ def url_by_tweet(t)
2
+ "http://twitter.com/#{t.user.screen_name}/status/#{t.id}"
3
+ end
4
+
5
+ Termtter::Client.register_command(:name => :url,
6
+ :exec => lambda do |arg|
7
+ t = Termtter::API.twitter.show(arg)
8
+ puts url_by_tweet(t)
9
+ end)
10
+
11
+
@@ -1,3 +1,53 @@
1
+ module Termtter
2
+ class UserStreamReceiver
3
+
4
+ def run(&block)
5
+ loop {
6
+ begin
7
+ self.process &block
8
+ rescue => error
9
+ Termtter::Client.handle_error error
10
+ sleep 10
11
+ end
12
+ }
13
+ end
14
+
15
+ def self.repack_error(error, chunk)
16
+ new_error = error.class.new("#{error.message} (#{JSON.parse(chunk).inspect})")
17
+ error.instance_variables.each{ |v|
18
+ new_error.instance_variable_set(v, error.instance_variable_get(v))
19
+ }
20
+ new_error
21
+ rescue
22
+ error
23
+ end
24
+
25
+ protected
26
+ ENDPOINT = URI.parse('https://userstream.twitter.com/2/user.json')
27
+
28
+ def process(&block)
29
+ Termtter::Client.logger.info("connecting to UserStream")
30
+ https = Net::HTTP.new(ENDPOINT.host, ENDPOINT.port)
31
+ https.use_ssl = true
32
+ https.verify_mode = OpenSSL::SSL::VERIFY_NONE
33
+
34
+ https.start{ |https|
35
+ request = Net::HTTP::Get.new(ENDPOINT.request_uri)
36
+ request.oauth!(https, Termtter::API.twitter.access_token.consumer, Termtter::API.twitter.access_token)
37
+ https.request(request){ |response|
38
+ raise StandardError, response.code.to_i unless response.code.to_i == 200
39
+ raise StandardError, 'Response is not chuncked' unless response.chunked?
40
+ Termtter::Client.logger.info("connected to UserStream")
41
+ response.read_body{ |chunk|
42
+ Termtter::Client.logger.debug("received: #{chunk}")
43
+ yield chunk
44
+ }
45
+ }
46
+ }
47
+ end
48
+ end
49
+ end
50
+
1
51
  module Termtter::Client
2
52
  register_command(:"user_stream stop", :help => 'user_stream stop') do |arg|
3
53
  logger.info 'stopping user stream'
@@ -5,11 +55,13 @@ module Termtter::Client
5
55
  @user_stream_thread.exit
6
56
  end
7
57
  @user_stream_thread = nil
58
+ plug "defaults/auto_reload"
8
59
  end
9
60
 
10
61
  handle_chunk = lambda { |chunk|
11
62
  data = Termtter::ActiveRubytter.new(JSON.parse(chunk)) rescue return
12
63
  Termtter::Client.logger.debug "user_stream: received #{JSON.parse(chunk).inspect}"
64
+ Termtter::Client.clear_line
13
65
  begin
14
66
  if data[:event]
15
67
  if /list_/ =~ data[:event]
@@ -34,79 +86,52 @@ module Termtter::Client
34
86
  elsif data[:friends]
35
87
  puts "You have #{data[:friends].length} friends."
36
88
  elsif data[:delete]
37
- status = Termtter::API.twitter.safe.show(data.delete.status.id)
38
- puts "#{status.user.screen_name} deleted: #{status.text}"
89
+ # status = Termtter::API.twitter.cached_status(data.delete.status.id)
90
+ # puts "#{status.user.screen_name} deleted: #{status.text}"
91
+ elsif data[:direct_message]
92
+ dm = data[:direct_message]
93
+ sender = dm.sender
94
+ text = dm.text
95
+ puts "[DM] #{sender.screen_name}: #{text}"
39
96
  else
40
- output([data], Termtter::Event.new(:update_friends_timeline, :type => :main))
41
97
  Termtter::API.twitter.store_status_cache(data)
98
+ output([data], :update_friends_timeline)
42
99
  end
43
- rescue Termtter::RubytterProxy::FrequentAccessError
44
- # ignore
45
100
  rescue Timeout::Error, StandardError => error
46
- new_error = error.class.new("#{error.message} (#{JSON.parse(chunk).inspect})")
47
- error.instance_variables.each{ |v|
48
- new_error.instance_variable_set(v, error.instance_variable_get(v))
49
- }
50
- handle_error new_error
101
+ Termtter::Client.handle_error Termtter::UserStreamReceiver.repack_error(error, chunk)
102
+ ensure
103
+ Readline.refresh_line
51
104
  end
52
105
  }
53
106
 
54
107
  register_command(:"user_stream", :help => 'user_stream') do |arg|
55
- unless config.password.kind_of? String
56
- raise "config.password is required."
57
- end
58
-
59
- uri = URI.parse('http://betastream.twitter.com/2b/user.json')
60
-
61
- unless @user_stream_thread
62
- logger.info 'checking API status'
63
- 1.times{ # to use break
64
- Net::HTTP.start(uri.host, uri.port){ |http|
65
- request = Net::HTTP::Get.new(uri.request_uri)
66
- request.basic_auth(config.user_name, config.password)
67
- http.request(request){ |response|
68
- raise response.code.to_i unless response.code.to_i == 200
69
- break
70
- }
71
- }
72
- }
73
- logger.info 'API seems working'
74
- end
75
108
 
76
109
  execute('user_stream stop') if @user_stream_thread
77
110
  delete_task(:auto_reload)
78
111
 
79
112
  @user_stream_thread = Thread.new {
80
- loop do
81
- begin
82
- logger.info 'connecting to user stream'
83
- Net::HTTP.start(uri.host, uri.port){ |http|
84
- request = Net::HTTP::Get.new(uri.request_uri)
85
- request.basic_auth(config.user_name, config.password)
86
- http.request(request){ |response|
87
- raise response.code.to_i unless response.code.to_i == 200
88
- raise 'Response is not chuncked' unless response.chunked?
89
- response.read_body{ |chunk|
90
- handle_chunk.call(chunk)
91
- }
92
- }
93
- }
94
- rescue Timeout::Error, StandardError => e
95
- handle_error e
96
- logger.info 'sleeping'
97
- sleep 10
98
- end
99
- end
113
+ Termtter::UserStreamReceiver.new.run{|chunk|
114
+ call_hooks(:user_stream_receive, chunk)
115
+ }
100
116
  }
101
117
  end
102
118
 
103
119
  register_hook(
104
120
  :name => :user_stream_init,
105
- :author => '?', # FIXME
106
121
  :point => :initialize,
107
122
  :exec => lambda {
108
123
  execute('user_stream')
109
124
  })
125
+
126
+ register_hook(
127
+ :name => :user_stream_print,
128
+ :point => :user_stream_receive,
129
+ :exec => lambda {|chunk|
130
+ Thread.new {
131
+ handle_chunk.call(chunk)
132
+ }
133
+ })
134
+
110
135
  end
111
136
 
112
137
  # user_stream.rb
data/lib/termtter.rb CHANGED
@@ -43,6 +43,7 @@ module Termtter
43
43
  OptParser.parse!(ARGV)
44
44
  CONF_DIR = File.expand_path('~/.termtter') unless defined? CONF_DIR
45
45
  CONF_FILE = File.join(Termtter::CONF_DIR, 'config') unless defined? CONF_FILE
46
+ config.token_file = File.join(Termtter::CONF_DIR, config.token_file_name)
46
47
  $:.unshift(CONF_DIR)
47
48
 
48
49
  CONSUMER_KEY = 'eFFLaGJ3M0VMZExvNmtlNHJMVndsQQ=='
@@ -1,6 +1,7 @@
1
1
  # -*- coding: utf-8 -*-
2
- gem 'rubytter', '>= 0.6.5'
3
2
  require 'rubytter'
3
+ raise Gem::LoadError, 'Could not find RubyGem rubytter (>= 0.6.5)' \
4
+ unless Rubytter::VERSION >= '0.6.5'
4
5
 
5
6
  module Termtter
6
7
  class ActiveRubytter
@@ -17,11 +18,7 @@ module Termtter
17
18
  end
18
19
 
19
20
  def method_missing(name, *args)
20
- if @data.key?(name)
21
- return @data[name]
22
- else
23
- super
24
- end
21
+ @data[name]
25
22
  end
26
23
 
27
24
  def attributes=(raw_hash)
data/lib/termtter/api.rb CHANGED
@@ -2,9 +2,9 @@
2
2
 
3
3
  config.set_default(:host, 'api.twitter.com')
4
4
  config.set_default(:oauth_consumer_site, 'http://api.twitter.com')
5
- if ENV.has_key?('HTTP_PROXY')
5
+ if ENV['HTTP_PROXY'] || ENV['http_proxy']
6
6
  require 'uri'
7
- proxy = ENV['HTTP_PROXY']
7
+ proxy = ENV['HTTP_PROXY'] || ENV['http_proxy']
8
8
  proxy = "http://" + proxy if proxy !~ /^http:\/\//
9
9
  u = URI.parse(proxy)
10
10
  config.proxy.set_default(:host, u.host)
@@ -50,12 +50,12 @@ module Termtter
50
50
  end
51
51
 
52
52
  def authorize_by_oauth(show_information=false, save_to_token_file=true, put_to_config=true, verbose=true)
53
- puts '1. Contacting to twitter...' if verbose
53
+ puts '1. Connecting to twitter...' if verbose
54
54
 
55
55
  request_token = consumer.get_request_token
56
56
 
57
- puts '2. URL for authorize: ' + request_token.authorize_url if verbose
58
- puts ' Opening web page to authorization...' if verbose
57
+ puts '2. Authorization URL: ' + request_token.authorize_url if verbose
58
+ puts ' Opening authorization web page...' if verbose
59
59
 
60
60
  begin
61
61
  open_browser(request_token.authorize_url)
@@ -67,7 +67,7 @@ module Termtter
67
67
  ui = create_highline
68
68
  pin = ui.ask('3. Enter PIN: ')
69
69
  puts ""
70
- puts "4. Getting access_token..."
70
+ puts "4. Fetching access_token..."
71
71
  access_token = request_token.get_access_token(:oauth_verifier => pin)
72
72
 
73
73
  if put_to_config
@@ -83,7 +83,7 @@ module Termtter
83
83
  end
84
84
  end
85
85
 
86
- puts "Authorize is successfully done."
86
+ puts "Authorization successfully completed."
87
87
 
88
88
  return {:token => access_token.token,
89
89
  :secret => access_token.secret}
@@ -94,7 +94,7 @@ module Termtter
94
94
  Termtter::Crypt.decrypt(CONSUMER_KEY),
95
95
  Termtter::Crypt.decrypt(CONSUMER_SECRET),
96
96
  :site => config.oauth_consumer_site,
97
- :proxy => ENV['http_proxy']
97
+ :proxy => proxy_string
98
98
  )
99
99
  end
100
100
 
@@ -115,6 +115,16 @@ module Termtter
115
115
  :proxy_password => config.proxy.password
116
116
  }
117
117
  end
118
+
119
+ def proxy_string
120
+ return unless config.proxy.host
121
+ if config.proxy.user_name && config.proxy.password
122
+ "http://#{config.proxy.user_name}:#{config.proxy.password}@#{config.proxy.host}:#{config.proxy.port}"
123
+ else
124
+ "http://#{config.proxy.host}:#{config.proxy.port}"
125
+ end
126
+ end
127
+
118
128
  end
119
129
  end
120
130
  end
@@ -19,7 +19,8 @@ module Termtter
19
19
  class << self
20
20
  attr_reader :commands, :logger, :task_manager
21
21
 
22
- # plug :: Name -> (Hash) -> IO () where NAME = String | Symbol | [NAME]
22
+ # call-seq:
23
+ # plug :: Name -> (Hash) -> IO () where NAME = String | Symbol | [NAME]
23
24
  def plug(name, options = {})
24
25
  if Array === name # Obviously `name.respond_to?(:each)` is better, but for 1.8.6 compatibility we cannot.
25
26
  name.each {|i| plug(i, options) }
@@ -116,7 +117,7 @@ module Termtter
116
117
  # :text => status,
117
118
  # :original_data => original data,
118
119
  # }
119
- def output(statuses, event)
120
+ def output(statuses, event = :default)
120
121
  return if statuses.nil? || statuses.empty?
121
122
  event = Termtter::Event.new(event) unless event.kind_of? Termtter::Event
122
123
 
@@ -132,7 +133,7 @@ module Termtter
132
133
 
133
134
  call_hooks(:post_filter, filtered, event)
134
135
  get_hooks(:output).each do |hook|
135
- Termtter::Client.logger.debug "output: call hook :output #{hook.inspect}"
136
+ Termtter::Client.logger.debug { "output: call hook :output #{hook.inspect}" }
136
137
  hook.call(
137
138
  apply_filters_for_hook(:"filter_for_#{hook.name}", filtered, event),
138
139
  event)
@@ -232,6 +233,10 @@ module Termtter
232
233
  ConfigSetup.run
233
234
  end
234
235
  load Termtter::CONF_FILE
236
+ unless config.dmsg_permission
237
+ require 'termtter/config_setup'
238
+ ConfigSetup.reauth
239
+ end
235
240
  end
236
241
 
237
242
  def legacy_config_support
@@ -261,6 +266,7 @@ module Termtter
261
266
  def setup_logger
262
267
  @logger = config.logger || default_logger
263
268
  @logger.level = config.devel ? Logger::DEBUG : Logger::INFO
269
+ call_hooks(:post_setup_logger)
264
270
  @logger
265
271
  end
266
272
 
@@ -314,24 +320,20 @@ module Termtter
314
320
 
315
321
  def run
316
322
  parse_options
317
- config.__freeze__(:user_name) unless config.user_name.empty?
318
- show_splash
323
+ show_splash unless config.system.cmd_mode
319
324
  setup_task_manager
320
325
  load_config
321
326
  load_plugins
322
327
  eval_init_block
323
- config.__unfreeze__(:user_name)
324
- Termtter::API.setup
325
-
326
- config.system.eval_scripts.each do |script|
327
- begin
328
- eval script
329
- rescue Exception => e
330
- handle_error(e)
331
- end
328
+ begin
329
+ Termtter::API.setup
330
+ rescue Rubytter::APIError => e
331
+ handle_error(e)
332
+ exit!
332
333
  end
333
334
 
334
- config.system.run_commands.each {|cmd| execute(cmd) }
335
+ config.system.eval_scripts.each {|script| rescue_error { eval script }}
336
+ config.system.run_commands.each {|cmd| rescue_error { execute(cmd) }}
335
337
 
336
338
  unless config.system.cmd_mode
337
339
  @task_manager.run()
@@ -344,13 +346,23 @@ module Termtter
344
346
  end
345
347
 
346
348
  def handle_error(e)
347
- message = e.message
348
- message = message[0..29] + " ..." if message.size >= 30 && !config.devel
349
- logger.error("#{e.class.to_s}: #{message}")
349
+ logger.error("#{e.class.to_s}: #{e.message}")
350
350
  logger.error(e.backtrace.join("\n")) if (e.backtrace and config.devel)
351
351
  get_hooks(:on_error).each {|hook| hook.call(e) }
352
352
  end
353
353
 
354
+ def rescue_error
355
+ begin
356
+ yield
357
+ rescue Exception => e
358
+ handle_error(e)
359
+ end
360
+ end
361
+
362
+ def clear_line
363
+ print "\e[0G" + "\e[K" unless win?
364
+ end
365
+
354
366
  def confirm(message, default_yes = true, &block)
355
367
  pause # TODO: TaskManager から呼ばれるならこれいらないなぁ
356
368
 
@@ -28,7 +28,8 @@ module Termtter
28
28
  set cfg
29
29
  end
30
30
 
31
- # set :: Hash -> ()
31
+ # call-seq:
32
+ # set :: Hash -> ()
32
33
  def set(cfg)
33
34
  self.name = cfg[:name].to_sym
34
35
  self.aliases = cfg[:aliases]
@@ -38,7 +39,8 @@ module Termtter
38
39
  self.author = cfg[:author]
39
40
  end
40
41
 
41
- # complement :: String -> [String]
42
+ # call-seq:
43
+ # complement :: String -> [String]
42
44
  def complement(input)
43
45
  input = input.sub(/^\s*/, '')
44
46
  if match?(input) && input =~ /^[^\s]+\s/
@@ -53,7 +55,8 @@ module Termtter
53
55
  end
54
56
  end
55
57
 
56
- # call :: ???
58
+ # call-seq:
59
+ # call :: ???
57
60
  def call(cmd = nil, arg = nil, original_text = nil)
58
61
  from = Time.now
59
62
  arg = case arg
@@ -64,27 +67,30 @@ module Termtter
64
67
  else
65
68
  raise ArgumentError, 'arg should be String or nil'
66
69
  end
67
- Termtter::Client.logger.debug "command: #{cmd} #{arg}"
70
+ Termtter::Client.logger.debug { "command: #{cmd} #{arg}" }
68
71
  result = exec_proc.call(arg)
69
- Termtter::Client.logger.debug "command: #{cmd} #{arg} #{'%.2fsec' % (Time.now - from)}"
72
+ Termtter::Client.logger.debug { "command: #{cmd} #{arg} #{'%.2fsec' % (Time.now - from)}" }
70
73
  result
71
74
  rescue => e
72
- Termtter::Client.logger.debug "command: #{cmd} #{arg} #{e.message} #{'%.2fsec' % (Time.now - from)}"
75
+ Termtter::Client.logger.debug { "command: #{cmd} #{arg} #{e.message} #{'%.2fsec' % (Time.now - from)}" }
73
76
  raise e
74
77
  end
75
78
 
76
- # match? :: String -> Boolean
79
+ # call-seq:
80
+ # match? :: String -> Boolean
77
81
  def match?(input)
78
82
  !!pattern.match(input)
79
83
  end
80
84
 
81
- # pattern :: Regexp
85
+ # call-seq:
86
+ # pattern :: Regexp
82
87
  def pattern
83
88
  commands_regex = commands.map {|name| name.to_s.split(' ').map {|i| Regexp.quote(i)}.join('\s+') }.join('|')
84
- /^\s*((#{commands_regex})|(#{commands_regex})\s+(.*?))\s*$/
89
+ /^\s*((#{commands_regex})|(#{commands_regex})(?:\s+|\b)(.*?))\s*$/
85
90
  end
86
91
 
87
- # commands :: [Symbol]
92
+ # call-seq:
93
+ # commands :: [Symbol]
88
94
  def commands
89
95
  [name] + aliases
90
96
  end
@@ -93,12 +99,14 @@ module Termtter
93
99
  @aliases = as.map { |a| a.to_sym }
94
100
  end
95
101
 
96
- # alias= :: Symbol -> ()
102
+ # call-seq:
103
+ # alias= :: Symbol -> ()
97
104
  def alias=(a)
98
105
  self.aliases = [a]
99
106
  end
100
107
 
101
- # author= :: String -> ()
108
+ # call-seq:
109
+ # author= :: String -> ()
102
110
  def author=(a)
103
111
  @author = a
104
112
  end
@@ -107,7 +115,8 @@ module Termtter
107
115
  name.to_s.split(/\s+/)
108
116
  end
109
117
 
110
- # split_command_line :: String -> (String, String)
118
+ # call-seq:
119
+ # split_command_line :: String -> (String, String)
111
120
  def split_command_line(line)
112
121
  m = pattern.match(line)
113
122
  if m