Skip to content

Commit

Permalink
adding some documentation to methods
Browse files Browse the repository at this point in the history
  • Loading branch information
ryanb committed Jan 15, 2012
1 parent 9c69e73 commit b4b9147
Show file tree
Hide file tree
Showing 7 changed files with 36 additions and 17 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ The Ruby `subscribe_to` helper call is still necessary with this approach to gra

The configuration is set separately for each environment in the generated `config/private_pub.yml` file. Here are the options.

* `server`: The URL to use for the Faye server.
* `server`: The URL to use for the Faye server such as `http://localhost:9292/faye`.
* `secret_token`: A secret hash to secure the server. Can be any string.
* `signature_expiration`: The length of time in seconds before a subscription signature expires. If this is not set there is no expiration. Note: if Faye is on a separate server from the Rails app, the system clocks must be in sync for the expiration to work properly.

Expand All @@ -102,7 +102,7 @@ The `subscribe_to` helper will output the following script which subscribes the
</script>
```

The signature and timestamp checked on the Faye server to ensure users are only able to access channels you subscribe them too. The signature will automatically expire after the time specified in the configuration.
The signature and timestamp checked on the Faye server to ensure users are only able to access channels you subscribe them to. The signature will automatically expire after the time specified in the configuration.

The `publish_to` method will send a post request to the Faye server (using `Net::HTTP`) instructing it to send the given data back to the browser.

Expand Down
26 changes: 18 additions & 8 deletions lib/private_pub.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,30 @@ class Error < StandardError; end
class << self
attr_reader :config

# Resets the configuration to the default (empty hash)
def reset_config
@config = {}
end

# Loads the configuration from a given YAML file and environment (such as production)
def load_config(filename, environment)
yaml = YAML.load_file(filename)[environment.to_s]
raise ArgumentError, "The #{environment} environment does not exist in #{filename}" if yaml.nil?
yaml.each { |k, v| config[k.to_sym] = v }
end

def subscription(options = {})
sub = {:server => config[:server], :timestamp => (Time.now.to_f * 1000).round}.merge(options)
sub[:signature] = Digest::SHA1.hexdigest([config[:secret_token], sub[:channel], sub[:timestamp]].join)
sub
end

# Publish the given data to a specific channel. This ends up sending
# a Net::HTTP POST request to the Faye server.
def publish_to(channel, data)
publish_message(message(channel, data))
end

# Sends the given message hash to the Faye server using Net::HTTP.
def publish_message(message)
Net::HTTP.post_form(URI.parse(config[:server]), :message => message.to_json)
end

# Returns a message hash for sending to Faye
def message(channel, data)
message = {:channel => channel, :data => {:channel => channel}, :ext => {:private_pub_token => config[:secret_token]}}
if data.kind_of? String
Expand All @@ -40,14 +44,20 @@ def message(channel, data)
message
end

def publish_message(message)
Net::HTTP.post_form(URI.parse(config[:server]), :message => message.to_json)
# Returns a subscription hash to pass to the PrivatePub.sign call in JavaScript.
def subscription
sub = {:server => config[:server], :timestamp => (Time.now.to_f * 1000).round}.merge(options)
sub[:signature] = Digest::SHA1.hexdigest([config[:secret_token], sub[:channel], sub[:timestamp]].join)
sub
end

# Determine if the signature has expired given a timestamp.
def signature_expired?(timestamp)
timestamp < ((Time.now.to_f - config[:signature_expiration])*1000).round if config[:signature_expiration]
end

# Returns the Faye Rack application.
# Any options given are passed to the Faye::RackAdapter.
def faye_app(options = {})
options = {:mount => "/faye", :timeout => 45, :extensions => [FayeExtension.new]}.merge(options)
Faye::RackAdapter.new(options)
Expand Down
2 changes: 2 additions & 0 deletions lib/private_pub/engine.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@

module PrivatePub
class Engine < Rails::Engine
# Loads the private_pub.yml file if it exists.
initializer "private_pub.config" do
path = Rails.root.join("config/private_pub.yml")
PrivatePub.load_config(path, Rails.env) if path.exist?
end

# Adds the ViewHelpers into ActionView::Base
initializer "private_pub.view_helpers" do
ActionView::Base.send :include, ViewHelpers
end
Expand Down
6 changes: 6 additions & 0 deletions lib/private_pub/faye_extension.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
module PrivatePub
# This class is an extension for the Faye::RackAdapter.
# It is used inside of PrivatePub.faye_app.
class FayeExtension
# Callback to handle incoming Faye messages. This authenticates both
# subscribe and publish calls.
def incoming(message, callback)
if message["channel"] == "/meta/subscribe"
authenticate_subscribe(message)
Expand All @@ -11,6 +15,7 @@ def incoming(message, callback)

private

# Ensure the subscription signature is correct and that it has not expired.
def authenticate_subscribe(message)
subscription = PrivatePub.subscription(:channel => message["subscription"], :timestamp => message["ext"]["private_pub_timestamp"])
if message["ext"]["private_pub_signature"] != subscription[:signature]
Expand All @@ -20,6 +25,7 @@ def authenticate_subscribe(message)
end
end

# Ensures the secret token is correct before publishing.
def authenticate_publish(message)
if PrivatePub.config[:secret_token].nil?
raise Error, "No secret_token config set, ensure private_pub.yml is loaded properly."
Expand Down
8 changes: 8 additions & 0 deletions lib/private_pub/view_helpers.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
module PrivatePub
module ViewHelpers
# Publish the given data or block to the client by sending
# a Net::HTTP POST request to the Faye server. If a block
# or string is passed in, it is evaluated as JavaScript
# on the client. Otherwise it will be converted to JSON
# for use in a JavaScript callback.
def publish_to(channel, data = nil, &block)
PrivatePub.publish_to(channel, data || capture(&block))
end

# Subscribe the client to the given channel. This generates
# some JavaScript calling PrivatePub.sign with the subscription
# options.
def subscribe_to(channel)
subscription = PrivatePub.subscription(:channel => channel)
content_tag "script", :type => "text/javascript" do
Expand Down
2 changes: 0 additions & 2 deletions spec/fixtures/private_pub.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,3 @@ production:
server: http://example.com/faye
secret_token: PRODUCTION_SECRET_TOKEN
signature_expiration: 600
no_signature_expiration:
signature_expiration:
5 changes: 0 additions & 5 deletions spec/private_pub_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,6 @@
PrivatePub.config[:signature_expiration].should == 600
end

it "supports a nil signature_expiration via a blank value in the configuration file" do
PrivatePub.load_config("spec/fixtures/private_pub.yml", :no_signature_expiration)
PrivatePub.config[:signature_expiration].should be_nil
end

it "raises an exception if an invalid environment is passed to load_config" do
lambda {
PrivatePub.load_config("spec/fixtures/private_pub.yml", :test)
Expand Down

0 comments on commit b4b9147

Please sign in to comment.