familia 1.0.0.pre.rc7 → 1.2.0
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 +4 -4
- data/.gitignore +1 -0
- data/.pre-commit-config.yaml +1 -1
- data/Gemfile +3 -1
- data/Gemfile.lock +3 -1
- data/README.md +5 -5
- data/VERSION.yml +1 -2
- data/familia.gemspec +1 -2
- data/lib/familia/base.rb +11 -11
- data/lib/familia/connection.rb +18 -2
- data/lib/familia/errors.rb +14 -0
- data/lib/familia/features/expiration.rb +13 -2
- data/lib/familia/features/quantization.rb +1 -1
- data/lib/familia/horreum/class_methods.rb +31 -21
- data/lib/familia/horreum/commands.rb +58 -15
- data/lib/familia/horreum/relations_management.rb +9 -2
- data/lib/familia/horreum/serialization.rb +130 -29
- data/lib/familia/horreum.rb +69 -19
- data/lib/familia/redistype/commands.rb +5 -2
- data/lib/familia/redistype/serialization.rb +17 -16
- data/lib/familia/redistype/types/hashkey.rb +167 -0
- data/lib/familia/{types → redistype/types}/list.rb +19 -14
- data/lib/familia/{types → redistype/types}/sorted_set.rb +22 -19
- data/lib/familia/{types → redistype/types}/string.rb +8 -6
- data/lib/familia/{types → redistype/types}/unsorted_set.rb +16 -12
- data/lib/familia/redistype.rb +19 -9
- data/lib/familia.rb +6 -1
- data/try/10_familia_try.rb +1 -1
- data/try/20_redis_type_try.rb +1 -1
- data/try/21_redis_type_zset_try.rb +1 -1
- data/try/22_redis_type_set_try.rb +1 -1
- data/try/23_redis_type_list_try.rb +2 -2
- data/try/24_redis_type_string_try.rb +3 -3
- data/try/25_redis_type_hash_try.rb +1 -1
- data/try/26_redis_bool_try.rb +2 -2
- data/try/27_redis_horreum_try.rb +2 -2
- data/try/28_redis_horreum_serialization_try.rb +159 -0
- data/try/29_redis_horreum_initialization_try.rb +113 -0
- data/try/30_familia_object_try.rb +8 -5
- data/try/40_customer_try.rb +6 -6
- data/try/91_json_bug_try.rb +86 -0
- data/try/92_symbolize_try.rb +96 -0
- data/try/93_string_coercion_try.rb +154 -0
- metadata +20 -15
- data/lib/familia/types/hashkey.rb +0 -108
@@ -0,0 +1,113 @@
|
|
1
|
+
require_relative '../lib/familia'
|
2
|
+
require_relative './test_helpers'
|
3
|
+
|
4
|
+
Familia.debug = false
|
5
|
+
|
6
|
+
## Existing positional argument initialization still works
|
7
|
+
@customer1 = Customer.new 'tryouts-29@test.com', '', '', '', '', 'John Doe'
|
8
|
+
[@customer1.custid, @customer1.name]
|
9
|
+
#=> ["tryouts-29@test.com", "John Doe"]
|
10
|
+
|
11
|
+
## Keyword argument initialization works (order independent)
|
12
|
+
@customer2 = Customer.new(name: 'Jane Smith', custid: 'jane@test.com', email: 'jane@example.com')
|
13
|
+
[@customer2.custid, @customer2.name, @customer2.email]
|
14
|
+
#=> ["jane@test.com", "Jane Smith", "jane@example.com"]
|
15
|
+
|
16
|
+
## Keyword arguments are order independent (different order, same result)
|
17
|
+
@customer3 = Customer.new(email: 'bob@example.com', custid: 'bob@test.com', name: 'Bob Jones')
|
18
|
+
[@customer3.custid, @customer3.name, @customer3.email]
|
19
|
+
#=> ["bob@test.com", "Bob Jones", "bob@example.com"]
|
20
|
+
|
21
|
+
## Legacy hash support (single hash argument)
|
22
|
+
@customer4 = Customer.new({custid: 'legacy@test.com', name: 'Legacy User', role: 'admin'})
|
23
|
+
[@customer4.custid, @customer4.name, @customer4.role]
|
24
|
+
#=> ["legacy@test.com", "Legacy User", "admin"]
|
25
|
+
|
26
|
+
## Empty initialization works
|
27
|
+
@customer5 = Customer.new
|
28
|
+
@customer5.class
|
29
|
+
#=> Customer
|
30
|
+
|
31
|
+
## Keyword args with save and retrieval
|
32
|
+
@customer6 = Customer.new(custid: 'save-test@test.com', name: 'Save Test', email: 'save@example.com')
|
33
|
+
@customer6.save
|
34
|
+
#=> true
|
35
|
+
|
36
|
+
## Saved customer can be retrieved with correct values
|
37
|
+
@customer6.refresh!
|
38
|
+
[@customer6.custid, @customer6.name, @customer6.email]
|
39
|
+
#=> ["save-test@test.com", "Save Test", "save@example.com"]
|
40
|
+
|
41
|
+
## Keyword initialization sets key field correctly
|
42
|
+
@customer6.key
|
43
|
+
#=> "save-test@test.com"
|
44
|
+
|
45
|
+
## Mixed valid and nil values in keyword args (nil values stay nil)
|
46
|
+
@customer7 = Customer.new(custid: 'mixed@test.com', name: 'Mixed Test', email: nil, role: 'user')
|
47
|
+
[@customer7.custid, @customer7.name, @customer7.email, @customer7.role]
|
48
|
+
#=> ["mixed@test.com", "Mixed Test", nil, "user"]
|
49
|
+
|
50
|
+
## to_h works correctly with keyword-initialized objects
|
51
|
+
@customer2.to_h[:name]
|
52
|
+
#=> "Jane Smith"
|
53
|
+
|
54
|
+
## to_a works correctly with keyword-initialized objects
|
55
|
+
@customer2.to_a[5] # name field should be at index 5
|
56
|
+
#=> "Jane Smith"
|
57
|
+
|
58
|
+
## Session has limited fields (only sessid defined)
|
59
|
+
@session1 = Session.new('sess123')
|
60
|
+
@session1.sessid
|
61
|
+
#=> "sess123"
|
62
|
+
|
63
|
+
## Session with keyword args works
|
64
|
+
@session2 = Session.new(sessid: 'keyword-sess')
|
65
|
+
@session2.sessid
|
66
|
+
#=> "keyword-sess"
|
67
|
+
|
68
|
+
## Session with legacy hash
|
69
|
+
@session3 = Session.new({sessid: 'hash-sess'})
|
70
|
+
@session3.sessid
|
71
|
+
#=> "hash-sess"
|
72
|
+
|
73
|
+
## CustomDomain with keyword initialization
|
74
|
+
@domain1 = CustomDomain.new(display_domain: 'api.example.com', custid: 'domain-test@test.com')
|
75
|
+
[@domain1.display_domain, @domain1.custid]
|
76
|
+
#=> ["api.example.com", "domain-test@test.com"]
|
77
|
+
|
78
|
+
## CustomDomain still works with positional args
|
79
|
+
@domain2 = CustomDomain.new('web.example.com', 'positional@test.com')
|
80
|
+
[@domain2.display_domain, @domain2.custid]
|
81
|
+
#=> ["web.example.com", "positional@test.com"]
|
82
|
+
|
83
|
+
## Keyword initialization can skip fields (they remain nil/empty)
|
84
|
+
@partial = Customer.new(custid: 'partial@test.com', name: 'Partial User')
|
85
|
+
[@partial.custid, @partial.name, @partial.email]
|
86
|
+
#=> ["partial@test.com", "Partial User", nil]
|
87
|
+
|
88
|
+
## Complex initialization with save/refresh cycle
|
89
|
+
@complex = Customer.new(
|
90
|
+
custid: 'complex@test.com',
|
91
|
+
name: 'Complex User',
|
92
|
+
email: 'complex@example.com',
|
93
|
+
role: 'admin',
|
94
|
+
verified: true
|
95
|
+
)
|
96
|
+
@complex.save
|
97
|
+
@complex.refresh!
|
98
|
+
[@complex.custid, @complex.name, @complex.role, @complex.verified]
|
99
|
+
#=> ["complex@test.com", "Complex User", "admin", "true"]
|
100
|
+
|
101
|
+
## Clean up saved test objects
|
102
|
+
[@customer6, @complex].map(&:delete!)
|
103
|
+
#=> [true, true]
|
104
|
+
|
105
|
+
## "Cleaning up" test objects that were never saved returns false.
|
106
|
+
@customer1.save
|
107
|
+
ret = [
|
108
|
+
@customer1, @customer2, @customer3, @customer4, @customer6, @customer7,
|
109
|
+
@session1, @session2, @session3,
|
110
|
+
@domain1, @domain2,
|
111
|
+
@partial, @complex
|
112
|
+
].map(&:destroy!)
|
113
|
+
#=> [true, false, false, false, false, false, false, false, false, false, false, false, false]
|
@@ -36,8 +36,8 @@ Bone.suffix
|
|
36
36
|
Customer.values.all.collect(&:custid)
|
37
37
|
##=> ['delano']
|
38
38
|
|
39
|
-
##
|
40
|
-
obj = Customer.
|
39
|
+
## Can load an object from an identifier
|
40
|
+
obj = Customer.find_by_id :delano
|
41
41
|
[obj.class, obj.custid]
|
42
42
|
#=> [Customer, 'delano']
|
43
43
|
|
@@ -68,9 +68,8 @@ Customer.customers.size
|
|
68
68
|
#=> 3
|
69
69
|
|
70
70
|
## Familia class clear
|
71
|
-
Customer.customers.
|
72
|
-
#=>
|
73
|
-
|
71
|
+
Customer.customers.delete!
|
72
|
+
#=> true
|
74
73
|
|
75
74
|
## Familia class replace 1 of 4
|
76
75
|
Customer.message.value = "msg1"
|
@@ -87,3 +86,7 @@ Customer.message = "msg2"
|
|
87
86
|
## Familia class replace 4 of 4
|
88
87
|
Customer.message.value
|
89
88
|
#=> "msg2"
|
89
|
+
|
90
|
+
|
91
|
+
# Teardown
|
92
|
+
Customer.values.delete!
|
data/try/40_customer_try.rb
CHANGED
@@ -18,7 +18,7 @@ require_relative './test_helpers'
|
|
18
18
|
#=> true
|
19
19
|
|
20
20
|
## Customer can be retrieved by identifier
|
21
|
-
retrieved_customer = Customer.
|
21
|
+
retrieved_customer = Customer.find_by_id("test@example.com")
|
22
22
|
retrieved_customer.custid
|
23
23
|
#=> "test@example.com"
|
24
24
|
|
@@ -35,11 +35,11 @@ retrieved_customer.custid
|
|
35
35
|
@customer.planid = "premium"
|
36
36
|
@customer.save
|
37
37
|
ident = @customer.identifier
|
38
|
-
Customer.
|
38
|
+
Customer.find_by_id(ident).planid
|
39
39
|
#=> "premium"
|
40
40
|
|
41
41
|
## Customer can increment secrets_created counter
|
42
|
-
@customer.secrets_created.
|
42
|
+
@customer.secrets_created.delete!
|
43
43
|
@customer.secrets_created.increment
|
44
44
|
@customer.secrets_created.value
|
45
45
|
#=> '1'
|
@@ -74,7 +74,7 @@ Customer.instances.member?(@customer)
|
|
74
74
|
#=> true
|
75
75
|
|
76
76
|
## Customer can be removed from class-level sorted set
|
77
|
-
Customer.instances.
|
77
|
+
Customer.instances.remove(@customer)
|
78
78
|
Customer.instances.member?(@customer)
|
79
79
|
#=> false
|
80
80
|
|
@@ -90,7 +90,7 @@ Customer.instances.member?(@customer)
|
|
90
90
|
|
91
91
|
## Customer can be destroyed
|
92
92
|
ret = @customer.destroy!
|
93
|
-
cust = Customer.
|
93
|
+
cust = Customer.find_by_id("test@example.com")
|
94
94
|
exists = Customer.exists?("test@example.com")
|
95
95
|
[ret, cust.nil?, exists]
|
96
96
|
#=> [true, true, false]
|
@@ -137,4 +137,4 @@ Customer.instances.uri.to_s
|
|
137
137
|
|
138
138
|
|
139
139
|
# Teardown
|
140
|
-
Customer.instances.
|
140
|
+
Customer.instances.delete!
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# try/91_json_bug_try.rb
|
2
|
+
|
3
|
+
require_relative '../lib/familia'
|
4
|
+
require_relative './test_helpers'
|
5
|
+
|
6
|
+
Familia.debug = false
|
7
|
+
|
8
|
+
# Define a simple model with fields that should handle JSON data
|
9
|
+
class JsonTest < Familia::Horreum
|
10
|
+
identifier :id
|
11
|
+
field :id
|
12
|
+
field :config # This should be able to store Hash objects
|
13
|
+
field :tags # This should be able to store Array objects
|
14
|
+
field :simple # This should store simple strings as-is
|
15
|
+
end
|
16
|
+
|
17
|
+
# Create an instance with JSON data
|
18
|
+
@test_obj = JsonTest.new
|
19
|
+
@test_obj.id = "json_test_1"
|
20
|
+
|
21
|
+
## Test 1: Store a Hash - should serialize to JSON automatically
|
22
|
+
@test_obj.config = { theme: "dark", notifications: true, settings: { volume: 80 } }
|
23
|
+
@test_obj.config.class
|
24
|
+
#=> Hash
|
25
|
+
|
26
|
+
## Test 2: Store an Array - should serialize to JSON automatically
|
27
|
+
@test_obj.tags = ["ruby", "redis", "json", "familia"]
|
28
|
+
@test_obj.tags.class
|
29
|
+
#=> Array
|
30
|
+
|
31
|
+
## Test 3: Store a simple string - should remain as string
|
32
|
+
@test_obj.simple = "just a string"
|
33
|
+
@test_obj.simple.class
|
34
|
+
#=> String
|
35
|
+
|
36
|
+
## Save the object - this should call serialize_value and use to_json
|
37
|
+
@test_obj.save
|
38
|
+
#=> true
|
39
|
+
|
40
|
+
## Verify what's actually stored in Redis (raw)
|
41
|
+
raw_data = @test_obj.hgetall
|
42
|
+
p [:plop, @test_obj]
|
43
|
+
puts "Raw Redis data:"
|
44
|
+
raw_data
|
45
|
+
#=> {"id"=>"json_test_1", "config"=>"{\"theme\":\"dark\",\"notifications\":true,\"settings\":{\"volume\":80}}", "tags"=>"[\"ruby\",\"redis\",\"json\",\"familia\"]", "simple"=>"just a string", "key"=>"json_test_1"}
|
46
|
+
|
47
|
+
## BUG: After refresh, JSON data comes back as strings instead of parsed objects
|
48
|
+
@test_obj.refresh!
|
49
|
+
|
50
|
+
## Test 4: Hash should be deserialized back to Hash
|
51
|
+
puts "Config after refresh:"
|
52
|
+
puts @test_obj.config.inspect
|
53
|
+
puts "Config class: "
|
54
|
+
[@test_obj.config.class, @test_obj.config.inspect]
|
55
|
+
#=> [Hash, "{:theme=>\"dark\", :notifications=>true, :settings=>{:volume=>80}}"]
|
56
|
+
|
57
|
+
## Test 5: Array should be deserialized back to Array
|
58
|
+
puts "Tags after refresh:"
|
59
|
+
puts @test_obj.tags.inspect
|
60
|
+
puts "Tags class: #{@test_obj.tags.class}"
|
61
|
+
@test_obj.tags.inspect
|
62
|
+
@test_obj.tags.class
|
63
|
+
#=> ["ruby", "redis", "json", "familia"]
|
64
|
+
#=> Array
|
65
|
+
|
66
|
+
## Test 6: Simple string should remain a string (this works correctly)
|
67
|
+
puts "Simple after refresh:"
|
68
|
+
puts @test_obj.simple.inspect
|
69
|
+
puts "Simple class: #{@test_obj.simple.class}"
|
70
|
+
@test_obj.simple.inspect
|
71
|
+
@test_obj.simple.class
|
72
|
+
#=> "just a string"
|
73
|
+
#=> String
|
74
|
+
|
75
|
+
## Demonstrate the asymmetry:
|
76
|
+
puts "\n=== ASYMMETRY DEMONSTRATION ==="
|
77
|
+
puts "Before save: config is #{@test_obj.config.class}"
|
78
|
+
@test_obj.config = { example: "data" }
|
79
|
+
puts "After assignment: config is #{@test_obj.config.class}"
|
80
|
+
@test_obj.save
|
81
|
+
puts "After save: config is still #{@test_obj.config.class}"
|
82
|
+
@test_obj.refresh!
|
83
|
+
puts "After refresh: config is now #{@test_obj.config.class}!"
|
84
|
+
|
85
|
+
## Clean up
|
86
|
+
@test_obj.destroy!
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require_relative '../lib/familia'
|
2
|
+
require_relative './test_helpers'
|
3
|
+
|
4
|
+
Familia.debug = false
|
5
|
+
|
6
|
+
# Test the updated deserialize_value method
|
7
|
+
class SymbolizeTest < Familia::Horreum
|
8
|
+
identifier :id
|
9
|
+
field :id
|
10
|
+
field :config
|
11
|
+
end
|
12
|
+
|
13
|
+
@test_obj = SymbolizeTest.new
|
14
|
+
@test_obj.id = "symbolize_test_1"
|
15
|
+
|
16
|
+
## Test with a hash containing string keys
|
17
|
+
@test_hash = { "name" => "John", "age" => 30, "nested" => { "theme" => "dark" } }
|
18
|
+
@test_obj.config = @test_hash
|
19
|
+
@test_obj.save
|
20
|
+
|
21
|
+
## Original hash has string keys
|
22
|
+
@test_hash.keys
|
23
|
+
#=> ["name", "age", "nested"]
|
24
|
+
|
25
|
+
## After save and refresh, default behavior uses symbol keys
|
26
|
+
@test_obj.refresh!
|
27
|
+
@test_obj.config.keys
|
28
|
+
#=> [:name, :age, :nested]
|
29
|
+
|
30
|
+
## Nested hash also has symbol keys
|
31
|
+
@test_obj.config[:nested].keys
|
32
|
+
#=> [:theme]
|
33
|
+
|
34
|
+
## Get raw JSON from Redis
|
35
|
+
@raw_json = @test_obj.hget('config')
|
36
|
+
@raw_json.class
|
37
|
+
#=> String
|
38
|
+
|
39
|
+
## deserialize_value with default symbolize: true returns symbol keys
|
40
|
+
@symbol_result = @test_obj.deserialize_value(@raw_json)
|
41
|
+
@symbol_result.keys
|
42
|
+
#=> [:name, :age, :nested]
|
43
|
+
|
44
|
+
## Nested hash in symbol result also has symbol keys
|
45
|
+
@symbol_result[:nested].keys
|
46
|
+
#=> [:theme]
|
47
|
+
|
48
|
+
## deserialize_value with symbolize: false returns string keys
|
49
|
+
@string_result = @test_obj.deserialize_value(@raw_json, symbolize: false)
|
50
|
+
@string_result.keys
|
51
|
+
#=> ["name", "age", "nested"]
|
52
|
+
|
53
|
+
## Nested hash in string result also has string keys
|
54
|
+
@string_result["nested"].keys
|
55
|
+
#=> ["theme"]
|
56
|
+
|
57
|
+
## Values are preserved correctly in both cases
|
58
|
+
@symbol_result[:name]
|
59
|
+
#=> "John"
|
60
|
+
|
61
|
+
@string_result["name"]
|
62
|
+
#=> "John"
|
63
|
+
|
64
|
+
## Arrays are handled correctly too
|
65
|
+
@test_obj.config = [{ "item" => "value" }, "string", 123]
|
66
|
+
@test_obj.save
|
67
|
+
@array_json = @test_obj.hget('config')
|
68
|
+
#=> "[{\"item\":\"value\"},\"string\",123]"
|
69
|
+
|
70
|
+
## Array with symbolize: true converts hash keys to symbols
|
71
|
+
@symbol_array = @test_obj.deserialize_value(@array_json)
|
72
|
+
@symbol_array[0].keys
|
73
|
+
#=> [:item]
|
74
|
+
|
75
|
+
## Array with symbolize: false keeps hash keys as strings
|
76
|
+
@string_array = @test_obj.deserialize_value(@array_json, symbolize: false)
|
77
|
+
@string_array[0].keys
|
78
|
+
#=> ["item"]
|
79
|
+
|
80
|
+
## Non-hash/array values are returned as-is
|
81
|
+
@test_obj.deserialize_value('"just a string"')
|
82
|
+
#=> "\"just a string\""
|
83
|
+
|
84
|
+
## Non-hash/array values are returned as-is
|
85
|
+
@test_obj.deserialize_value('just a string')
|
86
|
+
#=> "just a string"
|
87
|
+
|
88
|
+
@test_obj.deserialize_value('42')
|
89
|
+
#=> "42"
|
90
|
+
|
91
|
+
## Invalid JSON returns original string
|
92
|
+
@test_obj.deserialize_value('invalid json')
|
93
|
+
#=> "invalid json"
|
94
|
+
|
95
|
+
## Clean up
|
96
|
+
@test_obj.destroy!
|
@@ -0,0 +1,154 @@
|
|
1
|
+
# try/93_string_coercion_try.rb
|
2
|
+
|
3
|
+
require_relative '../lib/familia'
|
4
|
+
require_relative './test_helpers'
|
5
|
+
|
6
|
+
Familia.debug = false
|
7
|
+
|
8
|
+
# Error handling: object without proper identifier setup
|
9
|
+
class ::BadIdentifierTest < Familia::Horreum
|
10
|
+
# No identifier method defined - should cause issues
|
11
|
+
end
|
12
|
+
|
13
|
+
@bad_obj = ::BadIdentifierTest.new
|
14
|
+
|
15
|
+
# Test polymorphic string usage for Familia objects
|
16
|
+
@customer_id = 'customer-string-coercion-test'
|
17
|
+
@customer = Customer.new(@customer_id)
|
18
|
+
@customer.name = 'John Doe'
|
19
|
+
@customer.planid = 'premium'
|
20
|
+
@customer.save
|
21
|
+
|
22
|
+
@session_id = 'session-string-coercion-test'
|
23
|
+
@session = Session.new(@session_id)
|
24
|
+
@session.custid = @customer_id
|
25
|
+
@session.useragent = 'Test Browser'
|
26
|
+
@session.save
|
27
|
+
|
28
|
+
## Complex identifier test with array-based identifier
|
29
|
+
@bone = Bone.new
|
30
|
+
@bone.token = 'test_token'
|
31
|
+
@bone.name = 'test_name'
|
32
|
+
|
33
|
+
|
34
|
+
## Basic to_s functionality returns identifier
|
35
|
+
@customer.to_s
|
36
|
+
#=> @customer_id
|
37
|
+
|
38
|
+
## String interpolation works seamlessly
|
39
|
+
"Customer: #{@customer}"
|
40
|
+
#=> "Customer: #{@customer_id}"
|
41
|
+
|
42
|
+
## Explicit identifier call returns same value
|
43
|
+
@customer.to_s == @customer.identifier
|
44
|
+
#=> true
|
45
|
+
|
46
|
+
## Session to_s works with generated identifier
|
47
|
+
@session.to_s
|
48
|
+
#=> @session_id
|
49
|
+
|
50
|
+
## Method accepting string parameter works with Familia object
|
51
|
+
def lookup_by_id(id_string)
|
52
|
+
id_string.to_s.upcase
|
53
|
+
end
|
54
|
+
|
55
|
+
lookup_by_id(@customer)
|
56
|
+
#=> @customer_id.upcase
|
57
|
+
|
58
|
+
## Hash key assignment using Familia object (implicit string conversion)
|
59
|
+
@data = {}
|
60
|
+
@data[@customer] = 'customer_data'
|
61
|
+
@data[@customer]
|
62
|
+
#=> 'customer_data'
|
63
|
+
|
64
|
+
## Array operations work with mixed types
|
65
|
+
@mixed_array = [@customer_id, @customer, @session]
|
66
|
+
@mixed_array.map(&:to_s)
|
67
|
+
#=> [@customer_id, @customer_id, @session_id]
|
68
|
+
|
69
|
+
## String comparison works
|
70
|
+
@customer.to_s == @customer_id
|
71
|
+
#=> true
|
72
|
+
|
73
|
+
## Join operations work seamlessly
|
74
|
+
[@customer, 'separator', @session].join(':')
|
75
|
+
#=> "#{@customer_id}:separator:#{@session_id}"
|
76
|
+
|
77
|
+
## Case statement works with string matching
|
78
|
+
def classify_id(obj)
|
79
|
+
case obj.to_s
|
80
|
+
when /customer/
|
81
|
+
'customer_type'
|
82
|
+
when /session/
|
83
|
+
'session_type'
|
84
|
+
else
|
85
|
+
'unknown_type'
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
classify_id(@customer)
|
90
|
+
#=> 'customer_type'
|
91
|
+
|
92
|
+
classify_id(@session)
|
93
|
+
#=> 'session_type'
|
94
|
+
|
95
|
+
## Polymorphic method accepting both strings and Familia objects
|
96
|
+
def process_identifier(id_or_object)
|
97
|
+
# Can handle both string IDs and Familia objects uniformly
|
98
|
+
processed_id = id_or_object.to_s
|
99
|
+
"processed:#{processed_id}"
|
100
|
+
end
|
101
|
+
|
102
|
+
process_identifier(@customer_id)
|
103
|
+
#=> "processed:#{@customer_id}"
|
104
|
+
|
105
|
+
process_identifier(@customer)
|
106
|
+
#=> "processed:#{@customer_id}"
|
107
|
+
|
108
|
+
## Redis storage using object as string key
|
109
|
+
@metadata = Familia::HashKey.new 'metadata'
|
110
|
+
@metadata[@customer] = 'customer_metadata'
|
111
|
+
@metadata[@customer.to_s] # Same key access
|
112
|
+
#=> 'customer_metadata'
|
113
|
+
|
114
|
+
## Cleanup after test
|
115
|
+
@metadata.delete!
|
116
|
+
#=> true
|
117
|
+
|
118
|
+
@customer.delete!
|
119
|
+
#=> true
|
120
|
+
|
121
|
+
@session.delete!
|
122
|
+
#=> true
|
123
|
+
|
124
|
+
## to_s handles identifier errors gracefully
|
125
|
+
@bad_obj.to_s.include?('BadIdentifierTest')
|
126
|
+
#=> true
|
127
|
+
|
128
|
+
## Array-based identifier works with to_s
|
129
|
+
@bone.to_s
|
130
|
+
#=> 'test_token:test_name'
|
131
|
+
|
132
|
+
## String operations on complex identifier
|
133
|
+
@bone.to_s.split(':')
|
134
|
+
#=> ['test_token', 'test_name']
|
135
|
+
|
136
|
+
## Cleanup a key that does not exist
|
137
|
+
@bone.delete!
|
138
|
+
#=> false
|
139
|
+
|
140
|
+
## Cleanup a key that exists
|
141
|
+
@bone.save
|
142
|
+
@bone.delete!
|
143
|
+
#=> true
|
144
|
+
|
145
|
+
## Performance consideration: to_s caching behavior
|
146
|
+
@customer2 = Customer.new('performance-test')
|
147
|
+
@first_call = @customer2.to_s
|
148
|
+
@second_call = @customer2.to_s
|
149
|
+
@first_call == @second_call
|
150
|
+
#=> true
|
151
|
+
|
152
|
+
## Delete customer2
|
153
|
+
[@customer2.exists?, @customer2.delete!]
|
154
|
+
#=> [false, false]
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: familia
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Delano Mandelbaum
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2025-05-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: redis
|
@@ -31,33 +31,33 @@ dependencies:
|
|
31
31
|
- !ruby/object:Gem::Version
|
32
32
|
version: '6.0'
|
33
33
|
- !ruby/object:Gem::Dependency
|
34
|
-
name:
|
34
|
+
name: stringio
|
35
35
|
requirement: !ruby/object:Gem::Requirement
|
36
36
|
requirements:
|
37
37
|
- - "~>"
|
38
38
|
- !ruby/object:Gem::Version
|
39
|
-
version:
|
39
|
+
version: 3.1.1
|
40
40
|
type: :runtime
|
41
41
|
prerelease: false
|
42
42
|
version_requirements: !ruby/object:Gem::Requirement
|
43
43
|
requirements:
|
44
44
|
- - "~>"
|
45
45
|
- !ruby/object:Gem::Version
|
46
|
-
version:
|
46
|
+
version: 3.1.1
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
|
-
name:
|
48
|
+
name: uri-redis
|
49
49
|
requirement: !ruby/object:Gem::Requirement
|
50
50
|
requirements:
|
51
51
|
- - "~>"
|
52
52
|
- !ruby/object:Gem::Version
|
53
|
-
version: '
|
54
|
-
type: :
|
53
|
+
version: '1.3'
|
54
|
+
type: :runtime
|
55
55
|
prerelease: false
|
56
56
|
version_requirements: !ruby/object:Gem::Requirement
|
57
57
|
requirements:
|
58
58
|
- - "~>"
|
59
59
|
- !ruby/object:Gem::Version
|
60
|
-
version: '
|
60
|
+
version: '1.3'
|
61
61
|
description: 'Familia: An ORM for Redis in Ruby.. Organize and store ruby objects
|
62
62
|
in Redis'
|
63
63
|
email: gems@solutious.com
|
@@ -96,14 +96,14 @@ files:
|
|
96
96
|
- lib/familia/redistype.rb
|
97
97
|
- lib/familia/redistype/commands.rb
|
98
98
|
- lib/familia/redistype/serialization.rb
|
99
|
+
- lib/familia/redistype/types/hashkey.rb
|
100
|
+
- lib/familia/redistype/types/list.rb
|
101
|
+
- lib/familia/redistype/types/sorted_set.rb
|
102
|
+
- lib/familia/redistype/types/string.rb
|
103
|
+
- lib/familia/redistype/types/unsorted_set.rb
|
99
104
|
- lib/familia/refinements.rb
|
100
105
|
- lib/familia/settings.rb
|
101
106
|
- lib/familia/tools.rb
|
102
|
-
- lib/familia/types/hashkey.rb
|
103
|
-
- lib/familia/types/list.rb
|
104
|
-
- lib/familia/types/sorted_set.rb
|
105
|
-
- lib/familia/types/string.rb
|
106
|
-
- lib/familia/types/unsorted_set.rb
|
107
107
|
- lib/familia/utils.rb
|
108
108
|
- lib/familia/version.rb
|
109
109
|
- lib/redis_middleware.rb
|
@@ -117,10 +117,15 @@ files:
|
|
117
117
|
- try/25_redis_type_hash_try.rb
|
118
118
|
- try/26_redis_bool_try.rb
|
119
119
|
- try/27_redis_horreum_try.rb
|
120
|
+
- try/28_redis_horreum_serialization_try.rb
|
121
|
+
- try/29_redis_horreum_initialization_try.rb
|
120
122
|
- try/30_familia_object_try.rb
|
121
123
|
- try/35_feature_safedump_try.rb
|
122
124
|
- try/40_customer_try.rb
|
123
125
|
- try/41_customer_safedump_try.rb
|
126
|
+
- try/91_json_bug_try.rb
|
127
|
+
- try/92_symbolize_try.rb
|
128
|
+
- try/93_string_coercion_try.rb
|
124
129
|
- try/test_helpers.rb
|
125
130
|
homepage: https://github.com/delano/familia
|
126
131
|
licenses:
|
@@ -142,7 +147,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
142
147
|
- !ruby/object:Gem::Version
|
143
148
|
version: '0'
|
144
149
|
requirements: []
|
145
|
-
rubygems_version: 3.5.
|
150
|
+
rubygems_version: 3.5.16
|
146
151
|
signing_key:
|
147
152
|
specification_version: 4
|
148
153
|
summary: An ORM for Redis in Ruby.
|