mudis 0.4.1 → 0.4.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2dc10c0b461aac92e8c559f3e3a97b7654d6c3480c8ba8755fbe10f083945183
4
- data.tar.gz: 90292495545e270316d273f525d306c77ca1351e47ac99207d2739352b472f6e
3
+ metadata.gz: 6a4a27b63d070e4a8e356d6ffed09eda290c7f633ffb3bf7b9008499170b4a55
4
+ data.tar.gz: 1ccaf9415f6e595272884c926615861975f376d5a52c7109a845dcb4b4480e8d
5
5
  SHA512:
6
- metadata.gz: 72fcae767bb8d79492b66b76854c65bf30b776caa1a4dd7c1342da6972fe68ca65a54ec56c796a799503681576cf40827d8d85916ea674a9d48c25debc314409
7
- data.tar.gz: 2b2886cbdfebb83d63c5093812d898383d33f6b9a6eea60bf3a119c3b72b345d516d7416281cfd18146a2665e4c151bc301dbc7999eca82aea4e8aea495b0759
6
+ metadata.gz: 2fbcea123de9db1aefcb31e8aea21069bbbdae11300b3a6dd2b73b65c801c6b9dbdc5f60d5d84e001e26059a2e6a2e1e1ef01b61e23261f143cbf0ae3c129e40
7
+ data.tar.gz: 3344818c66b36d4fbb9c5e5e287a3067ee7a8c6779b2803084b8b7d4001337e073ccd45406665f2e50bc27ef58ac0e4bc9a17168f85fb302e2b0331cbc11ba2c
data/README.md CHANGED
@@ -21,7 +21,7 @@ There are plenty out there, in various states of maintenance and in many shapes
21
21
 
22
22
  #### Feature / Function Comparison
23
23
 
24
- | **Feature** | **Mudis v0.3.0** | **MemoryStore** (`Rails.cache`) | **FastCache** | **Zache** | **EasyCache** | **MiniCache** |
24
+ | **Feature** | **Mudis** | **MemoryStore** (`Rails.cache`) | **FastCache** | **Zache** | **EasyCache** | **MiniCache** |
25
25
  | -------------------------------------- | ---------------- | ------------------------------- | -------------- | ------------- | ------------- | -------------- |
26
26
  | **LRU eviction strategy** | ✅ Per-bucket | ✅ Global | ✅ Global | ❌ | ❌ | ✅ Simplistic |
27
27
  | **TTL expiry support** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
@@ -309,8 +309,9 @@ end
309
309
  | `start_expiry_thread` | Background TTL cleanup loop (every N sec) | Disabled by default|
310
310
  | `hard_memory_limit` | Enforce hard memory limits on key size and reject if exceeded | `false`|
311
311
  | `max_bytes` | Maximum allowed cache size | `1GB`|
312
+ | `buckets` | Number of memory buckets (shards) | `32` |
312
313
 
313
- To customize the number of buckets, set the `MUDIS_BUCKETS` environment variable.
314
+ Buckets can also be set using a `MUDIS_BUCKETS` environment variable.
314
315
 
315
316
  When setting `serializer`, be mindful of the below
316
317
 
data/lib/mudis/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- MUDIS_VERSION = "0.4.1"
3
+ MUDIS_VERSION = "0.4.3"
data/lib/mudis.rb CHANGED
@@ -1,4 +1,4 @@
1
- # lib/mudis.rb
1
+ # frozen_string_literal: true
2
2
 
3
3
  require "json"
4
4
  require "thread" # rubocop:disable Lint/RedundantRequireStatement
@@ -19,8 +19,8 @@ class Mudis # rubocop:disable Metrics/ClassLength
19
19
  @stop_expiry = false # Signal for stopping expiry thread
20
20
 
21
21
  class << self
22
- attr_accessor :serializer, :compress, :max_value_bytes, :hard_memory_limit
23
- attr_reader :max_bytes
22
+ attr_accessor :serializer, :compress, :hard_memory_limit
23
+ attr_reader :max_bytes, :max_value_bytes
24
24
 
25
25
  # Configures Mudis with a block, allowing customization of settings
26
26
  def configure
@@ -35,6 +35,8 @@ class Mudis # rubocop:disable Metrics/ClassLength
35
35
 
36
36
  # Applies the current configuration to Mudis
37
37
  def apply_config!
38
+ validate_config!
39
+
38
40
  self.serializer = config.serializer
39
41
  self.compress = config.compress
40
42
  self.max_value_bytes = config.max_value_bytes
@@ -42,6 +44,18 @@ class Mudis # rubocop:disable Metrics/ClassLength
42
44
  self.max_bytes = config.max_bytes
43
45
  end
44
46
 
