shooting_star 3.2.1 → 3.2.2

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt CHANGED
@@ -1,13 +1,28 @@
1
+ *** 3.2.2 / 2007-08-26
2
+ + 3 major enhancements:
3
+ + Profiling by RubyProf if CONFIG.profile.
4
+ + Serialized event execution.
5
+ + Separated type and phase on connection management.
6
+ + 3 minor enhancements:
7
+ + Added new task meteor_strike:update for updating plugin.
8
+ + Refined a code of reconnection in flash client.
9
+ + Updated chat generator.
10
+ + 2 minor spec changes:
11
+ + Added lib/version.rb.
12
+ + Added :without_logging to shooting_star.yml
13
+ + 1 minor bugfix:
14
+ + Renamed helper module of a chat generator template.
15
+
1
16
  *** 3.2.1 / 2007-08-09
2
- + 1 critical bugfix:
17
+ + 2 critical bugfixes:
3
18
  + Fixed a behaviour of xhr client initialization.
19
+ + Fixed a behaviour of flash client initialization.
4
20
 
5
21
  *** 3.2.0 / 2007-08-09
6
22
  + 3 major enhancements:
7
23
  + Automatic activation/deactivation of flash client.
8
24
  + Ported meteor_strike.swf from CS3 to mtasc.
9
25
  + Heart beat.
10
- - Serialized event execution.
11
26
  + 7 minor enhancements:
12
27
  + shooting_star.yml is processed by ERB.
13
28
  + Updated implementation of observers.
data/Manifest.txt CHANGED
@@ -4,6 +4,7 @@ README.txt
4
4
  Rakefile
5
5
  bin/shooting_star
6
6
  lib/shooting_star.rb
7
+ lib/shooting_star/version.rb
7
8
  lib/shooting_star/server.rb
8
9
  lib/shooting_star/shooter.rb
9
10
  lib/shooting_star/channel.rb
@@ -26,9 +27,12 @@ vendor/plugins/meteor_strike/lib
26
27
  vendor/plugins/meteor_strike/lib/meteor_strike.rb
27
28
  vendor/plugins/meteor_strike/lib/meteor_strike
28
29
  vendor/plugins/meteor_strike/lib/meteor_strike/helper.rb
30
+ vendor/plugins/meteor_strike/lib/meteor_strike/controller.rb
29
31
  vendor/plugins/meteor_strike/views
30
32
  vendor/plugins/meteor_strike/views/xhr.rhtml
31
33
  vendor/plugins/meteor_strike/views/flash.rhtml
34
+ vendor/plugins/meteor_strike/tasks
35
+ vendor/plugins/meteor_strike/tasks/meteor_strike.rake
32
36
  vendor/plugins/meteor_strike/test
33
37
  vendor/plugins/meteor_strike/test/meteor_strike_test.rb
34
38
  vendor/plugins/meteor_strike/generators
data/Rakefile CHANGED
@@ -10,9 +10,10 @@ namespace :gem do
10
10
 
11
11
  $: << './lib'
12
12
  $: << './ext'
13
- require 'shooting_star'
13
+ require 'shooting_star/version'
14
14
 
15
15
  Hoe.new('shooting_star', ShootingStar::VERSION) do |hoe|
16
+
16
17
  hoe.author = 'Genki Takiuchi'
17
18
  hoe.email = 'takiuchi@drecom.co.jp'
18
19
  hoe.description = 'Comet server.'
@@ -69,3 +70,20 @@ file 'public/meteor_strike.swf' => 'as/meteor_strike.as' do
69
70
  '-swf public/meteor_strike.swf',
70
71
  '-main as/meteor_strike.as'].join(' ')
71
72
  end
73
+
74
+ desc 'making ext'
75
+ task :ext => 'ext/asteroid.so'
76
+
77
+ file 'ext/asteroid.so' => 'ext/Makefile' do
78
+ cwd = `pwd`
79
+ cd 'ext/'
80
+ sh 'make'
81
+ cd cwd.chomp
82
+ end
83
+
84
+ file 'ext/Makefile' => 'ext/extconf.rb' do
85
+ cwd = `pwd`
86
+ cd 'ext/'
87
+ sh 'ruby extconf.rb'
88
+ cd cwd.chomp
89
+ end
data/ext/asteroid.c CHANGED
@@ -9,6 +9,7 @@
9
9
  #include <errno.h>
10
10
  #include <sys/types.h>
11
11
  #include <sys/socket.h>
12
+ #include <sys/time.h>
12
13
  #include <netinet/in.h>
13
14
  #include <arpa/inet.h>
14
15
  #include "extconf.h"
@@ -21,7 +22,6 @@
21
22
  * -------------------------------------------------------------------------- */
22
23
  #ifdef HAVE_SYS_EVENT_H
23
24
  #include <sys/event.h>
24
- #include <sys/time.h>
25
25
  typedef int asteroid_pollfd_t;
26
26
  typedef struct kevent asteroid_poll_event_t;
27
27
  #endif
@@ -137,6 +137,7 @@ void Init_asteroid(){
137
137
  Asteroid = rb_define_module("Asteroid");
138
138
  rb_define_singleton_method(Asteroid, "run", asteroid_s_run, 3);
139
139
  rb_define_singleton_method(Asteroid, "stop", asteroid_s_stop, 0);
140
+ rb_define_singleton_method(Asteroid, "now", asteroid_s_now, 0);
140
141
  rb_define_class_variable(Asteroid, "@@clients", clients = rb_hash_new());
141
142
  }
142
143
 
@@ -204,12 +205,18 @@ static VALUE asteroid_s_stop(VALUE Self){
204
205
  return Qnil;
205
206
  }
206
207
 
