pause 0.2.1 → 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.
@@ -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
-