familia 1.2.0 → 2.0.0.pre.pre

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 (115) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +68 -0
  3. data/.github/workflows/docs.yml +64 -0
  4. data/.gitignore +4 -0
  5. data/.pre-commit-config.yaml +3 -1
  6. data/.rubocop.yml +16 -9
  7. data/.rubocop_todo.yml +177 -31
  8. data/.yardopts +9 -0
  9. data/CLAUDE.md +141 -0
  10. data/Gemfile +15 -2
  11. data/Gemfile.lock +76 -34
  12. data/README.md +39 -23
  13. data/bin/irb +3 -0
  14. data/docs/connection_pooling.md +317 -0
  15. data/familia.gemspec +9 -5
  16. data/lib/familia/base.rb +19 -9
  17. data/lib/familia/connection.rb +232 -65
  18. data/lib/familia/core_ext.rb +1 -1
  19. data/lib/familia/datatype/commands.rb +59 -0
  20. data/lib/familia/{redistype → datatype}/serialization.rb +9 -13
  21. data/lib/familia/{redistype → datatype}/types/hashkey.rb +25 -25
  22. data/lib/familia/{redistype → datatype}/types/list.rb +13 -13
  23. data/lib/familia/{redistype → datatype}/types/sorted_set.rb +20 -20
  24. data/lib/familia/{redistype → datatype}/types/string.rb +22 -21
  25. data/lib/familia/{redistype → datatype}/types/unsorted_set.rb +11 -11
  26. data/lib/familia/datatype.rb +243 -0
  27. data/lib/familia/errors.rb +5 -2
  28. data/lib/familia/features/expiration.rb +33 -34
  29. data/lib/familia/features/quantization.rb +9 -3
  30. data/lib/familia/features/safe_dump.rb +2 -3
  31. data/lib/familia/features.rb +2 -2
  32. data/lib/familia/horreum/class_methods.rb +97 -110
  33. data/lib/familia/horreum/commands.rb +46 -51
  34. data/lib/familia/horreum/connection.rb +82 -0
  35. data/lib/familia/horreum/{relations_management.rb → related_fields_management.rb} +37 -35
  36. data/lib/familia/horreum/serialization.rb +61 -198
  37. data/lib/familia/horreum/settings.rb +6 -17
  38. data/lib/familia/horreum/utils.rb +11 -10
  39. data/lib/familia/horreum.rb +69 -60
  40. data/lib/familia/logging.rb +12 -12
  41. data/lib/familia/multi_result.rb +72 -0
  42. data/lib/familia/refinements.rb +7 -44
  43. data/lib/familia/settings.rb +11 -11
  44. data/lib/familia/utils.rb +123 -90
  45. data/lib/familia/version.rb +4 -21
  46. data/lib/familia.rb +17 -12
  47. data/lib/middleware/database_middleware.rb +150 -0
  48. data/try/configuration/scenarios_try.rb +65 -0
  49. data/try/core/connection_try.rb +58 -0
  50. data/try/core/errors_try.rb +93 -0
  51. data/try/core/extensions_try.rb +26 -0
  52. data/try/{10_familia_try.rb → core/familia_extended_try.rb} +11 -10
  53. data/try/{00_familia_try.rb → core/familia_try.rb} +5 -3
  54. data/try/core/middleware_try.rb +68 -0
  55. data/try/core/refinements_try.rb +39 -0
  56. data/try/core/settings_try.rb +76 -0
  57. data/try/core/tools_try.rb +54 -0
  58. data/try/core/utils_try.rb +189 -0
  59. data/try/{26_redis_bool_try.rb → datatypes/boolean_try.rb} +4 -2
  60. data/try/datatypes/datatype_base_try.rb +69 -0
  61. data/try/{25_redis_type_hash_try.rb → datatypes/hash_try.rb} +5 -3
  62. data/try/{23_redis_type_list_try.rb → datatypes/list_try.rb} +5 -3
  63. data/try/{22_redis_type_set_try.rb → datatypes/set_try.rb} +5 -3
  64. data/try/{21_redis_type_zset_try.rb → datatypes/sorted_set_try.rb} +6 -4
  65. data/try/{24_redis_type_string_try.rb → datatypes/string_try.rb} +8 -8
  66. data/try/edge_cases/empty_identifiers_try.rb +48 -0
  67. data/try/{92_symbolize_try.rb → edge_cases/hash_symbolization_try.rb} +12 -7
  68. data/try/edge_cases/json_serialization_try.rb +85 -0
  69. data/try/edge_cases/race_conditions_try.rb +60 -0
  70. data/try/edge_cases/reserved_keywords_try.rb +59 -0
  71. data/try/{93_string_coercion_try.rb → edge_cases/string_coercion_try.rb} +60 -59
  72. data/try/edge_cases/ttl_side_effects_try.rb +51 -0
  73. data/try/features/expiration_try.rb +86 -0
  74. data/try/features/quantization_try.rb +90 -0
  75. data/try/{35_feature_safedump_try.rb → features/safe_dump_advanced_try.rb} +7 -6
  76. data/try/features/safe_dump_try.rb +137 -0
  77. data/try/{test_helpers.rb → helpers/test_helpers.rb} +25 -60
  78. data/try/{27_redis_horreum_try.rb → horreum/base_try.rb} +39 -14
  79. data/try/horreum/class_methods_try.rb +41 -0
  80. data/try/horreum/commands_try.rb +49 -0
  81. data/try/{29_redis_horreum_initialization_try.rb → horreum/initialization_try.rb} +9 -7
  82. data/try/horreum/relations_try.rb +146 -0
  83. data/try/{28_redis_horreum_serialization_try.rb → horreum/serialization_try.rb} +13 -11
  84. data/try/horreum/settings_try.rb +43 -0
  85. data/try/integration/cross_component_try.rb +46 -0
  86. data/try/{41_customer_safedump_try.rb → models/customer_safe_dump_try.rb} +9 -7
  87. data/try/{40_customer_try.rb → models/customer_try.rb} +20 -17
  88. data/try/models/datatype_base_try.rb +101 -0
  89. data/try/{30_familia_object_try.rb → models/familia_object_try.rb} +18 -16
  90. data/try/performance/benchmarks_try.rb +55 -0
  91. data/try/pooling/README.md +20 -0
  92. data/try/pooling/configurable_stress_test_try.rb +435 -0
  93. data/try/pooling/connection_pool_test_try.rb +273 -0
  94. data/try/pooling/lib/atomic_saves_v3_connection_pool_helpers.rb +192 -0
  95. data/try/pooling/lib/connection_pool_metrics.rb +372 -0
  96. data/try/pooling/lib/connection_pool_stress_test.rb +959 -0
  97. data/try/pooling/lib/connection_pool_threading_models.rb +421 -0
  98. data/try/pooling/lib/visualize_stress_results.rb +434 -0
  99. data/try/pooling/pool_siege_try.rb +509 -0
  100. data/try/pooling/run_stress_tests_try.rb +482 -0
  101. data/try/prototypes/atomic_saves_v1_context_proxy.rb +121 -0
  102. data/try/prototypes/atomic_saves_v2_connection_switching.rb +161 -0
  103. data/try/prototypes/atomic_saves_v3_connection_pool.rb +189 -0
  104. data/try/prototypes/atomic_saves_v4.rb +105 -0
  105. data/try/prototypes/lib/atomic_saves_v2_connection_switching_helpers.rb +124 -0
  106. data/try/prototypes/lib/atomic_saves_v3_connection_pool_helpers.rb +192 -0
  107. metadata +140 -43
  108. data/.github/workflows/ruby.yml +0 -71
  109. data/VERSION.yml +0 -4
  110. data/lib/familia/redistype/commands.rb +0 -59
  111. data/lib/familia/redistype.rb +0 -228
  112. data/lib/familia/tools.rb +0 -68
  113. data/lib/redis_middleware.rb +0 -109
  114. data/try/20_redis_type_try.rb +0 -70
  115. data/try/91_json_bug_try.rb +0 -86
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4a664f87a493fffb0e362752c8f988c8de27d83008f3a796b66737e3d15e771d
4
- data.tar.gz: cb22d85f774e40a7b656713760f2f1e31b154171620038c282ce2dbe5e88492b
3
+ metadata.gz: 92ee81bd30cdbcb84de0e72a26bde44a170efcd6578d81bdd47458aee37f52be
4
+ data.tar.gz: 4127847dbc955df3e6a7c7a3dd616dd3438da046f8703a59c734399697cf8903
5
5
  SHA512:
