Skip to content

Commit 949b036

Browse files
authored
Merge pull request #27 from delano/26-rc3-feedback-collection-and-issue-resolution-for-rc4-prep
Naming and Documentation Improvements, and Minor Functional Adjustments
2 parents 9b2d325 + 106d347 commit 949b036

36 files changed

+898
-406
lines changed

README.md

+198-48
Original file line numberDiff line numberDiff line change
@@ -1,84 +1,234 @@
1-
# Familia - 1.0.0-rc2 (August 2024)
1+
# Familia - 1.0.0-rc4 (August 2024)
22

3-
**Organize and store ruby objects in Redis. A Ruby ORM for Redis.**
3+
**Organize and store Ruby objects in Redis. A powerful Ruby ORM (of sorts) for Redis.**
44

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.
5+
Familia provides a flexible and feature-rich way to interact with Redis using Ruby objects. It's designed to make working with Redis as natural as working with Ruby classes, while offering advanced features for complex data management.
66

77
## Installation
88

9+
910
Get it in one of the following ways:
1011

11-
* In your Gemfile: `gem 'familia', '>= 1.0.0-rc2'`
12-
* Install it by hand: `gem install familia`
12+
* In your Gemfile: `gem 'familia', '>= 1.0.0-rc4'`
13+
* Install it by hand: `gem install familia --pre`
1314
* Or for development: `git clone [email protected]:delano/familia.git`
1415

15-
## Basic Example
16+
17+
## Core Concepts and Features
18+
19+
### 1. Defining Horreum Classes
20+
21+
Familia uses the concept of "Horreum" classes to represent Redis-backed objects:
1622

1723
```ruby
1824
class Flower < Familia::Horreum
19-
identifier :generate_id
20-
field :token
21-
field :name
22-
list :owners
23-
set :tags
24-
zset :metrics
25+
identifier :token
26+
field :name
27+
list :owners
28+
set :tags
29+
zset :metrics
2530
hashkey :props
26-
string :counter
31+
string :counter
32+
end
33+
```
34+
35+
### 2. Flexible Identifiers
36+
37+
You can define identifiers in various ways:
38+
39+
```ruby
40+
class User < Familia::Horreum
41+
identifier :email
42+
# or
43+
identifier -> (user) { "user:#{user.email}" }
44+
# or
45+
identifier [:type, :email]
46+
47+
field :email
48+
field :type
49+
end
50+
```
51+
52+
### 3. Redis Data Types
53+
54+
Familia supports various Redis data types:
55+
56+
```ruby
57+
class Product < Familia::Horreum
58+
string :name
59+
list :categories
60+
set :tags
61+
zset :ratings
62+
hashkey :attributes
63+
end
64+
```
65+
66+
### 4. Class-level Redis Types
67+
68+
You can also define Redis types at the class level:
69+
70+
```ruby
71+
class Customer < Familia::Horreum
72+
class_sorted_set :values, key: 'project:customers'
73+
class_hashkey :projects
74+
class_list :customers, suffix: []
75+
class_string :message
76+
end
77+
```
78+
79+
### 5. Automatic Expiration
80+
81+
Use the expiration feature to set TTL for objects:
82+
83+
```ruby
84+
class Session < Familia::Horreum
85+
feature :expiration
86+
ttl 180.minutes
87+
end
88+
```
89+
90+
### 6. Safe Dumping for APIs
91+
92+
Control which fields are exposed when serializing objects:
93+
94+
```ruby
95+
class User < Familia::Horreum
96+
feature :safe_dump
97+
98+
@safe_dump_fields = [
99+
:id,
100+
:username,
101+
{full_name: ->(user) { "#{user.first_name} #{user.last_name}" }}
102+
]
103+
end
104+
```
105+
106+
### 7. Quantization for Time-based Data
107+
108+
Use quantization for time-based metrics:
109+
110+
```ruby
111+
class DailyMetric < Familia::Horreum
112+
feature :quantization
113+
string :counter, ttl: 1.day, quantize: [10.minutes, '%H:%M']
27114
end
28115
```
29116

30-
## What Familia::Horreum Can Do
117+
### 8. Custom Methods and Logic
31118

32-
Familia::Horreum provides a powerful abstraction layer over Redis, allowing you to:
119+
Add custom methods to your Horreum classes:
33120

34-
1. **Define Redis-backed Ruby Classes**: As shown in the example, you can easily define classes that map to Redis structures.
121+
```ruby
122+
class User < Familia::Horreum
123+
def full_name
124+
"#{first_name} #{last_name}"
125+
end
126+
127+
def active?
128+
status == 'active'
129+
end
130+
end
131+
```
35132

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
133+
### 9. Custom Methods and Logic
43134

44-
3. **Custom Identifiers**: Use the `identifier` method to specify how objects are uniquely identified in Redis.
135+
You can add custom methods to your Horreum classes:
45136

46-
4. **Automatic Serialization**: Familia handles the serialization and deserialization of your objects to and from Redis.
137+
```ruby
138+
class Customer < Familia::Horreum
139+
def active?
140+
verified && !reset_requested
141+
end
142+
end
47143

48-
5. **Redis Commands as Ruby Methods**: Interact with Redis using familiar Ruby syntax instead of raw Redis commands.
144+
class Session < Familia::Horreum
145+
def external_identifier
146+
elements = [ipaddress || 'UNKNOWNIP', custid || 'anon']
147+
@external_identifier ||= Familia.generate_sha_hash(elements)
148+
end
149+
end
150+
```
151+
### 10. Open-ended Serialization
49152

