rack-idempotency_key 0.2.0 → 0.2.1

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.
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