websocket-rails 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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