familia 1.2.1 → 2.0.0.pre.pre

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.
Files changed (115) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +68 -0
  3. data/.github/workflows/docs.yml +64 -0
  4. data/.gitignore +4 -0
  5. data/.pre-commit-config.yaml +3 -1
  6. data/.rubocop.yml +16 -9
  7. data/.rubocop_todo.yml +177 -31
  8. data/.yardopts +9 -0
  9. data/CLAUDE.md +141 -0
  10. data/Gemfile +15 -2
  11. data/Gemfile.lock +76 -34
  12. data/README.md +39 -23
  13. data/bin/irb +3 -0
  14. data/docs/connection_pooling.md +317 -0
  15. data/familia.gemspec +9 -5
  16. data/lib/familia/base.rb +19 -9
  17. data/lib/familia/connection.rb +232 -65
  18. data/lib/familia/core_ext.rb +1 -1
  19. data/lib/familia/datatype/commands.rb +59 -0
  20. data/lib/familia/{redistype → datatype}/serialization.rb +9 -13
  21. data/lib/familia/{redistype → datatype}/types/hashkey.rb +25 -25
  22. data/lib/familia/{redistype → datatype}/types/list.rb +13 -13
  23. data/lib/familia/{redistype → datatype}/types/sorted_set.rb +20 -20
  24. data/lib/familia/{redistype → datatype}/types/string.rb +22 -21
  25. data/lib/familia/{redistype → datatype}/types/unsorted_set.rb +11 -11
  26. data/lib/familia/datatype.rb +243 -0
  27. data/lib/familia/errors.rb +5 -2
  28. data/lib/familia/features/expiration.rb +33 -34
  29. data/lib/familia/features/quantization.rb +9 -3
  30. data/lib/familia/features/safe_dump.rb +2 -3
  31. data/lib/familia/features.rb +2 -2
  32. data/lib/familia/horreum/class_methods.rb +97 -110
  33. data/lib/familia/horreum/commands.rb +46 -51
  34. data/lib/familia/horreum/connection.rb +82 -0
  35. data/lib/familia/horreum/{relations_management.rb → related_fields_management.rb} +37 -35
  36. data/lib/familia/horreum/serialization.rb +61 -198
  37. data/lib/familia/horreum/settings.rb +6 -17
  38. data/lib/familia/horreum/utils.rb +11 -10
  39. data/lib/familia/horreum.rb +69 -60
  40. data/lib/familia/logging.rb +12 -12
  41. data/lib/familia/multi_result.rb +72 -0
  42. data/lib/familia/refinements.rb +7 -44
  43. data/lib/familia/settings.rb +11 -11
  44. data/lib/familia/utils.rb +123 -90
  45. data/lib/familia/version.rb +4 -21
  46. data/lib/familia.rb +17 -12
  47. data/lib/middleware/database_middleware.rb +150 -0
  48. data/try/configuration/scenarios_try.rb +65 -0
  49. data/try/core/connection_try.rb +58 -0
  50. data/try/core/errors_try.rb +93 -0
  51. data/try/core/extensions_try.rb +26 -0
  52. data/try/{10_familia_try.rb → core/familia_extended_try.rb} +11 -10
  53. data/try/{00_familia_try.rb → core/familia_try.rb} +5 -3
  54. data/try/core/middleware_try.rb +68 -0
  55. data/try/core/refinements_try.rb +39 -0
  56. data/try/core/settings_try.rb +76 -0
  57. data/try/core/tools_try.rb +54 -0
  58. data/try/core/utils_try.rb +189 -0
  59. data/try/{26_redis_bool_try.rb → datatypes/boolean_try.rb} +4 -2
  60. data/try/datatypes/datatype_base_try.rb +69 -0
  61. data/try/{25_redis_type_hash_try.rb → datatypes/hash_try.rb} +5 -3
  62. data/try/{23_redis_type_list_try.rb → datatypes/list_try.rb} +5 -3
  63. data/try/{22_redis_type_set_try.rb → datatypes/set_try.rb} +5 -3
  64. data/try/{21_redis_type_zset_try.rb → datatypes/sorted_set_try.rb} +6 -4
  65. data/try/{24_redis_type_string_try.rb → datatypes/string_try.rb} +8 -8
  66. data/try/edge_cases/empty_identifiers_try.rb +48 -0
  67. data/try/{92_symbolize_try.rb → edge_cases/hash_symbolization_try.rb} +12 -7
  68. data/try/edge_cases/json_serialization_try.rb +85 -0
  69. data/try/edge_cases/race_conditions_try.rb +60 -0
  70. data/try/edge_cases/reserved_keywords_try.rb +59 -0
  71. data/try/{93_string_coercion_try.rb → edge_cases/string_coercion_try.rb} +60 -59
  72. data/try/edge_cases/ttl_side_effects_try.rb +51 -0
  73. data/try/features/expiration_try.rb +86 -0
  74. data/try/features/quantization_try.rb +90 -0
  75. data/try/{35_feature_safedump_try.rb → features/safe_dump_advanced_try.rb} +7 -6
  76. data/try/features/safe_dump_try.rb +137 -0
  77. data/try/{test_helpers.rb → helpers/test_helpers.rb} +25 -60
  78. data/try/{27_redis_horreum_try.rb → horreum/base_try.rb} +39 -14
  79. data/try/horreum/class_methods_try.rb +41 -0
  80. data/try/horreum/commands_try.rb +49 -0
  81. data/try/{29_redis_horreum_initialization_try.rb → horreum/initialization_try.rb} +9 -7
  82. data/try/horreum/relations_try.rb +146 -0
  83. data/try/{28_redis_horreum_serialization_try.rb → horreum/serialization_try.rb} +13 -11
  84. data/try/horreum/settings_try.rb +43 -0
  85. data/try/integration/cross_component_try.rb +46 -0
  86. data/try/{41_customer_safedump_try.rb → models/customer_safe_dump_try.rb} +9 -7
  87. data/try/{40_customer_try.rb → models/customer_try.rb} +20 -17
  88. data/try/models/datatype_base_try.rb +101 -0
  89. data/try/{30_familia_object_try.rb → models/familia_object_try.rb} +18 -16
  90. data/try/performance/benchmarks_try.rb +55 -0
  91. data/try/pooling/README.md +20 -0
  92. data/try/pooling/configurable_stress_test_try.rb +435 -0
  93. data/try/pooling/connection_pool_test_try.rb +273 -0
  94. data/try/pooling/lib/atomic_saves_v3_connection_pool_helpers.rb +192 -0
  95. data/try/pooling/lib/connection_pool_metrics.rb +372 -0
  96. data/try/pooling/lib/connection_pool_stress_test.rb +959 -0
  97. data/try/pooling/lib/connection_pool_threading_models.rb +421 -0
  98. data/try/pooling/lib/visualize_stress_results.rb +434 -0
  99. data/try/pooling/pool_siege_try.rb +509 -0
  100. data/try/pooling/run_stress_tests_try.rb +482 -0
  101. data/try/prototypes/atomic_saves_v1_context_proxy.rb +121 -0
  102. data/try/prototypes/atomic_saves_v2_connection_switching.rb +161 -0
  103. data/try/prototypes/atomic_saves_v3_connection_pool.rb +189 -0
  104. data/try/prototypes/atomic_saves_v4.rb +105 -0
  105. data/try/prototypes/lib/atomic_saves_v2_connection_switching_helpers.rb +124 -0
  106. data/try/prototypes/lib/atomic_saves_v3_connection_pool_helpers.rb +192 -0
  107. metadata +140 -43
  108. data/.github/workflows/ruby.yml +0 -71
  109. data/VERSION.yml +0 -4
  110. data/lib/familia/redistype/commands.rb +0 -59
  111. data/lib/familia/redistype.rb +0 -228
  112. data/lib/familia/tools.rb +0 -68
  113. data/lib/redis_middleware.rb +0 -109
  114. data/try/20_redis_type_try.rb +0 -70
  115. data/try/91_json_bug_try.rb +0 -86
