private_pub 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.rdoc +9 -0
- data/LICENSE +20 -0
- data/README.rdoc +18 -7
- data/lib/generators/private_pub/install_generator.rb +4 -3
- data/lib/generators/private_pub/templates/faye.ru +10 -5
- data/lib/generators/private_pub/templates/private_pub.yml +9 -0
- data/lib/private_pub/faye_extension.rb +6 -4
- data/lib/private_pub/railtie.rb +14 -0
- data/lib/private_pub/view_helpers.rb +17 -0
- data/lib/private_pub.rb +15 -26
- data/spec/fixtures/private_pub.yml +10 -0
- data/spec/private_pub/faye_extension_spec.rb +12 -3
- data/spec/private_pub_spec.rb +39 -13
- metadata +7 -4
- data/lib/generators/private_pub/templates/private_pub_helper.rb +0 -15
- data/lib/generators/private_pub/templates/private_pub_initializer.rb +0 -2
data/CHANGELOG.rdoc
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
0.2.0 (April 7, 2011)
|
2
|
+
|
3
|
+
* switched to YAML file for config. BACKWARDS INCOMPATIBLE: you will need to remove config/initializers/private_pub.rb
|
4
|
+
|
5
|
+
* moved view helpers into Railtie so helper file is no longer generated
|
6
|
+
|
7
|
+
* error out when signature has expired
|
8
|
+
|
9
|
+
|
1
10
|
0.1.0 (April 4, 2011)
|
2
11
|
|
3
12
|
* initial release
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 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.rdoc
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
= Private Pub
|
2
2
|
|
3
|
-
This is a Ruby gem for use with Rails to publish and subscribe to messages through
|
3
|
+
This is a Ruby gem for use with Rails to publish and subscribe to messages through {Faye}[http://faye.jcoglan.com/]. All channels are automatically made private.
|
4
|
+
|
5
|
+
This project was motivated by {Railscasts episode #260}[http://railscasts.com/episodes/260-messaging-with-faye].
|
4
6
|
|
5
7
|
|
6
8
|
== Setup
|
@@ -22,7 +24,7 @@ Next, install and start up Faye using the rackup file that was generated.
|
|
22
24
|
gem install faye
|
23
25
|
rackup faye.ru -s thin -E production
|
24
26
|
|
25
|
-
It's not necessary to
|
27
|
+
It's not necessary to include faye.js since that will be handled automatically.
|
26
28
|
|
27
29
|
|
28
30
|
== Usage
|
@@ -31,13 +33,13 @@ Use the +subscribe_to+ helper method on any page to subscribe to a channel.
|
|
31
33
|
|
32
34
|
<%= subscribe_to "/messages/new" %>
|
33
35
|
|
34
|
-
Use the +publish_to+ helper method to publish messages to that channel. If a block of JavaScript is passed it will be evaluated automatically. This is usually done
|
36
|
+
Use the +publish_to+ helper method to publish messages to that channel. If a block of JavaScript is passed it will be evaluated automatically. This is usually done in a JavaScript AJAX response (such as a <tt>create.js.erb</tt> file).
|
35
37
|
|
36
38
|
<% publish_to "/messages/new" do %>
|
37
39
|
$("#chat").append("<%= escape_javascript render(@messages) %>");
|
38
40
|
<% end %>
|
39
41
|
|
40
|
-
There will be alternative ways to publish/subscribe to
|
42
|
+
There will be alternative ways to publish/subscribe to channels in the future.
|
41
43
|
|
42
44
|
|
43
45
|
== Security
|
@@ -48,8 +50,17 @@ Here's how it works. The +subscribe_to+ helper will output an element containing
|
|
48
50
|
|
49
51
|
<span class="private_pub_subscription" data-channel="/messages/new" data-signature="2aae6c35c94fcfb415dbe95f408b9ce91ee846ed" data-timestamp="13019431281234"></span>
|
50
52
|
|
51
|
-
The data-signature is a combination of the channel, timestamp, and secret token set in the Rails app. This is checked by the Faye extension when subscribing to a channel to ensure the signature is correct. The signature is automatically expired after 1 hour but this can be configured.
|
53
|
+
The data-signature is a combination of the channel, timestamp, and secret token set in the Rails app. This is checked by the Faye extension when subscribing to a channel to ensure the signature is correct. The signature is automatically expired after 1 hour but this can be configured in the generated YAML file.
|
54
|
+
|
55
|
+
signature_expiration: 600 # 10 minutes, expressed in seconds
|
56
|
+
|
57
|
+
Or use a blank value for no expiration.
|
58
|
+
|
59
|
+
signature_expiration:
|
60
|
+
|
61
|
+
Note: if Faye is on a separate server from the Rails app it's important that the system clocks be in sync.
|
62
|
+
|
52
63
|
|
53
|
-
|
64
|
+
== Development & Feedback
|
54
65
|
|
55
|
-
|
66
|
+
Questions or comments? Please use the {issue tracker}[https://github.com/ryanb/private_pub/issues]. If you would like to contribue to this project, clone this repository and run +bundle+ and +rake+ to run the tests.
|
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
module PrivatePub
|
2
2
|
module Generators
|
3
3
|
class InstallGenerator < Rails::Generators::Base
|
4
4
|
def self.source_root
|
@@ -6,8 +6,9 @@ class PrivatePub
|
|
6
6
|
end
|
7
7
|
|
8
8
|
def copy_files
|
9
|
-
|
10
|
-
|
9
|
+
remove_file "config/initializers/private_pub.rb"
|
10
|
+
remove_file "app/helpers/private_pub_helper.rb"
|
11
|
+
template "private_pub.yml", "config/private_pub.yml"
|
11
12
|
copy_file "private_pub.js", "public/javascripts/private_pub.js"
|
12
13
|
copy_file "faye.ru", "faye.ru"
|
13
14
|
end
|
@@ -1,7 +1,12 @@
|
|
1
|
+
# Run with: rackup faye.ru -s thin -E production
|
2
|
+
require "yaml"
|
1
3
|
require "faye"
|
2
|
-
|
3
|
-
require
|
4
|
+
begin
|
5
|
+
require "private_pub"
|
6
|
+
rescue LoadError
|
7
|
+
require "bundler/setup"
|
8
|
+
require "private_pub"
|
9
|
+
end
|
4
10
|
|
5
|
-
|
6
|
-
|
7
|
-
run faye_server
|
11
|
+
PrivatePub.load_config(File.expand_path("../config/private_pub.yml", __FILE__), ENV["RAILS_ENV"] || "development")
|
12
|
+
run Faye::RackAdapter.new(:mount => "/faye", :timeout => 45, :extensions => [PrivatePub.faye_extension])
|
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
module PrivatePub
|
2
2
|
class FayeExtension
|
3
3
|
def incoming(message, callback)
|
4
4
|
if message["channel"] == "/meta/subscribe"
|
@@ -15,13 +15,15 @@ class PrivatePub
|
|
15
15
|
subscription = PrivatePub.subscription(:channel => message["subscription"], :timestamp => message["ext"]["private_pub_timestamp"])
|
16
16
|
if message["ext"]["private_pub_signature"] != subscription[:signature]
|
17
17
|
message["error"] = "Incorrect signature."
|
18
|
+
elsif PrivatePub.signature_expired? message["ext"]["private_pub_timestamp"].to_i
|
19
|
+
message["error"] = "Signature has expired."
|
18
20
|
end
|
19
21
|
end
|
20
22
|
|
21
23
|
def authenticate_publish(message)
|
22
|
-
if PrivatePub.secret_token.nil?
|
23
|
-
raise Error, "No
|
24
|
-
elsif message["ext"]["private_pub_token"] != PrivatePub.secret_token
|
24
|
+
if PrivatePub.config[:secret_token].nil?
|
25
|
+
raise Error, "No secret_token config set, ensure private_pub.yml is loaded properly."
|
26
|
+
elsif message["ext"]["private_pub_token"] != PrivatePub.config[:secret_token]
|
25
27
|
message["error"] = "Incorrect token."
|
26
28
|
end
|
27
29
|
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require "private_pub/view_helpers"
|
2
|
+
|
3
|
+
module PrivatePub
|
4
|
+
class Railtie < Rails::Railtie
|
5
|
+
initializer "private_pub.config" do
|
6
|
+
path = Rails.root.join("config/private_pub.yml")
|
7
|
+
PrivatePub.load_config(path, Rails.env) if path.exist?
|
8
|
+
end
|
9
|
+
|
10
|
+
initializer "private_pub.view_helpers" do
|
11
|
+
ActionView::Base.send :include, ViewHelpers
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module PrivatePub
|
2
|
+
module ViewHelpers
|
3
|
+
def publish_to(channel, &block)
|
4
|
+
message = {:channel => channel, :data => {:_eval => capture(&block)}, :ext => {:private_pub_token => PrivatePub.config[:secret_token]}}
|
5
|
+
PrivatePub.publish(:message => message.to_json)
|
6
|
+
end
|
7
|
+
|
8
|
+
def subscribe_to(channel)
|
9
|
+
subscription = PrivatePub.subscription(:channel => channel)
|
10
|
+
content_tag :span, "", :class => "private_pub_subscription",
|
11
|
+
"data-server" => PrivatePub.config[:server],
|
12
|
+
"data-channel" => subscription[:channel],
|
13
|
+
"data-signature" => subscription[:signature],
|
14
|
+
"data-timestamp" => subscription[:timestamp]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/private_pub.rb
CHANGED
@@ -2,34 +2,13 @@ require "digest/sha1"
|
|
2
2
|
require "net/http"
|
3
3
|
|
4
4
|
require "private_pub/faye_extension"
|
5
|
+
require "private_pub/railtie" if defined? Rails
|
5
6
|
|
6
|
-
|
7
|
+
module PrivatePub
|
7
8
|
class Error < StandardError; end
|
8
9
|
|
9
10
|
class << self
|
10
|
-
|
11
|
-
@config[:server] = server
|
12
|
-
end
|
13
|
-
|
14
|
-
def server
|
15
|
-
@config[:server]
|
16
|
-
end
|
17
|
-
|
18
|
-
def signature_expiration=(signature_expiration)
|
19
|
-
@config[:signature_expiration] = signature_expiration
|
20
|
-
end
|
21
|
-
|
22
|
-
def signature_expiration
|
23
|
-
@config[:signature_expiration]
|
24
|
-
end
|
25
|
-
|
26
|
-
def secret_token=(secret_token)
|
27
|
-
@config[:secret_token] = secret_token
|
28
|
-
end
|
29
|
-
|
30
|
-
def secret_token
|
31
|
-
@config[:secret_token]
|
32
|
-
end
|
11
|
+
attr_reader :config
|
33
12
|
|
34
13
|
def reset_config
|
35
14
|
@config = {
|
@@ -38,19 +17,29 @@ class PrivatePub
|
|
38
17
|
}
|
39
18
|
end
|
40
19
|
|
20
|
+
def load_config(filename, environment)
|
21
|
+
yaml = YAML.load_file(filename)[environment.to_s]
|
22
|
+
raise ArgumentError, "The #{environment} environment does not exist in #{filename}" if yaml.nil?
|
23
|
+
yaml.each { |k, v| config[k.to_sym] = v }
|
24
|
+
end
|
25
|
+
|
41
26
|
def subscription(options = {})
|
42
27
|
sub = {:timestamp => (Time.now.to_f * 1000).round}.merge(options)
|
43
|
-
sub[:signature] = Digest::SHA1.hexdigest([secret_token, sub[:channel], sub[:timestamp]].join)
|
28
|
+
sub[:signature] = Digest::SHA1.hexdigest([config[:secret_token], sub[:channel], sub[:timestamp]].join)
|
44
29
|
sub
|
45
30
|
end
|
46
31
|
|
47
32
|
def publish(data)
|
48
|
-
Net::HTTP.post_form(URI.parse(
|
33
|
+
Net::HTTP.post_form(URI.parse(config[:server]), data)
|
49
34
|
end
|
50
35
|
|
51
36
|
def faye_extension
|
52
37
|
FayeExtension.new
|
53
38
|
end
|
39
|
+
|
40
|
+
def signature_expired?(timestamp)
|
41
|
+
timestamp < ((Time.now.to_f - config[:signature_expiration])*1000).round if config[:signature_expiration]
|
42
|
+
end
|
54
43
|
end
|
55
44
|
|
56
45
|
reset_config
|
@@ -0,0 +1,10 @@
|
|
1
|
+
development:
|
2
|
+
server: http://dev.local:9292/faye
|
3
|
+
secret_token: DEVELOPMENT_SECRET_TOKEN
|
4
|
+
signature_expiration: 600
|
5
|
+
production:
|
6
|
+
server: http://example.com/faye
|
7
|
+
secret_token: PRODUCTION_SECRET_TOKEN
|
8
|
+
signature_expiration: 600
|
9
|
+
no_signature_expiration:
|
10
|
+
signature_expiration:
|
@@ -16,7 +16,7 @@ describe PrivatePub::FayeExtension do
|
|
16
16
|
end
|
17
17
|
|
18
18
|
it "has no error when the signature matches the subscription" do
|
19
|
-
sub = PrivatePub.subscription(:
|
19
|
+
sub = PrivatePub.subscription(:channel => "hello")
|
20
20
|
@message["subscription"] = sub[:channel]
|
21
21
|
@message["ext"]["private_pub_signature"] = sub[:signature]
|
22
22
|
@message["ext"]["private_pub_timestamp"] = sub[:timestamp]
|
@@ -24,8 +24,17 @@ describe PrivatePub::FayeExtension do
|
|
24
24
|
message["error"].should be_nil
|
25
25
|
end
|
26
26
|
|
27
|
+
it "has an error when signature is just expired" do
|
28
|
+
sub = PrivatePub.subscription(:timestamp => 123, :channel => "hello")
|
29
|
+
@message["subscription"] = sub[:channel]
|
30
|
+
@message["ext"]["private_pub_signature"] = sub[:signature]
|
31
|
+
@message["ext"]["private_pub_timestamp"] = sub[:timestamp]
|
32
|
+
message = @faye.incoming(@message, lambda { |m| m })
|
33
|
+
message["error"].should == "Signature has expired."
|
34
|
+
end
|
35
|
+
|
27
36
|
it "has an error when trying to publish to a custom channel with a bad token" do
|
28
|
-
PrivatePub.secret_token = "good"
|
37
|
+
PrivatePub.config[:secret_token] = "good"
|
29
38
|
@message["channel"] = "/custom/channel"
|
30
39
|
@message["ext"]["private_pub_token"] = "bad"
|
31
40
|
message = @faye.incoming(@message, lambda { |m| m })
|
@@ -37,7 +46,7 @@ describe PrivatePub::FayeExtension do
|
|
37
46
|
@message["ext"]["private_pub_token"] = "bad"
|
38
47
|
lambda {
|
39
48
|
message = @faye.incoming(@message, lambda { |m| m })
|
40
|
-
}.should raise_error("No
|
49
|
+
}.should raise_error("No secret_token config set, ensure private_pub.yml is loaded properly.")
|
41
50
|
end
|
42
51
|
|
43
52
|
it "has no error on other meta calls" do
|
data/spec/private_pub_spec.rb
CHANGED
@@ -5,21 +5,12 @@ describe PrivatePub do
|
|
5
5
|
PrivatePub.reset_config
|
6
6
|
end
|
7
7
|
|
8
|
-
it "has secret token, server, and signature expiration settings" do
|
9
|
-
PrivatePub.secret_token = "secret token"
|
10
|
-
PrivatePub.secret_token.should == "secret token"
|
11
|
-
PrivatePub.server = "http://localhost/"
|
12
|
-
PrivatePub.server.should == "http://localhost/"
|
13
|
-
PrivatePub.signature_expiration = 1000
|
14
|
-
PrivatePub.signature_expiration.should == 1000
|
15
|
-
end
|
16
|
-
|
17
8
|
it "defaults server to localhost:9292/faye" do
|
18
|
-
PrivatePub.server.should == "http://localhost:9292/faye"
|
9
|
+
PrivatePub.config[:server].should == "http://localhost:9292/faye"
|
19
10
|
end
|
20
11
|
|
21
12
|
it "defaults signature_expiration to 1 hour" do
|
22
|
-
PrivatePub.signature_expiration.should == 60 * 60
|
13
|
+
PrivatePub.config[:signature_expiration].should == 60 * 60
|
23
14
|
end
|
24
15
|
|
25
16
|
it "defaults subscription timestamp to current time in milliseconds" do
|
@@ -28,6 +19,24 @@ describe PrivatePub do
|
|
28
19
|
PrivatePub.subscription[:timestamp].should == (time.to_f * 1000).round
|
29
20
|
end
|
30
21
|
|
22
|
+
it "loads a simple configuration file via load_config" do
|
23
|
+
PrivatePub.load_config("spec/fixtures/private_pub.yml", "production")
|
24
|
+
PrivatePub.config[:server].should == "http://example.com/faye"
|
25
|
+
PrivatePub.config[:secret_token].should == "PRODUCTION_SECRET_TOKEN"
|
26
|
+
PrivatePub.config[:signature_expiration].should == 600
|
27
|
+
end
|
28
|
+
|
29
|
+
it "supports a nil signature_expiration via a blank value in the configuration file" do
|
30
|
+
PrivatePub.load_config("spec/fixtures/private_pub.yml", :no_signature_expiration)
|
31
|
+
PrivatePub.config[:signature_expiration].should be_nil
|
32
|
+
end
|
33
|
+
|
34
|
+
it "raises an exception if an invalid environment is passed to load_config" do
|
35
|
+
lambda {
|
36
|
+
PrivatePub.load_config("spec/fixtures/private_pub.yml", :test)
|
37
|
+
}.should raise_error ArgumentError
|
38
|
+
end
|
39
|
+
|
31
40
|
it "includes channel and custom time in subscription" do
|
32
41
|
subscription = PrivatePub.subscription(:timestamp => 123, :channel => "hello")
|
33
42
|
subscription[:timestamp].should == 123
|
@@ -35,17 +44,34 @@ describe PrivatePub do
|
|
35
44
|
end
|
36
45
|
|
37
46
|
it "does a sha1 digest of channel, timestamp, and secret token" do
|
38
|
-
PrivatePub.secret_token = "token"
|
47
|
+
PrivatePub.config[:secret_token] = "token"
|
39
48
|
subscription = PrivatePub.subscription(:timestamp => 123, :channel => "channel")
|
40
49
|
subscription[:signature].should == Digest::SHA1.hexdigest("tokenchannel123")
|
41
50
|
end
|
42
51
|
|
43
52
|
it "publishes to server using Net::HTTP" do
|
44
|
-
Net::HTTP.should_receive(:post_form).with(URI.parse(PrivatePub.server), "hello world").and_return(:result)
|
53
|
+
Net::HTTP.should_receive(:post_form).with(URI.parse(PrivatePub.config[:server]), "hello world").and_return(:result)
|
45
54
|
PrivatePub.publish("hello world").should == :result
|
46
55
|
end
|
47
56
|
|
48
57
|
it "has a FayeExtension instance" do
|
49
58
|
PrivatePub.faye_extension.should be_kind_of(PrivatePub::FayeExtension)
|
50
59
|
end
|
60
|
+
|
61
|
+
it "says signature has expired when time passed in is greater than expiration" do
|
62
|
+
PrivatePub.config[:signature_expiration] = 30*60
|
63
|
+
time = PrivatePub.subscription[:timestamp] - 31*60*1000
|
64
|
+
PrivatePub.signature_expired?(time).should be_true
|
65
|
+
end
|
66
|
+
|
67
|
+
it "says signature has not expired when time passed in is less than expiration" do
|
68
|
+
PrivatePub.config[:signature_expiration] = 30*60
|
69
|
+
time = PrivatePub.subscription[:timestamp] - 29*60*1000
|
70
|
+
PrivatePub.signature_expired?(time).should be_false
|
71
|
+
end
|
72
|
+
|
73
|
+
it "says signature has not expired when expiration is nil" do
|
74
|
+
PrivatePub.config[:signature_expiration] = nil
|
75
|
+
PrivatePub.signature_expired?(0).should be_false
|
76
|
+
end
|
51
77
|
end
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: private_pub
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.
|
5
|
+
version: 0.2.0
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Ryan Bates
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2011-04-
|
13
|
+
date: 2011-04-07 00:00:00 -07:00
|
14
14
|
default_executable:
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
@@ -36,15 +36,18 @@ files:
|
|
36
36
|
- lib/generators/private_pub/install_generator.rb
|
37
37
|
- lib/generators/private_pub/templates/faye.ru
|
38
38
|
- lib/generators/private_pub/templates/private_pub.js
|
39
|
-
- lib/generators/private_pub/templates/
|
40
|
-
- lib/generators/private_pub/templates/private_pub_initializer.rb
|
39
|
+
- lib/generators/private_pub/templates/private_pub.yml
|
41
40
|
- lib/private_pub/faye_extension.rb
|
41
|
+
- lib/private_pub/railtie.rb
|
42
|
+
- lib/private_pub/view_helpers.rb
|
42
43
|
- lib/private_pub.rb
|
44
|
+
- spec/fixtures/private_pub.yml
|
43
45
|
- spec/private_pub/faye_extension_spec.rb
|
44
46
|
- spec/private_pub_spec.rb
|
45
47
|
- spec/spec_helper.rb
|
46
48
|
- CHANGELOG.rdoc
|
47
49
|
- Gemfile
|
50
|
+
- LICENSE
|
48
51
|
- Rakefile
|
49
52
|
- README.rdoc
|
50
53
|
has_rdoc: true
|
@@ -1,15 +0,0 @@
|
|
1
|
-
module PrivatePubHelper
|
2
|
-
def publish_to(channel, &block)
|
3
|
-
message = {:channel => channel, :data => {:_eval => capture(&block)}, :ext => {:private_pub_token => PrivatePub.secret_token}}
|
4
|
-
PrivatePub.publish(:message => message.to_json)
|
5
|
-
end
|
6
|
-
|
7
|
-
def subscribe_to(channel)
|
8
|
-
subscription = PrivatePub.subscription(:channel => channel)
|
9
|
-
content_tag :span, "", :class => "private_pub_subscription",
|
10
|
-
"data-server" => PrivatePub.server,
|
11
|
-
"data-channel" => subscription[:channel],
|
12
|
-
"data-signature" => subscription[:signature],
|
13
|
-
"data-timestamp" => subscription[:timestamp]
|
14
|
-
end
|
15
|
-
end
|