6
- metadata.gz: 3942bab094053731bd0d45c47a89c7bde62d26b99339cd2fad36e5481b28f91cd3eecb17c8ef6f77e3e486858151f6d5821e973f9fb7c068431abf11634806db
7
- data.tar.gz: 32933d4adbb991b3cb790f4a47134f7e428fa617b3f33d3068d98a2e795d2ee58b2654ac22fb913abdc46dd0f3459bcdfd50d71273a137d2f337c4da521e475d
6
+ metadata.gz: 87f8ff4b5423771ce6c1c93640cd3e83ea1e9beb023c130896878bed64fd61341ac18b365641bd7c30310fafdcf1b31569ca726dbaedf5c7ec23d308805b3305
7
+ data.tar.gz: d76036d625a604014380a67e5765263c2ea51bb608da09344a57fb4cf8386ae724499bb27e352c7d7133d15ae16a052dcb27ea262943cb509662666c72983d13
@@ -0,0 +1,68 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+
8
+ pull_request:
9
+
10
+ workflow_dispatch:
11
+
12
+ permissions:
13
+ contents: read
14
+
15
+ jobs:
16
+ build:
17
+ timeout-minutes: 10
18
+
19
+ runs-on: ubuntu-24.04
20
+
21
+ strategy:
22
+ fail-fast: true
23
+ matrix:
24
+ ruby: ["3.4", "3.5"]
25
+ continue-on-error: [false]
26
+
27
+ services:
28
+ redis:
29
+ image: valkey/valkey:8.1-bookworm
30
+ # Set health checks to wait until database server has started
31
+ options: >-
32
+ --health-cmd "redis-cli ping"
33
+ --health-interval 10s
34
+ --health-timeout 3s
35
+ --health-retries 5
36
+ ports:
37
+ # https://docs.github.com/en/actions/using-containerized-services/creating-redis-service-containers#running-jobs-in-containers
38
+ # Maps port 6379 on service container to the host
39
+ - 6379:6379
40
+
41
+ steps:
42
+ - uses: actions/checkout@v4
43
+ - name: Set up Ruby
44
+ uses: ruby/setup-ruby@v1
45
+ with:
46
+ ruby-version: ${{ matrix.ruby }}
47
+ # When the following is true, also run "bundle install",
48
+ # and cache the result automatically. Ran into an issue
49
+ # with the caching and multiple ruby versions. Needs
50
+ # further investigation.
51
+ bundler-cache: true
52
+
53
+ - name: Setup tmate session
54
+ uses: mxschmitt/action-tmate@7b6a61a73bbb9793cb80ad69b8dd8ac19261834c # v3
55
+ if: ${{ github.event_name == 'workflow_dispatch' && inputs.debug_enabled }}
56
+ with:
57
+ detached: true
58
+
59
+ - name: Configure Bundler for secure gem installation
60
+ run: |
61
+ bundle config set --local path 'vendor/bundle'
62
+ bundle config set --local deployment 'false'
63
+
64
+ - name: Re-run bundle install
65
+ run: bundle install
66
+
67
+ - name: Run the tryouts
68
+ run: bundle exec try -vf
@@ -0,0 +1,64 @@
1
+ name: Build and Publish YARD Documentation
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ workflow_dispatch:
8
+ inputs:
9
+ reason:
10
+ description: 'Reason for manual documentation build'
11
+ required: false
12
+ default: 'Manual documentation update'
13
+
14
+ permissions:
15
+ contents: read
16
+ pages: write
17
+ id-token: write
18
+
19
+ concurrency:
20
+ group: "pages"
21
+ cancel-in-progress: false
22
+
23
+ jobs:
24
+ build_docs:
25
+ runs-on: ubuntu-24.04
26
+
27
+ steps:
28
+ - name: Checkout code
29
+ uses: actions/checkout@v4
30
+ with:
31
+ fetch-depth: 0
32
+
33
+ - name: Set up Ruby
34
+ uses: ruby/setup-ruby@v1
35
+ with:
36
+ ruby-version: '3.4'
37
+ bundler-cache: true
38
+
39
+ - name: Install dependencies
40
+ run: bundle install
41
+
42
+ - name: Build YARD documentation
43
+ run: |
44
+ bundle exec yard doc --output-dir ./doc --readme README.md
45
+ # Ensure doc directory exists and create .nojekyll file to prevent GitHub Pages Jekyll processing
46
+ mkdir -p ./doc
47
+ touch ./doc/.nojekyll
48
+
49
+ - name: Upload documentation artifacts
50
+ uses: actions/upload-pages-artifact@v3
51
+ with:
52
+ path: ./doc
53
+
54
+ deploy:
55
+ environment:
56
+ name: github-pages
57
+ url: ${{ steps.deployment.outputs.page_url }}
58
+ runs-on: ubuntu-24.04
59
+ needs: build_docs
60
+
61
+ steps:
62
+ - name: Deploy to GitHub Pages
63
+ id: deployment
64
+ uses: actions/deploy-pages@v4
data/.gitignore CHANGED
@@ -9,6 +9,7 @@
9
9
  *.log
