pusher 0.9.4 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -18,7 +18,6 @@ coverage
18
18
  rdoc
19
19
  pkg
20
20
  .yardoc
21
- Gemfile.lock
22
21
 
23
22
  ## PROJECT::SPECIFIC
24
23
  .bundle
data/.travis.yml CHANGED
@@ -1 +1,12 @@
1
+ language: ruby
2
+
3
+ rvm:
4
+ - 1.8.7
5
+ - 1.9.2
6
+ - 1.9.3
7
+ - jruby-18mode
8
+ - jruby-19mode
9
+ - rbx-18mode
10
+ - rbx-19mode
11
+
1
12
  script: "rspec spec/*_spec.rb"
data/Gemfile.lock ADDED
@@ -0,0 +1,50 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ pusher (0.10.0)
5
+ multi_json (~> 1.0)
6
+ signature (~> 0.1.4)
7
+
8
+ GEM
9
+ remote: http://rubygems.org/
10
+ specs:
11
+ addressable (2.3.2)
12
+ cookiejar (0.3.0)
13
+ crack (0.3.1)
14
+ diff-lcs (1.1.3)
15
+ em-http-request (1.0.3)
16
+ addressable (>= 2.2.3)
17
+ cookiejar
18
+ em-socksify
19
+ eventmachine (>= 1.0.0.beta.4)
20
+ http_parser.rb (>= 0.5.3)
21
+ em-socksify (0.2.1)
22
+ eventmachine (>= 1.0.0.beta.4)
23
+ eventmachine (1.0.0)
24
+ http_parser.rb (0.5.3)
25
+ multi_json (1.3.6)
26
+ rack (1.4.1)
27
+ rake (0.9.2.2)
28
+ rspec (2.11.0)
29
+ rspec-core (~> 2.11.0)
30
+ rspec-expectations (~> 2.11.0)
31
+ rspec-mocks (~> 2.11.0)
32
+ rspec-core (2.11.1)
33
+ rspec-expectations (2.11.3)
34
+ diff-lcs (~> 1.1.3)
35
+ rspec-mocks (2.11.3)
36
+ signature (0.1.4)
37
+ webmock (1.8.10)
38
+ addressable (>= 2.2.7)
39
+ crack (>= 0.1.7)
40
+
41
+ PLATFORMS
42
+ ruby
43
+
44
+ DEPENDENCIES
45
+ em-http-request (~> 1.0.0)
46
+ pusher!
47
+ rack
48
+ rake
49
+ rspec (~> 2.0)
50
+ webmock
data/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  Pusher gem
2
2
  ==========
3
3
 
