ably-rest 0.7.1 → 0.7.3

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 (148) hide show
  1. checksums.yaml +13 -5
  2. data/.gitmodules +1 -1
  3. data/.rspec +1 -0
  4. data/.travis.yml +7 -3
  5. data/SPEC.md +495 -419
  6. data/ably-rest.gemspec +19 -5
  7. data/lib/ably-rest.rb +9 -1
  8. data/lib/submodules/ably-ruby/.gitignore +6 -0
  9. data/lib/submodules/ably-ruby/.rspec +1 -0
  10. data/lib/submodules/ably-ruby/.ruby-version.old +1 -0
  11. data/lib/submodules/ably-ruby/.travis.yml +10 -0
  12. data/lib/submodules/ably-ruby/Gemfile +4 -0
  13. data/lib/submodules/ably-ruby/LICENSE.txt +22 -0
  14. data/lib/submodules/ably-ruby/README.md +122 -0
  15. data/lib/submodules/ably-ruby/Rakefile +34 -0
  16. data/lib/submodules/ably-ruby/SPEC.md +1794 -0
  17. data/lib/submodules/ably-ruby/ably.gemspec +36 -0
  18. data/lib/submodules/ably-ruby/lib/ably.rb +12 -0
  19. data/lib/submodules/ably-ruby/lib/ably/auth.rb +438 -0
  20. data/lib/submodules/ably-ruby/lib/ably/exceptions.rb +69 -0
  21. data/lib/submodules/ably-ruby/lib/ably/logger.rb +102 -0
  22. data/lib/submodules/ably-ruby/lib/ably/models/error_info.rb +37 -0
  23. data/lib/submodules/ably-ruby/lib/ably/models/idiomatic_ruby_wrapper.rb +223 -0
  24. data/lib/submodules/ably-ruby/lib/ably/models/message.rb +132 -0
  25. data/lib/submodules/ably-ruby/lib/ably/models/message_encoders/base.rb +108 -0
  26. data/lib/submodules/ably-ruby/lib/ably/models/message_encoders/base64.rb +40 -0
  27. data/lib/submodules/ably-ruby/lib/ably/models/message_encoders/cipher.rb +83 -0
  28. data/lib/submodules/ably-ruby/lib/ably/models/message_encoders/json.rb +34 -0
  29. data/lib/submodules/ably-ruby/lib/ably/models/message_encoders/utf8.rb +26 -0
  30. data/lib/submodules/ably-ruby/lib/ably/models/nil_logger.rb +20 -0
  31. data/lib/submodules/ably-ruby/lib/ably/models/paginated_resource.rb +173 -0
  32. data/lib/submodules/ably-ruby/lib/ably/models/presence_message.rb +147 -0
  33. data/lib/submodules/ably-ruby/lib/ably/models/protocol_message.rb +210 -0
  34. data/lib/submodules/ably-ruby/lib/ably/models/stat.rb +161 -0
  35. data/lib/submodules/ably-ruby/lib/ably/models/token.rb +74 -0
  36. data/lib/submodules/ably-ruby/lib/ably/modules/ably.rb +15 -0
  37. data/lib/submodules/ably-ruby/lib/ably/modules/async_wrapper.rb +62 -0
  38. data/lib/submodules/ably-ruby/lib/ably/modules/channels_collection.rb +69 -0
  39. data/lib/submodules/ably-ruby/lib/ably/modules/conversions.rb +100 -0
  40. data/lib/submodules/ably-ruby/lib/ably/modules/encodeable.rb +69 -0
  41. data/lib/submodules/ably-ruby/lib/ably/modules/enum.rb +202 -0
  42. data/lib/submodules/ably-ruby/lib/ably/modules/event_emitter.rb +128 -0
  43. data/lib/submodules/ably-ruby/lib/ably/modules/event_machine_helpers.rb +26 -0
  44. data/lib/submodules/ably-ruby/lib/ably/modules/http_helpers.rb +41 -0
  45. data/lib/submodules/ably-ruby/lib/ably/modules/message_pack.rb +14 -0
  46. data/lib/submodules/ably-ruby/lib/ably/modules/model_common.rb +41 -0
  47. data/lib/submodules/ably-ruby/lib/ably/modules/state_emitter.rb +153 -0
  48. data/lib/submodules/ably-ruby/lib/ably/modules/state_machine.rb +57 -0
  49. data/lib/submodules/ably-ruby/lib/ably/modules/statesman_monkey_patch.rb +33 -0
  50. data/lib/submodules/ably-ruby/lib/ably/modules/uses_state_machine.rb +74 -0
  51. data/lib/submodules/ably-ruby/lib/ably/realtime.rb +64 -0
  52. data/lib/submodules/ably-ruby/lib/ably/realtime/channel.rb +298 -0
  53. data/lib/submodules/ably-ruby/lib/ably/realtime/channel/channel_manager.rb +92 -0
  54. data/lib/submodules/ably-ruby/lib/ably/realtime/channel/channel_state_machine.rb +69 -0
  55. data/lib/submodules/ably-ruby/lib/ably/realtime/channels.rb +50 -0
  56. data/lib/submodules/ably-ruby/lib/ably/realtime/client.rb +184 -0
  57. data/lib/submodules/ably-ruby/lib/ably/realtime/client/incoming_message_dispatcher.rb +184 -0
  58. data/lib/submodules/ably-ruby/lib/ably/realtime/client/outgoing_message_dispatcher.rb +70 -0
  59. data/lib/submodules/ably-ruby/lib/ably/realtime/connection.rb +445 -0
  60. data/lib/submodules/ably-ruby/lib/ably/realtime/connection/connection_manager.rb +368 -0
  61. data/lib/submodules/ably-ruby/lib/ably/realtime/connection/connection_state_machine.rb +91 -0
  62. data/lib/submodules/ably-ruby/lib/ably/realtime/connection/websocket_transport.rb +188 -0
  63. data/lib/submodules/ably-ruby/lib/ably/realtime/models/nil_channel.rb +30 -0
  64. data/lib/submodules/ably-ruby/lib/ably/realtime/presence.rb +564 -0
  65. data/lib/submodules/ably-ruby/lib/ably/rest.rb +43 -0
  66. data/lib/submodules/ably-ruby/lib/ably/rest/channel.rb +104 -0
  67. data/lib/submodules/ably-ruby/lib/ably/rest/channels.rb +44 -0
  68. data/lib/submodules/ably-ruby/lib/ably/rest/client.rb +396 -0
  69. data/lib/submodules/ably-ruby/lib/ably/rest/middleware/encoder.rb +49 -0
  70. data/lib/submodules/ably-ruby/lib/ably/rest/middleware/exceptions.rb +41 -0
  71. data/lib/submodules/ably-ruby/lib/ably/rest/middleware/external_exceptions.rb +24 -0
  72. data/lib/submodules/ably-ruby/lib/ably/rest/middleware/fail_if_unsupported_mime_type.rb +17 -0
  73. data/lib/submodules/ably-ruby/lib/ably/rest/middleware/logger.rb +58 -0
  74. data/lib/submodules/ably-ruby/lib/ably/rest/middleware/parse_json.rb +27 -0
  75. data/lib/submodules/ably-ruby/lib/ably/rest/middleware/parse_message_pack.rb +27 -0
  76. data/lib/submodules/ably-ruby/lib/ably/rest/presence.rb +92 -0
  77. data/lib/submodules/ably-ruby/lib/ably/util/crypto.rb +105 -0
  78. data/lib/submodules/ably-ruby/lib/ably/util/pub_sub.rb +43 -0
  79. data/lib/submodules/ably-ruby/lib/ably/version.rb +3 -0
  80. data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_history_spec.rb +154 -0
  81. data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_spec.rb +558 -0
  82. data/lib/submodules/ably-ruby/spec/acceptance/realtime/client_spec.rb +119 -0
  83. data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_failures_spec.rb +575 -0
  84. data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_spec.rb +785 -0
  85. data/lib/submodules/ably-ruby/spec/acceptance/realtime/message_spec.rb +457 -0
  86. data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_history_spec.rb +55 -0
  87. data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_spec.rb +1001 -0
  88. data/lib/submodules/ably-ruby/spec/acceptance/realtime/stats_spec.rb +23 -0
  89. data/lib/submodules/ably-ruby/spec/acceptance/realtime/time_spec.rb +27 -0
  90. data/lib/submodules/ably-ruby/spec/acceptance/rest/auth_spec.rb +564 -0
  91. data/lib/submodules/ably-ruby/spec/acceptance/rest/base_spec.rb +165 -0
  92. data/lib/submodules/ably-ruby/spec/acceptance/rest/channel_spec.rb +134 -0
  93. data/lib/submodules/ably-ruby/spec/acceptance/rest/channels_spec.rb +41 -0
  94. data/lib/submodules/ably-ruby/spec/acceptance/rest/client_spec.rb +273 -0
  95. data/lib/submodules/ably-ruby/spec/acceptance/rest/encoders_spec.rb +185 -0
  96. data/lib/submodules/ably-ruby/spec/acceptance/rest/message_spec.rb +247 -0
  97. data/lib/submodules/ably-ruby/spec/acceptance/rest/presence_spec.rb +292 -0
  98. data/lib/submodules/ably-ruby/spec/acceptance/rest/stats_spec.rb +172 -0
  99. data/lib/submodules/ably-ruby/spec/acceptance/rest/time_spec.rb +15 -0
  100. data/lib/submodules/ably-ruby/spec/resources/crypto-data-128.json +56 -0
  101. data/lib/submodules/ably-ruby/spec/resources/crypto-data-256.json +56 -0
  102. data/lib/submodules/ably-ruby/spec/rspec_config.rb +57 -0
  103. data/lib/submodules/ably-ruby/spec/shared/client_initializer_behaviour.rb +212 -0
  104. data/lib/submodules/ably-ruby/spec/shared/model_behaviour.rb +86 -0
  105. data/lib/submodules/ably-ruby/spec/shared/protocol_msgbus_behaviour.rb +36 -0
  106. data/lib/submodules/ably-ruby/spec/spec_helper.rb +20 -0
  107. data/lib/submodules/ably-ruby/spec/support/api_helper.rb +60 -0
  108. data/lib/submodules/ably-ruby/spec/support/event_machine_helper.rb +104 -0
  109. data/lib/submodules/ably-ruby/spec/support/markdown_spec_formatter.rb +118 -0
  110. data/lib/submodules/ably-ruby/spec/support/private_api_formatter.rb +36 -0
  111. data/lib/submodules/ably-ruby/spec/support/protocol_helper.rb +32 -0
  112. data/lib/submodules/ably-ruby/spec/support/random_helper.rb +15 -0
  113. data/lib/submodules/ably-ruby/spec/support/rest_testapp_before_retry.rb +15 -0
  114. data/lib/submodules/ably-ruby/spec/support/test_app.rb +113 -0
  115. data/lib/submodules/ably-ruby/spec/unit/auth_spec.rb +68 -0
  116. data/lib/submodules/ably-ruby/spec/unit/logger_spec.rb +146 -0
  117. data/lib/submodules/ably-ruby/spec/unit/models/error_info_spec.rb +18 -0
  118. data/lib/submodules/ably-ruby/spec/unit/models/idiomatic_ruby_wrapper_spec.rb +349 -0
  119. data/lib/submodules/ably-ruby/spec/unit/models/message_encoders/base64_spec.rb +181 -0
  120. data/lib/submodules/ably-ruby/spec/unit/models/message_encoders/cipher_spec.rb +260 -0
  121. data/lib/submodules/ably-ruby/spec/unit/models/message_encoders/json_spec.rb +135 -0
  122. data/lib/submodules/ably-ruby/spec/unit/models/message_encoders/utf8_spec.rb +56 -0
  123. data/lib/submodules/ably-ruby/spec/unit/models/message_spec.rb +389 -0
  124. data/lib/submodules/ably-ruby/spec/unit/models/paginated_resource_spec.rb +288 -0
  125. data/lib/submodules/ably-ruby/spec/unit/models/presence_message_spec.rb +386 -0
  126. data/lib/submodules/ably-ruby/spec/unit/models/protocol_message_spec.rb +315 -0
  127. data/lib/submodules/ably-ruby/spec/unit/models/stat_spec.rb +113 -0
  128. data/lib/submodules/ably-ruby/spec/unit/models/token_spec.rb +86 -0
  129. data/lib/submodules/ably-ruby/spec/unit/modules/async_wrapper_spec.rb +124 -0
  130. data/lib/submodules/ably-ruby/spec/unit/modules/conversions_spec.rb +72 -0
  131. data/lib/submodules/ably-ruby/spec/unit/modules/enum_spec.rb +272 -0
  132. data/lib/submodules/ably-ruby/spec/unit/modules/event_emitter_spec.rb +184 -0
  133. data/lib/submodules/ably-ruby/spec/unit/modules/state_emitter_spec.rb +283 -0
  134. data/lib/submodules/ably-ruby/spec/unit/realtime/channel_spec.rb +206 -0
  135. data/lib/submodules/ably-ruby/spec/unit/realtime/channels_spec.rb +81 -0
  136. data/lib/submodules/ably-ruby/spec/unit/realtime/client_spec.rb +30 -0
  137. data/lib/submodules/ably-ruby/spec/unit/realtime/connection_spec.rb +33 -0
  138. data/lib/submodules/ably-ruby/spec/unit/realtime/incoming_message_dispatcher_spec.rb +36 -0
  139. data/lib/submodules/ably-ruby/spec/unit/realtime/presence_spec.rb +111 -0
  140. data/lib/submodules/ably-ruby/spec/unit/realtime/realtime_spec.rb +9 -0
  141. data/lib/submodules/ably-ruby/spec/unit/realtime/websocket_transport_spec.rb +25 -0
  142. data/lib/submodules/ably-ruby/spec/unit/rest/channel_spec.rb +109 -0
  143. data/lib/submodules/ably-ruby/spec/unit/rest/channels_spec.rb +79 -0
  144. data/lib/submodules/ably-ruby/spec/unit/rest/client_spec.rb +53 -0
  145. data/lib/submodules/ably-ruby/spec/unit/rest/rest_spec.rb +10 -0
  146. data/lib/submodules/ably-ruby/spec/unit/util/crypto_spec.rb +87 -0
  147. data/lib/submodules/ably-ruby/spec/unit/util/pub_sub_spec.rb +86 -0
  148. metadata +182 -27