208
+ static VALUE asteroid_s_now(VALUE Self){
209
+ struct timeval now;
210
+ gettimeofday(&now, NULL);
211
+ return rb_float_new(now.tv_sec + now.tv_usec/1000000.0);
212
+ }
213
+
207
214
  static VALUE asteroid_server_send_data(VALUE Self, VALUE Data){
208
215
  VALUE Fd = rb_iv_get(Self, "@fd");
209
216
  int fd = FIX2INT(Fd), remain = RSTRING(Data)->len, len, trial = 100;
210
217
  char *data = StringValuePtr(Data);
211
218
  while(remain){
212
- len = send(fd, data, remain, MSG_NOSIGNAL);
219
+ len = send(fd, data, remain, MSG_DONTWAIT|MSG_NOSIGNAL);
213
220
  if(len == -1){
214
221
  if(errno == EAGAIN && --trial){
215
222
  rb_thread_schedule();
data/ext/asteroid.h CHANGED
@@ -1,4 +1,5 @@
1
1
  static VALUE asteroid_s_run(VALUE Self, VALUE Host, VALUE Port, VALUE Module);
2
2
  static VALUE asteroid_s_stop(VALUE Self);
3
+ static VALUE asteroid_s_now(VALUE Self);
3
4
  static VALUE asteroid_server_send_data(VALUE Self, VALUE Data);
4
5
  static VALUE asteroid_server_write_and_close(VALUE Self);
@@ -10,16 +10,23 @@ module ShootingStar
10
10
  @@channels[path] = self
11
11
  end
12
12
 
13
+ # A message is sent to observers if params includes a :event key.
14
+ # If there are no observers, the message is sent to clients.
15
+ # Others are sent to clients.
13
16
  def transmit(id, params)
17
+ need_event_handling = false
14
18
  if event = params[:event]
15
19
  observers = @@observers.has_key?(@path) ? @@observers[@path].dup : nil
16
20
  observers.each do |name, obs|
17
21
  begin obs.__send__(event, params) if obs.respond_to?(event)
18
22
  rescue Exception; Channel.ignore(@path, name) end
19
23
  end if observers
24
+ need_event_handling = observers.nil? || observers.empty?
20
25
  end
21
- @waiters.each do |signature, server|
22
- server.commit if server.respond(id, params)
26
+ if event.nil? || need_event_handling
27
+ @waiters.each do |signature, server|
28
+ server.commit if server.respond(id, params)
29
+ end
23
30
  end
24
31
  end
25
32
 
@@ -57,17 +57,19 @@ module ShootingStar
57
57
  @channel_path ||= CGI.unescape(channel_path)
58
58
  @query = "channel=#{channel_path}&sig=#{@signature}"
59
59
  @type = @params['__t__']
60
+ @phase = @params['__p__']
60
61
  # process verb
61
62
  if !@type
62
- make_connection(path)
63
+ make_xhr_connection(path)
63
64
  else
64
65
  prepare_channel(@channel_path)
65
66
  @uid = @@uids[@signature] ||= @params['uid']
66
67
  @tag = @@tags[@signature] ||=
67
68
  (@params['tag'] || '').split(',').map{|i| CGI.unescape(i)}
68
- if !@@servers[@signature] && @type != 'rc'
69
+ unless @phase == 'reconnect'
70
+ make_flash_connection if @type == 'flash'
69
71
  notify(:event => :enter, :uid => @uid, :tag => @tag)
70
- log "Connected: #{@uid}"
72
+ log{"Connected: #{@uid}"}
71
73
  end
72
74
  @executing = @@executings[@signature] ||= Hash.new
73
75
  @@servers[@signature] = self
@@ -76,7 +78,7 @@ module ShootingStar
76
78
  rescue MethodNotAcceptable
77
79
  write_and_close
78
80
  rescue Exception => e
79
- log "ERROR: #{e.message}\n#{e.backtrace.join("\n")}\n#{data}"
81
+ log{"ERROR: #{e.message}\n#{e.backtrace.join("\n")}\n#{data}"}
80
82
  write_and_close
81
83
  end
82
84
 
@@ -91,9 +93,9 @@ module ShootingStar
91
93
  @@uids.delete(@signature)
92
94
  @@tags.delete(@signature)
93
95
  @@executings.delete(@signature)
94
- log "Disconnected: #{@uid}:#{@signature}"
96
+ log{"Disconnected: #{@uid}:#{@signature}"}
95
97
  if Channel.cleanup(@channel_path)
96
- log "Channel closed: #{@channel_path}"
98
+ log{"Channel closed: #{@channel_path}"}
97
99
  end
98
100
  end
99
101
 
@@ -113,24 +115,18 @@ module ShootingStar
113
115
  return false if @unbound || !@waiting
114
116
  @executing.each{|id, params| execute(id, params)}
115
117
  return false if @execution.empty?
116
- return false unless send_data(@type == 'f' ? "#{@execution}\0" :
118
+ return false unless send_data(@type == 'flash' ? "#{@execution}\0" :
117
119
  "HTTP/1.1 200 OK\nContent-Type: text/javascript\n\n#{@execution}")
118
- @committed_at = Time.now
120
+ @committed_at = Asteroid::now
119
121
  @execution = ''
120
- @executing = Hash.new
122
+ @executing.clear
121
123
  @@executings.delete(@signature)
122
- unless @type == 'f'
124
+ unless @type == 'flash'
123
125
  @waiting = nil
124
126
  write_and_close
125
127
  end
126
128
  true
127
129
  end
128
-
129
- # noticed execution and remove the command from execution buffer.
130
- def executed(id)
131
- @executing = @@executings[@signature] ||= Hash.new
132
- @executing.delete(id)
133
- end
134
130
 
135
131
  # update current status of servant.
136
132
  def update(uid, tag)
@@ -140,7 +136,7 @@ module ShootingStar
140
136
  @@tags[@signature] = @tag = tag
141
137
  notify(:event => :enter, :uid => @uid, :tag => @tag)
142
138
  end
143
- log "Update: #{@uid}:#{@tag.join(',')}"
139
+ log{"Update: #{@uid}:#{@tag.join(',')}"}
144
140
  end
145
141
 
146
142
  def uid; @@uids[@signature] end
@@ -152,31 +148,31 @@ module ShootingStar
152
148
  end
153
149
 
154
150
  private
155
- def log(*arg, &block) ShootingStar::log(*arg, &block) end
151
+ def log(&block) ShootingStar::log(&block) end
156
152
 
157
153
  # check session timeout
158
154
  def session_timeout?
159
155
  return true unless @committed_at
160
- Time.now - @committed_at > ShootingStar::CONFIG.session_timeout
156
+ Asteroid::now - @committed_at > ShootingStar::CONFIG.session_timeout
161
157
  end
162
158
 
163
159
  # broadcast event to clients.
164
160
  def notify(params = {})
165
161
  return unless Channel[@channel_path]
166
162
  event_id = ShootingStar::timestamp
167
- log "Event(#{event_id}): #{@channel_path}:#{params.inspect}"
163
+ log{"Event(#{event_id}): #{@channel_path}:#{params.inspect}"}
168
164
  Channel[@channel_path].transmit("event-#{event_id}", params)
169
165
  rescue Exception => e
170
- log "ERROR: #{e.message}\n#{e.backtrace.join("\n")}"
166
+ log{"ERROR: #{e.message}\n#{e.backtrace.join("\n")}"}
171
167
  end
172
168
 
173
169
  # wait for commands or events until they occur. if they're already in
174
170
  # the execution buffer, they'll be flushed and return on the spot.
175
171
  def wait_for
176
172
  @waiting = true
177
- log "Wait for: #{@channel_path}:#{@uid}:#{@tag.join(',')}:#{@signature}"
173
+ log{"Wait for: #{@channel_path}:#{@uid}:#{@tag.inspect}:#{@signature}"}
178
174
  if prepare_channel(@channel_path).join(self)
179
- log "Flushed: #{@channel_path}:#{@uid}:#{@tag.join(',')}:#{@signature}"
175
+ log{"Flushed: #{@channel_path}:#{@uid}:#{@tag.inspect}:#{@signature}"}
180
176
  end
181
177
  end
182
178
 
@@ -184,41 +180,68 @@ module ShootingStar
184
180
  def prepare_channel(channel_path)
185
181
  unless Channel[channel_path]
186
182
  Channel.new(channel_path)
187
- log "Channel opened: #{channel_path}"
183
+ log{"Channel opened: #{channel_path}"}
188
184
  end
189
185
  Channel[channel_path]
190
186
  end
191
187
 
192
188
  # add execution line to the buffer.
193
189
  def execute(id, params)
194
- sweep_timeout = ShootingStar::CONFIG.sweep_timeout
195
190
  @executing[id] = params
196
191
  query = @query.sub(%r[\&sig=\d+], '')
197
192
  query += "&" + FormEncoder.encode(params) if params
198
- @execution += <<-"EOH"
199
- (function(){
200
- var ms1 = document.getElementById('meteor-strike-1-form');
201
- var box = ms1 ? ms1.parentNode : document.body;
202
- var iframe = document.createElement('iframe');
203
- var remove = function(){box.removeChild(iframe)};
204
- var timer = setTimeout(remove, #{sweep_timeout});
205
- iframe.onload = function(){
206
- clearTimeout(timer);
207
- setTimeout(remove, 0);
193
+ @execution += "meteorStrike.execute(#{id.to_json},#{query.to_json});"
194
+ end
195
+
196
+ def executioner(initial_serial_id = 0)
197
+ sweep_timeout = ShootingStar::CONFIG.sweep_timeout
198
+ <<-"EOH"
199
+ var meteorStrike = window.meteorStrike || new Object;
200
+ meteorStrike.execute = function(id, query){
201
+ var channel = #{@channel_path.to_json};
202
+ var ms = meteorStrike[channel] = meteorStrike[channel] || new Object;
203
+ ms.serialId = ms.serialId || #{initial_serial_id};
204
+ var ms1 = document.getElementById('meteor-strike-1-form');
205
+ var box = ms1 ? ms1.parentNode : document.body;
206
+ var iframe = document.createElement('iframe');
207
+ var remove = function(){
208
+ if(iframe) box.removeChild(iframe);
209
+ iframe = null;
210
+ };
211
+ var timer = setTimeout(remove, #{sweep_timeout});
212
+ var ready = function(){
213
+ clearTimeout(timer);
214
+ setTimeout(remove, 0);
215
+ };
216
+ iframe.onload = ready;
217
+ iframe.onreadystatechange = function(){
218
+ if(this.readyState == 'complete') ready();
219
+ };
220
+ iframe.src = ['#{@params['execute']}/', id, '?', query,
221
+ '&__s__=', ms.serialId++].join('');
222
+ box.appendChild(iframe);
208
223
  };
209
- iframe.src = '#{@params['execute']}/#{id}?#{query}';
210
- box.appendChild(iframe);
211
- })();
212
224
  EOH
213
225
  end
214
226
 
215
- # make client connect us.
216
- def make_connection(path)
227
+ # make flash client connect us.
228
+ def make_flash_connection
229
+ query = @query.sub(%r[\&sig=\d+], '')
230
+ query += "&" + FormEncoder.encode(:event => :init, :type => :flash)
231
+ event_id = MD5.new("event-init-flash-#{Asteroid::now}").to_s
232
+ send_data executioner + %Q{
233
+ meteorStrike.execute(#{event_id.to_json}, #{query.to_json});
234
+ } + "\0"
235
+ end
236
+
237
+ # make xhr client connect us.
238
+ def make_xhr_connection(path)
217
239
  assets = URI.parse(@params['execute'])
218
240
  assets.path = '/javascripts/prototype.js'
219
241
  assets.query = assets.fragment = nil
220
- query = @query.sub(%r[\&sig=\d+], '')
242
+ query = @query.sub(%r[\&sig=\d+], '') + '&__s__=0'
221
243
  query += "&" + FormEncoder.encode(:event => :init, :type => :xhr)
244
+ event_id = MD5.new("event-init-xhr-#{Asteroid::now}").to_s
222
245
  heartbeat = @params['heartbeat'].to_i
223
246
  send_data "HTTP/1.1 200 OK\nContent-Type: text/html\n\n" +
224
247
  <<-"EOH"
@@ -229,16 +252,18 @@ module ShootingStar
229
252
  <script type="text/javascript" src="#{assets}"></script>
230
253
  <script type="text/javascript">
231
254
  //<![CDATA[
232
- var connect = function(reconnect)
233
- { var body = $H(#{@params.to_json});
234
- body.__t__ = reconnect ? 'rc' : 'c';
235
- var request = new Ajax.Request(#{path.to_json},
236
- {evalScript: true, onComplete: function(xhr){
255
+ #{executioner(1)}
256
+ var connect = function(reconnect){
257
+ var body = $H(#{@params.to_json});
258
+ body.__t__ = 'xhr';
259
+ body.__p__ = reconnect ? 'reconnect' : 'connect';
260
+ var request = new Ajax.Request(#{path.to_json}, {evalScript: true,
261
+ onComplete: function(xhr){
237
262
  setTimeout(function(){connect(true)},
238
263
  xhr.getResponseHeader('Content-Type') ? 0 : 3000);
239
264
  }, postBody: body.toQueryString()});
240
- var disconnect = function()
241
- { request.options.onComplete = function(){};
265
+ var disconnect = function(){
266
+ request.options.onComplete = function(){};
242
267
  request.transport.abort();
243
268
  };
244
269
  Event.observe(window, 'unload', disconnect);
@@ -249,7 +274,7 @@ module ShootingStar
249
274
  setTimeout(function(){connect(false)}, 0);
250
275
  //]]>
251
276
  </script></head><body>
252
- <iframe src="#{@params['execute']}/0?#{query}"></iframe>
277
+ <iframe src="#{@params['execute']}/#{event_id}?#{query}"></iframe>
253
278
  </body></html>
254
279
  EOH
255
280
  rescue Exception
@@ -6,7 +6,8 @@ module ShootingStar
6
6
  # broadcast/multicast message
7
7
  def shoot(channel_path, id, tag, options = {})
8
8
  return unless Channel[channel_path]
9
- log "Shot: #{channel_path}:#{id}:#{tag.join(',')}:#{options}"
9
+ tag ||= []
10
+ log{"Shot: #{channel_path}:#{id}:#{tag.join(',')}:#{options}"}
10
11
  Channel[channel_path].transmit(id, options.merge(:tag => tag))
11
12
  end
12
13
 
@@ -49,12 +50,6 @@ module ShootingStar
49
50
  servers(channel_path, tag).map{|s| s.signature}
50
51
  end
51
52
 
52
- # notification entry point of message execution.
53
- def executed(sig, id)
54
- ::ShootingStar::Server[sig].executed(id)
55
- rescue Exception
56
- end
57
-
58
53
  # observe server side events
59
54
  def observe(channel_path, observer)
60
55
  Channel.observe(channel_path, observer)
@@ -62,7 +57,7 @@ module ShootingStar
62
57
  end
63
58
 
64
59
  private
65
- def log(*arg, &block) ShootingStar::log(*arg, &block) end
60
+ def log(&block) ShootingStar::log(&block) end
66
61
 
67
62
  def servers(channel_path, tag = nil)
68
63
  return [] unless Channel[channel_path]
@@ -0,0 +1,3 @@
1
+ module ShootingStar
2
+ VERSION = '3.2.2'
3
+ end
data/lib/shooting_star.rb CHANGED
@@ -5,19 +5,19 @@ require 'yaml'
5
5
  require 'ftools'
6
6
  require 'fileutils'
7
7
  require 'erb'
8
+ require 'shooting_star/version'
8
9
  require 'shooting_star/config'
9
10
  require 'shooting_star/shooter'
10
11
 
11
12
  module ShootingStar
12
- VERSION = '3.2.1'
13
13
  CONFIG = Config.new(
14
14
  :config => 'config/shooting_star.yml',
15
15
  :pid_file => 'tmp/pids/shooting_star.pid',
16
16
  :log_file => 'log/shooting_star.log',
17
17
  :daemon => false,
18
18
  :slient => false,
19
- :session_timeout => 10,
20
- :sweep_timeout => 500_000)
19
+ :session_timeout => 10.0,
20
+ :sweep_timeout => 30_000)
21
21
 
22
22
  def self.configure(options = {})
23
23
  if @log_file
@@ -35,6 +35,8 @@ module ShootingStar
35
35
  @@shooter ||= DRb.start_service && DRbObject.new(nil, CONFIG.shooter.uri)
36
36
  end
37
37
 
38
+ def self.timestamp; ("%.6f" % Asteroid::now).tr('.', '') end
39
+
38
40
  # install config file and plugin
39
41
  def self.init
40
42
  base_dir = CONFIG.directory || FileUtils.pwd
@@ -63,13 +65,22 @@ module ShootingStar
63
65
 
64
66
  def self.start(&block)
65
67
  if File.exist?(CONFIG.pid_file)
66
- log 'shooting_star is already running.'
67
- return
68
+ begin
69
+ shooter.signature
70
+ log{'shooting_star is already running.'}
71
+ return
72
+ rescue Exception
73
+ log{'shooting_star seems having been shut down incorrectly.'}
74
+ end
68
75
  end
69
76
  if CONFIG.daemon
70
77
  Signal.trap(:ALRM){exit} and sleep if fork
71
78
  Process.setsid
72
79
  end
80
+ if CONFIG.profile
81
+ require 'ruby-prof'
82
+ RubyProf.start
83
+ end
73
84
  require 'shooting_star/shooter'
74
85
  @@druby = DRb.start_service(CONFIG.shooter.uri, Shooter.new)
75
86
  require 'shooting_star/server'
@@ -81,14 +92,17 @@ module ShootingStar
81
92
  Signal.trap(:INT) do
82
93
  Asteroid::stop
83
94
  @@druby.stop_service
84
- log "shooting_star service stopped."
95
+ log{"shooting_star service stopped."}
85
96
  File.rm_f(CONFIG.pid_file)
97
+ if CONFIG.profile
98
+ RubyProf::FlatPrinter.new(RubyProf.stop).print(STDOUT, 0)
99
+ end
86
100
  end
87
101
  Signal.trap(:EXIT) do
88
102
  File.rm_f(CONFIG.pid_file)
89
103
  @log_file.close if @log_file
90
104
  end
91
- log "shooting_star service started."
105
+ log{"shooting_star service started."}
92
106
  Process.kill(:ALRM, Process.ppid) rescue nil if CONFIG.daemon
93
107
  block.call if block
94
108
  end
@@ -102,7 +116,7 @@ module ShootingStar
102
116
  end
103
117
  Thread.pass while File.exist?(CONFIG.pid_file)
104
118
  rescue Errno::ENOENT
105
- log "shooting_star service is not running."
119
+ log{"shooting_star service is not running."}
106
120
  rescue Errno::ESRCH
107
121
  File.unlink(CONFIG.pid_file)
108
122
  ensure
@@ -135,16 +149,13 @@ module ShootingStar
135
149
  puts "#{'-' * 79}\n%11d %s\n#{'-' * 79}" % [total_observers, 'TOTAL']
136
150
  end
137
151
 
138
- def self.timestamp
139
- now = Time.now
140
- "%d%06d" % [now.tv_sec, now.tv_usec]
141
- end
142
-
143
152
  private
144
- def self.log(*arg, &block)
145
- puts(*arg, &block) unless CONFIG.silent
153
+ def self.log(&block)
154
+ return if CONFIG.without_logging
155
+ message = block.call if block
156
+ puts(message) unless CONFIG.silent
146
157
  @log_file ||= open(CONFIG.log_file, 'a')
147
- @log_file.puts(*arg, &block) if @log_file
158
+ @log_file.puts(message) if @log_file
148
159
  end
149
160
  end
150
161
  __END__
@@ -20,14 +20,9 @@ end
20
20
 
21
21
  class ShootingStarTest < Test::Unit::TestCase
22
22
  def setup
23
- mutex = Mutex.new
24
- mutex.lock
25
- @thread = Thread.new do
26
- Asteroid::run('127.0.0.1', 7124, Server) do
27
- mutex.unlock
28
- end
29
- end
30
- mutex.lock
23
+ flag = false
24
+ @thread = Thread.new{Asteroid::run('127.0.0.1', 7124, Server){flag = true}}
25
+ Thread.pass until flag
31
26
  end
32
27
 
33
28
  def teardown
@@ -19,10 +19,9 @@ class ShootingStarTest < Test::Unit::TestCase
19
19
  :log_file => 'log/shooting_star.test.log',
20
20
  :server => {:host => '127.0.0.1', :port => 8081},
21
21
  :shooter => {:uri => 'druby://127.0.0.1:7124'}
22
- mutex = Mutex.new
23
- mutex.lock
24
- @thread = Thread.new{ShootingStar.start{mutex.unlock}}
25
- mutex.lock
22
+ flag = false
23
+ @thread = Thread.new{ShootingStar.start{flag = true}}
24
+ Thread.pass until flag
26
25
  @query = "sig=0123456789&execute=http://127.0.0.1:4001/meteor/strike"
27
26
  @query2 = "sig=1123456789&execute=http://127.0.0.1:4001/meteor/strike"
28
27
  end
@@ -49,19 +48,18 @@ class ShootingStarTest < Test::Unit::TestCase
49
48
  assert_not_nil result.index('test\/channel')
50
49
  client.close
51
50
 
52
- mutex = Mutex.new
53
- mutex.lock
51
+ flag = false
54
52
  Thread.new do
55
53
  client = TCPSocket.open('127.0.0.1', 8081)
56
54
  send(client, "POST", "test/channel", "#{@query}&__t__=c")
57
- mutex.unlock
55
+ flag = true
58
56
  end
59
- mutex.lock
57
+ Thread.pass until flag
60
58
  shooter = DRbObject.new_with_uri('druby://127.0.0.1:7124')
61
59
  assert_not_nil shooter
62
60
  shooter.shoot("test/channel", 12, [])
63
61
  assert_not_nil result = client.read
64
- assert_not_nil result.index('meteor/strike/12')
62
+ assert_not_nil result.index('meteorStrike.execute(12,')
65
63
  end
66
64
 
67
65
  def test_multi_user_communication
@@ -74,27 +72,28 @@ class ShootingStarTest < Test::Unit::TestCase
74
72
  observer = TestObserver.new
75
73
  assert_not_nil observer
76
74
  shooter.observe('test/channel', observer)
77
- mutex = Mutex.new
78
- mutex.lock
75
+ flag = false
79
76
  assert_nil observer.params
80
77
  Thread.new do
81
78
  send(client1, "POST", "test/channel", "#{@query}&__t__=c")
82
- mutex.unlock
79
+ flag = true
83
80
  end
84
- mutex.lock
85
- assert_nil observer.params
81
+ Thread.pass until flag
82
+ assert_not_nil observer.params
83
+ assert_equal :enter, observer.params[:event]
84
+ flag = false
86
85
  Thread.new do
87
86
  send(client2, "POST", "test/channel", "#{@query2}&__t__=c")
88
- mutex.unlock
87
+ flag = true
89
88
  end
90
- mutex.lock
89
+ Thread.pass until flag
91
90
  assert_not_nil observer.params
92
91
  assert_equal :enter, observer.params[:event]
93
- assert_not_nil result1 = client1.read
94
- assert_not_nil result1.index('meteor/strike/event-')
95
92
  shooter.shoot("test/channel", 12, [])
93
+ assert_not_nil result1 = client1.read
94
+ assert_not_nil result1.index('meteorStrike.execute(12,')
96
95
  assert_not_nil result2 = client2.read
97
- assert_not_nil result2.index('meteor/strike/12')
96
+ assert_not_nil result2.index('meteorStrike.execute(12,')
98
97
  end
99
98
 
100
99
  def test_xmlsocket_server
@@ -2,6 +2,7 @@ class <%= class_name %>Controller < ApplicationController
2
2
  layout '<%= file_name %>', :only => :index
3
3
 
4
4
  def index
5
+ session[:name] ||= 'guest'
5
6
  @chats = <%= class_name %>.find(:all).reverse
6
7
  end
7
8
 
@@ -9,24 +10,43 @@ class <%= class_name %>Controller < ApplicationController
9
10
  @chat = <%= class_name %>.find(params[:id])
10
11
  end
11
12
 
12
- def talk
13
- @chat = <%= class_name %>.create!(params[:chat])
14
- content = render_component_as_string :action => 'show', :id => @chat.id
15
- javascript = render_to_string :update do |page|
16
- page.insert_html :top, 'chat-list', content
17
- end
18
- Meteor.shoot '<%= file_name %>', javascript
13
+ def listen
14
+ session[:name] = params[:name]
19
15
  render :update do |page|
20
- page[:chat_message].clear
21
- page[:chat_message].focus
16
+ page << <<-"EOH"
17
+ meteorStrike['<%= file_name %>'].update(#{session[:name].to_json});
18
+ EOH
19
+ end
20
+ end
21
+
22
+ def talk
23
+ @chat = <%= class_name %>.new(
24
+ :name => session[:name], :message => params[:message])
25
+ if @chat.save
26
+ content = render_component_as_string :action => 'show', :id => @chat.id
27
+ javascript = render_to_string :update do |page|
28
+ page.insert_html :top, 'chat-list', content
29
+ end
30
+ Meteor.shoot '<%= file_name %>', javascript
31
+ render :update do |page|
32
+ page[:message].clear
33
+ page[:message].focus
34
+ end
35
+ else
36
+ render :nothing => true
22
37
  end
23
38
  end
24
39
 
25
- def connect
40
+ def event
41
+ message = case params[:event]
42
+ when 'init'; "connection established by #{params[:type]}."
43
+ when 'enter'; "#{params[:uid]} joined."
44
+ when 'leave'; "#{params[:uid]} left."
45
+ end
26
46
  @chat = <%= class_name %>.new(
27
47
  :name => '(* system *)',
28
48
  :created_at => Time.now,
29
- :message => "connection established on #{params[:client_type]}.")
49
+ :message => message)
30
50
  render :action => 'show'
31
51
  end
32
52
  end
@@ -1,2 +1,2 @@
1
- module MeteorHelper
1
+ module <%= class_name %>Helper
2
2
  end
@@ -1,18 +1,17 @@
1
- <%% form_remote_for(:chat, :url => {:action => 'talk'}) do |f| %>
2
- <%%= f.text_field :name %>
1
+ <%% form_remote_tag(:url => {:action => 'listen'}) do |f| %>
2
+ <%%= text_field_tag :name, session[:name] %>
3
+ <%%= submit_tag 'Listen' %><br />
4
+ <%% end %>
5
+ <%% form_remote_tag(:url => {:action => 'talk'}) do |f| %>
6
+ <%%= text_field_tag :message %>
3
7
  <%%= submit_tag 'Talk' %><br />
4
- <%%= f.text_area :message, :rows => 3 %>
5
8
  <%% end %>
6
9
  <ul id="chat-list">
7
- <%% for chat in @chats %>
8
- <%%= render_component :action => 'show', :id => chat.id %>
9
- <%% end %>
10
+ <%% for chat in @chats %>
11
+ <%%= render_component :action => 'show', :id => chat.id %>
12
+ <%% end %>
10
13
  </ul>
11
- <%%= meteor_strike '<%= file_name %>', :event => %Q{
12
- switch(params.event){
13
- case 'init':
14
- new Ajax.Updater('chat-list', #{url_for(:action => 'connect').to_json}, {
15
- insertion: Insertion.Top, parameters: {client_type: params.type}});
16
- break;
17
- }
18
- } %>
14
+ <%%= meteor_strike '<%= file_name %>', :uid => session[:name], :event => %Q{
15
+ new Ajax.Updater('chat-list', #{url_for(:action => 'event').to_json}, {
16
+ insertion: Insertion.Top, parameters: params})}
17
+ %>
@@ -1,7 +1,6 @@
1
1
  class MeteorController < ApplicationController
2
2
  layout nil
3
3
  caches_action :strike
4
- after_filter :notify_execution, :only => [:strike]
5
4
 
6
5
  def strike
7
6
  @channel = params[:channel]
@@ -24,9 +23,4 @@ class MeteorController < ApplicationController
24
23
  Meteor.shooter.sweep
25
24
  render :nothing => true
26
25
  end
27
-
28
- private
29
- def notify_execution
30
- Meteor.shooter.executed(params[:sig], params[:id])
31
- end
32
26
  end
@@ -9,8 +9,8 @@
9
9
  var javascript = #{@javascript.to_json};
10
10
  var execute = function(){
11
11
  var ms = parent.parent.meteorStrike[channel];
12
- if(ms) ms.execute(javascript);
13
- else setTimeout(execute, 0);
12
+ if(ms) ms.evaluate(javascript, #{params[:__s__]});
13
+ else setTimeout(evaluate, 0);
14
14
  };
15
15
  execute();
16
16
  })();
@@ -0,0 +1,37 @@
1
+ module MeteorStrike
2
+ module Controller
3
+ def self.included(base)
4
+ base.class_eval do
5
+ after_filter :meteor_strike
6
+ hide_action :meteor_strike
7
+ hide_action :install_meteor_strike
8
+ end
9
+ end
10
+
11
+ def install_meteor_strike
12
+ if parent_controller
13
+ parent_controller.install_meteor_strike
14
+ else
15
+ @install_meteor_strike ||= 0
16
+ @install_meteor_strike += 1
17
+ end
18
+ end
19
+
20
+ def meteor_strike
21
+ return unless @install_meteor_strike
22
+ result = <<-"EOH"
23
+ <script language="VBScript">
24
+ '<![CDATA[
25
+ On Error Resume Next
26
+ EOH
27
+ (1..@install_meteor_strike).each do |i|
28
+ result << <<-"EOH"
29
+ Sub meteor_strike_#{i}_FSCommand(ByVal command, ByVal args)
30
+ Call meteor_strike_#{i}_DoFSCommand(command, args)
31
+ End Sub
32
+ EOH
33
+ end
34
+ response.body.sub!(%r{</head>}i, "#{result}\n']]>\n</script></head>")
35
+ end
36
+ end
37
+ end
@@ -18,7 +18,7 @@ module MeteorStrike
18
18
  cc += 'post-check=0, pre-check=0'
19
19
  controller.headers['Cache-Control'] = cc
20
20
  end
21
- @meteor_strike ||= 0 and @meteor_strike += 1
21
+ @meteor_strike = controller.install_meteor_strike
22
22
  config = Meteor::config
23
23
  server = Meteor::server
24
24
  shooting_star_uri = "#{server}/#{channel}"
@@ -27,17 +27,19 @@ module MeteorStrike
27
27
  shooting_star_uri = [subdomain, shooting_star_uri].join('.')
28
28
  end
29
29
  uri = url_for(:only_path => false).split('/')[0..2].join('/')
30
- uid = options[:uid] ? CGI.escape(options[:uid].to_s) : ''
30
+ uid = options[:uid] ? options[:uid].to_s : ''
31
+ escaped_uid = CGI.escape(uid)
31
32
  tags = options[:tag] || []
32
33
  tag = tags.map{|i| CGI.escape(i.to_s)}.join(',')
33
34
  update_uri = "#{uri}/meteor/update"
34
- sig = Meteor.shooter.signature
35
+ now = Time.now
36
+ sig = "%d%06d" % [now.tv_sec, now.tv_usec]
35
37
  iframe_id = "meteor-strike-#{@meteor_strike}"
36
38
  host_port = (server.split(':') << '80')[0..1].join(':')
37
- flash_vars = ["channel=#{channel}", "tag=#{tag}", "uid=#{uid}",
39
+ flash_vars = ["channel=#{channel}", "tag=#{tag}", "uid=#{escaped_uid}",
38
40
  "sig=#{sig}", "base_uri=#{uri}", "server=#{host_port}",
39
- "heartbeat=#{options[:heartbeat]}", "debug=#{options[:debug].to_json}"
40
- ].join('&')
41
+ "heartbeat=#{options[:heartbeat]}", "debug=#{options[:debug].to_json}",
42
+ "meteor_strike_id=#{@meteor_strike}"].join('&')
41
43
  unless options[:noflash]
42
44
  @flash_html = render :use_full_path => false,
43
45
  :file => File.join(PLUGIN_ROOT, 'views/flash.rhtml'),
@@ -1,5 +1,9 @@
1
+ require 'meteor_strike/controller'
1
2
  require 'meteor_strike/helper'
3
+ require 'action_controller'
2
4
 
3
5
  module MeteorStrike
4
6
  PLUGIN_ROOT = File.join(File.dirname(__FILE__), '..')
5
7
  end
8
+
9
+ ActionController::Base.__send__ :include, MeteorStrike::Controller
@@ -0,0 +1,7 @@
1
+ namespace :meteor_strike do
2
+ desc 'Update meteor_strike plugin'
3
+ task :update do
4
+ sh 'shooting_star init'
5
+ sh './script/generate meteor -f'
6
+ end
7
+ end
@@ -11,6 +11,7 @@
11
11
  <input name="tag" /><input name="uid" /><input name="sig" />
12
12
  <input name="heartbeat" value="<%= options[:heartbeat] %>" />
13
13
  </form>
14
+ <div id="<%= iframe_id %>-flash"></div>
14
15
  <%= javascript_tag %Q{
15
16
  var meteorStrike = meteorStrike || $H();
16
17
  meteorStrike.getFlashVersion = function(){
@@ -41,40 +42,53 @@
41
42
  return $A(tags).uniq().map(encode).join(',');
42
43
  };
43
44
  var ms = meteorStrike[channel] = meteorStrike[channel] || new Object;
44
- Event.observe(window, 'load', function(){
45
- ms.getTags = function(){return TAGS};
46
- ms.getUid = function(){return UID};
47
- ms.execute = function(js){eval(js)};
48
- ms.event = function(params){
49
- if(params.event == 'init'){
50
- if(ms.connection) return Element.remove('#{iframe_id}');
51
- ms.connection = params.type;
45
+ ms.getTags = function(){return TAGS};
46
+ ms.getUid = function(){return UID};
47
+ ms.executionQueue = {};
48
+ ms.executionCounter = 0;
49
+ ms.evaluate = function(js, serialId){
50
+ ms.executionQueue[serialId] = js;
51
+ if(serialId == ms.executionCounter){
52
+ while(js = ms.executionQueue[ms.executionCounter]){
53
+ eval(js);
54
+ delete ms.executionQueue[ms.executionCounter];
55
+ ++ms.executionCounter;
52
56
  }
53
- (function(){#{options[:event]}})();
54
- };
55
- ms.update = function(uid, tags){
56
- new Ajax.Request(#{update_uri.to_json}, {postBody: $H({
57
- channel: channel, uid: uid || UID,
58
- tag: encodeTags(tags || TAGS), sig: #{sig.to_json}
59
- }).toQueryString(), asynchronous: true});
60
- UID = uid, TAGS = tags;
61
- };
62
- ms.tuneIn = function(tags){
63
- ms.update(UID, TAGS.concat(tags || []).uniq());
64
- };
65
- ms.tuneOut = function(tags){
66
- ms.update(UID, Array.prototype.without.apply(TAGS, tags));
67
- };
68
- ms.tuneInOut = function(tagsIn, tagsOut){
69
- var tags = TAGS.concat(tagsIn || []).uniq();
70
- ms.update(UID, Array.prototype.without.apply(tags, tagsOut));
71
- };
72
- ms.tuneOutIn = function(tagsOut, tagsIn){
73
- var tags = Array.prototype.without.apply(TAGS, tagsOut);
74
- ms.update(UID, tags.concat(tagsIn || []).uniq());
75
- };
76
- setTimeout(ms.connector = function(){
57
+ }
58
+ };
59
+ ms.event = function(params){
60
+ if(params.event == 'init'){
77
61
  if(ms.connection) return;
62
+ if(ms.connecting && ms.connecting != params.type) return;
63
+ ms.connection = params.type;
64
+ }
65
+ (function(){#{options[:event]}})();
66
+ };
67
+ ms.update = function(uid, tags){
68
+ new Ajax.Request(#{update_uri.to_json}, {postBody: $H({
69
+ channel: channel, uid: uid || UID,
70
+ tag: encodeTags(tags || TAGS), sig: #{sig.to_json}
71
+ }).toQueryString(), asynchronous: true});
72
+ UID = uid, TAGS = tags;
73
+ };
74
+ ms.tuneIn = function(tags){
75
+ ms.update(UID, TAGS.concat(tags || []).uniq());
76
+ };
77
+ ms.tuneOut = function(tags){
78
+ ms.update(UID, Array.prototype.without.apply(TAGS, tags));
79
+ };
80
+ ms.tuneInOut = function(tagsIn, tagsOut){
81
+ var tags = TAGS.concat(tagsIn || []).uniq();
82
+ ms.update(UID, Array.prototype.without.apply(tags, tagsOut));
83
+ };
84
+ ms.tuneOutIn = function(tagsOut, tagsIn){
85
+ var tags = Array.prototype.without.apply(TAGS, tagsOut);
86
+ ms.update(UID, tags.concat(tagsIn || []).uniq());
87
+ };
88
+ Event.observe(window, 'load', function(){
89
+ setTimeout(ms.connector = function(){
90
+ if(ms.connection) return;
91
+ if(ms.connecting && ms.connecting != 'xhr') return;
78
92
  var form = $("#{iframe_id}-form");
79
93
  form.uid.value = #{uid.to_json};
80
94
  form.tag.value = #{tag.to_json};
@@ -88,26 +102,23 @@
88
102
  function meteor_strike_#{@meteor_strike}_DoFSCommand(command, args){
89
103
  var ms = meteorStrike[#{channel.to_json}];
90
104
  switch(command){
91
- case 'execute': ms.execute(args); break;
105
+ case 'execute':
106
+ if(ms.connection == 'flash' || ms.connecting == 'flash') eval(args);
107
+ else if($('#{iframe_id}-flash')) Element.remove('#{iframe_id}-flash');
108
+ break;
92
109
  case 'event':
93
- if(args == 'connect') ms.event({event: 'init', type: 'flash'});
110
+ switch(args){
111
+ case 'connect': // intercept xhr connection
112
+ ms.connecting = 'flash';
113
+ if(ms.connection != 'flash') ms.connection = null;
114
+ if($('#{iframe_id}')) Element.remove('#{iframe_id}');
115
+ break;
116
+ }
94
117
  break;
95
118
  }
96
119
  }
97
- if(navigator.appName && navigator.appName.indexOf("Microsoft") != -1 &&
98
- navigator.userAgent.indexOf("Windows") != -1 &&
99
- navigator.userAgent.indexOf("Windows 3.1") == -1){
100
- document.write([
101
- '<script language="VBScript"\\>',
102
- 'On Error Resume Next',
103
- ['Sub meteor_strike_', #{@meteor_strike},
104
- '_FSCommand(ByVal command, ByVal args)'].join(''),
105
- [' Call meteor_strike_', #{@meteor_strike},
106
- '_DoFSCommand(command, args)'].join(''),
107
- 'End Sub', '</script\\>'].join(#{"\n".to_json}));
108
- }
109
120
  if(meteorStrike.getFlashVersion() >= 6){
110
- document.write(#{@flash_html.to_json});
121
+ $('#{iframe_id}-flash').innerHTML = #{@flash_html.to_json};
111
122
  }
112
123
  } %>
113
124
  </div>
metadata CHANGED
@@ -1,10 +1,10 @@
1
1
  --- !ruby/object:Gem::Specification
2
- rubygems_version: 0.9.3
2
+ rubygems_version: 0.9.4
3
3
  specification_version: 1
4
4
  name: shooting_star
5
5
  version: !ruby/object:Gem::Version
6
- version: 3.2.1
7
- date: 2007-08-09 00:00:00 +09:00
6
+ version: 3.2.2
7
+ date: 2007-08-30 00:00:00 +09:00
8
8
  summary: Our goal is development of practical comet server which will be achieving over 100,000 simultaneous connections per host. On this purpose, we abandon portability and use system calls depending on particular OS such as epoll and kqueue.
9
9
  require_paths:
10
10
  - lib
@@ -36,6 +36,7 @@ files:
36
36
  - Rakefile
37
37
  - bin/shooting_star
38
38
  - lib/shooting_star.rb
39
+ - lib/shooting_star/version.rb
39
40
  - lib/shooting_star/server.rb
40
41
  - lib/shooting_star/shooter.rb
41
42
  - lib/shooting_star/channel.rb
@@ -58,9 +59,12 @@ files:
58
59
  - vendor/plugins/meteor_strike/lib/meteor_strike.rb
59
60
  - vendor/plugins/meteor_strike/lib/meteor_strike
60
61
  - vendor/plugins/meteor_strike/lib/meteor_strike/helper.rb
62
+ - vendor/plugins/meteor_strike/lib/meteor_strike/controller.rb
61
63
  - vendor/plugins/meteor_strike/views
62
64
  - vendor/plugins/meteor_strike/views/xhr.rhtml
63
65
  - vendor/plugins/meteor_strike/views/flash.rhtml
66
+ - vendor/plugins/meteor_strike/tasks
67
+ - vendor/plugins/meteor_strike/tasks/meteor_strike.rake
64
68
  - vendor/plugins/meteor_strike/test
65
69
  - vendor/plugins/meteor_strike/test/meteor_strike_test.rb
66
70
  - vendor/plugins/meteor_strike/generators
@@ -125,5 +129,5 @@ dependencies:
125
129
  requirements:
126
130
  - - ">="
127
131
  - !ruby/object:Gem::Version
128
- version: 1.2.2
132
+ version: 1.3.0
129
133
  version: