danthes 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +7 -0
- data/.rspec +1 -0
- data/.rvmrc +1 -0
- data/.travis.yml +7 -0
- data/CHANGELOG.md +54 -0
- data/Gemfile +11 -0
- data/Guardfile +7 -0
- data/LICENSE +21 -0
- data/README.md +153 -0
- data/Rakefile +29 -0
- data/app/assets/javascripts/danthes.js.coffee +142 -0
- data/danthes.gemspec +17 -0
- data/lib/danthes/engine.rb +16 -0
- data/lib/danthes/faye_extension.rb +40 -0
- data/lib/danthes/version.rb +3 -0
- data/lib/danthes/view_helpers.rb +22 -0
- data/lib/danthes.rb +109 -0
- data/lib/generators/danthes/install_generator.rb +16 -0
- data/lib/generators/danthes/redis_install_generator.rb +17 -0
- data/lib/generators/danthes.rb +25 -0
- data/lib/generators/templates/danthes.ru +16 -0
- data/lib/generators/templates/danthes.yml +16 -0
- data/lib/generators/templates/danthes_redis.yml +14 -0
- data/spec/coffeescripts/danthes_spec.js.coffee +127 -0
- data/spec/danthes/faye_extension_spec.rb +67 -0
- data/spec/danthes_spec.rb +183 -0
- data/spec/fixtures/danthes.yml +10 -0
- data/spec/fixtures/danthes_redis.yml +6 -0
- data/spec/javascripts/support/jasmine.yml +6 -0
- data/spec/javascripts/support/jasmine_config.rb +23 -0
- data/spec/javascripts/support/jasmine_runner.rb +32 -0
- data/spec/spec_helper.rb +8 -0
- metadata +104 -0
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'generators/danthes'
|
2
|
+
|
3
|
+
module Danthes
|
4
|
+
module Generators
|
5
|
+
class RedisInstallGenerator < Base
|
6
|
+
desc 'Create sample redis config file and add faye-redis gem to Gemfile'
|
7
|
+
|
8
|
+
def copy_files
|
9
|
+
template "danthes_redis.yml", "config/danthes_redis.yml"
|
10
|
+
end
|
11
|
+
|
12
|
+
def add_redis_gem
|
13
|
+
add_gem 'faye-redis'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'rails/generators/base'
|
2
|
+
|
3
|
+
module Danthes
|
4
|
+
module Generators
|
5
|
+
class Base < Rails::Generators::Base
|
6
|
+
def self.source_root
|
7
|
+
File.dirname(__FILE__) + "/templates"
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.banner
|
11
|
+
"rails generate danthes:#{generator_name}"
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def add_gem(name, options = {})
|
17
|
+
gemfile_path = File.join(destination_root, 'Gemfile')
|
18
|
+
gemfile_content = File.read(gemfile_path)
|
19
|
+
File.open(gemfile_path, 'a') { |f| f.write("\n") } unless gemfile_content =~ /\n\Z/
|
20
|
+
gem name, options unless gemfile_content.include? name
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# Run with: rackup private_pub.ru -s thin -E production
|
2
|
+
require "bundler/setup"
|
3
|
+
require "yaml"
|
4
|
+
require "faye"
|
5
|
+
require "danthes"
|
6
|
+
require "thin"
|
7
|
+
|
8
|
+
Danthes.load_config(File.expand_path("../config/danthes.yml", __FILE__))
|
9
|
+
Faye::WebSocket.load_adapter(Danthes.config[:adapter])
|
10
|
+
|
11
|
+
path = File.expand_path("../config/danthes_redis.yml", __FILE__)
|
12
|
+
if File.exist?(path)
|
13
|
+
Danthes.load_redis_config(path)
|
14
|
+
end
|
15
|
+
|
16
|
+
run Danthes.faye_app
|
@@ -0,0 +1,16 @@
|
|
1
|
+
development:
|
2
|
+
adapter: thin
|
3
|
+
server: "http://localhost:9292"
|
4
|
+
secret_token: "secret"
|
5
|
+
mount: '/faye'
|
6
|
+
test:
|
7
|
+
adapter: thin
|
8
|
+
server: "http://localhost:9292"
|
9
|
+
secret_token: "secret"
|
10
|
+
mount: '/faye'
|
11
|
+
production:
|
12
|
+
adapter: thin
|
13
|
+
server: "http://faye.example.com"
|
14
|
+
mount: '/faye'
|
15
|
+
secret_token: "<%= defined?(SecureRandom) ? SecureRandom.hex(32) : ActiveSupport::SecureRandom.hex(32) %>"
|
16
|
+
signature_expiration: 3600 # one hour
|
@@ -0,0 +1,14 @@
|
|
1
|
+
development:
|
2
|
+
host: localhost
|
3
|
+
port: 6379
|
4
|
+
namespace: '/danthes'
|
5
|
+
test:
|
6
|
+
host: localhost
|
7
|
+
port: 6379
|
8
|
+
namespace: '/danthes'
|
9
|
+
production:
|
10
|
+
host: redis_host
|
11
|
+
port: redis_port
|
12
|
+
password: redis_password
|
13
|
+
database: redis_database
|
14
|
+
namespace: '/namespace'
|
@@ -0,0 +1,127 @@
|
|
1
|
+
describe "Danthes", ->
|
2
|
+
window.Faye = undefined
|
3
|
+
pub = undefined
|
4
|
+
|
5
|
+
signToChannel = (channel) ->
|
6
|
+
faye = {subscribe: jasmine.createSpy()}
|
7
|
+
spyOn(pub, 'faye').andCallFake (callback) ->
|
8
|
+
callback(faye)
|
9
|
+
options = {server: "server", channel: "#{channel}"}
|
10
|
+
pub.sign(options)
|
11
|
+
return [faye, options]
|
12
|
+
|
13
|
+
beforeEach ->
|
14
|
+
pub = window.Danthes
|
15
|
+
pub.reset()
|
16
|
+
script = document.getElementById('faye-connection-script')
|
17
|
+
if script?
|
18
|
+
script.parentNode.removeChild(script)
|
19
|
+
|
20
|
+
it "not adds a subscription callback without signing", ->
|
21
|
+
expect(pub.subscribe("hello", "callback")).toBe(false)
|
22
|
+
expect(pub.subscriptions).toEqual({})
|
23
|
+
|
24
|
+
it "adds a subscription callback", ->
|
25
|
+
signToChannel('hello')
|
26
|
+
pub.subscribe("hello", "callback")
|
27
|
+
expect(pub.subscriptions["hello"]['callback']).toEqual("callback")
|
28
|
+
|
29
|
+
it "has a fayeExtension which adds matching subscription signature and timestamp to outgoing message", ->
|
30
|
+
called = false
|
31
|
+
message = {channel: "/meta/subscribe", subscription: "hello"}
|
32
|
+
pub.subscriptions['hello'] = {}
|
33
|
+
pub.subscriptions['hello']['opts'] = {signature: "abcd", timestamp: "1234"}
|
34
|
+
pub.fayeExtension.outgoing message, (message) ->
|
35
|
+
expect(message.ext.danthes_signature).toEqual("abcd")
|
36
|
+
expect(message.ext.danthes_timestamp).toEqual("1234")
|
37
|
+
called = true
|
38
|
+
expect(called).toBeTruthy()
|
39
|
+
|
40
|
+
it "evaluates javascript in message response", ->
|
41
|
+
pub.handleResponse(eval: 'this.subscriptions.foo = "bar"')
|
42
|
+
expect(pub.subscriptions.foo).toEqual("bar")
|
43
|
+
|
44
|
+
it "triggers callback matching message channel in response", ->
|
45
|
+
called = false
|
46
|
+
signToChannel('test')
|
47
|
+
pub.subscribe "test", (data, channel) ->
|
48
|
+
expect(data).toEqual("abcd")
|
49
|
+
expect(channel).toEqual("test")
|
50
|
+
called = true
|
51
|
+
pub.handleResponse(channel: "test", data: "abcd")
|
52
|
+
expect(called).toBeTruthy()
|
53
|
+
|
54
|
+
it "triggers faye callback function immediately when fayeClient is available", ->
|
55
|
+
called = false
|
56
|
+
pub.fayeClient = "faye"
|
57
|
+
pub.faye (faye) ->
|
58
|
+
expect(faye).toEqual("faye")
|
59
|
+
called = true
|
60
|
+
expect(called).toBeTruthy()
|
61
|
+
|
62
|
+
it "adds fayeCallback when client and server aren't available", ->
|
63
|
+
pub.faye("callback")
|
64
|
+
expect(pub.fayeCallbacks[0]).toEqual("callback")
|
65
|
+
|
66
|
+
it "adds a script tag loading faye js when the server is present", ->
|
67
|
+
client = {addExtension: jasmine.createSpy()}
|
68
|
+
callback = jasmine.createSpy()
|
69
|
+
pub.server = "path/to/faye"
|
70
|
+
pub.faye(callback)
|
71
|
+
expect(pub.fayeCallbacks[0]).toEqual(callback)
|
72
|
+
script = document.getElementById('faye-connection-script')
|
73
|
+
expect(script).toBeDefined()
|
74
|
+
expect(script.type).toEqual("text/javascript")
|
75
|
+
expect(script.src).toMatch("path/to/faye/client.js")
|
76
|
+
|
77
|
+
it "adds a faye subscription with response handler when signing", ->
|
78
|
+
pub.fayeClient = 'string'
|
79
|
+
[faye, options] = signToChannel('somechannel')
|
80
|
+
expect(faye.subscribe).toHaveBeenCalled()
|
81
|
+
expect(pub.server).toEqual("server")
|
82
|
+
expect(pub.subscriptions.somechannel.opts).toEqual(options)
|
83
|
+
|
84
|
+
it "connects to faye server, adds extension, and executes callbacks", ->
|
85
|
+
callback = jasmine.createSpy()
|
86
|
+
client = {addExtension: jasmine.createSpy()}
|
87
|
+
window.Faye = {}
|
88
|
+
window.Faye.Client = (server) ->
|
89
|
+
expect(server).toEqual("server")
|
90
|
+
return client
|
91
|
+
pub.server = "server"
|
92
|
+
pub.fayeCallbacks.push(callback)
|
93
|
+
spyOn(pub, 'fayeExtension')
|
94
|
+
pub.connectToFaye()
|
95
|
+
expect(pub.fayeClient).toEqual(client)
|
96
|
+
expect(client.addExtension).toHaveBeenCalledWith(pub.fayeExtension)
|
97
|
+
expect(callback).toHaveBeenCalledWith(client)
|
98
|
+
|
99
|
+
it "adds subscription faye object into channel object", ->
|
100
|
+
sub = {callback: jasmine.createSpy(), errback: jasmine.createSpy()}
|
101
|
+
pub.fayeClient = {subscribe: jasmine.createSpy().andReturn(sub)}
|
102
|
+
options = {server: "server", channel: 'somechannel'}
|
103
|
+
pub.sign(options)
|
104
|
+
expect(sub.callback).toHaveBeenCalled()
|
105
|
+
expect(sub.errback).toHaveBeenCalled()
|
106
|
+
expect(pub.subscriptions.somechannel.sub).toEqual(sub)
|
107
|
+
|
108
|
+
it "removes subscription to the channel", ->
|
109
|
+
sub = {callback: jasmine.createSpy(), errback: jasmine.createSpy(), cancel: jasmine.createSpy()}
|
110
|
+
pub.fayeClient = {subscribe: jasmine.createSpy().andReturn(sub)}
|
111
|
+
options = {server: "server", channel: 'somechannel'}
|
112
|
+
pub.sign(options)
|
113
|
+
pub.unsubscribe('somechannel')
|
114
|
+
expect(sub.cancel).toHaveBeenCalled()
|
115
|
+
expect(pub.subscriptions.somechannel).toBeUndefined()
|
116
|
+
expect(pub.subscriptions).toEqual({})
|
117
|
+
|
118
|
+
it "removes all subscription to the channels", ->
|
119
|
+
sub = {callback: jasmine.createSpy(), errback: jasmine.createSpy(), cancel: jasmine.createSpy()}
|
120
|
+
pub.fayeClient = {subscribe: jasmine.createSpy().andReturn(sub)}
|
121
|
+
options = {server: "server", channel: 'somechannel'}
|
122
|
+
pub.sign(options)
|
123
|
+
pub.unsubscribeAll()
|
124
|
+
expect(sub.cancel).toHaveBeenCalled()
|
125
|
+
expect(pub.subscriptions.somechannel).toBeUndefined()
|
126
|
+
expect(pub.subscriptions).toEqual({})
|
127
|
+
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Danthes::FayeExtension do
|
4
|
+
before(:each) do
|
5
|
+
Danthes.startup
|
6
|
+
@faye = Danthes::FayeExtension.new
|
7
|
+
@message = {"channel" => "/meta/subscribe", "ext" => {}}
|
8
|
+
end
|
9
|
+
|
10
|
+
it "adds an error on an incoming subscription with a bad signature" do
|
11
|
+
@message["subscription"] = "hello"
|
12
|
+
@message["ext"]["danthes_signature"] = "bad"
|
13
|
+
@message["ext"]["danthes_timestamp"] = "123"
|
14
|
+
message = @faye.incoming(@message, lambda { |m| m })
|
15
|
+
message["error"].should eq("Incorrect signature.")
|
16
|
+
end
|
17
|
+
|
18
|
+
it "has no error when the signature matches the subscription" do
|
19
|
+
sub = Danthes.subscription(:channel => "hello")
|
20
|
+
@message["subscription"] = sub[:channel]
|
21
|
+
@message["ext"]["danthes_signature"] = sub[:signature]
|
22
|
+
@message["ext"]["danthes_timestamp"] = sub[:timestamp]
|
23
|
+
message = @faye.incoming(@message, lambda { |m| m })
|
24
|
+
message["error"].should be_nil
|
25
|
+
end
|
26
|
+
|
27
|
+
it "has an error when signature just expired" do
|
28
|
+
Danthes.config[:signature_expiration] = 1
|
29
|
+
sub = Danthes.subscription(:timestamp => 123, :channel => "hello")
|
30
|
+
@message["subscription"] = sub[:channel]
|
31
|
+
@message["ext"]["danthes_signature"] = sub[:signature]
|
32
|
+
@message["ext"]["danthes_timestamp"] = sub[:timestamp]
|
33
|
+
message = @faye.incoming(@message, lambda { |m| m })
|
34
|
+
message["error"].should eq("Signature has expired.")
|
35
|
+
end
|
36
|
+
|
37
|
+
it "has an error when trying to publish to a custom channel with a bad token" do
|
38
|
+
Danthes.config[:secret_token] = "good"
|
39
|
+
@message["channel"] = "/custom/channel"
|
40
|
+
@message["ext"]["danthes_token"] = "bad"
|
41
|
+
message = @faye.incoming(@message, lambda { |m| m })
|
42
|
+
message["error"].should eq("Incorrect token.")
|
43
|
+
end
|
44
|
+
|
45
|
+
it "raises an exception when attempting to call a custom channel without a secret_token set" do
|
46
|
+
@message["channel"] = "/custom/channel"
|
47
|
+
@message["ext"]["danthes_token"] = "bad"
|
48
|
+
lambda {
|
49
|
+
message = @faye.incoming(@message, lambda { |m| m })
|
50
|
+
}.should raise_error("No secret_token config set, ensure danthes.yml is loaded properly.")
|
51
|
+
end
|
52
|
+
|
53
|
+
it "has no error on other meta calls" do
|
54
|
+
@message["channel"] = "/meta/connect"
|
55
|
+
message = @faye.incoming(@message, lambda { |m| m })
|
56
|
+
message["error"].should be_nil
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should not let message carry the private pub token after server's validation" do
|
60
|
+
Danthes.config[:secret_token] = "good"
|
61
|
+
@message["channel"] = "/custom/channel"
|
62
|
+
@message["ext"]["danthes_token"] = Danthes.config[:secret_token]
|
63
|
+
message = @faye.incoming(@message, lambda { |m| m })
|
64
|
+
message['ext']["danthes_token"].should be_nil
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
@@ -0,0 +1,183 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Danthes do
|
4
|
+
before(:each) do
|
5
|
+
Danthes.startup
|
6
|
+
end
|
7
|
+
|
8
|
+
let(:config) { Danthes.config }
|
9
|
+
|
10
|
+
it "defaults server to nil" do
|
11
|
+
config[:server].should be_nil
|
12
|
+
end
|
13
|
+
|
14
|
+
it "defaults signature_expiration to nil" do
|
15
|
+
config[:signature_expiration].should be_nil
|
16
|
+
end
|
17
|
+
|
18
|
+
it "defaults subscription timestamp to current time in milliseconds" do
|
19
|
+
time = Time.now
|
20
|
+
Time.stub!(:now).and_return(time)
|
21
|
+
Danthes.subscription[:timestamp].should eq((time.to_f * 1000).round)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "loads a simple configuration file via load_config" do
|
25
|
+
Danthes.env = 'production'
|
26
|
+
Danthes.load_config("spec/fixtures/danthes.yml")
|
27
|
+
config[:server].should eq("http://example.com/faye")
|
28
|
+
config[:secret_token].should eq("PRODUCTION_SECRET_TOKEN")
|
29
|
+
config[:signature_expiration].should eq(600)
|
30
|
+
config[:adapter].should eq('thin')
|
31
|
+
end
|
32
|
+
|
33
|
+
context "when redis config exists" do
|
34
|
+
before do
|
35
|
+
Danthes.env = 'test'
|
36
|
+
Danthes.load_redis_config("spec/fixtures/danthes_redis.yml")
|
37
|
+
end
|
38
|
+
|
39
|
+
it "passes redis config to faye engine options" do
|
40
|
+
config[:engine][:type].should eq Faye::Redis
|
41
|
+
config[:engine][:host].should eq 'redis_host'
|
42
|
+
config[:engine][:port].should eq 'redis_port'
|
43
|
+
config[:engine][:password].should eq 'redis_password'
|
44
|
+
config[:engine][:database].should eq 'redis_database'
|
45
|
+
config[:engine][:namespace].should eq '/namespace'
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should pass redis config and default options to faye" do
|
49
|
+
Faye::RackAdapter.should_receive(:new) do |options|
|
50
|
+
options[:engine].should eq Danthes.config[:engine]
|
51
|
+
options[:mount].should eq '/faye'
|
52
|
+
end
|
53
|
+
Danthes.faye_app
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context "when redis config does not exist" do
|
58
|
+
it "should not have :engine inside of config hash" do
|
59
|
+
config.should_not include :engine
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should have mount point" do
|
63
|
+
config[:mount].should eq '/faye'
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
it "raises an exception if an invalid environment is passed to load_config" do
|
68
|
+
lambda {
|
69
|
+
Danthes.load_config("spec/fixtures/danthes.yml", 'foo')
|
70
|
+
}.should raise_error ArgumentError
|
71
|
+
end
|
72
|
+
|
73
|
+
it "includes channel, server, and custom time in subscription" do
|
74
|
+
Danthes.config[:server] = "server"
|
75
|
+
Danthes.config[:mount] = '/faye'
|
76
|
+
subscription = Danthes.subscription(:timestamp => 123, :channel => "hello")
|
77
|
+
subscription[:timestamp].should eq(123)
|
78
|
+
subscription[:channel].should eq("hello")
|
79
|
+
subscription[:server].should eq("server/faye")
|
80
|
+
end
|
81
|
+
|
82
|
+
it "returns full server url from server and mount configs" do
|
83
|
+
Danthes.config[:server] = "server.com"
|
84
|
+
Danthes.config[:mount] = '/faye'
|
85
|
+
Danthes.server_url.should == 'server.com/faye'
|
86
|
+
end
|
87
|
+
|
88
|
+
it "does a sha1 digest of channel, timestamp, and secret token" do
|
89
|
+
Danthes.config[:secret_token] = "token"
|
90
|
+
subscription = Danthes.subscription(:timestamp => 123, :channel => "channel")
|
91
|
+
subscription[:signature].should eq(Digest::SHA1.hexdigest("tokenchannel123"))
|
92
|
+
end
|
93
|
+
|
94
|
+
it "formats a message hash given a channel and a string for eval" do
|
95
|
+
Danthes.config[:secret_token] = "token"
|
96
|
+
Danthes.message("chan", "foo").should eq(
|
97
|
+
:ext => {:danthes_token => "token"},
|
98
|
+
:channel => "chan",
|
99
|
+
:data => {
|
100
|
+
:channel => "chan",
|
101
|
+
:eval => "foo"
|
102
|
+
}
|
103
|
+
)
|
104
|
+
end
|
105
|
+
|
106
|
+
it "formats a message hash given a channel and a hash" do
|
107
|
+
Danthes.config[:secret_token] = "token"
|
108
|
+
Danthes.message("chan", :foo => "bar").should eq(
|
109
|
+
:ext => {:danthes_token => "token"},
|
110
|
+
:channel => "chan",
|
111
|
+
:data => {
|
112
|
+
:channel => "chan",
|
113
|
+
:data => {:foo => "bar"}
|
114
|
+
}
|
115
|
+
)
|
116
|
+
end
|
117
|
+
|
118
|
+
it "publish message as json to server using Net::HTTP" do
|
119
|
+
Danthes.config[:server] = "http://localhost"
|
120
|
+
Danthes.config[:mount] = '/faye/path'
|
121
|
+
message = 'foo'
|
122
|
+
faye = stub_request(:post, "http://localhost/faye/path").
|
123
|
+
with(:body => {"message"=>"\"foo\""},
|
124
|
+
:headers => {'Accept'=>'*/*', 'Content-Type'=>'application/x-www-form-urlencoded', 'User-Agent'=>'Ruby'}).
|
125
|
+
to_return(:status => 200, :body => "", :headers => {})
|
126
|
+
Danthes.publish_message(message)
|
127
|
+
faye.should have_been_made.once
|
128
|
+
end
|
129
|
+
|
130
|
+
it "it should use HTTPS if the server URL says so" do
|
131
|
+
Danthes.config[:server] = "https://localhost"
|
132
|
+
Danthes.config[:mount] = '/faye/path'
|
133
|
+
http = mock(:http).as_null_object
|
134
|
+
|
135
|
+
Net::HTTP.should_receive(:new).and_return(http)
|
136
|
+
http.should_receive(:use_ssl=).with(true)
|
137
|
+
|
138
|
+
Danthes.publish_message('foo')
|
139
|
+
end
|
140
|
+
|
141
|
+
it "it should not use HTTPS if the server URL says not to" do
|
142
|
+
Danthes.config[:server] = "http://localhost"
|
143
|
+
http = mock(:http).as_null_object
|
144
|
+
|
145
|
+
Net::HTTP.should_receive(:new).and_return(http)
|
146
|
+
http.should_receive(:use_ssl=).with(false)
|
147
|
+
|
148
|
+
Danthes.publish_message('foo')
|
149
|
+
end
|
150
|
+
|
151
|
+
it "raises an exception if no server is specified when calling publish_message" do
|
152
|
+
lambda {
|
153
|
+
Danthes.publish_message("foo")
|
154
|
+
}.should raise_error(Danthes::Error)
|
155
|
+
end
|
156
|
+
|
157
|
+
it "publish_to passes message to publish_message call" do
|
158
|
+
Danthes.should_receive(:message).with("chan", "foo").and_return("message")
|
159
|
+
Danthes.should_receive(:publish_message).with("message").and_return(:result)
|
160
|
+
Danthes.publish_to("chan", "foo").should eq(:result)
|
161
|
+
end
|
162
|
+
|
163
|
+
it "has a Faye rack app instance" do
|
164
|
+
Danthes.faye_app.should be_kind_of(Faye::RackAdapter)
|
165
|
+
end
|
166
|
+
|
167
|
+
it "says signature has expired when time passed in is greater than expiration" do
|
168
|
+
Danthes.config[:signature_expiration] = 30*60
|
169
|
+
time = Danthes.subscription[:timestamp] - 31*60*1000
|
170
|
+
Danthes.signature_expired?(time).should be_true
|
171
|
+
end
|
172
|
+
|
173
|
+
it "says signature has not expired when time passed in is less than expiration" do
|
174
|
+
Danthes.config[:signature_expiration] = 30*60
|
175
|
+
time = Danthes.subscription[:timestamp] - 29*60*1000
|
176
|
+
Danthes.signature_expired?(time).should be_false
|
177
|
+
end
|
178
|
+
|
179
|
+
it "says signature has not expired when expiration is nil" do
|
180
|
+
Danthes.config[:signature_expiration] = nil
|
181
|
+
Danthes.signature_expired?(0).should be_false
|
182
|
+
end
|
183
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
development:
|
2
|
+
adapter: thin
|
3
|
+
server: http://dev.local:9292/faye
|
4
|
+
secret_token: DEVELOPMENT_SECRET_TOKEN
|
5
|
+
signature_expiration: 600
|
6
|
+
production:
|
7
|
+
adapter: thin
|
8
|
+
server: http://example.com/faye
|
9
|
+
secret_token: PRODUCTION_SECRET_TOKEN
|
10
|
+
signature_expiration: 600
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Jasmine
|
2
|
+
class Config
|
3
|
+
|
4
|
+
# Add your overrides or custom config code here
|
5
|
+
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
|
10
|
+
# Note - this is necessary for rspec2, which has removed the backtrace
|
11
|
+
module Jasmine
|
12
|
+
class SpecBuilder
|
13
|
+
def declare_spec(parent, spec)
|
14
|
+
me = self
|
15
|
+
example_name = spec["name"]
|
16
|
+
@spec_ids << spec["id"]
|
17
|
+
backtrace = @example_locations[parent.description + " " + example_name]
|
18
|
+
parent.it example_name, {} do
|
19
|
+
me.report_spec(spec["id"])
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
$:.unshift(ENV['JASMINE_GEM_PATH']) if ENV['JASMINE_GEM_PATH'] # for gem testing purposes
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'jasmine'
|
5
|
+
jasmine_config_overrides = File.expand_path(File.join(File.dirname(__FILE__), 'jasmine_config.rb'))
|
6
|
+
require jasmine_config_overrides if File.exist?(jasmine_config_overrides)
|
7
|
+
if Jasmine::Dependencies.rspec2?
|
8
|
+
require 'rspec'
|
9
|
+
else
|
10
|
+
require 'spec'
|
11
|
+
end
|
12
|
+
|
13
|
+
jasmine_config = Jasmine::Config.new
|
14
|
+
spec_builder = Jasmine::SpecBuilder.new(jasmine_config)
|
15
|
+
|
16
|
+
should_stop = false
|
17
|
+
|
18
|
+
if Jasmine::Dependencies.rspec2?
|
19
|
+
RSpec.configuration.after(:suite) do
|
20
|
+
spec_builder.stop if should_stop
|
21
|
+
end
|
22
|
+
else
|
23
|
+
Spec::Runner.configure do |config|
|
24
|
+
config.after(:suite) do
|
25
|
+
spec_builder.stop if should_stop
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
spec_builder.start
|
31
|
+
should_stop = true
|
32
|
+
spec_builder.declare_suites
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: danthes
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Alexander Simonov
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-08-26 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: faye
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 0.8.0
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 0.8.0
|
30
|
+
description: Private pub/sub messaging in Rails through Faye. More Faye features supported.
|
31
|
+
Based on PrivatePub.
|
32
|
+
email:
|
33
|
+
- alex@simonov.me
|
34
|
+
executables: []
|
35
|
+
extensions: []
|
36
|
+
extra_rdoc_files: []
|
37
|
+
files:
|
38
|
+
- .gitignore
|
39
|
+
- .rspec
|
40
|
+
- .rvmrc
|
41
|
+
- .travis.yml
|
42
|
+
- CHANGELOG.md
|
43
|
+
- Gemfile
|
44
|
+
- Guardfile
|
45
|
+
- LICENSE
|
46
|
+
- README.md
|
47
|
+
- Rakefile
|
48
|
+
- app/assets/javascripts/danthes.js.coffee
|
49
|
+
- danthes.gemspec
|
50
|
+
- lib/danthes.rb
|
51
|
+
- lib/danthes/engine.rb
|
52
|
+
- lib/danthes/faye_extension.rb
|
53
|
+
- lib/danthes/version.rb
|
54
|
+
- lib/danthes/view_helpers.rb
|
55
|
+
- lib/generators/danthes.rb
|
56
|
+
- lib/generators/danthes/install_generator.rb
|
57
|
+
- lib/generators/danthes/redis_install_generator.rb
|
58
|
+
- lib/generators/templates/danthes.ru
|
59
|
+
- lib/generators/templates/danthes.yml
|
60
|
+
- lib/generators/templates/danthes_redis.yml
|
61
|
+
- spec/coffeescripts/danthes_spec.js.coffee
|
62
|
+
- spec/danthes/faye_extension_spec.rb
|
63
|
+
- spec/danthes_spec.rb
|
64
|
+
- spec/fixtures/danthes.yml
|
65
|
+
- spec/fixtures/danthes_redis.yml
|
66
|
+
- spec/javascripts/support/jasmine.yml
|
67
|
+
- spec/javascripts/support/jasmine_config.rb
|
68
|
+
- spec/javascripts/support/jasmine_runner.rb
|
69
|
+
- spec/spec_helper.rb
|
70
|
+
homepage: http://github.com/phenomena/danthes
|
71
|
+
licenses: []
|
72
|
+
post_install_message:
|
73
|
+
rdoc_options: []
|
74
|
+
require_paths:
|
75
|
+
- lib
|
76
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
77
|
+
none: false
|
78
|
+
requirements:
|
79
|
+
- - ! '>='
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '0'
|
82
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
83
|
+
none: false
|
84
|
+
requirements:
|
85
|
+
- - ! '>='
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '0'
|
88
|
+
requirements: []
|
89
|
+
rubyforge_project:
|
90
|
+
rubygems_version: 1.8.24
|
91
|
+
signing_key:
|
92
|
+
specification_version: 3
|
93
|
+
summary: Private pub/sub messaging through Faye.
|
94
|
+
test_files:
|
95
|
+
- spec/coffeescripts/danthes_spec.js.coffee
|
96
|
+
- spec/danthes/faye_extension_spec.rb
|
97
|
+
- spec/danthes_spec.rb
|
98
|
+
- spec/fixtures/danthes.yml
|
99
|
+
- spec/fixtures/danthes_redis.yml
|
100
|
+
- spec/javascripts/support/jasmine.yml
|
101
|
+
- spec/javascripts/support/jasmine_config.rb
|
102
|
+
- spec/javascripts/support/jasmine_runner.rb
|
103
|
+
- spec/spec_helper.rb
|
104
|
+
has_rdoc:
|