private_pub_plus 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +3 -0
- data/LICENSE +20 -0
- data/README.md +43 -0
- data/Rakefile +10 -0
- data/lib/generators/private_pub_plus/install_generator.rb +14 -0
- data/lib/generators/private_pub_plus/templates/private_pub.ru +45 -0
- data/lib/generators/private_pub_plus/templates/private_pub.yml +10 -0
- data/lib/private_pub_plus/engine.rb +14 -0
- data/lib/private_pub_plus/faye_extension.rb +39 -0
- data/lib/private_pub_plus/version.rb +3 -0
- data/lib/private_pub_plus.rb +85 -0
- data/spec/fixtures/private_pub.yml +8 -0
- data/spec/private_pub_plus_spec.rb +141 -0
- data/spec/spec_helper.rb +9 -0
- metadata +100 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b4ac9b51e06591b63f0129bf5d46d41ff2abb3bf
|
4
|
+
data.tar.gz: 8ab9a851ac353c79bde3218e7eaf98eadde9d998
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 92681a6f81ef940a9e07357626cec5bca7ffc9983118bf3a38c0e2c09d9ffe35cab9b4e979e6db227ea0800f81d2ed948c7dac4cec862053b2c43aabc23353f2
|
7
|
+
data.tar.gz: 88ed9116c56e5bbee7f11e8da6d5f79a4952cf8483fee5ca0afe1b43eff887c5fbacdc9b83ff21d60e04c4c250529f97c111acbb4f6651f5d6f038478061e4a7
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2012 Ryan Bates
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
# Private Pub Plus
|
2
|
+
|
3
|
+
Private Pub Plus is a Ruby gem for use with Rails to publish and subscribe to messages through [Faye](http://faye.jcoglan.com/). It allows you to easily provide real-time updates through an open socket without tying up a Rails process. All channels are private so users can only listen to events you subscribe them to.
|
4
|
+
|
5
|
+
Inspired by [Private pub Gem](https://github.com/ryanb/private_pub)
|
6
|
+
Watch [RailsCasts Episode 316](http://railscasts.com/episodes/316-private-pub) for a demonstration of Private Pub.
|
7
|
+
|
8
|
+
|
9
|
+
## Setup
|
10
|
+
|
11
|
+
Add the gem to your Gemfile and run the `bundle` command to install it. You'll probably want to add "thin" to your Gemfile as well to serve Faye.
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
gem "private_pub_plus", git: "git://github.com/amritsingh/private_pub.git"
|
15
|
+
gem "thin"
|
16
|
+
```
|
17
|
+
|
18
|
+
Run the generator to create the initial files.
|
19
|
+
|
20
|
+
```
|
21
|
+
rails g private_pub_plus:install
|
22
|
+
```
|
23
|
+
This will generate a file in Rails root directory - 'private_pub.ru'.
|
24
|
+
Authentication of subscribe and publish messages can be modified based on the needs of the application.
|
25
|
+
|
26
|
+
|
27
|
+
Next, start up Faye using the rackup file that was generated.
|
28
|
+
|
29
|
+
```
|
30
|
+
rackup private_pub.ru -s thin -E production
|
31
|
+
```
|
32
|
+
|
33
|
+
## Usage
|
34
|
+
|
35
|
+
Use the `publish_to` method to send data to that channel.
|
36
|
+
|
37
|
+
```ruby
|
38
|
+
PrivatePubPlus.publish_to "/messages/new", :chat_message => "Hello, world!" do |channel, data|
|
39
|
+
# construct a message here. This message is sent to the subscribed clients e.g.
|
40
|
+
# {:channel => channel, :data => {:channel => channel, :data => data, :time => Time.now}, :ext => {:private_pub_token => <secret_token from config/private_pub.yml>}}
|
41
|
+
end
|
42
|
+
```
|
43
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
module PrivatePubPlus
|
2
|
+
module Generators
|
3
|
+
class InstallGenerator < Rails::Generators::Base
|
4
|
+
def self.source_root
|
5
|
+
File.dirname(__FILE__) + "/templates"
|
6
|
+
end
|
7
|
+
|
8
|
+
def copy_files
|
9
|
+
template "private_pub.yml", "config/private_pub.yml"
|
10
|
+
copy_file "private_pub.ru", "private_pub.ru"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# Run with: rackup private_pub.ru -s thin -E production
|
2
|
+
require "bundler/setup"
|
3
|
+
require "yaml"
|
4
|
+
require "faye"
|
5
|
+
require "private_pub"
|
6
|
+
|
7
|
+
module PrivatePubPlus
|
8
|
+
# This class is an extension for the Faye::RackAdapter.
|
9
|
+
# It is used inside of PrivatePubPlus.faye_app.
|
10
|
+
class FayeExtension
|
11
|
+
# Callback to handle incoming Faye messages. This authenticates both
|
12
|
+
# subscribe and publish calls.
|
13
|
+
def incoming(message, callback)
|
14
|
+
if message["channel"] == "/meta/subscribe"
|
15
|
+
authenticate_subscribe(message)
|
16
|
+
elsif message["channel"] !~ %r{^/meta/}
|
17
|
+
authenticate_publish(message)
|
18
|
+
end
|
19
|
+
callback.call(message)
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
def authenticate_subscribe(message)
|
24
|
+
# TODO:
|
25
|
+
# Code to authenticate the subscribe message.
|
26
|
+
end
|
27
|
+
|
28
|
+
def authenticate_publish(message)
|
29
|
+
# TODO:
|
30
|
+
# Code to authenticate the publish message
|
31
|
+
if PrivatePubPlus.config[:secret_token].nil?
|
32
|
+
raise Error, "No secret_token config set, ensure private_pub.yml is loaded properly."
|
33
|
+
elsif message["ext"]["private_pub_token"] != PrivatePubPlus.config[:secret_token]
|
34
|
+
message["error"] = "Incorrect token."
|
35
|
+
else
|
36
|
+
message["ext"]["private_pub_token"] = nil
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
Faye::WebSocket.load_adapter('thin')
|
43
|
+
|
44
|
+
PrivatePubPlus.load_config(File.expand_path("../config/private_pub.yml", __FILE__), ENV["RAILS_ENV"] || "development")
|
45
|
+
run PrivatePubPlus.faye_app
|
@@ -0,0 +1,10 @@
|
|
1
|
+
development:
|
2
|
+
server: "http://localhost:9292/faye"
|
3
|
+
secret_token: "secret"
|
4
|
+
test:
|
5
|
+
server: "http://localhost:9292/faye"
|
6
|
+
secret_token: "secret"
|
7
|
+
production:
|
8
|
+
server: "http://example.com/faye"
|
9
|
+
secret_token: "<%= defined?(SecureRandom) ? SecureRandom.hex(32) : ActiveSupport::SecureRandom.hex(32) %>"
|
10
|
+
signature_expiration: 3600 # one hour
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module PrivatePubPlus
|
2
|
+
class Engine < Rails::Engine
|
3
|
+
# Loads the private_pub.yml file if it exists.
|
4
|
+
initializer "private_pub.config" do
|
5
|
+
path = Rails.root.join("config/private_pub.yml")
|
6
|
+
PrivatePubPlus.load_config(path, Rails.env) if path.exist?
|
7
|
+
end
|
8
|
+
|
9
|
+
# Adds the ViewHelpers into ActionView::Base
|
10
|
+
#initializer "private_pub.view_helpers" do
|
11
|
+
# ActionView::Base.send :include, ViewHelpers
|
12
|
+
#end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module PrivatePubPlus
|
2
|
+
# This class is an extension for the Faye::RackAdapter.
|
3
|
+
# It is used inside of PrivatePubPlus.faye_app.
|
4
|
+
class FayeExtension
|
5
|
+
# Callback to handle incoming Faye messages. This authenticates both
|
6
|
+
# subscribe and publish calls.
|
7
|
+
def incoming(message, callback)
|
8
|
+
if message["channel"] == "/meta/subscribe"
|
9
|
+
authenticate_subscribe(message)
|
10
|
+
elsif message["channel"] !~ %r{^/meta/}
|
11
|
+
authenticate_publish(message)
|
12
|
+
end
|
13
|
+
callback.call(message)
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
# Ensure the subscription signature is correct and that it has not expired.
|
19
|
+
def authenticate_subscribe(message)
|
20
|
+
subscription = PrivatePubPlus.subscription(:channel => message["subscription"], :timestamp => message["ext"]["private_pub_timestamp"])
|
21
|
+
if message["ext"]["private_pub_signature"] != subscription[:signature]
|
22
|
+
message["error"] = "Incorrect signature."
|
23
|
+
elsif PrivatePubPlus.signature_expired? message["ext"]["private_pub_timestamp"].to_i
|
24
|
+
message["error"] = "Signature has expired."
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Ensures the secret token is correct before publishing.
|
29
|
+
def authenticate_publish(message)
|
30
|
+
if PrivatePubPlus.config[:secret_token].nil?
|
31
|
+
raise Error, "No secret_token config set, ensure private_pub.yml is loaded properly."
|
32
|
+
elsif message["ext"]["private_pub_token"] != PrivatePubPlus.config[:secret_token]
|
33
|
+
message["error"] = "Incorrect token."
|
34
|
+
else
|
35
|
+
message["ext"]["private_pub_token"] = nil
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require "digest/sha1"
|
2
|
+
require "net/http"
|
3
|
+
require "net/https"
|
4
|
+
require "yaml"
|
5
|
+
|
6
|
+
require "private_pub_plus/faye_extension"
|
7
|
+
require "private_pub_plus/engine" if defined? Rails
|
8
|
+
|
9
|
+
module PrivatePubPlus
|
10
|
+
class Error < StandardError; end
|
11
|
+
|
12
|
+
class << self
|
13
|
+
attr_reader :config
|
14
|
+
|
15
|
+
# Resets the configuration to the default (empty hash)
|
16
|
+
def reset_config
|
17
|
+
@config = {}
|
18
|
+
end
|
19
|
+
|
20
|
+
# Loads the configuration from a given YAML file and environment (such as production)
|
21
|
+
def load_config(filename, environment)
|
22
|
+
yaml = YAML.load_file(filename)[environment.to_s]
|
23
|
+
raise ArgumentError, "The #{environment} environment does not exist in #{filename}" if yaml.nil?
|
24
|
+
yaml.each { |k, v| config[k.to_sym] = v }
|
25
|
+
end
|
26
|
+
|
27
|
+
# Publish the given data to a specific channel. This ends up sending
|
28
|
+
# a Net::HTTP POST request to the Faye server.
|
29
|
+
def publish_to(channel, data, &block)
|
30
|
+
m = nil
|
31
|
+
if block_given?
|
32
|
+
m = block.call(channel, data)
|
33
|
+
else
|
34
|
+
m = message(channel, data)
|
35
|
+
end
|
36
|
+
publish_message(m)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Sends the given message hash to the Faye server using Net::HTTP.
|
40
|
+
def publish_message(message)
|
41
|
+
raise Error, "No server specified, ensure private_pub.yml was loaded properly." unless config[:server]
|
42
|
+
url = URI.parse(config[:server])
|
43
|
+
|
44
|
+
form = Net::HTTP::Post.new(url.path.empty? ? '/' : url.path)
|
45
|
+
form.set_form_data(:message => message.to_json)
|
46
|
+
|
47
|
+
http = Net::HTTP.new(url.host, url.port)
|
48
|
+
http.use_ssl = url.scheme == "https"
|
49
|
+
http.start {|h| h.request(form)}
|
50
|
+
end
|
51
|
+
|
52
|
+
# Returns a message hash for sending to Faye
|
53
|
+
def message(channel, data)
|
54
|
+
message = {:channel => channel, :data => {:channel => channel}, :ext => {:private_pub_token => config[:secret_token]}}
|
55
|
+
if data.kind_of? String
|
56
|
+
message[:data][:eval] = data
|
57
|
+
else
|
58
|
+
message[:data][:data] = data
|
59
|
+
end
|
60
|
+
message
|
61
|
+
end
|
62
|
+
|
63
|
+
# Returns a subscription hash to pass to the PrivatePubPlus.sign call in JavaScript.
|
64
|
+
# Any options passed are merged to the hash.
|
65
|
+
def subscription(options = {})
|
66
|
+
sub = {:server => config[:server], :timestamp => (Time.now.to_f * 1000).round}.merge(options)
|
67
|
+
sub[:signature] = Digest::SHA1.hexdigest([config[:secret_token], sub[:channel], sub[:timestamp]].join)
|
68
|
+
sub
|
69
|
+
end
|
70
|
+
|
71
|
+
# Determine if the signature has expired given a timestamp.
|
72
|
+
def signature_expired?(timestamp)
|
73
|
+
timestamp < ((Time.now.to_f - config[:signature_expiration])*1000).round if config[:signature_expiration]
|
74
|
+
end
|
75
|
+
|
76
|
+
# Returns the Faye Rack application.
|
77
|
+
# Any options given are passed to the Faye::RackAdapter.
|
78
|
+
def faye_app(options = {})
|
79
|
+
options = {:mount => "/faye", :timeout => 45, :extensions => [FayeExtension.new]}.merge(options)
|
80
|
+
Faye::RackAdapter.new(options)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
reset_config
|
85
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe PrivatePubPlus do
|
4
|
+
before(:each) do
|
5
|
+
PrivatePubPlus.reset_config
|
6
|
+
end
|
7
|
+
|
8
|
+
it "defaults server to nil" do
|
9
|
+
PrivatePubPlus.config[:server].should be_nil
|
10
|
+
end
|
11
|
+
|
12
|
+
it "defaults signature_expiration to nil" do
|
13
|
+
PrivatePubPlus.config[:signature_expiration].should be_nil
|
14
|
+
end
|
15
|
+
|
16
|
+
it "defaults subscription timestamp to current time in milliseconds" do
|
17
|
+
time = Time.now
|
18
|
+
Time.stub!(:now).and_return(time)
|
19
|
+
PrivatePubPlus.subscription[:timestamp].should eq((time.to_f * 1000).round)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "loads a simple configuration file via load_config" do
|
23
|
+
PrivatePubPlus.load_config("spec/fixtures/private_pub.yml", "production")
|
24
|
+
PrivatePubPlus.config[:server].should eq("http://example.com/faye")
|
25
|
+
PrivatePubPlus.config[:secret_token].should eq("PRODUCTION_SECRET_TOKEN")
|
26
|
+
PrivatePubPlus.config[:signature_expiration].should eq(600)
|
27
|
+
end
|
28
|
+
|
29
|
+
it "raises an exception if an invalid environment is passed to load_config" do
|
30
|
+
lambda {
|
31
|
+
PrivatePubPlus.load_config("spec/fixtures/private_pub.yml", :test)
|
32
|
+
}.should raise_error ArgumentError
|
33
|
+
end
|
34
|
+
|
35
|
+
it "includes channel, server, and custom time in subscription" do
|
36
|
+
PrivatePubPlus.config[:server] = "server"
|
37
|
+
subscription = PrivatePubPlus.subscription(:timestamp => 123, :channel => "hello")
|
38
|
+
subscription[:timestamp].should eq(123)
|
39
|
+
subscription[:channel].should eq("hello")
|
40
|
+
subscription[:server].should eq("server")
|
41
|
+
end
|
42
|
+
|
43
|
+
it "does a sha1 digest of channel, timestamp, and secret token" do
|
44
|
+
PrivatePubPlus.config[:secret_token] = "token"
|
45
|
+
subscription = PrivatePubPlus.subscription(:timestamp => 123, :channel => "channel")
|
46
|
+
subscription[:signature].should eq(Digest::SHA1.hexdigest("tokenchannel123"))
|
47
|
+
end
|
48
|
+
|
49
|
+
it "formats a message hash given a channel and a string for eval" do
|
50
|
+
PrivatePubPlus.config[:secret_token] = "token"
|
51
|
+
PrivatePubPlus.message("chan", "foo").should eq(
|
52
|
+
:ext => {:private_pub_token => "token"},
|
53
|
+
:channel => "chan",
|
54
|
+
:data => {
|
55
|
+
:channel => "chan",
|
56
|
+
:eval => "foo"
|
57
|
+
}
|
58
|
+
)
|
59
|
+
end
|
60
|
+
|
61
|
+
it "formats a message hash given a channel and a hash" do
|
62
|
+
PrivatePubPlus.config[:secret_token] = "token"
|
63
|
+
PrivatePubPlus.message("chan", :foo => "bar").should eq(
|
64
|
+
:ext => {:private_pub_token => "token"},
|
65
|
+
:channel => "chan",
|
66
|
+
:data => {
|
67
|
+
:channel => "chan",
|
68
|
+
:data => {:foo => "bar"}
|
69
|
+
}
|
70
|
+
)
|
71
|
+
end
|
72
|
+
|
73
|
+
it "publish message as json to server using Net::HTTP" do
|
74
|
+
PrivatePubPlus.config[:server] = "http://localhost"
|
75
|
+
message = 'foo'
|
76
|
+
form = mock(:post).as_null_object
|
77
|
+
http = mock(:http).as_null_object
|
78
|
+
|
79
|
+
Net::HTTP::Post.should_receive(:new).with('/').and_return(form)
|
80
|
+
form.should_receive(:set_form_data).with(message: 'foo'.to_json)
|
81
|
+
|
82
|
+
Net::HTTP.should_receive(:new).with('localhost', 80).and_return(http)
|
83
|
+
http.should_receive(:start).and_yield(http)
|
84
|
+
http.should_receive(:request).with(form).and_return(:result)
|
85
|
+
|
86
|
+
PrivatePubPlus.publish_message(message).should eq(:result)
|
87
|
+
end
|
88
|
+
|
89
|
+
it "it should use HTTPS if the server URL says so" do
|
90
|
+
PrivatePubPlus.config[:server] = "https://localhost"
|
91
|
+
http = mock(:http).as_null_object
|
92
|
+
|
93
|
+
Net::HTTP.should_receive(:new).and_return(http)
|
94
|
+
http.should_receive(:use_ssl=).with(true)
|
95
|
+
|
96
|
+
PrivatePubPlus.publish_message('foo')
|
97
|
+
end
|
98
|
+
|
99
|
+
it "it should not use HTTPS if the server URL says not to" do
|
100
|
+
PrivatePubPlus.config[:server] = "http://localhost"
|
101
|
+
http = mock(:http).as_null_object
|
102
|
+
|
103
|
+
Net::HTTP.should_receive(:new).and_return(http)
|
104
|
+
http.should_receive(:use_ssl=).with(false)
|
105
|
+
|
106
|
+
PrivatePubPlus.publish_message('foo')
|
107
|
+
end
|
108
|
+
|
109
|
+
it "raises an exception if no server is specified when calling publish_message" do
|
110
|
+
lambda {
|
111
|
+
PrivatePubPlus.publish_message("foo")
|
112
|
+
}.should raise_error(PrivatePubPlus::Error)
|
113
|
+
end
|
114
|
+
|
115
|
+
it "publish_to passes message to publish_message call" do
|
116
|
+
PrivatePubPlus.should_receive(:message).with("chan", "foo").and_return("message")
|
117
|
+
PrivatePubPlus.should_receive(:publish_message).with("message").and_return(:result)
|
118
|
+
PrivatePubPlus.publish_to("chan", "foo").should eq(:result)
|
119
|
+
end
|
120
|
+
|
121
|
+
it "has a Faye rack app instance" do
|
122
|
+
PrivatePubPlus.faye_app.should be_kind_of(Faye::RackAdapter)
|
123
|
+
end
|
124
|
+
|
125
|
+
it "says signature has expired when time passed in is greater than expiration" do
|
126
|
+
PrivatePubPlus.config[:signature_expiration] = 30*60
|
127
|
+
time = PrivatePubPlus.subscription[:timestamp] - 31*60*1000
|
128
|
+
PrivatePubPlus.signature_expired?(time).should be_true
|
129
|
+
end
|
130
|
+
|
131
|
+
it "says signature has not expired when time passed in is less than expiration" do
|
132
|
+
PrivatePubPlus.config[:signature_expiration] = 30*60
|
133
|
+
time = PrivatePubPlus.subscription[:timestamp] - 29*60*1000
|
134
|
+
PrivatePubPlus.signature_expired?(time).should be_false
|
135
|
+
end
|
136
|
+
|
137
|
+
it "says signature has not expired when expiration is nil" do
|
138
|
+
PrivatePubPlus.config[:signature_expiration] = nil
|
139
|
+
PrivatePubPlus.signature_expired?(0).should be_false
|
140
|
+
end
|
141
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: private_pub_plus
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Amrit Singh
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-04-15 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: faye
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 2.8.0
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 2.8.0
|
55
|
+
description: Private pub/sub messaging in Rails through Faye. Modified the private
|
56
|
+
pub gem from https://github.com/ryanb/private_pub to make it customizable. Removed
|
57
|
+
js support
|
58
|
+
email: amrit0403@gmail.com
|
59
|
+
executables: []
|
60
|
+
extensions: []
|
61
|
+
extra_rdoc_files: []
|
62
|
+
files:
|
63
|
+
- Gemfile
|
64
|
+
- LICENSE
|
65
|
+
- README.md
|
66
|
+
- Rakefile
|
67
|
+
- lib/generators/private_pub_plus/install_generator.rb
|
68
|
+
- lib/generators/private_pub_plus/templates/private_pub.ru
|
69
|
+
- lib/generators/private_pub_plus/templates/private_pub.yml
|
70
|
+
- lib/private_pub_plus.rb
|
71
|
+
- lib/private_pub_plus/engine.rb
|
72
|
+
- lib/private_pub_plus/faye_extension.rb
|
73
|
+
- lib/private_pub_plus/version.rb
|
74
|
+
- spec/fixtures/private_pub.yml
|
75
|
+
- spec/private_pub_plus_spec.rb
|
76
|
+
- spec/spec_helper.rb
|
77
|
+
homepage: http://github.com/amritsingh/private_pub
|
78
|
+
licenses: []
|
79
|
+
metadata: {}
|
80
|
+
post_install_message:
|
81
|
+
rdoc_options: []
|
82
|
+
require_paths:
|
83
|
+
- lib
|
84
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0'
|
89
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
90
|
+
requirements:
|
91
|
+
- - ">="
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: 1.3.4
|
94
|
+
requirements: []
|
95
|
+
rubyforge_project: private_pub_plus
|
96
|
+
rubygems_version: 2.4.5
|
97
|
+
signing_key:
|
98
|
+
specification_version: 4
|
99
|
+
summary: Private pub/sub messaging in Rails.
|
100
|
+
test_files: []
|