50-
6. **TTL Support**: Set expiration times for your objects in Redis.
153+
```ruby
154+
class ComplexObject < Familia::Horreum
155+
def to_redis
156+
custom_serialization_method
157+
end
158+
159+
def self.from_redis(data)
160+
custom_deserialization_method(data)
161+
end
162+
end
163+
```
51164

52-
7. **Flexible Configuration**: Configure Redis connection details, serialization methods, and more.
165+
### 11. Transactional Operations
166+
167+
```ruby
168+
user.transaction do |conn|
169+
conn.set("user:#{user.id}:status", "active")
170+
conn.zadd("active_users", Time.now.to_i, user.id)
171+
end
172+
```
53173

54-
## Advanced Features
55174

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.
175+
## Usage Examples
60176

61-
## Usage Example
177+
### Creating and Saving Objects
62178

63179
```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")
180+
flower = Flower.create(name: "Red Rose", token: "rrose")
181+
flower.owners.push("Alice", "Bob")
182+
flower.tags.add("romantic")
183+
flower.metrics.increment("views", 1)
184+
flower.props[:color] = "red"
185+
flower.save
186+
```
187+
188+
### Retrieving and Updating Objects
189+
190+
```ruby
191+
rose = Flower.from_identifier("rrose")
192+
rose.name = "Pink Rose"
69193
rose.save
194+
```
195+
196+
### Using Safe Dump
197+
198+
```ruby
199+
user = User.create(username: "rosedog", first_name: "Rose", last_name: "Dog")
200+
user.safe_dump
201+
# => {id: "user:rosedog", username: "rosedog", full_name: "Rose Dog"}
202+
```
203+
204+
### Working with Time-based Data
205+
206+
```ruby
207+
metric = DailyMetric.new
208+
metric.counter.increment # Increments the counter for the current hour
209+
```
210+
211+
### Bulk Operations
70212

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"]
213+
```ruby
214+
Flower.multiget("rrose", "tulip", "daisy")
215+
```
216+
217+
### Transactional Operations
218+
219+
```ruby
220+
user.transaction do |conn|
221+
conn.set("user:#{user.id}:status", "active")
222+
conn.zadd("active_users", Time.now.to_i, user.id)
223+
end
75224
```
76225

77-
## More Information
226+
## Conclusion
78227

79-
* [Github](https://github.com/delano/familia)
80-
* [Rubygems](https://rubygems.org/gems/familia)
228+
Familia provides a powerful and flexible way to work with Redis in Ruby applications. Its features like automatic expiration, safe dumping, and quantization make it suitable for a wide range of use cases, from simple key-value storage to complex time-series data management.
81229

82-
## Contributing
230+
For more information, visit:
231+
- [Github Repository](https://github.com/delano/familia)
232+
- [RubyGems Page](https://rubygems.org/gems/familia)
83233

84-
Contributions are welcome! Please feel free to submit a Pull Request.
234+
Contributions are welcome! Feel free to submit a Pull Request.

VERSION.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
:MAJOR: 1
33
:MINOR: 0
44
:PATCH: 0
5-
:PRE: rc2
5+
:PRE: rc4

lib/familia/base.rb

+29-1
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,38 @@ class << self
2424

2525
def add_feature(klass, methname)
2626
@features ||= {}
27-
Familia.ld "[#{self}] Adding feature #{klass} as #{methname}"
27+
Familia.ld "[#{self}] Adding feature #{klass} as #{methname.inspect}"
2828

2929
features[methname] = klass
3030
end
3131
end
32+
33+
# Yo, this class is like that one friend who never checks expiration dates.
34+
# It's living life on the edge, data-style!
35+
#
36+
# @param ttl [Integer, nil] Time To Live? More like Time To Laugh! This param
37+
# is here for compatibility, but it's as useful as a chocolate teapot.
38+
#
39+
# @return [nil] Always returns nil. It's consistent in its laziness!
40+
#
41+
# @example Trying to teach an old dog new tricks
42+
# immortal_data.update_expiration(86400) # Nice try, but this data is here to stay!
43+
#
44+
# @note This method is a no-op. It's like shouting into the void, but less echo-y.
45+
#
46+
def update_expiration(_ = nil)
47+
Familia.info "[update_expiration] Skipped for #{rediskey}. #{self.class} data is immortal!"
48+
nil
49+
end
50+
51+
def generate_id
52+
@key ||= Familia.generate_id
53+
@key
54+
end
55+
56+
def uuid
57+
@uuid ||= SecureRandom.uuid
58+
@uuid
59+
end
3260
end
3361
end

lib/familia/features.rb

+5-4
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,14 @@
22

33
module Familia
44

5-
@features_enabled = nil
6-
75
module Features
86

7+
@features_enabled = nil
98
attr_reader :features_enabled
109

1110
def feature(val = nil)
1211
@features_enabled ||= []
1312

14-
Familia.ld "[Familia::Settings] feature: #{val.inspect}"
15-
1613
# If there's a value provied check that it's a valid feature
1714
if val
1815
val = val.to_sym
@@ -24,6 +21,8 @@ def feature(val = nil)
2421
return
2522
end
2623

24+
Familia.trace :FEATURE, nil, "#{self} includes #{val.inspect}", caller(1..1) if Familia.debug?
25+
2726
klass = Familia::Base.features[val]
2827

2928
# Extend the Familia::Base subclass (e.g. Customer) with the feature module
@@ -48,4 +47,6 @@ def feature(val = nil)
4847

4948
end
5049

50+
require_relative 'features/expiration'
51+
require_relative 'features/quantization'
5152
require_relative 'features/safe_dump'

lib/familia/features/api_version.rb

-19
This file was deleted.

lib/familia/features/atomic_saves.rb

-8
This file was deleted.

0 commit comments

Comments
 (0)