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
NoMethodErrorfor unknown keys instead of returningnil.
Class Method Summary collapse
-
.build(context = EMPTY_HASH) ⇒ Context
Normalizes
contextinto 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
otheris 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/#cloneproduce 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
valueunderkey, 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.inspectpairs. - #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 |