4
+ [![Build Status](https://secure.travis-ci.org/pusher/pusher-gem.png?branch=master)](http://travis-ci.org/pusher/pusher-gem)
5
+
4
6
  Getting started
5
7
  ---------------
6
8
 
data/lib/pusher.rb CHANGED
@@ -30,6 +30,8 @@ module Pusher
30
30
  def_delegators :default_client, :authentication_token, :url
31
31
  def_delegators :default_client, :encrypted=, :url=
32
32
 
33
+ def_delegators :default_client, :channels, :channel_info, :trigger
34
+
33
35
  attr_writer :logger
34
36
 
35
37
  # Return a channel by name
@@ -62,21 +62,21 @@ module Pusher
62
62
  Pusher.logger.error("#{e.message} (#{e.class})")
63
63
  Pusher.logger.debug(e.backtrace.join("\n"))
64
64
  end
65
-
66
- # Request channel stats
65
+
66
+ # Request info for a channel
67
67
  #
68
- # @return [Hash] See Pusher api docs for reported stats
68
+ # @param info [Array] Array of attributes required (as lowercase strings)
69
+ # @return [Hash] Hash of requested attributes for this channel
69
70
  # @raise [Pusher::Error] on invalid Pusher response - see the error message for more details
70
71
  # @raise [Pusher::HTTPError] on any error raised inside Net::HTTP - the original error is available in the original_error attribute
71
72
  #
72
- def stats
73
- request = Pusher::Request.new(:get, @uri + 'stats', {}, nil, nil, @client)
74
- return request.send_sync
73
+ def info(attributes = [])
74
+ @client.channel_info(name, :info => attributes.join(','))
75
75
  end
76
76
 
77
- # Compute authentication string required to subscribe to this channel.
78
- #
79
- # See http://pusher.com/docs/auth_signatures for more details.
77
+ # Compute authentication string required as part of the authentication
78
+ # endpoint response. Generally the authenticate method should be used in
79
+ # preference to this one
80
80
  #
81
81
  # @param socket_id [String] Each Pusher socket connection receives a
82
82
  # unique socket_id. This is sent from pusher.js to your server when
@@ -85,10 +85,16 @@ module Pusher
85
85
  # @return [String]
86
86
  #
87
87
  def authentication_string(socket_id, custom_string = nil)
88
- raise "Invalid socket_id" if socket_id.nil? || socket_id.empty?
89
- raise 'Custom argument must be a string' unless custom_string.nil? || custom_string.kind_of?(String)
88
+ if socket_id.nil? || socket_id.empty?
89
+ raise Error, "Invalid socket_id #{socket_id}"
90
+ end
91
+
92
+ unless custom_string.nil? || custom_string.kind_of?(String)
93
+ raise Error, 'Custom argument must be a string'
94
+ end
90
95
 
91
- string_to_sign = [socket_id, name, custom_string].compact.map{|e|e.to_s}.join(':')
96
+ string_to_sign = [socket_id, name, custom_string].
97
+ compact.map(&:to_s).join(':')
92
98
  Pusher.logger.debug "Signing #{string_to_sign}"
93
99
  token = @client.authentication_token
94
100
  digest = OpenSSL::Digest::SHA256.new
@@ -96,11 +102,9 @@ module Pusher
96
102
 
97
103
  return "#{token.key}:#{signature}"
98
104
  end
99
-
100
- # Deprecated - for backward compatibility
101
- alias :socket_auth :authentication_string
102
105
 
103
- # Generate an authentication endpoint response
106
+ # Generate the expected response for an authentication endpoint.
107
+ # See http://pusher.com/docs/authenticating_users for details.
104
108
  #
105
109
  # @example Private channels
106
110
  # render :json => Pusher['private-my_channel'].authenticate(params[:socket_id])
@@ -123,7 +127,7 @@ module Pusher
123
127
  #
124
128
  def authenticate(socket_id, custom_data = nil)
125
129
  custom_data = MultiJson.encode(custom_data) if custom_data
126
- auth = socket_auth(socket_id, custom_data)
130
+ auth = authentication_string(socket_id, custom_data)
127
131
  r = {:auth => auth}
128
132
  r[:channel_data] = custom_data if custom_data
129
133
  r
data/lib/pusher/client.rb CHANGED
@@ -21,13 +21,13 @@ module Pusher
21
21
  Signature::Token.new(@key, @secret)
22
22
  end
23
23
 
24
- # @private Builds a connection url for Pusherapp
25
- def url
24
+ # @private Builds a url for this app, optionally appending a path
25
+ def url(path = nil)
26
26
  URI::Generic.build({
27
27
  :scheme => @scheme,
28
28
  :host => @host,
29
29
  :port => @port,
30
- :path => "/apps/#{@app_id}"
30
+ :path => "/apps/#{@app_id}#{path}"
31
31
  })
32
32
  end
33
33
 
@@ -59,7 +59,7 @@ module Pusher
59
59
  @port = boolean ? 443 : 80
60
60
  end
61
61
 
62
- # Return a channel by name
62
+ # Return a convenience channel object by name. No API request is made.
63
63
  #
64
64
  # @example
65
65
  # Pusher['my-channel']
@@ -72,6 +72,74 @@ module Pusher
72
72
  @channels[channel_name.to_s] ||= Channel.new(url, channel_name, self)
73
73
  end
74
74
 
75
+ # Request a list of occupied channels from the API
76
+ #
77
+ # GET /apps/[id]/channels
78
+ #
79
+ # @param options [Hash] Hash of options for the API - see Pusher API docs
80
+ # @return [Hash] See Pusher API docs
81
+ # @raise [Pusher::Error] on invalid Pusher response - see the error message for more details
82
+ # @raise [Pusher::HTTPError] on any error raised inside Net::HTTP - the original error is available in the original_error attribute
83
+ #
84
+ def channels(options = {})
85
+ @_channels_url ||= url('/channels')
86
+ request = Request.new(:get, @_channels_url, options, nil, nil, self)
87
+ return request.send_sync
88
+ end
89
+
90
+ # Request info for a specific channel
91
+ #
92
+ # GET /apps/[id]/channels/[channel_name]
93
+ #
94
+ # @param channel_name [String] Channel name
95
+ # @param options [Hash] Hash of options for the API - see Pusher API docs
96
+ # @return [Hash] See Pusher API docs
97
+ # @raise [Pusher::Error] on invalid Pusher response - see the error message for more details
98
+ # @raise [Pusher::HTTPError] on any error raised inside Net::HTTP - the original error is available in the original_error attribute
99
+ #
100
+ def channel_info(channel_name, options = {})
101
+ request = Request.new(:get, url("/channels/#{channel_name}"), options, nil, nil, self)
102
+ return request.send_sync
103
+ end
104
+
105
+ # Trigger an event on one or more channels
106
+ #
107
+ # POST /apps/[app_id]/events
108
+ #
109
+ # @param channels [Array] One of more channel names
110
+ # @param event_name [String]
111
+ # @param data [Object] Event data to be triggered in javascript.
112
+ # Objects other than strings will be converted to JSON
113
+ # @param options [Hash] Additional options to send to api, e.g socket_id
114
+ # @return [Hash] See Pusher API docs
115
+ # @raise [Pusher::Error] on invalid Pusher response - see the error message for more details
116
+ # @raise [Pusher::HTTPError] on any error raised inside Net::HTTP - the original error is available in the original_error attribute
117
+ #
118
+ def trigger(channels, event_name, data, options = {})
119
+ @_trigger_url ||= url('/events')
120
+
121
+ encoded_data = case data
122
+ when String
123
+ data
124
+ else
125
+ begin
126
+ MultiJson.encode(data)
127
+ rescue MultiJson::DecodeError => e
128
+ Pusher.logger.error("Could not convert #{data.inspect} into JSON")
129
+ raise e
130
+ end
131
+ end
132
+
133
+ options.merge!({
134
+ :name => event_name,
135
+ :channels => channels,
136
+ :data => encoded_data,
137
+ })
138
+
139
+ request = Request.new(:post, @_trigger_url, {}, MultiJson.encode(options), nil, self)
140
+ return request.send_sync
141
+ end
142
+
75
143
  private
76
144
 
77
145
  def configured?
@@ -57,7 +57,6 @@ module Pusher
57
57
  def initialize(verb, uri, params, body = nil, token = nil, client = Pusher)
58
58
  @verb = verb
59
59
  @uri = uri
60
- @client = client
61
60
 
62
61
  if body
63
62
  @body = body
@@ -65,13 +64,16 @@ module Pusher
65
64
  end
66
65
 
67
66
  request = Signature::Request.new(verb.to_s.upcase, uri.path, params)
68
- auth_hash = request.sign(token || @client.authentication_token)
69
- @params = params.merge(auth_hash)
67
+ request.sign(token || client.authentication_token)
68
+ @params = request.signed_params
70
69
  end
71
70
 
72
71
  def send_sync
73
- require 'net/http' unless defined?(Net::HTTP)
74
- require 'net/https' if (ssl? && !defined?(Net::HTTPS))
72
+ if ssl?
73
+ require 'net/https' unless defined?(Net::HTTPS)
74
+ else
75
+ require 'net/http' unless defined?(Net::HTTP)
76
+ end
75
77
 
76
78
  @http_sync ||= begin
77
79
  http = Net::HTTP.new(@uri.host, @uri.port)
@@ -94,7 +96,7 @@ module Pusher
94
96
  raise "Unknown verb"
95
97
  end
96
98
  rescue Errno::EINVAL, Errno::ECONNRESET, Errno::ECONNREFUSED,
97
- Errno::ETIMEDOUT, Errno::EHOSTUNREACH, Errno::ECONNRESET,
99
+ Errno::ETIMEDOUT, Errno::EHOSTUNREACH,
98
100
  Timeout::Error, EOFError,
99
101
  Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError,
100
102
  Net::ProtocolError => e
@@ -103,7 +105,9 @@ module Pusher
103
105
  raise error
104
106
  end
105
107
 
106
- return handle_response(response.code.to_i, response.body.chomp)
108
+ body = response.body ? response.body.chomp : nil
109
+
110
+ return handle_response(response.code.to_i, body)
107
111
  end
108
112
 
109
113
  def send_async
@@ -139,7 +143,7 @@ module Pusher
139
143
  def handle_response(status_code, body)
140
144
  case status_code
141
145
  when 200
142
- return MultiJson.decode(body, :symbolize_keys => true)
146
+ return symbolize_first_level(MultiJson.decode(body))
143
147
  when 202
144
148
  return true
145
149
  when 400
@@ -156,5 +160,12 @@ module Pusher
156
160
  def ssl?
157
161
  @uri.scheme == 'https'
158
162
  end
163
+
164
+ def symbolize_first_level(hash)
165
+ hash.inject({}) do |result, (key, value)|
166
+ result[key.to_sym] = value
167
+ result
168
+ end
169
+ end
159
170
  end
160
171
  end
data/pusher.gemspec CHANGED
@@ -3,7 +3,7 @@ $:.push File.expand_path("../lib", __FILE__)
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = "pusher"
6
- s.version = "0.9.4"
6
+ s.version = "0.10.0"
7
7
  s.platform = Gem::Platform::RUBY
8
8
  s.authors = ["Pusher"]
9
9
  s.email = ["support@pusher.com"]
@@ -12,7 +12,8 @@ Gem::Specification.new do |s|
12
12
  s.description = %q{Wrapper for pusher.com REST api}
13
13
 
14
14
  s.add_dependency "multi_json", "~> 1.0"
15
- s.add_dependency 'signature', "~> 0.1.2"
15
+ s.add_dependency 'signature', "~> 0.1.4"
16
+ s.add_dependency "jruby-openssl" if defined?(JRUBY_VERSION)
16
17
 
17
18
  s.add_development_dependency "rspec", "~> 2.0"
18
19
  s.add_development_dependency "webmock"
data/spec/channel_spec.rb CHANGED
@@ -10,18 +10,28 @@ describe Pusher::Channel do
10
10
  :port => 80,
11
11
  })
