rack-idempotency_key 0.2.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/test.yml +3 -3
- data/.gitignore +1 -0
- data/.rubocop.yml +5 -1
- data/Appraisals +9 -0
- data/CHANGELOG.md +12 -0
- data/Gemfile +4 -3
- data/README.md +4 -9
- data/gemfiles/rack_2.gemfile +18 -0
- data/gemfiles/rack_3.gemfile +18 -0
- data/idempotency_key.gemspec +0 -2
- data/lib/rack/idempotency_key/redis_store.rb +60 -0
- data/lib/rack/idempotency_key/version.rb +1 -1
- data/lib/rack/idempotency_key.rb +7 -1
- metadata +6 -18
- data/Rakefile +0 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 157af590e24f6928fc61715b006c746296b9a9b0192d42bf14975b1722117dc7
|
4
|
+
data.tar.gz: 41f8668647cf15a010732444f5c8071f93dc1434e9be5e0edbd3a45c403854bb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e3b80b32393b26f2ef685948a39b95c0acbcf549a2b6d3bc08b3e654faacd866aa707d5d412e5a80b76d388dbfe417778d484fdbc1bda7c773ed6236d7adbe8f
|
7
|
+
data.tar.gz: c9b2b228020e64ce63c4aa0f19a9216a84812a48be30be15701d0eda01fd5d9e46d1c5d841b19cf3848e9168332bdcc907d1906ccfa96acc0d679b7f96f31409
|
data/.github/workflows/test.yml
CHANGED
@@ -25,8 +25,8 @@ jobs:
|
|
25
25
|
with:
|
26
26
|
bundler-cache: true
|
27
27
|
ruby-version: ${{ matrix.ruby }}
|
28
|
-
- run: bundle install
|
29
|
-
- run: bundle exec
|
28
|
+
- run: bundle exec appraisal install
|
29
|
+
- run: bundle exec appraisal rspec
|
30
30
|
coverage:
|
31
31
|
runs-on: ubuntu-latest
|
32
32
|
steps:
|
@@ -41,5 +41,5 @@ jobs:
|
|
41
41
|
env:
|
42
42
|
CC_TEST_REPORTER_ID: ${{secrets.CC_TEST_REPORTER_ID}}
|
43
43
|
with:
|
44
|
-
coverageCommand: bundle exec
|
44
|
+
coverageCommand: bundle exec rspec
|
45
45
|
debug: true
|
data/.gitignore
CHANGED
data/.rubocop.yml
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
---
|
2
2
|
require:
|
3
3
|
- rubocop-performance
|
4
|
-
- rubocop-rake
|
5
4
|
- rubocop-rspec
|
6
5
|
|
7
6
|
AllCops:
|
@@ -45,6 +44,11 @@ RSpec/MultipleMemoizedHelpers:
|
|
45
44
|
Style/Documentation:
|
46
45
|
Enabled: false
|
47
46
|
|
47
|
+
Style/FrozenStringLiteralComment:
|
48
|
+
Enabled: true
|
49
|
+
Exclude:
|
50
|
+
- 'gemfiles/*'
|
51
|
+
|
48
52
|
Style/StringLiterals:
|
49
53
|
Enabled: true
|
50
54
|
EnforcedStyle: double_quotes
|
data/Appraisals
ADDED
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,17 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## [0.2.1](https://github.com/matteoredz/rack-idempotency_key/compare/v0.2.0...v0.2.1) (2025-01-31)
|
4
|
+
|
5
|
+
|
6
|
+
### Continuous Integration
|
7
|
+
|
8
|
+
* add rack 2 and 3 via appraisal gem ([#21](https://github.com/matteoredz/rack-idempotency_key/issues/21)) ([b603427](https://github.com/matteoredz/rack-idempotency_key/commit/b603427915422dabf658dcaad5a2af5199f64d44))
|
9
|
+
|
10
|
+
|
11
|
+
### Code Refactoring
|
12
|
+
|
13
|
+
* remove direct dependency on redis-rb ([#19](https://github.com/matteoredz/rack-idempotency_key/issues/19)) ([08d7a48](https://github.com/matteoredz/rack-idempotency_key/commit/08d7a488876c2029c8f8b80ce54ff1ad0086532e))
|
14
|
+
|
3
15
|
## [0.2.0](https://github.com/matteoredz/rack-idempotency_key/compare/v0.1.1...v0.2.0) (2025-01-30)
|
4
16
|
|
5
17
|
|
data/Gemfile
CHANGED
@@ -4,14 +4,15 @@ source "https://rubygems.org"
|
|
4
4
|
|
5
5
|
gemspec
|
6
6
|
|
7
|
+
gem "appraisal", "~> 2.5"
|
7
8
|
gem "byebug"
|
8
9
|
gem "mock_redis"
|
10
|
+
gem "rack", "~> 3.1", ">= 3.1.9"
|
9
11
|
gem "rack-test"
|
10
|
-
gem "
|
11
|
-
gem "rspec"
|
12
|
+
gem "redis", "~> 5.3"
|
13
|
+
gem "rspec", "~> 3.13"
|
12
14
|
gem "rubocop"
|
13
15
|
gem "rubocop-performance"
|
14
|
-
gem "rubocop-rake"
|
15
16
|
gem "rubocop-rspec"
|
16
17
|
gem "simplecov"
|
17
18
|
gem "timecop"
|
data/README.md
CHANGED
@@ -76,13 +76,13 @@ Rack::IdempotencyKey::MemoryStore.new(expires_in: 300)
|
|
76
76
|
|
77
77
|
### RedisStore
|
78
78
|
|
79
|
-
This one is the suggested store to use in production. It relies on the [redis
|
79
|
+
This one is the suggested store to use in production. It relies on the [redis-rb](https://github.com/redis/redis-rb) gem, so make sure you're bundling it with your application.
|
80
80
|
|
81
81
|
```ruby
|
82
|
-
Rack::IdempotencyKey::RedisStore.new(Redis.
|
82
|
+
Rack::IdempotencyKey::RedisStore.new(Redis.new)
|
83
83
|
|
84
84
|
# Explicitly set the key's expiration, in seconds. The default is 300 (5 minutes)
|
85
|
-
Rack::IdempotencyKey::RedisStore.new(Redis.
|
85
|
+
Rack::IdempotencyKey::RedisStore.new(Redis.new, expires_in: 300)
|
86
86
|
```
|
87
87
|
|
88
88
|
If you're using a [Connection Pool](https://github.com/mperham/connection_pool), you can pass it instead of the single instance:
|
@@ -147,14 +147,9 @@ The Array returned must conform to the [Rack Specification](https://github.com/r
|
|
147
147
|
## Development
|
148
148
|
|
149
149
|
After checking out the repo, run `bin/setup` to install dependencies.
|
150
|
-
Then, run `
|
150
|
+
Then, run `bundle exec rspec` to run the tests.
|
151
151
|
You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
152
152
|
|
153
|
-
To install this gem onto your local machine, run `bundle exec rake install`.
|
154
|
-
To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`,
|
155
|
-
which will create a git tag for the version, push git commits and tags,
|
156
|
-
and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
157
|
-
|
158
153
|
## Contributing
|
159
154
|
|
160
155
|
Bug reports and pull requests are welcome on GitHub at https://github.com/matteoredz/rack-idempotency_key.
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# This file was generated by Appraisal
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
4
|
+
|
5
|
+
gem "appraisal", "~> 2.5"
|
6
|
+
gem "byebug"
|
7
|
+
gem "mock_redis"
|
8
|
+
gem "rack", "~> 2.0"
|
9
|
+
gem "rack-test"
|
10
|
+
gem "redis", "~> 5.3"
|
11
|
+
gem "rspec", "~> 3.13"
|
12
|
+
gem "rubocop"
|
13
|
+
gem "rubocop-performance"
|
14
|
+
gem "rubocop-rspec"
|
15
|
+
gem "simplecov"
|
16
|
+
gem "timecop"
|
17
|
+
|
18
|
+
gemspec path: "../"
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# This file was generated by Appraisal
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
4
|
+
|
5
|
+
gem "appraisal", "~> 2.5"
|
6
|
+
gem "byebug"
|
7
|
+
gem "mock_redis"
|
8
|
+
gem "rack", "~> 3.0"
|
9
|
+
gem "rack-test"
|
10
|
+
gem "redis", "~> 5.3"
|
11
|
+
gem "rspec", "~> 3.13"
|
12
|
+
gem "rubocop"
|
13
|
+
gem "rubocop-performance"
|
14
|
+
gem "rubocop-rspec"
|
15
|
+
gem "simplecov"
|
16
|
+
gem "timecop"
|
17
|
+
|
18
|
+
gemspec path: "../"
|
data/idempotency_key.gemspec
CHANGED
@@ -4,14 +4,47 @@ require "redis"
|
|
4
4
|
|
5
5
|
module Rack
|
6
6
|
class IdempotencyKey
|
7
|
+
# Redis-based store for handling idempotency keys.
|
8
|
+
#
|
9
|
+
# This class provides methods to store, retrieve, and delete idempotency keys
|
10
|
+
# in a Redis database, ensuring that the same request is not processed multiple
|
11
|
+
# times. It supports both direct Redis instances and connection pools.
|
12
|
+
#
|
13
|
+
# @example Using a direct Redis instance
|
14
|
+
# redis = Redis.new
|
15
|
+
# store = Rack::IdempotencyKey::RedisStore.new(redis)
|
16
|
+
#
|
17
|
+
# @example Using a Redis connection pool
|
18
|
+
# redis_pool = ConnectionPool.new(size: 5, timeout: 5) { Redis.new }
|
19
|
+
# store = Rack::IdempotencyKey::RedisStore.new(redis_pool)
|
7
20
|
class RedisStore
|
8
21
|
DEFAULT_EXPIRATION = 300 # 5 minutes in seconds
|
9
22
|
|
23
|
+
# Initializes a new RedisStore instance.
|
24
|
+
#
|
25
|
+
# @param store [Redis, ConnectionPool] A Redis instance or a connection pool.
|
26
|
+
# @param expires_in [Integer] The default expiration time for stored values, in seconds.
|
27
|
+
#
|
28
|
+
# @example
|
29
|
+
# redis = Redis.new
|
30
|
+
# store = Rack::IdempotencyKey::RedisStore.new(redis, expires_in: 600)
|
10
31
|
def initialize(store, expires_in: DEFAULT_EXPIRATION)
|
11
32
|
@store = store
|
12
33
|
@expires_in = expires_in
|
13
34
|
end
|
14
35
|
|
36
|
+
# Retrieves a value from Redis by key.
|
37
|
+
#
|
38
|
+
# The stored value is expected to be JSON-encoded and is automatically parsed.
|
39
|
+
# If the key does not exist, `nil` is returned.
|
40
|
+
#
|
41
|
+
# @param key [String] The Redis key to retrieve.
|
42
|
+
# @return [Object, nil] The parsed JSON value, or `nil` if the key does not exist.
|
43
|
+
#
|
44
|
+
# @raise [Rack::IdempotencyKey::StoreError] If a Redis-related error occurs.
|
45
|
+
#
|
46
|
+
# @example Retrieve a value from Redis
|
47
|
+
# store.get("key") # => "value"
|
15
48
|
def get(key)
|
16
49
|
value = with_redis { |redis| redis.get(key) }
|
17
50
|
JSON.parse(value) unless value.nil?
|
@@ -19,6 +52,22 @@ module Rack
|
|
19
52
|
raise Rack::IdempotencyKey::StoreError, "#{self.class}: #{e.message}"
|
20
53
|
end
|
21
54
|
|
55
|
+
# Stores a value in Redis with an optional time-to-live (TTL).
|
56
|
+
#
|
57
|
+
# This method ensures that the key is only set if it does not already exist (`NX` flag).
|
58
|
+
# If the key is already present, a `ConflictError` is raised.
|
59
|
+
#
|
60
|
+
# @param key [String] The Redis key to set.
|
61
|
+
# @param value [String] The value to store.
|
62
|
+
# @param ttl [Integer] The expiration time in seconds (defaults to `expires_in`).
|
63
|
+
#
|
64
|
+
# @return [Object, nil] The stored value retrieved from Redis.
|
65
|
+
#
|
66
|
+
# @raise [Rack::IdempotencyKey::ConflictError] If the key already exists and is locked.
|
67
|
+
# @raise [Rack::IdempotencyKey::StoreError] If a Redis-related error occurs.
|
68
|
+
#
|
69
|
+
# @example Store a new idempotency key
|
70
|
+
# store.set("key", "value", ttl: 600)
|
22
71
|
def set(key, value, ttl: expires_in)
|
23
72
|
with_redis do |redis|
|
24
73
|
result = redis.set(key, value, nx: true, ex: ttl)
|
@@ -30,6 +79,17 @@ module Rack
|
|
30
79
|
raise Rack::IdempotencyKey::StoreError, "#{self.class}: #{e.message}"
|
31
80
|
end
|
32
81
|
|
82
|
+
# Deletes a key from Redis.
|
83
|
+
#
|
84
|
+
# This method removes the idempotency key from Redis, allowing the same request
|
85
|
+
# to be processed again in the future.
|
86
|
+
#
|
87
|
+
# @param key [String] The Redis key to delete.
|
88
|
+
#
|
89
|
+
# @raise [Rack::IdempotencyKey::StoreError] If a Redis-related error occurs.
|
90
|
+
#
|
91
|
+
# @example Remove an idempotency key
|
92
|
+
# store.unset("key")
|
33
93
|
def unset(key)
|
34
94
|
with_redis { |redis| redis.del(key) }
|
35
95
|
rescue Redis::BaseError => e
|
data/lib/rack/idempotency_key.rb
CHANGED
@@ -5,7 +5,13 @@ require "rack/idempotency_key/error"
|
|
5
5
|
|
6
6
|
# Stores
|
7
7
|
require "rack/idempotency_key/memory_store"
|
8
|
-
|
8
|
+
|
9
|
+
begin
|
10
|
+
require "rack/idempotency_key/redis_store"
|
11
|
+
rescue LoadError => e
|
12
|
+
warn "RedisStore was not required: #{e.message}"
|
13
|
+
warn "* Add 'redis' to your bundle to use this store."
|
14
|
+
end
|
9
15
|
|
10
16
|
# Collaborators
|
11
17
|
require "rack/idempotency_key/request"
|
metadata
CHANGED
@@ -1,29 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rack-idempotency_key
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matteo Rossi
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-01-
|
12
|
-
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: redis
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - "~>"
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '5.2'
|
20
|
-
type: :runtime
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - "~>"
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: '5.2'
|
11
|
+
date: 2025-01-31 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
27
13
|
description:
|
28
14
|
email:
|
29
15
|
- mttrss5@gmail.com
|
@@ -39,14 +25,16 @@ files:
|
|
39
25
|
- ".gitignore"
|
40
26
|
- ".rspec"
|
41
27
|
- ".rubocop.yml"
|
28
|
+
- Appraisals
|
42
29
|
- CHANGELOG.md
|
43
30
|
- CODE_OF_CONDUCT.md
|
44
31
|
- Gemfile
|
45
32
|
- README.md
|
46
|
-
- Rakefile
|
47
33
|
- bin/console
|
48
34
|
- bin/release
|
49
35
|
- bin/setup
|
36
|
+
- gemfiles/rack_2.gemfile
|
37
|
+
- gemfiles/rack_3.gemfile
|
50
38
|
- idempotency_key.gemspec
|
51
39
|
- lib/rack/idempotency_key.rb
|
52
40
|
- lib/rack/idempotency_key/error.rb
|