47
+ # Validates the current configuration, raising errors for invalid settings
48
+ def validate_config! # rubocop:disable Metrics/AbcSize
49
+ if config.max_value_bytes && config.max_value_bytes > config.max_bytes
50
+ raise ArgumentError,
51
+ "max_value_bytes cannot exceed max_bytes"
52
+ end
53
+
54
+ raise ArgumentError, "max_value_bytes must be > 0" if config.max_value_bytes && config.max_value_bytes <= 0
55
+
56
+ raise ArgumentError, "buckets must be > 0" if config.buckets && config.buckets <= 0
57
+ end
58
+
45
59
  # Returns a snapshot of metrics (thread-safe)
46
60
  def metrics # rubocop:disable Metrics/MethodLength
47
61
  @metrics_mutex.synchronize do
@@ -89,9 +103,18 @@ class Mudis # rubocop:disable Metrics/ClassLength
89
103
 
90
104
  # Sets the maximum size for a single value in bytes
91
105
  def max_bytes=(value)
106
+ raise ArgumentError, "max_bytes must be > 0" if value.to_i <= 0
107
+
92
108
  @max_bytes = value
93
109
  @threshold_bytes = (@max_bytes * 0.9).to_i
94
110
  end
111
+
112
+ # Sets the maximum size for a single value in bytes, raising an error if invalid
113
+ def max_value_bytes=(value)
114
+ raise ArgumentError, "max_value_bytes must be > 0" if value && value.to_i <= 0
115
+
116
+ @max_value_bytes = value
117
+ end
95
118
  end
96
119
 
97
120
  # Node structure for the LRU doubly-linked list
@@ -107,7 +130,12 @@ class Mudis # rubocop:disable Metrics/ClassLength
107
130
 
108
131
  # Number of cache buckets (shards). Default: 32
109
132
  def self.buckets
110
- @buckets ||= (ENV["MUDIS_BUCKETS"]&.to_i || 32) # rubocop:disable Style/RedundantParentheses
133
+ return @buckets if @buckets
134
+
135
+ val = config.buckets || ENV["MUDIS_BUCKETS"]&.to_i || 32
136
+ raise ArgumentError, "bucket count must be > 0" if val <= 0
137
+
138
+ @buckets = val
111
139
  end
112
140
 
113
141
  # --- Internal Structures ---
data/lib/mudis_config.rb CHANGED
@@ -7,7 +7,8 @@ class MudisConfig
7
7
  :compress,
8
8
  :max_value_bytes,
9
9
  :hard_memory_limit,
10
- :max_bytes
10
+ :max_bytes,
11
+ :buckets
11
12
 
12
13
  def initialize
13
14
  @serializer = JSON # Default serialization strategy
@@ -15,5 +16,6 @@ class MudisConfig
15
16
  @max_value_bytes = nil # Max size per value (optional)
16
17
  @hard_memory_limit = false # Enforce max_bytes as hard cap
17
18
  @max_bytes = 1_073_741_824 # 1 GB default max cache size
19
+ @buckets = nil # use nil to signal fallback to ENV or default
18
20
  end
19
21
  end
data/sig/mudis.rbs CHANGED
@@ -1,11 +1,17 @@
1
- # sig/mudis.rbs
2
-
3
1
  class Mudis
4
2
  # Configuration
5
3
  class << self
6
4
  attr_accessor serializer : Object
7
5
  attr_accessor compress : bool
8
- attr_accessor max_value_bytes : Integer?
6
+ attr_accessor hard_memory_limit : bool
7
+ attr_reader max_bytes : Integer
8
+ attr_reader max_value_bytes : Integer?
9
+
10
+ def configure: () { (config: MudisConfig) -> void } -> void
11
+ def config: () -> MudisConfig
12
+ def apply_config!: () -> void
13
+
14
+ def buckets: () -> Integer
9
15
  end
10
16
 
11
17
  # Lifecycle
@@ -13,27 +19,32 @@ class Mudis
13
19
  def self.stop_expiry_thread: () -> void
14
20
 
15
21
  # Core operations
16
- def self.write: (String, untyped, ?expires_in: Integer) -> void
17
- def self.read: (String) -> untyped?
18
- def self.update: (String) { (untyped) -> untyped } -> void
19
- def self.delete: (String) -> void
20
- def self.exists?: (String) -> bool
22
+ def self.write: (String, untyped, ?expires_in: Integer, ?namespace: String) -> void
23
+ def self.read: (String, ?namespace: String) -> untyped?
24
+ def self.update: (String, ?namespace: String) { (untyped) -> untyped } -> void
25
+ def self.delete: (String, ?namespace: String) -> void
26
+ def self.exists?: (String, ?namespace: String) -> bool
21
27
 
22
28
  # DSL & Helpers
23
29
  def self.fetch: (
24
30
  String,
25
31
  ?expires_in: Integer,
26
- ?force: bool
32
+ ?force: bool,
33
+ ?namespace: String
27
34
  ) { () -> untyped } -> untyped
28
35
 
