faye 0.5.5 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of faye might be problematic. Click here for more details.

Files changed (62) hide show
  1. data/History.txt +14 -0
  2. data/README.rdoc +98 -0
  3. data/Rakefile +17 -15
  4. data/lib/faye-browser-min.js +1 -1
  5. data/lib/faye.rb +14 -5
  6. data/lib/faye/adapters/rack_adapter.rb +12 -5
  7. data/lib/faye/engines/base.rb +62 -0
  8. data/lib/faye/engines/connection.rb +63 -0
  9. data/lib/faye/engines/memory.rb +89 -0
  10. data/lib/faye/engines/redis.rb +141 -0
  11. data/lib/faye/error.rb +16 -4
  12. data/lib/faye/mixins/publisher.rb +6 -0
  13. data/lib/faye/protocol/channel.rb +34 -86
  14. data/lib/faye/protocol/client.rb +36 -52
  15. data/lib/faye/protocol/extensible.rb +3 -0
  16. data/lib/faye/protocol/server.rb +119 -169
  17. data/lib/faye/transport/http.rb +45 -0
  18. data/lib/faye/transport/local.rb +15 -0
  19. data/lib/faye/{network → transport}/transport.rb +36 -49
  20. data/spec/browser.html +35 -0
  21. data/spec/install.sh +48 -0
  22. data/spec/javascript/channel_spec.js +15 -0
  23. data/spec/javascript/client_spec.js +610 -0
  24. data/spec/javascript/engine_spec.js +319 -0
  25. data/spec/javascript/faye_spec.js +15 -0
  26. data/spec/javascript/grammar_spec.js +66 -0
  27. data/spec/javascript/node_adapter_spec.js +276 -0
  28. data/spec/javascript/server/connect_spec.js +168 -0
  29. data/spec/javascript/server/disconnect_spec.js +121 -0
  30. data/spec/javascript/server/extensions_spec.js +60 -0
  31. data/spec/javascript/server/handshake_spec.js +153 -0
  32. data/spec/javascript/server/subscribe_spec.js +245 -0
  33. data/spec/javascript/server/unsubscribe_spec.js +245 -0
  34. data/spec/javascript/server_spec.js +146 -0
  35. data/spec/javascript/transport_spec.js +130 -0
  36. data/spec/node.js +34 -0
  37. data/spec/ruby/channel_spec.rb +17 -0
  38. data/spec/ruby/client_spec.rb +615 -0
  39. data/spec/ruby/engine_spec.rb +312 -0
  40. data/spec/ruby/faye_spec.rb +14 -0
  41. data/spec/ruby/grammar_spec.rb +68 -0
  42. data/spec/ruby/rack_adapter_spec.rb +209 -0
  43. data/spec/ruby/server/connect_spec.rb +170 -0
  44. data/spec/ruby/server/disconnect_spec.rb +120 -0
  45. data/spec/ruby/server/extensions_spec.rb +69 -0
  46. data/spec/ruby/server/handshake_spec.rb +151 -0
  47. data/spec/ruby/server/subscribe_spec.rb +247 -0
  48. data/spec/ruby/server/unsubscribe_spec.rb +247 -0
  49. data/spec/ruby/server_spec.rb +138 -0
  50. data/spec/ruby/transport_spec.rb +128 -0
  51. data/spec/spec_helper.rb +5 -0
  52. data/spec/testswarm.pl +200 -0
  53. data/spec/thin_proxy.rb +36 -0
  54. metadata +119 -84
  55. data/Manifest.txt +0 -27
  56. data/README.txt +0 -98
  57. data/lib/faye/protocol/connection.rb +0 -111
  58. data/test/scenario.rb +0 -172
  59. data/test/test_channel.rb +0 -54
  60. data/test/test_clients.rb +0 -381
  61. data/test/test_grammar.rb +0 -86
  62. data/test/test_server.rb +0 -488