@@ -0,0 +1,243 @@
1
+ # lib/familia/datatype.rb
2
+
3
+ require_relative 'datatype/commands'
4
+ require_relative 'datatype/serialization'
5
+
6
+ module Familia
7
+
8
+ # DataType - Base class for Database data type wrappers
9
+ #
10
+ # This class provides common functionality for various Database data types
11
+ # such as String, List, Set, SortedSet, and HashKey.
12
+ #
13
+ # @abstract Subclass and implement Database data type specific methods
14
+ class DataType
15
+ include Familia::Base
16
+ extend Familia::Features
17
+
18
+ @registered_types = {}
19
+ @valid_options = %i[class parent default_expiration default logical_database dbkey dbclient suffix prefix]
20
+ @logical_database = nil
21
+
22
+ feature :expiration
23
+ feature :quantization
24
+
25
+ class << self
26
+ attr_reader :registered_types, :valid_options, :has_relations
27
+ attr_accessor :parent
28
+ attr_writer :logical_database, :uri
29
+ end
30
+
31
+ module ClassMethods
32
+ # To be called inside every class that inherits DataType
33
+ # +methname+ is the term used for the class and instance methods
34
+ # that are created for the given +klass+ (e.g. set, list, etc)
35
+ def register(klass, methname)
36
+ Familia.ld "[#{self}] Registering #{klass} as #{methname.inspect}"
37
+
38
+ @registered_types[methname] = klass
39
+ end
40
+
41
+ def logical_database(val = nil)
42
+ @logical_database = val unless val.nil?
43
+ @logical_database || parent&.logical_database
44
+ end
45
+
46
+ def uri(val = nil)
47
+ @uri = val unless val.nil?
48
+ @uri || (parent ? parent.uri : Familia.uri)
49
+ end
50
+
51
+ def inherited(obj)
52
+ Familia.trace :DATATYPE, nil, "#{obj} is my kinda type", caller(1..1) if Familia.debug?
53
+ obj.logical_database = logical_database
54
+ obj.default_expiration = default_expiration # method added via Features::Expiration
55
+ obj.uri = uri
56
+ obj.parent = self
57
+ super(obj)
58
+ end
59
+
60
+ def valid_keys_only(opts)
61
+ opts.select { |k, _| DataType.valid_options.include? k }
62
+ end
63
+
64
+ def has_relations?
65
+ @has_relations ||= false
66
+ end
67
+ end
68
+ extend ClassMethods
69
+
70
+ attr_reader :keystring, :parent, :opts
71
+ attr_writer :dump_method, :load_method
72
+
73
+ # +keystring+: If parent is set, this will be used as the suffix
74
+ # for dbkey. Otherwise this becomes the value of the key.
75
+ # If this is an Array, the elements will be joined.
76
+ #
77
+ # Options:
78
+ #
79
+ # :class => A class that responds to Familia.load_method and
80
+ # Familia.dump_method. These will be used when loading and
81
+ # saving data from/to the database to unmarshal/marshal the class.
82
+ #
83
+ # :parent => The Familia object that this datatype object belongs
84
+ # to. This can be a class that includes Familia or an instance.
85
+ #
86
+ # :default_expiration => the time to live in seconds. When not nil, this will
87
+ # set the default expiration for this dbkey whenever #save is called.
88
+ # You can also call it explicitly via #update_expiration.
89
+ #
90
+ # :default => the default value (String-only)
91
+ #
92
+ # :logical_database => the logical database index to use (ignored if :dbclient is used).
93
+ #
94
+ # :dbclient => an instance of database client.
95
+ #
96
+ # :dbkey => a hardcoded key to use instead of the deriving the from
97
+ # the name and parent (e.g. a derived key: customer:custid:secret_counter).
98
+ #
99
+ # :suffix => the suffix to use for the key (e.g. 'scores' in customer:custid:scores).
100
+ # :prefix => the prefix to use for the key (e.g. 'customer' in customer:custid:scores).
101
+ #
102
+ # Connection precendence: uses the database connection of the parent or the
103
+ # value of opts[:dbclient] or Familia.dbclient (in that order).
104
+ def initialize(keystring, opts = {})
105
+ #Familia.ld " [initializing] #{self.class} #{opts}"
106
+ @keystring = keystring
107
+ @keystring = @keystring.join(Familia.delim) if @keystring.is_a?(Array)
108
+
109
+ # Remove all keys from the opts that are not in the allowed list
110
+ @opts = opts || {}
111
+ @opts = DataType.valid_keys_only(@opts)
112
+
113
+ # Apply the options to instance method setters of the same name
114
+ @opts.each do |k, v|
115
+ # Bewarde logging :parent instance here implicitly calls #to_s which for
116
+ # some classes could include the identifier which could still be nil at
117
+ # this point. This would result in a Familia::Problem being raised. So
118
+ # to be on the safe-side here until we have a better understanding of
119
+ # the issue, we'll just log the class name for each key-value pair.
120
+ Familia.ld " [setting] #{k} #{v.class}"
121
+ send(:"#{k}=", v) if respond_to? :"#{k}="
122
+ end
123
+
124
+ init if respond_to? :init
125
+ end
126
+
127
+ def dbclient
128
+ return Fiber[:familia_transaction] if Fiber[:familia_transaction]
129
+ return @dbclient if @dbclient
130
+
131
+ parent? ? parent.dbclient : Familia.dbclient(opts[:logical_database])
132
+ end
133
+
134
+ # Produces the full dbkey for this object.
135
+ #
136
+ # @return [String] The full dbkey.
137
+ #
138
+ # This method determines the appropriate dbkey based on the context of the DataType object:
139
+ #
140
+ # 1. If a hardcoded key is set in the options, it returns that key.
141
+ # 2. For instance-level DataType objects, it uses the parent instance's dbkey method.
142
+ # 3. For class-level DataType objects, it uses the parent class's dbkey method.
143
+ # 4. For standalone DataType objects, it uses the keystring as the full dbkey.
144
+ #
145
+ # For class-level DataType objects (parent_class? == true):
146
+ # - The suffix is optional and used to differentiate between different types of objects.
147
+ # - If no suffix is provided, the class's default suffix is used (via the self.suffix method).
148
+ # - If a nil suffix is explicitly passed, it won't appear in the resulting dbkey.
149
+ # - Passing nil as the suffix is how class-level DataType objects are created without
150
+ # the global default 'object' suffix.
151
+ #
152
+ # @example Instance-level DataType
153
+ # user_instance.some_datatype.dbkey # => "user:123:some_datatype"
154
+ #
155
+ # @example Class-level DataType
156
+ # User.some_datatype.dbkey # => "user:some_datatype"
157
+ #
158
+ # @example Standalone DataType
159
+ # DataType.new("mykey").dbkey # => "mykey"
160
+ #
161
+ # @example Class-level DataType with explicit nil suffix
162
+ # User.dbkey("123", nil) # => "user:123"
163
+ #
164
+ def dbkey
165
+ # Return the hardcoded key if it's set. This is useful for
166
+ # support legacy keys that aren't derived in the same way.
167
+ return opts[:dbkey] if opts[:dbkey]
168
+
169
+ if parent_instance?
170
+ # This is an instance-level datatype object so the parent instance's
171
+ # dbkey method is defined in Familia::Horreum::InstanceMethods.
172
+ parent.dbkey(keystring)
173
+ elsif parent_class?
174
+ # This is a class-level datatype object so the parent class' dbkey
175
+ # method is defined in Familia::Horreum::ClassMethods.
176
+ parent.dbkey(keystring, nil)
177
+ else
178
+ # This is a standalone DataType object where it's keystring
179
+ # is the full database key (dbkey).
180
+ keystring
181
+ end
182
+ end
183
+
184
+ def class?
185
+ !@opts[:class].to_s.empty? && @opts[:class].is_a?(Familia)
186
+ end
187
+
188
+ def parent_instance?
189
+ parent.is_a?(Familia::Horreum)
190
+ end
191
+
192
+ def parent_class?
193
+ parent.is_a?(Class) && parent <= Familia::Horreum
194
+ end
195
+
196
+ def parent?
197
+ parent_class? || parent_instance?
198
+ end
199
+
200
+ def parent
201
+ @opts[:parent]
202
+ end
203
+
204
+ def logical_database
205
+ @opts[:logical_database] || self.class.logical_database
206
+ end
207
+
208
+ def uri
209
+ # If a specific URI is set in opts, use it
210
+ return @opts[:uri] if @opts[:uri]
211
+
212
+ # If parent has a DB set, create a URI with that DB
213
+ if parent? && parent.respond_to?(:logical_database) && parent.logical_database
214
+ base_uri = self.class.uri || Familia.uri
215
+ if base_uri
216
+ uri_with_db = base_uri.dup
217
+ uri_with_db.db = parent.logical_database
218
+ return uri_with_db
219
+ end
220
+ end
221
+
222
+ # Otherwise fall back to class URI
223
+ self.class.uri
224
+ end
225
+
226
+ def dump_method
227
+ @dump_method || self.class.dump_method
228
+ end
229
+
230
+ def load_method
231
+ @load_method || self.class.load_method
232
+ end
233
+
234
+ include Commands
235
+ include Serialization
236
+ end
237
+
238
+ require_relative 'datatype/types/list'
239
+ require_relative 'datatype/types/unsorted_set'
240
+ require_relative 'datatype/types/sorted_set'
241
+ require_relative 'datatype/types/hashkey'
242
+ require_relative 'datatype/types/string'
243
+ end
@@ -1,5 +1,5 @@
1
- # frozen_string_literal: true
2
-
1
+ # lib/familia/errors.rb
2
+ #
3
3
  module Familia
