websocket-rails 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # WebsocketRails Change Log
2
2
 
3
+ ## Version 0.3.0
4
+
5
+ * Extend the event router DSL to accept routes similar to the routes.rb
6
+ shorthand `controller#action`. - Thanks to @nessche.
7
+ * Add a custom RSpec matcher suite for verifying event routes
8
+ and easily asserting that WebsocketRails controller actions are
9
+ triggering events correctly. - Also thanks to @nessche.
10
+ * Fix fiber yielded across threads bug when running in standalone mode
11
+ by disabling Thin threaded mode as default option.
12
+
3
13
  ## Version 0.2.1
4
14
 
5
15
  * Fix default redis driver issue that was causing problems when using
data/README.md CHANGED
@@ -46,7 +46,18 @@ Map events to controller actions using an Event Router.
46
46
  ````ruby
47
47
  WebsocketRails::EventMap.describe do
48
48
  namespace :tasks do
49
+
50
+ # using a Hash to specify the target
49
51
  subscribe :create, :to => TaskController, :with_method => :create
52
+
53
+ # using the same syntax as routes.rb
54
+ subscribe :update, 'task#update'
55
+
56
+ # if your controller is not a top-level object
57
+ subscribe :create_admin, :to => Admin::TaskController, :with_method => :create
58
+
59
+ subscribe :update_admin, 'admin/task#update'
60
+
50
61
  end
51
62
  end
52
63
  ````