12
12
  @client.encrypted = false
13
+ @channel = @client['test_channel']
13
14
 
14
15
  WebMock.reset!
15
16
  WebMock.disable_net_connect!
17
+ end
18
+
19
+ let(:pusher_url_regexp) { %r{/apps/20/channels/test_channel/events} }
16
20
 
17
- @pusher_url_regexp = %r{/apps/20/channels/test_channel/events}
21
+ def stub_post(status, body = nil)
22
+ options = {:status => status}
23
+ options.merge!({:body => body}) if body
24
+
25
+ WebMock.stub_request(:post, pusher_url_regexp).to_return(options)
26
+ end
27
+
28
+ def stub_post_to_raise(e)
29
+ WebMock.stub_request(:post, pusher_url_regexp).to_raise(e)
18
30
  end
19
31
 
20
32
  describe 'trigger!' do
21
33
  before :each do
22
- WebMock.stub_request(:post, @pusher_url_regexp).
23
- to_return(:status => 202)
24
- @channel = @client['test_channel']
34
+ stub_post 202
25
35
  end
26
36
 
27
37
  it 'should configure HTTP library to talk to pusher API' do
@@ -41,7 +51,7 @@ describe Pusher::Channel do
41
51
  :name => 'Pusher',
42
52
  :last_name => 'App'
