pause 0.2.1 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
  require 'timecop'
3
5
 
@@ -11,20 +13,17 @@ describe Pause::Analyzer do
11
13
  end
12
14
 
13
15
  let(:resolution) { 10 }
16
+ let(:analyzer) { Pause.analyzer }
17
+ let(:action) { FollowPushNotification.new('1243123') }
14
18
  let(:history) { 60 }
15
19
  let(:configuration) { Pause::Configuration.new }
16
20
  let(:adapter) { Pause::Redis::Adapter.new(configuration) }
17
21
 
18
22
  before do
19
- allow(Pause).to receive(:config).and_return(configuration)
20
- allow(Pause.config).to receive(:resolution).and_return(resolution)
21
- allow(Pause.config).to receive(:history).and_return(history)
22
- allow(Pause).to receive(:adapter).and_return(adapter)
23
+ allow(Pause.config).to receive_messages(resolution: resolution, history: history)
24
+ allow(Pause).to receive_messages(config: configuration, adapter: adapter)
23
25
  end
24
26
 
25
- let(:analyzer) { Pause.analyzer }
26
- let(:action) { FollowPushNotification.new('1243123') }
27
-
28
27
  describe '#analyze' do
29
28
  it 'checks and blocks if max_allowed is reached' do
30
29
  time = Time.now
@@ -39,11 +38,11 @@ describe Pause::Analyzer do
39
38
  end
40
39
 
41
40
  describe '#check' do
42
- it 'should return nil if action is NOT blocked' do
43
- expect(analyzer.check(action)).to be nil
41
+ it 'returns nil if action is NOT blocked' do
42
+ expect(analyzer.check(action)).to be_nil
44
43
  end
45
44
 
46
- it 'should return blocked action if action is blocked' do
45
+ it 'returns blocked action if action is blocked' do
47
46
  Timecop.freeze Time.now do
48
47
  5.times do
49
48
  action.increment!
@@ -1,10 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
 
3
5
  describe Pause::Configuration, '#configure' do
6
+ subject { described_class.new }
4
7
 
5
- subject { Pause::Configuration.new }
6
-
7
- it 'should allow configuration via block' do
8
+ it 'allows configuration via block' do
8
9
  subject.configure do |c|
9
10
  c.redis_host = '128.23.12.8'
10
11
  c.redis_port = '2134'
@@ -24,7 +25,7 @@ describe Pause::Configuration, '#configure' do
24
25
  expect(subject.sharded).to be true
25
26
  end
26
27
 
27
- it 'should provide redis defaults' do
28
+ it 'provides redis defaults' do
28
29
  subject.configure do |config|
29
30
  # do nothing
30
31
  end
@@ -33,7 +34,7 @@ describe Pause::Configuration, '#configure' do
33
34
  expect(subject.redis_port).to eq(6379)
34
35
  expect(subject.redis_db).to eq('1')
35
36
  expect(subject.resolution).to eq(600) # 10 minutes
36
- expect(subject.history).to eq(86400) # one day
37
+ expect(subject.history).to eq(86_400) # one day
37
38
  expect(subject.sharded).to be false
38
39
  end
39
40
  end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Pause::Logger do
6
+ describe 'when accessed #puts' do
7
+ before do
8
+ expect($stdout).to receive(:puts).with('hello')
9
+ end
10
+
11
+ it 'calls through to puts without color' do
12
+ described_class.puts('hello')
13
+ end
14
+ end
15
+
16
+ describe 'when accessed via #fatal' do
17
+ before do
18
+ expect($stderr).to receive(:puts).with("\e[31mwhoops\e[0m")
19
+ end
20
+
21
+ it 'calls through to puts with color' do
22
+ described_class.fatal('whoops')
23
+ end
24
+ end
25
+ end
@@ -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
- Pause.adapter = nil
9
- allow(Pause).to receive(:config).and_return(configuration)
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(Pause.adapter).to be_a(Pause::Redis::ShardedAdapter)
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(Pause.adapter).to be_a(Pause::Redis::Adapter)
27
+ expect(described_class.adapter).to be_a(Pause::Redis::Adapter)
26
28
  end
27
29
  end
28
30
  end
@@ -1,52 +1,81 @@
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 receive(:resolution).and_return(resolution)
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) { 'i:blah:|213213|' }
23
+ let(:tracked_key) { "i:#{scope}:|#{identifier}|" }
24
+ let(:now) { Time.now.to_i }
25
25
 
26
- it 'should add key to a redis set' do
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, :with_scores => true)
29
- expect(set).to_not be_empty
30
- expect(set.size).to eql(1)
31
- expect(set[0].size).to eql(2)
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
32
  end
33
33
 
