danthes 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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,6 @@
1
+ test:
2
+ host: redis_host
3
+ port: redis_port
4
+ password: redis_password
5
+ database: redis_database
6
+ namespace: '/namespace'
@@ -0,0 +1,6 @@
1
+ src_dir: app/assets/javascripts/compiled
2
+
3
+ src_files:
4
+ - danthes.js
5
+
6
+ spec_dir: spec/coffeescripts/compiled
@@ -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
@@ -0,0 +1,8 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+ require 'faye'
4
+ require 'faye/redis'
5
+ Bundler.require(:default)
6
+ require 'webmock/rspec'
7
+ RSpec.configure do |config|
8
+ end
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: