puggernaut 0.1.5 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -79,7 +79,7 @@ Include [jQuery](http://jquery.com) and [puggernaut.js](https://github.com/winto
79
79
  Javascript client example:
80
80
 
81
81
  <pre>
82
- Puggernaut.path = '/long_poll';
82
+ Puggernaut.path = '/long_poll'; // (default)
83
83
 
84
84
  Puggernaut
85
85
  .watch('channel', function(e, message) {
@@ -1,4 +1,5 @@
1
1
  puggernaut:
2
+ em-websocket: ~>0.2.1
2
3
  eventmachine: ~>0.12.10
3
4
  rake: >=0.8.7
4
5
  rspec: ~>1.0
@@ -1,5 +1,5 @@
1
1
  name: puggernaut
2
- version: 0.1.5
2
+ version: 0.2.0
3
3
  authors:
4
4
  - Winton Welsh
5
5
  email: mail@wintoni.us
@@ -7,5 +7,6 @@ homepage: http://github.com/winton/puggernaut
7
7
  summary: Simple server push implementation using eventmachine and long polling
8
8
  description: Simple server push implementation using eventmachine and long polling
9
9
  dependencies:
10
+ - em-websocket
10
11
  - eventmachine
11
12
  development_dependencies: null
@@ -1,8 +1,9 @@
1
1
  require File.dirname(__FILE__) + '/puggernaut/gems'
2
2
 
3
- Puggernaut::Gems.activate %w(eventmachine)
3
+ Puggernaut::Gems.activate %w(eventmachine em-websocket)
4
4
 
5
5
  require 'eventmachine'
6
+ require 'em-websocket'
6
7
 
7
8
  $:.unshift File.dirname(__FILE__)
8
9
 
@@ -1,5 +1,6 @@
1
1
  require "#{File.dirname(__FILE__)}/logger"
2
2
  require 'socket'
3
+ require 'timeout'
3
4
 
4
5
  module Puggernaut
5
6
  class Client
@@ -43,9 +44,12 @@ module Puggernaut
43
44
  begin
44
45
  host_port = "#{host}:#{port}"
45
46
  logger.info "Client#send - #{host_port} - #{data}"
46
- connection = @connections[host_port] ||= TCPSocket.open(host, port)
47
- connection.print(data)
48
- response = connection.gets
47
+ response = nil
48
+ Timeout.timeout(10) do
49
+ connection = @connections[host_port] ||= TCPSocket.open(host, port)
50
+ connection.print(data)
51
+ response = connection.gets
52
+ end
49
53
  raise 'not ok' if !response || !response.include?('OK')
50
54
  rescue Exception => e
51
55
  logger.info "Client#send - Exception - #{e.message} - #{host_port} - #{data}"
@@ -1,44 +1,41 @@
1
+ require 'puggernaut/server/shared'
1
2
  require 'puggernaut/server/http'
2
3
  require 'puggernaut/server/channel'
3
4
  require 'puggernaut/server/tcp'
5
+ require 'puggernaut/server/websocket'
4
6
 
5
7
  module Puggernaut
6
8
  class Server
7
9
 
8
10
  include Logger
9
11
 
10
- def initialize(http_port=8100, tcp_port=http_port.to_i+1)
11
- puts "\nPuggernaut is starting on #{http_port} (HTTP) and #{tcp_port} (TCP)"
12
+ def initialize(http_port=8100, tcp_port=http_port.to_i+1, ws_port=tcp_port.to_i+1)
13
+ puts "\nPuggernaut is starting on #{http_port} (Long Poll HTTP), #{tcp_port} (Puggernaut TCP), and #{ws_port} (WebSocket TCP)"
12
14
  puts "*snort*\n\n"
13
-
14
- errors = 0
15
-
16
- while errors <= 10
17
- begin
18
- Channel.channels = []
19
- GC.start
20
- EM.epoll if EM.epoll?
21
- EM.run do
22
- logger.info "Server#initialize - Starting HTTP - #{http_port}"
23
- EM.start_server '0.0.0.0', http_port, Http
24
-
25
- logger.info "Server#initialize - Starting TCP - #{tcp_port}"
26
- EM.start_server '0.0.0.0', tcp_port, Tcp
27
-
28
- errors = 0
29
- end
30
- rescue Interrupt
31
- logger.info "Server#initialize - Shutting down"
32
- exit
33
- rescue
34
- errors += 1
35
- logger.error "Server#initialize - Error - #{$!.message}"
36
- logger.error "\t" + $!.backtrace.join("\n\t")
15
+
16
+ begin
17
+ Channel.channels = []
18
+ GC.start
19
+ EM.epoll if EM.epoll?
20
+ EM.run do
21
+ logger.info "Server#initialize - Starting HTTP - #{http_port}"
22
+ EM.start_server '0.0.0.0', http_port, Http
23
+
24
+ logger.info "Server#initialize - Starting TCP - #{tcp_port}"
25
+ EM.start_server '0.0.0.0', tcp_port, Tcp
26
+
27
+ logger.info "Server#initialize - Starting WebSocket - #{ws_port}"
28
+ Websocket.new '0.0.0.0', ws_port
29
+
30
+ errors = 0
37
31
  end
32
+ rescue Interrupt
33
+ logger.info "Server#initialize - Shutting down"
34
+ exit
35
+ rescue
36
+ logger.error "Server#initialize - Error - #{$!.message}"
37
+ logger.error "\t" + $!.backtrace.join("\n\t")
38
38
  end
39
-
40
- puts "Exiting because of too many consecutive errors :("
41
- puts "Check #{Dir.pwd}/log/puggernaut.log\n\n"
42
39
  end
43
40
  end
44
41
  end
@@ -2,31 +2,32 @@ module Puggernaut
2
2
  class Server
3
3
  class Channel < EM::Channel
4
4
 
5
- include Logger
5
+ attr_reader :channels, :user_id
6
6
 
7
- attr_reader :channels
8
-
9
- def initialize(channels)
7
+ def initialize(channels, user_id)
10
8
  @channels = channels
9
+ @user_id = user_id
11
10
  super()
12
11
  end
13
12
 
14
13
  class <<self
14
+
15
+ include Logger
15
16
 
16
17
  attr_accessor :channels
17
18
 
18
- def create(channels)
19
- channel = self.new(channels)
19
+ def create(channels, user_id)
20
+ channel = self.new(channels, user_id)
20
21
  @channels ||= []
21
22
  @channels << channel
22
23
  channel
23
24
  end
24
25
 
25
- def all_messages_after(channel, identifier)
26
+ def all_messages_after_id(channel, identifier)
26
27
  if @messages && @messages[channel]
27
28
  found = false
28
29
  (
29
- @messages[channel].select { |(id, message)|
30
+ @messages[channel].select { |(id, message, time)|
30
31
  found = true if id == identifier
31
32
  found
32
33
  }[1..-1] || []
@@ -38,22 +39,45 @@ module Puggernaut
38
39
  []
39
40
  end
40
41
  end
42
+
43
+ def all_messages_after_time(channel, after_time)
44
+ if @messages && @messages[channel]
45
+ (
46
+ @messages[channel].select { |(id, message, time)|
47
+ after_time < time
48
+ } || []
49
+
50
+ ).collect { |message|
51
+ "#{channel}|#{message.join '|'}"
52
+ }
53
+ else
54
+ []
55
+ end
56
+ end
57
+
58
+ def inhabitants(channel_name)
59
+ user_ids = @channels.collect do |channel|
60
+ channel.user_id if channel.channels.include?(channel_name)
61
+ end
62
+ user_ids.compact
63
+ end
41
64
 
42
- def say(messages)
65
+ def say(messages, exclude_user_id=nil)
43
66
  @messages ||= {}
44
67
  messages = messages.inject({}) do |hash, (channel_name, messages)|
45
68
  messages = messages.collect do |message|
46
- [ rand.to_s[2..-1], message ]
69
+ [ rand.to_s[2..-1], message, Time.now ]
47
70
  end
48
71
  @messages[channel_name] ||= []
49
72
  @messages[channel_name] += messages
50
- if @messages[channel_name].length > 100
51
- @messages[channel_name] = @messages[channel_name][-100..-1]
73
+ @messages[channel_name] = @messages[channel_name].select do |message|
74
+ message[2] >= Time.now - 2 * 60 * 60
52
75
  end
53
76
  hash[channel_name] = messages
54
77
  hash
55
78
  end
56
79
  @channels.each do |channel|
80
+ next if exclude_user_id && channel.user_id == exclude_user_id
57
81
  push = channel.channels.collect do |channel_name|
58
82
  if messages[channel_name]
59
83
  messages[channel_name].collect { |message|
@@ -1,10 +1,12 @@
1
1
  require 'cgi'
2
+ require 'time'
2
3
 
3
4
  module Puggernaut
4
5
  class Server
5
6
  module Http
6
7
 
7
8
  include Logger
9
+ include Shared
8
10
 
9
11
  def receive_data(data)
10
12
  lines = data.split(/[\r\n]+/)
@@ -21,27 +23,30 @@ module Puggernaut
21
23
  end
22
24
 
23
25
  if path == '/'
24
- channels = query['channel'].dup rescue []
25
- lasts = query['last'].dup rescue []
26
+ channels, @join_leave, lasts, time, user_id = query_defaults(query)
26
27
 
27
28
  unless channels.empty?
28
- @channel = Channel.create(channels)
29
- unless lasts.empty?
30
- lasts = channels.inject([]) { |array, channel|
31
- array += Channel.all_messages_after(channel, lasts.shift)
32
- array
33
- }.join("\n")
29
+ @channel = Channel.create(channels, user_id)
30
+ if @join_leave && user_id
31
+ join_channels(channels, user_id)
34
32
  end
35
- unless lasts.empty?
36
- respond lasts
33
+ messages = gather_messages(channels, lasts, time)
34
+ unless messages.empty?
35
+ respond messages
37
36
  else
38
37
  EM::Timer.new(30) { respond }
39
- logger.info "Server::Channel#create - Subscribed - #{@channel.channels.join(", ")}"
38
+ logger.info "Server::Http#receive_data - Subscribed - #{@channel.channels.join(", ")}"
40
39
  @subscription_id = @channel.subscribe { |str| respond str }
41
40
  end
42
41
  else
43
42
  respond "no channel specified", 500
44
43
  end
44
+ elsif path == '//inhabitants'
45
+ channels = query['channel'].dup rescue []
46
+ user_ids = channels.collect do |c|
47
+ Channel.inhabitants(c)
48
+ end
49
+ respond user_ids.flatten.uniq.join('|')
45
50
  else
46
51
  respond "not found", 404
47
52
  end
@@ -65,6 +70,9 @@ module Puggernaut
65
70
  if @subscription_id
66
71
  @channel.unsubscribe(@subscription_id)
67
72
  logger.info "Sever::Http#unbind - Unsubscribe - #{@channel.channels.join(", ")}"
73
+ if @join_leave && @channel.user_id
74
+ leave_channel(channel)
75
+ end
68
76
  Channel.channels.delete @channel
69
77
  end
70
78
  end
@@ -0,0 +1,46 @@
1
+ module Puggernaut
2
+ class Server
3
+ module Shared
4
+
5
+ def gather_messages(channels, lasts, time)
6
+ if time
7
+ channels.inject([]) { |array, channel|
8
+ array += Channel.all_messages_after_time(channel, Time.parse(time))
9
+ array
10
+ }.join("\n")
11
+ elsif !lasts.empty?
12
+ channels.inject([]) { |array, channel|
13
+ array += Channel.all_messages_after_id(channel, lasts.shift)
14
+ array
15
+ }.join("\n")
16
+ else
17
+ ''
18
+ end
19
+ end
20
+
21
+ def join_channels(channels, user_id)
22
+ Channel.say channels.inject({}) { |hash, channel|
23
+ hash[channel] = "!PUGJOIN#{user_id}"
24
+ hash
25
+ }, user_id
26
+ end
27
+
28
+ def leave_channel(channel)
29
+ Channel.say channel.channels.inject({}) { |hash, c|
30
+ hash[c] = "!PUGLEAVE#{channel.user_id}"
31
+ hash
32
+ }, channel.user_id
33
+ end
34
+
35
+ def query_defaults(query)
36
+ [
37
+ (query['channel'].dup rescue []),
38
+ (query['join_leave'].dup[0] rescue nil),
39
+ (query['last'].dup rescue []),
40
+ (query['time'].dup[0] rescue nil),
41
+ (query['user_id'].dup[0] rescue nil)
42
+ ]
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,47 @@
1
+ module Puggernaut
2
+ class Server
3
+ class Websocket
4
+
5
+ include Logger
6
+ include Shared
7
+
8
+ def initialize(host, port)
9
+ EventMachine::WebSocket.start(:host => host, :port => port) do |ws|
10
+ ws.onopen do
11
+ logger.info "Server::Websocket#initialize - Open"
12
+ channel = nil
13
+ join_leave = nil
14
+ joined = nil
15
+ subscription_id = nil
16
+ ws.onmessage do |msg|
17
+ logger.info "Server::Websocket#initialize - Message - #{msg}"
18
+ channels, join_leave, lasts, time, user_id = query_defaults(CGI.parse(msg))
19
+ channel ||= Channel.create(channels, user_id)
20
+ if join_leave && user_id && !joined
21
+ joined = true
22
+ join_channels(channels, user_id)
23
+ end
24
+ messages = gather_messages(channels, lasts, time)
25
+ unless messages.empty?
26
+ ws.send messages
27
+ else
28
+ logger.info "Server::Websocket#initialize - Subscribed - #{channel.channels.join(", ")}"
29
+ subscription_id = channel.subscribe { |str| ws.send str }
30
+ end
31
+ end
32
+ ws.onclose do
33
+ if subscription_id
34
+ channel.unsubscribe(subscription_id)
35
+ logger.info "Sever::Websocket#initialize - Unsubscribe - #{channel.channels.join(", ")}"
36
+ if join_leave && channel.user_id
37
+ leave_channel(channel)
38
+ end
39
+ Channel.channels.delete channel
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -26,17 +26,29 @@ class SpecServer < Sinatra::Base
26
26
 
27
27
  get '/single' do
28
28
  begin
29
- client = Puggernaut::Client.new("localhost:8001")
29
+ client = Puggernaut::Client.new("localhost:8101")
30
30
  client.push :single => "single message"
31
31
  client.close
32
32
  rescue Exception => e
33
33
  e.message
34
34
  end
35
35
  end
36
+
37
+ get '/join_leave_join' do
38
+ begin
39
+ client = Puggernaut::Client.new("localhost:8101")
40
+ client.push :single => [
41
+ "!PUGJOINtest", "!PUGLEAVEtest", "!PUGJOINtest"
42
+ ]
43
+ client.close
44
+ rescue Exception => e
45
+ e.message
46
+ end
47
+ end
36
48
 
37
49
  get '/multiple' do
38
50
  begin
39
- client = Puggernaut::Client.new("localhost:8001")
51
+ client = Puggernaut::Client.new("localhost:8101")
40
52
  client.push :multiple => [ "multiple message 1", "multiple message 2" ]
41
53
  client.close
42
54
  rescue Exception => e
@@ -46,7 +58,7 @@ class SpecServer < Sinatra::Base
46
58
 
47
59
  get '/last/:count' do
48
60
  begin
49
- client = Puggernaut::Client.new("localhost:8001")
61
+ client = Puggernaut::Client.new("localhost:8101")
50
62
  client.push :last => "last message #{params[:count]}"
51
63
  client.close
52
64
  rescue Exception => e
@@ -56,7 +68,7 @@ class SpecServer < Sinatra::Base
56
68
 
57
69
  get '/multiple/channels' do
58
70
  begin
59
- client = Puggernaut::Client.new("localhost:8001")
71
+ client = Puggernaut::Client.new("localhost:8101")
60
72
  client.push :single => "single message", :multiple => [ "multiple message 1", "multiple message 2" ]
61
73
  client.close
62
74
  rescue Exception => e
@@ -5,34 +5,43 @@ var Puggernaut = new function() {
5
5
 
6
6
  this.disabled = false;
7
7
  this.path = '/long_poll';
8
+ this.inhabitants = inhabitants;
8
9
  this.unwatch = unwatch;
9
10
  this.watch = watch;
11
+ this.watch_join = watch_join;
12
+ this.watch_leave = watch_leave;
13
+ this.webSocketPort = webSocketPort;
10
14
 
11
15
  var channels = {};
12
16
  var errors = 0;
13
17
  var events = $('<div/>');
18
+ var leaves = {};
14
19
  var started = false;
15
-
16
- function ajax() {
20
+ var request;
21
+ var request_id;
22
+ var port = 8102;
23
+
24
+ function ajax(join_leave, time, user_id) {
17
25
  if (channelLength() > 0 && !self.disabled && errors <= 10) {
18
26
  started = true;
19
- $.ajax({
27
+ request = $.ajax({
20
28
  cache: false,
21
- data: params(),
29
+ data: params(join_leave, time, user_id),
22
30
  dataType: 'text',
23
- error: function() {
24
- errors += 1;
25
- ajax();
31
+ error: function(xhr, status, error) {
32
+ if (started && status != 'abort') {
33
+ errors += 1;
34
+ ajax(join_leave, null, user_id);
35
+ }
26
36
  },
27
- success: function(data) {
28
- errors = 0;
29
- $.each(data.split("\n"), function(i, line) {
30
- line = line.split('|', 3);
31
- if (typeof channels[line[0]] != 'undefined')
32
- channels[line[0]] = line[1];
33
- events.trigger(line[0], line[2]);
34
- });
35
- ajax();
37
+ success: function(data, status, xhr) {
38
+ if (started) {
39
+ errors = 0;
40
+ $.each(data.split("\n"), function(i, line) {
41
+ processMessage(line);
42
+ });
43
+ ajax(join_leave, null, user_id);
44
+ }
36
45
  },
37
46
  timeout: 100000,
38
47
  traditional: true,
@@ -49,31 +58,82 @@ var Puggernaut = new function() {
49
58
  });
50
59
  return length;
51
60
  }
61
+
62
+ function inhabitants() {
63
+ var args = $.makeArray(arguments);
64
+ var fn = args.pop();
65
+ $.ajax({
66
+ cache: false,
67
+ data: { channel: args },
68
+ dataType: 'text',
69
+ success: function(data, status, xhr) {
70
+ fn(data.split('|'));
71
+ },
72
+ traditional: true,
73
+ url: self.path + '/inhabitants'
74
+ });
75
+ }
52
76
 
53
- function params() {
77
+ function params(join_leave, time, user_id) {
54
78
  var ch = [];
55
79
  var la = [];
80
+
56
81
  $.each(channels, function(channel, last) {
57
82
  ch.push(channel);
58
- la.push(last);
83
+ if (last)
84
+ la.push(last);
59
85
  });
60
- return { channel: ch, last: la };
86
+
87
+ var data = { channel: ch };
88
+
89
+ if (la.length)
90
+ data.last = la;
91
+ if (join_leave)
92
+ data.join_leave = join_leave;
93
+ if (time)
94
+ data.time = time + '';
95
+ if (user_id)
96
+ data.user_id = user_id;
97
+
98
+ return data;
99
+ }
100
+
101
+ function processMessage(line) {
102
+ line = line.split('|', 4);
103
+ if (line[0] && typeof channels[line[0]] != 'undefined') {
104
+ var id;
105
+ channels[line[0]] = line[1];
106
+ if (line[2].substring(0, 8) == '!PUGJOIN') {
107
+ id = line[2].substring(8);
108
+ if (leaves[id]) {
109
+ delete leaves[id];
110
+ clearTimeout(leaves[id]);
111
+ } else
112
+ events.trigger('join_' + line[0], id);
113
+ } else if (line[2].substring(0, 9) == '!PUGLEAVE') {
114
+ id = line[2].substring(9);
115
+ leaves[id] = setTimeout(function() {
116
+ events.trigger('leave_' + line[0], id);
117
+ }, 10000);
118
+ } else
119
+ events.trigger(line[0], [ line[2], new Date(line[3]) ]);
120
+ }
61
121
  }
62
122
 
63
123
  function unwatch() {
64
124
  var args = $.makeArray(arguments);
125
+ started = false;
126
+ if (request.abort)
127
+ request.abort();
128
+ if (request.close)
129
+ request.close();
65
130
  if (args.length) {
66
- if (args[args.length-1].constructor == String)
67
- $.each(args, function(i, item) {
68
- delete channels[item];
69
- });
70
- args = $.map(args, function(item) {
71
- if (item.constructor == String)
72
- return 'watch.' + item;
73
- else
74
- return item;
131
+ $.each(args, function(i, item) {
132
+ delete channels[item];
133
+ events.unbind(item);
134
+ events.unbind('join_' + item);
135
+ events.unbind('leave_' + item);
75
136
  });
76
- events.unbind.apply(events, args);
77
137
  } else
78
138
  events.unbind();
79
139
  return this;
@@ -82,6 +142,15 @@ var Puggernaut = new function() {
82
142
  function watch() {
83
143
  var ch = $.makeArray(arguments);
84
144
  var fn = ch.pop();
145
+ var join_leave, time, user_id;
146
+
147
+ if (ch[ch.length-1] && ch[ch.length-1].constructor === Object) {
148
+ var options = ch.pop();
149
+
150
+ join_leave = options.join_leave;
151
+ time = options.time;
152
+ user_id = options.user_id;
153
+ }
85
154
 
86
155
  if (ch.length && fn) {
87
156
  $.each(ch, function(i, item) {
@@ -89,10 +158,67 @@ var Puggernaut = new function() {
89
158
  events.bind(item, fn);
90
159
  });
91
160
 
92
- if (!started)
93
- ajax();
161
+ if (!started) {
162
+ if (window.WebSocket)
163
+ websocket(join_leave, time, user_id);
164
+ else
165
+ ajax(join_leave, time, user_id);
166
+ }
94
167
  }
95
168
 
96
169
  return this;
97
170
  }
171
+
172
+ function watch_join() {
173
+ var args = $.makeArray(arguments);
174
+ var fn = args.pop();
175
+ $.each(args, function(i, item) {
176
+ events.bind('join_' + item, fn);
177
+ });
178
+ return this;
179
+ }
180
+
181
+ function watch_leave() {
182
+ var args = $.makeArray(arguments);
183
+ var fn = args.pop();
184
+ $.each(args, function(i, item) {
185
+ events.bind('leave_' + item, fn);
186
+ });
187
+ return this;
188
+ }
189
+
190
+ function websocket(join_leave, time, user_id) {
191
+ if (channelLength() > 0 && !self.disabled && errors <= 10) {
192
+ started = true;
193
+ request = new WebSocket("ws://" + window.location.host + ":" + webSocketPort() + "/");
194
+ request.onopen = function() {
195
+ errors = 0;
196
+ if (started)
197
+ request.send($.param(params(join_leave, time, user_id), true));
198
+ };
199
+ request.onmessage = function(evt) {
200
+ errors = 0;
201
+ if (started) {
202
+ $.each(evt.data.split("\n"), function(i, line) {
203
+ processMessage(line);
204
+ });
205
+ request.send($.param(params(join_leave, null, user_id), true));
206
+ }
207
+ };
208
+ request.onerror = function() {
209
+ errors += 1;
210
+ if (started)
211
+ websocket(join_leave, null, user_id);
212
+ };
213
+ request.onclose = function() {
214
+ if (started)
215
+ websocket(join_leave, null, user_id);
216
+ };
217
+ }
218
+ }
219
+
220
+ function webSocketPort(p) {
221
+ if (p) port = p;
222
+ return port;
223
+ }
98
224
  };
@@ -2,8 +2,9 @@ $(function() {
2
2
 
3
3
  module("Single message", {
4
4
  setup: function() {
5
- Puggernaut.watch('single', function(e, message) {
5
+ Puggernaut.watch('single', function(e, message, time) {
6
6
  equals(message, 'single message');
7
+ ok(time.constructor === Date);
7
8
  Puggernaut.unwatch('single');
8
9
  start();
9
10
  });
@@ -12,15 +13,100 @@ $(function() {
12
13
 
13
14
  test("should receive a message", function() {
14
15
  stop();
16
+ expect(2);
15
17
  $.get('/single');
16
18
  });
19
+
20
+ module("Single message unbind", {
21
+ setup: function() {
22
+ Puggernaut.watch('single', function(e, message, time) {
23
+ equals(message, 'single message');
24
+ ok(time.constructor === Date);
25
+ time_test = [ message, time ];
26
+ Puggernaut.unwatch('single');
27
+ });
28
+ }
29
+ });
30
+
31
+ test("should only receive one message", function() {
32
+ stop();
33
+ expect(2);
34
+ $.get('/single', function() {
35
+ $.get('/single', function() {
36
+ setTimeout(function() {
37
+ start();
38
+ }, 2000);
39
+ });
40
+ });
41
+ });
42
+
43
+ module("Single message after time");
44
+
45
+ test("should receive messages after time", function() {
46
+ stop();
47
+ expect(2);
48
+ var time_now = new Date();
49
+ $.get('/single', function() {
50
+ Puggernaut.watch('single', { time: time_now }, function(e, message, time) {
51
+ equals(message, 'single message');
52
+ ok(time.constructor === Date);
53
+ Puggernaut.unwatch('single');
54
+ start();
55
+ });
56
+ });
57
+ });
58
+
59
+ module("Single message inhabitants", {
60
+ setup: function() {
61
+ Puggernaut.watch('single', { user_id: 'test' }, function(e, message, time) {
62
+ setTimeout(function() {
63
+ Puggernaut.inhabitants('single', function(users) {
64
+ equals(users[0], 'test');
65
+ equals(users.length, 1);
66
+ Puggernaut.unwatch('single');
67
+ start();
68
+ });
69
+ }, 1000);
70
+ });
71
+ }
72
+ });
73
+
74
+ test("should receive a message", function() {
75
+ stop();
76
+ expect(2);
77
+ $.get('/single');
78
+ });
79
+
80
+ module("Single message join/leave/join", {
81
+ setup: function() {
82
+ Puggernaut
83
+ .watch('single', { join_leave: true }, function(e, message, time) {})
84
+ .watch_join('single', function(e, user_id) {
85
+ equals(user_id, 'test');
86
+ setTimeout(function() {
87
+ Puggernaut.unwatch('single');
88
+ start();
89
+ }, 1000);
90
+ })
91
+ .watch_leave('single', function(e, user_id) {
92
+ ok(false);
93
+ });
94
+ }
95
+ });
96
+
97
+ test("should trigger join event without leave", function() {
98
+ stop();
99
+ expect(1);
100
+ $.get('/join_leave_join');
101
+ });
17
102
 
18
103
  module("Multiple messages", {
19
104
  setup: function() {
20
105
  var executions = 0;
21
- Puggernaut.watch('multiple', function(e, message) {
106
+ Puggernaut.watch('multiple', function(e, message, time) {
22
107
  executions += 1;
23
108
  equals(message, 'multiple message ' + executions);
109
+ ok(time.constructor === Date);
24
110
  if (executions == 2) {
25
111
  Puggernaut.unwatch('multiple');
26
112
  start();
@@ -31,23 +117,21 @@ $(function() {
31
117
 
32
118
  test("should receive multiple messages", function() {
33
119
  stop();
120
+ expect(4);
34
121
  $.get('/multiple');
35
122
  });
36
123
 
37
124
  module("Last message", {
38
125
  setup: function() {
39
- Puggernaut.watch('last', function(e, message) {
40
- if (message != 'last message 2') {
41
- equals(message, 'last message 1');
42
- Puggernaut.disabled = true;
43
- $.get('/last/2', function() {
44
- Puggernaut.disabled = false;
45
- Puggernaut.watch('last', function(e, message) {
46
- equals(message, 'last message 2');
47
- Puggernaut.unwatch('last');
48
- start();
49
- });
50
- });
126
+ Puggernaut.watch('last', function(e, message, time) {
127
+ if (message == 'last message 1') {
128
+ ok(time.constructor === Date);
129
+ $.get('/last/2');
130
+ }
131
+ if (message == 'last message 2') {
132
+ ok(time.constructor === Date);
133
+ Puggernaut.unwatch('last');
134
+ start();
51
135
  }
52
136
  });
53
137
  }
@@ -55,6 +139,7 @@ $(function() {
55
139
 
56
140
  test("should pick up last message", function() {
57
141
  stop();
142
+ expect(2);
58
143
  $.get('/last/1');
59
144
  });
60
145
 
@@ -62,6 +147,7 @@ $(function() {
62
147
 
63
148
  test("should receive all messages", function() {
64
149
  stop();
150
+ expect(6);
65
151
 
66
152
  var executions = 0;
67
153
  var total_runs = 0;
@@ -69,9 +155,10 @@ $(function() {
69
155
  Puggernaut.disabled = true;
70
156
 
71
157
  Puggernaut
72
- .watch('single', function(e, message) {
158
+ .watch('single', function(e, message, time) {
73
159
  total_runs += 1;
74
160
  equals(message, 'single message');
161
+ ok(time.constructor === Date);
75
162
  Puggernaut.unwatch('single');
76
163
  if (total_runs == 3)
77
164
  start();
@@ -80,10 +167,11 @@ $(function() {
80
167
  Puggernaut.disabled = false;
81
168
 
82
169
  Puggernaut
83
- .watch('multiple', function(e, message) {
170
+ .watch('multiple', function(e, message, time) {
84
171
  executions += 1;
85
172
  total_runs += 1;
86
173
  equals(message, 'multiple message ' + executions);
174
+ ok(time.constructor === Date);
87
175
  if (executions == 2)
88
176
  Puggernaut.unwatch('multiple');
89
177
  if (total_runs == 3)
metadata CHANGED
@@ -1,12 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: puggernaut
3
3
  version: !ruby/object:Gem::Version
4
- prerelease: false
4
+ hash: 23
5
+ prerelease:
5
6
  segments:
6
7
  - 0
7
- - 1
8
- - 5
9
- version: 0.1.5
8
+ - 2
9
+ - 0
10
+ version: 0.2.0
10
11
  platform: ruby
11
12
  authors:
12
13
  - Winton Welsh
@@ -14,24 +15,41 @@ autorequire:
14
15
  bindir: bin
15
16
  cert_chain: []
16
17
 
17
- date: 2011-01-24 00:00:00 -08:00
18
+ date: 2011-03-15 00:00:00 -07:00
18
19
  default_executable:
19
20
  dependencies:
20
21
  - !ruby/object:Gem::Dependency
21
- name: eventmachine
22
+ name: em-websocket
22
23
  prerelease: false
23
24
  requirement: &id001 !ruby/object:Gem::Requirement
24
25
  none: false
25
26
  requirements:
26
27
  - - ~>
27
28
  - !ruby/object:Gem::Version
29
+ hash: 21
30
+ segments:
31
+ - 0
32
+ - 2
33
+ - 1
34
+ version: 0.2.1
35
+ type: :runtime
36
+ version_requirements: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ name: eventmachine
39
+ prerelease: false
40
+ requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ hash: 59
28
46
  segments:
29
47
  - 0
30
48
  - 12
31
49
  - 10
32
50
  version: 0.12.10
33
51
  type: :runtime
34
- version_requirements: *id001
52
+ version_requirements: *id002
35
53
  description: Simple server push implementation using eventmachine and long polling
36
54
  email: mail@wintoni.us
37
55
  executables:
@@ -56,7 +74,9 @@ files:
56
74
  - lib/puggernaut/server.rb
57
75
  - lib/puggernaut/server/channel.rb
58
76
  - lib/puggernaut/server/http.rb
77
+ - lib/puggernaut/server/shared.rb
59
78
  - lib/puggernaut/server/tcp.rb
79
+ - lib/puggernaut/server/websocket.rb
60
80
  - lib/puggernaut/spec_server.rb
61
81
  - public/jquery.js
62
82
  - public/puggernaut.js
@@ -85,6 +105,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
85
105
  requirements:
86
106
  - - ">="
87
107
  - !ruby/object:Gem::Version
108
+ hash: 3
88
109
  segments:
89
110
  - 0
90
111
  version: "0"
@@ -93,13 +114,14 @@ required_rubygems_version: !ruby/object:Gem::Requirement
93
114
  requirements:
94
115
  - - ">="
95
116
  - !ruby/object:Gem::Version
117
+ hash: 3
96
118
  segments:
97
119
  - 0
98
120
  version: "0"
99
121
  requirements: []
100
122
 
101
123
  rubyforge_project:
102
- rubygems_version: 1.3.7
124
+ rubygems_version: 1.4.2
103
125
  signing_key:
104
126
  specification_version: 3
105
127
  summary: Simple server push implementation using eventmachine and long polling