34
- it 'should remove old key from a redis set' do
35
- time = Time.now
36
- expect(redis_conn).to receive(:zrem).with(tracked_key, [adapter.period_marker(resolution, time)])
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)
37
44
 
38
- adapter.time_blocks_to_keep = 1
39
- Timecop.freeze time do
40
45
  adapter.increment(scope, identifier, Time.now.to_i)
41
46
  end
42
- Timecop.freeze time + (adapter.resolution + 1) do
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
+
43
52
  adapter.increment(scope, identifier, Time.now.to_i)
44
53
  end
45
54
  end
46
55
 
47
- it 'sets expiry on key' do
48
- expect(redis_conn).to receive(:expire).with(tracked_key, history)
49
- adapter.increment(scope, identifier, Time.now.to_i)
56
+ context 'removing two elements' do
57
+ let(:to_delete) { 2 }
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
+
65
+ before do
66
+ redis_conn.flushall
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])
70
+ end
71
+
72
+ it 'removes old elements' do
73
+ adapter.increment(scope, identifier, now.to_i)
74
+ to_delete.times do |t|
75
+ next_time = now + (adapter.resolution + t + 1)
76
+ adapter.increment(scope, identifier, next_time.to_i)
77
+ end
78
+ end
50
79
  end
51
80
  end
52
81
 
@@ -68,21 +97,18 @@ describe Pause::Redis::Adapter do
68
97
 
69
98
  adapter.expire_block_list(scope)
70
99
 
71
- expect(redis_conn.zscore('b:|a|', blocked_identifier)).not_to be nil
72
- expect(redis_conn.zscore('b:|a|', expired_identifier)).to be nil
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
73
102
  end
74
103
  end
75
104
 
76
- describe '#rate_limit!' do
77
- end
78
-
79
105
  describe '#rate_limited?' do
80
106
  let(:scope) { 'ipn:follow' }
81
107
  let(:identifier) { '123461234' }
82
108
  let(:blocked_key) { "b:#{key}" }
83
- let(:ttl) { 110000 }
109
+ let(:ttl) { 110_000 }
84
110
 
85
- it 'should return true if blocked' do
111
+ it 'returns true if blocked' do
86
112
  adapter.rate_limit!(scope, identifier, ttl)
87
113
  expect(adapter.rate_limited?(scope, identifier)).to be true
88
114
  end
@@ -122,17 +148,16 @@ describe Pause::Redis::Adapter do
122
148
  let(:scope) { 'ipn:follow' }
123
149
  let(:identifier) { '1234' }
124
150
  let(:blocked_key) { "b:|#{scope}|" }
125
- let(:ttl) { 110000 }
151
+ let(:ttl) { 110_000 }
126
152
 
127
153
  it 'saves ip to redis with expiration' do
128
154
  time = Time.now
129
155
  Timecop.freeze time do
130
156
  adapter.rate_limit!(scope, identifier, ttl)
131
157
  end
132
- expect(redis_conn.zscore(blocked_key, identifier)).to_not be nil
158
+ expect(redis_conn.zscore(blocked_key, identifier)).not_to be_nil
133
159
  expect(redis_conn.zscore(blocked_key, identifier)).to eq(time.to_i + ttl)
134
160
  end
135
-
136
161
  end
137
162
  end
138
163
 
@@ -1,24 +1,38 @@
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 receive(:resolution).and_return(resolution)
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)
22
21
  end
23
22
  end
23
+
24
+ describe '#with_multi' do
25
+ let(:redis) { adapter.send(:redis) }
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
+ end
31
+ end
32
+
33
+ describe '#redis' do
34
+ it 'does not use redis db when connecting' do
35
+ expect(adapter.send(:redis_connection_opts)).not_to include(:db)
36
+ end
37
+ end
24
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,20 +7,31 @@
5
7
  #
6
8
  # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
7
9
 
8
- ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
9
- require 'rubygems'
10
- require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE'])
10
+ require 'fileutils'
11
+
12
+ require 'simplecov'
13
+ SimpleCov.start
14
+
11
15
  require 'pause'
12
- require 'pry'
13
- require 'support/fakeredis'
16
+
17
+ if ENV['PAUSE_REAL_REDIS']
18
+ require 'pause/redis/adapter'
19
+ puts
20
+ puts "NOTE: Using real Redis-server at #{Pause::Redis::Adapter.redis.inspect}\n\n"
21
+ else
22
+ require 'fakeredis/rspec'
23
+ end
14
24
 
15
25
  RSpec.configure do |config|
16
- config.run_all_when_everything_filtered = true
17
- config.filter_run :focus
26
+ rspec_dir = './.spec'
27
+ FileUtils.mkdir_p(rspec_dir)
28
+ config.example_status_persistence_file_path = "#{rspec_dir}/results.txt"
18
29
 
