familia 2.0.0.pre7 → 2.0.0.pre10
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/.github/workflows/ci.yml +13 -0
- data/.github/workflows/docs.yml +1 -1
- data/.gitignore +9 -9
- data/.rubocop.yml +19 -0
- data/.yardopts +22 -1
- data/CHANGELOG.md +184 -0
- data/CLAUDE.md +8 -5
- data/Gemfile +1 -1
- data/Gemfile.lock +3 -3
- data/README.md +97 -2
- data/changelog.d/README.md +66 -0
- data/changelog.d/fragments/.keep +0 -0
- data/changelog.d/template.md.j2 +29 -0
- data/docs/archive/.gitignore +2 -0
- data/docs/archive/FAMILIA_RELATIONSHIPS.md +210 -0
- data/docs/archive/FAMILIA_TECHNICAL.md +823 -0
- data/docs/archive/FAMILIA_UPDATE.md +226 -0
- data/docs/archive/README.md +67 -0
- data/docs/guides/.gitignore +2 -0
- data/docs/{wiki → guides}/Feature-System-Guide.md +0 -15
- data/docs/{wiki → guides}/Relationships-Guide.md +103 -50
- data/docs/guides/relationships-methods.md +266 -0
- data/examples/relationships_basic.rb +90 -157
- data/familia.gemspec +4 -4
- data/lib/familia/connection.rb +4 -21
- data/lib/familia/features/external_identifiers/external_identifier_field_type.rb +120 -0
- data/lib/familia/features/external_identifiers.rb +111 -0
- data/lib/familia/features/object_identifiers/object_identifier_field_type.rb +91 -0
- data/lib/familia/features/object_identifiers.rb +194 -0
- data/lib/familia/features/relationships/cascading.rb +0 -1
- data/lib/familia/features/relationships/indexing.rb +160 -176
- data/lib/familia/features/relationships/membership.rb +16 -22
- data/lib/familia/features/relationships/querying.rb +7 -12
- data/lib/familia/features/relationships/score_encoding.rb +1 -3
- data/lib/familia/features/relationships/tracking.rb +61 -22
- data/lib/familia/features/relationships.rb +15 -8
- data/lib/familia/features/transient_fields.rb +8 -10
- data/lib/familia/features.rb +16 -13
- data/lib/familia/horreum/core/serialization.rb +2 -5
- data/lib/familia/horreum/subclass/definition.rb +36 -0
- data/lib/familia/horreum.rb +15 -24
- data/lib/familia/version.rb +1 -3
- data/setup.cfg +12 -0
- data/try/core/errors_try.rb +1 -1
- data/try/features/{encrypted_fields_core_try.rb → encrypted_fields/encrypted_fields_core_try.rb} +1 -1
- data/try/features/{encrypted_fields_integration_try.rb → encrypted_fields/encrypted_fields_integration_try.rb} +1 -1
- data/try/features/{encrypted_fields_no_cache_security_try.rb → encrypted_fields/encrypted_fields_no_cache_security_try.rb} +1 -1
- data/try/features/{encrypted_fields_security_try.rb → encrypted_fields/encrypted_fields_security_try.rb} +1 -1
- data/try/features/{expiration_try.rb → expiration/expiration_try.rb} +1 -1
- data/try/features/external_identifiers/external_identifiers_try.rb +203 -0
- data/try/features/object_identifiers/object_identifiers_integration_try.rb +289 -0
- data/try/features/object_identifiers/object_identifiers_try.rb +191 -0
- data/try/features/{quantization_try.rb → quantization/quantization_try.rb} +1 -1
- data/try/features/{categorical_permissions_try.rb → relationships/categorical_permissions_try.rb} +1 -1
- data/try/features/relationships/relationships_api_changes_try.rb +339 -0
- data/try/features/{relationships_edge_cases_try.rb → relationships/relationships_edge_cases_try.rb} +1 -1
- data/try/features/{relationships_performance_minimal_try.rb → relationships/relationships_performance_minimal_try.rb} +1 -1
- data/try/features/{relationships_performance_simple_try.rb → relationships/relationships_performance_simple_try.rb} +1 -1
- data/try/features/{relationships_performance_try.rb → relationships/relationships_performance_try.rb} +1 -1
- data/try/features/{relationships_performance_working_try.rb → relationships/relationships_performance_working_try.rb} +1 -1
- data/try/features/{relationships_try.rb → relationships/relationships_try.rb} +7 -6
- data/try/features/{safe_dump_advanced_try.rb → safe_dump/safe_dump_advanced_try.rb} +1 -1
- data/try/features/{safe_dump_try.rb → safe_dump/safe_dump_try.rb} +1 -1
- data/try/features/{transient_fields_core_try.rb → transient_fields/transient_fields_core_try.rb} +1 -1
- data/try/features/{transient_fields_integration_try.rb → transient_fields/transient_fields_integration_try.rb} +1 -1
- metadata +80 -60
- /data/docs/{wiki → guides}/API-Reference.md +0 -0
- /data/docs/{wiki → guides}/Connection-Pooling-Guide.md +0 -0
- /data/docs/{wiki → guides}/Encrypted-Fields-Overview.md +0 -0
- /data/docs/{wiki → guides}/Expiration-Feature-Guide.md +0 -0
- /data/docs/{wiki → guides}/Features-System-Developer-Guide.md +0 -0
- /data/docs/{wiki → guides}/Field-System-Guide.md +0 -0
- /data/docs/{wiki → guides}/Home.md +0 -0
- /data/docs/{wiki → guides}/Implementation-Guide.md +0 -0
- /data/docs/{wiki → guides}/Quantization-Feature-Guide.md +0 -0
- /data/docs/{wiki → guides}/Security-Model.md +0 -0
- /data/docs/{wiki → guides}/Transient-Fields-Guide.md +0 -0
- /data/try/features/{encryption_fields → encrypted_fields}/aad_protection_try.rb +0 -0
- /data/try/features/{encryption_fields → encrypted_fields}/concealed_string_core_try.rb +0 -0
- /data/try/features/{encryption_fields → encrypted_fields}/context_isolation_try.rb +0 -0
- /data/try/features/{encryption_fields → encrypted_fields}/error_conditions_try.rb +0 -0
- /data/try/features/{encryption_fields → encrypted_fields}/fresh_key_derivation_try.rb +0 -0
- /data/try/features/{encryption_fields → encrypted_fields}/fresh_key_try.rb +0 -0
- /data/try/features/{encryption_fields → encrypted_fields}/key_rotation_try.rb +0 -0
- /data/try/features/{encryption_fields → encrypted_fields}/memory_security_try.rb +0 -0
- /data/try/features/{encryption_fields → encrypted_fields}/missing_current_key_version_try.rb +0 -0
- /data/try/features/{encryption_fields → encrypted_fields}/nonce_uniqueness_try.rb +0 -0
- /data/try/features/{encryption_fields → encrypted_fields}/secure_by_default_behavior_try.rb +0 -0
- /data/try/features/{encryption_fields → encrypted_fields}/thread_safety_try.rb +0 -0
- /data/try/features/{encryption_fields → encrypted_fields}/universal_serialization_safety_try.rb +0 -0
@@ -0,0 +1,226 @@
|
|
1
|
+
# Familia v2.0.0-pre Series Update Overview
|
2
|
+
|
3
|
+
**Familia** is a Ruby ORM for Redis/Valkey that provides object mapping and persistence capabilities. This document summarizes the major updates from v2.0.0-pre through v2.0.0-pre7, representing a significant evolution with new features, security enhancements, and architectural improvements.
|
4
|
+
|
5
|
+
## Version Summary
|
6
|
+
|
7
|
+
| Version | Focus | Key Features |
|
8
|
+
|---------|-------|-------------|
|
9
|
+
| v2.0.0-pre | Foundation | Modern API, Valkey support, connection pooling |
|
10
|
+
| v2.0.0-pre5 | Security | Encrypted fields, transient fields, RedactedString |
|
11
|
+
| v2.0.0-pre6 | Architecture | Horreum reorganization, enhanced persistence |
|
12
|
+
| v2.0.0-pre7 | Relationships | Comprehensive relationship system, permissions |
|
13
|
+
|
14
|
+
---
|
15
|
+
|
16
|
+
## v2.0.0-pre - Foundation Release
|
17
|
+
|
18
|
+
### Major API Modernization
|
19
|
+
- **Complete API redesign** for clarity and modern Ruby conventions
|
20
|
+
- **Valkey compatibility** alongside traditional Redis support
|
21
|
+
- **Ruby 3.4+ modernization** with fiber and thread safety improvements
|
22
|
+
- **Connection pooling foundation** with provider pattern architecture
|
23
|
+
|
24
|
+
### Security & Dependency Management
|
25
|
+
- **Critical security fixes** in Ruby workflow vulnerabilities
|
26
|
+
- **Systematic dependency resolution** via multi-constraint optimization
|
27
|
+
- **GitHub Actions security hardening** with matrix optimization
|
28
|
+
|
29
|
+
### Documentation Infrastructure
|
30
|
+
- **YARD documentation workflow** with automated GitHub Pages deployment
|
31
|
+
- **Comprehensive wiki system** with structured documentation
|
32
|
+
- **Developer-focused guides** for implementation and usage
|
33
|
+
|
34
|
+
---
|
35
|
+
|
36
|
+
## v2.0.0-pre5 - Security Enhancement Release
|
37
|
+
|
38
|
+
### Encrypted Fields System
|
39
|
+
- **Field-level encryption** with transparent access patterns
|
40
|
+
- **Multiple encryption providers**:
|
41
|
+
- XChaCha20-Poly1305 (preferred, requires rbnacl)
|
42
|
+
- AES-256-GCM (fallback, OpenSSL-based)
|
43
|
+
- **Field-specific key derivation** for cryptographic domain separation
|
44
|
+
- **Configurable key versioning** supporting key rotation
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
class Vault < Familia::Horreum
|
48
|
+
feature :encrypted_fields
|
49
|
+
|
50
|
+
field :name # Plaintext
|
51
|
+
encrypted_field :secret_key # Encrypted at rest
|
52
|
+
encrypted_field :api_token # Transparent access
|
53
|
+
end
|
54
|
+
```
|
55
|
+
|
56
|
+
### Transient Fields & RedactedString
|
57
|
+
- **Non-persistent field storage** for sensitive runtime data
|
58
|
+
- **RedactedString wrapper** preventing accidental logging/serialization
|
59
|
+
- **Memory-safe handling** of sensitive data in Ruby objects
|
60
|
+
- **API-safe serialization** excluding transient fields
|
61
|
+
|
62
|
+
```ruby
|
63
|
+
class User < Familia::Horreum
|
64
|
+
feature :transient_fields
|
65
|
+
|
66
|
+
field :email
|
67
|
+
transient_field :password # Never persisted
|
68
|
+
transient_field :session_token # Runtime only
|
69
|
+
end
|
70
|
+
```
|
71
|
+
|
72
|
+
### Connection Pooling Enhancement
|
73
|
+
- **Connection provider pattern** for flexible pooling strategies
|
74
|
+
- **Multi-database support** with intelligent pool management
|
75
|
+
- **Thread-safe connection handling** for concurrent applications
|
76
|
+
- **Configurable pool sizing** and timeout management
|
77
|
+
|
78
|
+
---
|
79
|
+
|
80
|
+
## v2.0.0-pre6 - Architecture Enhancement Release
|
81
|
+
|
82
|
+
### Horreum Architecture Reorganization
|
83
|
+
- **Modular class structure** with cleaner separation of concerns
|
84
|
+
- **Enhanced feature system** with dependency management
|
85
|
+
- **Improved inheritance patterns** for better code organization
|
86
|
+
- **Streamlined base class functionality**
|
87
|
+
|
88
|
+
### Enhanced Persistence Operations
|
89
|
+
- **New `save_if_not_exists` method** for conditional persistence
|
90
|
+
- **Atomic persistence operations** with transaction support
|
91
|
+
- **Enhanced error handling** for persistence failures
|
92
|
+
- **Improved data consistency** guarantees
|
93
|
+
|
94
|
+
### Security Improvements
|
95
|
+
- **Encryption field security hardening** with additional validation
|
96
|
+
- **Enhanced memory protection** for sensitive data handling
|
97
|
+
- **Improved key management** patterns and best practices
|
98
|
+
- **Security test suite expansion** with comprehensive coverage
|
99
|
+
|
100
|
+
---
|
101
|
+
|
102
|
+
## v2.0.0-pre7 - Relationships & Permissions Release
|
103
|
+
|
104
|
+
### Comprehensive Relationships System
|
105
|
+
- **Three relationship types** optimized for different use cases:
|
106
|
+
- `tracked_in` - Multi-presence tracking with score encoding
|
107
|
+
- `indexed_by` - O(1) hash-based lookups
|
108
|
+
- `member_of` - Bidirectional membership with collision-free naming
|
109
|
+
|
110
|
+
```ruby
|
111
|
+
class Customer < Familia::Horreum
|
112
|
+
feature :relationships
|
113
|
+
|
114
|
+
identifier_field :custid
|
115
|
+
field :custid, :name, :email
|
116
|
+
|
117
|
+
# Define collections
|
118
|
+
set :domains
|
119
|
+
tracked_in :active_users, type: :sorted_set
|
120
|
+
end
|
121
|
+
|
122
|
+
class Domain < Familia::Horreum
|
123
|
+
feature :relationships
|
124
|
+
|
125
|
+
identifier_field :domain_id
|
126
|
+
field :domain_id, :name
|
127
|
+
|
128
|
+
# Bidirectional relationship
|
129
|
+
member_of Customer, :domains, type: :set
|
130
|
+
end
|
131
|
+
```
|
132
|
+
|
133
|
+
### Categorical Permission System
|
134
|
+
- **Bit-encoded permissions** for efficient storage and querying
|
135
|
+
- **Time-based permission scoring** for temporal access control
|
136
|
+
- **Permission tier hierarchies** with inheritance patterns
|
137
|
+
- **Scalable permission management** for large object collections
|
138
|
+
|
139
|
+
### Advanced Relationship Features
|
140
|
+
- **Score-based sorting** with custom scoring functions
|
141
|
+
- **Permission-aware queries** filtering by access levels
|
142
|
+
- **Relationship validation framework** ensuring data integrity
|
143
|
+
- **Performance optimizations** for large-scale relationship operations
|
144
|
+
|
145
|
+
---
|
146
|
+
|
147
|
+
## Breaking Changes & Migration
|
148
|
+
|
149
|
+
### v2.0.0-pre Series
|
150
|
+
- **API method renaming** for consistency and clarity
|
151
|
+
- **Configuration changes** in connection management
|
152
|
+
- **Feature activation syntax** updates for the new system
|
153
|
+
- **Identifier field declaration** syntax modernization
|
154
|
+
|
155
|
+
### Security Considerations
|
156
|
+
- **Encryption key configuration** required for encrypted fields
|
157
|
+
- **Memory handling changes** for sensitive data protection
|
158
|
+
- **Permission system migration** for existing relationship data
|
159
|
+
|
160
|
+
---
|
161
|
+
|
162
|
+
## Performance Improvements
|
163
|
+
|
164
|
+
### Connection Management
|
165
|
+
- **Pool-based connections** reducing connection overhead
|
166
|
+
- **Intelligent connection reuse** across operations
|
167
|
+
- **Concurrent operation support** with thread-safe pooling
|
168
|
+
|
169
|
+
### Relationship Operations
|
170
|
+
- **O(1) indexed lookups** for field-based queries
|
171
|
+
- **Optimized sorted set operations** for scored relationships
|
172
|
+
- **Batch relationship operations** for bulk updates
|
173
|
+
- **Memory-efficient bit encoding** for permission storage
|
174
|
+
|
175
|
+
### Feature System
|
176
|
+
- **Lazy feature loading** reducing memory footprint
|
177
|
+
- **Dependency-aware activation** preventing conflicts
|
178
|
+
- **Optimized method dispatch** for feature methods
|
179
|
+
|
180
|
+
---
|
181
|
+
|
182
|
+
## Migration Guide Summary
|
183
|
+
|
184
|
+
### From v1.x to v2.0.0-pre
|
185
|
+
1. **Update connection configuration** to use new pooling system
|
186
|
+
2. **Migrate identifier declarations** to new syntax
|
187
|
+
3. **Update feature activations** to use `feature :name` syntax
|
188
|
+
4. **Review method calls** for renamed API methods
|
189
|
+
|
190
|
+
### Security Feature Adoption
|
191
|
+
1. **Configure encryption keys** for encrypted fields
|
192
|
+
2. **Identify sensitive fields** for encryption/transient marking
|
193
|
+
3. **Update serialization code** to handle RedactedString
|
194
|
+
4. **Implement key rotation procedures** for production systems
|
195
|
+
|
196
|
+
### Relationship System Migration
|
197
|
+
1. **Analyze existing relationships** for optimization opportunities
|
198
|
+
2. **Choose appropriate relationship types** based on usage patterns
|
199
|
+
3. **Implement permission systems** for access-controlled relationships
|
200
|
+
4. **Update queries** to use new relationship methods
|
201
|
+
|
202
|
+
---
|
203
|
+
|
204
|
+
## Next Steps & Roadmap
|
205
|
+
|
206
|
+
### Immediate (v2.0.0 Final)
|
207
|
+
- **Production stability** testing and bug fixes
|
208
|
+
- **Performance benchmarking** and optimization
|
209
|
+
- **Documentation completion** for all features
|
210
|
+
- **Migration tooling** for existing applications
|
211
|
+
|
212
|
+
### Future Releases
|
213
|
+
- **Advanced encryption features** with hardware security modules
|
214
|
+
- **Extended relationship types** for specialized use cases
|
215
|
+
- **Performance analytics** and monitoring integration
|
216
|
+
- **Cloud-native deployment** patterns and examples
|
217
|
+
|
218
|
+
---
|
219
|
+
|
220
|
+
## Resources
|
221
|
+
|
222
|
+
- [GitHub Repository](https://github.com/delano/familia)
|
223
|
+
- [Wiki Documentation](https://github.com/delano/familia/wiki)
|
224
|
+
- [API Reference](docs/wiki/API-Reference.md)
|
225
|
+
- [Implementation Guide](docs/wiki/Implementation-Guide.md)
|
226
|
+
- [Security Model](docs/wiki/Security-Model.md)
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# Archived Documentation
|
2
|
+
|
3
|
+
This directory contains original documentation files that have been migrated to the new Scriv-based changelog system and reorganized documentation structure.
|
4
|
+
|
5
|
+
## Migration Date
|
6
|
+
**September 1, 2025** - As part of implementing [Issue #84: Scriv-based changelog system](https://github.com/delano/familia/issues/84)
|
7
|
+
|
8
|
+
## Archived Files
|
9
|
+
|
10
|
+
### FAMILIA_UPDATE.md
|
11
|
+
**Original Purpose:** Version summary table and detailed release notes for v2.0.0-pre series
|
12
|
+
|
13
|
+
**Migration Destinations:**
|
14
|
+
- **Changelog entries** → Extracted to Scriv fragments, aggregated into `CHANGELOG.md`
|
15
|
+
- **Migration guides** → Reorganized into `docs/migration/v2.0.0-pre*.md`
|
16
|
+
- **Feature descriptions** → Cross-referenced with existing feature guides in `docs/guides/`
|
17
|
+
|
18
|
+
### FAMILIA_RELATIONSHIPS.md
|
19
|
+
**Original Purpose:** Auto-generated relationship methods reference
|
20
|
+
|
21
|
+
**Migration Destination:**
|
22
|
+
- **Complete content** → Moved to `docs/guides/relationships-methods.md`
|
23
|
+
- **Cross-referenced** with existing `docs/guides/Relationships-Guide.md`
|
24
|
+
|
25
|
+
### FAMILIA_TECHNICAL.md
|
26
|
+
**Original Purpose:** Technical API reference for v2.0.0-pre series classes and methods
|
27
|
+
|
28
|
+
**Migration Destination:**
|
29
|
+
- **Core technical content** → Moved to `docs/reference/api-technical.md`
|
30
|
+
- **Cross-referenced** with existing `docs/guides/API-Reference.md`
|
31
|
+
|
32
|
+
## New Documentation Structure
|
33
|
+
|
34
|
+
```
|
35
|
+
docs/
|
36
|
+
├── migration/ # Version-specific migration guides
|
37
|
+
│ ├── v2.0.0-pre.md # Foundation migration
|
38
|
+
│ ├── v2.0.0-pre5.md # Security features
|
39
|
+
│ ├── v2.0.0-pre6.md # Architecture improvements
|
40
|
+
│ └── v2.0.0-pre7.md # Relationships system
|
41
|
+
├── guides/ # Feature guides (moved from wiki/)
|
42
|
+
│ ├── relationships-methods.md # From FAMILIA_RELATIONSHIPS.md
|
43
|
+
│ └── [other guides...]
|
44
|
+
├── reference/ # Technical reference
|
45
|
+
│ └── api-technical.md # From FAMILIA_TECHNICAL.md
|
46
|
+
└── archive/ # This directory
|
47
|
+
```
|
48
|
+
|
49
|
+
## Why These Files Were Archived
|
50
|
+
|
51
|
+
1. **Sustainability** - The original files accumulated overlapping content without clear organization
|
52
|
+
2. **Maintainability** - Scriv fragment system prevents documentation bloat
|
53
|
+
3. **Clarity** - Separation of changelog, guides, and reference improves findability
|
54
|
+
4. **Workflow** - Fragment-based workflow scales better with development
|
55
|
+
|
56
|
+
## Finding Migrated Content
|
57
|
+
|
58
|
+
- **Version changes** → Check `CHANGELOG.md` (generated from fragments)
|
59
|
+
- **How to upgrade** → See `docs/migration/` for version-specific guides
|
60
|
+
- **Feature usage** → See `docs/guides/` for implementation examples
|
61
|
+
- **API reference** → See `docs/reference/` for technical details
|
62
|
+
|
63
|
+
## References
|
64
|
+
|
65
|
+
- [Issue #84](https://github.com/delano/familia/issues/84) - Original migration plan
|
66
|
+
- [Scriv Documentation](https://scriv.readthedocs.io/) - Changelog management tool
|
67
|
+
- [Keep a Changelog](https://keepachangelog.com/) - Changelog format standard
|
@@ -517,21 +517,6 @@ class ConfigurableModel < Familia::Horreum
|
|
517
517
|
end
|
518
518
|
```
|
519
519
|
|
520
|
-
### Runtime Feature Checking
|
521
|
-
|
522
|
-
```ruby
|
523
|
-
class Model < Familia::Horreum
|
524
|
-
feature :expiration
|
525
|
-
|
526
|
-
def available_features
|
527
|
-
self.class.features_enabled
|
528
|
-
end
|
529
|
-
end
|
530
|
-
|
531
|
-
model = Model.new
|
532
|
-
model.available_features # => [:expiration]
|
533
|
-
```
|
534
|
-
|
535
520
|
## Testing Features
|
536
521
|
|
537
522
|
### Feature Testing
|
@@ -27,9 +27,13 @@ class Customer < Familia::Horreum
|
|
27
27
|
identifier_field :custid
|
28
28
|
field :custid, :name, :email
|
29
29
|
|
30
|
-
#
|
31
|
-
|
32
|
-
|
30
|
+
# Collections for relationships
|
31
|
+
set :domains # Simple set of domain IDs
|
32
|
+
sorted_set :activity # Activity feed with timestamps
|
33
|
+
|
34
|
+
# Class-level relationship collections (automatically managed)
|
35
|
+
class_tracked_in :all_customers, score: :created_at
|
36
|
+
class_indexed_by :email, :email_lookup
|
33
37
|
end
|
34
38
|
|
35
39
|
class Domain < Familia::Horreum
|
@@ -39,7 +43,7 @@ class Domain < Familia::Horreum
|
|
39
43
|
field :domain_id, :name, :dns_zone
|
40
44
|
|
41
45
|
# Define bidirectional membership
|
42
|
-
member_of Customer, :domains
|
46
|
+
member_of Customer, :domains
|
43
47
|
end
|
44
48
|
```
|
45
49
|
|
@@ -57,27 +61,26 @@ class User < Familia::Horreum
|
|
57
61
|
field :user_id, :name, :score_value
|
58
62
|
|
59
63
|
# Simple sorted set tracking
|
60
|
-
|
64
|
+
class_tracked_in :leaderboard, score: :score_value
|
61
65
|
|
62
66
|
# Time-based tracking with automatic timestamps
|
63
|
-
|
67
|
+
class_tracked_in :activity_log, score: :created_at
|
64
68
|
|
65
69
|
# Proc-based scoring for complex calculations
|
66
|
-
|
67
|
-
score: ->(user) { (user.score_value || 0) * 2 }
|
70
|
+
class_tracked_in :performance_metrics, score: -> { (score_value || 0) * 2 }
|
68
71
|
end
|
69
72
|
|
70
73
|
# Usage
|
71
74
|
user = User.new(user_id: 'user123', score_value: 85)
|
72
75
|
|
73
76
|
# Add to collections
|
74
|
-
User.add_to_leaderboard(user)
|
75
|
-
User.add_to_activity_log(user)
|
76
|
-
User.add_to_performance_metrics(user)
|
77
|
+
User.add_to_leaderboard(user) # Uses score_value (85)
|
78
|
+
User.add_to_activity_log(user) # Uses created_at timestamp
|
79
|
+
User.add_to_performance_metrics(user) # Uses proc result (170)
|
77
80
|
|
78
81
|
# Query collections
|
79
82
|
User.leaderboard.score('user123') # => 85.0
|
80
|
-
User.activity_log.
|
83
|
+
User.activity_log.rangebyscore('-inf', '+inf') # All users by time
|
81
84
|
User.performance_metrics.rank('user123') # User's rank by performance
|
82
85
|
```
|
83
86
|
|
@@ -93,8 +96,7 @@ class Document < Familia::Horreum
|
|
93
96
|
field :doc_id, :title, :content
|
94
97
|
|
95
98
|
# Permission-based tracking with 8-bit encoding
|
96
|
-
|
97
|
-
score: :encode_permissions
|
99
|
+
class_tracked_in :authorized_users, score: :encode_permissions
|
98
100
|
|
99
101
|
private
|
100
102
|
|
@@ -183,7 +185,7 @@ end
|
|
183
185
|
|
184
186
|
### Hash-Based Lookups
|
185
187
|
|
186
|
-
The `indexed_by` relationship creates O(1) hash-based indexes for field values:
|
188
|
+
The `indexed_by` relationship creates O(1) hash-based indexes for field values. The `context` parameter determines index ownership and scope:
|
187
189
|
|
188
190
|
```ruby
|
189
191
|
class User < Familia::Horreum
|
@@ -191,55 +193,102 @@ class User < Familia::Horreum
|
|
191
193
|
|
192
194
|
field :email, :username, :department
|
193
195
|
|
194
|
-
#
|
195
|
-
|
196
|
-
|
197
|
-
|
196
|
+
# Global indexes for system-wide unique lookups
|
197
|
+
class_indexed_by :email, :email_index
|
198
|
+
class_indexed_by :username, :username_index
|
199
|
+
|
200
|
+
# Scoped indexes for values unique within a context
|
201
|
+
indexed_by :department, :department_index, parent: Organization
|
198
202
|
end
|
199
203
|
|
200
|
-
# Usage
|
204
|
+
# Usage for Global Context
|
201
205
|
user = User.new(email: 'john@example.com', username: 'johndoe')
|
202
|
-
User.add_to_email_index(user)
|
203
|
-
User.add_to_username_index(user)
|
204
206
|
|
205
|
-
#
|
207
|
+
# Add to global indexes (instance methods)
|
208
|
+
user.add_to_global_email_index
|
209
|
+
user.add_to_global_username_index
|
210
|
+
|
211
|
+
# Fast O(1) lookups (class methods)
|
206
212
|
user_id = User.email_index.get('john@example.com') # => user.identifier
|
207
213
|
user_id = User.username_index.get('johndoe') # => user.identifier
|
208
214
|
|
209
215
|
# Batch operations
|
210
216
|
users = [user1, user2, user3]
|
211
|
-
users.each { |u|
|
217
|
+
users.each { |u| u.add_to_global_email_index }
|
212
218
|
|
213
219
|
# Check if indexed
|
214
220
|
User.email_index.exists?('john@example.com') # => true
|
221
|
+
|
222
|
+
# Usage for Scoped Context
|
223
|
+
organization = Organization.new(org_id: 'acme_corp')
|
224
|
+
organization.find_by_department('engineering') # Find user by department within this org
|
215
225
|
```
|
216
226
|
|
217
|
-
###
|
227
|
+
### Context Parameter Usage Patterns
|
228
|
+
|
229
|
+
Understanding when to use global vs class context:
|
218
230
|
|
219
231
|
```ruby
|
220
232
|
class Product < Familia::Horreum
|
221
233
|
feature :relationships
|
222
234
|
|
223
|
-
field :
|
235
|
+
field :sku, :category, :brand
|
224
236
|
|
225
|
-
#
|
226
|
-
|
227
|
-
"#{product.category}:#{product.brand}"
|
228
|
-
}
|
237
|
+
# Global context: SKUs must be unique system-wide
|
238
|
+
class_indexed_by :sku, :sku_index
|
229
239
|
|
230
|
-
|
231
|
-
|
232
|
-
}
|
240
|
+
# Class context: Categories are unique per brand
|
241
|
+
indexed_by :category, :category_index, parent: Brand
|
233
242
|
end
|
234
243
|
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
244
|
+
class Brand < Familia::Horreum
|
245
|
+
feature :relationships
|
246
|
+
|
247
|
+
identifier_field :brand_id
|
248
|
+
field :brand_id, :name
|
249
|
+
sorted_set :products
|
250
|
+
end
|
251
|
+
|
252
|
+
# Usage patterns:
|
253
|
+
product = Product.new(sku: 'ELEC001', category: 'laptops', brand: 'apple')
|
254
|
+
|
255
|
+
# Global indexing (system-wide unique SKUs)
|
256
|
+
product.add_to_global_sku_index
|
257
|
+
Product.sku_index.get('ELEC001') # => product.identifier
|
239
258
|
|
240
|
-
#
|
241
|
-
|
242
|
-
|
259
|
+
# Scoped indexing (categories unique per brand)
|
260
|
+
brand = Brand.new(brand_id: 'apple', name: 'Apple Inc.')
|
261
|
+
brand.find_by_category('laptops') # Find products in this brand's laptop category
|
262
|
+
```
|
263
|
+
|
264
|
+
### Context Parameter Reference
|
265
|
+
|
266
|
+
The `context` parameter is a **required** architectural decision that determines index scope:
|
267
|
+
|
268
|
+
| Context Type | Usage | Redis Key Pattern | When to Use |
|
269
|
+
|--------------|--------|------------------|-------------|
|
270
|
+
| `:global` | `context: :global` | `global:index_name` | Field values unique system-wide (emails, usernames, API keys) |
|
271
|
+
| Class | `context: SomeClass` | `someclass:123:index_name` | Field values unique within parent object scope (project names per team) |
|
272
|
+
|
273
|
+
#### Generated Methods
|
274
|
+
|
275
|
+
**Global Context** (`context: :global`):
|
276
|
+
- **Instance methods**: `object.add_to_global_index_name`, `object.remove_from_global_index_name`
|
277
|
+
- **Class methods**: `Class.index_name` (returns hash), `Class.find_by_field`
|
278
|
+
|
279
|
+
**Class Context** (`context: Customer`):
|
280
|
+
- **Instance methods**: `object.add_to_customer_index_name(customer)`, `object.remove_from_customer_index_name(customer)`
|
281
|
+
- **Class methods on context**: `customer.find_by_field(value)`, `customer.find_all_by_field(values)`
|
282
|
+
|
283
|
+
#### Migration from Incorrect Syntax
|
284
|
+
|
285
|
+
```ruby
|
286
|
+
# ❌ Old incorrect syntax (will cause ArgumentError)
|
287
|
+
indexed_by :email_lookup, field: :email
|
288
|
+
|
289
|
+
# ✅ New correct syntax
|
290
|
+
class_indexed_by :email, :email_lookup # Global scope
|
291
|
+
indexed_by :email, :customer_lookup, parent: Customer # Scoped per customer
|
243
292
|
```
|
244
293
|
|
245
294
|
## Member Of Relationships
|
@@ -444,8 +493,8 @@ class User < Familia::Horreum
|
|
444
493
|
|
445
494
|
# Multi-tenant membership
|
446
495
|
member_of Organization, :members, type: :set
|
447
|
-
|
448
|
-
|
496
|
+
class_tracked_in :global_activity, score: :created_at
|
497
|
+
class_indexed_by :email, :email_lookup
|
449
498
|
end
|
450
499
|
|
451
500
|
class Project < Familia::Horreum
|
@@ -455,7 +504,7 @@ class Project < Familia::Horreum
|
|
455
504
|
field :project_id, :name, :status
|
456
505
|
|
457
506
|
member_of Organization, :projects, type: :set
|
458
|
-
|
507
|
+
class_tracked_in :status_timeline,
|
459
508
|
score: ->(proj) { "#{Time.now.to_i}.#{proj.status.hash}" }
|
460
509
|
end
|
461
510
|
|
@@ -464,17 +513,21 @@ org = Organization.new(org_id: 'org123', name: 'Acme Corp')
|
|
464
513
|
user = User.new(user_id: 'user456', email: 'john@acme.com')
|
465
514
|
project = Project.new(project_id: 'proj789', name: 'Website')
|
466
515
|
|
467
|
-
# Establish relationships
|
468
|
-
|
469
|
-
|
516
|
+
# Establish relationships with clean syntax
|
517
|
+
org.members << user # Clean Ruby-like syntax
|
518
|
+
org.projects << project
|
519
|
+
|
520
|
+
# Save objects to trigger automatic indexing
|
521
|
+
user.save # Automatically added to class-level indexes
|
522
|
+
project.save # Automatically added to class-level tracking
|
470
523
|
|
471
524
|
# Query organization structure
|
472
525
|
org.members.size # Number of organization members
|
473
526
|
org.projects.members # All project IDs in organization
|
474
527
|
|
475
|
-
#
|
476
|
-
User.add_to_email_lookup(user)
|
528
|
+
# Automatic class-level indexing (no manual calls needed)
|
477
529
|
user_id = User.email_lookup.get('john@acme.com') # Fast email lookup
|
530
|
+
found_user = User.find_by_email('john@acme.com') # Convenience method
|
478
531
|
```
|
479
532
|
|
480
533
|
### Analytics and Reporting
|
@@ -485,7 +538,7 @@ class AnalyticsService
|
|
485
538
|
since = (Time.now - days.days).to_f.floor
|
486
539
|
|
487
540
|
# Get all users active in time period
|
488
|
-
active_users = User.
|
541
|
+
active_users = User.activity.range_by_score(since, '+inf')
|
489
542
|
|
490
543
|
# Analyze permission levels
|
491
544
|
permission_breakdown = Document.authorized_users
|
@@ -495,7 +548,7 @@ class AnalyticsService
|
|
495
548
|
{
|
496
549
|
total_active_users: active_users.size,
|
497
550
|
permission_breakdown: permission_breakdown.transform_values(&:size),
|
498
|
-
top_contributors: User.
|
551
|
+
top_contributors: User.activity.range(0, 9, with_scores: true)
|
499
552
|
}
|
500
553
|
end
|
501
554
|
|