@@ -0,0 +1,138 @@
1
+ require "spec_helper"
2
+
3
+ describe Faye::Server do
4
+ let(:engine) { mock "engine" }
5
+ let(:server) { Faye::Server.new }
6
+
7
+ before do
8
+ Faye::Engine.stub(:get).and_return engine
9
+ end
10
+
11
+ describe :process do
12
+ let(:handshake) {{"channel" => "/meta/handshake", "data" => "handshake" }}
13
+ let(:connect) {{"channel" => "/meta/connect", "data" => "connect" }}
14
+ let(:disconnect) {{"channel" => "/meta/disconnect", "data" => "disconnect" }}
15
+ let(:subscribe) {{"channel" => "/meta/subscribe", "data" => "subscribe" }}
16
+ let(:unsubscribe) {{"channel" => "/meta/unsubscribe", "data" => "unsubscribe"}}
17
+ let(:publish) {{"channel" => "/some/channel", "data" => "publish" }}
18
+
19
+ before do
20
+ engine.stub(:interval).and_return(0)
21
+ engine.stub(:timeout).and_return(60)
22
+ end
23
+
24
+ it "returns an empty response for no messages" do
25
+ response = nil
26
+ server.process([], false) { |r| response = r }
27
+ response.should == []
28
+ end
29
+
30
+ it "ignores invalid messages" do
31
+ response = nil
32
+ server.process([{}, {"channel" => "invalid"}], false) { |r| response = r }
33
+ response.should == []
34
+ end
35
+
36
+ it "routes single messages to appropriate handlers" do
37
+ server.should_receive(:handshake).with(handshake, false)
38
+ engine.should_receive(:publish).with(handshake)
39
+ server.process(handshake, false)
40
+ end
41
+
42
+ it "routes a list of messages to appropriate handlers" do
43
+ server.should_receive(:handshake).with(handshake, false)
44
+ server.should_receive(:connect).with(connect, false)
45
+ server.should_receive(:disconnect).with(disconnect, false)
46
+ server.should_receive(:subscribe).with(subscribe, false)
47
+ server.should_receive(:unsubscribe).with(unsubscribe, false)
48
+
49
+ engine.should_receive(:publish).with(handshake)
50
+ engine.should_receive(:publish).with(connect)
51
+ engine.should_receive(:publish).with(disconnect)
52
+ engine.should_receive(:publish).with(subscribe)
53
+ engine.should_receive(:publish).with(unsubscribe)
54
+ engine.should_receive(:publish).with(publish)
55
+
56
+ server.process([handshake, connect, disconnect, subscribe, unsubscribe, publish], false)
57
+ end
58
+
59
+ describe "publishing a message" do
60
+ it "tells the engine to publish the message" do
61
+ engine.should_receive(:publish).with(publish)
62
+ server.process(publish, false) {}
63
+ end
64
+
65
+ it "returns no response" do
66
+ engine.stub(:publish)
67
+ server.process(publish, false) { |r| r.should == [] }
68
+ end
69
+
70
+ describe "with an error" do
71
+ before { publish["error"] = "invalid" }
72
+
73
+ it "does not tell the engine to publish the message" do
74
+ engine.should_not_receive(:publish)
75
+ server.process(publish, false) {}
76
+ end
77
+
78
+ it "returns no response" do
79
+ engine.stub(:publish)
80
+ server.process(publish, false) { |r| r.should == [] }
81
+ end
82
+ end
83
+
84
+ describe "to an invalid channel" do
85
+ before { publish["channel"] = "/invalid/*" }
86
+
87
+ it "does not tell the engine to publish the message" do
88
+ engine.should_not_receive(:publish)
89
+ server.process(publish, false) {}
90
+ end
91
+ end
92
+ end
93
+
94
+ describe "handshaking" do
95
+ before do
96
+ engine.should_receive(:publish).with(handshake)
97
+ server.should_receive(:handshake).with(handshake, false).and_yield({"successful" => true})
98
+ end
99
+
100
+ it "returns the handshake response with advice" do
101
+ server.process(handshake, false) do |response|
102
+ response.should == [
103
+ { "successful" => true,
104
+ "advice" => {"reconnect" => "retry", "interval" => 0, "timeout" => 60000}
105
+ }
106
+ ]
107
+ end
108
+ end
109
+ end
110
+
111
+ describe "connecting for messages" do
112
+ let(:messages) { [{"channel" => "/a"}, {"channel" => "/b"}] }
113
+
114
+ before do
115
+ engine.should_receive(:publish).with(connect)
116
+ server.should_receive(:connect).with(connect, false).and_yield(messages)
117
+ end
118
+
119
+ it "returns the new messages" do
120
+ server.process(connect, false) { |r| r.should == messages }
121
+ end
122
+ end
123
+ end
124
+
125
+ describe :flush_connection do
126
+ let(:message) {{"clientId" => "fakeclientid"}}
127
+
128
+ it "flushes the connection when given one message" do
129
+ engine.should_receive(:flush).with("fakeclientid")
130
+ server.flush_connection(message)
131
+ end
132
+
133
+ it "flushes the connection when given a list of messages" do
134
+ engine.should_receive(:flush).with("fakeclientid")
135
+ server.flush_connection([message])
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,128 @@
1
+ require "spec_helper"
2
+
3
+ describe Faye::Transport do
4
+ let :client do
5
+ client = mock("client")
6
+ client.stub(:endpoint).and_return("http://example.com/")
7
+ client
8
+ end
9
+
10
+ describe :get do
11
+ before do
12
+ Faye::Transport::Local.stub(:usable?).and_return(false)
13
+ Faye::Transport::Http.stub(:usable?).and_return(false)
14
+ end
15
+
16
+ let(:transport) { Faye::Transport.get(client, ["long-polling", "in-process"]) }
17
+ let(:local_transport) { Faye::Transport.get(client, ["in-process"]) }
18
+ let(:http_transport) { Faye::Transport.get(client, ["long-polling"]) }
19
+
20
+ describe "when no transport is usable" do
21
+ it "raises an exception" do
22
+ lambda { transport }.should raise_error
23
+ end
24
+ end
25
+
26
+ describe "when a less preferred transport is usable" do
27
+ before do
28
+ Faye::Transport::Http.stub(:usable?).and_return(true)
29
+ end
30
+
31
+ it "returns a transport of the usable type" do
32
+ transport.should be_kind_of(Faye::Transport::Http)
33
+ end
34
+
35
+ it "rasies an exception of the usable type is not requested" do
36
+ lambda { local_transport }.should raise_error
37
+ end
38
+
39
+ it "allows the usable type to be specifically selected" do
40
+ http_transport.should be_kind_of(Faye::Transport::Http)
41
+ end
42
+ end
43
+
44
+ describe "when all transports are usable" do
45
+ before do
46
+ Faye::Transport::Local.stub(:usable?).and_return(true)
47
+ Faye::Transport::Http.stub(:usable?).and_return(true)
48
+ end
49
+
50
+ it "returns the most preferred type" do
51
+ transport.should be_kind_of(Faye::Transport::Local)
52
+ end
53
+
54
+ it "allows types to be specifically selected" do
55
+ local_transport.should be_kind_of(Faye::Transport::Local)
56
+ http_transport.should be_kind_of(Faye::Transport::Http)
57
+ end
58
+ end
59
+ end
60
+
61
+ describe :send do
62
+ include EM::RSpec::FakeClock
63
+ before { clock.stub }
64
+ after { clock.reset }
65
+
66
+ before do
67
+ client.stub(:client_id).and_return("abc123")
68
+ end
69
+
70
+ describe "for batching transports" do
71
+ before do
72
+ transport_klass = Class.new(Faye::Transport) do
73
+ def batching?
74
+ true
75
+ end
76
+ end
77
+ @transport = transport_klass.new(client, "")
78
+ end
79
+
80
+ it "does not make an immediate request" do
81
+ @transport.should_not_receive(:request)
82
+ @transport.send({"batch" => "me"}, 60)
83
+ end
84
+
85
+ it "queues the message to be sent after a timeout" do
86
+ @transport.should_receive(:request).with([{"batch" => "me"}], 60)
87
+ @transport.send({"batch" => "me"}, 60)
88
+ clock.tick(0.01)
89
+ end
90
+
91
+ it "allows multiple messages to be batched together" do
92
+ @transport.should_receive(:request).with([{"id" => 1}, {"id" => 2}], 60)
93
+ @transport.send({"id" => 1}, 60)
94
+ @transport.send({"id" => 2}, 60)
95
+ clock.tick(0.01)
96
+ end
97
+
98
+ it "adds advice to connect messages sent with others" do
99
+ @transport.should_receive(:request).with([{"channel" => "/meta/connect", "advice" => {"timeout" => 0}}, {}], 60)
100
+ @transport.send({"channel" => "/meta/connect"}, 60)
101
+ @transport.send({}, 60)
102
+ clock.tick(0.01)
103
+ end
104
+
105
+ it "adds no advice to connect messages sent alone" do
106
+ @transport.should_receive(:request).with([{"channel" => "/meta/connect"}], 60)
107
+ @transport.send({"channel" => "/meta/connect"}, 60)
108
+ clock.tick(0.01)
109
+ end
110
+ end
111
+
112
+ describe "for non-batching transports" do
113
+ before do
114
+ transport_klass = Class.new(Faye::Transport) do
115
+ def batching?
116
+ false
117
+ end
118
+ end
119
+ @transport = transport_klass.new(client, "")
120
+ end
121
+
122
+ it "makes a request immediately" do
123
+ @transport.should_receive(:request).with([{"no" => "batch"}], 60)
124
+ @transport.send({"no" => "batch"}, 60)
125
+ end
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,5 @@
1
+ require 'bundler/setup'
2
+ dir = File.expand_path(File.dirname(__FILE__))
3
+ require dir + '/../lib/faye'
4
+ require dir + '/../vendor/em-rspec/lib/em-rspec'
5
+ require 'rack/test'
data/spec/testswarm.pl ADDED
@@ -0,0 +1,200 @@
1
+ #!/usr/bin/perl
2
+
3
+ # CONFIGURE
4
+
5
+ # The location of the TestSwarm that you're going to run against.
6
+
7
+ my $SWARM = "http://swarm.jcoglan.com";
8
+ my $SWARM_INJECT = "/js/inject.js";
9
+
10
+ # Your TestSwarm username.
11
+
12
+ my $USER = "faye";
13
+
14
+ # Your authorization token.
15
+
16
+ my $AUTH_TOKEN = "6d26e250d81b32099fccc59db53a3a0e648f0e6d";
17
+
18
+ # The maximum number of times you want the tests to be run.
19
+
20
+ my $MAX_RUNS = 5;
21
+
22
+ # The type of revision control system being used.
23
+ # Currently "svn" or "git" are supported.
24
+
25
+ my $RCS_TYPE = "git";
26
+
27
+ # The URL from which a copy will be checked out.
28
+
29
+ my $RCS_URL = "git://github.com/jcoglan/faye.git";
30
+
31
+ # The directory in which the checkouts will occur.
32
+
33
+ my $BASE_DIR = "/home/jcoglan/www/swarm.jcoglan.com/app/changeset/$USER";
34
+
35
+ # A script tag loading in the TestSwarm injection script will
36
+ # be added at the bottom of the <head> in the following file.
37
+
38
+ my $INJECT_FILE = "spec/browser.html";
39
+
40
+ # Any build commands that need to happen.
41
+
42
+ my $BUILD = "git submodule init && git submodule update && cd vendor/js.class && jake && cd ../.. && rm -rf build && jake";
43
+
44
+ # The name of the job that will be submitted
45
+ # (pick a descriptive, but short, name to make it easy to search)
46
+
47
+ # Note: The string {REV} will be replaced with the current
48
+ # commit number/hash.
49
+
50
+ my $JOB_NAME = "Faye Commit #{REV}";
51
+
52
+ # The browsers you wish to run against. Options include:
53
+ # - "all" all available browsers.
54
+ # - "popular" the most popular browser (99%+ of all browsers in use)
55
+ # - "current" the current release of all the major browsers
56
+ # - "gbs" the browsers currently supported in Yahoo's Graded Browser Support
57
+ # - "beta" upcoming alpha/beta of popular browsers
58
+ # - "popularbeta" the most popular browser and their upcoming releases
59
+
60
+ my $BROWSERS = "all";
61
+
62
+ # All the suites that you wish to run within this job
63
+ # (can be any number of suites)
64
+
65
+ my %SUITES = ();
66
+
67
+ # Comment these out if you wish to define a custom set of SUITES above
68
+ my $SUITE = "$SWARM/changeset/$USER/{REV}";
69
+ sub BUILD_SUITES {
70
+ %SUITES = map { /(\w+).html/; $1 => "$SUITE/$_"; } glob($INJECT_FILE);
71
+ }
72
+
73
+ ########### NO NEED TO CONFIGURE BELOW HERE ############
74
+
75
+ my $DEBUG = 1;
76
+ my $curdate = time;
77
+ my $co_dir = "tmp-$curdate";
78
+
79
+ print "chdir $BASE_DIR\n" if ( $DEBUG );
80
+ chdir( $BASE_DIR );
81
+
82
+ # Check out a specific revision
83
+ if ( $RCS_TYPE eq "svn" ) {
84
+ print "svn co $RCS_URL $co_dir\n" if ( $DEBUG );
85
+ `svn co $RCS_URL $co_dir`;
86
+ } elsif ( $RCS_TYPE eq "git" ) {
87
+ print "git clone $RCS_URL $co_dir\n" if ( $DEBUG );
88
+ `git clone $RCS_URL $co_dir`;
89
+ }
90
+
91
+ if ( ! -e $co_dir ) {
92
+ die "Problem checking out source.";
93
+ }
94
+
95
+ print "chdir $co_dir\n" if ( $DEBUG );
96
+ chdir( $co_dir );
97
+
98
+ my $rev;
99
+
100
+ # Figure out the revision of the checkout
101
+ if ( $RCS_TYPE eq "svn" ) {
102
+ print "svn info | grep Revision\n" if ( $DEBUG );
103
+ $rev = `svn info | grep Revision`;
104
+ $rev =~ s/Revision: //;
105
+ } elsif ( $RCS_TYPE eq "git" ) {
106
+ print "git log --abbrev-commit | head -1\n" if ( $DEBUG );
107
+ $rev = `git log --abbrev-commit | head -1`;
108
+ $rev =~ s/commit.*?(\w+).*$/$1/;
109
+ }
110
+
111
+ $rev =~ s/\s*//g;
112
+
113
+ print "Revision: $rev\n" if ( $DEBUG );
114
+
115
+ if ( ! $rev ) {
116
+ remove_tmp();
117
+ die "Revision information not found.";
118
+
119
+ } elsif ( ! -e "../$rev" ) {
120
+ print "chdir $BASE_DIR\n" if ( $DEBUG );
121
+ chdir( $BASE_DIR );
122
+
123
+ print "rename $co_dir $rev\n" if ( $DEBUG );
124
+ rename( $co_dir, $rev );
125
+
126
+ print "chdir $rev\n" if ( $DEBUG );
127
+ chdir ( $rev );
128
+
129
+ if ( $BUILD ) {
130
+ print "$BUILD\n" if ( $DEBUG );
131
+ `$BUILD`;
132
+ }
133
+
134
+ if ( exists &BUILD_SUITES ) {
135
+ &BUILD_SUITES();
136
+ }
137
+
138
+ foreach my $file ( glob($INJECT_FILE) ) {
139
+ my $inject_file = `cat $file`;
140
+
141
+ # Inject the TestSwarm injection script into the test suite
142
+ $inject_file =~ s/<\/head>/<script>document.write("<scr" + "ipt src='$SWARM$SWARM_INJECT?" + (new Date).getTime() + "'><\/scr" + "ipt>");<\/script><\/head>/;
143
+
144
+ open( F, ">$file" );
145
+ print F $inject_file;
146
+ close( F );
147
+ }
148
+
149
+ my %props = (
150
+ "state" => "addjob",
151
+ "output" => "dump",
152
+ "user" => $USER,
153
+ "max" => $MAX_RUNS,
154
+ "job_name" => $JOB_NAME,
155
+ "browsers" => $BROWSERS,
156
+ "auth" => $AUTH_TOKEN
157
+ );
158
+
159
+ my $query = "";
160
+
161
+ foreach my $prop ( keys %props ) {
162
+ $query .= ($query ? "&" : "") . $prop . "=" . clean($props{$prop});
163
+ }
164
+
165
+ foreach my $suite ( sort keys %SUITES ) {
166
+ $query .= "&suites[]=" . clean($suite) .
167
+ "&urls[]=" . clean($SUITES{$suite});
168
+ }
169
+
170
+ print "curl -d \"$query\" $SWARM\n" if ( $DEBUG );
171
+
172
+ my $results = `curl -d "$query" $SWARM`;
173
+
174
+ print "Results: $results\n" if ( $DEBUG );
175
+
176
+ if ( $results ) {
177
+ open( F, ">$rev/results.txt" );
178
+ print F "$SWARM$results";
179
+ close( F );
180
+
181
+ } else {
182
+ die "Job not submitted properly.";
183
+ }
184
+
185
+ # Otherwise, give up and clean up
186
+ } else {
187
+ remove_tmp();
188
+ }
189
+
190
+ sub remove_tmp {
191
+ chdir( $BASE_DIR );
192
+ `rm -rf $co_dir`;
193
+ }
194
+
195
+ sub clean {
196
+ my $str = shift;
197
+ $str =~ s/{REV}/$rev/g;
198
+ $str =~ s/([^A-Za-z0-9])/sprintf("%%%02X", ord($1))/seg;
199
+ $str;
200
+ }