socky-client 0.4.3 → 0.5.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -0
- data/.travis.yml +7 -0
- data/CHANGELOG.md +14 -1
- data/Gemfile +5 -0
- data/README.md +29 -11
- data/Rakefile +6 -22
- data/lib/socky/client/request.rb +50 -0
- data/lib/socky/client/version.rb +5 -0
- data/lib/socky/client.rb +181 -0
- data/socky-client.gemspec +24 -0
- data/spec/spec_helper.rb +1 -3
- data/spec/unit/socky/client_spec.rb +4 -0
- metadata +72 -71
- data/VERSION +0 -1
- data/lib/socky-client/websocket.rb +0 -230
- data/lib/socky-client.rb +0 -135
- data/socky_hosts.yml +0 -5
- data/spec/socky-client_spec.rb +0 -194
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/CHANGELOG.md
CHANGED
@@ -1,11 +1,24 @@
|
|
1
1
|
Changelog
|
2
2
|
=========
|
3
3
|
|
4
|
+
## 0.5.0.beta1 / 2011-08-01
|
5
|
+
|
6
|
+
Socky was rewritten from scratch. From this version it's Rack application and is based on
|
7
|
+
open protocol, and have a lot of new features. Some of them:
|
8
|
+
|
9
|
+
- Rack app - you can run both Socky and your web-application in the same process!
|
10
|
+
- New, standarized communication protocol(it will be easy to implement Socky in other languages)
|
11
|
+
- New user authentication process - much faster and more secure
|
12
|
+
- Allow users to dynamicly subscribe/unsubscribe from channels
|
13
|
+
- New events system - easier to learn and much more powerfull
|
14
|
+
|
15
|
+
And many more - please check [Socky website](http://socky.org) for more or check specific Socky elements at [Github](http://github.com/socky).
|
16
|
+
|
4
17
|
## 0.4.3 / 2010-10-30
|
5
18
|
|
6
19
|
- new features:
|
7
20
|
- new, simpler syntax
|
8
|
-
- bugfixes
|
21
|
+
- bugfixes:
|
9
22
|
- none
|
10
23
|
|
11
24
|
## 0.4.2 / 2010-10-29
|
data/Gemfile
ADDED
data/README.md
CHANGED
@@ -1,22 +1,40 @@
|
|
1
|
-
Socky - client in Ruby
|
2
|
-
===========
|
1
|
+
# Socky - client in Ruby [![](http://travis-ci.org/socky/socky-client-ruby.png)](http://travis-ci.org/socky/socky-client-ruby)
|
3
2
|
|
4
|
-
|
3
|
+
Also important information can be found on our [google group](http://groups.google.com/group/socky-users).
|
5
4
|
|
6
|
-
##
|
5
|
+
## Installation
|
7
6
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
7
|
+
``` bash
|
8
|
+
$ gem install socky-client --pre
|
9
|
+
```
|
10
|
+
|
11
|
+
## Usage
|
12
|
+
|
13
|
+
First require Socky Client:
|
14
|
+
|
15
|
+
``` ruby
|
16
|
+
require 'socky/client'
|
17
|
+
```
|
18
|
+
|
19
|
+
Then createn new Client instance. Parameters required are full address of Socky Server(including app name) and secret of app.
|
20
|
+
|
21
|
+
``` ruby
|
22
|
+
$socky_client = Socky::Client.new('http://ws.socky.org:3000/http/test_app', 'my_secret')
|
23
|
+
```
|
24
|
+
|
25
|
+
This instance of Socky Client can trigger events for all users of server. To do so you can use one of methods:
|
26
|
+
|
27
|
+
``` ruby
|
28
|
+
$socky_client.trigger!('my_event', :channel => 'my_channel', :data => 'my data') # Will raise on error
|
29
|
+
$socky_client.trigger('my_event', :channel => 'my_channel', :data => 'my data') # Will return false on error
|
30
|
+
$socky_client.trigger_async('my_event', :channel => 'my_channel', :data => 'my data') # Async method
|
31
|
+
```
|
14
32
|
|
15
33
|
## License
|
16
34
|
|
17
35
|
(The MIT License)
|
18
36
|
|
19
|
-
Copyright (c) 2010 Bernard Potocki
|
37
|
+
Copyright (c) 2010 Bernard Potocki
|
20
38
|
|
21
39
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
22
40
|
|
data/Rakefile
CHANGED
@@ -1,27 +1,11 @@
|
|
1
|
-
require '
|
2
|
-
|
3
|
-
CLEAN.include %w(**/*.{log,rbc})
|
1
|
+
require 'bundler'
|
2
|
+
Bundler::GemHelper.install_tasks
|
4
3
|
|
5
4
|
require 'rspec/core/rake_task'
|
6
5
|
|
7
|
-
|
8
|
-
|
9
|
-
|
6
|
+
RSpec::Core::RakeTask.new do |t|
|
7
|
+
t.rspec_opts = ["-c", "-f progress"]
|
8
|
+
t.pattern = 'spec/**/*_spec.rb'
|
10
9
|
end
|
11
10
|
|
12
|
-
|
13
|
-
require 'jeweler'
|
14
|
-
Jeweler::Tasks.new do |gemspec|
|
15
|
-
gemspec.name = "socky-client"
|
16
|
-
gemspec.summary = "Socky is a WebSocket server and client for Ruby"
|
17
|
-
gemspec.description = "Socky is a WebSocket server and client for Ruby"
|
18
|
-
gemspec.email = "bernard.potocki@imanel.org"
|
19
|
-
gemspec.homepage = "http://imanel.org/projects/socky"
|
20
|
-
gemspec.authors = ["Bernard Potocki"]
|
21
|
-
gemspec.add_dependency 'json'
|
22
|
-
gemspec.add_development_dependency 'rspec', '~> 2.0'
|
23
|
-
gemspec.files.exclude ".gitignore"
|
24
|
-
end
|
25
|
-
rescue LoadError
|
26
|
-
puts "Jeweler not available. Install it with: gem install jeweler"
|
27
|
-
end
|
11
|
+
task :default => :spec
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'socky/authenticator'
|
2
|
+
require 'multi_json'
|
3
|
+
require 'crack/core_extensions' # Used for Hash#to_params
|
4
|
+
|
5
|
+
module Socky
|
6
|
+
class Client
|
7
|
+
class Request
|
8
|
+
|
9
|
+
attr_reader :client, :event, :channel, :data
|
10
|
+
|
11
|
+
def initialize(client, event, channel, data = nil)
|
12
|
+
@client = client
|
13
|
+
@event = event
|
14
|
+
@channel = channel
|
15
|
+
@data = MultiJson.encode(data)
|
16
|
+
end
|
17
|
+
|
18
|
+
def timestamp
|
19
|
+
@timestamp ||= Time.now.to_i
|
20
|
+
end
|
21
|
+
|
22
|
+
def body
|
23
|
+
content = {}
|
24
|
+
content['event'] = @event
|
25
|
+
content['channel'] = @channel
|
26
|
+
content['timestamp'] = timestamp
|
27
|
+
content['data'] = @data
|
28
|
+
content['auth'] = auth_string
|
29
|
+
content.to_params
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def auth_string
|
35
|
+
Authenticator.authenticate({
|
36
|
+
:connection_id => timestamp,
|
37
|
+
:channel => @channel,
|
38
|
+
:event => @event,
|
39
|
+
:data => @data
|
40
|
+
}, {
|
41
|
+
:secret => @client.secret,
|
42
|
+
:method => :http
|
43
|
+
})['auth']
|
44
|
+
rescue => e
|
45
|
+
raise AuthenticationError, e.message
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
data/lib/socky/client.rb
ADDED
@@ -0,0 +1,181 @@
|
|
1
|
+
# Mostly copied from pusher gem - they did great job and probably there are no reason to reinvent wheel ;)
|
2
|
+
autoload 'Logger', 'logger'
|
3
|
+
require File.expand_path(File.dirname(__FILE__)) + '/client/request'
|
4
|
+
|
5
|
+
module Socky
|
6
|
+
class Client
|
7
|
+
# All Socky errors descend from this class so you can easily rescue Socky errors
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# begin
|
11
|
+
# socky_client.trigger!('an_event', :channel => 'my_channel', :data => {:some => 'data'})
|
12
|
+
# rescue Socky::Client::Error => e
|
13
|
+
# # Do something on error
|
14
|
+
# end
|
15
|
+
class Error < RuntimeError; end
|
16
|
+
class ArgumentError < Error; end
|
17
|
+
class AuthenticationError < Error; end
|
18
|
+
class ConfigurationError < Error; end
|
19
|
+
class HTTPError < Error; attr_accessor :original_error; end
|
20
|
+
|
21
|
+
class << self
|
22
|
+
attr_writer :logger
|
23
|
+
|
24
|
+
# @private
|
25
|
+
def logger
|
26
|
+
@logger ||= begin
|
27
|
+
log = Logger.new($stdout)
|
28
|
+
log.level = Logger::INFO
|
29
|
+
log
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
attr_reader :uri, :secret
|
35
|
+
attr_writer :logger
|
36
|
+
|
37
|
+
# Create Socky::Client instance for later use.
|
38
|
+
# This is usually needed only once per application so it's good idea to put it in global variable
|
39
|
+
#
|
40
|
+
# @example
|
41
|
+
# $socky_client = Socky::Client.new('http://example.org/http/my_app', 'my_secret')
|
42
|
+
#
|
43
|
+
# @param uri [String] Full uri(including app name) to Socky server
|
44
|
+
# @param secret [String] Socky App secret
|
45
|
+
#
|
46
|
+
def initialize(uri, secret)
|
47
|
+
@uri = URI.parse(uri)
|
48
|
+
@secret = secret
|
49
|
+
end
|
50
|
+
|
51
|
+
# Trigger event
|
52
|
+
#
|
53
|
+
# @example
|
54
|
+
# begin
|
55
|
+
# $socky_client.trigger!('an_event', :channel => 'my_channe', :data => {:some => 'data'})
|
56
|
+
# rescue Socky::Client::Error => e
|
57
|
+
# # Do something on error
|
58
|
+
# end
|
59
|
+
#
|
60
|
+
# @param [String] event Event name to be triggered in javascript.
|
61
|
+
# @param [Hash] opts Special options for request
|
62
|
+
# @option opts [String] :channel Channel to which event will be sent
|
63
|
+
# @option opts [Object] :data Data for trigger - Objects other than strings will be converted to JSON
|
64
|
+
#
|
65
|
+
# @raise [Socky::Client::Error] on invalid Socky Server response - see the error message for more details
|
66
|
+
# @raise [Socky::Client::HTTPError] on any error raised inside Net::HTTP - the original error is available in the original_error attribute
|
67
|
+
#
|
68
|
+
def trigger!(event, opts = {})
|
69
|
+
require 'net/http' unless defined?(Net::HTTP)
|
70
|
+
require 'net/https' if (ssl? && !defined?(Net::HTTPS))
|
71
|
+
|
72
|
+
channel = opts[:channel] || opts['channel']
|
73
|
+
data = opts[:data] || opts['data']
|
74
|
+
raise ArgumentError, 'no channel provided' unless channel
|
75
|
+
|
76
|
+
request = Socky::Client::Request.new(self, event, channel, data)
|
77
|
+
|
78
|
+
@http_sync ||= begin
|
79
|
+
http = Net::HTTP.new(@uri.host, @uri.port)
|
80
|
+
http.use_ssl = true if ssl?
|
81
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE if ssl?
|
82
|
+
http
|
83
|
+
end
|
84
|
+
|
85
|
+
begin
|
86
|
+
response = @http_sync.post(@uri.path,
|
87
|
+
request.body, { 'Content-Type'=> 'application/json' })
|
88
|
+
rescue Errno::EINVAL, Errno::ECONNRESET, Errno::ECONNREFUSED,
|
89
|
+
Timeout::Error, EOFError,
|
90
|
+
Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError,
|
91
|
+
Net::ProtocolError => e
|
92
|
+
error = Socky::Client::HTTPError.new("#{e.message} (#{e.class})")
|
93
|
+
error.original_error = e
|
94
|
+
raise error
|
95
|
+
end
|
96
|
+
|
97
|
+
return handle_response(response.code.to_i, response.body.chomp)
|
98
|
+
end
|
99
|
+
|
100
|
+
# Trigger event, catching and logging any errors.
|
101
|
+
#
|
102
|
+
# @note CAUTION! No exceptions will be raised on failure
|
103
|
+
# @param (see #trigger!)
|
104
|
+
#
|
105
|
+
def trigger(event, opts = {})
|
106
|
+
trigger!(event, opts)
|
107
|
+
rescue Socky::Client::Error => e
|
108
|
+
Socky::Client.logger.error("#{e.message} (#{e.class})")
|
109
|
+
Socky::Client.logger.debug(e.backtrace.join("\n"))
|
110
|
+
false
|
111
|
+
end
|
112
|
+
|
113
|
+
# Trigger event asynchronously using EventMachine::HttpRequest
|
114
|
+
#
|
115
|
+
# @param (see #trigger!)
|
116
|
+
#
|
117
|
+
# @return [EM::DefaultDeferrable]
|
118
|
+
# Attach a callback to be notified of success (with no parameters).
|
119
|
+
# Attach an errback to be notified of failure (with an error parameter
|
120
|
+
# which includes the HTTP status code returned)
|
121
|
+
#
|
122
|
+
# @raise [LoadError] unless em-http-request gem is available
|
123
|
+
# @raise [Socky::Client::Error] unless the eventmachine reactor is running.
|
124
|
+
# You probably want to run your application inside a server such as thin.
|
125
|
+
#
|
126
|
+
def trigger_async(event, opts = {}, &block)
|
127
|
+
unless defined?(EventMachine) && EventMachine.reactor_running?
|
128
|
+
raise Error, "In order to use trigger_async you must be running inside an eventmachine loop"
|
129
|
+
end
|
130
|
+
require 'em-http' unless defined?(EventMachine::HttpRequest)
|
131
|
+
|
132
|
+
channel = opts[:channel] || opts['channel']
|
133
|
+
data = opts[:data] || opts['data']
|
134
|
+
raise ArgumentError, 'no channel provided' unless channel
|
135
|
+
|
136
|
+
request = Socky::Client::Request.new(self, event, channel, data)
|
137
|
+
|
138
|
+
deferrable = EM::DefaultDeferrable.new
|
139
|
+
|
140
|
+
http = EventMachine::HttpRequest.new(@uri).post({
|
141
|
+
:timeout => 5, :body => request.body, :head => {'Content-Type'=> 'application/json'}
|
142
|
+
})
|
143
|
+
http.callback {
|
144
|
+
begin
|
145
|
+
handle_response(http.response_header.status, http.response.chomp)
|
146
|
+
deferrable.succeed
|
147
|
+
rescue => e
|
148
|
+
deferrable.fail(e)
|
149
|
+
end
|
150
|
+
}
|
151
|
+
http.errback {
|
152
|
+
Socky::Client.logger.debug("Network error connecting to socky server: #{http.inspect}")
|
153
|
+
deferrable.fail(Error.new("Network error connecting to socky server"))
|
154
|
+
}
|
155
|
+
|
156
|
+
deferrable
|
157
|
+
end
|
158
|
+
|
159
|
+
private
|
160
|
+
|
161
|
+
def handle_response(status_code, body)
|
162
|
+
case status_code
|
163
|
+
when 202
|
164
|
+
return true
|
165
|
+
when 400
|
166
|
+
raise Error, "Bad request: #{body}"
|
167
|
+
when 401
|
168
|
+
raise AuthenticationError, body
|
169
|
+
when 404
|
170
|
+
raise Error, "Resource not found: app name is probably invalid"
|
171
|
+
else
|
172
|
+
raise Error, "Unknown error (status code #{status_code}): #{body}"
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
def ssl?
|
177
|
+
@uri.scheme == 'https'
|
178
|
+
end
|
179
|
+
|
180
|
+
end
|
181
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "socky/client/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "socky-client"
|
7
|
+
s.version = Socky::Client::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Bernard Potocki"]
|
10
|
+
s.email = ["bernard.potocki@imanel.org"]
|
11
|
+
s.homepage = "http://socky.org"
|
12
|
+
s.summary = %q{Socky is a WebSocket server and client for Ruby}
|
13
|
+
s.description = %q{Socky is a WebSocket server and client for Ruby}
|
14
|
+
|
15
|
+
s.add_dependency 'crack', "~> 0.1.0"
|
16
|
+
s.add_dependency 'multi_json', '~> 1.0'
|
17
|
+
s.add_dependency 'socky-authenticator', '~> 0.5.0.beta5'
|
18
|
+
s.add_development_dependency 'rspec', '~> 2.0'
|
19
|
+
|
20
|
+
s.files = `git ls-files`.split("\n")
|
21
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
22
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
23
|
+
s.require_paths = ["lib"]
|
24
|
+
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,104 +1,105 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: socky-client
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
prerelease:
|
6
|
-
segments:
|
7
|
-
- 0
|
8
|
-
- 4
|
9
|
-
- 3
|
10
|
-
version: 0.4.3
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.5.0.beta1
|
5
|
+
prerelease: 6
|
11
6
|
platform: ruby
|
12
|
-
authors:
|
7
|
+
authors:
|
13
8
|
- Bernard Potocki
|
14
9
|
autorequire:
|
15
10
|
bindir: bin
|
16
11
|
cert_chain: []
|
17
|
-
|
18
|
-
date: 2010-10-30 00:00:00 +02:00
|
12
|
+
date: 2011-08-01 00:00:00.000000000 +02:00
|
19
13
|
default_executable:
|
20
|
-
dependencies:
|
21
|
-
- !ruby/object:Gem::Dependency
|
22
|
-
name:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: crack
|
17
|
+
requirement: &70226763937280 !ruby/object:Gem::Requirement
|
18
|
+
none: false
|
19
|
+
requirements:
|
20
|
+
- - ~>
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 0.1.0
|
23
|
+
type: :runtime
|
23
24
|
prerelease: false
|
24
|
-
|
25
|
+
version_requirements: *70226763937280
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: multi_json
|
28
|
+
requirement: &70226763936780 !ruby/object:Gem::Requirement
|
25
29
|
none: false
|
26
|
-
requirements:
|
27
|
-
- -
|
28
|
-
- !ruby/object:Gem::Version
|
29
|
-
|
30
|
-
segments:
|
31
|
-
- 0
|
32
|
-
version: "0"
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.0'
|
33
34
|
type: :runtime
|
34
|
-
version_requirements: *id001
|
35
|
-
- !ruby/object:Gem::Dependency
|
36
|
-
name: rspec
|
37
35
|
prerelease: false
|
38
|
-
|
36
|
+
version_requirements: *70226763936780
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: socky-authenticator
|
39
|
+
requirement: &70226763936320 !ruby/object:Gem::Requirement
|
39
40
|
none: false
|
40
|
-
requirements:
|
41
|
+
requirements:
|
41
42
|
- - ~>
|
42
|
-
- !ruby/object:Gem::Version
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: 0.5.0.beta5
|
45
|
+
type: :runtime
|
46
|
+
prerelease: false
|
47
|
+
version_requirements: *70226763936320
|
48
|
+
- !ruby/object:Gem::Dependency
|
49
|
+
name: rspec
|
50
|
+
requirement: &70226763935760 !ruby/object:Gem::Requirement
|
51
|
+
none: false
|
52
|
+
requirements:
|
53
|
+
- - ~>
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '2.0'
|
48
56
|
type: :development
|
49
|
-
|
57
|
+
prerelease: false
|
58
|
+
version_requirements: *70226763935760
|
50
59
|
description: Socky is a WebSocket server and client for Ruby
|
51
|
-
email:
|
60
|
+
email:
|
61
|
+
- bernard.potocki@imanel.org
|
52
62
|
executables: []
|
53
|
-
|
54
63
|
extensions: []
|
55
|
-
|
56
|
-
|
57
|
-
-
|
58
|
-
|
64
|
+
extra_rdoc_files: []
|
65
|
+
files:
|
66
|
+
- .gitignore
|
67
|
+
- .travis.yml
|
59
68
|
- CHANGELOG.md
|
69
|
+
- Gemfile
|
60
70
|
- README.md
|
61
71
|
- Rakefile
|
62
|
-
-
|
63
|
-
- lib/socky
|
64
|
-
- lib/socky
|
65
|
-
-
|
66
|
-
- spec/socky-client_spec.rb
|
72
|
+
- lib/socky/client.rb
|
73
|
+
- lib/socky/client/request.rb
|
74
|
+
- lib/socky/client/version.rb
|
75
|
+
- socky-client.gemspec
|
67
76
|
- spec/spec_helper.rb
|
77
|
+
- spec/unit/socky/client_spec.rb
|
68
78
|
has_rdoc: true
|
69
|
-
homepage: http://
|
79
|
+
homepage: http://socky.org
|
70
80
|
licenses: []
|
71
|
-
|
72
81
|
post_install_message:
|
73
|
-
rdoc_options:
|
74
|
-
|
75
|
-
require_paths:
|
82
|
+
rdoc_options: []
|
83
|
+
require_paths:
|
76
84
|
- lib
|
77
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
85
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
78
86
|
none: false
|
79
|
-
requirements:
|
80
|
-
- -
|
81
|
-
- !ruby/object:Gem::Version
|
82
|
-
|
83
|
-
|
84
|
-
- 0
|
85
|
-
version: "0"
|
86
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - ! '>='
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '0'
|
91
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
87
92
|
none: false
|
88
|
-
requirements:
|
89
|
-
- -
|
90
|
-
- !ruby/object:Gem::Version
|
91
|
-
|
92
|
-
segments:
|
93
|
-
- 0
|
94
|
-
version: "0"
|
93
|
+
requirements:
|
94
|
+
- - ! '>'
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 1.3.1
|
95
97
|
requirements: []
|
96
|
-
|
97
98
|
rubyforge_project:
|
98
|
-
rubygems_version: 1.
|
99
|
+
rubygems_version: 1.6.2
|
99
100
|
signing_key:
|
100
101
|
specification_version: 3
|
101
102
|
summary: Socky is a WebSocket server and client for Ruby
|
102
|
-
test_files:
|
103
|
-
- spec/socky-client_spec.rb
|
103
|
+
test_files:
|
104
104
|
- spec/spec_helper.rb
|
105
|
+
- spec/unit/socky/client_spec.rb
|
data/VERSION
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
0.4.3
|
@@ -1,230 +0,0 @@
|
|
1
|
-
# Copyright: Hiroshi Ichikawa <http://gimite.net/en/>
|
2
|
-
# Lincense: New BSD Lincense
|
3
|
-
# Reference: http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol
|
4
|
-
|
5
|
-
require "socket"
|
6
|
-
require "uri"
|
7
|
-
require "digest/md5"
|
8
|
-
require "openssl"
|
9
|
-
|
10
|
-
|
11
|
-
class WebSocket
|
12
|
-
|
13
|
-
class << self
|
14
|
-
|
15
|
-
attr_accessor(:debug)
|
16
|
-
|
17
|
-
end
|
18
|
-
|
19
|
-
class Error < RuntimeError
|
20
|
-
|
21
|
-
end
|
22
|
-
|
23
|
-
def initialize(arg, params = {})
|
24
|
-
|
25
|
-
uri = arg.is_a?(String) ? URI.parse(arg) : arg
|
26
|
-
|
27
|
-
if uri.scheme == "ws"
|
28
|
-
default_port = 80
|
29
|
-
elsif uri.scheme = "wss"
|
30
|
-
default_port = 443
|
31
|
-
else
|
32
|
-
raise(WebSocket::Error, "unsupported scheme: #{uri.scheme}")
|
33
|
-
end
|
34
|
-
|
35
|
-
@path = (uri.path.empty? ? "/" : uri.path) + (uri.query ? "?" + uri.query : "")
|
36
|
-
host = uri.host + (uri.port == default_port ? "" : ":#{uri.port}")
|
37
|
-
origin = params[:origin] || "http://#{uri.host}"
|
38
|
-
key1 = generate_key()
|
39
|
-
key2 = generate_key()
|
40
|
-
key3 = generate_key3()
|
41
|
-
|
42
|
-
socket = TCPSocket.new(uri.host, uri.port || default_port)
|
43
|
-
|
44
|
-
if uri.scheme == "ws"
|
45
|
-
@socket = socket
|
46
|
-
else
|
47
|
-
@socket = ssl_handshake(socket)
|
48
|
-
end
|
49
|
-
|
50
|
-
write(
|
51
|
-
"GET #{@path} HTTP/1.1\r\n" +
|
52
|
-
"Upgrade: WebSocket\r\n" +
|
53
|
-
"Connection: Upgrade\r\n" +
|
54
|
-
"Host: #{host}\r\n" +
|
55
|
-
"Origin: #{origin}\r\n" +
|
56
|
-
"Sec-WebSocket-Key1: #{key1}\r\n" +
|
57
|
-
"Sec-WebSocket-Key2: #{key2}\r\n" +
|
58
|
-
"\r\n" +
|
59
|
-
"#{key3}")
|
60
|
-
flush()
|
61
|
-
|
62
|
-
line = gets().chomp()
|
63
|
-
raise(WebSocket::Error, "bad response: #{line}") if !(line =~ /\AHTTP\/1.1 101 /n)
|
64
|
-
read_header()
|
65
|
-
if @header["Sec-WebSocket-Origin"] != origin
|
66
|
-
raise(WebSocket::Error,
|
67
|
-
"origin doesn't match: '#{@header["WebSocket-Origin"]}' != '#{origin}'")
|
68
|
-
end
|
69
|
-
reply_digest = read(16)
|
70
|
-
expected_digest = security_digest(key1, key2, key3)
|
71
|
-
if reply_digest != expected_digest
|
72
|
-
raise(WebSocket::Error,
|
73
|
-
"security digest doesn't match: %p != %p" % [reply_digest, expected_digest])
|
74
|
-
end
|
75
|
-
@handshaked = true
|
76
|
-
|
77
|
-
@closing_started = false
|
78
|
-
end
|
79
|
-
|
80
|
-
attr_reader(:header, :path)
|
81
|
-
|
82
|
-
def send(data)
|
83
|
-
if !@handshaked
|
84
|
-
raise(WebSocket::Error, "call WebSocket\#handshake first")
|
85
|
-
end
|
86
|
-
data = force_encoding(data.dup(), "ASCII-8BIT")
|
87
|
-
write("\x00#{data}\xff")
|
88
|
-
flush()
|
89
|
-
end
|
90
|
-
|
91
|
-
def receive()
|
92
|
-
if !@handshaked
|
93
|
-
raise(WebSocket::Error, "call WebSocket\#handshake first")
|
94
|
-
end
|
95
|
-
packet = gets("\xff")
|
96
|
-
return nil if !packet
|
97
|
-
if packet =~ /\A\x00(.*)\xff\z/nm
|
98
|
-
return force_encoding($1, "UTF-8")
|
99
|
-
elsif packet == "\xff" && read(1) == "\x00" # closing
|
100
|
-
if @server
|
101
|
-
@socket.close()
|
102
|
-
else
|
103
|
-
close()
|
104
|
-
end
|
105
|
-
return nil
|
106
|
-
else
|
107
|
-
raise(WebSocket::Error, "input must be either '\\x00...\\xff' or '\\xff\\x00'")
|
108
|
-
end
|
109
|
-
end
|
110
|
-
|
111
|
-
def tcp_socket
|
112
|
-
return @socket
|
113
|
-
end
|
114
|
-
|
115
|
-
def host
|
116
|
-
return @header["Host"]
|
117
|
-
end
|
118
|
-
|
119
|
-
def origin
|
120
|
-
return @header["Origin"]
|
121
|
-
end
|
122
|
-
|
123
|
-
# Does closing handshake.
|
124
|
-
def close()
|
125
|
-
return if @closing_started
|
126
|
-
write("\xff\x00")
|
127
|
-
@socket.close() if !@server
|
128
|
-
@closing_started = true
|
129
|
-
end
|
130
|
-
|
131
|
-
def close_socket()
|
132
|
-
@socket.close()
|
133
|
-
end
|
134
|
-
|
135
|
-
private
|
136
|
-
|
137
|
-
NOISE_CHARS = ("\x21".."\x2f").to_a() + ("\x3a".."\x7e").to_a()
|
138
|
-
|
139
|
-
def read_header()
|
140
|
-
@header = {}
|
141
|
-
while line = gets()
|
142
|
-
line = line.chomp()
|
143
|
-
break if line.empty?
|
144
|
-
if !(line =~ /\A(\S+): (.*)\z/n)
|
145
|
-
raise(WebSocket::Error, "invalid request: #{line}")
|
146
|
-
end
|
147
|
-
@header[$1] = $2
|
148
|
-
end
|
149
|
-
if @header["Upgrade"] != "WebSocket"
|
150
|
-
raise(WebSocket::Error, "invalid Upgrade: " + @header["Upgrade"])
|
151
|
-
end
|
152
|
-
if @header["Connection"] != "Upgrade"
|
153
|
-
raise(WebSocket::Error, "invalid Connection: " + @header["Connection"])
|
154
|
-
end
|
155
|
-
end
|
156
|
-
|
157
|
-
def gets(rs = $/)
|
158
|
-
line = @socket.gets(rs)
|
159
|
-
$stderr.printf("recv> %p\n", line) if WebSocket.debug
|
160
|
-
return line
|
161
|
-
end
|
162
|
-
|
163
|
-
def read(num_bytes)
|
164
|
-
str = @socket.read(num_bytes)
|
165
|
-
$stderr.printf("recv> %p\n", str) if WebSocket.debug
|
166
|
-
return str
|
167
|
-
end
|
168
|
-
|
169
|
-
def write(data)
|
170
|
-
if WebSocket.debug
|
171
|
-
data.scan(/\G(.*?(\n|\z))/n) do
|
172
|
-
$stderr.printf("send> %p\n", $&) if !$&.empty?
|
173
|
-
end
|
174
|
-
end
|
175
|
-
@socket.write(data)
|
176
|
-
end
|
177
|
-
|
178
|
-
def flush()
|
179
|
-
@socket.flush()
|
180
|
-
end
|
181
|
-
|
182
|
-
def security_digest(key1, key2, key3)
|
183
|
-
bytes1 = websocket_key_to_bytes(key1)
|
184
|
-
bytes2 = websocket_key_to_bytes(key2)
|
185
|
-
return Digest::MD5.digest(bytes1 + bytes2 + key3)
|
186
|
-
end
|
187
|
-
|
188
|
-
def generate_key()
|
189
|
-
spaces = 1 + rand(12)
|
190
|
-
max = 0xffffffff / spaces
|
191
|
-
number = rand(max + 1)
|
192
|
-
key = (number * spaces).to_s()
|
193
|
-
(1 + rand(12)).times() do
|
194
|
-
char = NOISE_CHARS[rand(NOISE_CHARS.size)]
|
195
|
-
pos = rand(key.size + 1)
|
196
|
-
key[pos...pos] = char
|
197
|
-
end
|
198
|
-
spaces.times() do
|
199
|
-
pos = 1 + rand(key.size - 1)
|
200
|
-
key[pos...pos] = " "
|
201
|
-
end
|
202
|
-
return key
|
203
|
-
end
|
204
|
-
|
205
|
-
def generate_key3()
|
206
|
-
return [rand(0x100000000)].pack("N") + [rand(0x100000000)].pack("N")
|
207
|
-
end
|
208
|
-
|
209
|
-
def websocket_key_to_bytes(key)
|
210
|
-
num = key.gsub(/[^\d]/n, "").to_i() / key.scan(/ /).size
|
211
|
-
return [num].pack("N")
|
212
|
-
end
|
213
|
-
|
214
|
-
def force_encoding(str, encoding)
|
215
|
-
if str.respond_to?(:force_encoding)
|
216
|
-
return str.force_encoding(encoding)
|
217
|
-
else
|
218
|
-
return str
|
219
|
-
end
|
220
|
-
end
|
221
|
-
|
222
|
-
def ssl_handshake(socket)
|
223
|
-
ssl_context = OpenSSL::SSL::SSLContext.new()
|
224
|
-
ssl_socket = OpenSSL::SSL::SSLSocket.new(socket, ssl_context)
|
225
|
-
ssl_socket.sync_close = true
|
226
|
-
ssl_socket.connect()
|
227
|
-
return ssl_socket
|
228
|
-
end
|
229
|
-
|
230
|
-
end
|
data/lib/socky-client.rb
DELETED
@@ -1,135 +0,0 @@
|
|
1
|
-
require 'json'
|
2
|
-
require 'logger'
|
3
|
-
require 'yaml'
|
4
|
-
|
5
|
-
require File.dirname(__FILE__) + '/socky-client/websocket'
|
6
|
-
|
7
|
-
module Socky
|
8
|
-
|
9
|
-
class << self
|
10
|
-
|
11
|
-
attr_accessor :config_path, :logger
|
12
|
-
def config_path
|
13
|
-
@config_path ||= 'socky_hosts.yml'
|
14
|
-
end
|
15
|
-
|
16
|
-
def config
|
17
|
-
@config ||= YAML.load_file(config_path).freeze
|
18
|
-
end
|
19
|
-
|
20
|
-
def send(*args)
|
21
|
-
options = normalize_options(*args)
|
22
|
-
send_message(options.delete(:data), options)
|
23
|
-
end
|
24
|
-
|
25
|
-
def show_connections
|
26
|
-
send_query(:show_connections)
|
27
|
-
end
|
28
|
-
|
29
|
-
def hosts
|
30
|
-
config[:hosts]
|
31
|
-
end
|
32
|
-
|
33
|
-
def logger
|
34
|
-
@logger ||= Logger.new(STDOUT)
|
35
|
-
end
|
36
|
-
|
37
|
-
def deprecation_warning(msg)
|
38
|
-
logger.warn "DEPRECATION WARNING: " + msg.to_s
|
39
|
-
end
|
40
|
-
|
41
|
-
private
|
42
|
-
|
43
|
-
def normalize_options(data, options = {})
|
44
|
-
case data
|
45
|
-
when Hash
|
46
|
-
options, data = data, nil
|
47
|
-
when String, Symbol
|
48
|
-
options[:data] = data
|
49
|
-
else
|
50
|
-
options.merge!(:data => data)
|
51
|
-
end
|
52
|
-
|
53
|
-
options[:data] = options[:data].to_s
|
54
|
-
options
|
55
|
-
end
|
56
|
-
|
57
|
-
def send_message(data, opts = {})
|
58
|
-
to = opts[:to] || {}
|
59
|
-
except = opts[:except] || {}
|
60
|
-
|
61
|
-
# Move to new syntax
|
62
|
-
if opts[:to] || opts[:except]
|
63
|
-
deprecation_warning "Using of :to and :except will be removed in next version - please move to new syntax."
|
64
|
-
end
|
65
|
-
to[:client] ||= opts[:client]
|
66
|
-
to[:clients] ||= opts[:clients]
|
67
|
-
to[:channel] ||= opts[:channel]
|
68
|
-
to[:channels] ||= opts[:channels]
|
69
|
-
# end of new syntax
|
70
|
-
|
71
|
-
unless to.is_a?(Hash) && except.is_a?(Hash)
|
72
|
-
raise "recipiend data should be in hash format"
|
73
|
-
end
|
74
|
-
|
75
|
-
to_clients = to[:client] || to[:clients]
|
76
|
-
to_channels = to[:channel] || to[:channels]
|
77
|
-
except_clients = except[:client] || except[:clients]
|
78
|
-
except_channels = except[:channel] || except[:channels]
|
79
|
-
|
80
|
-
# If clients or channels are non-nil but empty then there's no users to target message
|
81
|
-
return if (to_clients.is_a?(Array) && to_clients.empty?) || (to_channels.is_a?(Array) && to_channels.empty?)
|
82
|
-
|
83
|
-
hash = {
|
84
|
-
:command => :broadcast,
|
85
|
-
:body => data,
|
86
|
-
:to => {
|
87
|
-
:clients => to_clients,
|
88
|
-
:channels => to_channels,
|
89
|
-
},
|
90
|
-
:except => {
|
91
|
-
:clients => except_clients,
|
92
|
-
:channels => except_channels,
|
93
|
-
}
|
94
|
-
}
|
95
|
-
|
96
|
-
[:to, :except].each do |type|
|
97
|
-
hash[type].reject! { |key,val| val.nil? || (type == :except && val.empty?)}
|
98
|
-
hash.delete(type) if hash[type].empty?
|
99
|
-
end
|
100
|
-
|
101
|
-
send_data(hash)
|
102
|
-
end
|
103
|
-
|
104
|
-
def send_query(type)
|
105
|
-
hash = {
|
106
|
-
:command => :query,
|
107
|
-
:type => type
|
108
|
-
}
|
109
|
-
send_data(hash, true)
|
110
|
-
end
|
111
|
-
|
112
|
-
def send_data(hash, response = false)
|
113
|
-
res = []
|
114
|
-
hosts.each do |address|
|
115
|
-
begin
|
116
|
-
scheme = (address[:secure] ? "wss" : "ws")
|
117
|
-
@socket = WebSocket.new("#{scheme}://#{address[:host]}:#{address[:port]}/?admin=1&client_secret=#{address[:secret]}")
|
118
|
-
@socket.send(hash.to_json)
|
119
|
-
res << @socket.receive if response
|
120
|
-
rescue
|
121
|
-
puts "ERROR: Connection to server at '#{scheme}://#{address[:host]}:#{address[:port]}' failed"
|
122
|
-
ensure
|
123
|
-
@socket.close if @socket && !@socket.tcp_socket.closed?
|
124
|
-
end
|
125
|
-
end
|
126
|
-
if response
|
127
|
-
res.collect {|r| JSON.parse(r)["body"] }
|
128
|
-
else
|
129
|
-
true
|
130
|
-
end
|
131
|
-
end
|
132
|
-
|
133
|
-
end
|
134
|
-
|
135
|
-
end
|
data/socky_hosts.yml
DELETED
data/spec/socky-client_spec.rb
DELETED
@@ -1,194 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe Socky do
|
4
|
-
it "should have config in hash form" do
|
5
|
-
Socky.config.should_not be_nil
|
6
|
-
Socky.config.class.should eql(Hash)
|
7
|
-
end
|
8
|
-
|
9
|
-
it "should have host list taken from config" do
|
10
|
-
Socky.hosts.should eql(Socky.config[:hosts])
|
11
|
-
end
|
12
|
-
|
13
|
-
context "#send" do
|
14
|
-
before(:each) do
|
15
|
-
Socky.stub!(:send_data)
|
16
|
-
end
|
17
|
-
it "should send broadcast with data" do
|
18
|
-
Socky.should_receive(:send_data).with({:command => :broadcast, :body => "test"})
|
19
|
-
Socky.send("test")
|
20
|
-
end
|
21
|
-
context "should normalize options" do
|
22
|
-
it "when nil given" do
|
23
|
-
Socky.should_receive(:send_data).with({:command => :broadcast, :body => ""})
|
24
|
-
Socky.send(nil)
|
25
|
-
end
|
26
|
-
it "when string given" do
|
27
|
-
Socky.should_receive(:send_data).with({:command => :broadcast, :body => "test"})
|
28
|
-
Socky.send("test")
|
29
|
-
end
|
30
|
-
it "when hash given" do
|
31
|
-
Socky.should_receive(:send_data).with({:command => :broadcast, :body => "test"})
|
32
|
-
Socky.send({:data => "test"})
|
33
|
-
end
|
34
|
-
it "when hash without body given" do
|
35
|
-
Socky.should_receive(:send_data).with({:command => :broadcast, :body => ""})
|
36
|
-
Socky.send({})
|
37
|
-
end
|
38
|
-
end
|
39
|
-
context "should handle recipient conditions for" do
|
40
|
-
# New syntax
|
41
|
-
it ":client" do
|
42
|
-
Socky.should_receive(:send_data).with({:command => :broadcast, :body => "test", :to => { :clients => "first" }})
|
43
|
-
Socky.send("test", :client => "first")
|
44
|
-
end
|
45
|
-
it ":clients" do
|
46
|
-
Socky.should_receive(:send_data).with({:command => :broadcast, :body => "test", :to => { :clients => ["first","second"] }})
|
47
|
-
Socky.send("test", :clients => ["first","second"])
|
48
|
-
end
|
49
|
-
it ":channel" do
|
50
|
-
Socky.should_receive(:send_data).with({:command => :broadcast, :body => "test", :to => { :channels => "first" }})
|
51
|
-
Socky.send("test", :channel => "first")
|
52
|
-
end
|
53
|
-
it ":channels" do
|
54
|
-
Socky.should_receive(:send_data).with({:command => :broadcast, :body => "test", :to => { :channels => ["first","second"] }})
|
55
|
-
Socky.send("test", :channels => ["first","second"])
|
56
|
-
end
|
57
|
-
it "combination" do
|
58
|
-
Socky.should_receive(:send_data).with({
|
59
|
-
:command => :broadcast,
|
60
|
-
:body => "test",
|
61
|
-
:to => {
|
62
|
-
:clients => "allowed_user",
|
63
|
-
:channels => "allowed_channel"
|
64
|
-
}
|
65
|
-
})
|
66
|
-
Socky.send("test", :clients => "allowed_user", :channels => "allowed_channel")
|
67
|
-
end
|
68
|
-
# Old syntax
|
69
|
-
it ":to => :client" do
|
70
|
-
Socky.should_receive(:send_data).with({:command => :broadcast, :body => "test", :to => { :clients => "first" }})
|
71
|
-
Socky.send("test", :to => { :client => "first" })
|
72
|
-
end
|
73
|
-
it ":to => :clients" do
|
74
|
-
Socky.should_receive(:send_data).with({:command => :broadcast, :body => "test", :to => { :clients => ["first","second"] }})
|
75
|
-
Socky.send("test", :to => { :clients => ["first","second"] })
|
76
|
-
end
|
77
|
-
it ":to => :channel" do
|
78
|
-
Socky.should_receive(:send_data).with({:command => :broadcast, :body => "test", :to => { :channels => "first" }})
|
79
|
-
Socky.send("test", :to => { :channel => "first" })
|
80
|
-
end
|
81
|
-
it ":to => :channels" do
|
82
|
-
Socky.should_receive(:send_data).with({:command => :broadcast, :body => "test", :to => { :channels => ["first","second"] }})
|
83
|
-
Socky.send("test", :to => { :channels => ["first","second"] })
|
84
|
-
end
|
85
|
-
it ":except => :client" do
|
86
|
-
Socky.should_receive(:send_data).with({:command => :broadcast, :body => "test", :except => { :clients => "first" }})
|
87
|
-
Socky.send("test", :except => { :client => "first" })
|
88
|
-
end
|
89
|
-
it ":except => :clients" do
|
90
|
-
Socky.should_receive(:send_data).with({:command => :broadcast, :body => "test", :except => { :clients => ["first","second"] }})
|
91
|
-
Socky.send("test", :except => { :clients => ["first","second"] })
|
92
|
-
end
|
93
|
-
it ":except => :channel" do
|
94
|
-
Socky.should_receive(:send_data).with({:command => :broadcast, :body => "test", :except => { :channels => "first" }})
|
95
|
-
Socky.send("test", :except => { :channel => "first" })
|
96
|
-
end
|
97
|
-
it ":except => :channels" do
|
98
|
-
Socky.should_receive(:send_data).with({:command => :broadcast, :body => "test", :except => { :channels => ["first","second"] }})
|
99
|
-
Socky.send("test", :except => { :channels => ["first","second"] })
|
100
|
-
end
|
101
|
-
it "combination" do
|
102
|
-
Socky.should_receive(:send_data).with({
|
103
|
-
:command => :broadcast,
|
104
|
-
:body => "test",
|
105
|
-
:to => {
|
106
|
-
:clients => "allowed_user",
|
107
|
-
:channels => "allowed_channel"
|
108
|
-
},
|
109
|
-
:except => {
|
110
|
-
:clients => "disallowed_user",
|
111
|
-
:channels => "disallowed_channel"
|
112
|
-
}
|
113
|
-
})
|
114
|
-
Socky.send("test", :to => {
|
115
|
-
:clients => "allowed_user",
|
116
|
-
:channels => "allowed_channel"
|
117
|
-
},
|
118
|
-
:except => {
|
119
|
-
:clients => "disallowed_user",
|
120
|
-
:channels => "disallowed_channel"
|
121
|
-
})
|
122
|
-
end
|
123
|
-
end
|
124
|
-
context "should ignore nil value for" do
|
125
|
-
# New syntax
|
126
|
-
it ":clients" do
|
127
|
-
Socky.should_receive(:send_data).with({:command => :broadcast, :body => "test"})
|
128
|
-
Socky.send("test", :clients => nil)
|
129
|
-
end
|
130
|
-
it ":channels" do
|
131
|
-
Socky.should_receive(:send_data).with({:command => :broadcast, :body => "test"})
|
132
|
-
Socky.send("test", :channels => nil)
|
133
|
-
end
|
134
|
-
|
135
|
-
# Old syntax
|
136
|
-
it ":to => :clients" do
|
137
|
-
Socky.should_receive(:send_data).with({:command => :broadcast, :body => "test"})
|
138
|
-
Socky.send("test", :to => { :clients => nil })
|
139
|
-
end
|
140
|
-
it ":to => :channels" do
|
141
|
-
Socky.should_receive(:send_data).with({:command => :broadcast, :body => "test"})
|
142
|
-
Socky.send("test", :to => { :channels => nil })
|
143
|
-
end
|
144
|
-
it ":except => :clients" do
|
145
|
-
Socky.should_receive(:send_data).with({:command => :broadcast, :body => "test"})
|
146
|
-
Socky.send("test", :except => { :clients => nil })
|
147
|
-
end
|
148
|
-
it ":except => :channels" do
|
149
|
-
Socky.should_receive(:send_data).with({:command => :broadcast, :body => "test"})
|
150
|
-
Socky.send("test", :except => { :channels => nil })
|
151
|
-
end
|
152
|
-
end
|
153
|
-
context "should handle empty array for" do
|
154
|
-
# New syntax
|
155
|
-
it ":clients by not sending message" do
|
156
|
-
Socky.should_not_receive(:send_data)
|
157
|
-
Socky.send("test", :clients => [])
|
158
|
-
end
|
159
|
-
it ":channels by not sending message" do
|
160
|
-
Socky.should_not_receive(:send_data)
|
161
|
-
Socky.send("test", :channels => [])
|
162
|
-
end
|
163
|
-
|
164
|
-
# Old syntax
|
165
|
-
it ":to => :clients by not sending message" do
|
166
|
-
Socky.should_not_receive(:send_data)
|
167
|
-
Socky.send("test", :to => { :clients => [] })
|
168
|
-
end
|
169
|
-
it ":to => :channels by not sending message" do
|
170
|
-
Socky.should_not_receive(:send_data)
|
171
|
-
Socky.send("test", :to => { :channels => [] })
|
172
|
-
end
|
173
|
-
it ":except => :clients by ignoring it" do
|
174
|
-
Socky.should_receive(:send_data).with({:command => :broadcast, :body => "test"})
|
175
|
-
Socky.send("test", :except => { :clients => [] })
|
176
|
-
end
|
177
|
-
it ":except => :channels by ignoring it" do
|
178
|
-
Socky.should_receive(:send_data).with({:command => :broadcast, :body => "test"})
|
179
|
-
Socky.send("test", :except => { :channels => [] })
|
180
|
-
end
|
181
|
-
end
|
182
|
-
end
|
183
|
-
|
184
|
-
context "#show_connections" do
|
185
|
-
before(:each) do
|
186
|
-
Socky.stub!(:send_data)
|
187
|
-
end
|
188
|
-
it "should send query :show_connections" do
|
189
|
-
Socky.should_receive(:send_data).with({:command => :query, :type => :show_connections}, true)
|
190
|
-
Socky.show_connections
|
191
|
-
end
|
192
|
-
end
|
193
|
-
|
194
|
-
end
|