websocket-rails 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. data/CHANGELOG.md +41 -0
  2. data/README.md +1 -1
  3. data/lib/generators/websocket_rails/install/templates/events.rb +15 -5
  4. data/lib/rails/tasks/websocket_rails.tasks +8 -10
  5. data/lib/spec_helpers/matchers/route_matchers.rb +2 -1
  6. data/lib/spec_helpers/spec_helper_event.rb +4 -0
  7. data/lib/websocket-rails.rb +50 -73
  8. data/lib/websocket_rails/base_controller.rb +9 -7
  9. data/lib/websocket_rails/channel.rb +8 -1
  10. data/lib/websocket_rails/channel_manager.rb +6 -0
  11. data/lib/websocket_rails/configuration.rb +112 -0
  12. data/lib/websocket_rails/connection_adapters.rb +13 -4
  13. data/lib/websocket_rails/connection_manager.rb +3 -2
  14. data/lib/websocket_rails/controller_factory.rb +70 -0
  15. data/lib/websocket_rails/data_store.rb +128 -62
  16. data/lib/websocket_rails/dispatcher.rb +17 -16
  17. data/lib/websocket_rails/event.rb +12 -2
  18. data/lib/websocket_rails/event_map.rb +6 -41
  19. data/lib/websocket_rails/internal_events.rb +0 -7
  20. data/lib/websocket_rails/logging.rb +103 -14
  21. data/lib/websocket_rails/synchronization.rb +6 -8
  22. data/lib/websocket_rails/version.rb +1 -1
  23. data/spec/dummy/app/controllers/chat_controller.rb +6 -14
  24. data/spec/dummy/log/test.log +0 -750
  25. data/spec/integration/connection_manager_spec.rb +8 -1
  26. data/spec/spec_helper.rb +3 -16
  27. data/spec/spec_helpers/matchers/route_matchers_spec.rb +2 -11
  28. data/spec/spec_helpers/matchers/trigger_matchers_spec.rb +2 -12
  29. data/spec/unit/channel_manager_spec.rb +8 -0
  30. data/spec/unit/channel_spec.rb +16 -2
  31. data/spec/unit/connection_adapters_spec.rb +32 -11
  32. data/spec/unit/controller_factory_spec.rb +63 -0
  33. data/spec/unit/data_store_spec.rb +91 -24
  34. data/spec/unit/dispatcher_spec.rb +6 -11
  35. data/spec/unit/event_map_spec.rb +17 -27
  36. data/spec/unit/event_spec.rb +14 -0
  37. data/spec/unit/logging_spec.rb +122 -17
  38. metadata +11 -6
data/CHANGELOG.md CHANGED
@@ -1,19 +1,60 @@
1
1
  # WebsocketRails Change Log
2
2
 
3
+ February 27 2013
4
+
5
+ ## Version 0.4.0
6
+
7
+ __There have been a few breaking changes in the public API since the
8
+ last release. Please review the list below and consult the Wiki for more
9
+ information regarding the usage of the new features.__
10
+
11
+ * Controller instances no longer persist between events that are
12
+ triggered. Each event is processed by a new controller instance,
13
+ similar to a standard Rails request. Since you can no longer use
14
+ instance variables to temporarily persist data between events, there is
15
+ a new Controller Data Store that can be used for this purpose. This
16
+ change addresses issue #31.
17
+
18
+ * The original DataStore class has been deprecated. In it's place are
19
+ the new Controller Data Store and Connection Data Store. As mentioned
20
+ above, the Controller Data Store can be used to persist data between
21
+ events in much the same way that you would use instance variables. The
22
+ Connection Data Store acts like the Rails session store. Use it to store
23
+ data private to a connection. Data in the Connection Data Store can be
24
+ accessed from any controller. Check out the Wiki for more information on
25
+ both.
26
+
27
+ * The `websocket_rails.reload_controllers` event has been deprecated.
28
+ The new Controller instantiation model allows for automatic controller
29
+ class reloading while in the development environment. You no longer
30
+ need to trigger an event to pick up code changes in controllers while
31
+ connections are active.
32
+
33
+ * Real logging support has _finally_ been implemented. Check out the
34
+ configuration WIki for more information on the various logging options
35
+ available.
36
+
3
37
  ## Version 0.3.0
