pushkin 0.0.1

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.
@@ -0,0 +1,6 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ .rvmrc
6
+ .jhw-cache/
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in pushkin.gemspec
4
+ gemspec
5
+
6
+ # gem 'ruby-debug19'
@@ -0,0 +1,16 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+
4
+ desc "Run RSpec"
5
+ RSpec::Core::RakeTask.new do |t|
6
+ t.verbose = false
7
+ end
8
+
9
+ namespace :spec do
10
+ desc "Run javascript specs via jasmine-headless-webkit runner"
11
+ task :javascripts do
12
+ system "jasmine-headless-webkit -c"
13
+ end
14
+ end
15
+
16
+ task :default => [:spec, "spec:javascripts"]
@@ -0,0 +1 @@
1
+ //= require ./pushkin/pushkin
@@ -0,0 +1,59 @@
1
+ class Pushkin
2
+ constructor: (@options) ->
3
+ @fayeClient = null
4
+ @fayeCallbacks = []
5
+ @subscriptions = {}
6
+ @subscriptionCallbacks = {}
7
+
8
+ subscribe: (channel, callback) ->
9
+ @subscriptionCallbacks[channel] = callback
10
+
11
+ faye: (callback) ->
12
+ if @fayeClient
13
+ callback(@fayeClient)
14
+ else
15
+ @fayeCallbacks.push(callback)
16
+ if (@server && !@connecting)
17
+ @connecting = true
18
+ doc = @document()
19
+ script = doc.createElement("script")
20
+ script.type = "text/javascript"
21
+ script.src = @server + ".js"
22
+ script.onload = @connectToFaye
23
+ doc.documentElement.appendChild(script)
24
+
25
+ fayeExtension: ->
26
+ @fayeExtensionInstance ||= new FayeExtension(@subscriptions)
27
+
28
+ connectToFaye: ->
29
+ @fayeClient = new Faye.Client(@server)
30
+ @fayeClient.addExtension(@fayeExtension())
31
+ callback(@fayeClient) for callback in @fayeCallbacks
32
+
33
+ handleResponse: (message) ->
34
+ eval(message.eval) if message.eval
35
+ if (callback = @subscriptionCallbacks[message.channel])
36
+ callback(message.data, message.channel)
37
+
38
+ sign: (subscription) ->
39
+ @server = subscription.server if !@server
40
+ @subscriptions[subscription.channel] = subscription
41
+ @faye((faye) =>
42
+ faye.subscribe(subscription.channel, @handleResponse)
43
+ )
44
+
45
+ document: -> window.document
46
+
47
+ class FayeExtension
48
+ constructor: (@subscriptions) ->
49
+
50
+ outgoing: (message, callback) ->
51
+ if (message.channel == "/meta/subscribe")
52
+ subscription = @subscriptions[message.subscription]
53
+ message.ext ||= {}
54
+ message.ext.pushkin_signature = subscription.signature;
55
+ message.ext.pushkin_timestamp = subscription.timestamp;
56
+
57
+ callback(message)
58
+
59
+ this.Pushkin = Pushkin
@@ -0,0 +1,13 @@
1
+ # Run with: rackup pushkin.ru -s thin -E production
2
+ require "bundler/setup"
3
+ require "yaml"
4
+ require "faye"
5
+ require "pushkin"
6
+
7
+ Pushkin.configure do |config|
8
+ config.host = "http://localhost:9292"
9
+ config.endpoint = "/faye"
10
+ config.secret_token = "secret"
11
+ end
12
+
13
+ run Pushkin.server
@@ -0,0 +1,14 @@
1
+ module Pushkin
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 "pushkin.yml", "config/pushkin.yml"
10
+ copy_file "pushkin.ru", "pushkin.ru"
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,8 @@
1
+ # Run with: rackup pushkin.ru -s thin -E production
2
+ require "bundler/setup"
3
+ require "yaml"
4
+ require "faye"
5
+ require "pushkin"
6
+
7
+ Pushkin.configure(File.expand_path("../config/pushkin.yml", __FILE__)[ENV["RAILS_ENV"] || "development"])
8
+ run Pushkin.server
@@ -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,63 @@
1
+ require "pushkin/version"
2
+ require "pushkin/configuration"
3
+ require "pushkin/subscription"
4
+ require "pushkin/faye/authentication"
5
+
6
+ module Pushkin
7
+ extend self
8
+
9
+ def connection
10
+ # conn.post do |req|
11
+ # req.url '/nigiri'
12
+ # req.headers['Content-Type'] = 'application/json'
13
+ # req.body = '{ "name": "Unagi" }'
14
+ # end
15
+ @connection ||= begin
16
+ Faraday.new host do |conn|
17
+ conn.request :json
18
+
19
+ conn.response :logger # log the request to STDOUT
20
+ conn.response :json, :content_type => /\bjson$/
21
+
22
+ conn.adapter Faraday.default_adapter
23
+ end
24
+ end
25
+ end
26
+
27
+ def reset!
28
+ @configuration = nil
29
+ end
30
+
31
+ def config
32
+ @configuration ||= Configuration.new
33
+ end
34
+
35
+ def configure(opts = {}, &block)
36
+ opts.each do |attribute, setting|
37
+ Configuration.config.send("#{attribute}=", setting)
38
+ end
39
+ Configuration.configure &block if block_given?
40
+ end
41
+
42
+ delegate :host, :endpoint, :secret_token, :signature_expiration, to: :config
43
+
44
+ def publish_message(message)
45
+ response = connection.post(endpoint, message)
46
+ response.body
47
+ end
48
+
49
+ # Returns a subscription hash to pass to the Pushkin.sign call in JavaScript.
50
+ # Any options passed are merged to the hash.
51
+ def subscription(options = {})
52
+ Subscription.new(options).to_hash
53
+ end
54
+
55
+ # Returns the Faye Rack application.
56
+ # Any options given are passed to the Faye::RackAdapter.
57
+ def server(options = {})
58
+ options = {:mount => endpoint, :timeout => 45, :extensions => []}.merge(options)
59
+ ::Faye::RackAdapter.new(options)
60
+ end
61
+
62
+ reset!
63
+ end
@@ -0,0 +1,25 @@
1
+ module Pushkin
2
+ module ActionView
3
+ module Helpers
4
+
5
+ # Publish the given data or block to the client by sending
6
+ # a Faraday POST request to the Faye server. If a block
7
+ # or string is passed in, it is evaluated as JavaScript
8
+ # on the client. Otherwise it will be converted to JSON
9
+ # for use in a JavaScript callback.
10
+ def publish_to(channel, data = nil, &block)
11
+ PrivatePub.publish_to(channel, data || capture(&block))
12
+ end
13
+
14
+ # Subscribe the client to the given channel. This generates
15
+ # some JavaScript calling PrivatePub.sign with the subscription
16
+ # options.
17
+ def subscribe_to(channel)
18
+ subscription = PrivatePub.subscription(:channel => channel)
19
+ content_tag "script", :type => "text/javascript" do
20
+ raw("PrivatePub.sign(#{subscription.to_json});")
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,22 @@
1
+ require 'securerandom'
2
+ require 'active_support/configurable'
3
+
4
+ module Pushkin
5
+ class Configuration
6
+ include ActiveSupport::Configurable
7
+
8
+ config_accessor :host, :endpoint, :secret_token, :signature_expiration
9
+
10
+ def self.generate_token
11
+ defined?(SecureRandom) ? SecureRandom.hex(32) : ActiveSupport::SecureRandom.hex(32)
12
+ end
13
+
14
+ # Pushkin.configure do |config|
15
+ # config.host = 'http://localhost:9292'
16
+ # config.secret_token = generate_token
17
+ # config.endpoint = '/faye'
18
+ # # config.signature_expiration = 3600 # 1 hour
19
+ # end
20
+
21
+ end
22
+ end
@@ -0,0 +1,16 @@
1
+ require "pushkin/action_view/helpers"
2
+
3
+ module Pushkin
4
+ class Engine < Rails::Engine
5
+ # Loads the pushkin.yml file if it exists.
6
+ initializer "pushkin.config" do
7
+ path = Rails.root.join("config/pushkin.yml")
8
+ Pushkin.configure(path[Rails.env]) if path.exist?
9
+ end
10
+
11
+ # Adds the ViewHelpers into ActionView::Base
12
+ initializer "private_pub.view_helpers" do
13
+ ActionView::Base.send :include, Pushkin::ActionView::Helpers
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,41 @@
1
+ module Pushkin
2
+ module Faye
3
+ class AuthenticationError < StandardError; end
4
+
5
+ class Authentication
6
+ def incoming(message, callback)
7
+ if message["channel"] == '/meta/subscribe'
8
+ authenticate_subscribe(message)
9
+ elsif message["channel"] !~ %r{^/meta/}
10
+ authenticate_publish(message)
11
+ end
12
+ callback.call(message)
13
+ end
14
+
15
+ def authenticate_subscribe(message)
16
+ signature = message["ext"]["pushkin_signature"]
17
+ timestamp = message["ext"]["pushkin_timestamp"]
18
+
19
+ subscription = Pushkin::Subscription.new \
20
+ channel: message["subscription"],
21
+ timestamp: timestamp
22
+
23
+ if signature != subscription.signature
24
+ message["error"] = "Incorrect signature."
25
+ elsif subscription.signature_expired?
26
+ message["error"] = "Signature has expired."
27
+ end
28
+ end
29
+
30
+ def authenticate_publish(message)
31
+ if Pushkin.secret_token.nil?
32
+ raise AuthenticationError.new("No secret_token config set")
33
+ elsif message["ext"]["pushkin_token"] != Pushkin.secret_token
34
+ message["error"] = "Incorrect token."
35
+ else
36
+ message["ext"]["pushkin_token"] = nil
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,55 @@
1
+ module Pushkin
2
+ class Subscription
3
+ attr_accessor :host, :endpoint, :channel, :timestamp
4
+
5
+ def initialize(options = {})
6
+ @timestamp = options[:timestamp]
7
+ @host = options[:host]
8
+ @endpoint = options[:endpoint]
9
+ @channel = options[:channel]
10
+ end
11
+
12
+ def signature
13
+ @signature ||= Digest::SHA1.hexdigest([secret_token, channel, timestamp].join)
14
+ end
15
+
16
+ def signature_expired?
17
+ return false unless expiration
18
+ timestamp < ((Time.now.to_f - expiration)*1000).round
19
+ end
20
+
21
+ def expiration
22
+ Pushkin.signature_expiration
23
+ end
24
+
25
+ def secret_token
26
+ Pushkin.secret_token
27
+ end
28
+
29
+ def endpoint
30
+ @endpoint ||= Pushkin.endpoint
31
+ end
32
+
33
+ def host
34
+ @host ||= Pushkin.host
35
+ end
36
+
37
+ def server
38
+ host + endpoint
39
+ end
40
+
41
+ def timestamp
42
+ @timestamp ||= (Time.now.to_f * 1000).round
43
+ end
44
+
45
+ def to_hash
46
+ {
47
+ server: server,
48
+ timestamp: timestamp,
49
+ channel: channel,
50
+ signature: signature
51
+ }
52
+ end
53
+
54
+ end
55
+ end
@@ -0,0 +1,3 @@
1
+ module Pushkin
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,32 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "pushkin/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "pushkin"
7
+ s.version = Pushkin::VERSION
8
+ s.authors = ["Ross Kaffenberger"]
9
+ s.email = ["rosskaff@gmail.com"]
10
+ s.homepage = ""
11
+ s.summary = %q{Pub/Sub for Rails with Faye}
12
+ s.description = %q{Pub/Sub for Rails with Faye based on PrivatePub}
13
+
14
+ s.rubyforge_project = "pushkin"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ # specify any dependencies here; for example:
22
+ # s.add_development_dependency "rspec"
23
+ # s.add_runtime_dependency "rest-client"
24
+ s.add_dependency "faye"
25
+ s.add_dependency "faraday", "0.8.0.rc2"
26
+ s.add_dependency "faraday_middleware"
27
+ s.add_dependency "activesupport"
28
+
29
+ s.add_development_dependency "rake"
30
+ s.add_development_dependency "rspec"
31
+ s.add_development_dependency "jasmine-headless-webkit"
32
+ end
@@ -0,0 +1,124 @@
1
+ describe "Pushkin", ->
2
+ pushkin = null
3
+
4
+ it "should be defined", ->
5
+ expect(Pushkin).toEqual jasmine.any(Function)
6
+
7
+ describe "instance functions", ->
8
+ doc = null
9
+ called = null
10
+
11
+ beforeEach ->
12
+ window.Faye = {}
13
+ pushkin = new Pushkin
14
+ called = false
15
+ doc = {}
16
+
17
+ it "adds callback to subscriptions", ->
18
+ pushkin.subscribe("hello", "callback");
19
+ expect(pushkin.subscriptionCallbacks["hello"]).toEqual("callback")
20
+
21
+ it "has a fayeExtension which adds matching subscription signature and timestamp to outgoing message", ->
22
+ message =
23
+ channel: "/meta/subscribe"
24
+ subscription: "hello"
25
+
26
+ pushkin.subscriptions["hello"] = {signature: "abcd", timestamp: "1234"}
27
+ fayeExtension = pushkin.fayeExtension()
28
+ fayeExtension.outgoing(message, (message) ->
29
+ expect(message.ext.pushkin_signature).toEqual "abcd"
30
+ expect(message.ext.pushkin_timestamp).toEqual "1234"
31
+ called = true
32
+ )
33
+ expect(called).toBeTruthy()
34
+
35
+ it "evaluates javascript in message response", ->
36
+ pushkin.handleResponse(
37
+ eval: 'this.subscriptions.foo = "bar"'
38
+ )
39
+ expect(pushkin.subscriptions.foo).toEqual "bar"
40
+
41
+ it "triggers callback matching message channel in response", ->
42
+ pushkin.subscribe("test", (data, channel) ->
43
+ expect(data).toEqual "abcd"
44
+ expect(channel).toEqual "test"
45
+ called = true
46
+ )
47
+ pushkin.handleResponse(
48
+ channel: "test"
49
+ data: "abcd"
50
+ )
51
+ expect(called).toBeTruthy()
52
+
53
+ it "adds a faye subscription with response handler when signing", ->
54
+ faye =
55
+ subscribe: jasmine.createSpy()
56
+ subscription =
57
+ server: "server"
58
+ channel: "somechannel"
59
+
60
+ spyOn(pushkin, 'faye').andCallFake((callback) -> callback(faye))
61
+
62
+ pushkin.sign(subscription)
63
+ expect(faye.subscribe).toHaveBeenCalledWith "somechannel", pushkin.handleResponse
64
+ expect(pushkin.server).toEqual "server"
65
+ expect(pushkin.subscriptions.somechannel).toEqual subscription
66
+
67
+ it "adds a faye subscription with response handler when signing", ->
68
+ faye =
69
+ subscribe: jasmine.createSpy()
70
+
71
+ options =
72
+ server: "server"
73
+ channel: "somechannel"
74
+
75
+ spyOn(pushkin, 'faye').andCallFake((callback) -> callback(faye))
76
+ pushkin.sign(options)
77
+ expect(faye.subscribe).toHaveBeenCalledWith "somechannel", pushkin.handleResponse
78
+ expect(pushkin.server).toEqual "server"
79
+ expect(pushkin.subscriptions.somechannel).toEqual options
80
+
81
+ it "triggers faye callback function immediately when fayeClient is available", ->
82
+ pushkin.fayeClient = "faye"
83
+ pushkin.faye((faye) ->
84
+ expect(faye).toEqual "faye"
85
+ called = true
86
+ )
87
+ expect(called).toBeTruthy()
88
+
89
+ it "adds fayeCallback when client and server aren't available", ->
90
+ pushkin.faye "callback"
91
+ expect(pushkin.fayeCallbacks[0]).toEqual "callback"
92
+
93
+ it "adds a script tag loading faye js when the server is present", ->
94
+ script = {}
95
+ doc.createElement = -> return script
96
+ doc.documentElement =
97
+ appendChild: jasmine.createSpy()
98
+ spyOn(pushkin, "document").andReturn doc
99
+
100
+ pushkin.server = "path/to/faye"
101
+ pushkin.faye("callback")
102
+ expect(pushkin.fayeCallbacks[0]).toEqual "callback"
103
+ expect(script.type).toEqual "text/javascript"
104
+ expect(script.src).toEqual "path/to/faye.js"
105
+ expect(script.onload).toEqual pushkin.connectToFaye
106
+ expect(doc.documentElement.appendChild).toHaveBeenCalledWith script
107
+
108
+ it "connects to faye server, adds extension, and executes callbacks", ->
109
+ callback = jasmine.createSpy()
110
+ client =
111
+ addExtension: jasmine.createSpy()
112
+ Faye.Client = (server) ->
113
+ expect(server).toEqual("server")
114
+ client
115
+
116
+ pushkin.server = "server"
117
+ pushkin.fayeCallbacks.push(callback)
118
+ pushkin.connectToFaye()
119
+ expect(pushkin.fayeClient).toEqual client
120
+ expect(client.addExtension).toHaveBeenCalledWith pushkin.fayeExtension()
121
+ expect(callback).toHaveBeenCalledWith client
122
+
123
+
124
+ this.Faye = {}
@@ -0,0 +1,9 @@
1
+ src_files:
2
+ - "pushkin.js"
3
+ helpers:
4
+ - helpers/**/*
5
+ spec_files:
6
+ - "**/*spec.*"
7
+ src_dir:
8
+ - app/assets/javascripts
9
+ spec_dir: spec/javascripts
@@ -0,0 +1,76 @@
1
+ require 'spec_helper'
2
+
3
+ describe Pushkin::Faye::Authentication do
4
+ let(:faye) { Pushkin::Faye::Authentication.new }
5
+ let(:message) { {"channel" => "/meta/subscribe", "ext" => {}} }
6
+ let(:callback) { lambda { |m| m } }
7
+
8
+ it "adds an error on an incoming subscription with a bad signature" do
9
+ message["subscription"] = "hello"
10
+ message["ext"]["pushkin_signature"] = "bad"
11
+ message["ext"]["pushkin_timestamp"] = "123"
12
+
13
+ incoming = faye.incoming(message, callback)
14
+ incoming["error"].should == "Incorrect signature."
15
+ end
16
+
17
+ it "has no error when the signature matches the subscription" do
18
+ subscription = Pushkin::Subscription.new(:channel => "hello")
19
+ message["subscription"] = subscription.channel
20
+ message["ext"]["pushkin_signature"] = subscription.signature
21
+ message["ext"]["pushkin_timestamp"] = subscription.timestamp
22
+
23
+ incoming = faye.incoming(message, callback)
24
+ incoming["error"].should be_nil
25
+ end
26
+
27
+ it "has an error when signature just expired" do
28
+ Pushkin.config.signature_expiration = 1
29
+ subscription = Pushkin::Subscription.new(:timestamp => 123, :channel => "hello")
30
+ message["subscription"] = subscription.channel
31
+ message["ext"]["pushkin_signature"] = subscription.signature
32
+ message["ext"]["pushkin_timestamp"] = subscription.timestamp
33
+
34
+ incoming = faye.incoming(message, callback)
35
+ incoming["error"].should == "Signature has expired."
36
+ end
37
+
38
+ it "has an error when trying to publish to a custom channel with a bad token" do
39
+ Pushkin.config.secret_token = "good"
40
+ message["channel"] = "/custom/channel"
41
+ message["ext"]["pushkin_token"] = "bad"
42
+
43
+ incoming = faye.incoming(message, callback)
44
+ incoming["error"].should == "Incorrect token."
45
+ end
46
+
47
+ it "raises an exception when attempting to call a custom channel without a secret_token set" do
48
+ Pushkin.config.secret_token = nil
49
+ message["channel"] = "/custom/channel"
50
+ message["ext"]["pushkin_token"] = "bad"
51
+
52
+ lambda {
53
+ faye.incoming(message, callback)
54
+ }.should raise_error(Pushkin::Faye::AuthenticationError)
55
+
56
+ end
57
+
58
+ it "has no error on other meta calls" do
59
+ message["channel"] = "/meta/connect"
60
+
61
+ incoming = faye.incoming(message, callback)
62
+ incoming["error"].should be_nil
63
+
64
+ end
65
+
66
+ it "should not let message carry the pushkin token after server's validation" do
67
+ Pushkin.config.secret_token = "good"
68
+
69
+ message["channel"] = "/custom/channel"
70
+ message["ext"]["pushkin_token"] = Pushkin.secret_token
71
+
72
+ incoming = faye.incoming(message, callback)
73
+ incoming['ext']["pushkin_token"].should be_nil
74
+ end
75
+
76
+ end
@@ -0,0 +1,84 @@
1
+ require 'spec_helper'
2
+
3
+ describe Pushkin do
4
+ before(:each) do
5
+ Pushkin.reset!
6
+ end
7
+
8
+ it "is version 0.0.1" do
9
+ Pushkin::VERSION.should == '0.0.1'
10
+ end
11
+
12
+ it "publishes message as json to server using Faraday" do
13
+ Pushkin.configure { |c| c.endpoint = '/faye' }
14
+ connection = Faraday.new do |builder|
15
+ builder.adapter :test do |stub|
16
+ stub.post('/faye', "message_json") { [200, {}, 'result'] }
17
+ end
18
+ end
19
+ Pushkin.stub!(:connection => connection)
20
+ Pushkin.publish_message("message_json").should == 'result'
21
+ end
22
+
23
+ it "has a Faraday connection instance" do
24
+ Pushkin.connection.should be_kind_of(Faraday::Connection)
25
+ end
26
+
27
+ it "has a Faye rack app instance" do
28
+ Pushkin.server.should be_kind_of(Faye::RackAdapter)
29
+ end
30
+
31
+ describe "configure" do
32
+
33
+ it "has a server endpoint" do
34
+ Pushkin.configure { |c| c.endpoint = '/faye' }
35
+ Pushkin.endpoint.should == '/faye'
36
+ end
37
+
38
+ it "has a host" do
39
+ Pushkin.configure { |c| c.host = 'server' }
40
+ Pushkin.host.should == 'server'
41
+ end
42
+
43
+ it "has a secret token" do
44
+ Pushkin.configure { |c| c.secret_token = 'secret_token' }
45
+ Pushkin.secret_token.should == 'secret_token'
46
+ end
47
+
48
+ it "has an unsettabe secret token" do
49
+ Pushkin.configure { |c| c.signature_expiration = 600 }
50
+ Pushkin.signature_expiration.should == 600
51
+ end
52
+
53
+ it "accepts hash" do
54
+ Pushkin.configure host: 'server', endpoint: '/faye'
55
+ Pushkin.host.should == 'server'
56
+ Pushkin.endpoint.should == '/faye'
57
+ end
58
+
59
+ it "block settings take precedence" do
60
+ Pushkin.configure host: 'server', endpoint: '/faye' do |config|
61
+ config.host = 'server1'
62
+ config.endpoint = '/foo'
63
+ end
64
+ Pushkin.host.should == 'server1'
65
+ Pushkin.endpoint.should == '/foo'
66
+ end
67
+ end
68
+
69
+
70
+ it "includes channel, server, and custom time in subscription" do
71
+ Pushkin.configure { |c| c.host = 'localhost'; c.endpoint = '/faye' }
72
+ subscription = Pushkin.subscription(:timestamp => 123, :channel => "hello")
73
+ subscription[:timestamp].should eq(123)
74
+ subscription[:channel].should == "hello"
75
+ subscription[:server].should == 'localhost/faye'
76
+ end
77
+
78
+ it "does a sha1 digest of channel, timestamp, and secret token" do
79
+ Pushkin.configure { |c| c.secret_token = 'token' }
80
+ subscription = Pushkin.subscription(:timestamp => 123, :channel => "channel")
81
+ subscription[:signature].should == Digest::SHA1.hexdigest("tokenchannel123")
82
+ end
83
+
84
+ end
@@ -0,0 +1,11 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+
4
+ require 'faye'
5
+ require "faraday"
6
+ require "faraday_middleware"
7
+
8
+ Bundler.require(:default)
9
+
10
+ RSpec.configure do |config|
11
+ end
metadata ADDED
@@ -0,0 +1,155 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pushkin
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Ross Kaffenberger
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-02-18 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: faye
16
+ requirement: &70352006466220 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70352006466220
25
+ - !ruby/object:Gem::Dependency
26
+ name: faraday
27
+ requirement: &70352006465480 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - =
31
+ - !ruby/object:Gem::Version
32
+ version: 0.8.0.rc2
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *70352006465480
36
+ - !ruby/object:Gem::Dependency
37
+ name: faraday_middleware
38
+ requirement: &70352006464940 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: *70352006464940
47
+ - !ruby/object:Gem::Dependency
48
+ name: activesupport
49
+ requirement: &70352006464420 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :runtime
56
+ prerelease: false
57
+ version_requirements: *70352006464420
58
+ - !ruby/object:Gem::Dependency
59
+ name: rake
60
+ requirement: &70352006463800 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ type: :development
67
+ prerelease: false
68
+ version_requirements: *70352006463800
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: &70352006463380 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ! '>='
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: *70352006463380
80
+ - !ruby/object:Gem::Dependency
81
+ name: jasmine-headless-webkit
82
+ requirement: &70352006462960 !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ! '>='
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ type: :development
89
+ prerelease: false
90
+ version_requirements: *70352006462960
91
+ description: Pub/Sub for Rails with Faye based on PrivatePub
92
+ email:
93
+ - rosskaff@gmail.com
94
+ executables: []
95
+ extensions: []
96
+ extra_rdoc_files: []
97
+ files:
98
+ - .gitignore
99
+ - Gemfile
100
+ - Rakefile
101
+ - app/assets/javascripts/pushkin.js
102
+ - app/assets/javascripts/pushkin/pushkin.js.coffee
103
+ - config.ru
104
+ - lib/generators/pushkin/install_generator.rb
105
+ - lib/generators/pushkin/templates/pushkin.ru
106
+ - lib/generators/pushkin/templates/pushkin.yml
107
+ - lib/pushkin.rb
108
+ - lib/pushkin/action_view/helpers.rb
109
+ - lib/pushkin/configuration.rb
110
+ - lib/pushkin/engine.rb
111
+ - lib/pushkin/faye/authentication.rb
112
+ - lib/pushkin/subscription.rb
113
+ - lib/pushkin/version.rb
114
+ - pushkin.gemspec
115
+ - spec/javascripts/pushkin_spec.js.coffee
116
+ - spec/javascripts/support/jasmine.yml
117
+ - spec/pushkin/faye/authentication_spec.rb
118
+ - spec/pushkin_spec.rb
119
+ - spec/spec_helper.rb
120
+ homepage: ''
121
+ licenses: []
122
+ post_install_message:
123
+ rdoc_options: []
124
+ require_paths:
125
+ - lib
126
+ required_ruby_version: !ruby/object:Gem::Requirement
127
+ none: false
128
+ requirements:
129
+ - - ! '>='
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ segments:
133
+ - 0
134
+ hash: -3435973028842535534
135
+ required_rubygems_version: !ruby/object:Gem::Requirement
136
+ none: false
137
+ requirements:
138
+ - - ! '>='
139
+ - !ruby/object:Gem::Version
140
+ version: '0'
141
+ segments:
142
+ - 0
143
+ hash: -3435973028842535534
144
+ requirements: []
145
+ rubyforge_project: pushkin
146
+ rubygems_version: 1.8.10
147
+ signing_key:
148
+ specification_version: 3
149
+ summary: Pub/Sub for Rails with Faye
150
+ test_files:
151
+ - spec/javascripts/pushkin_spec.js.coffee
152
+ - spec/javascripts/support/jasmine.yml
153
+ - spec/pushkin/faye/authentication_spec.rb
154
+ - spec/pushkin_spec.rb
155
+ - spec/spec_helper.rb