4
4
  class Problem < RuntimeError; end
5
5
  class NoIdentifier < Problem; end
@@ -31,6 +31,9 @@ module Familia
31
31
  end
32
32
  end
33
33
 
34
+ # Set Familia.connection_provider or use middleware to provide connections.
35
+ class NoConnectionAvailable < Problem; end
36
+
34
37
  # Raised when attempting to refresh an object whose key doesn't exist in Redis
35
38
  class KeyNotFoundError < Problem
36
39
  attr_reader :key
@@ -1,19 +1,18 @@
1
- # rubocop:disable all
2
- # frozen_string_literal: true
1
+ # lib/familia/features/expiration.rb
3
2
 
4
3
 
5
4
  module Familia::Features
6
5
 
7
6
  module Expiration
8
- @ttl = nil
7
+ @default_expiration = nil
9
8
 
10
9
  module ClassMethods
11
10
 
12
- attr_writer :ttl
11
+ attr_writer :default_expiration
13
12
 
14
- def ttl(v = nil)
15
- @ttl = v.to_f unless v.nil?
16
- @ttl || parent&.ttl || Familia.ttl
13
+ def default_expiration(v = nil)
14
+ @default_expiration = v.to_f unless v.nil?
15
+ @default_expiration || parent&.default_expiration || Familia.default_expiration
17
16
  end