10
10
  *.md
11
11
  !README.md
12
+ !CLAUDE.md
12
13
  *.txt
13
14
  !LICENSE.txt
14
15
  .ruby-version
@@ -18,3 +19,6 @@ log
18
19
  tmp
19
20
  vendor
20
21
  *.gem
22
+ doc/
23
+ .yardoc
24
+ !docs/connection_pooling.md
@@ -33,7 +33,9 @@ default_install_hook_types:
33
33
  - pre-commit
34
34
  - prepare-commit-msg
35
35
 
36
- fail_fast: true
36
+ # Allow multiple failures so they can be corrected at the same time
37
+ # instead of after sequential commits.
38
+ fail_fast: false
37
39
 
38
40
  repos:
39
41
  - repo: meta
data/.rubocop.yml CHANGED
@@ -1,3 +1,4 @@
1
+ # .rubocop.yml
1
2
 
2
3
  ##
3
4
  # This is the RuboCop configuration file.
@@ -12,9 +13,12 @@
12
13
  # as those documented at the top of the todo file). This is
13
14
  # useful for a gradual migration of the codebase.
14
15
  #
16
+ # How to resolve "RuboCop version incompatibility found":
17
+ # `rubocop --stop-server`
18
+ #
15
19
  inherit_from: .rubocop_todo.yml
