Skip to content

Commit f064a48

Browse files
committed
Bump version, add spec and fix comments. Use semantic versioning as well.
1 parent e3bd798 commit f064a48

8 files changed

Lines changed: 178 additions & 25 deletions

File tree

.github/workflows/ci.yml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [master]
6+
pull_request:
7+
branches: [master]
8+
9+
jobs:
10+
test:
11+
runs-on: ubuntu-latest
12+
13+
strategy:
14+
matrix:
15+
ruby-version: ['3.0', '3.1', '3.2', '3.3']
16+
17+
steps:
18+
- uses: actions/checkout@v4
19+
20+
- name: Set up Ruby ${{ matrix.ruby-version }}
21+
uses: ruby/setup-ruby@v1
22+
with:
23+
ruby-version: ${{ matrix.ruby-version }}
24+
bundler-cache: true
25+
26+
- name: Run specs
27+
run: bundle exec rspec

.rspec

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
--format documentation
2+
--color
3+
--require spec_helper

README.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,10 @@ Requires `redis` to be available and installed. That's what it will use for memo
3838

3939
```ruby
4040
20.times do
41-
DontRepeatFor.new("Everyone/Test", 1.second) { puts "This message will only be displayed once every 1 second."; }
41+
DontRepeatFor.new(1.second, "Everyone/Test") { puts "This message will only be displayed once every 1 second."; }
4242
end
4343
sleep(1.second)
44-
DontRepeatFor.new("Everyone/Test", 1.second) { puts "This message will only be displayed once every 1 second."; }
44+
DontRepeatFor.new(1.second, "Everyone/Test") { puts "This message will only be displayed once every 1 second."; }
4545

4646
# Output:
4747
# This message will only be displayed once every 1 second.
@@ -54,12 +54,12 @@ DontRepeatFor.new("Everyone/Test", 1.second) { puts "This message will only be d
5454
user1 = User.new(id: -1)
5555
user2 = User.new(id: -2)
5656
20.times do
57-
DontRepeatFor.new("User-#{user1.id}/Test", 1.second) { puts "This message will only be displayed once every 1 second for user #{user1.id}."; }
58-
DontRepeatFor.new("User-#{user2.id}/Test", 1.second) { puts "This message will only be displayed once every 1 second for user #{user2.id}."; }
57+
DontRepeatFor.new(1.second, "User-#{user1.id}/Test") { puts "This message will only be displayed once every 1 second for user #{user1.id}."; }
58+
DontRepeatFor.new(1.second, "User-#{user2.id}/Test") { puts "This message will only be displayed once every 1 second for user #{user2.id}."; }
5959
end
6060
sleep(1.second)
61-
DontRepeatFor.new("User-#{user1.id}/Test", 1.second) { puts "This message will only be displayed once every 1 second for user #{user1.id}."; }
62-
DontRepeatFor.new("User-#{user2.id}/Test", 1.second) { puts "This message will only be displayed once every 1 second for user #{user2.id}."; }
61+
DontRepeatFor.new(1.second, "User-#{user1.id}/Test") { puts "This message will only be displayed once every 1 second for user #{user1.id}."; }
62+
DontRepeatFor.new(1.second, "User-#{user2.id}/Test") { puts "This message will only be displayed once every 1 second for user #{user2.id}."; }
6363

6464
# Output:
6565
# This message will only be displayed once every 1 second for user -1.
@@ -70,7 +70,7 @@ DontRepeatFor.new("User-#{user2.id}/Test", 1.second) { puts "This message will o
7070

7171
# Other Things
7272
### Roadmap
73-
* Make this system compatible with memory-baseds storage and other data storage systems.
73+
* Make this system compatible with memory-based storage and other data storage systems.
7474

7575
### Contributing
7676
Bug reports and pull requests are welcome on GitHub at https://github.com/jayelkaake/dont_repeat_for.

dont_repeat_for.gemspec

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,25 +14,15 @@ Gem::Specification.new do |spec|
1414
spec.homepage = "https://www.github.com/jayelkaake/dont_repeat_for"
1515
spec.license = "MIT"
1616

17-
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
18-
# to allow pushing to a single host or delete this section to allow pushing to any host.
19-
# if spec.respond_to?(:metadata)
20-
# spec.metadata['allowed_push_host'] = "TODO: Set to 'http://mygemserver.com'"
21-
# else
22-
# raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
23-
# end
24-
2517
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
2618
spec.bindir = "exe"
2719
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
2820
spec.require_paths = ["lib"]
2921

30-
spec.add_development_dependency "rails", ">= 4.0.0"
22+
spec.add_dependency "redis"
3123

3224
spec.add_development_dependency "bundler"
33-
spec.add_development_dependency "temping", "~> 3.3.0"
34-
spec.add_development_dependency "rake", "~> 12.3.3"
35-
spec.add_development_dependency "rspec", '~> 3.4'
36-
spec.add_development_dependency "sqlite3", '~> 1.3.11'
25+
spec.add_development_dependency "rake", "~> 13.0"
26+
spec.add_development_dependency "rspec", "~> 3.4"
3727

3828
end

lib/dont_repeat_for.rb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,18 @@
66
# Example 1:
77
# ```ruby
88
# 20.times do
9-
# DontRepeatFor.new("test_dont_repeat/test", 5.minutes) { puts "This message will only be displayed once every 5 minutes.";
9+
# DontRepeatFor.new(5.minutes, "test_dont_repeat/test") { puts "This message will only be displayed once every 5 minutes.";
1010
# sleep(1.minute)
1111
# end
1212
# ```
1313
#
14-
# @hint: You can include an ID in the key to enforce rate limiting for a specific store.
14+
# @hint: You can include a model ID in the key to enforce rate limiting for a specific model.
1515
class DontRepeatFor
1616
attr_accessor :key, :time
1717

