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) ⇒ Object
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 ⇒ Ratomic::Map
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) ⇒ Object
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.
212 213 214 |
# File 'lib/ratomic/map.rb', line 212 def [](key) get(key) end |
#[]=(key, value) ⇒ Object
Set a value for key.
In assignment form, Ruby returns the assigned value.
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 320 321 322 323 324 325 326 327 328 329 330 331 332 333 |
# File 'lib/ratomic/map.rb', line 194 class Map # Set a value for +key+. # # In assignment form, Ruby returns the assigned value. # # @param key [Object] key to write # @param value [Object] value to store # @return [Object] the assigned value 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] lookup key # @return [Object, nil] the stored value, or nil when the key is missing def [](key) get(key) end # Fetch a value by +key+. # # Unlike #[], this distinguishes missing keys from explicit nil values. # # @param key [Object] lookup key # @param default [Object] fallback value to return when the key is missing # @yieldparam key [Object] the missing key # @return [Object] the found value, default, or block result # @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] counter key to increment # @param by [Numeric] amount to add # @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] counter key to decrement # @param by [Numeric] amount to subtract # @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] bucket key to append into # @param value [Object] value to append # @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] bucket key to update # @param value [Object] value to add to the set # @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] the current number of entries def length size end # Check whether the map currently has no entries. # # @return [Boolean] true when the map currently has no entries def empty? size.zero? end # Alias for #key?. # # @param key [Object] lookup key # @return [Boolean] true when the key currently exists def include?(key) key?(key) end alias member? include? 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.
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 320 321 322 323 324 325 326 327 328 329 330 331 332 333 |
# File 'lib/ratomic/map.rb', line 194 class Map # Set a value for +key+. # # In assignment form, Ruby returns the assigned value. # # @param key [Object] key to write # @param value [Object] value to store # @return [Object] the assigned value 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] lookup key # @return [Object, nil] the stored value, or nil when the key is missing def [](key) get(key) end # Fetch a value by +key+. # # Unlike #[], this distinguishes missing keys from explicit nil values. # # @param key [Object] lookup key # @param default [Object] fallback value to return when the key is missing # @yieldparam key [Object] the missing key # @return [Object] the found value, default, or block result # @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] counter key to increment # @param by [Numeric] amount to add # @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] counter key to decrement # @param by [Numeric] amount to subtract # @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] bucket key to append into # @param value [Object] value to append # @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] bucket key to update # @param value [Object] value to add to the set # @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] the current number of entries def length size end # Check whether the map currently has no entries. # # @return [Boolean] true when the map currently has no entries def empty? size.zero? end # Alias for #key?. # # @param key [Object] lookup key # @return [Boolean] true when the key currently exists 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.
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 320 321 322 323 324 325 326 327 328 329 330 331 332 333 |
# File 'lib/ratomic/map.rb', line 194 class Map # Set a value for +key+. # # In assignment form, Ruby returns the assigned value. # # @param key [Object] key to write # @param value [Object] value to store # @return [Object] the assigned value 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] lookup key # @return [Object, nil] the stored value, or nil when the key is missing def [](key) get(key) end # Fetch a value by +key+. # # Unlike #[], this distinguishes missing keys from explicit nil values. # # @param key [Object] lookup key # @param default [Object] fallback value to return when the key is missing # @yieldparam key [Object] the missing key # @return [Object] the found value, default, or block result # @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] counter key to increment # @param by [Numeric] amount to add # @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] counter key to decrement # @param by [Numeric] amount to subtract # @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] bucket key to append into # @param value [Object] value to append # @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] bucket key to update # @param value [Object] value to add to the set # @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] the current number of entries def length size end # Check whether the map currently has no entries. # # @return [Boolean] true when the map currently has no entries def empty? size.zero? end # Alias for #key?. # # @param key [Object] lookup key # @return [Boolean] true when the key currently exists def include?(key) key?(key) end alias member? include? end |
#clear ⇒ Ratomic::Map
Remove all entries from the map.
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 320 321 322 323 324 325 326 327 328 329 330 331 332 333 |
# File 'lib/ratomic/map.rb', line 194 class Map # Set a value for +key+. # # In assignment form, Ruby returns the assigned value. # # @param key [Object] key to write # @param value [Object] value to store # @return [Object] the assigned value 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] lookup key # @return [Object, nil] the stored value, or nil when the key is missing def [](key) get(key) end # Fetch a value by +key+. # # Unlike #[], this distinguishes missing keys from explicit nil values. # # @param key [Object] lookup key # @param default [Object] fallback value to return when the key is missing # @yieldparam key [Object] the missing key # @return [Object] the found value, default, or block result # @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] counter key to increment # @param by [Numeric] amount to add # @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] counter key to decrement # @param by [Numeric] amount to subtract # @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] bucket key to append into # @param value [Object] value to append # @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] bucket key to update # @param value [Object] value to add to the set # @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] the current number of entries def length size end # Check whether the map currently has no entries. # # @return [Boolean] true when the map currently has no entries def empty? size.zero? end # Alias for #key?. # # @param key [Object] lookup key # @return [Boolean] true when the key currently exists 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.
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 320 321 322 323 324 325 326 327 328 329 330 331 332 333 |
# File 'lib/ratomic/map.rb', line 194 class Map # Set a value for +key+. # # In assignment form, Ruby returns the assigned value. # # @param key [Object] key to write # @param value [Object] value to store # @return [Object] the assigned value 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] lookup key # @return [Object, nil] the stored value, or nil when the key is missing def [](key) get(key) end # Fetch a value by +key+. # # Unlike #[], this distinguishes missing keys from explicit nil values. # # @param key [Object] lookup key # @param default [Object] fallback value to return when the key is missing # @yieldparam key [Object] the missing key # @return [Object] the found value, default, or block result # @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] counter key to increment # @param by [Numeric] amount to add # @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] counter key to decrement # @param by [Numeric] amount to subtract # @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] bucket key to append into # @param value [Object] value to append # @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] bucket key to update # @param value [Object] value to add to the set # @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] the current number of entries def length size end # Check whether the map currently has no entries. # # @return [Boolean] true when the map currently has no entries def empty? size.zero? end # Alias for #key?. # # @param key [Object] lookup key # @return [Boolean] true when the key currently exists 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.
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 320 321 322 323 324 325 326 327 328 329 330 331 332 333 |
# File 'lib/ratomic/map.rb', line 194 class Map # Set a value for +key+. # # In assignment form, Ruby returns the assigned value. # # @param key [Object] key to write # @param value [Object] value to store # @return [Object] the assigned value 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] lookup key # @return [Object, nil] the stored value, or nil when the key is missing def [](key) get(key) end # Fetch a value by +key+. # # Unlike #[], this distinguishes missing keys from explicit nil values. # # @param key [Object] lookup key # @param default [Object] fallback value to return when the key is missing # @yieldparam key [Object] the missing key # @return [Object] the found value, default, or block result # @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] counter key to increment # @param by [Numeric] amount to add # @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] counter key to decrement # @param by [Numeric] amount to subtract # @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] bucket key to append into # @param value [Object] value to append # @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] bucket key to update # @param value [Object] value to add to the set # @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] the current number of entries def length size end # Check whether the map currently has no entries. # # @return [Boolean] true when the map currently has no entries def empty? size.zero? end # Alias for #key?. # # @param key [Object] lookup key # @return [Boolean] true when the key currently exists 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.
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 320 321 322 323 324 325 326 327 328 329 330 331 332 333 |
# File 'lib/ratomic/map.rb', line 194 class Map # Set a value for +key+. # # In assignment form, Ruby returns the assigned value. # # @param key [Object] key to write # @param value [Object] value to store # @return [Object] the assigned value 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] lookup key # @return [Object, nil] the stored value, or nil when the key is missing def [](key) get(key) end # Fetch a value by +key+. # # Unlike #[], this distinguishes missing keys from explicit nil values. # # @param key [Object] lookup key # @param default [Object] fallback value to return when the key is missing # @yieldparam key [Object] the missing key # @return [Object] the found value, default, or block result # @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] counter key to increment # @param by [Numeric] amount to add # @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] counter key to decrement # @param by [Numeric] amount to subtract # @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] bucket key to append into # @param value [Object] value to append # @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] bucket key to update # @param value [Object] value to add to the set # @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] the current number of entries def length size end # Check whether the map currently has no entries. # # @return [Boolean] true when the map currently has no entries def empty? size.zero? end # Alias for #key?. # # @param key [Object] lookup key # @return [Boolean] true when the key currently exists def include?(key) key?(key) end alias member? include? end |
#empty? ⇒ Boolean
Check whether the map currently has no entries.
321 322 323 |
# File 'lib/ratomic/map.rb', line 321 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.
225 226 227 228 229 230 231 |
# File 'lib/ratomic/map.rb', line 225 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.
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 320 321 322 323 324 325 326 327 328 329 330 331 332 333 |
# File 'lib/ratomic/map.rb', line 194 class Map # Set a value for +key+. # # In assignment form, Ruby returns the assigned value. # # @param key [Object] key to write # @param value [Object] value to store # @return [Object] the assigned value 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] lookup key # @return [Object, nil] the stored value, or nil when the key is missing def [](key) get(key) end # Fetch a value by +key+. # # Unlike #[], this distinguishes missing keys from explicit nil values. # # @param key [Object] lookup key # @param default [Object] fallback value to return when the key is missing # @yieldparam key [Object] the missing key # @return [Object] the found value, default, or block result # @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] counter key to increment # @param by [Numeric] amount to add # @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] counter key to decrement # @param by [Numeric] amount to subtract # @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] bucket key to append into # @param value [Object] value to append # @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] bucket key to update # @param value [Object] value to add to the set # @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] the current number of entries def length size end # Check whether the map currently has no entries. # # @return [Boolean] true when the map currently has no entries def empty? size.zero? end # Alias for #key?. # # @param key [Object] lookup key # @return [Boolean] true when the key currently exists 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.
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 320 321 322 323 324 325 326 327 328 329 330 331 332 333 |
# File 'lib/ratomic/map.rb', line 194 class Map # Set a value for +key+. # # In assignment form, Ruby returns the assigned value. # # @param key [Object] key to write # @param value [Object] value to store # @return [Object] the assigned value 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] lookup key # @return [Object, nil] the stored value, or nil when the key is missing def [](key) get(key) end # Fetch a value by +key+. # # Unlike #[], this distinguishes missing keys from explicit nil values. # # @param key [Object] lookup key # @param default [Object] fallback value to return when the key is missing # @yieldparam key [Object] the missing key # @return [Object] the found value, default, or block result # @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] counter key to increment # @param by [Numeric] amount to add # @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] counter key to decrement # @param by [Numeric] amount to subtract # @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] bucket key to append into # @param value [Object] value to append # @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] bucket key to update # @param value [Object] value to add to the set # @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] the current number of entries def length size end # Check whether the map currently has no entries. # # @return [Boolean] true when the map currently has no entries def empty? size.zero? end # Alias for #key?. # # @param key [Object] lookup key # @return [Boolean] true when the key currently exists 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.
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 320 321 322 323 324 325 326 327 328 329 330 331 332 333 |
# File 'lib/ratomic/map.rb', line 194 class Map # Set a value for +key+. # # In assignment form, Ruby returns the assigned value. # # @param key [Object] key to write # @param value [Object] value to store # @return [Object] the assigned value 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] lookup key # @return [Object, nil] the stored value, or nil when the key is missing def [](key) get(key) end # Fetch a value by +key+. # # Unlike #[], this distinguishes missing keys from explicit nil values. # # @param key [Object] lookup key # @param default [Object] fallback value to return when the key is missing # @yieldparam key [Object] the missing key # @return [Object] the found value, default, or block result # @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] counter key to increment # @param by [Numeric] amount to add # @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] counter key to decrement # @param by [Numeric] amount to subtract # @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] bucket key to append into # @param value [Object] value to append # @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] bucket key to update # @param value [Object] value to add to the set # @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] the current number of entries def length size end # Check whether the map currently has no entries. # # @return [Boolean] true when the map currently has no entries def empty? size.zero? end # Alias for #key?. # # @param key [Object] lookup key # @return [Boolean] true when the key currently exists def include?(key) key?(key) end alias member? include? end |
#include?(key) ⇒ Boolean Also known as: member?
Alias for #key?.
329 330 331 |
# File 'lib/ratomic/map.rb', line 329 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.
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 320 321 322 323 324 325 326 327 328 329 330 331 332 333 |
# File 'lib/ratomic/map.rb', line 194 class Map # Set a value for +key+. # # In assignment form, Ruby returns the assigned value. # # @param key [Object] key to write # @param value [Object] value to store # @return [Object] the assigned value 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] lookup key # @return [Object, nil] the stored value, or nil when the key is missing def [](key) get(key) end # Fetch a value by +key+. # # Unlike #[], this distinguishes missing keys from explicit nil values. # # @param key [Object] lookup key # @param default [Object] fallback value to return when the key is missing # @yieldparam key [Object] the missing key # @return [Object] the found value, default, or block result # @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] counter key to increment # @param by [Numeric] amount to add # @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] counter key to decrement # @param by [Numeric] amount to subtract # @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] bucket key to append into # @param value [Object] value to append # @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] bucket key to update # @param value [Object] value to add to the set # @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] the current number of entries def length size end # Check whether the map currently has no entries. # # @return [Boolean] true when the map currently has no entries def empty? size.zero? end # Alias for #key?. # # @param key [Object] lookup key # @return [Boolean] true when the key currently exists 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.
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 320 321 322 323 324 325 326 327 328 329 330 331 332 333 |
# File 'lib/ratomic/map.rb', line 194 class Map # Set a value for +key+. # # In assignment form, Ruby returns the assigned value. # # @param key [Object] key to write # @param value [Object] value to store # @return [Object] the assigned value 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] lookup key # @return [Object, nil] the stored value, or nil when the key is missing def [](key) get(key) end # Fetch a value by +key+. # # Unlike #[], this distinguishes missing keys from explicit nil values. # # @param key [Object] lookup key # @param default [Object] fallback value to return when the key is missing # @yieldparam key [Object] the missing key # @return [Object] the found value, default, or block result # @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] counter key to increment # @param by [Numeric] amount to add # @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] counter key to decrement # @param by [Numeric] amount to subtract # @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] bucket key to append into # @param value [Object] value to append # @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] bucket key to update # @param value [Object] value to add to the set # @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] the current number of entries def length size end # Check whether the map currently has no entries. # # @return [Boolean] true when the map currently has no entries def empty? size.zero? end # Alias for #key?. # # @param key [Object] lookup key # @return [Boolean] true when the key currently exists def include?(key) key?(key) end alias member? include? end |
#length ⇒ Integer
Alias for #size.
314 315 316 |
# File 'lib/ratomic/map.rb', line 314 def length size end |
#set(key, value) ⇒ Object
Set a value for key.
This is the method behind #[]= and follows Ruby setter semantics by
returning the assigned value.
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 320 321 322 323 324 325 326 327 328 329 330 331 332 333 |
# File 'lib/ratomic/map.rb', line 194 class Map # Set a value for +key+. # # In assignment form, Ruby returns the assigned value. # # @param key [Object] key to write # @param value [Object] value to store # @return [Object] the assigned value 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] lookup key # @return [Object, nil] the stored value, or nil when the key is missing def [](key) get(key) end # Fetch a value by +key+. # # Unlike #[], this distinguishes missing keys from explicit nil values. # # @param key [Object] lookup key # @param default [Object] fallback value to return when the key is missing # @yieldparam key [Object] the missing key # @return [Object] the found value, default, or block result # @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] counter key to increment # @param by [Numeric] amount to add # @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] counter key to decrement # @param by [Numeric] amount to subtract # @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] bucket key to append into # @param value [Object] value to append # @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] bucket key to update # @param value [Object] value to add to the set # @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] the current number of entries def length size end # Check whether the map currently has no entries. # # @return [Boolean] true when the map currently has no entries def empty? size.zero? end # Alias for #key?. # # @param key [Object] lookup key # @return [Boolean] true when the key currently exists 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.
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 320 321 322 323 324 325 326 327 328 329 330 331 332 333 |
# File 'lib/ratomic/map.rb', line 194 class Map # Set a value for +key+. # # In assignment form, Ruby returns the assigned value. # # @param key [Object] key to write # @param value [Object] value to store # @return [Object] the assigned value 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] lookup key # @return [Object, nil] the stored value, or nil when the key is missing def [](key) get(key) end # Fetch a value by +key+. # # Unlike #[], this distinguishes missing keys from explicit nil values. # # @param key [Object] lookup key # @param default [Object] fallback value to return when the key is missing # @yieldparam key [Object] the missing key # @return [Object] the found value, default, or block result # @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] counter key to increment # @param by [Numeric] amount to add # @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] counter key to decrement # @param by [Numeric] amount to subtract # @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] bucket key to append into # @param value [Object] value to append # @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] bucket key to update # @param value [Object] value to add to the set # @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] the current number of entries def length size end # Check whether the map currently has no entries. # # @return [Boolean] true when the map currently has no entries def empty? size.zero? end # Alias for #key?. # # @param key [Object] lookup key # @return [Boolean] true when the key currently exists 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.
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 320 321 322 323 324 325 326 327 328 329 330 331 332 333 |
# File 'lib/ratomic/map.rb', line 194 class Map # Set a value for +key+. # # In assignment form, Ruby returns the assigned value. # # @param key [Object] key to write # @param value [Object] value to store # @return [Object] the assigned value 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] lookup key # @return [Object, nil] the stored value, or nil when the key is missing def [](key) get(key) end # Fetch a value by +key+. # # Unlike #[], this distinguishes missing keys from explicit nil values. # # @param key [Object] lookup key # @param default [Object] fallback value to return when the key is missing # @yieldparam key [Object] the missing key # @return [Object] the found value, default, or block result # @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] counter key to increment # @param by [Numeric] amount to add # @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] counter key to decrement # @param by [Numeric] amount to subtract # @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] bucket key to append into # @param value [Object] value to append # @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] bucket key to update # @param value [Object] value to add to the set # @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] the current number of entries def length size end # Check whether the map currently has no entries. # # @return [Boolean] true when the map currently has no entries def empty? size.zero? end # Alias for #key?. # # @param key [Object] lookup key # @return [Boolean] true when the key currently exists def include?(key) key?(key) end alias member? include? end |