pushkin 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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