socky 0.0.7 → 0.0.8
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.textile +25 -0
- data/README.textile +27 -0
- data/Rakefile +19 -0
- data/VERSION +1 -1
- data/lib/em-websocket_hacks.rb +1 -3
- data/lib/socky.rb +17 -15
- data/lib/socky/connection.rb +12 -6
- data/lib/socky/connection/authentication.rb +7 -6
- data/lib/socky/connection/finders.rb +3 -3
- data/lib/socky/message.rb +26 -20
- data/lib/socky/misc.rb +2 -2
- data/lib/socky/net_request.rb +2 -2
- data/lib/socky/options.rb +16 -18
- data/lib/socky/options/config.rb +37 -18
- data/lib/socky/options/parser.rb +50 -40
- data/lib/socky/runner.rb +3 -4
- data/lib/socky/server.rb +1 -1
- data/spec/em-websocket_spec.rb +37 -0
- data/spec/files/default.yml +7 -0
- data/spec/files/invalid.yml +1 -0
- data/spec/socky/connection/authentication_spec.rb +162 -0
- data/spec/socky/connection/finders_spec.rb +96 -0
- data/spec/socky/connection_spec.rb +143 -0
- data/spec/socky/message_spec.rb +260 -0
- data/spec/socky/misc_spec.rb +74 -0
- data/spec/socky/net_request_spec.rb +43 -0
- data/spec/socky/options/config_spec.rb +67 -0
- data/spec/socky/options/parser_spec.rb +59 -0
- data/spec/socky/options_spec.rb +65 -0
- data/spec/socky/runner_spec.rb +73 -0
- data/spec/socky/server_spec.rb +35 -0
- data/spec/socky_spec.rb +82 -0
- data/spec/spec_helper.rb +6 -0
- data/spec/stallion.rb +96 -0
- metadata +39 -7
- data/README +0 -10
data/CHANGELOG.textile
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
h1. Changelog
|
2
|
+
|
3
|
+
h2. 0.0.8 / 2010-06-06
|
4
|
+
|
5
|
+
* new features:
|
6
|
+
** full rspec suite
|
7
|
+
** allow admin param to be both "1" and "true"
|
8
|
+
* bugfixes:
|
9
|
+
** proper configuration options parsing order
|
10
|
+
** rescue from invalid config file
|
11
|
+
** rescue from invalid args
|
12
|
+
** symbolize_keys will no longer raise error if argument is not hash
|
13
|
+
** modules will no longer depend on parent class
|
14
|
+
** message will no longer raise exeption is JSON is invalid
|
15
|
+
|
16
|
+
h2. 0.0.7 / 2010-05-25
|
17
|
+
|
18
|
+
* new features:
|
19
|
+
** user authentication is now async
|
20
|
+
* bugfixes:
|
21
|
+
** server will no longer fail when starting from source instead of gem
|
22
|
+
|
23
|
+
h2. 0.0.6 / 2010-05-24
|
24
|
+
|
25
|
+
* initial release
|
data/README.textile
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
h1. Socky
|
2
|
+
|
3
|
+
Socky is push server for Ruby on Rails based on WebSockets. It allows you to breake border between your application and client browser by letting the server initialize a connection and push data to the client.
|
4
|
+
|
5
|
+
h2. Getting Started
|
6
|
+
|
7
|
+
* "Install":http://wiki.github.com/imanel/socky_gem/install the gem
|
8
|
+
* Read up about its "Usage":http://wiki.github.com/imanel/socky_gem/usage and "Configuration":http://wiki.github.com/imanel/socky_gem/configuration
|
9
|
+
* Try "Example Application":http://github.com/imanel/socky_example
|
10
|
+
* Fork and Contribute your own modifications
|
11
|
+
|
12
|
+
h2. Runtime Dependencies
|
13
|
+
|
14
|
+
* EM-WebSocket: Backend for WebSocket server
|
15
|
+
* EM-HTTP-Client: Sending authorize requests
|
16
|
+
|
17
|
+
h2. License
|
18
|
+
|
19
|
+
(The MIT License)
|
20
|
+
|
21
|
+
Copyright (c) 2010 Bernard Potocki
|
22
|
+
|
23
|
+
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:
|
24
|
+
|
25
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
26
|
+
|
27
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'spec/rake/spectask'
|
3
|
+
|
4
|
+
task :default => :spec
|
5
|
+
|
6
|
+
Spec::Rake::SpecTask.new do |t|
|
7
|
+
t.ruby_opts = ['-rtest/unit']
|
8
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
9
|
+
end
|
10
|
+
|
1
11
|
begin
|
2
12
|
require 'jeweler'
|
3
13
|
Jeweler::Tasks.new do |gemspec|
|
@@ -13,4 +23,13 @@ begin
|
|
13
23
|
end
|
14
24
|
rescue LoadError
|
15
25
|
puts "Jeweler not available. Install it with: gem install jeweler"
|
26
|
+
end
|
27
|
+
|
28
|
+
begin
|
29
|
+
require 'metric_fu'
|
30
|
+
MetricFu::Configuration.run do |config|
|
31
|
+
# config.graph_engine = :gchart
|
32
|
+
end
|
33
|
+
rescue LoadError
|
34
|
+
puts "MetricFu not available. Install it with: gem install metric_fu"
|
16
35
|
end
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.8
|
data/lib/em-websocket_hacks.rb
CHANGED
@@ -2,9 +2,7 @@ EventMachine::WebSocket::Connection.class_eval do
|
|
2
2
|
|
3
3
|
def debug(*data)
|
4
4
|
if @debug
|
5
|
-
|
6
|
-
Socky.logger.debug "Socket " + d.collect{|dd| dd.to_s.gsub("\r\n","\n").gsub("\n","\\n")}.join(" ")
|
7
|
-
end
|
5
|
+
Socky.logger.debug "Socket " + data.flatten.collect{|line| line.to_s.gsub("\r\n","\n").gsub("\n","\\n")}.join(" ")
|
8
6
|
end
|
9
7
|
end
|
10
8
|
|
data/lib/socky.rb
CHANGED
@@ -7,36 +7,30 @@ require 'em-websocket_hacks'
|
|
7
7
|
|
8
8
|
module Socky
|
9
9
|
|
10
|
-
class SockyError < StandardError #:nodoc:
|
11
|
-
end
|
10
|
+
class SockyError < StandardError; end #:nodoc:
|
12
11
|
|
13
12
|
VERSION = File.read(File.dirname(__FILE__) + '/../VERSION').strip
|
14
13
|
|
15
|
-
@@options = {}
|
16
|
-
|
17
14
|
class << self
|
18
15
|
def options
|
19
|
-
|
16
|
+
@options ||= {}
|
20
17
|
end
|
21
18
|
|
22
19
|
def options=(val)
|
23
|
-
|
20
|
+
@options = val
|
24
21
|
end
|
25
22
|
|
26
23
|
def logger
|
27
|
-
return
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
@@logger
|
24
|
+
return @logger if defined?(@logger) && !@logger.nil?
|
25
|
+
path = log_path
|
26
|
+
FileUtils.mkdir_p(File.dirname(path))
|
27
|
+
prepare_logger(path)
|
32
28
|
rescue
|
33
|
-
|
34
|
-
@@logger.level = Logger::INFO unless options[:debug]
|
35
|
-
@@logger
|
29
|
+
prepare_logger(STDOUT)
|
36
30
|
end
|
37
31
|
|
38
32
|
def logger=(logger)
|
39
|
-
|
33
|
+
@logger = logger
|
40
34
|
end
|
41
35
|
|
42
36
|
def log_path
|
@@ -46,6 +40,14 @@ module Socky
|
|
46
40
|
def config_path
|
47
41
|
options[:config_path] || File.join(%w( / var run socky.yml ))
|
48
42
|
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def prepare_logger(output)
|
47
|
+
@logger = Logger.new(output)
|
48
|
+
@logger.level = Logger::INFO unless options[:debug]
|
49
|
+
@logger
|
50
|
+
end
|
49
51
|
end
|
50
52
|
end
|
51
53
|
|
data/lib/socky/connection.rb
CHANGED
@@ -7,13 +7,11 @@ module Socky
|
|
7
7
|
include Socky::Connection::Authentication
|
8
8
|
include Socky::Connection::Finders
|
9
9
|
|
10
|
-
@@connections = []
|
11
|
-
|
12
10
|
attr_reader :socket
|
13
11
|
|
14
12
|
class << self
|
15
13
|
def connections
|
16
|
-
|
14
|
+
@connections ||= []
|
17
15
|
end
|
18
16
|
end
|
19
17
|
|
@@ -22,7 +20,7 @@ module Socky
|
|
22
20
|
end
|
23
21
|
|
24
22
|
def admin
|
25
|
-
socket.request["Query"]["admin"]
|
23
|
+
["true","1"].include?(socket.request["Query"]["admin"])
|
26
24
|
end
|
27
25
|
|
28
26
|
def client
|
@@ -64,12 +62,20 @@ module Socky
|
|
64
62
|
socket.close_connection_after_writing
|
65
63
|
end
|
66
64
|
|
65
|
+
def connection_pool
|
66
|
+
self.class.connections
|
67
|
+
end
|
68
|
+
|
69
|
+
def in_connection_pool?
|
70
|
+
connection_pool.include?(self)
|
71
|
+
end
|
72
|
+
|
67
73
|
def add_to_pool
|
68
|
-
|
74
|
+
connection_pool << self unless self.admin || in_connection_pool?
|
69
75
|
end
|
70
76
|
|
71
77
|
def remove_from_pool
|
72
|
-
|
78
|
+
connection_pool.delete(self)
|
73
79
|
end
|
74
80
|
|
75
81
|
def to_json
|
@@ -1,6 +1,7 @@
|
|
1
1
|
module Socky
|
2
2
|
class Connection
|
3
3
|
module Authentication
|
4
|
+
include Socky::Misc
|
4
5
|
|
5
6
|
def subscribe_request
|
6
7
|
send_subscribe_request do |response|
|
@@ -27,7 +28,7 @@ module Socky
|
|
27
28
|
end
|
28
29
|
|
29
30
|
def authenticate_as_admin
|
30
|
-
|
31
|
+
admin
|
31
32
|
end
|
32
33
|
|
33
34
|
def authenticate_as_user
|
@@ -59,11 +60,11 @@ module Socky
|
|
59
60
|
end
|
60
61
|
|
61
62
|
def params_for_request
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
63
|
+
{
|
64
|
+
:client_id => client,
|
65
|
+
:client_secret => secret,
|
66
|
+
:channels => channels
|
67
|
+
}.reject{|key,value| value.nil? || value.empty?}
|
67
68
|
end
|
68
69
|
|
69
70
|
end
|
@@ -8,7 +8,7 @@ module Socky
|
|
8
8
|
|
9
9
|
module ClassMethods
|
10
10
|
def find_all
|
11
|
-
connections
|
11
|
+
Socky::Connection.connections
|
12
12
|
end
|
13
13
|
|
14
14
|
def find(opts = {})
|
@@ -36,11 +36,11 @@ module Socky
|
|
36
36
|
private
|
37
37
|
|
38
38
|
def filter_by_clients(connections, clients)
|
39
|
-
connections.collect{ |
|
39
|
+
connections.collect{ |connection| connection if clients.include? connection.client }.compact
|
40
40
|
end
|
41
41
|
|
42
42
|
def filter_by_channels(connections, channels)
|
43
|
-
connections.collect{ |
|
43
|
+
connections.collect{ |connection| connection if channels.any?{ |channel| connection.channels.include?(channel) } }.compact
|
44
44
|
end
|
45
45
|
|
46
46
|
end
|
data/lib/socky/message.rb
CHANGED
@@ -4,29 +4,24 @@ module Socky
|
|
4
4
|
class Message
|
5
5
|
include Socky::Misc
|
6
6
|
|
7
|
-
class
|
8
|
-
end
|
9
|
-
|
10
|
-
class InvalidBroadcast < Socky::SockyError #:nodoc:
|
11
|
-
end
|
12
|
-
|
13
|
-
class InvalidQuery < Socky::SockyError #:nodoc:
|
14
|
-
end
|
7
|
+
class InvalidJSON < Socky::SockyError; end #:nodoc:
|
8
|
+
class UnauthorisedQuery < Socky::SockyError; end #:nodoc:
|
9
|
+
class InvalidQuery < Socky::SockyError; end #:nodoc:
|
15
10
|
|
16
11
|
attr_reader :params, :creator
|
17
12
|
|
18
13
|
class << self
|
19
14
|
def process(connection, message)
|
20
|
-
|
21
|
-
|
22
|
-
rescue SockyError =>
|
23
|
-
error connection.name,
|
24
|
-
|
15
|
+
message = new(connection, message)
|
16
|
+
message.process
|
17
|
+
rescue SockyError => error
|
18
|
+
error connection.name, error
|
19
|
+
connection.send_message(error.message.to_json)
|
25
20
|
end
|
26
21
|
end
|
27
22
|
|
28
23
|
def initialize(creator, message)
|
29
|
-
@params = symbolize_keys
|
24
|
+
@params = symbolize_keys(JSON.parse(message)) rescue raise(InvalidJSON, "invalid request")
|
30
25
|
@creator = creator
|
31
26
|
end
|
32
27
|
|
@@ -35,23 +30,32 @@ module Socky
|
|
35
30
|
|
36
31
|
verify_secret!
|
37
32
|
|
38
|
-
|
39
|
-
when :broadcast then broadcast
|
40
|
-
when :query then query
|
41
|
-
end
|
33
|
+
execute
|
42
34
|
end
|
43
35
|
|
44
36
|
def verify_secret!
|
45
37
|
raise(UnauthorisedQuery, "invalid secret") unless options[:secret].nil? || options[:secret] == params[:secret]
|
46
38
|
end
|
47
39
|
|
40
|
+
def execute
|
41
|
+
case params[:command].to_sym
|
42
|
+
when :broadcast then broadcast
|
43
|
+
when :query then query
|
44
|
+
else raise
|
45
|
+
end
|
46
|
+
rescue
|
47
|
+
raise(InvalidQuery, "unknown command")
|
48
|
+
end
|
49
|
+
|
48
50
|
def broadcast
|
49
51
|
case params[:type].to_sym
|
50
52
|
when :to_clients then broadcast_to_clients
|
51
53
|
when :to_channels then broadcast_to_channels
|
52
54
|
when :to_clients_on_channels then broadcast_to_clients_on_channels
|
53
|
-
else raise
|
55
|
+
else raise
|
54
56
|
end
|
57
|
+
rescue
|
58
|
+
raise(InvalidQuery, "unknown broadcast type")
|
55
59
|
end
|
56
60
|
|
57
61
|
def broadcast_to_clients
|
@@ -73,8 +77,10 @@ module Socky
|
|
73
77
|
def query
|
74
78
|
case params[:type].to_sym
|
75
79
|
when :show_connections then query_show_connections
|
76
|
-
else raise
|
80
|
+
else raise
|
77
81
|
end
|
82
|
+
rescue
|
83
|
+
raise(InvalidQuery, "unknown query type")
|
78
84
|
end
|
79
85
|
|
80
86
|
def query_show_connections
|
data/lib/socky/misc.rb
CHANGED
@@ -38,9 +38,9 @@ module Socky
|
|
38
38
|
end
|
39
39
|
|
40
40
|
def symbolize_keys(hash)
|
41
|
-
return
|
41
|
+
return hash unless hash.is_a?(Hash)
|
42
42
|
hash.inject({}) do |options, (key, value)|
|
43
|
-
options[(key.to_sym
|
43
|
+
options[(key.to_sym if key.respond_to?(:to_sym)) || key] = value
|
44
44
|
options
|
45
45
|
end
|
46
46
|
end
|
data/lib/socky/net_request.rb
CHANGED
data/lib/socky/options.rb
CHANGED
@@ -1,28 +1,26 @@
|
|
1
|
-
require 'optparse'
|
2
|
-
require 'yaml'
|
3
|
-
require 'erb'
|
4
1
|
require 'socky/options/config'
|
5
2
|
require 'socky/options/parser'
|
6
3
|
|
7
4
|
module Socky
|
8
|
-
|
9
|
-
include Socky::
|
10
|
-
include Socky::Options::Parser
|
5
|
+
class Options
|
6
|
+
include Socky::Misc
|
11
7
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
8
|
+
class << self
|
9
|
+
def prepare(argv)
|
10
|
+
self.options = {
|
11
|
+
:config_path => config_path,
|
12
|
+
:port => 8080,
|
13
|
+
:debug => false,
|
14
|
+
:deep_debug => false,
|
15
|
+
:log_path => log_path
|
16
|
+
}
|
16
17
|
|
17
|
-
|
18
|
-
|
18
|
+
parsed_options = Parser.parse(argv)
|
19
|
+
config_options = Config.read(parsed_options[:config_path] || config_path)
|
19
20
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
:deep_debug => false,
|
24
|
-
:log_path => log_path
|
25
|
-
}.merge(self.options)
|
21
|
+
self.options.merge!(config_options)
|
22
|
+
self.options.merge!(parsed_options)
|
23
|
+
end
|
26
24
|
end
|
27
25
|
|
28
26
|
end
|
data/lib/socky/options/config.rb
CHANGED
@@ -1,30 +1,48 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'erb'
|
3
|
+
|
1
4
|
module Socky
|
2
|
-
|
3
|
-
|
5
|
+
class Options
|
6
|
+
class Config
|
4
7
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
8
|
+
class NoConfigFile < Socky::SockyError #:nodoc:
|
9
|
+
end
|
10
|
+
|
11
|
+
class InvalidConfig < Socky::SockyError #:nodoc:
|
12
|
+
end
|
13
|
+
|
14
|
+
class AlreadyExists < Socky::SockyError #:nodoc:
|
15
|
+
end
|
16
|
+
|
17
|
+
class ConfigUnavailable < Socky::SockyError #:nodoc:
|
18
|
+
end
|
10
19
|
|
11
|
-
|
20
|
+
class SuccessfullyCreated < Socky::SockyError #:nodoc:
|
12
21
|
end
|
13
22
|
|
14
|
-
|
15
|
-
|
16
|
-
|
23
|
+
class << self
|
24
|
+
def read(path)
|
25
|
+
raise(NoConfigFile, "You must generate a config file (socky -g filename.yml)") unless File.exists?(path)
|
26
|
+
result = YAML::load(ERB.new(IO.read(path)).result)
|
27
|
+
raise(InvalidConfig, "Provided config file is invalid.") unless result.is_a?(Hash)
|
28
|
+
result
|
29
|
+
rescue SockyError => error
|
30
|
+
puts error.message
|
17
31
|
exit
|
18
32
|
end
|
19
|
-
|
20
|
-
|
21
|
-
file.
|
33
|
+
|
34
|
+
def generate(path)
|
35
|
+
raise(AlreadyExists, "Config file already exists. You must remove it before generating a new one.") if File.exists?(path)
|
36
|
+
File.open(path, 'w+') do |file|
|
37
|
+
file.write DEFAULT_CONFIG_FILE
|
38
|
+
end rescue raise(ConfigUnavailable, "Config file is unavailable - please choose another.")
|
39
|
+
raise(SuccessfullyCreated, "Config file generated at #{path}")
|
40
|
+
rescue SockyError => error
|
41
|
+
puts error.message
|
42
|
+
exit
|
22
43
|
end
|
23
|
-
puts "Config file generated at #{config_path}"
|
24
|
-
exit
|
25
|
-
end
|
26
44
|
|
27
|
-
|
45
|
+
DEFAULT_CONFIG_FILE= <<-EOF
|
28
46
|
:port: 8080
|
29
47
|
:debug: false
|
30
48
|
|
@@ -35,6 +53,7 @@ module Socky
|
|
35
53
|
|
36
54
|
# :timeout: 3
|
37
55
|
EOF
|
56
|
+
end
|
38
57
|
|
39
58
|
end
|
40
59
|
end
|