18
17
 
19
18
  end
@@ -22,51 +21,51 @@ module Familia::Features
22
21
  Familia.ld "[#{base}] Loaded #{self}"
23
22
  base.extend ClassMethods
24
23
 
25
- # Optionally define ttl in the class to make
24
+ # Optionally define default_expiration in the class to make
26
25
  # sure we always have an array to work with.
27
- unless base.instance_variable_defined?(:@ttl)
28
- base.instance_variable_set(:@ttl, @ttl) # set above
26
+ unless base.instance_variable_defined?(:@default_expiration)
27
+ base.instance_variable_set(:@default_expiration, @default_expiration) # set above
29
28
  end
30
29
  end
31
30
 
32
- def ttl=(v)
33
- @ttl = v.to_f
31
+ def default_expiration=(v)
32
+ @default_expiration = v.to_f
34
33
  end
35
34
 
36
- def ttl
37
- @ttl || self.class.ttl
35
+ def default_expiration
36
+ @default_expiration || self.class.default_expiration
38
37
  end
39
38
 
40
- # Sets an expiration time for the Redis data associated with this object.
39
+ # Sets an expiration time for the Database data associated with this object.
41
40
  #
42
41
  # This method allows setting a Time To Live (TTL) for the data in Redis,
43
42
  # after which it will be automatically removed.
