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 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.7
1
+ 0.0.8
@@ -2,9 +2,7 @@ EventMachine::WebSocket::Connection.class_eval do
2
2
 
3
3
  def debug(*data)
4
4
  if @debug
5
- data.each do |d|
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
- @@options
16
+ @options ||= {}
20
17
  end
21
18
 
22
19
  def options=(val)
23
- @@options = val
20
+ @options = val
24
21
  end
25
22
 
26
23
  def logger
27
- return @@logger if defined?(@@logger) && !@@logger.nil?
28
- FileUtils.mkdir_p(File.dirname(log_path))
29
- @@logger = Logger.new(log_path)
30
- @@logger.level = Logger::INFO unless options[:debug]
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
- @@logger = Logger.new(STDOUT)
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
- @@logger = logger
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
 
@@ -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
- @@connections
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"] == "1"
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
- @@connections << self unless self.admin || @@connections.include?(self)
74
+ connection_pool << self unless self.admin || in_connection_pool?
69
75
  end
70
76
 
71
77
  def remove_from_pool
72
- @@connections.delete(self)
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
- true
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
- params = {}
63
- params.merge!(:client_id => self.client) unless self.client.nil?
64
- params.merge!(:client_secret => self.secret) unless self.secret.nil?
65
- params.merge!(:channels => self.channels) unless self.channels.empty?
66
- params
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{ |con| con if clients.include? con.client }.compact
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{ |con| con if channels.any?{ |chan| con.channels.include?(chan) } }.compact
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 UnauthorisedQuery < Socky::SockyError #:nodoc:
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
- m = new(connection, message)
21
- m.process
22
- rescue SockyError => e
23
- error connection.name, e
24
- m.respond e.message
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 JSON.parse(message)
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
- case params[:command].to_sym
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 InvalidQuery, "unknown broadcast type"
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 InvalidQuery, "unknown query type"
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 self unless hash.is_a?(Hash)
41
+ return hash unless hash.is_a?(Hash)
42
42
  hash.inject({}) do |options, (key, value)|
43
- options[(key.to_sym rescue key) || key] = value
43
+ options[(key.to_sym if key.respond_to?(:to_sym)) || key] = value
44
44
  options
45
45
  end
46
46
  end
@@ -11,8 +11,8 @@ module Socky
11
11
  http.errback { yield false }
12
12
  http.callback { yield http.response_header.status == 200 }
13
13
  true
14
- rescue => e
15
- error "Bad request", e
14
+ rescue => error
15
+ error "Bad request", error
16
16
  false
17
17
  end
18
18
 
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
- module Options
9
- include Socky::Options::Config
10
- include Socky::Options::Parser
5
+ class Options
6
+ include Socky::Misc
11
7
 
12
- def prepare_options(argv)
13
- self.options = {
14
- :config_path => config_path
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
- parse_options(argv)
18
- read_config_file
18
+ parsed_options = Parser.parse(argv)
19
+ config_options = Config.read(parsed_options[:config_path] || config_path)
19
20
 
20
- self.options = {
21
- :port => 8080,
22
- :debug => false,
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
@@ -1,30 +1,48 @@
1
+ require 'yaml'
2
+ require 'erb'
3
+
1
4
  module Socky
2
- module Options
3
- module Config
5
+ class Options
6
+ class Config
4
7
 
5
- def read_config_file
6
- if !File.exists?(config_path)
7
- puts "You must generate a config file (socky -g filename.yml)"
8
- exit
9
- end
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
- self.options = YAML::load(ERB.new(IO.read(config_path)).result).merge!(self.options)
20
+ class SuccessfullyCreated < Socky::SockyError #:nodoc:
12
21
  end
13
22
 
14
- def generate_config_file
15
- if File.exists?(config_path)
16
- puts "Config file already exists. You must remove it before generating a new one."
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
- puts "Generating config file...."
20
- File.open(config_path, 'w+') do |file|
21
- file.write DEFAULT_CONFIG_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
- DEFAULT_CONFIG_FILE= <<-EOF
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