puggernaut 0.1.5 → 0.2.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.
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