16
20
 
17
- require:
21
+ plugins:
18
22
  - rubocop-performance
19
23
  - rubocop-thread_safety
20
24
 
@@ -22,13 +26,13 @@ AllCops:
22
26
  NewCops: enable
23
27
  UseCache: true
24
28
  MaxFilesInCache: 100
25
- TargetRubyVersion: 3.0
29
+ TargetRubyVersion: 3.4
26
30
  Exclude:
27
- - 'migrate/**/*.rb'
28
- - 'migrate/*.rb'
29
- - 'try/**/*'
30
- - 'try/*.rb'
31
- - 'vendor/**/*'
31
+ - "migrate/**/*.rb"
32
+ - "migrate/*.rb"
33
+ - "try/**/*"
34
+ - "try/*.rb"
35
+ - "vendor/**/*"
32
36
 
33
37
  Gemspec/DeprecatedAttributeAssignment:
34
38
  Enabled: true
@@ -56,12 +60,12 @@ Metrics/CyclomaticComplexity:
56
60
  Metrics/MethodLength:
57
61
  Enabled: true
58
62
  Max: 40
59
- CountAsOne: ['method_call']
63
+ CountAsOne: ["method_call"]
60
64
 
61
65
  Metrics/ModuleLength:
62
66
  Enabled: true
63
67
  Max: 250
64
- CountAsOne: ['method_call']
68
+ CountAsOne: ["method_call"]
65
69
 
66
70
  Performance/Size:
67
71
  Enabled: true
@@ -73,3 +77,6 @@ Style/NegatedIfElseCondition:
73
77
 
74
78
  Naming/AsciiIdentifiers:
75
79
  Enabled: false
80
+
81
+ Style/FrozenStringLiteralComment:
82
+ Enabled: false
data/.rubocop_todo.yml CHANGED
@@ -1,63 +1,209 @@
1
1
  # This configuration was generated by
2
2
  # `rubocop --auto-gen-config`
3
- # on 2024-08-12 20:49:29 UTC using RuboCop version 1.65.0.
3
+ # on 2025-07-20 01:49:27 UTC using RuboCop version 1.78.0.
4
4
  # The point is for the user to remove these configuration records
5
5
  # one by one as the offenses are removed from the code base.
6
6
  # Note that changes in the inspected code, or installation of new
7
7
  # versions of RuboCop, may require this file to be generated again.
8
8
 
9
+ # Offense count: 2
10
+ # This cop supports safe autocorrection (--autocorrect).
11
+ # Configuration parameters: AutoCorrect, AllowBorderComment, AllowMarginComment.
12
+ Layout/EmptyComment:
13
+ Exclude:
14
+ - "lib/familia/base.rb"
15
+ - "lib/familia/connection.rb"
16
+
17
+ # Offense count: 1
18
+ # This cop supports safe autocorrection (--autocorrect).
19
+ Layout/EmptyLines:
20
+ Exclude:
21
+ - "Gemfile"
22
+
23
+ # Offense count: 2
24
+ # This cop supports safe autocorrection (--autocorrect).
25
+ # Configuration parameters: EnforcedStyle.
26
+ # SupportedStyles: empty_lines, empty_lines_except_namespace, empty_lines_special, no_empty_lines, beginning_only, ending_only
27
+ Layout/EmptyLinesAroundClassBody:
28
+ Exclude:
29
+ - "lib/familia/datatype/types/list.rb"
30
+ - "lib/familia/datatype/types/unsorted_set.rb"
31
+
9
32
  # Offense count: 1