@@ -0,0 +1,86 @@
1
+ # encoding: utf-8
2
+
3
+ shared_examples 'a model' do |shared_options = {}|
4
+ let(:base_model_options) { shared_options.fetch(:base_model_options, {}) }
5
+ let(:args) { ([base_model_options.merge(model_options)] + model_args) }
6
+ let(:model) { subject.new(*args) }
7
+
8
+ context 'attributes' do
9
+ let(:unique_value) { random_str }
10
+
11
+ Array(shared_options[:with_simple_attributes]).each do |attribute|
12
+ context "##{attribute}" do
13
+ let(:model_options) { { attribute.to_sym => unique_value } }
14
+
15
+ it "retrieves attribute :#{attribute}" do
16
+ expect(model.public_send(attribute)).to eql(unique_value)
17
+ end
18
+ end
19
+ end
20
+
21
+ context '#hash', :api_private do
22
+ let(:model_options) { { action: 5 } }
23
+
24
+ it 'provides access to #hash' do
25
+ expect(model.hash).to eq(model_options)
26
+ end
27
+ end
28
+
29
+ context '#[]', :api_private do
30
+ let(:model_options) { { unusual: 'attribute' } }
31
+
32
+ it 'provides accessor method to #hash' do
33
+ expect(model[:unusual]).to eql('attribute')
34
+ end
35
+ end
36
+ end
37
+
38
+ context '#==' do
39
+ let(:model_options) { { channel: 'unique' } }
40
+
41
+ it 'is true when attributes are the same' do
42
+ new_message = -> { subject.new(*args) }
43
+ expect(new_message[]).to eq(new_message[])
44
+ end
45
+
46
+ it 'is false when attributes are not the same' do
47
+ expect(subject.new(*[action: 1] + model_args)).to_not eq(subject.new(*[action: 2] + model_args))
48
+ end
49
+
50
+ it 'is false when class type differs' do
51
+ expect(subject.new(*[action: 1] + model_args)).to_not eq(nil)
52
+ end
53
+ end
54
+
55
+ context '#to_msgpack', :api_private do
56
+ let(:model_options) { { name: 'test', action: 0, channel_snake_case: 'unique' } }
57
+ let(:serialized) { model.to_msgpack }
58
+
59
+ it 'returns a msgpack object with Ably payload naming' do
60
+ expect(MessagePack.unpack(serialized)).to include('channelSnakeCase' => 'unique')
61
+ end
62
+ end
63
+
64
+ context '#to_json', :api_private do
65
+ let(:model_options) { { name: 'test', action: 0, channel_snake_case: 'unique' } }
66
+ let(:serialized) { model.to_json }
67
+
68
+ it 'returns a JSON string with Ably payload naming' do
69
+ expect(JSON.parse(serialized)).to include('channelSnakeCase' => 'unique')
70
+ end
71
+ end
72
+
73
+ context 'is immutable' do
74
+ let(:model_options) { { channel: 'name' } }
75
+
76
+ it 'prevents changes' do
77
+ expect { model.hash[:channel] = 'new' }.to raise_error RuntimeError, /can't modify frozen.*Hash/
78
+ end
79
+
80
+ it 'dups options' do
81
+ expect(model.hash[:channel]).to eql('name')
82
+ model_options[:channel] = 'new'
83
+ expect(model.hash[:channel]).to eql('name')
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,36 @@
1
+ shared_examples 'a protocol message bus' do
2
+ describe '__protocol_msgbus__ PubSub', :api_private do
3
+ let(:protocol_message) do
4
+ Ably::Models::ProtocolMessage.new(
5
+ action: 15,
6
+ channel: 'channel',
7
+ msg_serial: 0,
8
+ messages: []
9
+ )
10
+ end
11
+
12
+ specify 'supports valid ProtocolMessage messages' do
13
+ received = 0
14
+ msgbus.subscribe(:protocol_message) { received += 1 }
15
+ expect { msgbus.publish(:protocol_message, protocol_message) }.to change { received }.to(1)
16
+ end
17
+
18
+ specify 'fail with unacceptable STATE event names' do
19
+ expect { msgbus.subscribe(:invalid) }.to raise_error KeyError
20
+ expect { msgbus.publish(:invalid) }.to raise_error KeyError
21
+ expect { msgbus.unsubscribe(:invalid) }.to raise_error KeyError
22
+ end
23
+ end
24
+ end
25
+
26
+ shared_examples 'an incoming protocol message bus' do
27
+ it_behaves_like 'a protocol message bus' do
28
+ let(:msgbus) { subject.__incoming_protocol_msgbus__ }
29
+ end
30
+ end
31
+
32
+ shared_examples 'an outgoing protocol message bus' do
33
+ it_behaves_like 'a protocol message bus' do
34
+ let(:msgbus) { subject.__outgoing_protocol_msgbus__ }
35
+ end
36
+ end
@@ -0,0 +1,20 @@
1
+ # Output the message to the console
2
+ # Useful for debugging as clearly visible, and name is not used anywhere else in library as opposed to debug or puts
3
+ def console(message)
4
+ puts "\033[31m[#{Time.now.strftime('%H:%M:%S.%L')}]\033[0m \033[33m#{message}\033[0m"
5
+ end
6
+
7
+ require 'webmock/rspec'
8
+
9
+ require 'ably'
10
+
11
+ require 'support/api_helper'
12
+ require 'support/private_api_formatter'
13
+ require 'support/protocol_helper'
14
+ require 'support/random_helper'
15
+
16
+ require 'rspec_config'
17
+
18
+ # EM Helper must be loaded after rspec_config to ensure around block occurs before RSpec retry
19
+ require 'support/event_machine_helper'
20
+ require 'support/rest_testapp_before_retry'
@@ -0,0 +1,60 @@
1
+ require 'support/test_app'
2
+
3
+ module ApiHelper
4
+ def app_id
5
+ TestApp.instance.app_id
6
+ end
7
+
8
+ def key_id
9
+ TestApp.instance.key_id
10
+ end
11
+
12
+ def key_secret
13
+ api_key.split(':')[1]
14
+ end
15
+
16
+ def api_key
17
+ TestApp.instance.api_key
18
+ end
19
+
20
+ def restricted_api_key
21
+ TestApp.instance.restricted_api_key
22
+ end
23
+
24
+ def environment
25
+ TestApp.instance.environment
26
+ end
27
+
28
+ def reload_test_app
29
+ WebMock.disable!
30
+ TestApp.reload
31
+ end
32
+
33
+ def encode64(text)
34
+ Base64.encode64(text).gsub("\n", '')
35
+ end
36
+ end
37
+
38
+ RSpec.configure do |config|
39
+ config.include ApiHelper
40
+
41
+ config.before(:suite) do
42
+ WebMock.disable!
43
+ end
44
+
45
+ config.after(:suite) do
46
+ WebMock.disable!
47
+ TestApp.instance.delete
48
+ end
49
+ end
50
+
51
+ module ApiPreloader
52
+ def self.included(mod)
53
+ WebMock.disable!
54
+ TestApp.instance.api_key
55
+ end
56
+
57
+ RSpec.configure do |config|
58
+ config.include self, :file_path => %r(spec/acceptance)
59
+ end
60
+ end
@@ -0,0 +1,104 @@
1
+ require 'eventmachine'
2
+ require 'rspec'
3
+ require 'timeout'
4
+
5
+ module RSpec
6
+ module EventMachine
7
+ extend self
8
+
9
+ DEFAULT_TIMEOUT = 10
10
+
11
+ def run_reactor(timeout = DEFAULT_TIMEOUT)
12
+ Timeout::timeout(timeout + 0.5) do
13
+ ::EventMachine.run do
14
+ yield
15
+ end
16
+ end
17
+ end
18
+
19
+ def stop_reactor
20
+ ::EventMachine.next_tick do
21
+ ::EventMachine.stop
22
+ end
23
+ end
24
+
25
+ # Allows multiple Deferrables to be passed in and calls the provided block when
26
+ # all success callbacks have completed
27
+ def when_all(*deferrables)
28
+ raise ArgumentError, 'Block required' unless block_given?
29
+
30
+ options = if deferrables.last.kind_of?(Hash)
31
+ deferrables.pop
32
+ else
33
+ {}
34
+ end
35
+
36
+ successful_deferrables = {}
37
+
38
+ deferrables.each do |deferrable|
39
+ deferrable.callback do
40
+ successful_deferrables[deferrable.object_id] = true
41
+ if successful_deferrables.keys.sort == deferrables.map(&:object_id).sort
42
+ if options[:and_wait]
43
+ ::EventMachine.add_timer(options[:and_wait]) { yield }
44
+ else
45
+ yield
46
+ end
47
+ end
48
+ end
49
+
50
+ deferrable.errback do |error|
51
+ raise RuntimeError, "Deferrable failed: #{error.message}"
52
+ end
53
+ end
54
+ end
55
+
56
+ def wait_until(condition_block, &block)
57
+ raise ArgumentError, 'Block required' unless block_given?
58
+
59
+ yield if condition_block.call
60
+ ::EventMachine.add_timer(0.1) do
61
+ wait_until condition_block, &block
62
+ end
63
+ end
64
+ end
65
+ end
66
+
67
+ RSpec.configure do |config|
68
+ config.before(:context, :event_machine) do |context|
69
+ context.class.class_eval do
70
+ include RSpec::EventMachine
71
+ end
72
+ end
73
+
74
+ # Run the test block wrapped in an EventMachine reactor that has a configured timeout.
75
+ # As RSpec does not provide an API to wrap blocks, accessing the instance variables is required.
76
+ # Note, if you start a reactor and simply run the example with example#run then the example
77
+ # will run and not wait for the reactor to stop thus triggering after callbacks prematurely.
78
+ #
79
+ config.around(:example, :event_machine) do |example|
80
+ timeout = if example.metadata[:em_timeout].is_a?(Numeric)
81
+ example.metadata[:em_timeout]
82
+ else
83
+ RSpec::EventMachine::DEFAULT_TIMEOUT
84
+ end
85
+
86
+ example_block = example.example.instance_variable_get('@example_block')
87
+ example_group_instance = example.example.instance_variable_get('@example_group_instance')
88
+
89
+ event_machine_block = Proc.new do
90
+ RSpec::EventMachine.run_reactor(timeout) do
91
+ example_group_instance.instance_exec(example, &example_block)
92
+ end
93
+ end
94
+
95
+ example.example.instance_variable_set('@example_block', event_machine_block)
96
+
97
+ example.run
98
+ end
99
+
100
+ config.before(:example) do
101
+ # Ensure EventMachine shutdown hooks are deregistered for every test
102
+ EventMachine.instance_variable_set '@tails', []
103
+ end
104
+ end
@@ -0,0 +1,118 @@
1
+ module Ably
2
+ module RSpec
3
+ # Generate Markdown Specification from the RSpec public API tests
4
+ #
5
+ class MarkdownSpecFormatter
6
+ ::RSpec::Core::Formatters.register self, :start, :close,
7
+ :example_group_started, :example_group_finished,
8
+ :example_passed, :example_failed, :example_pending,
9
+ :dump_summary
10
+
11
+ def initialize(output)
12
+ @output = if documenting_rest_only?
13
+ File.open(File.expand_path('../../../../../../SPEC.md', __FILE__), 'w')
14
+ else
15
+ File.open(File.expand_path('../../../SPEC.md', __FILE__), 'w')
16
+ end
17
+
18
+ @indent = 0
19
+ @passed = 0
20
+ @pending = 0
21
+ @failed = 0
22
+ end
23
+
24
+ def start(notification)
25
+ puts "\n\e[33m --> Creating SPEC.md <--\e[0m\n"
26
+ scope = if defined?(Ably::Realtime)
27
+ 'Real-time & REST'
28
+ else
29
+ 'REST'
30
+ end
31
+ output.write "# Ably #{scope} Client Library #{Ably::VERSION} Specification\n"
32
+ end
33
+
34
+ def close(notification)
35
+ output.close
36
+ end
37
+
38
+ def example_group_started(notification)
39
+ output.write "#{indent_prefix}#{notification.group.description}\n"
40
+ output.write "_(see #{heading_location_path(notification)})_\n" if indent == 0
41
+ @indent += 1
42
+ end
43
+
44
+ def example_group_finished(notification)
45
+ @indent -= 1
46
+ end
47
+
48
+ def example_passed(notification)
49
+ unless notification.example.metadata[:api_private]
50
+ output.write "#{indent_prefix}#{example_name_and_link(notification)}\n"
51
+ @passed += 1
52
+ end
53
+ end
54
+
55
+ def example_failed(notification)
56
+ unless notification.example.metadata[:api_private]
57
+ output.write "#{indent_prefix}FAILED: ~~#{example_name_and_link(notification)}~~\n"
58
+ @failed += 1
59
+ end
60
+ end
61
+
62
+ def example_pending(notification)
63
+ unless notification.example.metadata[:api_private]
64
+ output.write "#{indent_prefix}PENDING: *#{example_name_and_link(notification)}*\n"
65
+ @pending += 1
66
+ end
67
+ end
68
+
69
+ def dump_summary(notification)
70
+ output.write <<-EOF.gsub(' ', '')
71
+
72
+ -------
73
+
74
+ ## Test summary
75
+
76
+ * Passing tests: #{@passed}
77
+ * Pending tests: #{@pending}
78
+ * Failing tests: #{@failed}
79
+ EOF
80
+ end
81
+
82
+ private
83
+ attr_reader :output, :indent
84
+
85
+ def documenting_rest_only?
86
+ File.exists?(File.expand_path('../../../../../../ably-rest.gemspec', __FILE__))
87
+ end
88
+
89
+ def example_name_and_link(notification)
90
+ "[#{notification.example.metadata[:description]}](#{path_for(notification.example.location).gsub(/\:(\d+)/, '#L\1')})"
91
+ end
92
+
93
+ def heading_location_path(notification)
94
+ "[#{notification.group.location.gsub(/\:(\d+)/, '').gsub(%r{^\.\/}, '')}](#{path_for(notification.group.location).gsub(/\:(\d+)/, '')})"
95
+ end
96
+
97
+ def path_for(location)
98
+ if documenting_rest_only?
99
+ "https://github.com/ably/ably-ruby/tree/#{submodule_sha}#{location.gsub(%r{^\./lib/submodules/ably-ruby}, '')}"
100
+ else
101
+ location
102
+ end
103
+ end
104
+
105
+ def submodule_sha
106
+ @sha ||= `git ls-tree HEAD:lib/submodules grep ably-ruby`[/^\w+\s+\w+\s+(\w+)/, 1]
107
+ end
108
+
109
+ def indent_prefix
110
+ if indent > 0
111
+ "#{' ' * indent}* "
112
+ else
113
+ "\n### "
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,36 @@
1
+ module Ably::RSpec
2
+ # PrivateApiFormatter is an RSpec Formatter that prefixes all tests that are part of a Private API with '(private)'
3
+ #
4
+ # Private API methods are tested for this library, but every implementation of the Ably client library
5
+ # will likely be different and thus the private API method tests are not shared.
6
+ #
7
+ # Filter private API tests with `rspec --tag ~api_private`
8
+ #
9
+ class PrivateApiFormatter
10
+ ::RSpec::Core::Formatters.register self, :example_started
11
+
12
+ def initialize(output)
13
+ @output = output
14
+ end
15
+
16
+ def example_started(notification)
17
+ if notification.example.metadata[:api_private]
18
+ notification.example.metadata[:description] = "#{yellow('(private)')} #{green(notification.example.metadata[:description])}"
19
+ end
20
+ end
21
+
22
+ private
23
+ def colorize(color_code, string)
24
+ "\e[#{color_code}m#{string}\e[0m"
25
+ end
26
+
27
+ def yellow(string)
28
+ colorize(33, string)
29
+ end
30
+
31
+
32
+ def green(string)
33
+ colorize(32, string)
34
+ end
35
+ end
36
+ end