familia 2.0.0.pre8 → 2.0.0.pre12
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 +247 -0
- data/CLAUDE.md +12 -59
- data/Gemfile.lock +1 -1
- data/README.md +62 -2
- data/changelog.d/README.md +77 -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 +63 -0
- data/docs/guides/.gitignore +2 -0
- data/docs/{wiki → guides}/Home.md +1 -1
- data/docs/{wiki → guides}/Implementation-Guide.md +1 -1
- data/docs/{wiki → guides}/Relationships-Guide.md +103 -50
- data/docs/guides/relationships-methods.md +266 -0
- data/docs/migrating/.gitignore +2 -0
- data/docs/migrating/v2.0.0-pre.md +84 -0
- data/docs/migrating/v2.0.0-pre11.md +255 -0
- data/docs/migrating/v2.0.0-pre12.md +306 -0
- data/docs/migrating/v2.0.0-pre5.md +110 -0
- data/docs/migrating/v2.0.0-pre6.md +154 -0
- data/docs/migrating/v2.0.0-pre7.md +222 -0
- data/docs/overview.md +6 -7
- data/{examples/redis_command_validation_example.rb → docs/reference/auditing_database_commands.rb} +29 -32
- data/examples/{bit_encoding_integration.rb → permissions.rb} +30 -27
- data/examples/relationships.rb +205 -0
- data/examples/safe_dump.rb +281 -0
- data/familia.gemspec +4 -4
- data/lib/familia/base.rb +52 -0
- data/lib/familia/connection.rb +4 -21
- data/lib/familia/{encryption_request_cache.rb → encryption/request_cache.rb} +1 -1
- data/lib/familia/errors.rb +2 -0
- data/lib/familia/features/autoloader.rb +57 -0
- data/lib/familia/features/external_identifier.rb +310 -0
- data/lib/familia/features/object_identifier.rb +307 -0
- data/lib/familia/features/relationships/indexing.rb +160 -175
- data/lib/familia/features/relationships/membership.rb +16 -21
- data/lib/familia/features/relationships/tracking.rb +61 -21
- data/lib/familia/features/relationships.rb +15 -8
- data/lib/familia/features/safe_dump.rb +66 -72
- data/lib/familia/features.rb +93 -5
- data/lib/familia/horreum/subclass/definition.rb +49 -3
- data/lib/familia/horreum.rb +15 -24
- data/lib/familia/secure_identifier.rb +51 -75
- data/lib/familia/verifiable_identifier.rb +162 -0
- data/lib/familia/version.rb +1 -1
- data/lib/familia.rb +1 -0
- data/setup.cfg +5 -0
- data/try/core/secure_identifier_try.rb +47 -18
- data/try/core/verifiable_identifier_try.rb +171 -0
- data/try/features/{external_identifiers/external_identifiers_try.rb → external_identifier/external_identifier_try.rb} +25 -28
- data/try/features/feature_improvements_try.rb +126 -0
- data/try/features/{object_identifiers/object_identifiers_integration_try.rb → object_identifier/object_identifier_integration_try.rb} +28 -30
- data/try/features/{object_identifiers/object_identifiers_try.rb → object_identifier/object_identifier_try.rb} +13 -13
- data/try/features/real_feature_integration_try.rb +7 -6
- data/try/features/relationships/relationships_api_changes_try.rb +339 -0
- data/try/features/relationships/relationships_try.rb +6 -5
- data/try/features/safe_dump/safe_dump_try.rb +8 -9
- data/try/helpers/test_helpers.rb +17 -17
- metadata +62 -41
- data/examples/relationships_basic.rb +0 -273
- data/lib/familia/features/external_identifiers/external_identifier_field_type.rb +0 -120
- data/lib/familia/features/external_identifiers.rb +0 -111
- data/lib/familia/features/object_identifiers/object_identifier_field_type.rb +0 -91
- data/lib/familia/features/object_identifiers.rb +0 -194
- /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}/Feature-System-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}/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
@@ -0,0 +1,222 @@
|
|
1
|
+
# Migrating Guide: Relationships System (v2.0.0-pre7)
|
2
|
+
|
3
|
+
This guide covers adopting the comprehensive relationships system introduced in v2.0.0-pre7.
|
4
|
+
|
5
|
+
## Relationships System Overview
|
6
|
+
|
7
|
+
The v2.0.0-pre7 release introduces three powerful relationship types:
|
8
|
+
|
9
|
+
- **`tracked_in`** - Multi-presence tracking with score encoding
|
10
|
+
- **`indexed_by`** - O(1) hash-based lookups
|
11
|
+
- **`member_of`** - Bidirectional membership with collision-free naming
|
12
|
+
|
13
|
+
## Migration Steps
|
14
|
+
|
15
|
+
### 1. Enable Relationships Feature
|
16
|
+
|
17
|
+
Add the relationships feature to your models:
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
class Customer < Familia::Horreum
|
21
|
+
feature :relationships # Required for all relationship types
|
22
|
+
|
23
|
+
identifier_field :custid
|
24
|
+
field :custid, :name, :email
|
25
|
+
end
|
26
|
+
```
|
27
|
+
|
28
|
+
### 2. Choose Appropriate Relationship Types
|
29
|
+
|
30
|
+
**For Tracking Collections (sorted sets):**
|
31
|
+
```ruby
|
32
|
+
class Customer < Familia::Horreum
|
33
|
+
feature :relationships
|
34
|
+
|
35
|
+
# Global tracking with score-based sorting
|
36
|
+
tracked_in :all_customers, type: :sorted_set, score: :created_at
|
37
|
+
|
38
|
+
# Scoped tracking
|
39
|
+
tracked_in :active_users, type: :sorted_set, score: :last_seen
|
40
|
+
end
|
41
|
+
```
|
42
|
+
|
43
|
+
**For Fast Lookups (hash indexes):**
|
44
|
+
```ruby
|
45
|
+
class Customer < Familia::Horreum
|
46
|
+
feature :relationships
|
47
|
+
|
48
|
+
# Global email index for O(1) lookups
|
49
|
+
indexed_by :email, :email_lookup, context: :global
|
50
|
+
|
51
|
+
# Domain-specific indexes
|
52
|
+
indexed_by :account_id, :account_lookup, context: Domain
|
53
|
+
end
|
54
|
+
```
|
55
|
+
|
56
|
+
**For Bidirectional Membership:**
|
57
|
+
```ruby
|
58
|
+
class Domain < Familia::Horreum
|
59
|
+
feature :relationships
|
60
|
+
|
61
|
+
# Belongs to customer's domains collection
|
62
|
+
member_of Customer, :domains, type: :set
|
63
|
+
end
|
64
|
+
```
|
65
|
+
|
66
|
+
### 3. Implement Permission Systems
|
67
|
+
|
68
|
+
For access-controlled relationships:
|
69
|
+
|
70
|
+
```ruby
|
71
|
+
class Document < Familia::Horreum
|
72
|
+
feature :relationships
|
73
|
+
include Familia::Features::Relationships::PermissionManagement
|
74
|
+
|
75
|
+
# Permission-aware tracking
|
76
|
+
tracked_in Customer, :documents, score: :created_at
|
77
|
+
|
78
|
+
permission_tracking :user_permissions
|
79
|
+
|
80
|
+
def permission_bits
|
81
|
+
# Define permission levels (bit-encoded)
|
82
|
+
@permission_bits || 1 # Default: read-only
|
83
|
+
end
|
84
|
+
end
|
85
|
+
```
|
86
|
+
|
87
|
+
### 4. Update Queries
|
88
|
+
|
89
|
+
**Before (manual Redis operations):**
|
90
|
+
```ruby
|
91
|
+
# Manual sorted set operations
|
92
|
+
redis.zadd("all_customers", customer.created_at, customer.custid)
|
93
|
+
customers = redis.zrange("all_customers", 0, -1)
|
94
|
+
```
|
95
|
+
|
96
|
+
**After (relationship methods):**
|
97
|
+
```ruby
|
98
|
+
# Automatic method generation
|
99
|
+
Customer.add_to_all_customers(customer)
|
100
|
+
customers = Customer.all_customers.to_a
|
101
|
+
```
|
102
|
+
|
103
|
+
## Relationship Method Patterns
|
104
|
+
|
105
|
+
### `tracked_in` Methods
|
106
|
+
```ruby
|
107
|
+
# Class methods generated:
|
108
|
+
Customer.add_to_all_customers(customer)
|
109
|
+
Customer.remove_from_all_customers(customer)
|
110
|
+
Customer.all_customers # Access sorted set directly
|
111
|
+
```
|
112
|
+
|
113
|
+
### `indexed_by` Methods
|
114
|
+
```ruby
|
115
|
+
# Class methods generated:
|
116
|
+
Customer.add_to_email_lookup(customer)
|
117
|
+
Customer.remove_from_email_lookup(customer)
|
118
|
+
Customer.email_lookup.get(email) # O(1) lookup
|
119
|
+
```
|
120
|
+
|
121
|
+
### `member_of` Methods
|
122
|
+
```ruby
|
123
|
+
# Instance methods generated:
|
124
|
+
domain.add_to_customer_domains(customer)
|
125
|
+
domain.remove_from_customer_domains(customer)
|
126
|
+
domain.in_customer_domains?(customer)
|
127
|
+
```
|
128
|
+
|
129
|
+
## Permission System Migration
|
130
|
+
|
131
|
+
### Basic Permission Encoding
|
132
|
+
```ruby
|
133
|
+
# Permission levels (bit-encoded)
|
134
|
+
READ = 1 # 001
|
135
|
+
WRITE = 4 # 100
|
136
|
+
DELETE = 32 # 100000
|
137
|
+
ADMIN = 128 # 10000000
|
138
|
+
|
139
|
+
# Combine permissions
|
140
|
+
user_permissions = READ | WRITE # Can read and write
|
141
|
+
admin_permissions = ADMIN # All permissions via hierarchy
|
142
|
+
```
|
143
|
+
|
144
|
+
### Time-Based Permission Scores
|
145
|
+
```ruby
|
146
|
+
# Encode timestamp with permission bits
|
147
|
+
timestamp = Time.now.to_i
|
148
|
+
permissions = READ | WRITE
|
149
|
+
score = Familia::Features::Relationships::ScoreEncoding.encode_score(timestamp, permissions)
|
150
|
+
|
151
|
+
# Query by permission level
|
152
|
+
documents_with_write_access = customer.documents.accessible_items(
|
153
|
+
permissions: WRITE,
|
154
|
+
collection_key: customer.documents.key
|
155
|
+
)
|
156
|
+
```
|
157
|
+
|
158
|
+
## Performance Considerations
|
159
|
+
|
160
|
+
### Relationship Type Selection
|
161
|
+
|
162
|
+
**Use `indexed_by` for:**
|
163
|
+
- Unique field lookups (email, username, ID)
|
164
|
+
- Fields that need O(1) access
|
165
|
+
- Reference relationships
|
166
|
+
|
167
|
+
**Use `tracked_in` for:**
|
168
|
+
- Time-ordered collections
|
169
|
+
- Scored/ranked relationships
|
170
|
+
- Permission-based access control
|
171
|
+
|
172
|
+
**Use `member_of` for:**
|
173
|
+
- Simple membership tracking
|
174
|
+
- Bidirectional relationships
|
175
|
+
- Set-based collections
|
176
|
+
|
177
|
+
### Optimization Tips
|
178
|
+
|
179
|
+
**Batch Operations:**
|
180
|
+
```ruby
|
181
|
+
# Instead of multiple individual operations
|
182
|
+
customers.each { |c| Customer.add_to_all_customers(c) }
|
183
|
+
|
184
|
+
# Use batch operations where available
|
185
|
+
Customer.all_customers.add_batch(customers.map(&:identifier),
|
186
|
+
customers.map(&:created_at))
|
187
|
+
```
|
188
|
+
|
189
|
+
**Permission Queries:**
|
190
|
+
```ruby
|
191
|
+
# Efficient permission filtering
|
192
|
+
accessible_docs = document.accessible_items(
|
193
|
+
collection_key: customer.documents.key,
|
194
|
+
minimum_permissions: READ
|
195
|
+
)
|
196
|
+
```
|
197
|
+
|
198
|
+
## Breaking Changes
|
199
|
+
|
200
|
+
### Method Name Changes
|
201
|
+
- Relationship methods follow new naming patterns
|
202
|
+
- Previous manual Redis operations need updating
|
203
|
+
- Collection access methods standardized
|
204
|
+
|
205
|
+
### Permission System
|
206
|
+
- New bit-encoded permission system
|
207
|
+
- Time-based scoring for temporal access
|
208
|
+
- Permission hierarchy implementation required
|
209
|
+
|
210
|
+
## Next Steps
|
211
|
+
|
212
|
+
1. **Analyze Existing Relationships:** Review current Redis operations for optimization opportunities
|
213
|
+
2. **Choose Relationship Types:** Select appropriate types based on usage patterns
|
214
|
+
3. **Implement Permissions:** Add access control where needed
|
215
|
+
4. **Update Queries:** Replace manual operations with generated relationship methods
|
216
|
+
5. **Test Performance:** Benchmark relationship operations under load
|
217
|
+
|
218
|
+
## Resources
|
219
|
+
|
220
|
+
- [Relationships Methods Guide](../guides/relationships-methods.md) - Complete method reference
|
221
|
+
- [Relationships Guide](../guides/Relationships-Guide.md) - Implementation examples
|
222
|
+
- [Performance Guide](../guides/Implementation-Guide.md) - Optimization strategies
|
data/docs/overview.md
CHANGED
@@ -118,17 +118,16 @@ This is ideal for temporary data like authentication tokens or cache entries.
|
|
118
118
|
|
119
119
|
### Safe Dumping for APIs
|
120
120
|
|
121
|
-
Control which fields are exposed when serializing objects:
|
121
|
+
Control which fields are exposed when serializing objects using the clean DSL:
|
122
122
|
|
123
123
|
```ruby
|
124
124
|
class User < Familia::Horreum
|
125
125
|
feature :safe_dump
|
126
126
|
|
127
|
-
@safe_dump_fields
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
]
|
127
|
+
# Use clean DSL methods instead of @safe_dump_fields
|
128
|
+
safe_dump_field :id
|
129
|
+
safe_dump_field :email
|
130
|
+
safe_dump_field :full_name, ->(user) { "#{user.first_name} #{user.last_name}" }
|
132
131
|
|
133
132
|
field :id, :email, :first_name, :last_name, :password_hash
|
134
133
|
end
|
@@ -137,7 +136,7 @@ user.safe_dump
|
|
137
136
|
#=> {id: "123", email: "alice@example.com", full_name: "Alice Smith"}
|
138
137
|
```
|
139
138
|
|
140
|
-
|
139
|
+
The new DSL prevents accidental exposure of sensitive data and makes field definitions easier to organize in feature modules.
|
141
140
|
|
142
141
|
### Time-based Quantization
|
143
142
|
|
data/{examples/redis_command_validation_example.rb → docs/reference/auditing_database_commands.rb}
RENAMED
@@ -50,21 +50,21 @@ class TransferService
|
|
50
50
|
end
|
51
51
|
end
|
52
52
|
|
53
|
-
puts
|
54
|
-
puts
|
53
|
+
puts '🧪 Redis Command Validation Framework Demo'
|
54
|
+
puts '=' * 50
|
55
55
|
|
56
56
|
# Clean up any existing test data
|
57
|
-
cleanup_keys = Familia.dbclient.keys(
|
57
|
+
cleanup_keys = Familia.dbclient.keys('account:*')
|
58
58
|
Familia.dbclient.del(*cleanup_keys) if cleanup_keys.any?
|
59
59
|
|
60
60
|
# Example 1: Basic Command Recording
|
61
61
|
puts "\n1. Basic Command Recording"
|
62
|
-
puts
|
62
|
+
puts '-' * 30
|
63
63
|
|
64
64
|
CommandRecorder = Familia::Validation::CommandRecorder
|
65
65
|
CommandRecorder.start_recording
|
66
66
|
|
67
|
-
account = Account.new(account_id:
|
67
|
+
account = Account.new(account_id: 'acc001', balance: '1000', status: 'active')
|
68
68
|
account.save
|
69
69
|
|
70
70
|
commands = CommandRecorder.stop_recording
|
@@ -73,12 +73,12 @@ commands.commands.each { |cmd| puts " #{cmd}" }
|
|
73
73
|
|
74
74
|
# Example 2: Transaction Detection
|
75
75
|
puts "\n2. Transaction Detection"
|
76
|
-
puts
|
76
|
+
puts '-' * 30
|
77
77
|
|
78
78
|
CommandRecorder.start_recording
|
79
79
|
|
80
|
-
acc1 = Account.new(account_id:
|
81
|
-
acc2 = Account.new(account_id:
|
80
|
+
acc1 = Account.new(account_id: 'acc002', balance: '2000')
|
81
|
+
acc2 = Account.new(account_id: 'acc003', balance: '500')
|
82
82
|
acc1.save
|
83
83
|
acc2.save
|
84
84
|
|
@@ -96,7 +96,7 @@ end
|
|
96
96
|
|
97
97
|
# Example 3: Validation with Expectations DSL
|
98
98
|
puts "\n3. Command Validation with Expectations"
|
99
|
-
puts
|
99
|
+
puts '-' * 30
|
100
100
|
|
101
101
|
begin
|
102
102
|
validator = Familia::Validation::Validator.new
|
@@ -104,15 +104,15 @@ begin
|
|
104
104
|
# This should pass - we expect the exact Redis commands
|
105
105
|
result = validator.validate do |expect|
|
106
106
|
expect.transaction do |tx|
|
107
|
-
tx.hset(
|
108
|
-
.hset(
|
109
|
-
.hset(
|
110
|
-
.hset(
|
107
|
+
tx.hset('account:acc004:object', 'balance', '1500')
|
108
|
+
.hset('account:acc005:object', 'balance', '1000')
|
109
|
+
.hset('account:acc004:object', 'last_updated', Familia::Validation::ArgumentMatcher.new(:any_string))
|
110
|
+
.hset('account:acc005:object', 'last_updated', Familia::Validation::ArgumentMatcher.new(:any_string))
|
111
111
|
end
|
112
112
|
|
113
113
|
# Execute the operation
|
114
|
-
acc4 = Account.new(account_id:
|
115
|
-
acc5 = Account.new(account_id:
|
114
|
+
acc4 = Account.new(account_id: 'acc004', balance: '2000')
|
115
|
+
acc5 = Account.new(account_id: 'acc005', balance: '500')
|
116
116
|
acc4.save
|
117
117
|
acc5.save
|
118
118
|
|
@@ -121,51 +121,49 @@ begin
|
|
121
121
|
|
122
122
|
puts "Validation result: #{result.valid? ? 'PASS ✅' : 'FAIL ❌'}"
|
123
123
|
puts "Summary: #{result.summary}"
|
124
|
-
|
125
|
-
rescue => e
|
124
|
+
rescue StandardError => e
|
126
125
|
puts "Validation demo encountered error: #{e.message}"
|
127
|
-
puts
|
126
|
+
puts 'This is expected as the framework needs Redis middleware integration'
|
128
127
|
end
|
129
128
|
|
130
129
|
# Example 4: Performance Analysis
|
131
130
|
puts "\n4. Performance Analysis"
|
132
|
-
puts
|
131
|
+
puts '-' * 30
|
133
132
|
|
134
133
|
begin
|
135
134
|
commands = Familia::Validation.capture_commands do
|
136
135
|
# Create multiple accounts
|
137
136
|
accounts = []
|
138
137
|
(1..5).each do |i|
|
139
|
-
account = Account.new(account_id: "perf#{i}", balance:
|
138
|
+
account = Account.new(account_id: "perf#{i}", balance: '1000')
|
140
139
|
account.save
|
141
140
|
accounts << account
|
142
141
|
end
|
143
142
|
|
144
143
|
# Perform operations
|
145
|
-
accounts[0].balance =
|
144
|
+
accounts[0].balance = '1100'
|
146
145
|
accounts[0].save
|
147
146
|
end
|
148
147
|
|
149
148
|
analyzer = Familia::Validation::PerformanceAnalyzer.new(commands)
|
150
149
|
analysis = analyzer.analyze
|
151
150
|
|
152
|
-
puts
|
151
|
+
puts 'Performance Analysis:'
|
153
152
|
puts " Total Commands: #{analysis[:total_commands]}"
|
154
153
|
puts " Command Types: #{analysis[:command_type_breakdown].keys.join(', ')}"
|
155
154
|
puts " Efficiency Score: #{analysis[:efficiency_score]}/100"
|
156
|
-
|
157
|
-
rescue => e
|
155
|
+
rescue StandardError => e
|
158
156
|
puts "Performance analysis encountered error: #{e.message}"
|
159
157
|
end
|
160
158
|
|
161
159
|
# Example 5: Atomicity Validation
|
162
160
|
puts "\n5. Atomicity Validation"
|
163
|
-
puts
|
161
|
+
puts '-' * 30
|
164
162
|
|
165
163
|
begin
|
166
164
|
# Test atomic vs non-atomic operations
|
167
|
-
acc6 = Account.new(account_id:
|
168
|
-
acc7 = Account.new(account_id:
|
165
|
+
acc6 = Account.new(account_id: 'acc006', balance: '3000')
|
166
|
+
acc7 = Account.new(account_id: 'acc007', balance: '1000')
|
169
167
|
acc6.save
|
170
168
|
acc7.save
|
171
169
|
|
@@ -180,13 +178,12 @@ begin
|
|
180
178
|
result = atomicity_validator.validate
|
181
179
|
|
182
180
|
puts "Atomicity validation: #{result.valid? ? 'PASS ✅' : 'FAIL ❌'}"
|
183
|
-
|
184
|
-
rescue => e
|
181
|
+
rescue StandardError => e
|
185
182
|
puts "Atomicity validation encountered error: #{e.message}"
|
186
183
|
end
|
187
184
|
|
188
185
|
puts "\n6. Framework Architecture Overview"
|
189
|
-
puts
|
186
|
+
puts '-' * 30
|
190
187
|
puts "
|
191
188
|
The Redis Command Validation Framework provides:
|
192
189
|
|
@@ -224,8 +221,8 @@ Key Benefits:
|
|
224
221
|
"
|
225
222
|
|
226
223
|
# Cleanup
|
227
|
-
cleanup_keys = Familia.dbclient.keys(
|
224
|
+
cleanup_keys = Familia.dbclient.keys('account:*')
|
228
225
|
Familia.dbclient.del(*cleanup_keys) if cleanup_keys.any?
|
229
226
|
|
230
227
|
puts "\n🎉 Demo complete! The validation framework is ready for use."
|
231
|
-
puts
|
228
|
+
puts ' See try/validation/ for comprehensive test examples.'
|
@@ -17,11 +17,11 @@ class User < Familia::Horreum
|
|
17
17
|
field :user_id
|
18
18
|
field :email
|
19
19
|
field :name
|
20
|
-
field :role
|
20
|
+
field :role # admin, editor, viewer, guest
|
21
21
|
field :created_at
|
22
22
|
|
23
|
-
sorted_set :documents
|
24
|
-
sorted_set :recent_activity
|
23
|
+
sorted_set :documents # Documents this user can access
|
24
|
+
sorted_set :recent_activity # Recent document access
|
25
25
|
end
|
26
26
|
|
27
27
|
class Document < Familia::Horreum
|
@@ -39,10 +39,10 @@ class Document < Familia::Horreum
|
|
39
39
|
field :content
|
40
40
|
field :created_at
|
41
41
|
field :updated_at
|
42
|
-
field :document_type
|
42
|
+
field :document_type # public, private, confidential
|
43
43
|
|
44
|
-
sorted_set :collaborators
|
45
|
-
list :audit_log
|
44
|
+
sorted_set :collaborators # Users with access to this document
|
45
|
+
list :audit_log # Track permission changes and access
|
46
46
|
|
47
47
|
# Add document to user's collection with specific permissions
|
48
48
|
def share_with_user(user, *permissions)
|
@@ -78,7 +78,7 @@ class Document < Familia::Horreum
|
|
78
78
|
|
79
79
|
# Get users with specific permission level or higher
|
80
80
|
def users_with_permission(*required_permissions)
|
81
|
-
all_permissions.select do |
|
81
|
+
all_permissions.select do |_user_id, user_perms|
|
82
82
|
required_permissions.all? { |perm| user_perms.include?(perm) }
|
83
83
|
end.keys
|
84
84
|
end
|
@@ -102,7 +102,7 @@ class Document < Familia::Horreum
|
|
102
102
|
active_users: active_users,
|
103
103
|
total_collaborators: collaborators.size,
|
104
104
|
permission_breakdown: all_permissions,
|
105
|
-
audit_entries: audit_log.range(0, 50)
|
105
|
+
audit_entries: audit_log.range(0, 50),
|
106
106
|
}
|
107
107
|
end
|
108
108
|
end
|
@@ -113,10 +113,10 @@ class DocumentService
|
|
113
113
|
ROLE_PERMISSIONS = {
|
114
114
|
guest: [:read],
|
115
115
|
viewer: [:read],
|
116
|
-
commenter: [
|
117
|
-
editor: [
|
118
|
-
reviewer: [
|
119
|
-
admin: [
|
116
|
+
commenter: %i[read append],
|
117
|
+
editor: %i[read write edit],
|
118
|
+
reviewer: %i[read write edit delete],
|
119
|
+
admin: %i[read write edit delete configure transfer admin],
|
120
120
|
}.freeze
|
121
121
|
|
122
122
|
def self.create_document(owner, title, content, doc_type = 'private')
|
@@ -164,7 +164,7 @@ class DocumentService
|
|
164
164
|
|
165
165
|
documents.each do |doc|
|
166
166
|
users.each do |user|
|
167
|
-
doc.revoke_access(user)
|
167
|
+
doc.revoke_access(user) # Clear existing
|
168
168
|
doc.share_with_user(user, *permissions) if permissions
|
169
169
|
end
|
170
170
|
end
|
@@ -173,8 +173,8 @@ end
|
|
173
173
|
|
174
174
|
# Example Usage and Demonstration
|
175
175
|
if __FILE__ == $0
|
176
|
-
puts
|
177
|
-
puts
|
176
|
+
puts '🚀 Familia Bit Encoding Integration Example'
|
177
|
+
puts '=' * 50
|
178
178
|
|
179
179
|
# Create users
|
180
180
|
alice = User.new(user_id: 'alice', email: 'alice@company.com', name: 'Alice Smith', role: 'admin')
|
@@ -182,9 +182,9 @@ if __FILE__ == $0
|
|
182
182
|
charlie = User.new(user_id: 'charlie', email: 'charlie@company.com', name: 'Charlie Brown', role: 'viewer')
|
183
183
|
|
184
184
|
# Create documents
|
185
|
-
doc1 = DocumentService.create_document(alice,
|
186
|
-
doc2 = DocumentService.create_document(alice,
|
187
|
-
doc3 = DocumentService.create_document(bob,
|
185
|
+
doc1 = DocumentService.create_document(alice, 'Q4 Financial Report', 'Confidential financial data...', 'confidential')
|
186
|
+
doc2 = DocumentService.create_document(alice, 'Team Meeting Notes', 'Weekly standup notes...', 'private')
|
187
|
+
doc3 = DocumentService.create_document(bob, 'Project Proposal', 'New feature proposal...', 'public')
|
188
188
|
|
189
189
|
# Share documents with different permission levels
|
190
190
|
puts "\n📄 Document Sharing:"
|
@@ -209,14 +209,14 @@ if __FILE__ == $0
|
|
209
209
|
analytics = doc1.access_analytics
|
210
210
|
puts "Financial Report - Active Users: #{analytics[:active_users].size}"
|
211
211
|
puts "Total Collaborators: #{analytics[:total_collaborators]}"
|
212
|
-
puts
|
212
|
+
puts 'Permission Breakdown:'
|
213
213
|
analytics[:permission_breakdown].each do |user_id, perms|
|
214
214
|
puts " #{user_id}: #{perms.join(', ')}"
|
215
215
|
end
|
216
216
|
|
217
217
|
# Demonstrate bit encoding efficiency
|
218
218
|
puts "\n⚡ Bit Encoding Efficiency:"
|
219
|
-
score = Familia::Features::Relationships::ScoreEncoding.encode_score(Time.now, [
|
219
|
+
score = Familia::Features::Relationships::ScoreEncoding.encode_score(Time.now, %i[read write edit delete])
|
220
220
|
decoded = Familia::Features::Relationships::ScoreEncoding.decode_score(score)
|
221
221
|
puts "Encoded score: #{score}"
|
222
222
|
puts "Decoded permissions: #{decoded[:permission_list].join(', ')}"
|
@@ -225,13 +225,16 @@ if __FILE__ == $0
|
|
225
225
|
# Cleanup
|
226
226
|
puts "\n🧹 Cleanup:"
|
227
227
|
[alice, bob, charlie].each { |user| user.documents.clear }
|
228
|
-
[doc1, doc2, doc3].each
|
228
|
+
[doc1, doc2, doc3].each do |doc|
|
229
|
+
doc.clear_all_permissions
|
230
|
+
doc.collaborators.clear
|
231
|
+
end
|
229
232
|
|
230
|
-
puts
|
233
|
+
puts '✅ Integration example completed successfully!'
|
231
234
|
puts "\nThis demonstrates:"
|
232
|
-
puts
|
233
|
-
puts
|
234
|
-
puts
|
235
|
-
puts
|
236
|
-
puts
|
235
|
+
puts '• Fine-grained permission management with 8-bit encoding'
|
236
|
+
puts '• Role-based access control with business logic'
|
237
|
+
puts '• Time-based analytics and audit trails'
|
238
|
+
puts '• Efficient Redis storage with sorted sets'
|
239
|
+
puts '• Production-ready error handling and validation'
|
237
240
|
end
|