4
38
 
39
+ February 6 2013
40
+
5
41
  * Extend the event router DSL to accept routes similar to the routes.rb
6
42
  shorthand `controller#action`. - Thanks to @nessche.
43
+
7
44
  * Add a custom RSpec matcher suite for verifying event routes
8
45
  and easily asserting that WebsocketRails controller actions are
9
46
  triggering events correctly. - Also thanks to @nessche.
47
+
10
48
  * Fix fiber yielded across threads bug when running in standalone mode
11
49
  by disabling Thin threaded mode as default option.
12
50
 
13
51
  ## Version 0.2.1
14
52
 
53
+ January 29 2013
54
+
15
55
  * Fix default redis driver issue that was causing problems when using
16
56
  redis while event machine was not running.
57
+
17
58
  * Fix undefined data store value issue. Thanks to @burninggramma.
18
59
 
19
60
  ## Version 0.2.0
data/README.md CHANGED
@@ -203,7 +203,7 @@ Read the [Private Channel Wiki](https://github.com/DanKnox/websocket-rails/wiki/
203
203
  private channels from the JavaScript client and handling the
204
204
  authorization in your controller.
205
205
 
206
- ## Credit where credit is due.
206
+ ## Credit where credit is due
207
207
 
208
208
  Big thanks to our
209
209
  [contributors](https://github.com/DanKnox/websocket-rails/graphs/contributors)
@@ -1,21 +1,31 @@
1
1
  WebsocketRails.setup do |config|
2
2
 
3
- # Change to :debug for debugging output
4
- config.log_level = :default
3
+ # Uncomment to override the default log level. The log level can be
4
+ # any of the standard Logger log levels. By default it will mirror the
5
+ # current Rails environment log level.
6
+ # config.log_level = :debug
7
+
8
+ # Uncomment to change the default log file path.
9
+ # config.log_path = "#{Rails.root}/log/websocket_rails.log"
10
+
11
+ # Set to true if you wish to log the internal websocket_rails events
12
+ # such as the keepalive `websocket_rails.ping` event.
13
+ # config.log_internal_events = false
5
14
 
6
15
  # Change to true to enable standalone server mode
7
16
  # Start the standalone server with rake websocket_rails:start_server
8
- # Requires Redis
17
+ # * Requires Redis
9
18
  config.standalone = false
10
19
 
11
20
  # Change to true to enable channel synchronization between
12
- # multiple server instances. Requires Redis.
21
+ # multiple server instances.
22
+ # * Requires Redis.
13
23
  config.synchronize = false
14
24
 
15
25
  # Uncomment and edit to point to a different redis instance.
16
26
  # Will not be used unless standalone or synchronization mode
17
27
  # is enabled.
18
- #config.redis_options = {:host => 'localhost', :port => '6379'}
28
+ # config.redis_options = {:host => 'localhost', :port => '6379'}
19
29
  end
20
30
 
21
31
  WebsocketRails::EventMap.describe do
@@ -1,14 +1,12 @@
1
1
  namespace :websocket_rails do
2
- desc 'Test rails task'
2
+ desc 'Start the WebsocketRails standalone server.'
3
3
  task :start_server do
4
4
  require "thin"
5
5
  load "#{Rails.root}/config/initializers/events.rb"
6
6
 
7
- options = WebsocketRails.thin_options
7
+ options = WebsocketRails.config.thin_options
8
8
 
9
- unless WebsocketRails.standalone?
10
- warn_standalone_not_enabled!
11
- end
9
+ warn_if_standalone_not_enabled!
12
10
 
13
11
  fork do
14
12
  Thin::Controllers::Controller.new(options).start
@@ -17,21 +15,21 @@ namespace :websocket_rails do
17
15
  puts "Websocket Rails Standalone Server listening on port #{options[:port]}"
18
16
  end
19
17
 
18
+ desc 'Stop the WebsocketRails standalone server.'
20
19
  task :stop_server do
21
20
  require "thin"
22
21
  load "#{Rails.root}/config/initializers/events.rb"
23
22
 
24
- options = WebsocketRails.thin_options
23
+ options = WebsocketRails.config.thin_options
25
24
 
26
- unless WebsocketRails.standalone?
27
- warn_standalone_not_enabled!
28
- end
25
+ warn_if_standalone_not_enabled!
29
26
 
30
27
  Thin::Controllers::Controller.new(options).stop
31
28
  end
32
29
  end
33
30
 
34
- def warn_standalone_not_enabled!
31
+ def warn_if_standalone_not_enabled!
32
+ return if WebsocketRails.standalone?
35
33
  puts "Fail!"
36
34
  puts "You must enable standalone mode in your events.rb initializer to use the standalone server."
37
35
  exit 1
@@ -9,8 +9,9 @@ module WebsocketRails
9
9
 
10
10
  result = false
11
11
  no_of_routes = 0
12
- event.dispatcher.event_map.routes_for event do |controller, method|
12
+ event.dispatcher.event_map.routes_for event do |controller_class, method|
13
13
  no_of_routes += 1
14
+ controller = controller_class.new
14
15
  if controller.class == target_class and method == target_method
15
16
  result = true
16
17
  end
@@ -21,6 +21,10 @@ module WebsocketRails
21
21
  self
22
22
  end
23
23
 
24
+ def connection
25
+ OpenStruct.new(:id => 1)
26
+ end
27
+
24
28
  end
25
29
 
26
30
  end
@@ -1,92 +1,41 @@
1
1
  require "active_support/dependencies"
2
- require 'thin'
2
+ require "logger"
3
+ require "thin"
3
4
 
4
5
  module WebsocketRails
5
- mattr_accessor :app_root
6
6
 
7
- def self.setup
8
- yield self
9
- end
10
-
11
- def self.route_block=(routes)
12
- @event_routes = routes
13
- end
14
-
15
- def self.route_block
16
- @event_routes
17
- end
7
+ class << self
8
+ def setup
9
+ yield config
10
+ end
18
11
 
19
- def self.log_level
20
- @log_level ||= :warn
21
- end
12
+ def config
13
+ @config ||= Configuration.new
14
+ end
22
15
 
23
- def self.log_level=(level)
24
- @log_level = level
25
- end
16
+ def synchronize?
17
+ config.synchronize == true || config.standalone == true
18
+ end
26
19
 
27
- attr_accessor :synchronize
28
- module_function :synchronize, :synchronize=
20
+ def standalone?
21
+ config.standalone == true
22
+ end
29
23
 
30
- def self.synchronize?
31
- (@synchronize == true) || (@standalone == true)
24
+ def logger
25
+ config.logger
26
+ end
32
27
  end
33
28
 
34
- def self.redis_options
35
- @redis_options ||= redis_defaults
36
- end
37
-
38
- def self.redis_options=(options = {})
39
- @redis_options = redis_defaults.merge(options)
40
- end
41
-
42
- def self.redis_defaults
43
- {:host => '127.0.0.1', :port => 6379, :driver => :synchrony}
44
- end
45
-
46
- attr_accessor :standalone
47
- module_function :standalone, :standalone=
48
-
49
- def self.standalone?
50
- @standalone == true
51
- end
52
-
53
- def self.standalone_port
54
- @standalone_port ||= '3001'
55
- end
56
-
57
- def self.standalone_port=(port)
58
- @standalone_port = port
59
- end
60
-
61
- def self.thin_options
62
- @thin_options ||= thin_defaults
63
- end
64
-
65
- def self.thin_options=(options = {})
66
- @thin_options = thin_defaults.merge(options)
67
- end
68
-
69
- def self.thin_defaults
70
- {
71
- :port => standalone_port,
72
- :pid => "#{Rails.root}/tmp/pids/websocket_rails.pid",
73
- :log => "#{Rails.root}/log/websocket_rails.log",
74
- :tag => 'websocket_rails',
75
- :rackup => "#{Rails.root}/config.ru",
76
- :threaded => false,
77
- :daemonize => true,
78
- :dirname => Rails.root,
79
- :max_persistent_conns => 1024,
80
- :max_conns => 1024
81
- }
82
- end
83
29
  end
84
30
 
85
31
  require 'websocket_rails/engine'
32
+
33
+ require 'websocket_rails/configuration'
86
34
  require 'websocket_rails/logging'
87
35
  require 'websocket_rails/synchronization'
88
36
  require 'websocket_rails/connection_manager'
89
37
  require 'websocket_rails/dispatcher'
38
+ require 'websocket_rails/controller_factory'
90
39
  require 'websocket_rails/event'
91
40
  require 'websocket_rails/event_map'
92
41
  require 'websocket_rails/event_queue'
@@ -99,13 +48,41 @@ require 'websocket_rails/connection_adapters'
99
48
  require 'websocket_rails/connection_adapters/http'
100
49
  require 'websocket_rails/connection_adapters/web_socket'
101
50
 
51
+
102
52
  # Exceptions
103
- class InvalidConnectionError < StandardError
53
+ class WebsocketRails::InvalidConnectionError < StandardError
104
54
  def rack_response
105
55
  [400,{'Content-Type' => 'text/plain'},['invalid connection']]
106
56
  end
107
57
  end
108
58
 
59
+ class WebsocketRails::EventRoutingError < StandardError
60
+
61
+ attr_reader :event, :controller, :method
62
+
63
+ def initialize(event, controller, method)
64
+ @event = event
65
+ @controller = controller
66
+ end
67
+
68
+ def to_s
69
+ out = "Routing Error:\n"
70
+ out << "Event: #{event.name}\n"
71
+ out << "Controller #{controller.class} does not respond to #{method}"
72
+ out
73
+ end
74
+
75
+ def to_json
76
+ super({
77
+ error: "EventRoutingError",
78
+ event: event.name,
79
+ method: method,
80
+ reason: "Controller #{controller.class} does not respond to #{method}"
81
+ })
82
+ end
83
+
84
+ end
85
+
109
86
  # Deprecation Notices
110
87
  class WebsocketRails::Dispatcher
111
88
  def self.describe_events(&block)
@@ -1,4 +1,4 @@
1
- require 'websocket_rails/data_store'
1
+ require "websocket_rails/data_store"
2
2
 
3
3
  module WebsocketRails
4
4
  # Provides controller helper methods for developing a WebsocketRails controller. Action methods
@@ -18,6 +18,8 @@ module WebsocketRails
18
18
  #
19
19
  class BaseController
20
20
 
21
+ # Tell Rails that BaseController and children can be reloaded when in
22
+ # the Development environment.
21
23
  def self.inherited(controller)
22
24
  unloadable controller
23
25
  end
@@ -49,10 +51,6 @@ module WebsocketRails
49
51
  # Stores the observer Procs for the current controller. See {observe} for details.
50
52
  @@observers = Hash.new {|h,k| h[k] = Array.new}
51
53
 
52
- def initialize
53
- @data_store = DataStore.new(self)
54
- end
55
-
56
54
  # Provides direct access to the connection object for the client that
57
55
  # initiated the event that is currently being executed.
58
56
  def connection
@@ -142,8 +140,12 @@ module WebsocketRails
142
140
  # Provides access to the {DataStore} for the current controller. The {DataStore} provides convenience
143
141
  # methods for keeping track of data associated with active connections. See it's documentation for
144
142
  # more information.
145
- def data_store
146
- @data_store
143
+ def controller_store
144
+ @_controller_store
145
+ end
146
+
147
+ def connection_store
148
+ connection.data_store
147
149
  end
148
150
 
149
151
  private
@@ -12,10 +12,16 @@ module WebsocketRails
12
12
  end
13
13
 
14
14
  def subscribe(connection)
15
- log "#{connection} subscribed to channel #{name}"
15
+ info "#{connection} subscribed to channel #{name}"
16
16
  @subscribers << connection
17
17
  end
18
18
 
19
+ def unsubscribe(connection)
20
+ return unless @subscribers.include? connection
21
+ info "#{connection} unsubscribed from channel #{name}"
22
+ @subscribers.delete connection
23
+ end
24
+
19
25
  def trigger(event_name,data={},options={})
20
26
  options.merge! :channel => name
21
27
  options[:data] = data
@@ -26,6 +32,7 @@ module WebsocketRails
26
32
  end
27
33
 
28
34
  def trigger_event(event)
35
+ info "(processing_channel_event) name: #{event.name} namespace: #{event.namespace} connection: #{event.connection.id}"
29
36
  send_data event
30
37
  end
31
38
 
@@ -26,5 +26,11 @@ module WebsocketRails
26
26
  @channels[channel] ||= Channel.new channel
27
27
  end
28
28
 
29
+ def unsubscribe(connection)
30
+ @channels.each do |channel_name, channel|
31
+ channel.unsubscribe(connection)
32
+ end
33
+ end
34
+
29
35
  end
30
36
  end
@@ -0,0 +1,112 @@
1
+ module WebsocketRails
2
+ class Configuration
3
+
4
+ def route_block=(routes)
5
+ @event_routes = routes
6
+ end
7
+
8
+ def route_block
9
+ @event_routes
10
+ end
11
+
12
+ def log_level
13
+ @log_level ||= begin
14
+ case Rails.env.to_sym
15
+ when :production then :info
16
+ when :development then :debug
17
+ end
18
+ end
19
+ end
20
+
21
+ def log_level=(level)
22
+ @log_level = level
23
+ end
24
+
25
+ def logger
26
+ @logger ||= begin
27
+ logger = Logger.new(log_path)
28
+ Logging.configure(logger)
29
+ end
30
+ end
31
+
32
+ def logger=(logger)
33
+ @logger = logger
34
+ end
35
+
36
+ def log_path
37
+ @log_path ||= "#{Rails.root}/log/websocket_rails.log"
38
+ end
39
+
40
+ def log_path=(path)
41
+ @log_path = path
42
+ end
43
+
44
+ def log_internal_events?
45
+ @log_internal_events ||= false
46
+ end
47
+
48
+ def log_internal_events=(value)
49
+ @log_internal_events = value
50
+ end
51
+
52
+ def synchronize
53
+ @synchronize ||= false
54
+ end
55
+
56
+ def synchronize=(synchronize)
57
+ @synchronize = synchronize
58
+ end
59
+
60
+ def redis_options
61
+ @redis_options ||= redis_defaults
62
+ end
63
+
64
+ def redis_options=(options = {})
65
+ @redis_options = redis_defaults.merge(options)
66
+ end
67
+
68
+ def redis_defaults
69
+ {:host => '127.0.0.1', :port => 6379, :driver => :synchrony}
70
+ end
71
+
72
+ def standalone
73
+ @standalone ||= false
74
+ end
75
+
76
+ def standalone=(standalone)
77
+ @standalone = standalone
78
+ end
79
+
80
+ def standalone_port
81
+ @standalone_port ||= '3001'
82
+ end
83
+
84
+ def standalone_port=(port)
85
+ @standalone_port = port
86
+ end
87
+
88
+ def thin_options
89
+ @thin_options ||= thin_defaults
90
+ end
91
+
92
+ def thin_options=(options = {})
93
+ @thin_options = thin_defaults.merge(options)
94
+ end
95
+
96
+ def thin_defaults
97
+ {
98
+ :port => standalone_port,
99
+ :pid => "#{Rails.root}/tmp/pids/websocket_rails.pid",
100
+ :log => "#{Rails.root}/log/websocket_rails_server.log",
101
+ :tag => 'websocket_rails',
102
+ :rackup => "#{Rails.root}/config.ru",
103
+ :threaded => false,
104
+ :daemonize => true,
105
+ :dirname => Rails.root,
106
+ :max_persistent_conns => 1024,
107
+ :max_conns => 1024
108
+ }
109
+ end
110
+
111
+ end
112
+ end