private_pub_plus 1.0.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.
- 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: []
|