pause 0.4.0 → 0.5.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.
- checksums.yaml +4 -4
- data/.github/workflows/rspec.yml +31 -0
- data/.github/workflows/rubocop.yml +31 -0
- data/.gitignore +0 -1
- data/.rubocop.yml +28 -0
- data/.rubocop_todo.yml +192 -0
- data/Gemfile +16 -1
- data/Gemfile.lock +145 -0
- data/Guardfile +7 -6
- data/LICENSE.txt +1 -1
- data/README.md +50 -30
- data/Rakefile +11 -10
- data/bin/spec +14 -0
- data/lib/pause/action.rb +6 -5
- data/lib/pause/analyzer.rb +4 -0
- data/lib/pause/configuration.rb +3 -1
- data/lib/pause/helper/timing.rb +2 -0
- data/lib/pause/logger.rb +12 -5
- data/lib/pause/rate_limited_event.rb +2 -1
- data/lib/pause/redis/adapter.rb +12 -10
- data/lib/pause/redis/sharded_adapter.rb +3 -2
- data/lib/pause/version.rb +3 -1
- data/lib/pause.rb +14 -11
- data/pause.gemspec +14 -17
- data/spec/pause/action_spec.rb +48 -42
- data/spec/pause/analyzer_spec.rb +9 -10
- data/spec/pause/configuration_spec.rb +6 -5
- data/spec/pause/logger_spec.rb +25 -0
- data/spec/pause/pause_spec.rb +6 -4
- data/spec/pause/redis/adapter_spec.rb +54 -35
- data/spec/pause/redis/sharded_adapter_spec.rb +10 -10
- data/spec/spec_helper.rb +11 -7
- metadata +19 -120
- data/.travis.yml +0 -14
data/spec/pause/pause_spec.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
|
3
5
|
RSpec.describe Pause do
|
@@ -5,8 +7,8 @@ RSpec.describe Pause do
|
|
5
7
|
let(:configuration) { Pause::Configuration.new }
|
6
8
|
|
7
9
|
before do
|
8
|
-
|
9
|
-
allow(
|
10
|
+
described_class.adapter = nil
|
11
|
+
allow(described_class).to receive(:config).and_return(configuration)
|
10
12
|
configuration.configure { |c| c.sharded = sharded }
|
11
13
|
end
|
12
14
|
|
@@ -14,7 +16,7 @@ RSpec.describe Pause do
|
|
14
16
|
let(:sharded) { true }
|
15
17
|
|
16
18
|
it 'is a ShardedAdapter' do
|
17
|
-
expect(
|
19
|
+
expect(described_class.adapter).to be_a(Pause::Redis::ShardedAdapter)
|
18
20
|
end
|
19
21
|
end
|
20
22
|
|
@@ -22,7 +24,7 @@ RSpec.describe Pause do
|
|
22
24
|
let(:sharded) { false }
|
23
25
|
|
24
26
|
it 'is an Adapter' do
|
25
|
-
expect(
|
27
|
+
expect(described_class.adapter).to be_a(Pause::Redis::Adapter)
|
26
28
|
end
|
27
29
|
end
|
28
30
|
end
|
@@ -1,59 +1,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
require 'date'
|
3
5
|
require 'timecop'
|
4
6
|
|
5
7
|
describe Pause::Redis::Adapter do
|
6
|
-
|
7
8
|
let(:resolution) { 10 }
|
9
|
+
let(:adapter) { described_class.new(Pause.config) }
|
10
|
+
let(:redis_conn) { adapter.send(:redis) }
|
8
11
|
let(:history) { 60 }
|
9
12
|
let(:configuration) { Pause::Configuration.new }
|
10
13
|
|
11
14
|
before do
|
12
15
|
allow(Pause).to receive(:config).and_return(configuration)
|
13
|
-
allow(Pause.config).to
|
14
|
-
allow(Pause.config).to receive(:history).and_return(history)
|
16
|
+
allow(Pause.config).to receive_messages(resolution: resolution, history: history)
|
15
17
|
redis_conn.flushall
|
16
18
|
end
|
17
19
|
|
18
|
-
let(:adapter) { Pause::Redis::Adapter.new(Pause.config) }
|
19
|
-
let(:redis_conn) { adapter.send(:redis) }
|
20
|
-
|
21
20
|
describe '#increment' do
|
22
21
|
let(:scope) { 'blah' }
|
23
22
|
let(:identifier) { '213213' }
|
24
|
-
let(:tracked_key) {
|
23
|
+
let(:tracked_key) { "i:#{scope}:|#{identifier}|" }
|
24
|
+
let(:now) { Time.now.to_i }
|
25
25
|
|
26
|
-
it '
|
26
|
+
it 'adds key to a redis set' do
|
27
27
|
adapter.increment(scope, identifier, Time.now.to_i)
|
28
|
-
set = redis_conn.zrange(tracked_key, 0, -1, :
|
29
|
-
expect(set).
|
30
|
-
expect(set.size).to
|
31
|
-
expect(set[0].size).to
|
28
|
+
set = redis_conn.zrange(tracked_key, 0, -1, with_scores: true)
|
29
|
+
expect(set).not_to be_empty
|
30
|
+
expect(set.size).to be(1)
|
31
|
+
expect(set[0].size).to be(2)
|
32
|
+
end
|
33
|
+
|
34
|
+
describe 'when increment is called' do
|
35
|
+
let(:redis_conn_double) { instance_double(Redis) }
|
36
|
+
|
37
|
+
before do
|
38
|
+
allow(adapter).to receive(:with_multi).and_yield(redis_conn_double)
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'calls zincrby on the redis connection' do
|
42
|
+
allow(redis_conn_double).to receive(:expire)
|
43
|
+
expect(redis_conn_double).to receive(:zincrby)
|
44
|
+
|
45
|
+
adapter.increment(scope, identifier, Time.now.to_i)
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'calls expire on the redis key' do
|
49
|
+
expect(redis_conn_double).to receive(:expire).with(tracked_key, history)
|
50
|
+
allow(redis_conn_double).to receive(:zincrby)
|
51
|
+
|
52
|
+
adapter.increment(scope, identifier, Time.now.to_i)
|
53
|
+
end
|
32
54
|
end
|
33
55
|
|
34
56
|
context 'removing two elements' do
|
35
57
|
let(:to_delete) { 2 }
|
36
|
-
let(:
|
58
|
+
let(:period_start) { adapter.period_marker(resolution, now) }
|
59
|
+
let(:period_end) { adapter.period_marker(resolution, now + resolution) }
|
60
|
+
|
61
|
+
around do |example|
|
62
|
+
Timecop.freeze(now) { example.run }
|
63
|
+
end
|
64
|
+
|
37
65
|
before do
|
38
|
-
|
39
|
-
to_delete.times do |t|
|
40
|
-
expect(redis_conn).to receive(:zrem).with(tracked_key, [adapter.period_marker(resolution, time + t)]).once
|
41
|
-
end
|
66
|
+
redis_conn.flushall
|
42
67
|
adapter.time_blocks_to_keep = 1
|
68
|
+
allow(redis_conn).to receive(:zrem).with(tracked_key, [period_start])
|
69
|
+
allow(redis_conn).to receive(:zrem).with(tracked_key, [period_start, period_end])
|
43
70
|
end
|
44
|
-
|
45
|
-
|
71
|
+
|
72
|
+
it 'removes old elements' do
|
73
|
+
adapter.increment(scope, identifier, now.to_i)
|
46
74
|
to_delete.times do |t|
|
47
|
-
next_time =
|
75
|
+
next_time = now + (adapter.resolution + t + 1)
|
48
76
|
adapter.increment(scope, identifier, next_time.to_i)
|
49
77
|
end
|
50
78
|
end
|
51
79
|
end
|
52
|
-
|
53
|
-
it 'sets expiry on key' do
|
54
|
-
expect(redis_conn).to receive(:expire).with(tracked_key, history)
|
55
|
-
adapter.increment(scope, identifier, Time.now.to_i)
|
56
|
-
end
|
57
80
|
end
|
58
81
|
|
59
82
|
describe '#expire_block_list' do
|
@@ -74,21 +97,18 @@ describe Pause::Redis::Adapter do
|
|
74
97
|
|
75
98
|
adapter.expire_block_list(scope)
|
76
99
|
|
77
|
-
expect(redis_conn.zscore('b:|a|', blocked_identifier)).not_to
|
78
|
-
expect(redis_conn.zscore('b:|a|', expired_identifier)).to
|
100
|
+
expect(redis_conn.zscore('b:|a|', blocked_identifier)).not_to be_nil
|
101
|
+
expect(redis_conn.zscore('b:|a|', expired_identifier)).to be_nil
|
79
102
|
end
|
80
103
|
end
|
81
104
|
|
82
|
-
describe '#rate_limit!' do
|
83
|
-
end
|
84
|
-
|
85
105
|
describe '#rate_limited?' do
|
86
106
|
let(:scope) { 'ipn:follow' }
|
87
107
|
let(:identifier) { '123461234' }
|
88
108
|
let(:blocked_key) { "b:#{key}" }
|
89
|
-
let(:ttl) {
|
109
|
+
let(:ttl) { 110_000 }
|
90
110
|
|
91
|
-
it '
|
111
|
+
it 'returns true if blocked' do
|
92
112
|
adapter.rate_limit!(scope, identifier, ttl)
|
93
113
|
expect(adapter.rate_limited?(scope, identifier)).to be true
|
94
114
|
end
|
@@ -128,17 +148,16 @@ describe Pause::Redis::Adapter do
|
|
128
148
|
let(:scope) { 'ipn:follow' }
|
129
149
|
let(:identifier) { '1234' }
|
130
150
|
let(:blocked_key) { "b:|#{scope}|" }
|
131
|
-
let(:ttl) {
|
151
|
+
let(:ttl) { 110_000 }
|
132
152
|
|
133
153
|
it 'saves ip to redis with expiration' do
|
134
154
|
time = Time.now
|
135
155
|
Timecop.freeze time do
|
136
156
|
adapter.rate_limit!(scope, identifier, ttl)
|
137
157
|
end
|
138
|
-
expect(redis_conn.zscore(blocked_key, identifier)).
|
158
|
+
expect(redis_conn.zscore(blocked_key, identifier)).not_to be_nil
|
139
159
|
expect(redis_conn.zscore(blocked_key, identifier)).to eq(time.to_i + ttl)
|
140
160
|
end
|
141
|
-
|
142
161
|
end
|
143
162
|
end
|
144
163
|
|
@@ -1,21 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
require 'date'
|
3
5
|
require 'timecop'
|
4
6
|
|
5
7
|
describe Pause::Redis::ShardedAdapter do
|
6
|
-
|
7
8
|
let(:resolution) { 10 }
|
9
|
+
let(:adapter) { described_class.new(Pause.config) }
|
8
10
|
let(:history) { 60 }
|
9
11
|
let(:configuration) { Pause::Configuration.new }
|
10
12
|
|
11
13
|
before do
|
12
14
|
allow(Pause).to receive(:config).and_return(configuration)
|
13
|
-
allow(Pause.config).to
|
14
|
-
allow(Pause.config).to receive(:history).and_return(history)
|
15
|
+
allow(Pause.config).to receive_messages(resolution: resolution, history: history)
|
15
16
|
end
|
16
17
|
|
17
|
-
let(:adapter) { Pause::Redis::ShardedAdapter.new(Pause.config) }
|
18
|
-
|
19
18
|
describe '#all_keys' do
|
20
19
|
it 'is not supported' do
|
21
20
|
expect { adapter.all_keys('cake') }.to raise_error(Pause::Redis::OperationNotSupported)
|
@@ -24,15 +23,16 @@ describe Pause::Redis::ShardedAdapter do
|
|
24
23
|
|
25
24
|
describe '#with_multi' do
|
26
25
|
let(:redis) { adapter.send(:redis) }
|
27
|
-
|
28
|
-
|
29
|
-
expect
|
26
|
+
|
27
|
+
it 'does not call redis.multi' do
|
28
|
+
expect(redis).not_to receive(:multi)
|
29
|
+
expect { adapter.increment(:scope, 123, Time.now) }.not_to raise_error
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
33
|
describe '#redis' do
|
34
|
-
it '
|
35
|
-
expect(adapter.send(:redis_connection_opts)).
|
34
|
+
it 'does not use redis db when connecting' do
|
35
|
+
expect(adapter.send(:redis_connection_opts)).not_to include(:db)
|
36
36
|
end
|
37
37
|
end
|
38
38
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# This file was generated by the `rspec --init` command. Conventionally, all
|
2
4
|
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
3
5
|
# Require this file using `require "spec_helper"` to ensure that it is only
|
@@ -5,28 +7,30 @@
|
|
5
7
|
#
|
6
8
|
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
7
9
|
|
8
|
-
require 'pause'
|
9
10
|
require 'fileutils'
|
10
11
|
|
12
|
+
require 'simplecov'
|
13
|
+
SimpleCov.start
|
14
|
+
|
15
|
+
require 'pause'
|
16
|
+
|
11
17
|
if ENV['PAUSE_REAL_REDIS']
|
12
18
|
require 'pause/redis/adapter'
|
13
|
-
puts
|
19
|
+
puts
|
20
|
+
puts "NOTE: Using real Redis-server at #{Pause::Redis::Adapter.redis.inspect}\n\n"
|
14
21
|
else
|
15
22
|
require 'fakeredis/rspec'
|
16
23
|
end
|
17
24
|
|
18
25
|
RSpec.configure do |config|
|
19
|
-
|
20
|
-
config.filter_run :focus
|
21
|
-
|
22
|
-
rspec_dir = './.spec'.freeze
|
26
|
+
rspec_dir = './.spec'
|
23
27
|
FileUtils.mkdir_p(rspec_dir)
|
24
28
|
config.example_status_persistence_file_path = "#{rspec_dir}/results.txt"
|
25
29
|
|
26
30
|
config.order = 'random'
|
27
31
|
|
28
32
|
if ENV['PAUSE_REAL_REDIS']
|
29
|
-
config.before
|
33
|
+
config.before do
|
30
34
|
Pause::Redis::Adapter.redis.flushdb
|
31
35
|
end
|
32
36
|
end
|
metadata
CHANGED
@@ -1,20 +1,19 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pause
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
+
- Konstantin Gredeskoul
|
7
8
|
- Atasay Gokkaya
|
8
9
|
- Paul Henry
|
9
10
|
- Eric Saxby
|
10
|
-
- Konstantin Gredeskoul
|
11
|
-
autorequire:
|
12
11
|
bindir: bin
|
13
12
|
cert_chain: []
|
14
|
-
date:
|
13
|
+
date: 2025-02-07 00:00:00.000000000 Z
|
15
14
|
dependencies:
|
16
15
|
- !ruby/object:Gem::Dependency
|
17
|
-
name:
|
16
|
+
name: colored2
|
18
17
|
requirement: !ruby/object:Gem::Requirement
|
19
18
|
requirements:
|
20
19
|
- - ">="
|
@@ -28,7 +27,7 @@ dependencies:
|
|
28
27
|
- !ruby/object:Gem::Version
|
29
28
|
version: '0'
|
30
29
|
- !ruby/object:Gem::Dependency
|
31
|
-
name:
|
30
|
+
name: redis
|
32
31
|
requirement: !ruby/object:Gem::Requirement
|
33
32
|
requirements:
|
34
33
|
- - ">="
|
@@ -41,126 +40,34 @@ dependencies:
|
|
41
40
|
- - ">="
|
42
41
|
- !ruby/object:Gem::Version
|
43
42
|
version: '0'
|
44
|
-
- !ruby/object:Gem::Dependency
|
45
|
-
name: simplecov
|
46
|
-
requirement: !ruby/object:Gem::Requirement
|
47
|
-
requirements:
|
48
|
-
- - ">="
|
49
|
-
- !ruby/object:Gem::Version
|
50
|
-
version: '0'
|
51
|
-
type: :development
|
52
|
-
prerelease: false
|
53
|
-
version_requirements: !ruby/object:Gem::Requirement
|
54
|
-
requirements:
|
55
|
-
- - ">="
|
56
|
-
- !ruby/object:Gem::Version
|
57
|
-
version: '0'
|
58
|
-
- !ruby/object:Gem::Dependency
|
59
|
-
name: yard
|
60
|
-
requirement: !ruby/object:Gem::Requirement
|
61
|
-
requirements:
|
62
|
-
- - ">="
|
63
|
-
- !ruby/object:Gem::Version
|
64
|
-
version: '0'
|
65
|
-
type: :development
|
66
|
-
prerelease: false
|
67
|
-
version_requirements: !ruby/object:Gem::Requirement
|
68
|
-
requirements:
|
69
|
-
- - ">="
|
70
|
-
- !ruby/object:Gem::Version
|
71
|
-
version: '0'
|
72
|
-
- !ruby/object:Gem::Dependency
|
73
|
-
name: rspec
|
74
|
-
requirement: !ruby/object:Gem::Requirement
|
75
|
-
requirements:
|
76
|
-
- - ">="
|
77
|
-
- !ruby/object:Gem::Version
|
78
|
-
version: '0'
|
79
|
-
type: :development
|
80
|
-
prerelease: false
|
81
|
-
version_requirements: !ruby/object:Gem::Requirement
|
82
|
-
requirements:
|
83
|
-
- - ">="
|
84
|
-
- !ruby/object:Gem::Version
|
85
|
-
version: '0'
|
86
|
-
- !ruby/object:Gem::Dependency
|
87
|
-
name: fakeredis
|
88
|
-
requirement: !ruby/object:Gem::Requirement
|
89
|
-
requirements:
|
90
|
-
- - ">="
|
91
|
-
- !ruby/object:Gem::Version
|
92
|
-
version: '0'
|
93
|
-
type: :development
|
94
|
-
prerelease: false
|
95
|
-
version_requirements: !ruby/object:Gem::Requirement
|
96
|
-
requirements:
|
97
|
-
- - ">="
|
98
|
-
- !ruby/object:Gem::Version
|
99
|
-
version: '0'
|
100
|
-
- !ruby/object:Gem::Dependency
|
101
|
-
name: guard-rspec
|
102
|
-
requirement: !ruby/object:Gem::Requirement
|
103
|
-
requirements:
|
104
|
-
- - ">="
|
105
|
-
- !ruby/object:Gem::Version
|
106
|
-
version: '0'
|
107
|
-
type: :development
|
108
|
-
prerelease: false
|
109
|
-
version_requirements: !ruby/object:Gem::Requirement
|
110
|
-
requirements:
|
111
|
-
- - ">="
|
112
|
-
- !ruby/object:Gem::Version
|
113
|
-
version: '0'
|
114
|
-
- !ruby/object:Gem::Dependency
|
115
|
-
name: timecop
|
116
|
-
requirement: !ruby/object:Gem::Requirement
|
117
|
-
requirements:
|
118
|
-
- - ">="
|
119
|
-
- !ruby/object:Gem::Version
|
120
|
-
version: '0'
|
121
|
-
type: :development
|
122
|
-
prerelease: false
|
123
|
-
version_requirements: !ruby/object:Gem::Requirement
|
124
|
-
requirements:
|
125
|
-
- - ">="
|
126
|
-
- !ruby/object:Gem::Version
|
127
|
-
version: '0'
|
128
|
-
- !ruby/object:Gem::Dependency
|
129
|
-
name: rake
|
130
|
-
requirement: !ruby/object:Gem::Requirement
|
131
|
-
requirements:
|
132
|
-
- - ">="
|
133
|
-
- !ruby/object:Gem::Version
|
134
|
-
version: '0'
|
135
|
-
type: :development
|
136
|
-
prerelease: false
|
137
|
-
version_requirements: !ruby/object:Gem::Requirement
|
138
|
-
requirements:
|
139
|
-
- - ">="
|
140
|
-
- !ruby/object:Gem::Version
|
141
|
-
version: '0'
|
142
43
|
description: This gem provides highly flexible and easy to use interface to define
|
143
44
|
rate limit checks, register events as they come, and verify if the rate limit is
|
144
45
|
reached. Multiple checks for the same metric are easily supported. This gem is used
|
145
46
|
at very high scale on several popular web sites.
|
146
47
|
email:
|
48
|
+
- kigster@gmail.com
|
147
49
|
- atasay@wanelo.com
|
148
50
|
- paul@wanelo.com
|
149
51
|
- sax@ericsaxby.com
|
150
|
-
|
151
|
-
|
52
|
+
executables:
|
53
|
+
- spec
|
152
54
|
extensions: []
|
153
55
|
extra_rdoc_files: []
|
154
56
|
files:
|
57
|
+
- ".github/workflows/rspec.yml"
|
58
|
+
- ".github/workflows/rubocop.yml"
|
155
59
|
- ".gitignore"
|
156
60
|
- ".rspec"
|
61
|
+
- ".rubocop.yml"
|
62
|
+
- ".rubocop_todo.yml"
|
157
63
|
- ".rvmrc"
|
158
|
-
- ".travis.yml"
|
159
64
|
- Gemfile
|
65
|
+
- Gemfile.lock
|
160
66
|
- Guardfile
|
161
67
|
- LICENSE.txt
|
162
68
|
- README.md
|
163
69
|
- Rakefile
|
70
|
+
- bin/spec
|
164
71
|
- lib/pause.rb
|
165
72
|
- lib/pause/action.rb
|
166
73
|
- lib/pause/analyzer.rb
|
@@ -175,14 +82,15 @@ files:
|
|
175
82
|
- spec/pause/action_spec.rb
|
176
83
|
- spec/pause/analyzer_spec.rb
|
177
84
|
- spec/pause/configuration_spec.rb
|
85
|
+
- spec/pause/logger_spec.rb
|
178
86
|
- spec/pause/pause_spec.rb
|
179
87
|
- spec/pause/redis/adapter_spec.rb
|
180
88
|
- spec/pause/redis/sharded_adapter_spec.rb
|
181
89
|
- spec/spec_helper.rb
|
182
90
|
homepage: https://github.com/kigster/pause
|
183
91
|
licenses: []
|
184
|
-
metadata:
|
185
|
-
|
92
|
+
metadata:
|
93
|
+
rubygems_mfa_required: 'true'
|
186
94
|
rdoc_options: []
|
187
95
|
require_paths:
|
188
96
|
- lib
|
@@ -197,17 +105,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
197
105
|
- !ruby/object:Gem::Version
|
198
106
|
version: '0'
|
199
107
|
requirements: []
|
200
|
-
|
201
|
-
rubygems_version: 2.7.6
|
202
|
-
signing_key:
|
108
|
+
rubygems_version: 3.6.3
|
203
109
|
specification_version: 4
|
204
110
|
summary: Fast, scalable, and flexible real time rate limiting library for distributed
|
205
111
|
Ruby environments backed by Redis.
|
206
|
-
test_files:
|
207
|
-
- spec/pause/action_spec.rb
|
208
|
-
- spec/pause/analyzer_spec.rb
|
209
|
-
- spec/pause/configuration_spec.rb
|
210
|
-
- spec/pause/pause_spec.rb
|
211
|
-
- spec/pause/redis/adapter_spec.rb
|
212
|
-
- spec/pause/redis/sharded_adapter_spec.rb
|
213
|
-
- spec/spec_helper.rb
|
112
|
+
test_files: []
|
data/.travis.yml
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
language: ruby
|
2
|
-
rvm:
|
3
|
-
- 2.3.3
|
4
|
-
- 2.4.3
|
5
|
-
- 2.5.0
|
6
|
-
script: "bundle exec rake || ( sleep 1; bundle exec rspec --only-failures ) "
|
7
|
-
services:
|
8
|
-
- redis-server
|
9
|
-
notifications:
|
10
|
-
email:
|
11
|
-
recipients:
|
12
|
-
- kigster@gmail.com
|
13
|
-
on_success: never
|
14
|
-
on_failure: always
|