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.
Files changed (276) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/code-quality.yml +138 -0
  3. data/.github/workflows/code-smellage.yml +145 -0
  4. data/.github/workflows/docs.yml +31 -8
  5. data/.gitignore +1 -1
  6. data/.pre-commit-config.yaml +7 -1
  7. data/.reek.yml +98 -0
  8. data/.rubocop.yml +48 -10
  9. data/.talismanrc +9 -0
  10. data/.yardopts +18 -13
  11. data/CHANGELOG.rst +66 -6
  12. data/CLAUDE.md +1 -1
  13. data/Gemfile +6 -5
  14. data/Gemfile.lock +99 -23
  15. data/LICENSE.txt +1 -1
  16. data/README.md +285 -85
  17. data/changelog.d/README.md +2 -2
  18. data/docs/archive/FAMILIA_RELATIONSHIPS.md +22 -22
  19. data/docs/archive/FAMILIA_TECHNICAL.md +41 -41
  20. data/docs/archive/FAMILIA_UPDATE.md +3 -3
  21. data/docs/archive/README.md +3 -2
  22. data/docs/{guides/API-Reference.md → archive/api-reference.md} +87 -101
  23. data/docs/conf.py +29 -0
  24. data/docs/guides/{Field-System-Guide.md → core-field-system.md} +9 -9
  25. data/docs/guides/feature-encrypted-fields.md +785 -0
  26. data/docs/guides/{Expiration-Feature-Guide.md → feature-expiration.md} +11 -2
  27. data/docs/guides/feature-external-identifiers.md +637 -0
  28. data/docs/guides/feature-object-identifiers.md +435 -0
  29. data/docs/guides/{Quantization-Feature-Guide.md → feature-quantization.md} +94 -29
  30. data/docs/guides/feature-relationships-methods.md +684 -0
  31. data/docs/guides/feature-relationships.md +200 -0
  32. data/docs/guides/{Features-System-Developer-Guide.md → feature-system-devs.md} +4 -4
  33. data/docs/guides/{Feature-System-Guide.md → feature-system.md} +5 -5
  34. data/docs/guides/{Transient-Fields-Guide.md → feature-transient-fields.md} +2 -2
  35. data/docs/guides/{Implementation-Guide.md → implementation.md} +3 -3
  36. data/docs/guides/index.md +176 -0
  37. data/docs/guides/{Security-Model.md → security-model.md} +1 -1
  38. data/docs/migrating/v2.0.0-pre.md +1 -1
  39. data/docs/migrating/v2.0.0-pre11.md +4 -4
  40. data/docs/migrating/v2.0.0-pre12.md +2 -2
  41. data/docs/migrating/v2.0.0-pre13.md +1 -1
  42. data/docs/migrating/v2.0.0-pre5.md +33 -12
  43. data/docs/migrating/v2.0.0-pre6.md +2 -2
  44. data/docs/migrating/v2.0.0-pre7.md +8 -8
  45. data/docs/overview.md +623 -19
  46. data/docs/reference/api-technical.md +1365 -0
  47. data/examples/autoloader/mega_customer/features/deprecated_fields.rb +7 -0
  48. data/examples/autoloader/mega_customer/safe_dump_fields.rb +1 -1
  49. data/examples/autoloader/mega_customer.rb +3 -1
  50. data/examples/encrypted_fields.rb +378 -0
  51. data/examples/json_usage_patterns.rb +144 -0
  52. data/examples/relationships.rb +13 -13
  53. data/examples/safe_dump.rb +6 -6
  54. data/examples/single_connection_transaction_confusions.rb +379 -0
  55. data/lib/familia/base.rb +49 -10
  56. data/lib/familia/connection/handlers.rb +223 -0
  57. data/lib/familia/connection/individual_command_proxy.rb +64 -0
  58. data/lib/familia/connection/middleware.rb +75 -0
  59. data/lib/familia/connection/operation_core.rb +93 -0
  60. data/lib/familia/connection/operations.rb +277 -0
  61. data/lib/familia/connection/pipeline_core.rb +87 -0
  62. data/lib/familia/connection/transaction_core.rb +100 -0
  63. data/lib/familia/connection.rb +60 -186
  64. data/lib/familia/data_type/commands.rb +53 -51
  65. data/lib/familia/data_type/serialization.rb +108 -107
  66. data/lib/familia/data_type/types/counter.rb +1 -1
  67. data/lib/familia/data_type/types/hashkey.rb +13 -10
  68. data/lib/familia/data_type/types/{list.rb → listkey.rb} +13 -5
  69. data/lib/familia/data_type/types/lock.rb +3 -2
  70. data/lib/familia/data_type/types/sorted_set.rb +26 -15
  71. data/lib/familia/data_type/types/{string.rb → stringkey.rb} +7 -5
  72. data/lib/familia/data_type/types/unsorted_set.rb +20 -27
  73. data/lib/familia/data_type.rb +75 -47
  74. data/lib/familia/distinguisher.rb +85 -0
  75. data/lib/familia/encryption/encrypted_data.rb +15 -24
  76. data/lib/familia/encryption/manager.rb +6 -4
  77. data/lib/familia/encryption/providers/aes_gcm_provider.rb +1 -1
  78. data/lib/familia/encryption/providers/secure_xchacha20_poly1305_provider.rb +7 -9
  79. data/lib/familia/encryption/providers/xchacha20_poly1305_provider.rb +4 -5
  80. data/lib/familia/encryption/request_cache.rb +7 -7
  81. data/lib/familia/encryption.rb +2 -3
  82. data/lib/familia/errors.rb +9 -3
  83. data/lib/familia/{autoloader.rb → features/autoloader.rb} +49 -23
  84. data/lib/familia/features/encrypted_fields/concealed_string.rb +3 -4
  85. data/lib/familia/features/encrypted_fields/encrypted_field_type.rb +13 -14
  86. data/lib/familia/features/encrypted_fields.rb +68 -66
  87. data/lib/familia/features/expiration/extensions.rb +61 -0
  88. data/lib/familia/features/expiration.rb +35 -87
  89. data/lib/familia/features/external_identifier.rb +11 -12
  90. data/lib/familia/features/object_identifier.rb +58 -20
  91. data/lib/familia/features/quantization.rb +17 -22
  92. data/lib/familia/features/relationships/README.md +97 -0
  93. data/lib/familia/features/relationships/collection_operations.rb +104 -0
  94. data/lib/familia/features/relationships/indexing/multi_index_generators.rb +202 -0
  95. data/lib/familia/features/relationships/indexing/unique_index_generators.rb +301 -0
  96. data/lib/familia/features/relationships/indexing.rb +176 -256
  97. data/lib/familia/features/relationships/indexing_relationship.rb +35 -0
  98. data/lib/familia/features/relationships/participation/participant_methods.rb +160 -0
  99. data/lib/familia/features/relationships/participation/target_methods.rb +225 -0
  100. data/lib/familia/features/relationships/participation.rb +656 -0
  101. data/lib/familia/features/relationships/participation_relationship.rb +31 -0
  102. data/lib/familia/features/relationships/score_encoding.rb +20 -20
  103. data/lib/familia/features/relationships.rb +69 -271
  104. data/lib/familia/features/safe_dump.rb +127 -132
  105. data/lib/familia/features/transient_fields/redacted_string.rb +6 -6
  106. data/lib/familia/features/transient_fields/transient_field_type.rb +5 -5
  107. data/lib/familia/features/transient_fields.rb +5 -5
  108. data/lib/familia/features.rb +21 -21
  109. data/lib/familia/field_type.rb +24 -4
  110. data/lib/familia/horreum/core/connection.rb +229 -26
  111. data/lib/familia/horreum/core/database_commands.rb +27 -17
  112. data/lib/familia/horreum/core/serialization.rb +40 -20
  113. data/lib/familia/horreum/core/utils.rb +2 -1
  114. data/lib/familia/horreum/shared/settings.rb +2 -1
  115. data/lib/familia/horreum/subclass/definition.rb +33 -45
  116. data/lib/familia/horreum/subclass/management.rb +72 -24
  117. data/lib/familia/horreum/subclass/related_fields_management.rb +82 -21
  118. data/lib/familia/horreum.rb +196 -114
  119. data/lib/familia/json_serializer.rb +0 -1
  120. data/lib/familia/logging.rb +11 -114
  121. data/lib/familia/refinements/dear_json.rb +122 -0
  122. data/lib/familia/refinements/logger_trace.rb +20 -17
  123. data/lib/familia/refinements/stylize_words.rb +65 -0
  124. data/lib/familia/refinements/time_literals.rb +60 -52
  125. data/lib/familia/refinements.rb +2 -1
  126. data/lib/familia/secure_identifier.rb +60 -28
  127. data/lib/familia/settings.rb +83 -7
  128. data/lib/familia/utils.rb +5 -87
  129. data/lib/familia/verifiable_identifier.rb +4 -4
  130. data/lib/familia/version.rb +1 -1
  131. data/lib/familia.rb +72 -15
  132. data/lib/middleware/database_middleware.rb +56 -14
  133. data/lib/{familia/multi_result.rb → multi_result.rb} +23 -16
  134. data/try/configuration/scenarios_try.rb +1 -1
  135. data/try/connection/fiber_context_preservation_try.rb +250 -0
  136. data/try/connection/handler_constraints_try.rb +59 -0
  137. data/try/connection/operation_mode_guards_try.rb +208 -0
  138. data/try/connection/pipeline_fallback_integration_try.rb +128 -0
  139. data/try/connection/responsibility_chain_tracking_try.rb +72 -0
  140. data/try/connection/transaction_fallback_integration_try.rb +288 -0
  141. data/try/connection/transaction_mode_permissive_try.rb +153 -0
  142. data/try/connection/transaction_mode_strict_try.rb +98 -0
  143. data/try/connection/transaction_mode_warn_try.rb +131 -0
  144. data/try/connection/transaction_modes_try.rb +249 -0
  145. data/try/core/autoloader_try.rb +129 -11
  146. data/try/core/connection_try.rb +7 -7
  147. data/try/core/conventional_inheritance_try.rb +130 -0
  148. data/try/core/create_method_try.rb +15 -23
  149. data/try/core/database_consistency_try.rb +10 -10
  150. data/try/core/errors_try.rb +8 -11
  151. data/try/core/familia_extended_try.rb +2 -2
  152. data/try/core/familia_members_methods_try.rb +76 -0
  153. data/try/core/isolated_dbclient_try.rb +165 -0
  154. data/try/core/middleware_try.rb +16 -16
  155. data/try/core/persistence_operations_try.rb +4 -4
  156. data/try/core/pools_try.rb +42 -26
  157. data/try/core/secure_identifier_try.rb +28 -24
  158. data/try/core/time_utils_try.rb +10 -10
  159. data/try/core/tools_try.rb +1 -1
  160. data/try/core/utils_try.rb +2 -2
  161. data/try/data_types/boolean_try.rb +4 -4
  162. data/try/data_types/datatype_base_try.rb +0 -2
  163. data/try/data_types/list_try.rb +10 -10
  164. data/try/data_types/sorted_set_try.rb +5 -5
  165. data/try/data_types/string_try.rb +12 -12
  166. data/try/data_types/unsortedset_try.rb +33 -0
  167. data/try/debugging/cache_behavior_tracer.rb +7 -7
  168. data/try/debugging/debug_aad_process.rb +1 -1
  169. data/try/debugging/debug_concealed_internal.rb +1 -1
  170. data/try/debugging/debug_cross_context.rb +1 -1
  171. data/try/debugging/debug_fresh_cross_context.rb +1 -1
  172. data/try/debugging/encryption_method_tracer.rb +10 -10
  173. data/try/edge_cases/hash_symbolization_try.rb +1 -1
  174. data/try/edge_cases/ttl_side_effects_try.rb +1 -1
  175. data/try/encryption/config_persistence_try.rb +2 -2
  176. data/try/encryption/encryption_core_try.rb +19 -19
  177. data/try/encryption/instance_variable_scope_try.rb +1 -1
  178. data/try/encryption/module_loading_try.rb +2 -2
  179. data/try/encryption/providers/aes_gcm_provider_try.rb +1 -1
  180. data/try/encryption/providers/xchacha20_poly1305_provider_try.rb +1 -1
  181. data/try/encryption/secure_memory_handling_try.rb +1 -1
  182. data/try/features/encrypted_fields/concealed_string_core_try.rb +11 -7
  183. data/try/features/encrypted_fields/encrypted_fields_core_try.rb +1 -1
  184. data/try/features/encrypted_fields/encrypted_fields_integration_try.rb +3 -3
  185. data/try/features/encrypted_fields/encrypted_fields_no_cache_security_try.rb +10 -10
  186. data/try/features/encrypted_fields/encrypted_fields_security_try.rb +14 -14
  187. data/try/features/encrypted_fields/error_conditions_try.rb +7 -7
  188. data/try/features/encrypted_fields/fresh_key_try.rb +1 -1
  189. data/try/features/encrypted_fields/nonce_uniqueness_try.rb +1 -1
  190. data/try/features/encrypted_fields/secure_by_default_behavior_try.rb +7 -7
  191. data/try/features/encrypted_fields/universal_serialization_safety_try.rb +13 -20
  192. data/try/features/external_identifier/external_identifier_try.rb +1 -1
  193. data/try/features/feature_dependencies_try.rb +3 -3
  194. data/try/features/object_identifier/object_identifier_integration_try.rb +28 -34
  195. data/try/features/object_identifier/object_identifier_try.rb +10 -0
  196. data/try/features/quantization/quantization_try.rb +1 -1
  197. data/try/features/relationships/indexing_commands_verification_try.rb +136 -0
  198. data/try/features/relationships/indexing_try.rb +433 -0
  199. data/try/features/relationships/participation_commands_verification_spec.rb +102 -0
  200. data/try/features/relationships/participation_commands_verification_try.rb +105 -0
  201. data/try/features/relationships/participation_performance_improvements_try.rb +124 -0
  202. data/try/features/relationships/participation_reverse_index_try.rb +196 -0
  203. data/try/features/relationships/relationships_api_changes_try.rb +72 -71
  204. data/try/features/relationships/relationships_edge_cases_try.rb +15 -18
  205. data/try/features/relationships/relationships_performance_minimal_try.rb +2 -2
  206. data/try/features/relationships/relationships_performance_simple_try.rb +8 -8
  207. data/try/features/relationships/relationships_performance_try.rb +20 -20
  208. data/try/features/relationships/relationships_try.rb +27 -38
  209. data/try/features/safe_dump/safe_dump_advanced_try.rb +2 -2
  210. data/try/features/transient_fields/refresh_reset_try.rb +1 -1
  211. data/try/features/transient_fields/simple_refresh_test.rb +1 -1
  212. data/try/helpers/test_cleanup.rb +86 -0
  213. data/try/helpers/test_helpers.rb +3 -3
  214. data/try/horreum/base_try.rb +3 -2
  215. data/try/horreum/commands_try.rb +1 -1
  216. data/try/horreum/destroy_related_fields_cleanup_try.rb +330 -0
  217. data/try/horreum/initialization_try.rb +11 -7
  218. data/try/horreum/relations_try.rb +21 -13
  219. data/try/horreum/serialization_try.rb +12 -11
  220. data/try/integration/cross_component_try.rb +3 -3
  221. data/try/memory/memory_basic_test.rb +1 -1
  222. data/try/memory/memory_docker_ruby_dump.sh +1 -1
  223. data/try/models/customer_safe_dump_try.rb +1 -1
  224. data/try/models/customer_try.rb +8 -10
  225. data/try/models/datatype_base_try.rb +3 -3
  226. data/try/models/familia_object_try.rb +9 -8
  227. data/try/performance/benchmarks_try.rb +2 -2
  228. data/try/prototypes/atomic_saves_v1_context_proxy.rb +2 -2
  229. data/try/prototypes/atomic_saves_v3_connection_pool.rb +3 -3
  230. data/try/prototypes/atomic_saves_v4.rb +1 -1
  231. data/try/prototypes/lib/atomic_saves_v2_connection_switching_helpers.rb +4 -4
  232. data/try/prototypes/lib/atomic_saves_v3_connection_pool_helpers.rb +4 -4
  233. data/try/prototypes/pooling/lib/atomic_saves_v3_connection_pool_helpers.rb +4 -4
  234. data/try/prototypes/pooling/lib/connection_pool_metrics.rb +5 -5
  235. data/try/prototypes/pooling/lib/connection_pool_stress_test.rb +26 -26
  236. data/try/prototypes/pooling/lib/connection_pool_threading_models.rb +7 -7
  237. data/try/prototypes/pooling/lib/visualize_stress_results.rb +1 -1
  238. data/try/prototypes/pooling/pool_siege.rb +11 -11
  239. data/try/prototypes/pooling/run_stress_tests.rb +7 -7
  240. data/try/refinements/dear_json_array_methods_try.rb +53 -0
  241. data/try/refinements/dear_json_hash_methods_try.rb +54 -0
  242. data/try/refinements/logger_trace_methods_try.rb +44 -0
  243. data/try/refinements/time_literals_numeric_methods_try.rb +141 -0
  244. data/try/refinements/time_literals_string_methods_try.rb +80 -0
  245. metadata +77 -45
  246. data/.rubocop_todo.yml +0 -208
  247. data/docs/connection_pooling.md +0 -192
  248. data/docs/guides/Connection-Pooling-Guide.md +0 -437
  249. data/docs/guides/Encrypted-Fields-Overview.md +0 -101
  250. data/docs/guides/Feature-System-Autoloading.md +0 -228
  251. data/docs/guides/Home.md +0 -116
  252. data/docs/guides/Relationships-Guide.md +0 -737
  253. data/docs/guides/relationships-methods.md +0 -266
  254. data/docs/reference/auditing_database_commands.rb +0 -228
  255. data/examples/permissions.rb +0 -240
  256. data/lib/familia/features/autoloadable.rb +0 -113
  257. data/lib/familia/features/relationships/cascading.rb +0 -437
  258. data/lib/familia/features/relationships/membership.rb +0 -497
  259. data/lib/familia/features/relationships/permission_management.rb +0 -264
  260. data/lib/familia/features/relationships/querying.rb +0 -615
  261. data/lib/familia/features/relationships/redis_operations.rb +0 -274
  262. data/lib/familia/features/relationships/tracking.rb +0 -418
  263. data/lib/familia/refinements/snake_case.rb +0 -40
  264. data/lib/familia/validation/command_recorder.rb +0 -336
  265. data/lib/familia/validation/expectations.rb +0 -519
  266. data/lib/familia/validation/validation_helpers.rb +0 -443
  267. data/lib/familia/validation/validator.rb +0 -412
  268. data/lib/familia/validation.rb +0 -140
  269. data/try/data_types/set_try.rb +0 -33
  270. data/try/features/autoloadable/autoloadable_try.rb +0 -61
  271. data/try/features/relationships/categorical_permissions_try.rb +0 -515
  272. data/try/features/safe_dump/safe_dump_autoloading_try.rb +0 -111
  273. data/try/validation/atomic_operations_try.rb.disabled +0 -320
  274. data/try/validation/command_validation_try.rb.disabled +0 -207
  275. data/try/validation/performance_validation_try.rb.disabled +0 -324
  276. data/try/validation/real_world_scenarios_try.rb.disabled +0 -390
@@ -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