ably 0.7.0 → 0.7.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +2 -2
- data/Rakefile +2 -0
- data/SPEC.md +230 -194
- data/ably.gemspec +2 -0
- data/lib/ably/auth.rb +7 -5
- data/lib/ably/models/idiomatic_ruby_wrapper.rb +5 -7
- data/lib/ably/models/paginated_resource.rb +14 -21
- data/lib/ably/models/protocol_message.rb +1 -1
- data/lib/ably/modules/ably.rb +4 -0
- data/lib/ably/modules/async_wrapper.rb +2 -2
- data/lib/ably/modules/channels_collection.rb +31 -8
- data/lib/ably/modules/conversions.rb +10 -0
- data/lib/ably/modules/enum.rb +2 -3
- data/lib/ably/modules/state_emitter.rb +8 -8
- data/lib/ably/modules/state_machine.rb +7 -3
- data/lib/ably/realtime/channel.rb +6 -5
- data/lib/ably/realtime/channel/channel_manager.rb +11 -10
- data/lib/ably/realtime/channel/channel_state_machine.rb +10 -9
- data/lib/ably/realtime/channels.rb +3 -0
- data/lib/ably/realtime/client/incoming_message_dispatcher.rb +11 -1
- data/lib/ably/realtime/connection.rb +55 -16
- data/lib/ably/realtime/connection/connection_manager.rb +25 -8
- data/lib/ably/realtime/connection/connection_state_machine.rb +9 -9
- data/lib/ably/realtime/connection/websocket_transport.rb +2 -2
- data/lib/ably/realtime/presence.rb +16 -17
- data/lib/ably/util/crypto.rb +1 -1
- data/lib/ably/version.rb +1 -1
- data/spec/acceptance/realtime/channel_history_spec.rb +6 -5
- data/spec/acceptance/realtime/connection_failures_spec.rb +103 -27
- data/spec/acceptance/realtime/connection_spec.rb +81 -17
- data/spec/acceptance/realtime/presence_spec.rb +82 -30
- data/spec/acceptance/rest/auth_spec.rb +22 -19
- data/spec/acceptance/rest/client_spec.rb +4 -4
- data/spec/acceptance/rest/presence_spec.rb +12 -6
- data/spec/rspec_config.rb +9 -0
- data/spec/shared/model_behaviour.rb +1 -1
- data/spec/spec_helper.rb +4 -1
- data/spec/support/event_machine_helper.rb +26 -37
- data/spec/support/markdown_spec_formatter.rb +96 -68
- data/spec/support/rest_testapp_before_retry.rb +15 -0
- data/spec/support/test_app.rb +4 -0
- data/spec/unit/models/idiomatic_ruby_wrapper_spec.rb +20 -2
- data/spec/unit/models/message_spec.rb +1 -1
- data/spec/unit/models/paginated_resource_spec.rb +15 -1
- data/spec/unit/modules/enum_spec.rb +10 -0
- data/spec/unit/realtime/channels_spec.rb +30 -0
- data/spec/unit/rest/channels_spec.rb +30 -0
- metadata +101 -35
- checksums.yaml +0 -7
@@ -5,17 +5,19 @@ describe Ably::Auth do
|
|
5
5
|
include Ably::Modules::Conversions
|
6
6
|
|
7
7
|
def hmac_for(token_request, secret)
|
8
|
-
|
8
|
+
ruby_named_token_request = Ably::Models::IdiomaticRubyWrapper.new(token_request)
|
9
|
+
|
10
|
+
text = [
|
9
11
|
:id,
|
10
12
|
:ttl,
|
11
13
|
:capability,
|
12
14
|
:client_id,
|
13
15
|
:timestamp,
|
14
16
|
:nonce
|
15
|
-
|
17
|
+
].map { |key| "#{ruby_named_token_request[key]}\n" }.join("")
|
16
18
|
|
17
19
|
encode64(
|
18
|
-
|
20
|
+
OpenSSL::HMAC.digest(OpenSSL::Digest::SHA256.new, secret, text)
|
19
21
|
)
|
20
22
|
end
|
21
23
|
|
@@ -38,7 +40,7 @@ describe Ably::Auth do
|
|
38
40
|
else
|
39
41
|
JSON.parse(request.body)
|
40
42
|
end
|
41
|
-
body[key
|
43
|
+
body[convert_to_mixed_case(key)].to_s == val.to_s
|
42
44
|
end
|
43
45
|
|
44
46
|
def serialize(object, protocol)
|
@@ -50,7 +52,7 @@ describe Ably::Auth do
|
|
50
52
|
end
|
51
53
|
|
52
54
|
it 'has immutable options' do
|
53
|
-
expect { auth.options['key_id'] = 'new_id' }.to raise_error RuntimeError, /can't modify frozen
|
55
|
+
expect { auth.options['key_id'] = 'new_id' }.to raise_error RuntimeError, /can't modify frozen.*Hash/
|
54
56
|
end
|
55
57
|
|
56
58
|
describe '#request_token' do
|
@@ -70,7 +72,7 @@ describe Ably::Auth do
|
|
70
72
|
end
|
71
73
|
|
72
74
|
%w(client_id capability nonce timestamp ttl).each do |option|
|
73
|
-
context "option :#{option}", :webmock do
|
75
|
+
context "with option :#{option}", :webmock do
|
74
76
|
let(:random) { random_int_str }
|
75
77
|
let(:options) { { option.to_sym => random } }
|
76
78
|
|
@@ -88,7 +90,7 @@ describe Ably::Auth do
|
|
88
90
|
|
89
91
|
before { auth.request_token options }
|
90
92
|
|
91
|
-
it 'overrides default' do
|
93
|
+
it 'overrides default and uses camelCase notation for all attributes' do
|
92
94
|
expect(request_token_stub).to have_been_requested
|
93
95
|
end
|
94
96
|
end
|
@@ -134,7 +136,7 @@ describe Ably::Auth do
|
|
134
136
|
context 'without :query_time option' do
|
135
137
|
let(:options) { { query_time: false } }
|
136
138
|
|
137
|
-
it '
|
139
|
+
it 'does not query the server for the time' do
|
138
140
|
expect(client).to_not receive(:time)
|
139
141
|
auth.request_token(options)
|
140
142
|
end
|
@@ -356,25 +358,25 @@ describe Ably::Auth do
|
|
356
358
|
subject { auth.create_token_request(options) }
|
357
359
|
|
358
360
|
it 'uses the key ID from the client' do
|
359
|
-
expect(subject[
|
361
|
+
expect(subject['id']).to eql(key_id)
|
360
362
|
end
|
361
363
|
|
362
364
|
it 'uses the default TTL' do
|
363
|
-
expect(subject[
|
365
|
+
expect(subject['ttl']).to eql(Ably::Models::Token::DEFAULTS[:ttl])
|
364
366
|
end
|
365
367
|
|
366
368
|
it 'uses the default capability' do
|
367
|
-
expect(subject[
|
369
|
+
expect(subject['capability']).to eql(Ably::Models::Token::DEFAULTS[:capability].to_json)
|
368
370
|
end
|
369
371
|
|
370
372
|
context 'the nonce' do
|
371
373
|
it 'is unique for every request' do
|
372
|
-
unique_nonces = 100.times.map { auth.create_token_request[
|
374
|
+
unique_nonces = 100.times.map { auth.create_token_request['nonce'] }
|
373
375
|
expect(unique_nonces.uniq.length).to eql(100)
|
374
376
|
end
|
375
377
|
|
376
378
|
it 'is at least 16 characters' do
|
377
|
-
expect(subject[
|
379
|
+
expect(subject['nonce'].length).to be >= 16
|
378
380
|
end
|
379
381
|
end
|
380
382
|
|
@@ -385,7 +387,7 @@ describe Ably::Auth do
|
|
385
387
|
options[attribute.to_sym] = option_value
|
386
388
|
end
|
387
389
|
it "overrides default" do
|
388
|
-
expect(subject[attribute
|
390
|
+
expect(subject[convert_to_mixed_case(attribute)].to_s).to eql(option_value.to_s)
|
389
391
|
end
|
390
392
|
end
|
391
393
|
end
|
@@ -394,8 +396,9 @@ describe Ably::Auth do
|
|
394
396
|
let(:options) { { nonce: 'valid', is_not_used_by_token_request: 'invalid' } }
|
395
397
|
specify 'are ignored' do
|
396
398
|
expect(subject.keys).to_not include(:is_not_used_by_token_request)
|
397
|
-
expect(subject.keys).
|
398
|
-
expect(subject
|
399
|
+
expect(subject.keys).to_not include(convert_to_mixed_case(:is_not_used_by_token_request))
|
400
|
+
expect(subject.keys).to include('nonce')
|
401
|
+
expect(subject['nonce']).to eql('valid')
|
399
402
|
end
|
400
403
|
end
|
401
404
|
|
@@ -417,7 +420,7 @@ describe Ably::Auth do
|
|
417
420
|
|
418
421
|
it 'queries the server for the timestamp' do
|
419
422
|
expect(client).to receive(:time).and_return(time)
|
420
|
-
expect(subject[
|
423
|
+
expect(subject['timestamp']).to eql(time.to_i)
|
421
424
|
end
|
422
425
|
end
|
423
426
|
|
@@ -426,7 +429,7 @@ describe Ably::Auth do
|
|
426
429
|
let(:options) { { timestamp: token_request_time } }
|
427
430
|
|
428
431
|
it 'uses the provided timestamp in the token request' do
|
429
|
-
expect(subject[
|
432
|
+
expect(subject['timestamp']).to eql(token_request_time.to_i)
|
430
433
|
end
|
431
434
|
end
|
432
435
|
|
@@ -444,7 +447,7 @@ describe Ably::Auth do
|
|
444
447
|
|
445
448
|
it 'generates a valid HMAC' do
|
446
449
|
hmac = hmac_for(options, key_secret)
|
447
|
-
expect(subject[
|
450
|
+
expect(subject['mac']).to eql(hmac)
|
448
451
|
end
|
449
452
|
end
|
450
453
|
end
|
@@ -54,12 +54,12 @@ describe Ably::Rest::Client do
|
|
54
54
|
|
55
55
|
it 'creates a new token automatically when the old token expires' do
|
56
56
|
expect { client.channel('channel_name').publish('event', 'message') }.to change { client.auth.current_token }
|
57
|
-
expect(client.auth.current_token.client_id).to eql(token_request_1[
|
57
|
+
expect(client.auth.current_token.client_id).to eql(token_request_1['clientId'])
|
58
58
|
|
59
59
|
sleep 1
|
60
60
|
|
61
61
|
expect { client.channel('channel_name').publish('event', 'message') }.to change { client.auth.current_token }
|
62
|
-
expect(client.auth.current_token.client_id).to eql(token_request_2[
|
62
|
+
expect(client.auth.current_token.client_id).to eql(token_request_2['clientId'])
|
63
63
|
end
|
64
64
|
end
|
65
65
|
|
@@ -68,12 +68,12 @@ describe Ably::Rest::Client do
|
|
68
68
|
|
69
69
|
it 'reuses the existing token for every request' do
|
70
70
|
expect { client.channel('channel_name').publish('event', 'message') }.to change { client.auth.current_token }
|
71
|
-
expect(client.auth.current_token.client_id).to eql(token_request_1[
|
71
|
+
expect(client.auth.current_token.client_id).to eql(token_request_1['clientId'])
|
72
72
|
|
73
73
|
sleep 1
|
74
74
|
|
75
75
|
expect { client.channel('channel_name').publish('event', 'message') }.to_not change { client.auth.current_token }
|
76
|
-
expect(client.auth.current_token.client_id).to eql(token_request_1[
|
76
|
+
expect(client.auth.current_token.client_id).to eql(token_request_1['clientId'])
|
77
77
|
end
|
78
78
|
end
|
79
79
|
end
|
@@ -18,13 +18,13 @@ describe Ably::Rest::Presence do
|
|
18
18
|
end
|
19
19
|
|
20
20
|
context 'tested against presence fixture data set up in test app' do
|
21
|
-
before(:context) do
|
22
|
-
# When this test is run as a part of a test suite, the presence data injected in the test app may have expired
|
23
|
-
WebMock.disable!
|
24
|
-
TestApp.reload
|
25
|
-
end
|
26
|
-
|
27
21
|
describe '#get' do
|
22
|
+
before(:context) do
|
23
|
+
# When this test is run as a part of a test suite, the presence data injected in the test app may have expired
|
24
|
+
WebMock.disable!
|
25
|
+
TestApp.reload
|
26
|
+
end
|
27
|
+
|
28
28
|
let(:channel) { client.channel('persisted:presence_fixtures') }
|
29
29
|
let(:presence) { channel.presence.get }
|
30
30
|
|
@@ -52,6 +52,12 @@ describe Ably::Rest::Presence do
|
|
52
52
|
end
|
53
53
|
|
54
54
|
describe '#history' do
|
55
|
+
before(:context) do
|
56
|
+
# When this test is run as a part of a test suite, the presence data injected in the test app may have expired
|
57
|
+
WebMock.disable!
|
58
|
+
TestApp.reload
|
59
|
+
end
|
60
|
+
|
55
61
|
let(:channel) { client.channel('persisted:presence_fixtures') }
|
56
62
|
let(:presence_history) { channel.presence.history }
|
57
63
|
|
data/spec/rspec_config.rb
CHANGED
@@ -5,6 +5,8 @@
|
|
5
5
|
#
|
6
6
|
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
7
7
|
|
8
|
+
require 'rspec/retry'
|
9
|
+
|
8
10
|
RSpec.configure do |config|
|
9
11
|
config.run_all_when_everything_filtered = true
|
10
12
|
config.filter_run :focus
|
@@ -45,4 +47,11 @@ RSpec.configure do |config|
|
|
45
47
|
end
|
46
48
|
|
47
49
|
config.add_formatter Ably::RSpec::PrivateApiFormatter
|
50
|
+
|
51
|
+
if ENV['RSPEC_RETRY']
|
52
|
+
puts 'Running tests using RSpec retry'
|
53
|
+
config.verbose_retry = true # show retry status in spec process
|
54
|
+
config.default_retry_count = 3
|
55
|
+
config.default_sleep_interval = 2
|
56
|
+
end
|
48
57
|
end
|
@@ -74,7 +74,7 @@ shared_examples 'a model' do |shared_options = {}|
|
|
74
74
|
let(:model_options) { { channel: 'name' } }
|
75
75
|
|
76
76
|
it 'prevents changes' do
|
77
|
-
expect { model.hash[:channel] = 'new' }.to raise_error RuntimeError, /can't modify frozen
|
77
|
+
expect { model.hash[:channel] = 'new' }.to raise_error RuntimeError, /can't modify frozen.*Hash/
|
78
78
|
end
|
79
79
|
|
80
80
|
it 'dups options' do
|
data/spec/spec_helper.rb
CHANGED
@@ -9,9 +9,12 @@ require 'webmock/rspec'
|
|
9
9
|
require 'ably'
|
10
10
|
|
11
11
|
require 'support/api_helper'
|
12
|
-
require 'support/event_machine_helper'
|
13
12
|
require 'support/private_api_formatter'
|
14
13
|
require 'support/protocol_helper'
|
15
14
|
require 'support/random_helper'
|
16
15
|
|
17
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'
|
@@ -6,7 +6,7 @@ module RSpec
|
|
6
6
|
module EventMachine
|
7
7
|
extend self
|
8
8
|
|
9
|
-
DEFAULT_TIMEOUT =
|
9
|
+
DEFAULT_TIMEOUT = 10
|
10
10
|
|
11
11
|
def run_reactor(timeout = DEFAULT_TIMEOUT)
|
12
12
|
Timeout::timeout(timeout + 0.5) do
|
@@ -24,8 +24,8 @@ module RSpec
|
|
24
24
|
|
25
25
|
# Allows multiple Deferrables to be passed in and calls the provided block when
|
26
26
|
# all success callbacks have completed
|
27
|
-
def when_all(*deferrables
|
28
|
-
raise
|
27
|
+
def when_all(*deferrables)
|
28
|
+
raise ArgumentError, 'Block required' unless block_given?
|
29
29
|
|
30
30
|
options = if deferrables.last.kind_of?(Hash)
|
31
31
|
deferrables.pop
|
@@ -40,9 +40,9 @@ module RSpec
|
|
40
40
|
successful_deferrables[deferrable.object_id] = true
|
41
41
|
if successful_deferrables.keys.sort == deferrables.map(&:object_id).sort
|
42
42
|
if options[:and_wait]
|
43
|
-
::EventMachine.add_timer(options[:and_wait]) {
|
43
|
+
::EventMachine.add_timer(options[:and_wait]) { yield }
|
44
44
|
else
|
45
|
-
|
45
|
+
yield
|
46
46
|
end
|
47
47
|
end
|
48
48
|
end
|
@@ -52,6 +52,15 @@ module RSpec
|
|
52
52
|
end
|
53
53
|
end
|
54
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
|
55
64
|
end
|
56
65
|
end
|
57
66
|
|
@@ -62,32 +71,11 @@ RSpec.configure do |config|
|
|
62
71
|
end
|
63
72
|
end
|
64
73
|
|
65
|
-
#
|
66
|
-
# does not
|
67
|
-
#
|
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.
|
68
78
|
#
|
69
|
-
# As there is no public API to inject around blocks correctly without calling the after blocks,
|
70
|
-
# we have to monkey patch the run_after_example method at https://github.com/rspec/rspec-core/blob/master/lib/rspec/core/example.rb#L376
|
71
|
-
# so that it does not run until we explicitly call it once the EventMachine reactor loop is finished.
|
72
|
-
#
|
73
|
-
def patch_example_block_with_surrounding_eventmachine_reactor(example)
|
74
|
-
example.example.class.class_eval do
|
75
|
-
alias_method :run_after_example_original, :run_after_example
|
76
|
-
public :run_after_example_original
|
77
|
-
|
78
|
-
# prevent after hooks being run for example until EventMachine reactor has finished
|
79
|
-
def run_after_example; end
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
def remove_patch_example_block(example)
|
84
|
-
example.example.class.class_eval do
|
85
|
-
remove_method :run_after_example
|
86
|
-
alias_method :run_after_example, :run_after_example_original
|
87
|
-
remove_method :run_after_example_original
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
79
|
config.around(:example, :event_machine) do |example|
|
92
80
|
timeout = if example.metadata[:em_timeout].is_a?(Numeric)
|
93
81
|
example.metadata[:em_timeout]
|
@@ -95,17 +83,18 @@ RSpec.configure do |config|
|
|
95
83
|
RSpec::EventMachine::DEFAULT_TIMEOUT
|
96
84
|
end
|
97
85
|
|
98
|
-
|
86
|
+
example_block = example.example.instance_variable_get('@example_block')
|
87
|
+
example_group_instance = example.example.instance_variable_get('@example_group_instance')
|
99
88
|
|
100
|
-
|
89
|
+
event_machine_block = Proc.new do
|
101
90
|
RSpec::EventMachine.run_reactor(timeout) do
|
102
|
-
example
|
103
|
-
raise example.exception if example.exception
|
91
|
+
example_group_instance.instance_exec(example, &example_block)
|
104
92
|
end
|
105
|
-
ensure
|
106
|
-
example.example.run_after_example_original
|
107
|
-
remove_patch_example_block example
|
108
93
|
end
|
94
|
+
|
95
|
+
example.example.instance_variable_set('@example_block', event_machine_block)
|
96
|
+
|
97
|
+
example.run
|
109
98
|
end
|
110
99
|
|
111
100
|
config.before(:example) do
|
@@ -1,89 +1,117 @@
|
|
1
|
-
module Ably
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
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
|
17
23
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
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
|
22
33
|
|
23
|
-
|
24
|
-
|
25
|
-
|
34
|
+
def close(notification)
|
35
|
+
output.close
|
36
|
+
end
|
26
37
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
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
|
32
43
|
|
33
|
-
|
34
|
-
|
35
|
-
|
44
|
+
def example_group_finished(notification)
|
45
|
+
@indent -= 1
|
46
|
+
end
|
36
47
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
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
|
41
53
|
end
|
42
|
-
end
|
43
54
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
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
|
48
60
|
end
|
49
|
-
end
|
50
61
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
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
|
55
67
|
end
|
56
|
-
end
|
57
68
|
|
58
|
-
|
59
|
-
|
69
|
+
def dump_summary(notification)
|
70
|
+
output.write <<-EOF.gsub(' ', '')
|
60
71
|
|
61
|
-
|
72
|
+
-------
|
62
73
|
|
63
|
-
|
74
|
+
## Test summary
|
64
75
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
76
|
+
* Passing tests: #{@passed}
|
77
|
+
* Pending tests: #{@pending}
|
78
|
+
* Failing tests: #{@failed}
|
79
|
+
EOF
|
80
|
+
end
|
70
81
|
|
71
|
-
|
72
|
-
|
82
|
+
private
|
83
|
+
attr_reader :output, :indent
|
73
84
|
|
74
|
-
|
75
|
-
|
76
|
-
|
85
|
+
def documenting_rest_only?
|
86
|
+
File.exists?(File.expand_path('../../../../../../ably-rest.gemspec', __FILE__))
|
87
|
+
end
|
77
88
|
|
78
|
-
|
79
|
-
|
80
|
-
|
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
|
81
108
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
109
|
+
def indent_prefix
|
110
|
+
if indent > 0
|
111
|
+
"#{' ' * indent}* "
|
112
|
+
else
|
113
|
+
"\n### "
|
114
|
+
end
|
87
115
|
end
|
88
116
|
end
|
89
117
|
end
|