10
- # Configuration parameters: EnforcedStyle, AllowedGems, Include.
11
- # SupportedStyles: Gemfile, gems.rb, gemspec
12
- # Include: **/*.gemspec, **/Gemfile, **/gems.rb
13
- Gemspec/DevelopmentDependencies:
33
+ # This cop supports safe autocorrection (--autocorrect).
34
+ Layout/SpaceAfterComma:
35
+ Exclude:
36
+ - "lib/familia/datatype/types/hashkey.rb"
37
+
38
+ # Offense count: 4
39
+ # This cop supports safe autocorrection (--autocorrect).
40
+ # Configuration parameters: AutoCorrect, AllowUnusedKeywordArguments, IgnoreEmptyMethods, IgnoreNotImplementedMethods, NotImplementedExceptions.
41
+ # NotImplementedExceptions: NotImplementedError
42
+ Lint/UnusedMethodArgument:
14
43
  Exclude:
15
- - 'familia.gemspec'
44
+ - "lib/familia/base.rb"
45
+ - "lib/middleware/database_middleware.rb"
16
46
 
17
47
  # Offense count: 1
18
- # Configuration parameters: Severity, Include.
19
- # Include: **/*.gemspec
20
- Gemspec/RequiredRubyVersion:
48
+ # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
49
+ # AllowedMethods: refine
50
+ Metrics/BlockLength:
51
+ Max: 28
52
+
53
+ # Offense count: 2
54
+ # Configuration parameters: AllowedMethods, AllowedPatterns.
55
+ Metrics/PerceivedComplexity:
56
+ Max: 9
57
+
58
+ # Offense count: 50
59
+ # This cop supports safe autocorrection (--autocorrect).
60
+ # Configuration parameters: EnforcedStyle, BlockForwardingName.
61
+ # SupportedStyles: anonymous, explicit
62
+ Naming/BlockForwarding:
21
63
  Exclude:
22
- - 'familia.gemspec'
64
+ - "lib/familia/datatype/types/list.rb"
65
+ - "lib/familia/datatype/types/sorted_set.rb"
66
+ - "lib/familia/datatype/types/unsorted_set.rb"
67
+ - "lib/familia/refinements.rb"
23
68
 
24
- # Offense count: 5
69
+ # Offense count: 8
25
70
  # Configuration parameters: MinNameLength, AllowNamesEndingInNumbers, AllowedNames, ForbiddenNames.
26
71
  # AllowedNames: as, at, by, cc, db, id, if, in, io, ip, of, on, os, pp, to
27
72
  Naming/MethodParameterName:
28
73
  Exclude:
29
- - 'lib/familia/core_ext.rb'
30
- - 'lib/familia/types/list.rb'
31
- - 'lib/familia/types/sorted_set.rb'
32
- - 'lib/familia/types/unsorted_set.rb'
74
+ - "lib/familia/core_ext.rb"
75
+ - "lib/familia/horreum/class_methods.rb"
76
+ - "lib/familia/datatype/types/sorted_set.rb"
77
+ - "lib/familia/datatype/types/unsorted_set.rb"
78
+
79
+ # Offense count: 1
80
+ # Configuration parameters: Mode, AllowedMethods, AllowedPatterns, AllowBangMethods, WaywardPredicates.
81
+ # AllowedMethods: call
82
+ # WaywardPredicates: nonzero?
83
+ Naming/PredicateMethod:
84
+ Exclude:
85
+ - "lib/familia/horreum/class_methods.rb"
33
86
 
34
87
  # Offense count: 1
35
- # Configuration parameters: NamePrefix, ForbiddenPrefixes, AllowedMethods, MethodDefinitionMacros.
36
- # NamePrefix: is_, has_, have_
37
- # ForbiddenPrefixes: is_, has_, have_
88
+ # Configuration parameters: NamePrefix, ForbiddenPrefixes, AllowedMethods, MethodDefinitionMacros, UseSorbetSigs.
89
+ # NamePrefix: is_, has_, have_, does_
90
+ # ForbiddenPrefixes: is_, has_, have_, does_
38
91
  # AllowedMethods: is_a?
39
92
  # MethodDefinitionMacros: define_method, define_singleton_method
40
- Naming/PredicateName:
93
+ Naming/PredicatePrefix:
41
94
  Exclude:
42
- - 'spec/**/*'
43
- - 'lib/familia/types/hashkey.rb'
95
+ - "spec/**/*"
96
+ - "lib/familia/horreum/class_methods.rb"
44
97
 