1818
##
19-
# @param time [Integer] The amount of seconds that we don't want to repeat the process for
20-
# @param key [String] A unique key to identify the process that we don't want to repeat for the given time
19+
# @param time [ActiveSupport::Duration|Integer] The amount of seconds that we don't want to repeat the process for
20+
# @param key [String] A unique key to identify the process that we don't want to repeat for the given time
2121
def initialize(time, key)
2222
@key = key
2323
@time = time.to_i

lib/dont_repeat_for/version.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
class DontRepeatFor
2-
VERSION = '1.0.0.1'
2+
VERSION = '1.0.2'
33
end

spec/dont_repeat_for_spec.rb

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
RSpec.describe DontRepeatFor do
2+
let(:fake_redis) { FakeRedis.new }
3+
4+
around do |example|
5+
old_redis = $redis if defined?($redis)
6+
was_defined = defined?($redis)
7+
$redis = fake_redis
8+
fake_redis.reset!
9+
example.run
10+
ensure
11+
if was_defined
12+
$redis = old_redis
13+
else
14+
$redis = nil
15+
end
16+
end
17+
18+
describe "VERSION" do
19+
it "is defined" do
20+
expect(DontRepeatFor::VERSION).to be_a(String)
21+
end
22+
end
23+
24+
describe "#initialize" do
25+
it "executes the block the first time" do
26+
executed = false
27+
DontRepeatFor.new(300, "test/first_run") { executed = true }
28+
expect(executed).to be true
29+
end
30+
31+
it "does not execute the block on a second call with the same key" do
32+
call_count = 0
33+
2.times { DontRepeatFor.new(300, "test/duplicate") { call_count += 1 } }
34+
expect(call_count).to eq(1)
35+
end
36+
37+
it "does not execute the block on any subsequent calls with the same key" do
38+
call_count = 0
39+
5.times { DontRepeatFor.new(300, "test/many") { call_count += 1 } }
40+
expect(call_count).to eq(1)
41+
end
42+
43+
it "executes blocks with different keys independently" do
44+
results = []
45+
DontRepeatFor.new(300, "test/a") { results << :a }
46+
DontRepeatFor.new(300, "test/b") { results << :b }
47+
expect(results).to eq([:a, :b])
48+
end
49+
50+
it "converts time to integer" do
51+
instance = DontRepeatFor.new(300.5, "test/float") {}
52+
expect(instance.time).to eq(300)
53+
end
54+
55+
it "exposes key and time as accessors" do
56+
instance = DontRepeatFor.new(600, "test/accessors") {}
57+
expect(instance.key).to eq("test/accessors")
58+
expect(instance.time).to eq(600)
59+
end
60+
end
61+
62+
describe "redis key format" do
63+
it "namespaces keys under DontRepeatFor/v1/Keys/" do
64+
DontRepeatFor.new(300, "my/custom/key") {}
65+
expected_key = "DontRepeatFor/v1/Keys/my/custom/key"
66+
expect(fake_redis.expirations).to have_key(expected_key)
67+
end
68+
end
69+
70+
describe "expiry" do
71+
it "sets the expiry to the given time in seconds" do
72+
DontRepeatFor.new(600, "test/expiry") {}
73+
expected_key = "DontRepeatFor/v1/Keys/test/expiry"
74+
expect(fake_redis.expirations[expected_key]).to eq(600)
75+
end
76+
77+
it "does not set expiry on duplicate calls" do
78+
DontRepeatFor.new(600, "test/expiry_once") {}
79+
fake_redis.expirations.clear
80+
DontRepeatFor.new(600, "test/expiry_once") {}
81+
expect(fake_redis.expirations).to be_empty
82+
end
83+
end
84+
85+
describe "redis connection" do
86+
it "uses $redis global when defined" do
87+
executed = false
88+
DontRepeatFor.new(300, "test/global_redis") { executed = true }
89+
expect(executed).to be true
90+
expect(fake_redis.expirations).to have_key("DontRepeatFor/v1/Keys/test/global_redis")
91+
end
92+
end
93+
end

spec/spec_helper.rb

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
require "dont_repeat_for"
2+
3+
class FakeRedis
4+
def initialize
5+
@store = {}
6+
end
7+
8+
def incr(key)
9+
@store[key] = (@store[key] || 0) + 1
10+
end
11+
12+
def expire(key, seconds)
13+
# no-op for testing — just record that it was called
14+
@expirations ||= {}
15+
@expirations[key] = seconds
16+
end
17+
18+
def expirations
19+
@expirations || {}
20+
end
21+
22+
def reset!
23+
@store.clear
24+
@expirations = {}
25+
end
26+
end
27+
28+
RSpec.configure do |config|
29+
config.expect_with :rspec do |expectations|
30+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
31+
end
32+
33+
config.mock_with :rspec do |mocks|
34+
mocks.verify_partial_doubles = true
35+
end
36+
37+
config.disable_monkey_patching!
38+
config.order = :random
39+
Kernel.srand config.seed
40+
end

0 commit comments

Comments
 (0)