44
43
  #
45
- # @param ttl [Integer, nil] The Time To Live in seconds. If nil, the default
44
+ # @param default_expiration [Integer, nil] The Time To Live in seconds. If nil, the default
46
45
  # TTL will be used.
47
46
  #
48
47
  # @return [Boolean] Returns true if the expiration was set successfully,
49
48
  # false otherwise.
50
49
  #
51
50
  # @example Setting an expiration of one day
52
- # object.update_expiration(ttl: 86400)
51
+ # object.update_expiration(default_expiration: 86400)
53
52
  #
54
- # @note If TTL is set to zero, the expiration will be removed, making the
53
+ # @note If Default expiration is set to zero, the expiration will be removed, making the
55
54
  # data persist indefinitely.
56
55
  #
57
- # @raise [Familia::Problem] Raises an error if the TTL is not a non-negative
56
+ # @raise [Familia::Problem] Raises an error if the default expiration is not a non-negative
58
57
  # integer.
59
58
  #
60
- def update_expiration(ttl: nil)
61
- ttl ||= self.ttl
59
+ def update_expiration(default_expiration: nil)
60
+ default_expiration ||= self.default_expiration
62
61
 
63
62
  if self.class.has_relations?
64
- Familia.ld "[update_expiration] #{self.class} has relations: #{self.class.redis_types.keys}"
65
- self.class.redis_types.each do |name, definition|
66
- next if definition.opts[:ttl].nil?
63
+ Familia.ld "[update_expiration] #{self.class} has relations: #{self.class.related_fields.keys}"
64
+ self.class.related_fields.each do |name, definition|
65
+ next if definition.opts[:default_expiration].nil?
67
66
  obj = send(name)