@@ -192,6 +203,15 @@ Read the [Private Channel Wiki](https://github.com/DanKnox/websocket-rails/wiki/
192
203
  private channels from the JavaScript client and handling the
193
204
  authorization in your controller.
194
205
 
206
+ ## Credit where credit is due.
207
+
208
+ Big thanks to our
209
+ [contributors](https://github.com/DanKnox/websocket-rails/graphs/contributors)
210
+ who have helped keep this project moving.
211
+
212
+ Special thanks to [@nessche](https://github.com/nessche) who provided the improved routing DSL and
213
+ RSpec matcher suite.
214
+
195
215
  ## Development
196
216
 
197
217
  This gem is created and maintained by Dan Knox and Kyle Whalen under the MIT License.
@@ -0,0 +1,64 @@
1
+ module WebsocketRails
2
+
3
+ module SpecHelpers
4
+
5
+ def self.verify_route(event, target, non_exclusive)
6
+
7
+ raise ArgumentError, 'event must be of type SpecHelperEvent' unless event.is_a? WebsocketRails::SpecHelperEvent
8
+ target_class, target_method = WebsocketRails::TargetValidator.validate_target target
9
+
10
+ result = false
11
+ no_of_routes = 0
12
+ event.dispatcher.event_map.routes_for event do |controller, method|
13
+ no_of_routes += 1
14
+ if controller.class == target_class and method == target_method
15
+ result = true
16
+ end
17
+ end
18
+ result and (non_exclusive or no_of_routes == 1)
19
+ end
20
+
21
+ end
22
+
23
+ end
24
+
25
+
26
+ RSpec::Matchers.define :be_routed_to do |target|
27
+
28
+ match do |event|
29
+ WebsocketRails::SpecHelpers.verify_route event, target, true
30
+ end
31
+
32
+ failure_message_for_should do |event|
33
+ "expected event #{event.name} to be routed to target #{target}"
34
+ end
35
+
36
+ failure_message_for_should_not do |event|
37
+ "expected event #{event.name} not to be routed to target #{target}"
38
+ end
39
+
40
+ description do
41
+ "be routed to target #{target}"
42
+ end
43
+
44
+ end
45
+
46
+ RSpec::Matchers.define :be_routed_only_to do |target|
47
+
48
+ match do |event|
49
+ WebsocketRails::SpecHelpers.verify_route event, target, false
50
+ end
51
+
52
+ failure_message_for_should do |event|
53
+ "expected event #{event.name} to be routed only to target #{target}"
54
+ end
55
+
56
+ failure_message_for_should_not do |event|
57
+ "expected event #{event.name} not to be routed only to target #{target}"
58
+ end
59
+
60
+ description do
61
+ "be routed only to target #{target}"
62
+ end
63
+
64
+ end
@@ -0,0 +1,113 @@
1
+ module WebsocketRails
2
+
3
+ module SpecHelpers
4
+
5
+ def self.compare_trigger_data(event, data)
6
+ return true if data.nil?
7
+ return true if data == :any and event.data
8
+ return true if data == :nil and event.data.nil?
9
+ data.eql? event.data
10
+ end
11
+
12
+ def self.expected_data_for_spec_message(data)
13
+ case data
14
+ when nil
15
+ ''
16
+ when :nil
17
+ ' with no data'
18
+ when :any
19
+ ' with some data'
20
+ else
21
+ " with data #{data}"
22
+ end
23
+ end
24
+
25
+ def self.actual_data_for_spec_message(data)
26
+ data ? "with data #{data}": 'with no data'
27
+ end
28
+
29
+ def self.actual_for_spec_message(event, success)
30
+ if event.triggered?
31
+ if success.nil?
32
+ "triggered message #{actual_data_for_spec_message(event.data)}"
33
+ else
34
+ "triggered #{event.success ? 'a success' : 'a failure' } message #{actual_data_for_spec_message(event.data)}"
35
+ end
36
+ else
37
+ 'did not trigger any message'
38
+ end
39
+ end
40
+
41
+ def self.verify_trigger(event, data, success)
42
+ return false unless event.triggered?
43
+ return false unless compare_trigger_data(event, data)
44
+ success.nil? || success == event.success
45
+ end
46
+
47
+ end
48
+
49
+ end
50
+
51
+
52
+ RSpec::Matchers.define :trigger_message do |data|
53
+
54
+ match do |event|
55
+ WebsocketRails::SpecHelpers.verify_trigger event, data, nil
56
+ end
57
+
58
+ failure_message_for_should do |event|
59
+ "expected #{event.encoded_name} to trigger message#{WebsocketRails::SpecHelpers.expected_data_for_spec_message data}, " +
60
+ "instead it #{WebsocketRails::SpecHelpers.actual_for_spec_message event, nil}"
61
+ end
62
+
63
+ failure_message_for_should_not do |event|
64
+ "expected #{event.encoded_name} not to trigger message#{WebsocketRails::SpecHelpers.expected_data_for_spec_message data}"
65
+ end
66
+
67
+ description do
68
+ "trigger message#{WebsocketRails::SpecHelpers.expected_data_for_spec_message data}"
69
+ end
70
+
71
+ end
72
+
73
+ RSpec::Matchers.define :trigger_success_message do |data|
74
+
75
+ match do |event|
76
+ WebsocketRails::SpecHelpers.verify_trigger event, data, true
77
+ end
78
+
79
+ failure_message_for_should do |event|
80
+ "expected #{event.encoded_name} to trigger success message#{WebsocketRails::SpecHelpers.expected_data_for_spec_message data}, "+
81
+ "instead it #{WebsocketRails::SpecHelpers.actual_for_spec_message event, true}"
82
+ end
83
+
84
+ failure_message_for_should_not do |event|
85
+ "expected #{event.encoded_name} not to trigger success message#{WebsocketRails::SpecHelpers.expected_data_for_spec_message data}"
86
+ end
87
+
88
+ description do
89
+ "trigger success message#{WebsocketRails::SpecHelpers.expected_data_for_spec_message data}"
90
+ end
91
+
92
+ end
93
+
94
+ RSpec::Matchers.define :trigger_failure_message do |data|
95
+
96
+ match do |event|
97
+ WebsocketRails::SpecHelpers.verify_trigger event, data, false
98
+ end
99
+
100
+ failure_message_for_should do |event|
101
+ "expected #{event.encoded_name} to trigger failure message#{WebsocketRails::SpecHelpers.expected_data_for_spec_message data}, " +
102
+ "instead it #{WebsocketRails::SpecHelpers.actual_for_spec_message event, true}"
103
+ end
104
+
105
+ failure_message_for_should_not do |event|
106
+ "expected #{event.encoded_name} not to trigger failure message#{WebsocketRails::SpecHelpers.expected_data_for_spec_message data}"
107
+ end
108
+
109
+ description do
110
+ "trigger failure message#{WebsocketRails::SpecHelpers.expected_data_for_spec_message data}"
111
+ end
112
+
113
+ end
@@ -0,0 +1,30 @@
1
+ module WebsocketRails
2
+
3
+ class SpecHelperEvent < Event
4
+
5
+ attr_reader :dispatcher, :triggered
6
+
7
+ alias :triggered? :triggered
8
+
9
+ def initialize(event_name,options={})
10
+ super(event_name, options)
11
+ @triggered = false
12
+ @dispatcher = Dispatcher.new(nil)
13
+ end
14
+
15
+ def trigger
16
+ @triggered = true
17
+ end
18
+
19
+ def dispatch
20
+ @dispatcher.dispatch(self)
21
+ self
22
+ end
23
+
24
+ end
25
+
26
+ end
27
+
28
+ def create_event(name, data)
29
+ WebsocketRails::SpecHelperEvent.new(name, {data: data})
30
+ end
@@ -73,7 +73,7 @@ module WebsocketRails
73
73
  :log => "#{Rails.root}/log/websocket_rails.log",
74
74
  :tag => 'websocket_rails',
75
75
  :rackup => "#{Rails.root}/config.ru",
76
- :threaded => true,
76
+ :threaded => false,
77
77
  :daemonize => true,
78
78
  :dirname => Rails.root,
79
79
  :max_persistent_conns => 1024,
@@ -119,14 +119,6 @@ module WebsocketRails
119
119
  connection.trigger self if connection
120
120
  end
121
121
 
122
- private
123
-
124
- def validate_namespace(namespace)
125
- namespace = [namespace] unless namespace.is_a?(Array)
126
- namespace.unshift :global unless namespace.first == :global
127
- namespace.map(&:to_sym) rescue [:global]
128
- end
129
-
130
122
  def encoded_name
131
123
  if namespace.size > 1
132
124
  child_namespace = namespace.dup[1..-1]
@@ -138,5 +130,15 @@ module WebsocketRails
138
130
  combined_name
139
131
  end
140
132
 
133
+ private
134
+
135
+ def validate_namespace(namespace)
136
+ namespace = [namespace] unless namespace.is_a?(Array)
137
+ namespace.unshift :global unless namespace.first == :global
138
+ namespace.map(&:to_sym) rescue [:global]
139
+ end
140
+
141
+
142
+
141
143
  end
142
144
  end
@@ -99,12 +99,13 @@ module WebsocketRails
99
99
  # Stores controller/action pairs for events subscribed under
100
100
  # this namespace.
101
101
  def store(event_name,options)
102
- klass = options[:to] || raise("Must specify a class for to: option in event route")
103
- action = options[:with_method] || raise("Must specify a method for with_method: option in event route")
102
+ klass, action = TargetValidator.validate_target options
104
103
  create_controller_instance_for klass if controllers[klass].nil?
105
104
  actions[event_name] << [klass,action]
106
105
  end
107
106
 
107
+
108
+
108
109
  # Reloads the controller instances stored in the event map
109
110
  # collection, picking up code changes in development.
110
111
  def reload_controllers!
@@ -175,4 +176,44 @@ module WebsocketRails
175
176
  end
176
177
 
177
178
  end
179
+
180
+ class TargetValidator
181
+
182
+ # Parses the target and extracts controller/action pair or raises an error if target is invalid
183
+ def self.validate_target(target)
184
+ case target
185
+ when Hash
186
+ validate_hash_target target
187
+ when String
188
+ validate_string_target target
189
+ else
190
+ raise('Must specify the event target either as a string product#new_product or as a Hash to: ProductController, with_method: :new_product')
191
+ end
192
+ end
193
+
194
+ private
195
+
196
+ # Parses the target as a Hash, expecting keys to: and with_method:
197
+ def self.validate_hash_target(target)
198
+ klass = target[:to] || raise("Must specify a class for to: option in event route")
199
+ action = target[:with_method] || raise("Must specify a method for with_method: option in event route")
200
+ [klass, action]
201
+ end
202
+
203
+ # Parses the target as a String, expecting it to be in the format "product#new_product"
204
+ def self.validate_string_target(target)
205
+ strings = target.split('#')
206
+ raise('The string must be in a format like product#new_product') unless strings.count == 2
207
+ klass = constantize_controller strings[0]
208
+ action = strings[1].to_sym
209
+ [klass, action]
210
+ end
211
+
212
+ def self.constantize_controller(controller_string)
213
+ strings = (controller_string << '_controller').split('/')
214
+ strings.map{|string| string.camelize}.join('::').constantize
215
+ end
216
+
217
+ end
218
+
178
219
  end
@@ -0,0 +1,3 @@
1
+ require 'spec_helpers/spec_helper_event'
2
+ require 'spec_helpers/matchers/route_matchers'
3
+ require 'spec_helpers/matchers/trigger_matchers'
@@ -1,3 +1,3 @@
1
1
  module WebsocketRails
2
- VERSION = "0.2.1"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -763,3 +763,4 @@ Connecting to database specified by database.yml
763
763
  Connecting to database specified by database.yml
764
764
  Connecting to database specified by database.yml
765
765
  Connecting to database specified by database.yml
766
+ Connecting to database specified by database.yml
data/spec/spec_helper.rb CHANGED
@@ -14,6 +14,8 @@ require 'websocket-rails'
14
14
  # Requires supporting ruby files with custom matchers and macros, etc,
15
15
  # in spec/support/ and its subdirectories.
16
16
  Dir["./spec/support/**/*.rb"].each {|f| require f}
17
+ require 'websocket_rails/spec_helpers'
18
+ require 'rspec-matchers-matchers'
17
19
 
18
20
  RSpec.configure do |config|
19
21
  # == Mock Framework
@@ -0,0 +1,118 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Route Matchers' do
4
+
5
+ class RouteSpecProductController < WebsocketRails::BaseController
6
+
7
+ def update_product
8
+ end
9
+
10
+ def delete_product
11
+ end
12
+
13
+ end
14
+
15
+ class RouteSpecWarehouseController < WebsocketRails::BaseController
16
+
17
+ def remove_product
18
+ end
19
+
20
+ end
21
+
22
+
23
+ def define_route_test_events
24
+ WebsocketRails.route_block = nil
25
+ WebsocketRails::EventMap.describe do
26
+
27
+ namespace :product do
28
+ subscribe :update, :to => RouteSpecProductController, :with_method => :update_product
29
+ subscribe :delete, :to => RouteSpecProductController, :with_method => :delete_product
30
+ subscribe :delete, :to => RouteSpecWarehouseController, :with_method => :remove_product
31
+ end
32
+ end
33
+ end
34
+
35
+ before { define_route_test_events }
36
+
37
+ describe 'be_routed_to' do
38
+
39
+ it 'should return true when the event is routed only to the specified controller' do
40
+ create_event('product.update', nil).should be_routed_to 'route_spec_product#update_product'
41
+ end
42
+
43
+ it 'should return true when the event is routed also to the specified controller' do
44
+ create_event('product.delete', nil).should be_routed_to 'route_spec_product#delete_product'
45
+ end
46
+
47
+ it 'should return false when the event is not routed to the specified controller' do
48
+ create_event('product.update', nil).should_not be_routed_to 'route_spec_product#delete_product'
49
+ end
50
+
51
+ it 'should produce the correct failure message' do
52
+ event = create_event('route_spec_product.update', nil)
53
+ matcher = be_routed_to 'route_spec_product#delete_product'
54
+ cache_messages_for_matcher(matcher, event)
55
+ matcher.should produce_as_failure_message 'expected event update to be routed to target route_spec_product#delete_product'
56
+ end
57
+
58
+ it 'should produce the correct negative failure message' do
59
+ event = create_event('product.update', nil)
60
+ matcher = be_routed_to 'route_spec_product#update_product'
61
+ cache_messages_for_matcher(matcher, event)
62
+ matcher.should produce_as_negative_failure_message 'expected event update not to be routed to target route_spec_product#update_product'
63
+ end
64
+
65
+ it 'should produce the correct description' do
66
+ event = create_event('product.update', nil)
67
+ matcher = be_routed_to 'route_spec_product#update_product'
68
+ cache_messages_for_matcher(matcher, event)
69
+ matcher.should produce_as_description 'be routed to target route_spec_product#update_product'
70
+ end
71
+
72
+ end
73
+
74
+ describe 'be_routed_only_to' do
75
+
76
+ it 'should return true when the event is routed only to the specified controller' do
77
+ create_event('product.update', nil).should be_routed_only_to 'route_spec_product#update_product'
78
+ end
79
+
80
+ it 'should return false when the event is routed also to the specified controller' do
81
+ create_event('product.delete', nil).should_not be_routed_only_to 'route_spec_product#delete_product'
82
+ end
83
+
84
+ it 'should return false when the event is not routed to the specified controller' do
85
+ create_event('product.update', nil).should_not be_routed_only_to 'route_spec_product#delete_product'
86
+ end
87
+
88
+ it 'should produce the correct failure message' do
89
+ event = create_event('product.update', nil)
90
+ matcher = be_routed_only_to 'route_spec_product#delete_product'
91
+ cache_messages_for_matcher(matcher, event)
92
+ matcher.should produce_as_failure_message 'expected event update to be routed only to target route_spec_product#delete_product'
93
+ end
94
+
95
+ it 'should produce the correct negative failure message' do
96
+ event = create_event('product.update', nil)
97
+ matcher = be_routed_only_to 'route_spec_product#update_product'
98
+ cache_messages_for_matcher(matcher, event)
99
+ matcher.should produce_as_negative_failure_message 'expected event update not to be routed only to target route_spec_product#update_product'
100
+ end
101
+
102
+ it 'should produce the correct description' do
103
+ event = create_event('product.update', nil)
104
+ matcher = be_routed_only_to 'route_spec_product#update_product'
105
+ cache_messages_for_matcher(matcher, event)
106
+ matcher.should produce_as_description 'be routed only to target route_spec_product#update_product'
107
+ end
108
+
109
+
110
+ end
111
+
112
+
113
+
114
+
115
+
116
+
117
+
118
+ end
@@ -0,0 +1,257 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Trigger Matchers' do
4
+
5
+ class TriggerSpecProductController < WebsocketRails::BaseController
6
+
7
+ # a method that does not trigger messages
8
+ def update_product
9
+ end
10
+
11
+ def delete_product
12
+ data = message[:data] ? 'Return Data' : nil
13
+ if message[:confirm_delete]
14
+ trigger_success(data)
15
+ else
16
+ trigger_failure(data)
17
+ end
18
+ end
19
+
20
+ end
21
+
22
+ def define_test_events
23
+ WebsocketRails.route_block = nil
24
+ WebsocketRails::EventMap.describe do
25
+
26
+ namespace :product do
27
+ subscribe :update, :to => TriggerSpecProductController, :with_method => :update_product
28
+ subscribe :delete, :to => TriggerSpecProductController, :with_method => :delete_product
29
+ end
30
+ end
31
+ end
32
+
33
+ before { define_test_events }
34
+
35
+ around(:each) do |example|
36
+ EM.run do
37
+ example.run
38
+ end
39
+ end
40
+
41
+ after(:each) do
42
+ EM.stop
43
+ end
44
+
45
+ # as we have have 16 possible combinations of trigger messages and data matching pattern (data|no_data, success|failure,
46
+ # no_checking|checking_with_any|checking_with_nil|checking_with_exact_data) plus the case of no message at all
47
+ # for EACH of the matchers, resulting in a total 51 cases, we will not extensively test all cases for all matchers
48
+ # but we just make sure that coverage is 100%
49
+
50
+ describe 'trigger_message' do
51
+
52
+ it 'should return false when the event does not trigger any message' do
53
+ create_event('product.update', nil).dispatch.should_not trigger_message
54
+ end
55
+
56
+ it 'should produce the correct failure message when no trigger is generated' do
57
+ event = create_event('product.update', nil).dispatch
58
+ matcher = trigger_message
59
+ cache_messages_for_matcher(matcher, event)
60
+ matcher.should produce_as_failure_message 'expected product.update to trigger message, instead it did not trigger any message'
61
+ end
62
+
63
+ it 'should return true when the message is a failure' do
64
+ create_event('product.delete', {confirm_delete: false, data: true}).dispatch.should trigger_message
65
+ end
66
+
67
+ it 'should return true when the message is a success' do
68
+ create_event('product.delete', {confirm_delete: true, data: false}).dispatch.should trigger_message
69
+ end
70
+
71
+ context 'when a message is triggered with no data' do
72
+
73
+ it 'should return true when no check is done' do
74
+ create_event('product.delete', {confirm_delete: true, data: false}).dispatch.should trigger_message
75
+ end
76
+
77
+ it 'should produce the correct negative failure message when no check on data is done' do
78
+ event = create_event('product.delete', {confirm_delete: true, data: false}).dispatch
79
+ matcher = trigger_message
80
+ cache_messages_for_matcher(matcher, event)
81
+ matcher.should produce_as_negative_failure_message 'expected product.delete not to trigger message'
82
+ end
83
+
84
+ it 'should return true when explicitly checking no data' do
85
+ create_event('product.delete', {confirm_delete: false, data: false}).dispatch.should trigger_message :nil
86
+ end
87
+
88
+ it 'should produce the correct negative failure message when explicitly checking no data' do
89
+ event = create_event('product.delete', {confirm_delete: true, data: false}).dispatch
90
+ matcher = trigger_message :nil
91
+ cache_messages_for_matcher(matcher, event)
92
+ matcher.should produce_as_negative_failure_message 'expected product.delete not to trigger message with no data'
93
+ end
94
+
95
+ it 'should return false when checking for some data' do
96
+ create_event('product.delete', {confirm_delete: true, data: false}).dispatch.should_not trigger_message :any
97
+ end
98
+
99
+ it 'should return the correct failure message when checking for some data' do
100
+ event = create_event('product.delete', {confirm_delete: true, data: false}).dispatch
101
+ matcher = trigger_message :any
102
+ cache_messages_for_matcher(matcher, event)
103
+ matcher.should produce_as_failure_message 'expected product.delete to trigger message with some data, instead it triggered message with no data'
104
+ end
105
+
106
+ it 'should return false when checking for specific data' do
107
+ create_event('product.delete', {confirm_delete: false, data: false}).dispatch.should_not trigger_message 'Expected Data'
108
+ end
109
+
110
+ it 'should return the correct failure message when checking for specific data' do
111
+ event = create_event('product.delete', {confirm_delete: true, data: false}).dispatch
112
+ matcher = trigger_message 'Expected Data'
113
+ cache_messages_for_matcher(matcher, event)
114
+ matcher.should produce_as_failure_message 'expected product.delete to trigger message with data Expected Data, instead it triggered message with no data'
115
+ end
116
+
117
+ end
118
+
119
+ context 'when a message is triggered with some data' do
120
+
121
+ it 'should return true when no check is done' do
122
+ create_event('product.delete', {confirm_delete: true, data: true}).dispatch.should trigger_message
123
+ end
124
+
125
+ it 'should return false when explicitly checking no data' do
126
+ create_event('product.delete', {confirm_delete: false, data: true}).dispatch.should_not trigger_message :nil
127
+ end
128
+
129
+ it 'should produce the correct failure message when explicitly checking for no data' do
130
+ event = create_event('product.delete', {confirm_delete: false, data: true}).dispatch
131
+ matcher = trigger_message :nil
132
+ cache_messages_for_matcher(matcher, event)
133
+ matcher.should produce_as_failure_message 'expected product.delete to trigger message with no data, instead it triggered message with data Return Data'
134
+ end
135
+
136
+ it 'should return true when checking for some data' do
137
+ create_event('product.delete', {confirm_delete: true, data: true}).dispatch.should trigger_message :any
138
+ end
139
+
140
+ it 'should produce the correct negative failure message when checking for some data' do
141
+ event = create_event('product.delete', {confirm_delete: false, data: true}).dispatch
142
+ matcher = trigger_message :any
143
+ cache_messages_for_matcher(matcher, event)
144
+ matcher.should produce_as_negative_failure_message 'expected product.delete not to trigger message with some data'
145
+ end
146
+
147
+ it 'should return false when checking for specific data with wrong data' do
148
+ create_event('product.delete', {confirm_delete: false, data: true}).dispatch.should_not trigger_message 'Wrong Data'
149
+ end
150
+
151
+ it 'should produce the correct failure message when checking for specific data' do
152
+ event = create_event('product.delete', {confirm_delete: false, data: true}).dispatch
153
+ matcher = trigger_message 'Wrong Data'
154
+ cache_messages_for_matcher(matcher, event)
155
+ matcher.should produce_as_failure_message 'expected product.delete to trigger message with data Wrong Data, instead it triggered message with data Return Data'
156
+ end
157
+
158
+ it 'should return true when checking for specific data with correct data' do
159
+ create_event('product.delete', {confirm_delete: true, data: true}).dispatch.should trigger_message 'Return Data'
160
+ end
161
+
162
+ it 'should produce the correct negative failure message when checking for specific data' do
163
+ event = create_event('product.delete', {confirm_delete: false, data: true}).dispatch
164
+ matcher = trigger_message 'Return Data'
165
+ cache_messages_for_matcher(matcher, event)
166
+ matcher.should produce_as_negative_failure_message 'expected product.delete not to trigger message with data Return Data'
167
+ end
168
+
169
+ end
170
+
171
+ it 'should produce the correct description' do
172
+ event = create_event('product.update', nil)
173
+ matcher = trigger_message 'Return Data'
174
+ cache_messages_for_matcher(matcher, event)
175
+ matcher.should produce_as_description 'trigger message with data Return Data'
176
+ end
177
+
178
+
179
+ end
180
+
181
+ describe 'trigger_success_message' do
182
+
183
+ it 'should return false when the method does not trigger any message' do
184
+ create_event('product.update', nil).dispatch.should_not trigger_success_message
185
+ end
186
+
187
+ it 'should produce the correct failure message when the method does not trigger any message' do
188
+ event = create_event('product.update', nil).dispatch
189
+ matcher = trigger_success_message 'Return Data'
190
+ cache_messages_for_matcher(matcher, event)
191
+ matcher.should produce_as_failure_message 'expected product.update to trigger success message with data Return Data, instead it did not trigger any message'
192
+ end
193
+
194
+ it 'should return true when the method triggers a success message' do
195
+ create_event('product.delete', {confirm_delete: true, data: true}).dispatch.should trigger_success_message
196
+ end
197
+
198
+ it 'should produce the correct negative failure message when the method triggers a success message' do
199
+ event = create_event('product.delete', {confirm_delete: true, data: true}).dispatch
200
+ matcher = trigger_success_message :any
201
+ cache_messages_for_matcher(matcher, event)
202
+ matcher.should produce_as_negative_failure_message 'expected product.delete not to trigger success message with some data'
203
+ end
204
+
205
+ it 'should return false when the method triggers a failure message' do
206
+ create_event('product.delete', {confirm_delete: false, data: true}).dispatch.should_not trigger_success_message
207
+ end
208
+
209
+ it 'should produce the correct failure message when the method triggers a failure message' do
210
+ event = create_event('product.delete', {confirm_delete: false, data: true}).dispatch
211
+ matcher = trigger_success_message
212
+ cache_messages_for_matcher(matcher, event)
213
+ matcher.should produce_as_failure_message 'expected product.delete to trigger success message, instead it triggered a failure message with data Return Data'
214
+ end
215
+
216
+ end
217
+
218
+ describe 'trigger_failure_message' do
219
+
220
+ it 'should return false when the method does not trigger any message' do
221
+ create_event('product.update', nil).dispatch.should_not trigger_failure_message
222
+ end
223
+
224
+ it 'should produce the correct failure message when the method does not trigger any message' do
225
+ event = create_event('product.update', nil).dispatch
226
+ matcher = trigger_failure_message 'Return Data'
227
+ cache_messages_for_matcher(matcher, event)
228
+ matcher.should produce_as_failure_message 'expected product.update to trigger failure message with data Return Data, instead it did not trigger any message'
229
+ end
230
+
231
+ it 'should return false when the method triggers a success message' do
232
+ create_event('product.delete', {confirm_delete: true, data: true}).dispatch.should_not trigger_failure_message
233
+ end
234
+
235
+ it 'should produce the correct failure message when the method triggers a success message' do
236
+ event = create_event('product.delete', {confirm_delete: true, data: false}).dispatch
237
+ matcher = trigger_failure_message :any
238
+ cache_messages_for_matcher(matcher, event)
239
+ matcher.should produce_as_failure_message 'expected product.delete to trigger failure message with some data, instead it triggered a success message with no data'
240
+ end
241
+
242
+
243
+ it 'should return true when the method triggers a failure message' do
244
+ create_event('product.delete', {confirm_delete: false, data: true}).dispatch.should trigger_failure_message
245
+ end
246
+
247
+ it 'should produce the correct negative failure message when the method triggers a failure message' do
248
+ event = create_event('product.delete', {confirm_delete: false, data: true}).dispatch
249
+ matcher = trigger_failure_message
250
+ cache_messages_for_matcher(matcher, event)
251
+ matcher.should produce_as_negative_failure_message 'expected product.delete not to trigger failure message'
252
+ end
253
+
254
+ end
255
+
256
+
257
+ end
@@ -0,0 +1,66 @@
1
+ require 'spec_helper'
2
+
3
+ module WebsocketRails
4
+
5
+ describe SpecHelperEvent do
6
+
7
+ before do
8
+ @dispatcher = double(:dispatcher)
9
+ Dispatcher.stub(:new).and_return @dispatcher
10
+ @event = SpecHelperEvent.new('my_event', data: 'my_data')
11
+ end
12
+
13
+ describe 'initialize' do
14
+
15
+ it 'should initialize the name and namespace of the event' do
16
+ @event.namespace.should == [:global]
17
+ @event.name.should == :my_event
18
+ end
19
+
20
+ it 'should initialize the data of the event' do
21
+ @event.data.should == 'my_data'
22
+ end
23
+
24
+ it 'should set the event to not triggered' do
25
+ @event.should_not be_triggered
26
+ end
27
+
28
+ end
29
+
30
+ describe 'trigger' do
31
+
32
+ it 'should set the triggered variable to true' do
33
+ @event.trigger
34
+ @event.should be_triggered
35
+ end
36
+
37
+ end
38
+
39
+ describe 'dispatch' do
40
+
41
+ it 'should invoke dispatch on the dispatcher object' do
42
+ @dispatcher.should_receive(:dispatch).with(@event)
43
+ @event.dispatch
44
+ end
45
+
46
+ it 'should return itself to be able to chain matchers' do
47
+ @dispatcher.stub(:dispatch)
48
+ @event.dispatch.should == @event
49
+ end
50
+
51
+ end
52
+
53
+ end
54
+
55
+ end
56
+
57
+ describe 'create_event' do
58
+
59
+ it 'should create a SpecHelperEvent with the correct parameters' do
60
+ event = create_event('my_event','my_data')
61
+ event.should be_a WebsocketRails::SpecHelperEvent
62
+ event.name.should == :my_event
63
+ event.data.should == 'my_data'
64
+ end
65
+
66
+ end
@@ -1,14 +1,28 @@
1
1
  require 'spec_helper'
2
2
 
3
+ # this classes are outside the WebsocketRails namespace to better reflect the actual
4
+ # situation in a normal usage
5
+
6
+ class ProductController < WebsocketRails::BaseController
7
+ def update_product
8
+ true
9
+ end
10
+ end
11
+
12
+ # name is chosen so that we know camelize is working correctly
13
+ class ComplexProductController < WebsocketRails::BaseController
14
+
15
+ def simplify_product
16
+ true
17
+ end
18
+
19
+ end
20
+
21
+
3
22
  module WebsocketRails
4
23
  describe EventMap do
5
24
 
6
- class ProductController < WebsocketRails::BaseController
7
- def update_product
8
- true
9
- end
10
- end
11
-
25
+
12
26
  def define_test_events
13
27
  WebsocketRails.route_block = nil
14
28
  WebsocketRails::EventMap.describe do
@@ -17,6 +31,11 @@ module WebsocketRails
17
31
  namespace :product do
18
32
  subscribe :update, :to => ProductController, :with_method => :update_product
19
33
  end
34
+
35
+ namespace :complex_product do
36
+ subscribe :simplify, 'complex_product#simplify'
37
+ end
38
+
20
39
  end
21
40
  end
22
41
 
@@ -55,8 +74,9 @@ module WebsocketRails
55
74
 
56
75
  before { @namespace = subject.namespace }
57
76
 
58
- it "should store the event in the correct namespace" do
77
+ it "should store the event in the correct namespaces" do
59
78
  @namespace.namespaces[:product].actions[:update].should be_present
79
+ @namespace.namespaces[:complex_product].actions[:simplify].should be_present
60
80
  end
61
81
 
62
82
  end
@@ -78,7 +98,7 @@ module WebsocketRails
78
98
  end
79
99
 
80
100
  context "with events in a child namespace" do
81
- it "should yield the controller and action name for each route defined for an event" do
101
+ it "should yield the controller and action name for each route defined with a hash for an event" do
82
102
  ProductController.any_instance.should_receive(:action_executed)
83
103
  event = HelperMethods::MockEvent.new :update, [:global,:product]
84
104
 
@@ -87,7 +107,21 @@ module WebsocketRails
87
107
  controller.class.should == ProductController
88
108
  method.should == :update_product
89
109
  end
90
- end
110
+
111
+ end
112
+
113
+ it "should yield the controller and action name for each route defined with a string for an event" do
114
+ ComplexProductController.any_instance.should_receive(:action_executed)
115
+ event = HelperMethods::MockEvent.new :simplify, [:global,:complex_product]
116
+
117
+ subject.routes_for(event) do |controller,method|
118
+ controller.action_executed
119
+ controller.class.should == ComplexProductController
120
+ method.should == :simplify
121
+ end
122
+
123
+ end
124
+
91
125
  end
92
126
 
93
127
  end
@@ -0,0 +1,88 @@
1
+ require 'spec_helper'
2
+
3
+
4
+ class ComplexProductController < WebsocketRails::BaseController
5
+
6
+ def simplify
7
+ end
8
+
9
+ end
10
+
11
+ module MyModule
12
+
13
+ class AnotherController < WebsocketRails::BaseController
14
+
15
+ def complicate
16
+ end
17
+
18
+ end
19
+
20
+ module MySubModule
21
+
22
+ class AThirdController < WebsocketRails::BaseController
23
+
24
+ def confuse
25
+ end
26
+
27
+ end
28
+
29
+ end
30
+
31
+ end
32
+
33
+
34
+ module WebsocketRails
35
+
36
+ describe TargetValidator do
37
+
38
+ describe 'validate_target' do
39
+
40
+ it 'should raise an error when target class is not supported' do
41
+ expect{TargetValidator.validate_target(50)}.to raise_error
42
+ end
43
+
44
+ it 'should raise if passed hash does not contain the to: key' do
45
+ expect{TargetValidator.validate_target(from: ComplexProductController, with_method: :simplify)}.to raise_error
46
+ end
47
+
48
+ it 'should raise if passed hash does not contain the with_method: key' do
49
+ expect{TargetValidator.validate_target(to: ComplexProductController, without_method: :simplify)}.to raise_error
50
+ end
51
+
52
+ it 'should raise if the string is not in the correct format' do
53
+ expect{TargetValidator.validate_target('malformed_string')}.to raise_error
54
+ expect{TargetValidator.validate_target('very#malformed#string')}.to raise_error
55
+ end
56
+
57
+ it 'should raise if the class specified in the String does not exist' do
58
+ expect{TargetValidator.validate_target('my_non_existent#my_method')}.to raise_error
59
+ end
60
+
61
+ it 'should parse correctly a well-formed Hash' do
62
+ TargetValidator::validate_target(to: ComplexProductController, with_method: :simplify).should == [ComplexProductController, :simplify]
63
+ end
64
+
65
+ context 'when the string is well-formed' do
66
+
67
+ it 'should parse correctly when the controller is a top-level' do
68
+ TargetValidator::validate_target('complex_product#simplify').should == [ComplexProductController, :simplify]
69
+ end
70
+
71
+ it 'should parse correctly when the controller belongs to a module' do
72
+ TargetValidator::validate_target('my_module/another#complicate').should == [MyModule::AnotherController, :complicate]
73
+ end
74
+
75
+ it 'should parse correctly with many levels of module nesting' do
76
+ TargetValidator::validate_target('my_module/my_sub_module/a_third#confuse').should == [MyModule::MySubModule::AThirdController, :confuse]
77
+ end
78
+
79
+ end
80
+
81
+ end
82
+
83
+
84
+ end
85
+
86
+
87
+
88
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: websocket-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.3.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2013-01-29 00:00:00.000000000Z
14
+ date: 2013-02-06 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: rails
@@ -157,6 +157,22 @@ dependencies:
157
157
  - - ! '>='
158
158
  - !ruby/object:Gem::Version
159
159
  version: '0'
160
+ - !ruby/object:Gem::Dependency
161
+ name: rspec-matchers-matchers
162
+ requirement: !ruby/object:Gem::Requirement
163
+ none: false
164
+ requirements:
165
+ - - ! '>='
166
+ - !ruby/object:Gem::Version
167
+ version: '0'
168
+ type: :development
169
+ prerelease: false
170
+ version_requirements: !ruby/object:Gem::Requirement
171
+ none: false
172
+ requirements:
173
+ - - ! '>='
174
+ - !ruby/object:Gem::Version
175
+ version: '0'
160
176
  description: Seamless Ruby on Rails websocket integration.
161
177
  email:
162
178
  - dknox@threedotloft.com
@@ -177,6 +193,9 @@ files:
177
193
  - lib/rails/app/controllers/websocket_rails/delegation_controller.rb
178
194
  - lib/rails/config/routes.rb
179
195
  - lib/rails/tasks/websocket_rails.tasks
196
+ - lib/spec_helpers/matchers/route_matchers.rb
197
+ - lib/spec_helpers/matchers/trigger_matchers.rb
198
+ - lib/spec_helpers/spec_helper_event.rb
180
199
  - lib/websocket-rails.rb
181
200
  - lib/websocket_rails/base_controller.rb
182
201
  - lib/websocket_rails/channel.rb
@@ -193,6 +212,7 @@ files:
193
212
  - lib/websocket_rails/event_queue.rb
194
213
  - lib/websocket_rails/internal_events.rb
195
214
  - lib/websocket_rails/logging.rb
215
+ - lib/websocket_rails/spec_helpers.rb
196
216
  - lib/websocket_rails/synchronization.rb
197
217
  - lib/websocket_rails/version.rb
198
218
  - bin/thin-socketrails
@@ -251,6 +271,9 @@ files:
251
271
  - spec/javascripts/websocket_rails/websocket_connection_spec.coffee
252
272
  - spec/javascripts/websocket_rails/websocket_rails_spec.coffee
253
273
  - spec/spec_helper.rb
274
+ - spec/spec_helpers/matchers/route_matchers_spec.rb
275
+ - spec/spec_helpers/matchers/trigger_matchers_spec.rb
276
+ - spec/spec_helpers/spec_helper_event_spec.rb
254
277
  - spec/support/helper_methods.rb
255
278
  - spec/support/mock_web_socket.rb
256
279
  - spec/unit/channel_manager_spec.rb
@@ -266,6 +289,7 @@ files:
266
289
  - spec/unit/event_spec.rb
267
290
  - spec/unit/logging_spec.rb
268
291
  - spec/unit/synchronization_spec.rb
292
+ - spec/unit/target_validator_spec.rb
269
293
  - MIT-LICENSE
270
294
  - Rakefile
271
295
  - Gemfile
@@ -285,7 +309,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
285
309
  version: '0'
286
310
  segments:
287
311
  - 0
288
- hash: -982119143149111185
312
+ hash: 3143824869554570755
289
313
  required_rubygems_version: !ruby/object:Gem::Requirement
290
314
  none: false
291
315
  requirements:
@@ -294,10 +318,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
294
318
  version: '0'
295
319
  segments:
296
320
  - 0
297
- hash: -982119143149111185
321
+ hash: 3143824869554570755
298
322
  requirements: []
299
323
  rubyforge_project: websocket-rails
300
- rubygems_version: 1.8.19
324
+ rubygems_version: 1.8.24
301
325
  signing_key:
302
326
  specification_version: 3
303
327
  summary: Plug and play websocket support for ruby on rails. Includes event router