19
- # Run specs in random order to surface order dependencies. If you find an
20
- # order dependency and want to debug it, you can fix the order by providing
21
- # the seed, which is printed after each run.
22
- # --seed 1234
23
30
  config.order = 'random'
31
+
32
+ if ENV['PAUSE_REAL_REDIS']
33
+ config.before do
34
+ Pause::Redis::Adapter.redis.flushdb
35
+ end
36
+ end
24
37
  end
metadata CHANGED
@@ -1,18 +1,31 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pause
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
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: 2015-11-07 00:00:00.000000000 Z
13
+ date: 2025-02-07 00:00:00.000000000 Z
15
14
  dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: colored2
17
+ requirement: !ruby/object:Gem::Requirement
18
+ requirements:
19
+ - - ">="
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ version: '0'
16
29
  - !ruby/object:Gem::Dependency
17
30
  name: redis
18
31
  requirement: !ruby/object:Gem::Requirement
@@ -27,26 +40,34 @@ dependencies:
27
40
  - - ">="
28
41
  - !ruby/object:Gem::Version
29
42
  version: '0'
30
- description: Real time rate limiting for multi-process ruby environments based on
31
- Redis
43
+ description: This gem provides highly flexible and easy to use interface to define
44
+ rate limit checks, register events as they come, and verify if the rate limit is
45
+ reached. Multiple checks for the same metric are easily supported. This gem is used
46
+ at very high scale on several popular web sites.
32
47
  email:
48
+ - kigster@gmail.com
33
49
  - atasay@wanelo.com
34
50
  - paul@wanelo.com
35
- - sax@wanelo.com
36
- - kig@wanelo.com
37
- executables: []
51
+ - sax@ericsaxby.com
52
+ executables:
53
+ - spec
38
54
  extensions: []
39
55
  extra_rdoc_files: []
40
56
  files:
57
+ - ".github/workflows/rspec.yml"
58
+ - ".github/workflows/rubocop.yml"
41
59
  - ".gitignore"
42
60
  - ".rspec"
61
+ - ".rubocop.yml"
62
+ - ".rubocop_todo.yml"
43
63
  - ".rvmrc"
44
- - ".travis.yml"
45
64
  - Gemfile
65
+ - Gemfile.lock
46
66
  - Guardfile
47
67
  - LICENSE.txt
48
68
  - README.md
49
69
  - Rakefile
70
+ - bin/spec
50
71
  - lib/pause.rb
51
72
  - lib/pause/action.rb
52
73
  - lib/pause/analyzer.rb
@@ -61,15 +82,15 @@ files:
61
82
  - spec/pause/action_spec.rb
62
83
  - spec/pause/analyzer_spec.rb
63
84
  - spec/pause/configuration_spec.rb
85
+ - spec/pause/logger_spec.rb
64
86
  - spec/pause/pause_spec.rb
65
87
  - spec/pause/redis/adapter_spec.rb
66
88
  - spec/pause/redis/sharded_adapter_spec.rb
67
89
  - spec/spec_helper.rb
68
- - spec/support/fakeredis.rb
69
- homepage: https://github.com/wanelo/pause
90
+ homepage: https://github.com/kigster/pause
70
91
  licenses: []
71
- metadata: {}
72
- post_install_message:
92
+ metadata:
93
+ rubygems_mfa_required: 'true'
73
94
  rdoc_options: []
74
95
  require_paths:
75
96
  - lib
@@ -84,17 +105,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
84
105
  - !ruby/object:Gem::Version
85
106
  version: '0'
86
107
  requirements: []
87
- rubyforge_project:
88
- rubygems_version: 2.2.2
89
- signing_key:
108
+ rubygems_version: 3.6.3
90
109
  specification_version: 4
91
- summary: RReal time rate limiting for multi-process ruby environments based on Redis
92
- test_files:
93
- - spec/pause/action_spec.rb
94
- - spec/pause/analyzer_spec.rb
95
- - spec/pause/configuration_spec.rb
96
- - spec/pause/pause_spec.rb
97
- - spec/pause/redis/adapter_spec.rb
98
- - spec/pause/redis/sharded_adapter_spec.rb
99
- - spec/spec_helper.rb
100
- - spec/support/fakeredis.rb
110
+ summary: Fast, scalable, and flexible real time rate limiting library for distributed
111
+ Ruby environments backed by Redis.
112
+ test_files: []
data/.travis.yml DELETED
@@ -1,10 +0,0 @@
1
- language: ruby
2
- rvm:
3
- - 1.9.3
4
- script: "bundle exec rspec"
5
- notifications:
6
- email:
7
- recipients:
8
- - dev-info@wanelo.com
9
- on_success: never
10
- on_failure: always
@@ -1,2 +0,0 @@
1
- require 'fakeredis/rspec'
2
-