familia 1.0.0.pre.rc1 → 1.0.0.pre.rc3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +65 -12
- data/VERSION.yml +1 -1
- data/lib/familia/features/safe_dump.rb +21 -2
- data/lib/familia/horreum/class_methods.rb +129 -24
- data/lib/familia/horreum/commands.rb +84 -5
- data/lib/familia/horreum/serialization.rb +236 -53
- data/lib/familia/horreum/utils.rb +2 -1
- data/lib/familia/horreum.rb +58 -8
- data/lib/familia/logging.rb +4 -21
- data/lib/familia/redistype.rb +10 -10
- data/lib/familia/refinements.rb +88 -0
- data/lib/familia/types/hashkey.rb +6 -6
- data/lib/familia/types/list.rb +2 -0
- data/lib/familia/utils.rb +2 -6
- data/lib/familia.rb +1 -0
- data/try/27_redis_horreum_try.rb +53 -0
- data/try/test_helpers.rb +9 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c5cdd5bc9ccadb7e69c324e7a2716b378fa5e5fe9938c4ed44a61b23eef99c6a
|
4
|
+
data.tar.gz: de5bb8d9e3b6b09906e777f9a5586eff32a5b1d3ec7c5e51a3a26dac2bd85415
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 76ffa691585dde5c45aaa17e1d02171bacb3fad0267f638b02d7c2cf0a65eaed4d0062656be2496c27ff9bd9788839a71d8475f3e14577ef607684cd179209c1
|
7
|
+
data.tar.gz: 96d52e17f1c3f3092d4ec39ad0fbd1455ef54a902a2f2aaa65c5531d11cdd8b53ee50ef1fa0bd967bd4fa49fd4b0693d89031bcb6decb6cc2def5cb4e1d85c9a
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -1,31 +1,84 @@
|
|
1
|
-
# Familia - 1.0.0-
|
1
|
+
# Familia - 1.0.0-rc2 (August 2024)
|
2
2
|
|
3
3
|
**Organize and store ruby objects in Redis. A Ruby ORM for Redis.**
|
4
4
|
|
5
|
+
Familia provides a powerful and flexible way to interact with Redis using Ruby objects. It's designed to make working with Redis as natural as working with Ruby classes.
|
6
|
+
|
5
7
|
## Installation
|
6
8
|
|
7
9
|
Get it in one of the following ways:
|
8
10
|
|
9
|
-
* In your Gemfile: `gem 'familia', '>= 1.0.0-
|
11
|
+
* In your Gemfile: `gem 'familia', '>= 1.0.0-rc2'`
|
10
12
|
* Install it by hand: `gem install familia`
|
11
13
|
* Or for development: `git clone git@github.com:delano/familia.git`
|
12
14
|
|
13
15
|
## Basic Example
|
14
16
|
|
15
17
|
```ruby
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
18
|
+
class Flower < Familia::Horreum
|
19
|
+
identifier :generate_id
|
20
|
+
field :token
|
21
|
+
field :name
|
22
|
+
list :owners
|
23
|
+
set :tags
|
24
|
+
zset :metrics
|
25
|
+
hashkey :props
|
26
|
+
string :counter
|
27
|
+
end
|
28
|
+
```
|
29
|
+
|
30
|
+
## What Familia::Horreum Can Do
|
31
|
+
|
32
|
+
Familia::Horreum provides a powerful abstraction layer over Redis, allowing you to:
|
33
|
+
|
34
|
+
1. **Define Redis-backed Ruby Classes**: As shown in the example, you can easily define classes that map to Redis structures.
|
35
|
+
|
36
|
+
2. **Use Various Redis Data Types**: Familia supports multiple Redis data types:
|
37
|
+
- `field`: For simple key-value pairs
|
38
|
+
- `list`: For Redis lists
|
39
|
+
- `set`: For Redis sets
|
40
|
+
- `zset`: For Redis sorted sets
|
41
|
+
- `hashkey`: For Redis hashes
|
42
|
+
- `string`: For Redis strings
|
43
|
+
|
44
|
+
3. **Custom Identifiers**: Use the `identifier` method to specify how objects are uniquely identified in Redis.
|
45
|
+
|
46
|
+
4. **Automatic Serialization**: Familia handles the serialization and deserialization of your objects to and from Redis.
|
47
|
+
|
48
|
+
5. **Redis Commands as Ruby Methods**: Interact with Redis using familiar Ruby syntax instead of raw Redis commands.
|
49
|
+
|
50
|
+
6. **TTL Support**: Set expiration times for your objects in Redis.
|
51
|
+
|
52
|
+
7. **Flexible Configuration**: Configure Redis connection details, serialization methods, and more.
|
53
|
+
|
54
|
+
## Advanced Features
|
55
|
+
|
56
|
+
- **API Versioning**: Familia supports API versioning to help manage changes in your data model over time.
|
57
|
+
- **Custom Serialization**: You can specify custom serialization methods for your objects.
|
58
|
+
- **Redis URI Support**: Easily connect to Redis using URI strings.
|
59
|
+
- **Debugging Tools**: Built-in debugging capabilities to help troubleshoot Redis interactions.
|
60
|
+
|
61
|
+
## Usage Example
|
62
|
+
|
63
|
+
```ruby
|
64
|
+
# Create a new Flower
|
65
|
+
rose = Flower.new
|
66
|
+
rose.name = "Red Rose"
|
67
|
+
rose.tags << "romantic" << "red"
|
68
|
+
rose.owners.push("Alice", "Bob")
|
69
|
+
rose.save
|
70
|
+
|
71
|
+
# Retrieve a Flower
|
72
|
+
retrieved_rose = Flower.get(rose.identifier)
|
73
|
+
puts retrieved_rose.name # => "Red Rose"
|
74
|
+
puts retrieved_rose.tags.members # => ["romantic", "red"]
|
26
75
|
```
|
27
76
|
|
28
77
|
## More Information
|
29
78
|
|
30
79
|
* [Github](https://github.com/delano/familia)
|
31
80
|
* [Rubygems](https://rubygems.org/gems/familia)
|
81
|
+
|
82
|
+
## Contributing
|
83
|
+
|
84
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
data/VERSION.yml
CHANGED
@@ -12,13 +12,15 @@ module Familia::Features
|
|
12
12
|
# a symbol, the method with the same name will be called on the object to
|
13
13
|
# retrieve the value. If the field is a hash, the key is the field name and
|
14
14
|
# the value is a lambda that will be called with the object as an argument.
|
15
|
-
#
|
15
|
+
# The hash syntax allows you to:
|
16
16
|
# * define a field name that is different from the method name
|
17
17
|
# * define a field that requires some computation on-the-fly
|
18
18
|
# * define a field that is not a method on the object
|
19
19
|
#
|
20
20
|
# Example:
|
21
21
|
#
|
22
|
+
# feature :safe_dump
|
23
|
+
#
|
22
24
|
# @safe_dump_fields = [
|
23
25
|
# :objid,
|
24
26
|
# :updated,
|
@@ -26,11 +28,26 @@ module Familia::Features
|
|
26
28
|
# { :active => ->(obj) { obj.active? } }
|
27
29
|
# ]
|
28
30
|
#
|
29
|
-
# Internally, all fields are normalized to the hash syntax and
|
31
|
+
# Internally, all fields are normalized to the hash syntax and stored in
|
30
32
|
# @safe_dump_field_map. `SafeDump.safe_dump_fields` returns only the list
|
31
33
|
# of symbols in the order they were defined. From the example above, it would
|
32
34
|
# return `[:objid, :updated, :created, :active]`.
|
33
35
|
#
|
36
|
+
# Standalone Usage:
|
37
|
+
#
|
38
|
+
# You can also use SafeDump by including it in your model and defining the
|
39
|
+
# safe dump fields using the class instance variable `@safe_dump_fields`.
|
40
|
+
#
|
41
|
+
# Example:
|
42
|
+
#
|
43
|
+
# class MyModel
|
44
|
+
# include Familia::Features::SafeDump
|
45
|
+
#
|
46
|
+
# @safe_dump_fields = [
|
47
|
+
# :id, :name, { active: ->(obj) { obj.active? } }
|
48
|
+
# ]
|
49
|
+
# end
|
50
|
+
#
|
34
51
|
module SafeDump
|
35
52
|
@dump_method = :to_json
|
36
53
|
@load_method = :from_json
|
@@ -148,6 +165,8 @@ end
|
|
148
165
|
|
149
166
|
__END__
|
150
167
|
|
168
|
+
# Some leftovers related to dump_method and load_method
|
169
|
+
|
151
170
|
if value_to_distunguish.is_a?(Familia::Horreum)
|
152
171
|
Familia.trace :DISTINGUISHER, redis, "horreum", caller(1..1) if Familia.debug?
|
153
172
|
value_to_distunguish.identifier
|
@@ -8,15 +8,14 @@ module Familia
|
|
8
8
|
# These are set up as nil initially and populated later
|
9
9
|
@redis = nil
|
10
10
|
@identifier = nil
|
11
|
-
@fields = nil # []
|
12
11
|
@ttl = nil
|
13
12
|
@db = nil
|
14
13
|
@uri = nil
|
15
14
|
@suffix = nil
|
16
15
|
@prefix = nil
|
16
|
+
@fields = nil # []
|
17
17
|
@class_redis_types = nil # {}
|
18
18
|
@redis_types = nil # {}
|
19
|
-
@defined_fields = nil # {}
|
20
19
|
@dump_method = nil
|
21
20
|
@load_method = nil
|
22
21
|
|
@@ -37,23 +36,89 @@ module Familia
|
|
37
36
|
attr_accessor :parent
|
38
37
|
attr_writer :redis, :dump_method, :load_method
|
39
38
|
|
39
|
+
# Returns the Redis connection for the class.
|
40
|
+
#
|
41
|
+
# This method retrieves the Redis connection instance for the class. If no
|
42
|
+
# connection is set, it initializes a new connection using the provided URI
|
43
|
+
# or database configuration.
|
44
|
+
#
|
45
|
+
# @return [Redis] the Redis connection instance.
|
46
|
+
#
|
40
47
|
def redis
|
41
48
|
@redis || Familia.redis(uri || db)
|
42
49
|
end
|
43
50
|
|
44
|
-
#
|
45
|
-
#
|
46
|
-
#
|
51
|
+
# Sets or retrieves the unique identifier for the class.
|
52
|
+
#
|
53
|
+
# This method defines or returns the unique identifier used to generate the
|
54
|
+
# Redis key for the object. If a value is provided, it sets the identifier;
|
55
|
+
# otherwise, it returns the current identifier.
|
56
|
+
#
|
57
|
+
# @param [Object] val the value to set as the identifier (optional).
|
58
|
+
# @return [Object] the current identifier.
|
59
|
+
#
|
47
60
|
def identifier(val = nil)
|
48
61
|
@identifier = val if val
|
49
62
|
@identifier
|
50
63
|
end
|
51
64
|
|
52
|
-
#
|
53
|
-
#
|
65
|
+
# Defines a field for the class and creates accessor methods.
|
66
|
+
#
|
67
|
+
# This method defines a new field for the class, creating getter and setter
|
68
|
+
# instance methods similar to `attr_accessor`. It also generates a fast
|
69
|
+
# writer method for immediate persistence to Redis.
|
70
|
+
#
|
71
|
+
# @param [Symbol, String] name the name of the field to define.
|
72
|
+
#
|
54
73
|
def field(name)
|
55
74
|
fields << name
|
56
75
|
attr_accessor name
|
76
|
+
|
77
|
+
# Every field gets a fast writer method for immediately persisting
|
78
|
+
fast_writer! name
|
79
|
+
end
|
80
|
+
|
81
|
+
# Defines a writer method with a bang (!) suffix for a given attribute name.
|
82
|
+
#
|
83
|
+
# The dynamically defined method performs the following:
|
84
|
+
# - Checks if the correct number of arguments is provided (exactly one).
|
85
|
+
# - Converts the provided value to a format suitable for Redis storage.
|
86
|
+
# - Uses the existing accessor method to set the attribute value.
|
87
|
+
# - Persists the value to Redis immediately using the hset command.
|
88
|
+
# - Includes custom error handling to raise an ArgumentError if the wrong number of arguments is given.
|
89
|
+
# - Raises a custom error message if an exception occurs during the execution of the method.
|
90
|
+
#
|
91
|
+
# @param [Symbol, String] name the name of the attribute for which the writer method is defined.
|
92
|
+
# @raise [ArgumentError] if the wrong number of arguments is provided.
|
93
|
+
# @raise [RuntimeError] if an exception occurs during the execution of the method.
|
94
|
+
#
|
95
|
+
def fast_writer!(name)
|
96
|
+
define_method :"#{name}!" do |*args|
|
97
|
+
# Check if the correct number of arguments is provided (exactly one).
|
98
|
+
if args.size != 1
|
99
|
+
raise ArgumentError, "wrong number of arguments (given #{args.size}, expected 1)"
|
100
|
+
end
|
101
|
+
|
102
|
+
value = args.first
|
103
|
+
|
104
|
+
begin
|
105
|
+
# Trace the operation if debugging is enabled.
|
106
|
+
Familia.trace :FAST_WRITER, redis, "#{name}: #{value.inspect}", caller if Familia.debug?
|
107
|
+
|
108
|
+
# Convert the provided value to a format suitable for Redis storage.
|
109
|
+
prepared = to_redis(value)
|
110
|
+
Familia.ld "[.fast_writer!] #{name} val: #{value.class} prepared: #{prepared.class}"
|
111
|
+
|
112
|
+
# Use the existing accessor method to set the attribute value.
|
113
|
+
send :"#{name}=", value
|
114
|
+
|
115
|
+
# Persist the value to Redis immediately using the hset command.
|
116
|
+
hset name, prepared
|
117
|
+
rescue Familia::Problem => e
|
118
|
+
# Raise a custom error message if an exception occurs during the execution of the method.
|
119
|
+
raise "#{name}! method failed: #{e.message}", e.backtrace
|
120
|
+
end
|
121
|
+
end
|
57
122
|
end
|
58
123
|
|
59
124
|
# Returns the list of field names defined for the class in the order
|
@@ -81,11 +146,6 @@ module Familia
|
|
81
146
|
@redis_types
|
82
147
|
end
|
83
148
|
|
84
|
-
def defined_fields
|
85
|
-
@defined_fields ||= {}
|
86
|
-
@defined_fields
|
87
|
-
end
|
88
|
-
|
89
149
|
def ttl(v = nil)
|
90
150
|
@ttl = v unless v.nil?
|
91
151
|
@ttl || parent&.ttl
|
@@ -145,6 +205,34 @@ module Familia
|
|
145
205
|
redis.mget(*ids)
|
146
206
|
end
|
147
207
|
|
208
|
+
# Retrieves and instantiates an object from Redis using the full object
|
209
|
+
# key.
|
210
|
+
#
|
211
|
+
# @param objkey [String] The full Redis key for the object.
|
212
|
+
# @return [Object, nil] An instance of the class if the key exists, nil
|
213
|
+
# otherwise.
|
214
|
+
# @raise [ArgumentError] If the provided key is empty.
|
215
|
+
#
|
216
|
+
# This method performs a two-step process to safely retrieve and
|
217
|
+
# instantiate objects:
|
218
|
+
#
|
219
|
+
# 1. It first checks if the key exists in Redis. This is crucial because:
|
220
|
+
# - It provides a definitive answer about the object's existence.
|
221
|
+
# - It prevents ambiguity that could arise from `hgetall` returning an
|
222
|
+
# empty hash for non-existent keys, which could lead to the creation
|
223
|
+
# of "empty" objects.
|
224
|
+
#
|
225
|
+
# 2. If the key exists, it retrieves the object's data and instantiates
|
226
|
+
# it.
|
227
|
+
#
|
228
|
+
# This approach ensures that we only attempt to instantiate objects that
|
229
|
+
# actually exist in Redis, improving reliability and simplifying
|
230
|
+
# debugging.
|
231
|
+
#
|
232
|
+
# @example
|
233
|
+
# User.from_key("user:123") # Returns a User instance if it exists,
|
234
|
+
# nil otherwise
|
235
|
+
#
|
148
236
|
def from_key(objkey)
|
149
237
|
raise ArgumentError, 'Empty key' if objkey.to_s.empty?
|
150
238
|
|
@@ -153,27 +241,46 @@ module Familia
|
|
153
241
|
does_exist = redis.exists(objkey).positive?
|
154
242
|
|
155
243
|
Familia.ld "[.from_key] #{self} from key #{objkey} (exists: #{does_exist})"
|
156
|
-
Familia.trace :
|
244
|
+
Familia.trace :FROM_KEY, redis, objkey, caller if Familia.debug?
|
157
245
|
|
158
|
-
# This is reason for calling exists first. We want to definitively
|
159
|
-
# ambiguity know if the object exists in Redis. If it
|
160
|
-
# it does, we proceed to load the object.
|
161
|
-
#
|
162
|
-
# debug.
|
246
|
+
# This is the reason for calling exists first. We want to definitively
|
247
|
+
# and without any ambiguity know if the object exists in Redis. If it
|
248
|
+
# doesn't, we return nil. If it does, we proceed to load the object.
|
249
|
+
# Otherwise, hgetall will return an empty hash, which will be passed to
|
250
|
+
# the constructor, which will then be annoying to debug.
|
163
251
|
return unless does_exist
|
164
252
|
|
165
253
|
obj = redis.hgetall(objkey) # horreum objects are persisted as redis hashes
|
166
|
-
Familia.trace :
|
254
|
+
Familia.trace :FROM_KEY2, redis, "#{objkey}: #{obj.inspect}", caller if Familia.debug?
|
167
255
|
|
168
256
|
new(**obj)
|
169
257
|
end
|
170
258
|
|
259
|
+
# Retrieves and instantiates an object from Redis using its identifier.
|
260
|
+
#
|
261
|
+
# @param identifier [String, Integer] The unique identifier for the
|
262
|
+
# object.
|
263
|
+
# @param suffix [Symbol] The suffix to use in the Redis key (default:
|
264
|
+
# :object).
|
265
|
+
# @return [Object, nil] An instance of the class if found, nil otherwise.
|
266
|
+
#
|
267
|
+
# This method constructs the full Redis key using the provided identifier
|
268
|
+
# and suffix, then delegates to `from_key` for the actual retrieval and
|
269
|
+
# instantiation.
|
270
|
+
#
|
271
|
+
# It's a higher-level method that abstracts away the key construction,
|
272
|
+
# making it easier to retrieve objects when you only have their
|
273
|
+
# identifier.
|
274
|
+
#
|
275
|
+
# @example
|
276
|
+
# User.from_redis(123) # Equivalent to User.from_key("user:123:object")
|
277
|
+
#
|
171
278
|
def from_redis(identifier, suffix = :object)
|
172
279
|
return nil if identifier.to_s.empty?
|
173
280
|
|
174
281
|
objkey = rediskey(identifier, suffix)
|
175
282
|
Familia.ld "[.from_redis] #{self} from key #{objkey})"
|
176
|
-
Familia.trace :
|
283
|
+
Familia.trace :FROM_REDIS, Familia.redis(uri), objkey, caller(1..1).first if Familia.debug?
|
177
284
|
from_key objkey
|
178
285
|
end
|
179
286
|
|
@@ -183,10 +290,7 @@ module Familia
|
|
183
290
|
objkey = rediskey identifier, suffix
|
184
291
|
|
185
292
|
ret = redis.exists objkey
|
186
|
-
if Familia.debug?
|
187
|
-
Familia.trace :EXISTS, redis, "#{objkey} #{ret.inspect}",
|
188
|
-
caller
|
189
|
-
end
|
293
|
+
Familia.trace :EXISTS, redis, "#{objkey} #{ret.inspect}", caller if Familia.debug?
|
190
294
|
ret.positive?
|
191
295
|
end
|
192
296
|
|
@@ -236,5 +340,6 @@ module Familia
|
|
236
340
|
@load_method || :from_json # Familia.load_method
|
237
341
|
end
|
238
342
|
end
|
343
|
+
# End of ClassMethods module
|
239
344
|
end
|
240
345
|
end
|
@@ -20,9 +20,12 @@ module Familia
|
|
20
20
|
|
21
21
|
def exists?
|
22
22
|
ret = redis.exists rediskey
|
23
|
-
ret.positive?
|
23
|
+
ret.positive? # differs from redis API but I think it's okay bc `exists?` is a predicate method.
|
24
24
|
end
|
25
25
|
|
26
|
+
# Sets a timeout on key. After the timeout has expired, the key will automatically be deleted.
|
27
|
+
# Returns 1 if the timeout was set, 0 if key does not exist or the timeout could not be set.
|
28
|
+
#
|
26
29
|
def expire(ttl = nil)
|
27
30
|
ttl ||= self.class.ttl
|
28
31
|
redis.expire rediskey, ttl.to_i
|
@@ -32,18 +35,94 @@ module Familia
|
|
32
35
|
redis.ttl rediskey
|
33
36
|
end
|
34
37
|
|
38
|
+
# Deletes a field from the hash stored at the Redis key.
|
39
|
+
#
|
40
|
+
# @param field [String] The field to delete from the hash.
|
41
|
+
# @return [Integer] The number of fields that were removed from the hash (0 or 1).
|
42
|
+
# @note This method is destructive, as indicated by the bang (!).
|
35
43
|
def hdel!(field)
|
36
44
|
redis.hdel rediskey, field
|
37
45
|
end
|
38
46
|
|
39
|
-
def redistype
|
47
|
+
def redistype
|
40
48
|
redis.type rediskey(suffix)
|
41
49
|
end
|
42
50
|
|
43
|
-
|
44
|
-
|
45
|
-
redis.
|
51
|
+
# Parity with RedisType#rename
|
52
|
+
def rename(newkey)
|
53
|
+
redis.rename rediskey, newkey
|
54
|
+
end
|
55
|
+
|
56
|
+
# For parity with RedisType#hgetall
|
57
|
+
def hgetall
|
58
|
+
Familia.trace :HGETALL, redis, redisuri, caller(1..1) if Familia.debug?
|
59
|
+
redis.hgetall rediskey(suffix)
|
60
|
+
end
|
61
|
+
alias all hgetall
|
62
|
+
|
63
|
+
def hget(field)
|
64
|
+
redis.hget rediskey(suffix), field
|
65
|
+
end
|
66
|
+
|
67
|
+
# @return The number of fields that were added to the hash. If the
|
68
|
+
# field already exists, this will return 0.
|
69
|
+
def hset(field, value)
|
70
|
+
Familia.trace :HSET, redis, redisuri, caller(1..1) if Familia.debug?
|
71
|
+
redis.hset rediskey, field, value
|
72
|
+
end
|
73
|
+
|
74
|
+
def hmset
|
75
|
+
redis.hmset rediskey(suffix), self.to_h
|
76
|
+
end
|
77
|
+
|
78
|
+
def hkeys
|
79
|
+
Familia.trace :HKEYS, redis, 'redisuri', caller(1..1) if Familia.debug?
|
80
|
+
redis.hkeys rediskey(suffix)
|
81
|
+
end
|
82
|
+
|
83
|
+
def hvals
|
84
|
+
redis.hvals rediskey(suffix)
|
85
|
+
end
|
86
|
+
|
87
|
+
def incr(field)
|
88
|
+
redis.hincrby rediskey(suffix), field, 1
|
89
|
+
end
|
90
|
+
alias increment incr
|
91
|
+
|
92
|
+
def incrby(field, increment)
|
93
|
+
redis.hincrby rediskey(suffix), field, increment
|
94
|
+
end
|
95
|
+
alias incrementby incrby
|
96
|
+
|
97
|
+
def incrbyfloat(field, increment)
|
98
|
+
redis.hincrbyfloat rediskey(suffix), field, increment
|
99
|
+
end
|
100
|
+
alias incrementbyfloat incrbyfloat
|
101
|
+
|
102
|
+
def decrby(field, decrement)
|
103
|
+
redis.decrby rediskey(suffix), field, decrement
|
104
|
+
end
|
105
|
+
alias decrementby decrby
|
106
|
+
|
107
|
+
def decr(field)
|
108
|
+
redis.hdecr field
|
109
|
+
end
|
110
|
+
alias decrement decr
|
111
|
+
|
112
|
+
def hlen
|
113
|
+
redis.hlen rediskey(suffix)
|
114
|
+
end
|
115
|
+
alias hlength hlen
|
116
|
+
|
117
|
+
def hstrlen(field)
|
118
|
+
redis.hstrlen rediskey(suffix), field
|
119
|
+
end
|
120
|
+
alias hstrlength hstrlen
|
121
|
+
|
122
|
+
def key?(field)
|
123
|
+
redis.hexists rediskey(suffix), field
|
46
124
|
end
|
125
|
+
alias has_key? key?
|
47
126
|
|
48
127
|
def delete!
|
49
128
|
Familia.trace :DELETE!, redis, redisuri, caller(1..1) if Familia.debug?
|