43
53
  })
44
- WebMock.should have_requested(:post, %r{/apps/20/channels/test_channel/events}).with { |req|
54
+ WebMock.should have_requested(:post, pusher_url_regexp).with { |req|
45
55
  query_hash = req.uri.query_values
46
56
  query_hash["name"].should == 'new_event'
47
57
  query_hash["auth_key"].should == @client.key
@@ -60,57 +70,54 @@ describe Pusher::Channel do
60
70
  it "should POST string data unmodified in request body" do
61
71
  string = "foo\nbar\""
62
72
  @channel.trigger!('new_event', string)
63
- WebMock.should have_requested(:post, %r{/apps/20/channels/test_channel/events}).with { |req| req.body.should == "foo\nbar\"" }
73
+ WebMock.should have_requested(:post, pusher_url_regexp).with { |req| req.body.should == "foo\nbar\"" }
64
74
  end
65
75
 
66
- it "should catch all Net::HTTP exceptions and raise a Pusher::HTTPError, exposing the original error if required" do
67
- WebMock.stub_request(
68
- :post, %r{/apps/20/channels/test_channel/events}
69
- ).to_raise(Timeout::Error)
76
+ def trigger
77
+ lambda { @channel.trigger!('new_event', 'Some data') }
78
+ end
70
79
 
80
+ it "should catch all Net::HTTP exceptions and raise a Pusher::HTTPError, exposing the original error if required" do
81
+ stub_post_to_raise Timeout::Error
71
82
  error_raised = nil
83
+
72
84
  begin
73
- @client['test_channel'].trigger!('new_event', 'Some data')
85
+ trigger.call
74
86
  rescue => e
75
87
  error_raised = e
76
88
  end
89
+
77
90
  error_raised.class.should == Pusher::HTTPError
78
91
  error_raised.message.should == 'Exception from WebMock (Timeout::Error)'
79
92
  error_raised.original_error.class.should == Timeout::Error
80
93
  end
81
94
 
95
+
96
+ it "should raise Pusher::Error if pusher returns 400" do
97
+ stub_post 400
98
+ trigger.should raise_error(Pusher::Error)
99
+ end
100
+
82
101
  it "should raise AuthenticationError if pusher returns 401" do
83
- WebMock.stub_request(
84
- :post,
85
- %r{/apps/20/channels/test_channel/events}
86
- ).to_return(:status => 401)
87
- lambda {
88
- @client['test_channel'].trigger!('new_event', 'Some data')
89
- }.should raise_error(Pusher::AuthenticationError)
102
+ stub_post 401
103
+ trigger.should raise_error(Pusher::AuthenticationError)
90
104
  end
91
105
 
92
106
  it "should raise Pusher::Error if pusher returns 404" do
93
- WebMock.stub_request(
94
- :post, %r{/apps/20/channels/test_channel/events}
95
- ).to_return(:status => 404)
96
- lambda {
97
- @client['test_channel'].trigger!('new_event', 'Some data')
98
- }.should raise_error(Pusher::Error, 'Resource not found: app_id is probably invalid')
107
+ stub_post 404
108
+ trigger.should raise_error(Pusher::Error, 'Resource not found: app_id is probably invalid')
99
109
  end
100
110
 
101
111
  it "should raise Pusher::Error if pusher returns 500" do
102
- WebMock.stub_request(
103
- :post, %r{/apps/20/channels/test_channel/events}
104
- ).to_return(:status => 500, :body => "some error")
105
- lambda {
106
- @client['test_channel'].trigger!('new_event', 'Some data')
107
- }.should raise_error(Pusher::Error, 'Unknown error (status code 500): some error')
112
+ stub_post 500, "some error"
113
+ trigger.should raise_error(Pusher::Error, 'Unknown error (status code 500): some error')
108
114
  end
109
115
  end
110
116
 
111
- describe 'trigger' do
117
+ describe '#trigger' do
112
118
  it "should log failure if error raised in Net::HTTP call" do
113
- stub_request(:post, @pusher_url_regexp).to_raise(Net::HTTPBadResponse)
119
+ stub_post_to_raise(Net::HTTPBadResponse)
120
+
114
121
  Pusher.logger.should_receive(:error).with("Exception from WebMock (Net::HTTPBadResponse) (Pusher::HTTPError)")
115
122
  Pusher.logger.should_receive(:debug) #backtrace
116
123
  channel = Pusher::Channel.new(@client.url, 'test_channel', @client)
@@ -118,21 +125,19 @@ describe Pusher::Channel do
118
125
  end
119
126
 
120
127
  it "should log failure if Pusher returns an error response" do
121
- stub_request(:post, @pusher_url_regexp).to_return(:status => 401)
122
- # @http.should_receive(:post).and_raise(Net::HTTPBadResponse)
123
- Pusher.logger.should_receive(:error).with(" (Pusher::AuthenticationError)")
128
+ stub_post 401
129
+ Pusher.logger.should_receive(:error).with("Pusher::AuthenticationError (Pusher::AuthenticationError)")
124
130
  Pusher.logger.should_receive(:debug) #backtrace
125
131
  channel = Pusher::Channel.new(@client.url, 'test_channel', @client)
126
132
  channel.trigger('new_event', 'Some data')
127
133
  end
128
134
  end
129
135
 
130
- describe "trigger_async" do
136
+ describe "#trigger_async" do
131
137
  it "should by default POST to http api" do
132
138
  EM.run {
133
- stub_request(:post, @pusher_url_regexp).to_return(:status => 202)
134
- channel = Pusher::Channel.new(@client.url, 'test_channel', @client)
135
- channel.trigger_async('new_event', 'Some data').callback {
139
+ stub_post 202
140
+ @channel.trigger_async('new_event', 'Some data').callback {
136
141
  WebMock.should have_requested(:post, %r{http://api.pusherapp.com})
137
142
  EM.stop
138
143
  }
@@ -142,7 +147,7 @@ describe Pusher::Channel do
142
147
  it "should POST to https api if ssl enabled" do
143
148
  @client.encrypted = true
144
149
  EM.run {
145
- stub_request(:post, @pusher_url_regexp).to_return(:status => 202)
150
+ stub_post 202
146
151
  channel = Pusher::Channel.new(@client.url, 'test_channel', @client)
147
152
  channel.trigger_async('new_event', 'Some data').callback {
148
153
  WebMock.should have_requested(:post, %r{https://api.pusherapp.com})
@@ -152,12 +157,12 @@ describe Pusher::Channel do
152
157
  end
153
158
 
154
159
  it "should return a deferrable which succeeds in success case" do
155
- stub_request(:post, @pusher_url_regexp).to_return(:status => 202)
160
+ stub_post 202
156
161
 
157
162
  EM.run {
158
- d = @client['test_channel'].trigger_async('new_event', 'Some data')
163
+ d = @channel.trigger_async('new_event', 'Some data')
159
164
  d.callback {
160
- WebMock.should have_requested(:post, @pusher_url_regexp)
165
+ WebMock.should have_requested(:post, pusher_url_regexp)
161
166
  EM.stop
162
167
  }
163
168
  d.errback {
@@ -168,15 +173,15 @@ describe Pusher::Channel do
168
173
  end
169
174
 
170
175
  it "should return a deferrable which fails (with exception) in fail case" do
171
- stub_request(:post, @pusher_url_regexp).to_return(:status => 401)
176
+ stub_post 401
172
177
 
173
178
  EM.run {
174
- d = @client['test_channel'].trigger_async('new_event', 'Some data')
179
+ d = @channel.trigger_async('new_event', 'Some data')
175
180
  d.callback {
176
181
  fail
177
182
  }
178
183
  d.errback { |error|
179
- WebMock.should have_requested(:post, @pusher_url_regexp)
184
+ WebMock.should have_requested(:post, pusher_url_regexp)
180
185
  error.should be_kind_of(Pusher::AuthenticationError)
181
186
  EM.stop
182
187
  }
@@ -184,76 +189,58 @@ describe Pusher::Channel do
184
189
  end
185
190
  end
186
191
 
187
- describe "stats" do
188
- before :each do
189
- @api_path = %r{/apps/20/channels/presence-test_channel/stats}
192
+ describe '#info' do
193
+ it "should call the Client#channel_info" do
194
+ @client.should_receive(:channel_info).with('mychannel', anything)
195
+ @channel = @client['mychannel']
196
+ @channel.info
190
197
  end
191
198
 
192
- it "should call the user_count api" do
193
- WebMock.stub_request(:get, @api_path).to_return({
194
- :status => 200,
195
- :body => MultiJson.encode(:user_count => 1)
199
+ it "should assemble the requested attribes into the info option" do
200
+ @client.should_receive(:channel_info).with(anything, {
201
+ :info => "user_count,connection_count"
196
202
  })
197
- @channel = @client['presence-test_channel']
198
-
199
- @channel.stats.should == {
200
- :user_count => 1
201
- }
203
+ @channel = @client['presence-foo']
204
+ @channel.info(%w{user_count connection_count})
202
205
  end
203
206
  end
204
207
 
205
- describe "socket_auth" do
206
- before :each do
207
- @channel = @client['test_channel']
208
+ describe "#authentication_string" do
209
+ def authentication_string(*data)
210
+ lambda { @channel.authentication_string(*data) }
208
211
  end
209
212
 
210
213
  it "should return an authentication string given a socket id" do
211
- auth = @channel.socket_auth('socketid')
214
+ auth = @channel.authentication_string('socketid')
212
215
 
213
216
  auth.should == '12345678900000001:827076f551e22451357939e4c7bb1200de29f921d5bf80b40d71668f9cd61c40'
214
217
  end
215
218
 
216
219
  it "should raise error if authentication is invalid" do
217
220
  [nil, ''].each do |invalid|
218
- lambda {
219
- @channel.socket_auth(invalid)
220
- }.should raise_error
221
+ authentication_string(invalid).should raise_error Pusher::Error
221
222
  end
222
223
  end
223
224
 
224
225
  describe 'with extra string argument' do
225
-
226
226
  it 'should be a string or nil' do
227
- lambda {
228
- @channel.socket_auth('socketid', 'boom')
229
- }.should_not raise_error
230
-
231
- lambda {
232
- @channel.socket_auth('socketid', 123)
233
- }.should raise_error
227
+ authentication_string('socketid', 123).should raise_error Pusher::Error
228
+ authentication_string('socketid', {}).should raise_error Pusher::Error
234
229
 
235
- lambda {
236
- @channel.socket_auth('socketid', nil)
237
- }.should_not raise_error
238
-
239
- lambda {
240
- @channel.socket_auth('socketid', {})
241
- }.should raise_error
230
+ authentication_string('socketid', 'boom').should_not raise_error
231
+ authentication_string('socketid', nil).should_not raise_error
242
232
  end
243
233
 
244
234
  it "should return an authentication string given a socket id and custom args" do
245
- auth = @channel.socket_auth('socketid', 'foobar')
235
+ auth = @channel.authentication_string('socketid', 'foobar')
246
236
 
247
237
  auth.should == "12345678900000001:#{hmac(@client.secret, "socketid:test_channel:foobar")}"
248
238
  end
249
-
250
239
  end
251
240
  end
252
241
 
253
242
  describe '#authenticate' do
254
-
255
243
  before :each do
256
- @channel = @client['test_channel']
257
244
  @custom_data = {:uid => 123, :info => {:name => 'Foo'}}
258
245
  end
259
246
 
data/spec/client_spec.rb CHANGED
@@ -3,7 +3,7 @@ require 'spec_helper'
3
3
  require 'em-http'
4
4
 
5
5
  describe Pusher do
6
- describe 'different clients' do
6
+ describe 'using multiple Client objects' do
7
7
  before :each do
8
8
  @client1 = Pusher::Client.new
9
9
  @client2 = Pusher::Client.new
@@ -72,6 +72,8 @@ describe Pusher do
72
72
  end
73
73
  end
74
74
 
75
+ # The behaviour should be the same when using the Client object, or the
76
+ # 'global' client delegated through the Pusher class
75
77
  [lambda { Pusher }, lambda { Pusher::Client.new }].each do |client_gen|
76
78
  before :each do
77
79
  @client = client_gen.call
@@ -130,7 +132,7 @@ describe Pusher do
130
132
  @client.secret = '12345678900000001'
131
133
  end
132
134
 
133
- describe '.[]' do
135
+ describe '#[]' do
134
136
  before do
135
137
  @channel = @client['test_channel']
136
138
  end
@@ -154,6 +156,71 @@ describe Pusher do
154
156
  end
155
157
  end
156
158
  end
159
+
160
+ describe '#channels' do
161
+ it "should call the correct URL and symbolise response correctly" do
162
+ api_path = %r{/apps/20/channels}
163
+ WebMock.stub_request(:get, api_path).to_return({
164
+ :status => 200,
165
+ :body => MultiJson.encode('channels' => {
166
+ "channel1" => {},
167
+ "channel2" => {}
168
+ })
169
+ })
170
+ @client.channels.should == {
171
+ :channels => {
172
+ "channel1" => {},
173
+ "channel2" => {}
174
+ }
175
+ }
176
+ end
177
+ end
178
+
179
+ describe '#channel_info' do
180
+ it "should call correct URL and symbolise response" do
181
+ api_path = %r{/apps/20/channels/mychannel}
182
+ WebMock.stub_request(:get, api_path).to_return({
183
+ :status => 200,
184
+ :body => MultiJson.encode({
185
+ 'occupied' => false,
186
+ })
187
+ })
188
+ @client.channel_info('mychannel').should == {
189
+ :occupied => false,
190
+ }
191
+ end
192
+ end
193
+
194
+ describe '#trigger' do
195
+ before :each do
196
+ @api_path = %r{/apps/20/events}
197
+ WebMock.stub_request(:post, @api_path).to_return({
198
+ :status => 200,
199
+ :body => MultiJson.encode({})
200
+ })
201
+ end
202
+
203
+ it "should call correct URL" do
204
+ @client.trigger('mychannel', 'event', {'some' => 'data'}).
205
+ should == {}
206
+ end
207
+
208
+ it "should pass any options in the body of the request" do
209
+ @client.trigger('mychannel', 'event', {'some' => 'data'}, {
210
+ :socket_id => "1234"
211
+ })
212
+ WebMock.should have_requested(:post, @api_path).with { |req|
213
+ MultiJson.decode(req.body)["socket_id"].should == '1234'
214
+ }
215
+ end
216
+
217
+ it "should convert non string data to JSON before posting" do
218
+ @client.trigger('mychannel', 'event', {'some' => 'data'})
219
+ WebMock.should have_requested(:post, @api_path).with { |req|
220
+ MultiJson.decode(req.body)["data"].should == '{"some":"data"}'
221
+ }
222
+ end
223
+ end
157
224
  end
158
225
  end
159
226
  end
metadata CHANGED
@@ -1,105 +1,142 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: pusher
3
- version: !ruby/object:Gem::Version
4
- version: 0.9.4
3
+ version: !ruby/object:Gem::Version
4
+ hash: 55
5
5
  prerelease:
6
+ segments:
7
+ - 0
8
+ - 10
9
+ - 0
10
+ version: 0.10.0
6
11
  platform: ruby
7
- authors:
12
+ authors:
8
13
  - Pusher
9
14
  autorequire:
10
15
  bindir: bin
11
16
  cert_chain: []
12
- date: 2012-05-28 00:00:00.000000000 Z
13
- dependencies:
14
- - !ruby/object:Gem::Dependency
17
+
18
+ date: 2012-09-28 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
15
21
  name: multi_json
16
- requirement: &70359107850640 !ruby/object:Gem::Requirement
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
17
24
  none: false
18
- requirements:
25
+ requirements:
19
26
  - - ~>
20
- - !ruby/object:Gem::Version
21
- version: '1.0'
27
+ - !ruby/object:Gem::Version
28
+ hash: 15
29
+ segments:
30
+ - 1
31
+ - 0
32
+ version: "1.0"
22
33
  type: :runtime
23
- prerelease: false
24
- version_requirements: *70359107850640
25
- - !ruby/object:Gem::Dependency
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
26
36
  name: signature
27
- requirement: &70359107850160 !ruby/object:Gem::Requirement
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
28
39
  none: false
29
- requirements:
40
+ requirements:
30
41
  - - ~>
31
- - !ruby/object:Gem::Version
32
- version: 0.1.2
42
+ - !ruby/object:Gem::Version
43
+ hash: 19
44
+ segments:
45
+ - 0
46
+ - 1
47
+ - 4
48
+ version: 0.1.4
33
49
  type: :runtime
34
- prerelease: false
35
- version_requirements: *70359107850160
36
- - !ruby/object:Gem::Dependency
50
+ version_requirements: *id002
51
+ - !ruby/object:Gem::Dependency
37
52
  name: rspec
38
- requirement: &70359107849700 !ruby/object:Gem::Requirement
53
+ prerelease: false
54
+ requirement: &id003 !ruby/object:Gem::Requirement
39
55
  none: false
40
- requirements:
56
+ requirements:
41
57
  - - ~>
42
- - !ruby/object:Gem::Version
43
- version: '2.0'
58
+ - !ruby/object:Gem::Version
59
+ hash: 3
60
+ segments:
61
+ - 2
62
+ - 0
63
+ version: "2.0"
44
64
  type: :development
45
- prerelease: false
46
- version_requirements: *70359107849700
47
- - !ruby/object:Gem::Dependency
65
+ version_requirements: *id003
66
+ - !ruby/object:Gem::Dependency
48
67
  name: webmock
49
- requirement: &70359107849320 !ruby/object:Gem::Requirement
68
+ prerelease: false
69
+ requirement: &id004 !ruby/object:Gem::Requirement
50
70
  none: false
51
- requirements:
52
- - - ! '>='
53
- - !ruby/object:Gem::Version
54
- version: '0'
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ hash: 3
75
+ segments:
76
+ - 0
77
+ version: "0"
55
78
  type: :development
56
- prerelease: false
57
- version_requirements: *70359107849320
58
- - !ruby/object:Gem::Dependency
79
+ version_requirements: *id004
80
+ - !ruby/object:Gem::Dependency
59
81
  name: em-http-request
60
- requirement: &70359107848780 !ruby/object:Gem::Requirement
82
+ prerelease: false
83
+ requirement: &id005 !ruby/object:Gem::Requirement
61
84
  none: false
62
- requirements:
85
+ requirements:
63
86
  - - ~>
64
- - !ruby/object:Gem::Version
87
+ - !ruby/object:Gem::Version
88
+ hash: 23
89
+ segments:
90
+ - 1
91
+ - 0
92
+ - 0
65
93
  version: 1.0.0
66
94
  type: :development
67
- prerelease: false
68
- version_requirements: *70359107848780
69
- - !ruby/object:Gem::Dependency
95
+ version_requirements: *id005
96
+ - !ruby/object:Gem::Dependency
70
97
  name: rake
71
- requirement: &70359107848360 !ruby/object:Gem::Requirement
98
+ prerelease: false
99
+ requirement: &id006 !ruby/object:Gem::Requirement
72
100
  none: false
73
- requirements:
74
- - - ! '>='
75
- - !ruby/object:Gem::Version
76
- version: '0'
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ hash: 3
105
+ segments:
106
+ - 0
107
+ version: "0"
77
108
  type: :development
78
- prerelease: false
79
- version_requirements: *70359107848360
80
- - !ruby/object:Gem::Dependency
109
+ version_requirements: *id006
110
+ - !ruby/object:Gem::Dependency
81
111
  name: rack
82
- requirement: &70359107847900 !ruby/object:Gem::Requirement
112
+ prerelease: false
113
+ requirement: &id007 !ruby/object:Gem::Requirement
83
114
  none: false
84
- requirements:
85
- - - ! '>='
86
- - !ruby/object:Gem::Version
87
- version: '0'
115
+ requirements:
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ hash: 3
119
+ segments:
120
+ - 0
121
+ version: "0"
88
122
  type: :development
89
- prerelease: false
90
- version_requirements: *70359107847900
123
+ version_requirements: *id007
91
124
  description: Wrapper for pusher.com REST api
92
- email:
125
+ email:
93
126
  - support@pusher.com
94
127
  executables: []
128
+
95
129
  extensions: []
130
+
96
131
  extra_rdoc_files: []
97
- files:
132
+
133
+ files:
98
134
  - .document
99
135
  - .gemtest
100
136
  - .gitignore
101
137
  - .travis.yml
102
138
  - Gemfile
139
+ - Gemfile.lock
103
140
  - LICENSE
104
141
  - README.md
105
142
  - Rakefile
@@ -116,31 +153,39 @@ files:
116
153
  - spec/web_hook_spec.rb
117
154
  homepage: http://github.com/pusher/pusher-gem
118
155
  licenses: []
156
+
119
157
  post_install_message:
120
158
  rdoc_options: []
121
- require_paths:
159
+
160
+ require_paths:
122
161
  - lib
123
- required_ruby_version: !ruby/object:Gem::Requirement
162
+ required_ruby_version: !ruby/object:Gem::Requirement
124
163
  none: false
125
- requirements:
126
- - - ! '>='
127
- - !ruby/object:Gem::Version
128
- version: '0'
129
- required_rubygems_version: !ruby/object:Gem::Requirement
164
+ requirements:
165
+ - - ">="
166
+ - !ruby/object:Gem::Version
167
+ hash: 3
168
+ segments:
169
+ - 0
170
+ version: "0"
171
+ required_rubygems_version: !ruby/object:Gem::Requirement
130
172
  none: false
131
- requirements:
132
- - - ! '>='
133
- - !ruby/object:Gem::Version
134
- version: '0'
173
+ requirements:
174
+ - - ">="
175
+ - !ruby/object:Gem::Version
176
+ hash: 3
177
+ segments:
178
+ - 0
179
+ version: "0"
135
180
  requirements: []
181
+
136
182
  rubyforge_project:
137
- rubygems_version: 1.8.10
183
+ rubygems_version: 1.8.6
138
184
  signing_key:
139
185
  specification_version: 3
140
186
  summary: Pusher API client
141
- test_files:
187
+ test_files:
142
188
  - spec/channel_spec.rb
143
189
  - spec/client_spec.rb
144
190
  - spec/spec_helper.rb
145
191
  - spec/web_hook_spec.rb
146
- has_rdoc: