Class: Ratomic::Map
- Inherits:
-
Object
- Object
- Ratomic::Map
- Defined in:
- lib/ratomic/map.rb
Overview
A Ractor-shareable concurrent Hash backed by Rust’s DashMap.
Map gives Ruby code a small, Ruby-shaped API over DashMap’s concurrent storage. It is suitable for runtime state with shareable keys and values that are safe to access from multiple Ractors, such as integer counters or immutable offsets.
This is not a full Hash replacement. Iteration and arbitrary mutable object borrowing are intentionally absent.
Instance Method Summary collapse
-
#[](key) ⇒ Object?
Read a value by
key. -
#[]=(key, value) ⇒ void
Set a value for
key. -
#add_to_set(key, value) ⇒ Set
Atomically add
valueto a Set bucket forkey. -
#append(key, value) ⇒ Array
Atomically append
valueto an Array bucket forkey. -
#clear ⇒ void
Remove all entries from the map.
-
#compute(key) {|value| ... } ⇒ Object
Atomically compute and store a value for
key. -
#decrement(key, by = 1) ⇒ Numeric
Atomically decrement the numeric value for
key. -
#delete(key) ⇒ Object?
Remove
keyand return its previous value. -
#empty? ⇒ Boolean
Check whether the map currently has no entries.
-
#fetch(key, default = UNDEFINED) {|key| ... } ⇒ Object
Fetch a value by
key. -
#fetch_and_modify(key) {|value| ... } ⇒ void
Replace the existing value for
keywith the block return value. -
#fetch_or_store(key) ⇒ Object
Return the existing value for
key, or atomically store the block result. -
#get(key) ⇒ Object?
Read a value by
key. -
#include?(key) ⇒ Boolean
(also: #member?)
Alias for #key?.
-
#increment(key, by = 1) ⇒ Numeric
Atomically increment the numeric value for
key. -
#key?(key) ⇒ Boolean
Check whether
keycurrently exists in the map. -
#length ⇒ Integer
Alias for #size.
-
#set(key, value) ⇒ void
Set a value for
key. -
#size ⇒ Integer
Return the current number of entries.
-
#upsert(key, initial) {|value| ... } ⇒ Object
Atomically insert
initialfor a missing key, or update an existing value.
Instance Method Details
#[](key) ⇒ Object?
Read a value by key.
Missing keys currently return nil, so storing nil is ambiguous.
198 199 200 |
# File 'lib/ratomic/map.rb', line 198 def [](key) get(key) end |
#[]=(key, value) ⇒ void
This method returns an undefined value.
Set a value for key.
188 189 190 |
# File 'lib/ratomic/map.rb', line 188 def []=(key, value) set(key, value) end |
#add_to_set(key, value) ⇒ Set
Atomically add value to a Set bucket for key.
The stored Set is replaced rather than mutated in place.
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 |
# File 'lib/ratomic/map.rb', line 182 class Map # Set a value for +key+. # # @param key [Object] # @param value [Object] # @return [void] def []=(key, value) set(key, value) end # Read a value by +key+. # # Missing keys currently return nil, so storing nil is ambiguous. # # @param key [Object] # @return [Object, nil] def [](key) get(key) end # Fetch a value by +key+. # # Unlike #[], this distinguishes missing keys from explicit nil values. # # @param key [Object] # @param default [Object] # @yieldparam key [Object] # @return [Object] # @raise [KeyError] if +key+ is missing and no default or block is provided def fetch(key, default = UNDEFINED) return get(key) if key?(key) return yield key if block_given? return default unless default.equal?(UNDEFINED) raise KeyError, "key not found: #{key.inspect}" end # Atomically increment the numeric value for +key+. # # Missing keys start at zero. Existing non-numeric values raise TypeError # and are left unchanged. This uses a native update path and is the preferred # counter primitive for Ractor-heavy workloads. # # @param key [Object] # @param by [Numeric] # @return [Numeric] the newly stored value # @raise [TypeError] if +by+ or the existing value is not numeric def increment(key, by = 1) raise TypeError, "amount must be numeric: #{by.inspect}" unless by.is_a?(Numeric) __increment_numeric(key, by) end # Atomically decrement the numeric value for +key+. # # Missing keys start at zero. # # @param key [Object] # @param by [Numeric] # @return [Numeric] the newly stored value # @raise [TypeError] if +by+ or the existing value is not numeric def decrement(key, by = 1) raise TypeError, "amount must be numeric: #{by.inspect}" unless by.is_a?(Numeric) increment(key, -by) end # Atomically append +value+ to an Array bucket for +key+. # # The stored Array is replaced rather than mutated in place. # # @param key [Object] # @param value [Object] # @return [Array] the newly stored frozen Array # @raise [TypeError] if the existing value is not an Array def append(key, value) missing = !key?(key) compute(key) do |old_value| if old_value.nil? && missing [value].freeze else unless old_value.is_a?(Array) raise TypeError, "existing value for #{key.inspect} must be an Array: #{old_value.inspect}" end (old_value + [value]).freeze end end end # Atomically add +value+ to a Set bucket for +key+. # # The stored Set is replaced rather than mutated in place. # # @param key [Object] # @param value [Object] # @return [Set] the newly stored frozen Set # @raise [TypeError] if the existing value is not a Set def add_to_set(key, value) missing = !key?(key) compute(key) do |old_value| if old_value.nil? && missing Set[value].freeze else unless old_value.is_a?(Set) raise TypeError, "existing value for #{key.inspect} must be a Set: #{old_value.inspect}" end (old_value | [value]).freeze end end end # Alias for #size. # # @return [Integer] def length size end # Check whether the map currently has no entries. # # @return [Boolean] def empty? size.zero? end # Alias for #key?. # # @param key [Object] # @return [Boolean] def include?(key) key?(key) end alias member? include? end |
#append(key, value) ⇒ Array
Atomically append value to an Array bucket for key.
The stored Array is replaced rather than mutated in place.
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 |
# File 'lib/ratomic/map.rb', line 182 class Map # Set a value for +key+. # # @param key [Object] # @param value [Object] # @return [void] def []=(key, value) set(key, value) end # Read a value by +key+. # # Missing keys currently return nil, so storing nil is ambiguous. # # @param key [Object] # @return [Object, nil] def [](key) get(key) end # Fetch a value by +key+. # # Unlike #[], this distinguishes missing keys from explicit nil values. # # @param key [Object] # @param default [Object] # @yieldparam key [Object] # @return [Object] # @raise [KeyError] if +key+ is missing and no default or block is provided def fetch(key, default = UNDEFINED) return get(key) if key?(key) return yield key if block_given? return default unless default.equal?(UNDEFINED) raise KeyError, "key not found: #{key.inspect}" end # Atomically increment the numeric value for +key+. # # Missing keys start at zero. Existing non-numeric values raise TypeError # and are left unchanged. This uses a native update path and is the preferred # counter primitive for Ractor-heavy workloads. # # @param key [Object] # @param by [Numeric] # @return [Numeric] the newly stored value # @raise [TypeError] if +by+ or the existing value is not numeric def increment(key, by = 1) raise TypeError, "amount must be numeric: #{by.inspect}" unless by.is_a?(Numeric) __increment_numeric(key, by) end # Atomically decrement the numeric value for +key+. # # Missing keys start at zero. # # @param key [Object] # @param by [Numeric] # @return [Numeric] the newly stored value # @raise [TypeError] if +by+ or the existing value is not numeric def decrement(key, by = 1) raise TypeError, "amount must be numeric: #{by.inspect}" unless by.is_a?(Numeric) increment(key, -by) end # Atomically append +value+ to an Array bucket for +key+. # # The stored Array is replaced rather than mutated in place. # # @param key [Object] # @param value [Object] # @return [Array] the newly stored frozen Array # @raise [TypeError] if the existing value is not an Array def append(key, value) missing = !key?(key) compute(key) do |old_value| if old_value.nil? && missing [value].freeze else unless old_value.is_a?(Array) raise TypeError, "existing value for #{key.inspect} must be an Array: #{old_value.inspect}" end (old_value + [value]).freeze end end end # Atomically add +value+ to a Set bucket for +key+. # # The stored Set is replaced rather than mutated in place. # # @param key [Object] # @param value [Object] # @return [Set] the newly stored frozen Set # @raise [TypeError] if the existing value is not a Set def add_to_set(key, value) missing = !key?(key) compute(key) do |old_value| if old_value.nil? && missing Set[value].freeze else unless old_value.is_a?(Set) raise TypeError, "existing value for #{key.inspect} must be a Set: #{old_value.inspect}" end (old_value | [value]).freeze end end end # Alias for #size. # # @return [Integer] def length size end # Check whether the map currently has no entries. # # @return [Boolean] def empty? size.zero? end # Alias for #key?. # # @param key [Object] # @return [Boolean] def include?(key) key?(key) end alias member? include? end |
#clear ⇒ void
This method returns an undefined value.
Remove all entries from the map.
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 |
# File 'lib/ratomic/map.rb', line 182 class Map # Set a value for +key+. # # @param key [Object] # @param value [Object] # @return [void] def []=(key, value) set(key, value) end # Read a value by +key+. # # Missing keys currently return nil, so storing nil is ambiguous. # # @param key [Object] # @return [Object, nil] def [](key) get(key) end # Fetch a value by +key+. # # Unlike #[], this distinguishes missing keys from explicit nil values. # # @param key [Object] # @param default [Object] # @yieldparam key [Object] # @return [Object] # @raise [KeyError] if +key+ is missing and no default or block is provided def fetch(key, default = UNDEFINED) return get(key) if key?(key) return yield key if block_given? return default unless default.equal?(UNDEFINED) raise KeyError, "key not found: #{key.inspect}" end # Atomically increment the numeric value for +key+. # # Missing keys start at zero. Existing non-numeric values raise TypeError # and are left unchanged. This uses a native update path and is the preferred # counter primitive for Ractor-heavy workloads. # # @param key [Object] # @param by [Numeric] # @return [Numeric] the newly stored value # @raise [TypeError] if +by+ or the existing value is not numeric def increment(key, by = 1) raise TypeError, "amount must be numeric: #{by.inspect}" unless by.is_a?(Numeric) __increment_numeric(key, by) end # Atomically decrement the numeric value for +key+. # # Missing keys start at zero. # # @param key [Object] # @param by [Numeric] # @return [Numeric] the newly stored value # @raise [TypeError] if +by+ or the existing value is not numeric def decrement(key, by = 1) raise TypeError, "amount must be numeric: #{by.inspect}" unless by.is_a?(Numeric) increment(key, -by) end # Atomically append +value+ to an Array bucket for +key+. # # The stored Array is replaced rather than mutated in place. # # @param key [Object] # @param value [Object] # @return [Array] the newly stored frozen Array # @raise [TypeError] if the existing value is not an Array def append(key, value) missing = !key?(key) compute(key) do |old_value| if old_value.nil? && missing [value].freeze else unless old_value.is_a?(Array) raise TypeError, "existing value for #{key.inspect} must be an Array: #{old_value.inspect}" end (old_value + [value]).freeze end end end # Atomically add +value+ to a Set bucket for +key+. # # The stored Set is replaced rather than mutated in place. # # @param key [Object] # @param value [Object] # @return [Set] the newly stored frozen Set # @raise [TypeError] if the existing value is not a Set def add_to_set(key, value) missing = !key?(key) compute(key) do |old_value| if old_value.nil? && missing Set[value].freeze else unless old_value.is_a?(Set) raise TypeError, "existing value for #{key.inspect} must be a Set: #{old_value.inspect}" end (old_value | [value]).freeze end end end # Alias for #size. # # @return [Integer] def length size end # Check whether the map currently has no entries. # # @return [Boolean] def empty? size.zero? end # Alias for #key?. # # @param key [Object] # @return [Boolean] def include?(key) key?(key) end alias member? include? end |
#compute(key) {|value| ... } ⇒ Object
Atomically compute and store a value for key.
If key exists, yields the current value. If key is missing, yields nil. The block return value is stored and returned.
The operation is atomic for the key. The block runs while the map entry is locked, so avoid using this method for Ractor-hot loops or calling back into the same map from inside the block. Prefer native update helpers such as #increment when they fit the workflow.
If the block raises, the previous value is preserved. If the key was missing, no entry is inserted.
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 |
# File 'lib/ratomic/map.rb', line 182 class Map # Set a value for +key+. # # @param key [Object] # @param value [Object] # @return [void] def []=(key, value) set(key, value) end # Read a value by +key+. # # Missing keys currently return nil, so storing nil is ambiguous. # # @param key [Object] # @return [Object, nil] def [](key) get(key) end # Fetch a value by +key+. # # Unlike #[], this distinguishes missing keys from explicit nil values. # # @param key [Object] # @param default [Object] # @yieldparam key [Object] # @return [Object] # @raise [KeyError] if +key+ is missing and no default or block is provided def fetch(key, default = UNDEFINED) return get(key) if key?(key) return yield key if block_given? return default unless default.equal?(UNDEFINED) raise KeyError, "key not found: #{key.inspect}" end # Atomically increment the numeric value for +key+. # # Missing keys start at zero. Existing non-numeric values raise TypeError # and are left unchanged. This uses a native update path and is the preferred # counter primitive for Ractor-heavy workloads. # # @param key [Object] # @param by [Numeric] # @return [Numeric] the newly stored value # @raise [TypeError] if +by+ or the existing value is not numeric def increment(key, by = 1) raise TypeError, "amount must be numeric: #{by.inspect}" unless by.is_a?(Numeric) __increment_numeric(key, by) end # Atomically decrement the numeric value for +key+. # # Missing keys start at zero. # # @param key [Object] # @param by [Numeric] # @return [Numeric] the newly stored value # @raise [TypeError] if +by+ or the existing value is not numeric def decrement(key, by = 1) raise TypeError, "amount must be numeric: #{by.inspect}" unless by.is_a?(Numeric) increment(key, -by) end # Atomically append +value+ to an Array bucket for +key+. # # The stored Array is replaced rather than mutated in place. # # @param key [Object] # @param value [Object] # @return [Array] the newly stored frozen Array # @raise [TypeError] if the existing value is not an Array def append(key, value) missing = !key?(key) compute(key) do |old_value| if old_value.nil? && missing [value].freeze else unless old_value.is_a?(Array) raise TypeError, "existing value for #{key.inspect} must be an Array: #{old_value.inspect}" end (old_value + [value]).freeze end end end # Atomically add +value+ to a Set bucket for +key+. # # The stored Set is replaced rather than mutated in place. # # @param key [Object] # @param value [Object] # @return [Set] the newly stored frozen Set # @raise [TypeError] if the existing value is not a Set def add_to_set(key, value) missing = !key?(key) compute(key) do |old_value| if old_value.nil? && missing Set[value].freeze else unless old_value.is_a?(Set) raise TypeError, "existing value for #{key.inspect} must be a Set: #{old_value.inspect}" end (old_value | [value]).freeze end end end # Alias for #size. # # @return [Integer] def length size end # Check whether the map currently has no entries. # # @return [Boolean] def empty? size.zero? end # Alias for #key?. # # @param key [Object] # @return [Boolean] def include?(key) key?(key) end alias member? include? end |
#decrement(key, by = 1) ⇒ Numeric
Atomically decrement the numeric value for key.
Missing keys start at zero.
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 |
# File 'lib/ratomic/map.rb', line 182 class Map # Set a value for +key+. # # @param key [Object] # @param value [Object] # @return [void] def []=(key, value) set(key, value) end # Read a value by +key+. # # Missing keys currently return nil, so storing nil is ambiguous. # # @param key [Object] # @return [Object, nil] def [](key) get(key) end # Fetch a value by +key+. # # Unlike #[], this distinguishes missing keys from explicit nil values. # # @param key [Object] # @param default [Object] # @yieldparam key [Object] # @return [Object] # @raise [KeyError] if +key+ is missing and no default or block is provided def fetch(key, default = UNDEFINED) return get(key) if key?(key) return yield key if block_given? return default unless default.equal?(UNDEFINED) raise KeyError, "key not found: #{key.inspect}" end # Atomically increment the numeric value for +key+. # # Missing keys start at zero. Existing non-numeric values raise TypeError # and are left unchanged. This uses a native update path and is the preferred # counter primitive for Ractor-heavy workloads. # # @param key [Object] # @param by [Numeric] # @return [Numeric] the newly stored value # @raise [TypeError] if +by+ or the existing value is not numeric def increment(key, by = 1) raise TypeError, "amount must be numeric: #{by.inspect}" unless by.is_a?(Numeric) __increment_numeric(key, by) end # Atomically decrement the numeric value for +key+. # # Missing keys start at zero. # # @param key [Object] # @param by [Numeric] # @return [Numeric] the newly stored value # @raise [TypeError] if +by+ or the existing value is not numeric def decrement(key, by = 1) raise TypeError, "amount must be numeric: #{by.inspect}" unless by.is_a?(Numeric) increment(key, -by) end # Atomically append +value+ to an Array bucket for +key+. # # The stored Array is replaced rather than mutated in place. # # @param key [Object] # @param value [Object] # @return [Array] the newly stored frozen Array # @raise [TypeError] if the existing value is not an Array def append(key, value) missing = !key?(key) compute(key) do |old_value| if old_value.nil? && missing [value].freeze else unless old_value.is_a?(Array) raise TypeError, "existing value for #{key.inspect} must be an Array: #{old_value.inspect}" end (old_value + [value]).freeze end end end # Atomically add +value+ to a Set bucket for +key+. # # The stored Set is replaced rather than mutated in place. # # @param key [Object] # @param value [Object] # @return [Set] the newly stored frozen Set # @raise [TypeError] if the existing value is not a Set def add_to_set(key, value) missing = !key?(key) compute(key) do |old_value| if old_value.nil? && missing Set[value].freeze else unless old_value.is_a?(Set) raise TypeError, "existing value for #{key.inspect} must be a Set: #{old_value.inspect}" end (old_value | [value]).freeze end end end # Alias for #size. # # @return [Integer] def length size end # Check whether the map currently has no entries. # # @return [Boolean] def empty? size.zero? end # Alias for #key?. # # @param key [Object] # @return [Boolean] def include?(key) key?(key) end alias member? include? end |
#delete(key) ⇒ Object?
Remove key and return its previous value.
Missing keys return nil. Stored nil values also return nil; use #key? before deleting if that distinction matters.
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 |
# File 'lib/ratomic/map.rb', line 182 class Map # Set a value for +key+. # # @param key [Object] # @param value [Object] # @return [void] def []=(key, value) set(key, value) end # Read a value by +key+. # # Missing keys currently return nil, so storing nil is ambiguous. # # @param key [Object] # @return [Object, nil] def [](key) get(key) end # Fetch a value by +key+. # # Unlike #[], this distinguishes missing keys from explicit nil values. # # @param key [Object] # @param default [Object] # @yieldparam key [Object] # @return [Object] # @raise [KeyError] if +key+ is missing and no default or block is provided def fetch(key, default = UNDEFINED) return get(key) if key?(key) return yield key if block_given? return default unless default.equal?(UNDEFINED) raise KeyError, "key not found: #{key.inspect}" end # Atomically increment the numeric value for +key+. # # Missing keys start at zero. Existing non-numeric values raise TypeError # and are left unchanged. This uses a native update path and is the preferred # counter primitive for Ractor-heavy workloads. # # @param key [Object] # @param by [Numeric] # @return [Numeric] the newly stored value # @raise [TypeError] if +by+ or the existing value is not numeric def increment(key, by = 1) raise TypeError, "amount must be numeric: #{by.inspect}" unless by.is_a?(Numeric) __increment_numeric(key, by) end # Atomically decrement the numeric value for +key+. # # Missing keys start at zero. # # @param key [Object] # @param by [Numeric] # @return [Numeric] the newly stored value # @raise [TypeError] if +by+ or the existing value is not numeric def decrement(key, by = 1) raise TypeError, "amount must be numeric: #{by.inspect}" unless by.is_a?(Numeric) increment(key, -by) end # Atomically append +value+ to an Array bucket for +key+. # # The stored Array is replaced rather than mutated in place. # # @param key [Object] # @param value [Object] # @return [Array] the newly stored frozen Array # @raise [TypeError] if the existing value is not an Array def append(key, value) missing = !key?(key) compute(key) do |old_value| if old_value.nil? && missing [value].freeze else unless old_value.is_a?(Array) raise TypeError, "existing value for #{key.inspect} must be an Array: #{old_value.inspect}" end (old_value + [value]).freeze end end end # Atomically add +value+ to a Set bucket for +key+. # # The stored Set is replaced rather than mutated in place. # # @param key [Object] # @param value [Object] # @return [Set] the newly stored frozen Set # @raise [TypeError] if the existing value is not a Set def add_to_set(key, value) missing = !key?(key) compute(key) do |old_value| if old_value.nil? && missing Set[value].freeze else unless old_value.is_a?(Set) raise TypeError, "existing value for #{key.inspect} must be a Set: #{old_value.inspect}" end (old_value | [value]).freeze end end end # Alias for #size. # # @return [Integer] def length size end # Check whether the map currently has no entries. # # @return [Boolean] def empty? size.zero? end # Alias for #key?. # # @param key [Object] # @return [Boolean] def include?(key) key?(key) end alias member? include? end |
#empty? ⇒ Boolean
Check whether the map currently has no entries.
307 308 309 |
# File 'lib/ratomic/map.rb', line 307 def empty? size.zero? end |
#fetch(key, default = UNDEFINED) {|key| ... } ⇒ Object
Fetch a value by key.
Unlike #[], this distinguishes missing keys from explicit nil values.
211 212 213 214 215 216 217 |
# File 'lib/ratomic/map.rb', line 211 def fetch(key, default = UNDEFINED) return get(key) if key?(key) return yield key if block_given? return default unless default.equal?(UNDEFINED) raise KeyError, "key not found: #{key.inspect}" end |
#fetch_and_modify(key) {|value| ... } ⇒ void
This method returns an undefined value.
Replace the existing value for key with the block return value.
TODO: Revisit this name once the Map API settles. Prefer public method names that stay as close as possible to Ruby Hash semantics.
The key must already exist. The operation is atomic for the key. The block runs while the map entry is locked, so avoid using this method for Ractor-hot loops or calling back into the same map from inside the block. If the block raises, the previous value is preserved.
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 |
# File 'lib/ratomic/map.rb', line 182 class Map # Set a value for +key+. # # @param key [Object] # @param value [Object] # @return [void] def []=(key, value) set(key, value) end # Read a value by +key+. # # Missing keys currently return nil, so storing nil is ambiguous. # # @param key [Object] # @return [Object, nil] def [](key) get(key) end # Fetch a value by +key+. # # Unlike #[], this distinguishes missing keys from explicit nil values. # # @param key [Object] # @param default [Object] # @yieldparam key [Object] # @return [Object] # @raise [KeyError] if +key+ is missing and no default or block is provided def fetch(key, default = UNDEFINED) return get(key) if key?(key) return yield key if block_given? return default unless default.equal?(UNDEFINED) raise KeyError, "key not found: #{key.inspect}" end # Atomically increment the numeric value for +key+. # # Missing keys start at zero. Existing non-numeric values raise TypeError # and are left unchanged. This uses a native update path and is the preferred # counter primitive for Ractor-heavy workloads. # # @param key [Object] # @param by [Numeric] # @return [Numeric] the newly stored value # @raise [TypeError] if +by+ or the existing value is not numeric def increment(key, by = 1) raise TypeError, "amount must be numeric: #{by.inspect}" unless by.is_a?(Numeric) __increment_numeric(key, by) end # Atomically decrement the numeric value for +key+. # # Missing keys start at zero. # # @param key [Object] # @param by [Numeric] # @return [Numeric] the newly stored value # @raise [TypeError] if +by+ or the existing value is not numeric def decrement(key, by = 1) raise TypeError, "amount must be numeric: #{by.inspect}" unless by.is_a?(Numeric) increment(key, -by) end # Atomically append +value+ to an Array bucket for +key+. # # The stored Array is replaced rather than mutated in place. # # @param key [Object] # @param value [Object] # @return [Array] the newly stored frozen Array # @raise [TypeError] if the existing value is not an Array def append(key, value) missing = !key?(key) compute(key) do |old_value| if old_value.nil? && missing [value].freeze else unless old_value.is_a?(Array) raise TypeError, "existing value for #{key.inspect} must be an Array: #{old_value.inspect}" end (old_value + [value]).freeze end end end # Atomically add +value+ to a Set bucket for +key+. # # The stored Set is replaced rather than mutated in place. # # @param key [Object] # @param value [Object] # @return [Set] the newly stored frozen Set # @raise [TypeError] if the existing value is not a Set def add_to_set(key, value) missing = !key?(key) compute(key) do |old_value| if old_value.nil? && missing Set[value].freeze else unless old_value.is_a?(Set) raise TypeError, "existing value for #{key.inspect} must be a Set: #{old_value.inspect}" end (old_value | [value]).freeze end end end # Alias for #size. # # @return [Integer] def length size end # Check whether the map currently has no entries. # # @return [Boolean] def empty? size.zero? end # Alias for #key?. # # @param key [Object] # @return [Boolean] def include?(key) key?(key) end alias member? include? end |
#fetch_or_store(key) ⇒ Object
Return the existing value for key, or atomically store the block result.
If key exists, returns the current value and does not yield. If key is missing, yields once, stores the block return value, and returns it.
The operation is atomic for the key. Under contention, only one stored value wins for a missing key. The block runs while the map entry is locked, so avoid using this method for Ractor-hot loops or calling back into the same map from inside the block.
If the block raises, no entry is inserted.
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 |
# File 'lib/ratomic/map.rb', line 182 class Map # Set a value for +key+. # # @param key [Object] # @param value [Object] # @return [void] def []=(key, value) set(key, value) end # Read a value by +key+. # # Missing keys currently return nil, so storing nil is ambiguous. # # @param key [Object] # @return [Object, nil] def [](key) get(key) end # Fetch a value by +key+. # # Unlike #[], this distinguishes missing keys from explicit nil values. # # @param key [Object] # @param default [Object] # @yieldparam key [Object] # @return [Object] # @raise [KeyError] if +key+ is missing and no default or block is provided def fetch(key, default = UNDEFINED) return get(key) if key?(key) return yield key if block_given? return default unless default.equal?(UNDEFINED) raise KeyError, "key not found: #{key.inspect}" end # Atomically increment the numeric value for +key+. # # Missing keys start at zero. Existing non-numeric values raise TypeError # and are left unchanged. This uses a native update path and is the preferred # counter primitive for Ractor-heavy workloads. # # @param key [Object] # @param by [Numeric] # @return [Numeric] the newly stored value # @raise [TypeError] if +by+ or the existing value is not numeric def increment(key, by = 1) raise TypeError, "amount must be numeric: #{by.inspect}" unless by.is_a?(Numeric) __increment_numeric(key, by) end # Atomically decrement the numeric value for +key+. # # Missing keys start at zero. # # @param key [Object] # @param by [Numeric] # @return [Numeric] the newly stored value # @raise [TypeError] if +by+ or the existing value is not numeric def decrement(key, by = 1) raise TypeError, "amount must be numeric: #{by.inspect}" unless by.is_a?(Numeric) increment(key, -by) end # Atomically append +value+ to an Array bucket for +key+. # # The stored Array is replaced rather than mutated in place. # # @param key [Object] # @param value [Object] # @return [Array] the newly stored frozen Array # @raise [TypeError] if the existing value is not an Array def append(key, value) missing = !key?(key) compute(key) do |old_value| if old_value.nil? && missing [value].freeze else unless old_value.is_a?(Array) raise TypeError, "existing value for #{key.inspect} must be an Array: #{old_value.inspect}" end (old_value + [value]).freeze end end end # Atomically add +value+ to a Set bucket for +key+. # # The stored Set is replaced rather than mutated in place. # # @param key [Object] # @param value [Object] # @return [Set] the newly stored frozen Set # @raise [TypeError] if the existing value is not a Set def add_to_set(key, value) missing = !key?(key) compute(key) do |old_value| if old_value.nil? && missing Set[value].freeze else unless old_value.is_a?(Set) raise TypeError, "existing value for #{key.inspect} must be a Set: #{old_value.inspect}" end (old_value | [value]).freeze end end end # Alias for #size. # # @return [Integer] def length size end # Check whether the map currently has no entries. # # @return [Boolean] def empty? size.zero? end # Alias for #key?. # # @param key [Object] # @return [Boolean] def include?(key) key?(key) end alias member? include? end |
#get(key) ⇒ Object?
Read a value by key.
Missing keys return nil, so use #key? or #fetch when stored nil values need to be distinguished from missing entries.
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 |
# File 'lib/ratomic/map.rb', line 182 class Map # Set a value for +key+. # # @param key [Object] # @param value [Object] # @return [void] def []=(key, value) set(key, value) end # Read a value by +key+. # # Missing keys currently return nil, so storing nil is ambiguous. # # @param key [Object] # @return [Object, nil] def [](key) get(key) end # Fetch a value by +key+. # # Unlike #[], this distinguishes missing keys from explicit nil values. # # @param key [Object] # @param default [Object] # @yieldparam key [Object] # @return [Object] # @raise [KeyError] if +key+ is missing and no default or block is provided def fetch(key, default = UNDEFINED) return get(key) if key?(key) return yield key if block_given? return default unless default.equal?(UNDEFINED) raise KeyError, "key not found: #{key.inspect}" end # Atomically increment the numeric value for +key+. # # Missing keys start at zero. Existing non-numeric values raise TypeError # and are left unchanged. This uses a native update path and is the preferred # counter primitive for Ractor-heavy workloads. # # @param key [Object] # @param by [Numeric] # @return [Numeric] the newly stored value # @raise [TypeError] if +by+ or the existing value is not numeric def increment(key, by = 1) raise TypeError, "amount must be numeric: #{by.inspect}" unless by.is_a?(Numeric) __increment_numeric(key, by) end # Atomically decrement the numeric value for +key+. # # Missing keys start at zero. # # @param key [Object] # @param by [Numeric] # @return [Numeric] the newly stored value # @raise [TypeError] if +by+ or the existing value is not numeric def decrement(key, by = 1) raise TypeError, "amount must be numeric: #{by.inspect}" unless by.is_a?(Numeric) increment(key, -by) end # Atomically append +value+ to an Array bucket for +key+. # # The stored Array is replaced rather than mutated in place. # # @param key [Object] # @param value [Object] # @return [Array] the newly stored frozen Array # @raise [TypeError] if the existing value is not an Array def append(key, value) missing = !key?(key) compute(key) do |old_value| if old_value.nil? && missing [value].freeze else unless old_value.is_a?(Array) raise TypeError, "existing value for #{key.inspect} must be an Array: #{old_value.inspect}" end (old_value + [value]).freeze end end end # Atomically add +value+ to a Set bucket for +key+. # # The stored Set is replaced rather than mutated in place. # # @param key [Object] # @param value [Object] # @return [Set] the newly stored frozen Set # @raise [TypeError] if the existing value is not a Set def add_to_set(key, value) missing = !key?(key) compute(key) do |old_value| if old_value.nil? && missing Set[value].freeze else unless old_value.is_a?(Set) raise TypeError, "existing value for #{key.inspect} must be a Set: #{old_value.inspect}" end (old_value | [value]).freeze end end end # Alias for #size. # # @return [Integer] def length size end # Check whether the map currently has no entries. # # @return [Boolean] def empty? size.zero? end # Alias for #key?. # # @param key [Object] # @return [Boolean] def include?(key) key?(key) end alias member? include? end |
#include?(key) ⇒ Boolean Also known as: member?
Alias for #key?.
315 316 317 |
# File 'lib/ratomic/map.rb', line 315 def include?(key) key?(key) end |
#increment(key, by = 1) ⇒ Numeric
Atomically increment the numeric value for key.
Missing keys start at zero. Existing non-numeric values raise TypeError and are left unchanged. This uses a native update path and is the preferred counter primitive for Ractor-heavy workloads.
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 |
# File 'lib/ratomic/map.rb', line 182 class Map # Set a value for +key+. # # @param key [Object] # @param value [Object] # @return [void] def []=(key, value) set(key, value) end # Read a value by +key+. # # Missing keys currently return nil, so storing nil is ambiguous. # # @param key [Object] # @return [Object, nil] def [](key) get(key) end # Fetch a value by +key+. # # Unlike #[], this distinguishes missing keys from explicit nil values. # # @param key [Object] # @param default [Object] # @yieldparam key [Object] # @return [Object] # @raise [KeyError] if +key+ is missing and no default or block is provided def fetch(key, default = UNDEFINED) return get(key) if key?(key) return yield key if block_given? return default unless default.equal?(UNDEFINED) raise KeyError, "key not found: #{key.inspect}" end # Atomically increment the numeric value for +key+. # # Missing keys start at zero. Existing non-numeric values raise TypeError # and are left unchanged. This uses a native update path and is the preferred # counter primitive for Ractor-heavy workloads. # # @param key [Object] # @param by [Numeric] # @return [Numeric] the newly stored value # @raise [TypeError] if +by+ or the existing value is not numeric def increment(key, by = 1) raise TypeError, "amount must be numeric: #{by.inspect}" unless by.is_a?(Numeric) __increment_numeric(key, by) end # Atomically decrement the numeric value for +key+. # # Missing keys start at zero. # # @param key [Object] # @param by [Numeric] # @return [Numeric] the newly stored value # @raise [TypeError] if +by+ or the existing value is not numeric def decrement(key, by = 1) raise TypeError, "amount must be numeric: #{by.inspect}" unless by.is_a?(Numeric) increment(key, -by) end # Atomically append +value+ to an Array bucket for +key+. # # The stored Array is replaced rather than mutated in place. # # @param key [Object] # @param value [Object] # @return [Array] the newly stored frozen Array # @raise [TypeError] if the existing value is not an Array def append(key, value) missing = !key?(key) compute(key) do |old_value| if old_value.nil? && missing [value].freeze else unless old_value.is_a?(Array) raise TypeError, "existing value for #{key.inspect} must be an Array: #{old_value.inspect}" end (old_value + [value]).freeze end end end # Atomically add +value+ to a Set bucket for +key+. # # The stored Set is replaced rather than mutated in place. # # @param key [Object] # @param value [Object] # @return [Set] the newly stored frozen Set # @raise [TypeError] if the existing value is not a Set def add_to_set(key, value) missing = !key?(key) compute(key) do |old_value| if old_value.nil? && missing Set[value].freeze else unless old_value.is_a?(Set) raise TypeError, "existing value for #{key.inspect} must be a Set: #{old_value.inspect}" end (old_value | [value]).freeze end end end # Alias for #size. # # @return [Integer] def length size end # Check whether the map currently has no entries. # # @return [Boolean] def empty? size.zero? end # Alias for #key?. # # @param key [Object] # @return [Boolean] def include?(key) key?(key) end alias member? include? end |
#key?(key) ⇒ Boolean
Check whether key currently exists in the map.
Unlike #get and #[], this distinguishes missing keys from stored nil values.
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 |
# File 'lib/ratomic/map.rb', line 182 class Map # Set a value for +key+. # # @param key [Object] # @param value [Object] # @return [void] def []=(key, value) set(key, value) end # Read a value by +key+. # # Missing keys currently return nil, so storing nil is ambiguous. # # @param key [Object] # @return [Object, nil] def [](key) get(key) end # Fetch a value by +key+. # # Unlike #[], this distinguishes missing keys from explicit nil values. # # @param key [Object] # @param default [Object] # @yieldparam key [Object] # @return [Object] # @raise [KeyError] if +key+ is missing and no default or block is provided def fetch(key, default = UNDEFINED) return get(key) if key?(key) return yield key if block_given? return default unless default.equal?(UNDEFINED) raise KeyError, "key not found: #{key.inspect}" end # Atomically increment the numeric value for +key+. # # Missing keys start at zero. Existing non-numeric values raise TypeError # and are left unchanged. This uses a native update path and is the preferred # counter primitive for Ractor-heavy workloads. # # @param key [Object] # @param by [Numeric] # @return [Numeric] the newly stored value # @raise [TypeError] if +by+ or the existing value is not numeric def increment(key, by = 1) raise TypeError, "amount must be numeric: #{by.inspect}" unless by.is_a?(Numeric) __increment_numeric(key, by) end # Atomically decrement the numeric value for +key+. # # Missing keys start at zero. # # @param key [Object] # @param by [Numeric] # @return [Numeric] the newly stored value # @raise [TypeError] if +by+ or the existing value is not numeric def decrement(key, by = 1) raise TypeError, "amount must be numeric: #{by.inspect}" unless by.is_a?(Numeric) increment(key, -by) end # Atomically append +value+ to an Array bucket for +key+. # # The stored Array is replaced rather than mutated in place. # # @param key [Object] # @param value [Object] # @return [Array] the newly stored frozen Array # @raise [TypeError] if the existing value is not an Array def append(key, value) missing = !key?(key) compute(key) do |old_value| if old_value.nil? && missing [value].freeze else unless old_value.is_a?(Array) raise TypeError, "existing value for #{key.inspect} must be an Array: #{old_value.inspect}" end (old_value + [value]).freeze end end end # Atomically add +value+ to a Set bucket for +key+. # # The stored Set is replaced rather than mutated in place. # # @param key [Object] # @param value [Object] # @return [Set] the newly stored frozen Set # @raise [TypeError] if the existing value is not a Set def add_to_set(key, value) missing = !key?(key) compute(key) do |old_value| if old_value.nil? && missing Set[value].freeze else unless old_value.is_a?(Set) raise TypeError, "existing value for #{key.inspect} must be a Set: #{old_value.inspect}" end (old_value | [value]).freeze end end end # Alias for #size. # # @return [Integer] def length size end # Check whether the map currently has no entries. # # @return [Boolean] def empty? size.zero? end # Alias for #key?. # # @param key [Object] # @return [Boolean] def include?(key) key?(key) end alias member? include? end |
#length ⇒ Integer
Alias for #size.
300 301 302 |
# File 'lib/ratomic/map.rb', line 300 def length size end |
#set(key, value) ⇒ void
This method returns an undefined value.
Set a value for key.
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 |
# File 'lib/ratomic/map.rb', line 182 class Map # Set a value for +key+. # # @param key [Object] # @param value [Object] # @return [void] def []=(key, value) set(key, value) end # Read a value by +key+. # # Missing keys currently return nil, so storing nil is ambiguous. # # @param key [Object] # @return [Object, nil] def [](key) get(key) end # Fetch a value by +key+. # # Unlike #[], this distinguishes missing keys from explicit nil values. # # @param key [Object] # @param default [Object] # @yieldparam key [Object] # @return [Object] # @raise [KeyError] if +key+ is missing and no default or block is provided def fetch(key, default = UNDEFINED) return get(key) if key?(key) return yield key if block_given? return default unless default.equal?(UNDEFINED) raise KeyError, "key not found: #{key.inspect}" end # Atomically increment the numeric value for +key+. # # Missing keys start at zero. Existing non-numeric values raise TypeError # and are left unchanged. This uses a native update path and is the preferred # counter primitive for Ractor-heavy workloads. # # @param key [Object] # @param by [Numeric] # @return [Numeric] the newly stored value # @raise [TypeError] if +by+ or the existing value is not numeric def increment(key, by = 1) raise TypeError, "amount must be numeric: #{by.inspect}" unless by.is_a?(Numeric) __increment_numeric(key, by) end # Atomically decrement the numeric value for +key+. # # Missing keys start at zero. # # @param key [Object] # @param by [Numeric] # @return [Numeric] the newly stored value # @raise [TypeError] if +by+ or the existing value is not numeric def decrement(key, by = 1) raise TypeError, "amount must be numeric: #{by.inspect}" unless by.is_a?(Numeric) increment(key, -by) end # Atomically append +value+ to an Array bucket for +key+. # # The stored Array is replaced rather than mutated in place. # # @param key [Object] # @param value [Object] # @return [Array] the newly stored frozen Array # @raise [TypeError] if the existing value is not an Array def append(key, value) missing = !key?(key) compute(key) do |old_value| if old_value.nil? && missing [value].freeze else unless old_value.is_a?(Array) raise TypeError, "existing value for #{key.inspect} must be an Array: #{old_value.inspect}" end (old_value + [value]).freeze end end end # Atomically add +value+ to a Set bucket for +key+. # # The stored Set is replaced rather than mutated in place. # # @param key [Object] # @param value [Object] # @return [Set] the newly stored frozen Set # @raise [TypeError] if the existing value is not a Set def add_to_set(key, value) missing = !key?(key) compute(key) do |old_value| if old_value.nil? && missing Set[value].freeze else unless old_value.is_a?(Set) raise TypeError, "existing value for #{key.inspect} must be a Set: #{old_value.inspect}" end (old_value | [value]).freeze end end end # Alias for #size. # # @return [Integer] def length size end # Check whether the map currently has no entries. # # @return [Boolean] def empty? size.zero? end # Alias for #key?. # # @param key [Object] # @return [Boolean] def include?(key) key?(key) end alias member? include? end |
#size ⇒ Integer
Return the current number of entries.
Since this is a concurrent map, the value is a moment-in-time observation.
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 |
# File 'lib/ratomic/map.rb', line 182 class Map # Set a value for +key+. # # @param key [Object] # @param value [Object] # @return [void] def []=(key, value) set(key, value) end # Read a value by +key+. # # Missing keys currently return nil, so storing nil is ambiguous. # # @param key [Object] # @return [Object, nil] def [](key) get(key) end # Fetch a value by +key+. # # Unlike #[], this distinguishes missing keys from explicit nil values. # # @param key [Object] # @param default [Object] # @yieldparam key [Object] # @return [Object] # @raise [KeyError] if +key+ is missing and no default or block is provided def fetch(key, default = UNDEFINED) return get(key) if key?(key) return yield key if block_given? return default unless default.equal?(UNDEFINED) raise KeyError, "key not found: #{key.inspect}" end # Atomically increment the numeric value for +key+. # # Missing keys start at zero. Existing non-numeric values raise TypeError # and are left unchanged. This uses a native update path and is the preferred # counter primitive for Ractor-heavy workloads. # # @param key [Object] # @param by [Numeric] # @return [Numeric] the newly stored value # @raise [TypeError] if +by+ or the existing value is not numeric def increment(key, by = 1) raise TypeError, "amount must be numeric: #{by.inspect}" unless by.is_a?(Numeric) __increment_numeric(key, by) end # Atomically decrement the numeric value for +key+. # # Missing keys start at zero. # # @param key [Object] # @param by [Numeric] # @return [Numeric] the newly stored value # @raise [TypeError] if +by+ or the existing value is not numeric def decrement(key, by = 1) raise TypeError, "amount must be numeric: #{by.inspect}" unless by.is_a?(Numeric) increment(key, -by) end # Atomically append +value+ to an Array bucket for +key+. # # The stored Array is replaced rather than mutated in place. # # @param key [Object] # @param value [Object] # @return [Array] the newly stored frozen Array # @raise [TypeError] if the existing value is not an Array def append(key, value) missing = !key?(key) compute(key) do |old_value| if old_value.nil? && missing [value].freeze else unless old_value.is_a?(Array) raise TypeError, "existing value for #{key.inspect} must be an Array: #{old_value.inspect}" end (old_value + [value]).freeze end end end # Atomically add +value+ to a Set bucket for +key+. # # The stored Set is replaced rather than mutated in place. # # @param key [Object] # @param value [Object] # @return [Set] the newly stored frozen Set # @raise [TypeError] if the existing value is not a Set def add_to_set(key, value) missing = !key?(key) compute(key) do |old_value| if old_value.nil? && missing Set[value].freeze else unless old_value.is_a?(Set) raise TypeError, "existing value for #{key.inspect} must be a Set: #{old_value.inspect}" end (old_value | [value]).freeze end end end # Alias for #size. # # @return [Integer] def length size end # Check whether the map currently has no entries. # # @return [Boolean] def empty? size.zero? end # Alias for #key?. # # @param key [Object] # @return [Boolean] def include?(key) key?(key) end alias member? include? end |
#upsert(key, initial) {|value| ... } ⇒ Object
Atomically insert initial for a missing key, or update an existing value.
If key is missing, stores and returns initial without yielding. If key exists, yields the current value, stores the block return value, and returns it.
The operation is atomic for the key. The block runs while the map entry is locked, so avoid using this method for Ractor-hot loops or calling back into the same map from inside the block. Prefer native update helpers such as #increment when they fit the workflow.
If the block raises, the previous value is preserved.
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 |
# File 'lib/ratomic/map.rb', line 182 class Map # Set a value for +key+. # # @param key [Object] # @param value [Object] # @return [void] def []=(key, value) set(key, value) end # Read a value by +key+. # # Missing keys currently return nil, so storing nil is ambiguous. # # @param key [Object] # @return [Object, nil] def [](key) get(key) end # Fetch a value by +key+. # # Unlike #[], this distinguishes missing keys from explicit nil values. # # @param key [Object] # @param default [Object] # @yieldparam key [Object] # @return [Object] # @raise [KeyError] if +key+ is missing and no default or block is provided def fetch(key, default = UNDEFINED) return get(key) if key?(key) return yield key if block_given? return default unless default.equal?(UNDEFINED) raise KeyError, "key not found: #{key.inspect}" end # Atomically increment the numeric value for +key+. # # Missing keys start at zero. Existing non-numeric values raise TypeError # and are left unchanged. This uses a native update path and is the preferred # counter primitive for Ractor-heavy workloads. # # @param key [Object] # @param by [Numeric] # @return [Numeric] the newly stored value # @raise [TypeError] if +by+ or the existing value is not numeric def increment(key, by = 1) raise TypeError, "amount must be numeric: #{by.inspect}" unless by.is_a?(Numeric) __increment_numeric(key, by) end # Atomically decrement the numeric value for +key+. # # Missing keys start at zero. # # @param key [Object] # @param by [Numeric] # @return [Numeric] the newly stored value # @raise [TypeError] if +by+ or the existing value is not numeric def decrement(key, by = 1) raise TypeError, "amount must be numeric: #{by.inspect}" unless by.is_a?(Numeric) increment(key, -by) end # Atomically append +value+ to an Array bucket for +key+. # # The stored Array is replaced rather than mutated in place. # # @param key [Object] # @param value [Object] # @return [Array] the newly stored frozen Array # @raise [TypeError] if the existing value is not an Array def append(key, value) missing = !key?(key) compute(key) do |old_value| if old_value.nil? && missing [value].freeze else unless old_value.is_a?(Array) raise TypeError, "existing value for #{key.inspect} must be an Array: #{old_value.inspect}" end (old_value + [value]).freeze end end end # Atomically add +value+ to a Set bucket for +key+. # # The stored Set is replaced rather than mutated in place. # # @param key [Object] # @param value [Object] # @return [Set] the newly stored frozen Set # @raise [TypeError] if the existing value is not a Set def add_to_set(key, value) missing = !key?(key) compute(key) do |old_value| if old_value.nil? && missing Set[value].freeze else unless old_value.is_a?(Set) raise TypeError, "existing value for #{key.inspect} must be a Set: #{old_value.inspect}" end (old_value | [value]).freeze end end end # Alias for #size. # # @return [Integer] def length size end # Check whether the map currently has no entries. # # @return [Boolean] def empty? size.zero? end # Alias for #key?. # # @param key [Object] # @return [Boolean] def include?(key) key?(key) end alias member? include? end |