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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e373df67d62807148650baff69ae771992a755e3188e1ed7cc073ab1a96f4752
4
- data.tar.gz: d03656ab0464a9daa8773964de916fbc8f65cabed93449a79b39eedd854f044d
3
+ metadata.gz: 157af590e24f6928fc61715b006c746296b9a9b0192d42bf14975b1722117dc7
4
+ data.tar.gz: 41f8668647cf15a010732444f5c8071f93dc1434e9be5e0edbd3a45c403854bb
5
5
  SHA512:
6
- metadata.gz: 99c992fa0e6d742aac0d4b310034df7e329baf13dbb890b5adde181e433bbafccf2baa4d5d53ac9a3b99382d49592b094a67ccd8f624a5cd89006365a500890e
7
- data.tar.gz: 4d2ed1c9827cca08f177ccc09d5af6e717213d705ccf95d712772d91f0be21ab769f13b0dc373e09762d30d7b5a0538a766fd4b5d4320789aecf5901617eda2d
6
+ metadata.gz: e3b80b32393b26f2ef685948a39b95c0acbcf549a2b6d3bc08b3e654faacd866aa707d5d412e5a80b76d388dbfe417778d484fdbc1bda7c773ed6236d7adbe8f
7
+ data.tar.gz: c9b2b228020e64ce63c4aa0f19a9216a84812a48be30be15701d0eda01fd5d9e46d1c5d841b19cf3848e9168332bdcc907d1906ccfa96acc0d679b7f96f31409
@@ -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 rake
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 rake
44
+ coverageCommand: bundle exec rspec
45
45
  debug: true
data/.gitignore CHANGED
@@ -12,6 +12,7 @@
12
12
  /doc/
13
13
  /dump.rdb
14
14
  /Gemfile.lock
15
+ /gemfiles/*.lock
15
16
  /pkg/
16
17
  /rack-idempotency_key-*.gem
17
18
  /spec/reports/
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
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ appraise "rack-2" do
4
+ gem "rack", "~> 2.0"
5
+ end
6
+
7
+ appraise "rack-3" do
8
+ gem "rack", "~> 3.0"
9
+ end
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 "rake"
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 gem](https://github.com/redis/redis-rb).
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.current)
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.current, expires_in: 300)
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 `rake test` to run the tests.
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: "../"
@@ -24,6 +24,4 @@ Gem::Specification.new do |spec|
24
24
  spec.bindir = "exe"
25
25
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
26
26
  spec.require_paths = ["lib"]
27
-
28
- spec.add_runtime_dependency "redis", "~> 5.2"
29
27
  end
@@ -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
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Rack
4
4
  class IdempotencyKey
5
- VERSION = "0.2.0"
5
+ VERSION = "0.2.1"
6
6
  end
7
7
  end
@@ -5,7 +5,13 @@ require "rack/idempotency_key/error"
5
5
 
6
6
  # Stores
7
7
  require "rack/idempotency_key/memory_store"
8
- require "rack/idempotency_key/redis_store"
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.0
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-30 00:00:00.000000000 Z
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
data/Rakefile DELETED
@@ -1,8 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "bundler/gem_tasks"
4
- require "rspec/core/rake_task"
5
-
6
- RSpec::Core::RakeTask.new(:spec)
7
-
8
- task default: :spec