|
1 | 1 | module Flipper |
2 | 2 | module Adapters |
3 | 3 | module RedisShared |
| 4 | + private |
| 5 | + |
| 6 | + # Safely executes a block with a Redis connection, handling compatibility |
| 7 | + # issues between different Redis client versions and Rails versions. |
| 8 | + # |
| 9 | + # This method exists to fix a compatibility issue between Rails 7.1.* and |
| 10 | + # Redis versions below 4.7.0. The issue occurs because: |
| 11 | + # |
| 12 | + # 1. In Redis versions below 4.7.0, the `with` method is not defined on |
| 13 | + # the Redis client, so Flipper would fall back to `yield(@client)` |
| 14 | + # 2. However, Rails 7.1.* introduced `Object#with` via ActiveSupport, |
| 15 | + # which shadows the Redis client's `with` method |
| 16 | + # 3. Rails 7.1.*'s `Object#with` doesn't pass `self` to the block parameter |
| 17 | + # (this was fixed in Rails 7.2.0), causing the block parameter to be `nil` |
| 18 | + # |
| 19 | + # This method ensures that: |
| 20 | + # - For Redis >= 4.7.0: Uses the Redis client's native `with` method |
| 21 | + # - For ConnectionPool: Uses the ConnectionPool's `with` method |
| 22 | + # - For Redis < 4.7.0: Falls back to `yield(@client)` to avoid the Rails |
| 23 | + # ActiveSupport `Object#with` method |
| 24 | + # |
| 25 | + # @see https://github.com/redis/redis-rb/blob/master/CHANGELOG.md#470 |
| 26 | + # @see https://github.com/rails/rails/pull/46681 |
| 27 | + # @see https://github.com/rails/rails/pull/50470 |
4 | 28 | def with_connection(&block) |
5 | | - @client.respond_to?(:with) ? @client.with(&block) : yield(@client) |
| 29 | + if client_has_correct_with_method? |
| 30 | + @client.with(&block) |
| 31 | + else |
| 32 | + yield(@client) |
| 33 | + end |
| 34 | + end |
| 35 | + |
| 36 | + # Determines if the Redis client has a safe `with` method that can be used |
| 37 | + # without conflicts with Rails ActiveSupport's `Object#with`. |
| 38 | + # |
| 39 | + # This method checks for: |
| 40 | + # 1. ConnectionPool instances (which have their own `with` method) |
| 41 | + # 2. Redis instances with version >= 4.7.0 (which have a proper `with` method) |
| 42 | + # |
| 43 | + # The method caches its result to avoid repeated checks. |
| 44 | + # |
| 45 | + # @return [Boolean] true if the client has a safe `with` method, false otherwise |
| 46 | + def client_has_correct_with_method? |
| 47 | + return @client_has_correct_with_method if defined?(@client_has_correct_with_method) |
| 48 | + |
| 49 | + @client_has_correct_with_method = @client.respond_to?(:with) && (client_is_connection_pool? || client_is_redis_that_has_with?) |
| 50 | + rescue |
| 51 | + @client_has_correct_with_method = false |
| 52 | + end |
| 53 | + |
| 54 | + def client_is_connection_pool? |
| 55 | + defined?(ConnectionPool) && @client.is_a?(ConnectionPool) |
| 56 | + end |
| 57 | + |
| 58 | + def client_is_redis_that_has_with? |
| 59 | + @client.is_a?(::Redis) && defined?(::Redis::VERSION) && |
| 60 | + ::Gem::Version.new(::Redis::VERSION) >= ::Gem::Version.new('4.7.0') |
6 | 61 | end |
7 | 62 | end |
8 | 63 | end |
|
0 commit comments