68
- Familia.ld "[update_expiration] Updating expiration for #{name} (#{obj.rediskey}) to #{ttl}"
69
- obj.update_expiration(ttl: ttl)
67
+ Familia.ld "[update_expiration] Updating expiration for #{name} (#{obj.dbkey}) to #{default_expiration}"
68
+ obj.update_expiration(default_expiration: default_expiration)
70
69
  end
71
70
  end
72
71
 
@@ -75,23 +74,23 @@ module Familia::Features
75
74
  # retention issues (e.g. not removed in a timely fashion).
76
75
  #
77
76
  # For the same reason, we don't want to default to 0 bc there's not a
78
- # good reason for the ttl to not be set in the first place. If the
79
- # class doesn't have a ttl, the default comes from Familia.ttl (which
77
+ # good reason for the default_expiration to not be set in the first place. If the
78
+ # class doesn't have a default_expiration, the default comes from Familia.default_expiration (which
80
79
  # is 0).
81
- unless ttl.is_a?(Numeric)
82
- raise Familia::Problem, "TTL must be a number (#{ttl.class} in #{self.class})"
80
+ unless default_expiration.is_a?(Numeric)
81
+ raise Familia::Problem, "Default expiration must be a number (#{default_expiration.class} in #{self.class})"
83
82
  end
