Class: CMDx::Context
Overview
Shared data object passed through task execution. Wraps a symbol-keyed hash; supports ‘ctx.foo`/`ctx.foo = 1`/`ctx.foo?` dynamic accessors via #method_missing. Runtime freezes the root context during teardown so nested subtasks can’t mutate the outer task’s state after completion.
Instance Attribute Summary collapse
-
#strict ⇒ Boolean
Enables strict mode — when true, dynamic readers via #method_missing raise ‘NoMethodError` for unknown keys instead of returning `nil`.
Class Method Summary collapse
-
.build(context = EMPTY_HASH) ⇒ Context
Normalizes ‘context` into a Context instance.
Instance Method Summary collapse
- #[](key) ⇒ Object?
-
#as_json ⇒ Hash{Symbol => Object}
JSON-friendly hash view.
-
#clear ⇒ Context
Removes every entry.
-
#deconstruct ⇒ Array<Array(Symbol, Object)>
Pattern-matching support for ‘case context in […]`.
-
#deconstruct_keys(keys) ⇒ Hash{Symbol => Object}
Pattern-matching support for ‘case context in …`.
-
#deep_dup ⇒ Context
Returns a deep copy.
-
#deep_merge(context = EMPTY_HASH) ⇒ Context
Like #merge but recursive into Hash values: a nested Hash key collision merges the two Hashes instead of replacing the left with the right.
-
#delete(key) {|Symbol| ... } ⇒ Object?
Removed value.
- #dig(key, *keys) ⇒ Object?
- #each {|key, value| ... } ⇒ Context, Enumerator
- #each_key {|Symbol| ... } ⇒ Context, Enumerator
- #each_value {|Object| ... } ⇒ Context, Enumerator
- #empty? ⇒ Boolean
-
#eql?(other) ⇒ Boolean
(also: #==)
Equal when ‘other` is a Context with the same underlying hash.
-
#fetch(key) ⇒ Object
Hash-like fetch.
-
#freeze ⇒ Context
Freezes the context and its backing hash.
- #hash ⇒ Integer
-
#initialize(context = EMPTY_HASH) ⇒ Context
constructor
A new instance of Context.
-
#initialize_copy(source) ⇒ void
Ensures ‘#dup` / `#clone` produce a context with an independent backing hash so mutations on the copy do not leak into the original.
- #key?(key) ⇒ Boolean
- #keys ⇒ Array<Symbol>
-
#merge(context = EMPTY_HASH) ⇒ Context
Merges another context/hash-like into this one in place.
-
#retrieve(key, value = nil) { ... } ⇒ Object
Fetch-or-store.
- #size ⇒ Integer
-
#store(key, value) ⇒ Object
(also: #[]=)
Stores ‘value` under `key`, symbolizing the key.
-
#strict? ⇒ Boolean
Whether dynamic reads for unknown keys raise instead of returning ‘nil`.
-
#to_h ⇒ Hash{Symbol => Object}
A shallow copy of the underlying table.
-
#to_json(*args) ⇒ String
Serializes the context to a JSON string.
-
#to_s ⇒ String
Space-separated ‘key=value.inspect` pairs.
- #values ⇒ Array<Object>
Constructor Details
#initialize(context = EMPTY_HASH) ⇒ Context
Returns a new instance of Context.
43 44 45 46 47 48 49 50 51 52 53 54 55 |
# File 'lib/cmdx/context.rb', line 43 def initialize(context = EMPTY_HASH) @table = if context.respond_to?(:to_hash) context.to_hash elsif context.respond_to?(:to_h) context.to_h else raise ArgumentError, <<~MSG.chomp Context.build expected a Hash or an object responding to #to_h/#to_hash (got #{context.class}). See https://drexed.github.io/cmdx/basics/context/#assigning-data MSG end.transform_keys(&:to_sym) end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(method_name, *args, **_kwargs) ⇒ Object (private)
295 296 297 298 299 300 301 302 303 304 305 306 307 308 |
# File 'lib/cmdx/context.rb', line 295 def method_missing(method_name, *args, **_kwargs, &) if @table.key?(method_name) @table[method_name] elsif method_name.end_with?("=") @table[method_name[..-2].to_sym] = args.first elsif method_name.end_with?("?") !!@table[method_name[..-2].to_sym] elsif strict? raise UnknownAccessorError, <<~MSG.chomp unknown context key #{method_name.inspect} (strict mode); declared keys: #{@table.keys.inspect}. See https://drexed.github.io/cmdx/basics/context/#strict-mode MSG end end |
Instance Attribute Details
#strict ⇒ Boolean
Enables strict mode — when true, dynamic readers via #method_missing raise ‘NoMethodError` for unknown keys instead of returning `nil`. Set by `Task#initialize` from `Task.settings.strict_context`.
39 40 41 |
# File 'lib/cmdx/context.rb', line 39 def strict @strict end |
Class Method Details
.build(context = EMPTY_HASH) ⇒ Context
Normalizes ‘context` into a Context instance. Passes through an unfrozen Context unchanged (so nested tasks share state); unwraps anything with `#context` (e.g. a Task); wraps hashes/hash-likes into a new Context with symbolized keys.
22 23 24 25 26 27 28 29 30 |
# File 'lib/cmdx/context.rb', line 22 def build(context = EMPTY_HASH) if context.is_a?(self) && !context.frozen? context elsif context.respond_to?(:context) build(context.context) else new(context) end end |
Instance Method Details
#[](key) ⇒ Object?
111 112 113 |
# File 'lib/cmdx/context.rb', line 111 def [](key) @table[key.to_sym] end |
#as_json ⇒ Hash{Symbol => Object}
JSON-friendly hash view. Aliases #to_h for conventional ‘as_json` callers (e.g. Rails); values pass through unchanged — non-primitive entries rely on their own `as_json` / `to_json`.
232 233 234 |
# File 'lib/cmdx/context.rb', line 232 def as_json(*) to_h end |
#clear ⇒ Context
Removes every entry.
201 202 203 204 |
# File 'lib/cmdx/context.rb', line 201 def clear @table.clear self end |
#deconstruct ⇒ Array<Array(Symbol, Object)>
Pattern-matching support for ‘case context in […]`.
268 269 270 |
# File 'lib/cmdx/context.rb', line 268 def deconstruct @table.to_a end |
#deconstruct_keys(keys) ⇒ Hash{Symbol => Object}
Pattern-matching support for ‘case context in …`.
261 262 263 |
# File 'lib/cmdx/context.rb', line 261 def deconstruct_keys(keys) keys.nil? ? @table : @table.slice(*keys) end |
#deep_dup ⇒ Context
Returns a deep copy. Non-mutable scalars are shared; Hashes/Arrays are recursively duplicated; other objects fall back to ‘#dup` (and then to the original on `StandardError`). `@strict` is preserved on the copy.
277 278 279 280 281 282 |
# File 'lib/cmdx/context.rb', line 277 def deep_dup ctx = self.class.allocate ctx.instance_variable_set(:@table, Util.deep_dup(@table)) ctx.instance_variable_set(:@strict, @strict) ctx end |
#deep_merge(context = EMPTY_HASH) ⇒ Context
Like #merge but recursive into Hash values: a nested Hash key collision merges the two Hashes instead of replacing the left with the right. Non-Hash values follow last-write-wins (‘context` wins).
103 104 105 106 107 |
# File 'lib/cmdx/context.rb', line 103 def deep_merge(context = EMPTY_HASH) other = self.class.build(context) @table = Util.deep_merge(@table, other.to_h) self end |
#delete(key) {|Symbol| ... } ⇒ Object?
Returns removed value.
194 195 196 |
# File 'lib/cmdx/context.rb', line 194 def delete(key, &) @table.delete(key.to_sym, &) end |
#dig(key, *keys) ⇒ Object?
127 128 129 |
# File 'lib/cmdx/context.rb', line 127 def dig(key, *keys) @table.dig(key.to_sym, *keys) end |
#each {|key, value| ... } ⇒ Context, Enumerator
175 176 177 |
# File 'lib/cmdx/context.rb', line 175 def each(&) @table.each(&) end |
#each_key {|Symbol| ... } ⇒ Context, Enumerator
181 182 183 |
# File 'lib/cmdx/context.rb', line 181 def each_key(&) @table.each_key(&) end |
#each_value {|Object| ... } ⇒ Context, Enumerator
187 188 189 |
# File 'lib/cmdx/context.rb', line 187 def each_value(&) @table.each_value(&) end |
#empty? ⇒ Boolean
164 165 166 |
# File 'lib/cmdx/context.rb', line 164 def empty? @table.empty? end |
#eql?(other) ⇒ Boolean Also known as: ==
Equal when ‘other` is a Context with the same underlying hash.
210 211 212 |
# File 'lib/cmdx/context.rb', line 210 def eql?(other) other.is_a?(self.class) && (to_h == other.to_h) end |
#fetch(key) ⇒ Object
Hash-like fetch. Supports a default value, default block, or raises ‘KeyError` just like `Hash#fetch`.
120 121 122 |
# File 'lib/cmdx/context.rb', line 120 def fetch(key, ...) @table.fetch(key.to_sym, ...) end |
#freeze ⇒ Context
Freezes the context and its backing hash. Runtime calls this on the root task’s context during teardown.
288 289 290 291 |
# File 'lib/cmdx/context.rb', line 288 def freeze @table.freeze super end |
#hash ⇒ Integer
216 217 218 |
# File 'lib/cmdx/context.rb', line 216 def hash @table.hash end |
#initialize_copy(source) ⇒ void
This method returns an undefined value.
Ensures ‘#dup` / `#clone` produce a context with an independent backing hash so mutations on the copy do not leak into the original. `@strict` is preserved on the copy.
63 64 65 66 67 |
# File 'lib/cmdx/context.rb', line 63 def initialize_copy(source) super @table = source.instance_variable_get(:@table).dup @strict = source.strict? end |
#key?(key) ⇒ Boolean
149 150 151 |
# File 'lib/cmdx/context.rb', line 149 def key?(key) @table.key?(key.to_sym) end |
#keys ⇒ Array<Symbol>
154 155 156 |
# File 'lib/cmdx/context.rb', line 154 def keys @table.keys end |
#merge(context = EMPTY_HASH) ⇒ Context
Merges another context/hash-like into this one in place. Keys from ‘context` win on conflict.
91 92 93 94 95 |
# File 'lib/cmdx/context.rb', line 91 def merge(context = EMPTY_HASH) other = self.class.build(context) @table.merge!(other.to_h) self end |
#retrieve(key, value = nil) { ... } ⇒ Object
Fetch-or-store. Returns the existing value, or stores and returns the default (from block if given, else ‘value`).
139 140 141 142 143 144 145 |
# File 'lib/cmdx/context.rb', line 139 def retrieve(key, value = nil) nk = key.to_sym @table.fetch(nk) do @table[nk] = block_given? ? yield : value end end |
#size ⇒ Integer
169 170 171 |
# File 'lib/cmdx/context.rb', line 169 def size @table.size end |
#store(key, value) ⇒ Object Also known as: []=
Stores ‘value` under `key`, symbolizing the key. Overwrites any existing entry.
81 82 83 |
# File 'lib/cmdx/context.rb', line 81 def store(key, value) @table[key.to_sym] = value end |
#strict? ⇒ Boolean
Returns whether dynamic reads for unknown keys raise instead of returning ‘nil`.
71 72 73 |
# File 'lib/cmdx/context.rb', line 71 def strict? !!@strict end |
#to_h ⇒ Hash{Symbol => Object}
Returns a shallow copy of the underlying table. Frozen contexts return the frozen table directly to preserve ‘Hash#frozen?` semantics for serialization callers.
223 224 225 |
# File 'lib/cmdx/context.rb', line 223 def to_h @table.frozen? ? @table : @table.dup end |
#to_json(*args) ⇒ String
Serializes the context to a JSON string. Symbol keys are emitted as strings by the ‘json` stdlib.
241 242 243 |
# File 'lib/cmdx/context.rb', line 241 def to_json(*args) to_h.to_json(*args) end |
#to_s ⇒ String
Returns space-separated ‘key=value.inspect` pairs.
246 247 248 249 250 251 252 253 254 255 |
# File 'lib/cmdx/context.rb', line 246 def to_s buf = String.new(capacity: 256) @table.each do |k, v| buf << " " unless buf.empty? buf << k.to_s << "=" << v.inspect end buf end |
#values ⇒ Array<Object>
159 160 161 |
# File 'lib/cmdx/context.rb', line 159 def values @table.values end |