familia 2.0.0.pre14 → 2.0.0.pre16
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/code-quality.yml +138 -0
- data/.github/workflows/code-smellage.yml +145 -0
- data/.github/workflows/docs.yml +31 -8
- data/.gitignore +1 -1
- data/.pre-commit-config.yaml +7 -1
- data/.reek.yml +98 -0
- data/.rubocop.yml +48 -10
- data/.talismanrc +9 -0
- data/.yardopts +18 -13
- data/CHANGELOG.rst +66 -6
- data/CLAUDE.md +1 -1
- data/Gemfile +6 -5
- data/Gemfile.lock +99 -23
- data/LICENSE.txt +1 -1
- data/README.md +285 -85
- data/changelog.d/README.md +2 -2
- data/docs/archive/FAMILIA_RELATIONSHIPS.md +22 -22
- data/docs/archive/FAMILIA_TECHNICAL.md +41 -41
- data/docs/archive/FAMILIA_UPDATE.md +3 -3
- data/docs/archive/README.md +3 -2
- data/docs/{guides/API-Reference.md → archive/api-reference.md} +87 -101
- data/docs/conf.py +29 -0
- data/docs/guides/{Field-System-Guide.md → core-field-system.md} +9 -9
- data/docs/guides/feature-encrypted-fields.md +785 -0
- data/docs/guides/{Expiration-Feature-Guide.md → feature-expiration.md} +11 -2
- data/docs/guides/feature-external-identifiers.md +637 -0
- data/docs/guides/feature-object-identifiers.md +435 -0
- data/docs/guides/{Quantization-Feature-Guide.md → feature-quantization.md} +94 -29
- data/docs/guides/feature-relationships-methods.md +684 -0
- data/docs/guides/feature-relationships.md +200 -0
- data/docs/guides/{Features-System-Developer-Guide.md → feature-system-devs.md} +4 -4
- data/docs/guides/{Feature-System-Guide.md → feature-system.md} +5 -5
- data/docs/guides/{Transient-Fields-Guide.md → feature-transient-fields.md} +2 -2
- data/docs/guides/{Implementation-Guide.md → implementation.md} +3 -3
- data/docs/guides/index.md +176 -0
- data/docs/guides/{Security-Model.md → security-model.md} +1 -1
- data/docs/migrating/v2.0.0-pre.md +1 -1
- data/docs/migrating/v2.0.0-pre11.md +4 -4
- data/docs/migrating/v2.0.0-pre12.md +2 -2
- data/docs/migrating/v2.0.0-pre13.md +1 -1
- data/docs/migrating/v2.0.0-pre5.md +33 -12
- data/docs/migrating/v2.0.0-pre6.md +2 -2
- data/docs/migrating/v2.0.0-pre7.md +8 -8
- data/docs/overview.md +623 -19
- data/docs/reference/api-technical.md +1365 -0
- data/examples/autoloader/mega_customer/features/deprecated_fields.rb +7 -0
- data/examples/autoloader/mega_customer/safe_dump_fields.rb +1 -1
- data/examples/autoloader/mega_customer.rb +3 -1
- data/examples/encrypted_fields.rb +378 -0
- data/examples/json_usage_patterns.rb +144 -0
- data/examples/relationships.rb +13 -13
- data/examples/safe_dump.rb +6 -6
- data/examples/single_connection_transaction_confusions.rb +379 -0
- data/lib/familia/base.rb +49 -10
- data/lib/familia/connection/handlers.rb +223 -0
- data/lib/familia/connection/individual_command_proxy.rb +64 -0
- data/lib/familia/connection/middleware.rb +75 -0
- data/lib/familia/connection/operation_core.rb +93 -0
- data/lib/familia/connection/operations.rb +277 -0
- data/lib/familia/connection/pipeline_core.rb +87 -0
- data/lib/familia/connection/transaction_core.rb +100 -0
- data/lib/familia/connection.rb +60 -186
- data/lib/familia/data_type/commands.rb +53 -51
- data/lib/familia/data_type/serialization.rb +108 -107
- data/lib/familia/data_type/types/counter.rb +1 -1
- data/lib/familia/data_type/types/hashkey.rb +13 -10
- data/lib/familia/data_type/types/{list.rb → listkey.rb} +13 -5
- data/lib/familia/data_type/types/lock.rb +3 -2
- data/lib/familia/data_type/types/sorted_set.rb +26 -15
- data/lib/familia/data_type/types/{string.rb → stringkey.rb} +7 -5
- data/lib/familia/data_type/types/unsorted_set.rb +20 -27
- data/lib/familia/data_type.rb +75 -47
- data/lib/familia/distinguisher.rb +85 -0
- data/lib/familia/encryption/encrypted_data.rb +15 -24
- data/lib/familia/encryption/manager.rb +6 -4
- data/lib/familia/encryption/providers/aes_gcm_provider.rb +1 -1
- data/lib/familia/encryption/providers/secure_xchacha20_poly1305_provider.rb +7 -9
- data/lib/familia/encryption/providers/xchacha20_poly1305_provider.rb +4 -5
- data/lib/familia/encryption/request_cache.rb +7 -7
- data/lib/familia/encryption.rb +2 -3
- data/lib/familia/errors.rb +9 -3
- data/lib/familia/{autoloader.rb → features/autoloader.rb} +49 -23
- data/lib/familia/features/encrypted_fields/concealed_string.rb +3 -4
- data/lib/familia/features/encrypted_fields/encrypted_field_type.rb +13 -14
- data/lib/familia/features/encrypted_fields.rb +68 -66
- data/lib/familia/features/expiration/extensions.rb +61 -0
- data/lib/familia/features/expiration.rb +35 -87
- data/lib/familia/features/external_identifier.rb +11 -12
- data/lib/familia/features/object_identifier.rb +58 -20
- data/lib/familia/features/quantization.rb +17 -22
- data/lib/familia/features/relationships/README.md +97 -0
- data/lib/familia/features/relationships/collection_operations.rb +104 -0
- data/lib/familia/features/relationships/indexing/multi_index_generators.rb +202 -0
- data/lib/familia/features/relationships/indexing/unique_index_generators.rb +301 -0
- data/lib/familia/features/relationships/indexing.rb +176 -256
- data/lib/familia/features/relationships/indexing_relationship.rb +35 -0
- data/lib/familia/features/relationships/participation/participant_methods.rb +160 -0
- data/lib/familia/features/relationships/participation/target_methods.rb +225 -0
- data/lib/familia/features/relationships/participation.rb +656 -0
- data/lib/familia/features/relationships/participation_relationship.rb +31 -0
- data/lib/familia/features/relationships/score_encoding.rb +20 -20
- data/lib/familia/features/relationships.rb +69 -271
- data/lib/familia/features/safe_dump.rb +127 -132
- data/lib/familia/features/transient_fields/redacted_string.rb +6 -6
- data/lib/familia/features/transient_fields/transient_field_type.rb +5 -5
- data/lib/familia/features/transient_fields.rb +5 -5
- data/lib/familia/features.rb +21 -21
- data/lib/familia/field_type.rb +24 -4
- data/lib/familia/horreum/core/connection.rb +229 -26
- data/lib/familia/horreum/core/database_commands.rb +27 -17
- data/lib/familia/horreum/core/serialization.rb +40 -20
- data/lib/familia/horreum/core/utils.rb +2 -1
- data/lib/familia/horreum/shared/settings.rb +2 -1
- data/lib/familia/horreum/subclass/definition.rb +33 -45
- data/lib/familia/horreum/subclass/management.rb +72 -24
- data/lib/familia/horreum/subclass/related_fields_management.rb +82 -21
- data/lib/familia/horreum.rb +196 -114
- data/lib/familia/json_serializer.rb +0 -1
- data/lib/familia/logging.rb +11 -114
- data/lib/familia/refinements/dear_json.rb +122 -0
- data/lib/familia/refinements/logger_trace.rb +20 -17
- data/lib/familia/refinements/stylize_words.rb +65 -0
- data/lib/familia/refinements/time_literals.rb +60 -52
- data/lib/familia/refinements.rb +2 -1
- data/lib/familia/secure_identifier.rb +60 -28
- data/lib/familia/settings.rb +83 -7
- data/lib/familia/utils.rb +5 -87
- data/lib/familia/verifiable_identifier.rb +4 -4
- data/lib/familia/version.rb +1 -1
- data/lib/familia.rb +72 -15
- data/lib/middleware/database_middleware.rb +56 -14
- data/lib/{familia/multi_result.rb → multi_result.rb} +23 -16
- data/try/configuration/scenarios_try.rb +1 -1
- data/try/connection/fiber_context_preservation_try.rb +250 -0
- data/try/connection/handler_constraints_try.rb +59 -0
- data/try/connection/operation_mode_guards_try.rb +208 -0
- data/try/connection/pipeline_fallback_integration_try.rb +128 -0
- data/try/connection/responsibility_chain_tracking_try.rb +72 -0
- data/try/connection/transaction_fallback_integration_try.rb +288 -0
- data/try/connection/transaction_mode_permissive_try.rb +153 -0
- data/try/connection/transaction_mode_strict_try.rb +98 -0
- data/try/connection/transaction_mode_warn_try.rb +131 -0
- data/try/connection/transaction_modes_try.rb +249 -0
- data/try/core/autoloader_try.rb +129 -11
- data/try/core/connection_try.rb +7 -7
- data/try/core/conventional_inheritance_try.rb +130 -0
- data/try/core/create_method_try.rb +15 -23
- data/try/core/database_consistency_try.rb +10 -10
- data/try/core/errors_try.rb +8 -11
- data/try/core/familia_extended_try.rb +2 -2
- data/try/core/familia_members_methods_try.rb +76 -0
- data/try/core/isolated_dbclient_try.rb +165 -0
- data/try/core/middleware_try.rb +16 -16
- data/try/core/persistence_operations_try.rb +4 -4
- data/try/core/pools_try.rb +42 -26
- data/try/core/secure_identifier_try.rb +28 -24
- data/try/core/time_utils_try.rb +10 -10
- data/try/core/tools_try.rb +1 -1
- data/try/core/utils_try.rb +2 -2
- data/try/data_types/boolean_try.rb +4 -4
- data/try/data_types/datatype_base_try.rb +0 -2
- data/try/data_types/list_try.rb +10 -10
- data/try/data_types/sorted_set_try.rb +5 -5
- data/try/data_types/string_try.rb +12 -12
- data/try/data_types/unsortedset_try.rb +33 -0
- data/try/debugging/cache_behavior_tracer.rb +7 -7
- data/try/debugging/debug_aad_process.rb +1 -1
- data/try/debugging/debug_concealed_internal.rb +1 -1
- data/try/debugging/debug_cross_context.rb +1 -1
- data/try/debugging/debug_fresh_cross_context.rb +1 -1
- data/try/debugging/encryption_method_tracer.rb +10 -10
- data/try/edge_cases/hash_symbolization_try.rb +1 -1
- data/try/edge_cases/ttl_side_effects_try.rb +1 -1
- data/try/encryption/config_persistence_try.rb +2 -2
- data/try/encryption/encryption_core_try.rb +19 -19
- data/try/encryption/instance_variable_scope_try.rb +1 -1
- data/try/encryption/module_loading_try.rb +2 -2
- data/try/encryption/providers/aes_gcm_provider_try.rb +1 -1
- data/try/encryption/providers/xchacha20_poly1305_provider_try.rb +1 -1
- data/try/encryption/secure_memory_handling_try.rb +1 -1
- data/try/features/encrypted_fields/concealed_string_core_try.rb +11 -7
- data/try/features/encrypted_fields/encrypted_fields_core_try.rb +1 -1
- data/try/features/encrypted_fields/encrypted_fields_integration_try.rb +3 -3
- data/try/features/encrypted_fields/encrypted_fields_no_cache_security_try.rb +10 -10
- data/try/features/encrypted_fields/encrypted_fields_security_try.rb +14 -14
- data/try/features/encrypted_fields/error_conditions_try.rb +7 -7
- data/try/features/encrypted_fields/fresh_key_try.rb +1 -1
- data/try/features/encrypted_fields/nonce_uniqueness_try.rb +1 -1
- data/try/features/encrypted_fields/secure_by_default_behavior_try.rb +7 -7
- data/try/features/encrypted_fields/universal_serialization_safety_try.rb +13 -20
- data/try/features/external_identifier/external_identifier_try.rb +1 -1
- data/try/features/feature_dependencies_try.rb +3 -3
- data/try/features/object_identifier/object_identifier_integration_try.rb +28 -34
- data/try/features/object_identifier/object_identifier_try.rb +10 -0
- data/try/features/quantization/quantization_try.rb +1 -1
- data/try/features/relationships/indexing_commands_verification_try.rb +136 -0
- data/try/features/relationships/indexing_try.rb +433 -0
- data/try/features/relationships/participation_commands_verification_spec.rb +102 -0
- data/try/features/relationships/participation_commands_verification_try.rb +105 -0
- data/try/features/relationships/participation_performance_improvements_try.rb +124 -0
- data/try/features/relationships/participation_reverse_index_try.rb +196 -0
- data/try/features/relationships/relationships_api_changes_try.rb +72 -71
- data/try/features/relationships/relationships_edge_cases_try.rb +15 -18
- data/try/features/relationships/relationships_performance_minimal_try.rb +2 -2
- data/try/features/relationships/relationships_performance_simple_try.rb +8 -8
- data/try/features/relationships/relationships_performance_try.rb +20 -20
- data/try/features/relationships/relationships_try.rb +27 -38
- data/try/features/safe_dump/safe_dump_advanced_try.rb +2 -2
- data/try/features/transient_fields/refresh_reset_try.rb +1 -1
- data/try/features/transient_fields/simple_refresh_test.rb +1 -1
- data/try/helpers/test_cleanup.rb +86 -0
- data/try/helpers/test_helpers.rb +3 -3
- data/try/horreum/base_try.rb +3 -2
- data/try/horreum/commands_try.rb +1 -1
- data/try/horreum/destroy_related_fields_cleanup_try.rb +330 -0
- data/try/horreum/initialization_try.rb +11 -7
- data/try/horreum/relations_try.rb +21 -13
- data/try/horreum/serialization_try.rb +12 -11
- data/try/integration/cross_component_try.rb +3 -3
- data/try/memory/memory_basic_test.rb +1 -1
- data/try/memory/memory_docker_ruby_dump.sh +1 -1
- data/try/models/customer_safe_dump_try.rb +1 -1
- data/try/models/customer_try.rb +8 -10
- data/try/models/datatype_base_try.rb +3 -3
- data/try/models/familia_object_try.rb +9 -8
- data/try/performance/benchmarks_try.rb +2 -2
- data/try/prototypes/atomic_saves_v1_context_proxy.rb +2 -2
- data/try/prototypes/atomic_saves_v3_connection_pool.rb +3 -3
- data/try/prototypes/atomic_saves_v4.rb +1 -1
- data/try/prototypes/lib/atomic_saves_v2_connection_switching_helpers.rb +4 -4
- data/try/prototypes/lib/atomic_saves_v3_connection_pool_helpers.rb +4 -4
- data/try/prototypes/pooling/lib/atomic_saves_v3_connection_pool_helpers.rb +4 -4
- data/try/prototypes/pooling/lib/connection_pool_metrics.rb +5 -5
- data/try/prototypes/pooling/lib/connection_pool_stress_test.rb +26 -26
- data/try/prototypes/pooling/lib/connection_pool_threading_models.rb +7 -7
- data/try/prototypes/pooling/lib/visualize_stress_results.rb +1 -1
- data/try/prototypes/pooling/pool_siege.rb +11 -11
- data/try/prototypes/pooling/run_stress_tests.rb +7 -7
- data/try/refinements/dear_json_array_methods_try.rb +53 -0
- data/try/refinements/dear_json_hash_methods_try.rb +54 -0
- data/try/refinements/logger_trace_methods_try.rb +44 -0
- data/try/refinements/time_literals_numeric_methods_try.rb +141 -0
- data/try/refinements/time_literals_string_methods_try.rb +80 -0
- metadata +77 -45
- data/.rubocop_todo.yml +0 -208
- data/docs/connection_pooling.md +0 -192
- data/docs/guides/Connection-Pooling-Guide.md +0 -437
- data/docs/guides/Encrypted-Fields-Overview.md +0 -101
- data/docs/guides/Feature-System-Autoloading.md +0 -228
- data/docs/guides/Home.md +0 -116
- data/docs/guides/Relationships-Guide.md +0 -737
- data/docs/guides/relationships-methods.md +0 -266
- data/docs/reference/auditing_database_commands.rb +0 -228
- data/examples/permissions.rb +0 -240
- data/lib/familia/features/autoloadable.rb +0 -113
- data/lib/familia/features/relationships/cascading.rb +0 -437
- data/lib/familia/features/relationships/membership.rb +0 -497
- data/lib/familia/features/relationships/permission_management.rb +0 -264
- data/lib/familia/features/relationships/querying.rb +0 -615
- data/lib/familia/features/relationships/redis_operations.rb +0 -274
- data/lib/familia/features/relationships/tracking.rb +0 -418
- data/lib/familia/refinements/snake_case.rb +0 -40
- data/lib/familia/validation/command_recorder.rb +0 -336
- data/lib/familia/validation/expectations.rb +0 -519
- data/lib/familia/validation/validation_helpers.rb +0 -443
- data/lib/familia/validation/validator.rb +0 -412
- data/lib/familia/validation.rb +0 -140
- data/try/data_types/set_try.rb +0 -33
- data/try/features/autoloadable/autoloadable_try.rb +0 -61
- data/try/features/relationships/categorical_permissions_try.rb +0 -515
- data/try/features/safe_dump/safe_dump_autoloading_try.rb +0 -111
- data/try/validation/atomic_operations_try.rb.disabled +0 -320
- data/try/validation/command_validation_try.rb.disabled +0 -207
- data/try/validation/performance_validation_try.rb.disabled +0 -324
- data/try/validation/real_world_scenarios_try.rb.disabled +0 -390
data/examples/permissions.rb
DELETED
@@ -1,240 +0,0 @@
|
|
1
|
-
# examples/bit_encoding_integration.rb
|
2
|
-
#
|
3
|
-
# Production Integration Example: Document Management System with Fine-Grained Permissions
|
4
|
-
#
|
5
|
-
# This example demonstrates how to use Familia's bit encoding permission system
|
6
|
-
# in a real-world document management scenario with sophisticated access control.
|
7
|
-
|
8
|
-
require_relative '../lib/familia'
|
9
|
-
require_relative '../lib/familia/features/relationships/score_encoding'
|
10
|
-
require_relative '../lib/familia/features/relationships/permission_management'
|
11
|
-
|
12
|
-
# Document Management System Classes
|
13
|
-
class User < Familia::Horreum
|
14
|
-
logical_database 14
|
15
|
-
|
16
|
-
identifier_field :user_id
|
17
|
-
field :user_id
|
18
|
-
field :email
|
19
|
-
field :name
|
20
|
-
field :role # admin, editor, viewer, guest
|
21
|
-
field :created_at
|
22
|
-
|
23
|
-
sorted_set :documents # Documents this user can access
|
24
|
-
sorted_set :recent_activity # Recent document access
|
25
|
-
end
|
26
|
-
|
27
|
-
class Document < Familia::Horreum
|
28
|
-
include Familia::Features::Relationships::PermissionManagement
|
29
|
-
|
30
|
-
logical_database 14
|
31
|
-
|
32
|
-
# Enable fine-grained permission tracking
|
33
|
-
permission_tracking :user_permissions
|
34
|
-
|
35
|
-
identifier_field :doc_id
|
36
|
-
field :doc_id
|
37
|
-
field :title
|
38
|
-
field :owner_id
|
39
|
-
field :content
|
40
|
-
field :created_at
|
41
|
-
field :updated_at
|
42
|
-
field :document_type # public, private, confidential
|
43
|
-
|
44
|
-
sorted_set :collaborators # Users with access to this document
|
45
|
-
list :audit_log # Track permission changes and access
|
46
|
-
|
47
|
-
# Add document to user's collection with specific permissions
|
48
|
-
def share_with_user(user, *permissions)
|
49
|
-
permissions = [:read] if permissions.empty?
|
50
|
-
|
51
|
-
# Create time-based score with permissions encoded
|
52
|
-
timestamp = updated_at || Time.now
|
53
|
-
score = Familia::Features::Relationships::ScoreEncoding.encode_score(timestamp, permissions)
|
54
|
-
|
55
|
-
# Add to user's document list
|
56
|
-
user.documents.add(score, doc_id)
|
57
|
-
|
58
|
-
# Add user to document's collaborator list
|
59
|
-
collaborators.add(score, user.user_id)
|
60
|
-
|
61
|
-
# Grant permissions via permission management
|
62
|
-
grant(user, *permissions)
|
63
|
-
|
64
|
-
# Log the permission grant
|
65
|
-
log_entry = "#{Time.now.iso8601}: Granted #{permissions.join(', ')} to #{user.email}"
|
66
|
-
audit_log.push(log_entry)
|
67
|
-
end
|
68
|
-
|
69
|
-
# Remove user access
|
70
|
-
def revoke_access(user)
|
71
|
-
user.documents.zrem(doc_id)
|
72
|
-
collaborators.zrem(user.user_id)
|
73
|
-
revoke(user, :read, :write, :edit, :delete, :configure, :transfer, :admin)
|
74
|
-
|
75
|
-
log_entry = "#{Time.now.iso8601}: Revoked all access from #{user.email}"
|
76
|
-
audit_log.push(log_entry)
|
77
|
-
end
|
78
|
-
|
79
|
-
# Get users with specific permission level or higher
|
80
|
-
def users_with_permission(*required_permissions)
|
81
|
-
all_permissions.select do |_user_id, user_perms|
|
82
|
-
required_permissions.all? { |perm| user_perms.include?(perm) }
|
83
|
-
end.keys
|
84
|
-
end
|
85
|
-
|
86
|
-
# Advanced: Get document access history for analytics
|
87
|
-
def access_analytics(days_back = 30)
|
88
|
-
start_time = Time.now - (days_back * 24 * 60 * 60)
|
89
|
-
end_time = Time.now
|
90
|
-
|
91
|
-
# Use score range to get recent access
|
92
|
-
range = Familia::Features::Relationships::ScoreEncoding.score_range(
|
93
|
-
start_time,
|
94
|
-
end_time,
|
95
|
-
min_permissions: [:read]
|
96
|
-
)
|
97
|
-
|
98
|
-
# Get collaborators active in time range
|
99
|
-
active_users = collaborators.rangebyscore(*range)
|
100
|
-
|
101
|
-
{
|
102
|
-
active_users: active_users,
|
103
|
-
total_collaborators: collaborators.size,
|
104
|
-
permission_breakdown: all_permissions,
|
105
|
-
audit_entries: audit_log.range(0, 50),
|
106
|
-
}
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
|
-
# Document Management Service - Business Logic Layer
|
111
|
-
class DocumentService
|
112
|
-
# Permission role definitions matching business needs
|
113
|
-
ROLE_PERMISSIONS = {
|
114
|
-
guest: [:read],
|
115
|
-
viewer: [:read],
|
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
|
-
}.freeze
|
121
|
-
|
122
|
-
def self.create_document(owner, title, content, doc_type = 'private')
|
123
|
-
doc = Document.new(
|
124
|
-
doc_id: "doc_#{Time.now.to_i}_#{rand(1000)}",
|
125
|
-
title: title,
|
126
|
-
content: content,
|
127
|
-
owner_id: owner.user_id,
|
128
|
-
document_type: doc_type,
|
129
|
-
created_at: Time.now,
|
130
|
-
updated_at: Time.now
|
131
|
-
)
|
132
|
-
|
133
|
-
# Owner gets full admin access
|
134
|
-
doc.share_with_user(owner, *ROLE_PERMISSIONS[:admin])
|
135
|
-
doc
|
136
|
-
end
|
137
|
-
|
138
|
-
def self.share_document(document, user, role)
|
139
|
-
permissions = ROLE_PERMISSIONS[role] || ROLE_PERMISSIONS[:viewer]
|
140
|
-
document.share_with_user(user, *permissions)
|
141
|
-
end
|
142
|
-
|
143
|
-
def self.can_user_perform?(user, document, action)
|
144
|
-
case action
|
145
|
-
when :view, :read
|
146
|
-
document.can?(user, :read)
|
147
|
-
when :comment, :append
|
148
|
-
document.can?(user, :read, :append)
|
149
|
-
when :edit, :modify
|
150
|
-
document.can?(user, :read, :write, :edit)
|
151
|
-
when :delete, :remove
|
152
|
-
document.can?(user, :delete)
|
153
|
-
when :share, :configure
|
154
|
-
document.can?(user, :configure)
|
155
|
-
when :transfer_ownership
|
156
|
-
document.can?(user, :admin)
|
157
|
-
else
|
158
|
-
false
|
159
|
-
end
|
160
|
-
end
|
161
|
-
|
162
|
-
def self.bulk_permission_update(documents, users, role)
|
163
|
-
permissions = ROLE_PERMISSIONS[role]
|
164
|
-
|
165
|
-
documents.each do |doc|
|
166
|
-
users.each do |user|
|
167
|
-
doc.revoke_access(user) # Clear existing
|
168
|
-
doc.share_with_user(user, *permissions) if permissions
|
169
|
-
end
|
170
|
-
end
|
171
|
-
end
|
172
|
-
end
|
173
|
-
|
174
|
-
# Example Usage and Demonstration
|
175
|
-
if __FILE__ == $0
|
176
|
-
puts '🚀 Familia Bit Encoding Integration Example'
|
177
|
-
puts '=' * 50
|
178
|
-
|
179
|
-
# Create users
|
180
|
-
alice = User.new(user_id: 'alice', email: 'alice@company.com', name: 'Alice Smith', role: 'admin')
|
181
|
-
bob = User.new(user_id: 'bob', email: 'bob@company.com', name: 'Bob Jones', role: 'editor')
|
182
|
-
charlie = User.new(user_id: 'charlie', email: 'charlie@company.com', name: 'Charlie Brown', role: 'viewer')
|
183
|
-
|
184
|
-
# Create documents
|
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
|
-
|
189
|
-
# Share documents with different permission levels
|
190
|
-
puts "\n📄 Document Sharing:"
|
191
|
-
DocumentService.share_document(doc1, bob, :reviewer) # Bob can review financial report
|
192
|
-
DocumentService.share_document(doc1, charlie, :viewer) # Charlie can only view
|
193
|
-
|
194
|
-
DocumentService.share_document(doc2, bob, :editor) # Bob can edit meeting notes
|
195
|
-
DocumentService.share_document(doc2, charlie, :commenter) # Charlie can comment
|
196
|
-
|
197
|
-
DocumentService.share_document(doc3, alice, :admin) # Alice gets admin on Bob's doc
|
198
|
-
DocumentService.share_document(doc3, charlie, :editor) # Charlie can edit proposal
|
199
|
-
|
200
|
-
# Test permission checks
|
201
|
-
puts "\n🔐 Permission Testing:"
|
202
|
-
puts "Can Bob edit financial report? #{DocumentService.can_user_perform?(bob, doc1, :edit)}"
|
203
|
-
puts "Can Bob delete financial report? #{DocumentService.can_user_perform?(bob, doc1, :delete)}"
|
204
|
-
puts "Can Charlie comment on meeting notes? #{DocumentService.can_user_perform?(charlie, doc2, :comment)}"
|
205
|
-
puts "Can Charlie edit project proposal? #{DocumentService.can_user_perform?(charlie, doc3, :edit)}"
|
206
|
-
|
207
|
-
# Advanced analytics
|
208
|
-
puts "\n📊 Document Analytics:"
|
209
|
-
analytics = doc1.access_analytics
|
210
|
-
puts "Financial Report - Active Users: #{analytics[:active_users].size}"
|
211
|
-
puts "Total Collaborators: #{analytics[:total_collaborators]}"
|
212
|
-
puts 'Permission Breakdown:'
|
213
|
-
analytics[:permission_breakdown].each do |user_id, perms|
|
214
|
-
puts " #{user_id}: #{perms.join(', ')}"
|
215
|
-
end
|
216
|
-
|
217
|
-
# Demonstrate bit encoding efficiency
|
218
|
-
puts "\n⚡ Bit Encoding Efficiency:"
|
219
|
-
score = Familia::Features::Relationships::ScoreEncoding.encode_score(Time.now, %i[read write edit delete])
|
220
|
-
decoded = Familia::Features::Relationships::ScoreEncoding.decode_score(score)
|
221
|
-
puts "Encoded score: #{score}"
|
222
|
-
puts "Decoded permissions: #{decoded[:permission_list].join(', ')}"
|
223
|
-
puts "Permission bits: #{decoded[:permissions]} (#{decoded[:permissions].to_s(2).rjust(8, '0')})"
|
224
|
-
|
225
|
-
# Cleanup
|
226
|
-
puts "\n🧹 Cleanup:"
|
227
|
-
[alice, bob, charlie].each { |user| user.documents.clear }
|
228
|
-
[doc1, doc2, doc3].each do |doc|
|
229
|
-
doc.clear_all_permissions
|
230
|
-
doc.collaborators.clear
|
231
|
-
end
|
232
|
-
|
233
|
-
puts '✅ Integration example completed successfully!'
|
234
|
-
puts "\nThis demonstrates:"
|
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'
|
240
|
-
end
|
@@ -1,113 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative '../refinements/snake_case'
|
4
|
-
|
5
|
-
module Familia
|
6
|
-
module Features
|
7
|
-
# Enables automatic loading of feature-specific files when a feature is included in a user class.
|
8
|
-
#
|
9
|
-
# When included in a feature module, adds ClassMethods that detect when the feature is
|
10
|
-
# included in user classes, derives the feature name, and autoloads files matching
|
11
|
-
# conventional patterns in the user class's directory structure.
|
12
|
-
module Autoloadable
|
13
|
-
using Familia::Refinements::SnakeCase
|
14
|
-
|
15
|
-
# Sets up a feature module with autoloading capabilities.
|
16
|
-
#
|
17
|
-
# Extends the feature module with ClassMethods to handle post-inclusion autoloading.
|
18
|
-
#
|
19
|
-
# @param feature_module [Module] the feature module being enhanced
|
20
|
-
def self.included(feature_module)
|
21
|
-
feature_module.extend(ClassMethods)
|
22
|
-
end
|
23
|
-
|
24
|
-
# Methods added to feature modules that include Autoloadable.
|
25
|
-
module ClassMethods
|
26
|
-
# Triggered when the feature is included in a user class.
|
27
|
-
#
|
28
|
-
# Sets up for post-inclusion autoloading. The actual autoloading
|
29
|
-
# is deferred until after feature setup completes.
|
30
|
-
#
|
31
|
-
# @param base [Class] the user class including this feature
|
32
|
-
def included(base)
|
33
|
-
# Call parent included method if it exists (defensive programming for mixed-in contexts)
|
34
|
-
super if defined?(super)
|
35
|
-
|
36
|
-
# No autoloading here - it's deferred to post_inclusion_autoload
|
37
|
-
# to ensure the feature is fully set up before extension files are loaded
|
38
|
-
end
|
39
|
-
|
40
|
-
# Called by the feature system after the feature is fully included.
|
41
|
-
#
|
42
|
-
# Uses const_source_location to determine where the base class is defined,
|
43
|
-
# then autoloads feature-specific extension files from that location.
|
44
|
-
#
|
45
|
-
# @param base [Class] the class that included this feature
|
46
|
-
# @param feature_name [Symbol] the name of the feature
|
47
|
-
# @param options [Hash] feature options (unused but kept for compatibility)
|
48
|
-
def post_inclusion_autoload(base, feature_name, options)
|
49
|
-
Familia.trace :FEATURE, nil, "[Autoloadable] post_inclusion_autoload called for #{feature_name} on #{base.name || base}", caller(1..1) if Familia.debug?
|
50
|
-
|
51
|
-
# Get the source location via Ruby's built-in introspection
|
52
|
-
source_location = nil
|
53
|
-
|
54
|
-
# Check for named classes that can be looked up via const_source_location
|
55
|
-
# Class#name always returns String or nil, so type check is redundant
|
56
|
-
if base.name && !base.name.empty?
|
57
|
-
begin
|
58
|
-
location_info = Module.const_source_location(base.name)
|
59
|
-
source_location = location_info&.first
|
60
|
-
Familia.trace :FEATURE, nil, "[Autoloadable] Source location for #{base.name}: #{source_location}", caller(1..1) if Familia.debug?
|
61
|
-
rescue NameError => e
|
62
|
-
# Handle cases where the class name is not a valid constant name
|
63
|
-
# This can happen in test environments with dynamically created classes
|
64
|
-
Familia.trace :FEATURE, nil, "[Autoloadable] Cannot resolve source location for #{base.name}: #{e.message}", caller(1..1) if Familia.debug?
|
65
|
-
end
|
66
|
-
else
|
67
|
-
Familia.trace :FEATURE, nil, "[Autoloadable] Skipping source location detection - base.name=#{base.name.inspect}", caller(1..1) if Familia.debug?
|
68
|
-
end
|
69
|
-
|
70
|
-
# Autoload feature-specific files if we have a valid source location
|
71
|
-
if source_location && !source_location.include?('-e') # Skip eval/irb contexts
|
72
|
-
Familia.trace :FEATURE, nil, "[Autoloadable] Calling autoload_feature_files with #{source_location}", caller(1..1) if Familia.debug?
|
73
|
-
autoload_feature_files(source_location, base, feature_name.to_s.snake_case)
|
74
|
-
else
|
75
|
-
Familia.trace :FEATURE, nil, "[Autoloadable] Skipping autoload - no valid source location", caller(1..1) if Familia.debug?
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
private
|
80
|
-
|
81
|
-
# Autoloads feature-specific files from conventional directory patterns.
|
82
|
-
#
|
83
|
-
# Searches for files matching patterns like:
|
84
|
-
# - model_name/feature_name_*.rb
|
85
|
-
# - model_name/features/feature_name_*.rb
|
86
|
-
# - features/feature_name_*.rb
|
87
|
-
#
|
88
|
-
# @param location_path [String] path where the user class is defined
|
89
|
-
# @param base [Class] the user class including the feature
|
90
|
-
# @param feature_name [String] snake_case name of the feature
|
91
|
-
def autoload_feature_files(location_path, base, feature_name)
|
92
|
-
base_dir = File.dirname(location_path)
|
93
|
-
|
94
|
-
# Handle anonymous classes gracefully
|
95
|
-
model_name = base.name ? base.name.snake_case : "anonymous_#{base.object_id}"
|
96
|
-
|
97
|
-
# Look for feature-specific files in conventional locations
|
98
|
-
patterns = [
|
99
|
-
File.join(base_dir, model_name, "#{feature_name}_*.rb"),
|
100
|
-
File.join(base_dir, model_name, 'features', "#{feature_name}_*.rb"),
|
101
|
-
File.join(base_dir, 'features', "#{feature_name}_*.rb"),
|
102
|
-
]
|
103
|
-
|
104
|
-
# Use Autoloader's shared method for consistent file loading
|
105
|
-
Familia::Autoloader.autoload_files(
|
106
|
-
patterns,
|
107
|
-
log_prefix: "Autoloadable(#{feature_name})"
|
108
|
-
)
|
109
|
-
end
|
110
|
-
end
|
111
|
-
end
|
112
|
-
end
|
113
|
-
end
|