84
83
 
85
- if ttl.zero?
86
- return Familia.ld "[update_expiration] No expiration for #{self.class} (#{rediskey})"
84
+ if default_expiration.zero?
85
+ return Familia.ld "[update_expiration] No expiration for #{self.class} (#{dbkey})"
87
86
  end
88
87
 
89
- Familia.ld "[update_expiration] Expires #{rediskey} in #{ttl} seconds"
88
+ Familia.ld "[update_expiration] Expires #{dbkey} in #{default_expiration} seconds"
90
89
 
91
90
  # Redis' EXPIRE command returns 1 if the timeout was set, 0 if key does
92
91
  # not exist or the timeout could not be set. Via redis-rb here, it's
93
92
  # a bool.
94
- expire(ttl)
93
+ expire(default_expiration)
95
94
  end
96
95
 
97
96
  extend ClassMethods
@@ -1,4 +1,4 @@
1
- # rubocop:disable all
1
+ # lib/familia/features/quantization.rb
2
2
 
3
3
  module Familia::Features
4
4
 
@@ -28,7 +28,13 @@ module Familia::Features
28
28
  if quantum.is_a?(Array)
29
29
  quantum, pattern = quantum
30
30
  end
31
- quantum ||= @opts[:quantize] || ttl || 10.minutes
31
+
32
+ # Previously we erronously included `@opts.fetch(:quantize, nil)` in
33
+ # the list of default values here, but @opts is for horreum instances
34
+ # not at the class level. This method `qstamp` is part of the initial
35
+ # definition for whatever horreum subclass we're in right now. That's
36
+ # why default_expiration works (e.g. `class Plop; feature :quantization; default_expiration 90; end`).
37
+ quantum ||= default_expiration || 10.minutes
32
38
 
33
39
  # Validate quantum
34
40
  unless quantum.is_a?(Numeric) && quantum.positive?
@@ -46,7 +52,7 @@ module Familia::Features
46
52
  end
47
53
 
48
54
  def qstamp(quantum = nil, pattern: nil, time: nil)
49
- self.class.qstamp(quantum || self.class.ttl, pattern: pattern, time: time)
55
+ self.class.qstamp(quantum || self.class.default_expiration, pattern: pattern, time: time)
50
56
  end
51
57
 
52
58
  extend ClassMethods
@@ -1,5 +1,4 @@
1
- # rubocop:disable all
2
- # frozen_string_literal: true
1
+ # lib/familia/features/safe_dump.rb
3
2
 
4
3
 
5
4
  module Familia::Features
@@ -87,7 +86,7 @@ module Familia::Features
87
86
  field_name = el
88
87
  callable = lambda { |obj|
89
88
  if obj.respond_to?(:[]) && obj[field_name]
90
- obj[field_name] # Familia::RedisType classes
89
+ obj[field_name] # Familia::DataType classes
91
90
  elsif obj.respond_to?(field_name)
92
91
  obj.send(field_name) # Onetime::Models::RedisHash classes via method_missing 😩
93
92
  end
@@ -1,4 +1,4 @@
1
- # rubocop:disable all
1
+ # lib/familia/features.rb
2
2
 
3
3
  module Familia
4
4
 
@@ -28,7 +28,7 @@ module Familia
28
28
  # Extend the Familia::Base subclass (e.g. Customer) with the feature module
29
29
  include klass
30
30
 
31
- # NOTE: We may also want to extend Familia::RedisType here so that we can
31
+ # NOTE: We may also want to extend Familia::DataType here so that we can
32
32
  # call safe_dump on relations fields (e.g. list, set, zset, hashkey). Or
33
33
  # maybe that only makes sense for hashk/object relations.
34
34
  #