45
- # Offense count: 6
98
+ # Offense count: 54
99
+ # This cop supports safe autocorrection (--autocorrect).
100
+ # Configuration parameters: AllowOnlyRestArgument, UseAnonymousForwarding, RedundantRestArgumentNames, RedundantKeywordRestArgumentNames, RedundantBlockArgumentNames.
101
+ # RedundantRestArgumentNames: args, arguments
102
+ # RedundantKeywordRestArgumentNames: kwargs, options, opts
103
+ # RedundantBlockArgumentNames: blk, block, proc
104
+ Style/ArgumentsForwarding:
105
+ Exclude:
106
+ - "lib/familia/horreum/class_methods.rb"
107
+ - "lib/familia/datatype/types/list.rb"
108
+ - "lib/familia/datatype/types/sorted_set.rb"
109
+ - "lib/familia/datatype/types/unsorted_set.rb"
110
+ - "lib/familia/refinements.rb"
111
+
112
+ # Offense count: 11
46
113
  # Configuration parameters: AllowedConstants.
47
114
  Style/Documentation:
48
115
  Exclude:
49
- - 'spec/**/*'
50
- - 'test/**/*'
51
- - 'lib/familia/errors.rb'
52
- - 'lib/familia/types/hashkey.rb'
53
- - 'lib/familia/types/list.rb'
54
- - 'lib/familia/types/sorted_set.rb'
55
- - 'lib/familia/types/string.rb'
56
- - 'lib/familia/types/unsorted_set.rb'
116
+ - "spec/**/*"
117
+ - "test/**/*"
118
+ - "lib/familia/errors.rb"
119
+ - "lib/familia/horreum/class_methods.rb"
120
+ - "lib/familia/horreum/related_fields_management.rb"
121
+ - "lib/familia/datatype/types/hashkey.rb"
122
+ - "lib/familia/datatype/types/list.rb"
123
+ - "lib/familia/datatype/types/sorted_set.rb"
124
+ - "lib/familia/datatype/types/string.rb"
125
+ - "lib/familia/datatype/types/unsorted_set.rb"
126
+ - "lib/familia/version.rb"
127
+
128
+ # Offense count: 1
129
+ # This cop supports safe autocorrection (--autocorrect).
130
+ # Configuration parameters: AutoCorrect, EnforcedStyle, AllowComments.
131
+ # SupportedStyles: empty, nil, both
57
132
 
58
133
  # Offense count: 2
59
134
  # This cop supports safe autocorrection (--autocorrect).
60
- # Configuration parameters: MaxUnannotatedPlaceholdersAllowed, AllowedMethods, AllowedPatterns.
135
+ # Configuration parameters: MaxUnannotatedPlaceholdersAllowed, Mode, AllowedMethods, AllowedPatterns.
61
136
  # SupportedStyles: annotated, template, unannotated
62
137
  Style/FormatStringToken:
63
138
  EnforcedStyle: unannotated