29
- def self.clear: (String) -> void
30
- def self.replace: (String, untyped, ?expires_in: Integer) -> void
31
- def self.inspect: (String) -> Hash[Symbol, untyped]?
36
+ def self.clear: (String, ?namespace: String) -> void
37
+ def self.replace: (String, untyped, ?expires_in: Integer, ?namespace: String) -> void
38
+ def self.inspect: (String, ?namespace: String) -> Hash[Symbol, untyped]?
32
39
 
33
40
  # Introspection & management
34
- def self.metrics: () -> Hash[Symbol, Integer]
41
+ def self.metrics: () -> Hash[Symbol, untyped]
35
42
  def self.cleanup_expired!: () -> void
36
43
  def self.all_keys: () -> Array[String]
37
44
  def self.current_memory_bytes: () -> Integer
38
45
  def self.max_memory_bytes: () -> Integer
46
+
47
+ # State reset
48
+ def self.reset!: () -> void
49
+ def self.reset_metrics!: () -> void
39
50
  end
@@ -0,0 +1,8 @@
1
+ class MudisConfig
2
+ attr_accessor serializer : Object
3
+ attr_accessor compress : bool
4
+ attr_accessor max_value_bytes : Integer?
5
+ attr_accessor hard_memory_limit : bool
6
+ attr_accessor max_bytes : Integer
7
+ attr_accessor buckets : Integer?
8
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+ require "climate_control"
5
+
6
+ RSpec.describe "Mudis Configuration Guardrails" do # rubocop:disable Metrics/BlockLength
7
+ after { Mudis.reset! }
8
+
9
+ describe "bucket configuration" do
10
+ it "defaults to 32 buckets if ENV is nil" do
11
+ Mudis.instance_variable_set(:@buckets, nil) # force recomputation
12
+ ClimateControl.modify(MUDIS_BUCKETS: nil) do
13
+ expect(Mudis.send(:buckets)).to eq(32)
14
+ end
15
+ end
16
+
17
+ it "raises if MUDIS_BUCKETS is 0 or less" do
18
+ expect do
19
+ Mudis.instance_variable_set(:@buckets, nil) # force recomputation
20
+ ClimateControl.modify(MUDIS_BUCKETS: "0") { Mudis.send(:buckets) }
21
+ end.to raise_error(ArgumentError, /bucket count must be > 0/)
22
+
23
+ expect do
24
+ Mudis.instance_variable_set(:@buckets, nil) # force recomputation
25
+ ClimateControl.modify(MUDIS_BUCKETS: "-5") { Mudis.send(:buckets) }
26
+ end.to raise_error(ArgumentError, /bucket count must be > 0/)
27
+ end
28
+ end
29
+
30
+ describe "memory configuration" do
31
+ it "raises if max_bytes is set to 0 or less" do
32
+ expect do
33
+ Mudis.max_bytes = 0
34
+ end.to raise_error(ArgumentError, /max_bytes must be > 0/)
35
+
36
+ expect do
37
+ Mudis.max_bytes = -1
38
+ end.to raise_error(ArgumentError, /max_bytes must be > 0/)
39
+ end
40
+
41
+ it "raises if max_value_bytes is 0 or less via config" do
42
+ expect do
43
+ Mudis.configure do |c|
44
+ c.max_value_bytes = 0
45
+ end
46
+ end.to raise_error(ArgumentError, /max_value_bytes must be > 0/)
47
+ end
48
+
49
+ it "raises if max_value_bytes exceeds max_bytes" do
50
+ expect do
51
+ Mudis.configure do |c|
52
+ c.max_bytes = 1_000_000
53
+ c.max_value_bytes = 2_000_000
54
+ end
55
+ end.to raise_error(ArgumentError, /max_value_bytes cannot exceed max_bytes/)
56
+ end
57
+ end
58
+ end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mudis
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.1
4
+ version: 0.4.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - kiebor81
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-07-16 00:00:00.000000000 Z
11
+ date: 2025-07-17 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: climate_control
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 1.1.0
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 1.1.0
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: rspec
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -31,12 +45,15 @@ executables: []
31
45
  extensions: []
32
46
  extra_rdoc_files:
33
47
  - sig/mudis.rbs
48
+ - sig/mudis_config.rbs
34
49
  files:
35
50
  - README.md
36
51
  - lib/mudis.rb
37
52
  - lib/mudis/version.rb
38
53
  - lib/mudis_config.rb
39
54
  - sig/mudis.rbs
55
+ - sig/mudis_config.rbs
56
+ - spec/guardrails_spec.rb
40
57
  - spec/mudis_spec.rb
41
58
  homepage: https://github.com/kiebor81/mudis
42
59
  licenses:
@@ -62,4 +79,5 @@ signing_key:
62
79
  specification_version: 4
63
80
  summary: A fast in-memory Ruby LRU cache with compression and expiry.
64
81
  test_files:
82
+ - spec/guardrails_spec.rb
65
83
  - spec/mudis_spec.rb