139
+
140
+ # Offense count: 1
141
+ # This cop supports unsafe autocorrection (--autocorrect-all).
142
+ # Configuration parameters: EnforcedStyle.
143
+ # SupportedStyles: always, always_true, never
144
+ Style/FrozenStringLiteralComment:
145
+ Exclude:
146
+ - "**/*.arb"
147
+ - "lib/familia/horreum/related_fields_management.rb"
148
+
149
+ # Offense count: 1
150
+ # This cop supports unsafe autocorrection (--autocorrect-all).
151
+ Style/HashTransformValues:
152
+ Exclude:
153
+ - "lib/familia/datatype/types/hashkey.rb"
154
+
155
+ # Offense count: 1
156
+ # This cop supports unsafe autocorrection (--autocorrect-all).
157
+ # Configuration parameters: EnforcedStyle, Autocorrect.
158
+ # SupportedStyles: module_function, extend_self, forbidden
159
+
160
+ # Offense count: 1
161
+ # This cop supports unsafe autocorrection (--autocorrect-all).
162
+ # Configuration parameters: EnforcedStyle, AllowedMethods, AllowedPatterns.
163
+ # SupportedStyles: predicate, comparison
164
+ Style/NumericPredicate:
165
+ Exclude:
166
+ - "spec/**/*"
167
+ - "lib/familia/horreum/class_methods.rb"
168
+
169
+ # Offense count: 1
170
+ # This cop supports unsafe autocorrection (--autocorrect-all).
171
+
172
+ # Offense count: 1
173
+ # This cop supports safe autocorrection (--autocorrect).
174
+ # Configuration parameters: EnforcedStyle.
175
+ # SupportedStyles: implicit, explicit
176
+ Style/RescueStandardError:
177
+ Exclude:
178
+ - "lib/familia/horreum.rb"
179
+
180
+ # Offense count: 1
181
+ # This cop supports unsafe autocorrection (--autocorrect-all).
182
+ # Configuration parameters: ConvertCodeThatCanStartToReturnNil, AllowedMethods, MaxChainLength.
183
+ # AllowedMethods: present?, blank?, presence, try, try!
184
+ Style/SafeNavigation:
185
+ Exclude:
186
+ - "lib/familia/connection.rb"
187
+
188
+ # Offense count: 4
189
+ # Configuration parameters: ActiveSupportClassAttributeAllowed.
190
+ ThreadSafety/ClassAndModuleAttributes:
191
+ Exclude:
192
+ - "lib/familia/base.rb"
193
+ - "lib/familia/horreum.rb"
194
+ - "lib/middleware/database_middleware.rb"
195
+
196
+ # Offense count: 29
197
+ ThreadSafety/ClassInstanceVariable:
198
+ Exclude:
199
+ - "lib/familia/base.rb"
200
+ - "lib/familia/horreum/class_methods.rb"
201
+ - "lib/familia/version.rb"
202
+
203
+ # Offense count: 2
204
+ # This cop supports unsafe autocorrection (--autocorrect-all).
205
+ # Configuration parameters: EnforcedStyle.
206
+ # SupportedStyles: literals, strict
207
+ ThreadSafety/MutableClassInstanceVariable:
208
+ Exclude:
209
+ - "lib/familia/connection.rb"
data/.yardopts ADDED
@@ -0,0 +1,9 @@
1
+ --readme README.md
2
+ --title "Familia Ruby Library Documentation"
3
+ --markup markdown
4
+ --markup-provider kramdown
5
+ --protected
6
+ --no-private
7
+ lib/**/*.rb
8
+ -
9
+ - LICENSE.txt
data/CLAUDE.md ADDED
@@ -0,0 +1,141 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Development Commands
6
+
7
+ ### Testing
8
+ - **Run tests**: `bundle exec tryouts` (uses tryouts testing framework)
9
+ - **Run specific test file**: `bundle exec tryouts try/specific_test_try.rb`
10
+ - **Debug mode**: `FAMILIA_DEBUG=1 bundle exec tryouts`
11
+ - **Trace mode**: `FAMILIA_TRACE=1 bundle exec tryouts` (detailed Redis operation logging)
12
+
13
+ ### Development Setup
14
+ - **Install dependencies**: `bundle install`
15
+ - **Generate documentation**: `bundle exec yard`
16
+ - **Code linting**: `bundle exec rubocop`
17
+
18
+ ### Known Issues & Quirks
19
+ - **Reserved Keywords**: Cannot use `ttl`, `db`, `redis` as field names - use prefixed alternatives
20
+ - **Empty Identifiers**: Cause stack overflow in key generation - validate before operations
21
+ - **Connection Pool Race Conditions**: Thread safety issues under high concurrency
22
+ - **Manual Key Sync**: `key` field doesn't auto-sync with identifier changes
23
+ - **RedisType Redis Parameter**: `:redis` parameter silently ignored (missing setter)
24
+
25
+ ### Debugging
26
+ - **Database command logging**: `tail plop.log` - Real-time Database command monitoring
27
+ - Shows all Database operations with timestamps, database numbers, and full commands
28
+ - Updates live as tests run or code executes
29
+ - Essential for debugging Familia ORM Database interactions
30
+
31
+ ### Testing Framework
32
+ This project uses `tryouts` instead of RSpec/Minitest. Test files are located in the `try/` directory and follow the pattern `*_try.rb`.
33
+
34
+ ## Architecture Overview
35
+
36
+ ### Core Components
37
+
38
+ **Familia**: A Valkey-compatible ORM library that provides Ruby object storage with advanced features like expiration, safe dumping, and quantization.
39
+
40
+ #### Primary Classes
41
+ 1. **`Familia::Horreum`** - Base class for Valkey-backed objects (like ActiveRecord models)
42
+ - Located in `lib/familia/horreum.rb`
43
+ - Provides field definitions, data type relationships, and object lifecycle management
44
+ - Supports multiple identifier strategies: symbols, procs, arrays
45
+
46
+ 2. **`Familia::DataType`** - Base class for Valkey data type wrappers
47
+ - Located in `lib/familia/datatype.rb`
48
+ - Provides String, List, Set, SortedSet, HashKey implementations
49
+ - Each type has its own class in `lib/familia/datatype/types/`
50
+
51
+ 3. **`Familia::Base`** - Common module for both Horreum and DataType
52
+ - Located in `lib/familia/base.rb`
53
+ - Provides shared functionality and feature system
54
+
55
+ #### Feature System
56
+ Familia uses a modular feature system where features are mixed into classes:
57
+ - **Expiration** (`lib/familia/features/expiration.rb`) - TTL management with cascading
58
+ - **SafeDump** (`lib/familia/features/safe_dump.rb`) - API-safe object serialization
59
+ - **Quantization** (`lib/familia/features/quantization.rb`) - Time-based data bucketing
60
+
61
+ #### Key Architectural Patterns
62
+
63
+ **Inheritance Chain**: `MyClass < Familia::Horreum` automatically extends `ClassMethods` and `Features`
64
+
65
+ **DataType Definition**: Use class methods to define keystore database-backed attributes:
66
+ ```ruby
67
+ class User < Familia::Horreum
68
+ field :email # Simple field
69
+ list :sessions # Valkey/Redis list
70
+ set :tags # Valkey/Redis set
71
+ zset :metrics # Valkey/Redis sorted set
72
+ hashkey :settings # Valkey/Redis hash
73
+ end
74
+ ```
75
+
76
+ **Identifier Resolution**: Multiple strategies for object identification:
77
+ - Symbol: `identifier :email`
78
+ - Proc: `identifier ->(user) { "user:#{user.email}" }`
79
+ - Array: `identifier [:type, :email]`
80
+
81
+ ### Directory Structure
82
+
83
+ - `lib/familia.rb` - Main entry point and module definition
84
+ - `lib/familia/horreum/` - Horreum class implementation (class_methods, commands, serialization, etc.)
85
+ - `lib/familia/datatype/` - Valkey/Redis type implementations and commands
86
+ - `lib/familia/features/` - Modular feature implementations
87
+ - `try/` - Test files using tryouts framework
88
+ - `try/test_helpers.rb` - Shared test utilities and sample classes
89
+
90
+ ### Database Connection Management
91
+ - Connection handling in `lib/familia/connection.rb`
92
+ - Settings management in `lib/familia/settings.rb`
93
+ - Database selection via `logical_database` class method
94
+ - URI-based configuration support
95
+
96
+ ### Important Implementation Notes
97
+
98
+ **Field Initialization**: Objects can be initialized with positional args (brittle) or keyword args (robust). Keyword args are recommended.
99
+
100
+ **Serialization**: Uses JSON by default but supports custom `serialize_value`/`deserialize_value` methods.
101
+
102
+ **Database Key Generation**: Automatic key generation using class name, identifier, and field/type names (aka dbkey). Pattern: `classname:identifier:fieldname`
103
+
104
+ **Memory Efficiency**: Only non-nil values are stored in keystore database to optimize memory usage.
105
+
106
+ **Thread Safety**: Data types are frozen after instantiation to ensure immutability.
107
+
108
+ ## Common Patterns
109
+
110
+ ### Defining a Horreum Class
111
+ ```ruby
112
+ class Customer < Familia::Horreum
113
+ feature :safe_dump
114
+ feature :expiration
115
+
116
+ identifier_field :custid
117
+ default_expiration 5.years
118
+
119
+ field :custid
120
+ field :email
121
+ list :sessions
122
+ hashkey :settings
123
+ end
124
+ ```
125
+
126
+ ### Using Features
127
+ ```ruby
128
+ # Safe dump for API responses
129
+ customer.safe_dump # Returns only whitelisted fields
130
+
131
+ # Expiration management
132
+ customer.update_expiration(default_expiration: 1.hour)
133
+ ```
134
+
135
+ ### Transaction Support
136
+ ```ruby
137
+ customer.transaction do |conn|
138
+ conn.set("key1", "value1")
139
+ conn.zadd("key2", score, member)
140
+ end
141
+ ```
data/Gemfile CHANGED
@@ -1,15 +1,28 @@
1
- # frozen_string_literal: true
1
+ # Gemfile
2
2
 
3
3
  source 'https://rubygems.org'
4
4
 
5
5
  gemspec
6
6
 
7
+ group :test do
8
+ if ENV['LOCAL_DEV']
9
+ gem 'tryouts', path: '../../d/tryouts'
10
+ else
11
+ gem 'tryouts', '~> 3.1.1', require: false
12
+ end
13
+ gem 'concurrent-ruby', '~> 1.3.5', require: false
14
+ gem 'ruby-prof'
15
+ gem 'stackprof'
16
+ end
17
+
18
+
7
19
  group :development, :test do
8
20
  # byebug only works with MRI
9
21
  gem 'byebug', '~> 11.0', require: false if RUBY_ENGINE == 'ruby'
22
+ gem 'kramdown', require: false # Required for YARD markdown processing
10
23
  gem 'pry-byebug', '~> 3.10.1', require: false if RUBY_ENGINE == 'ruby'
11
24
  gem 'rubocop', require: false
12
25
  gem 'rubocop-performance', require: false
13
26
  gem 'rubocop-thread_safety', require: false
14
- gem 'tryouts', '~> 2.4', require: false
27
+ gem 'yard', '~> 0.9', require: false
15
28
  end