Class: Reduxco::Context
- Inherits:
-
Object
- Object
- Reduxco::Context
- Defined in:
- lib/reduxco/context.rb,
lib/reduxco/context/callstack.rb,
lib/reduxco/context/callable_table.rb
Overview
Context is the client facing object for Reduxco.
Typically, one instantiates a Context with one or more maps of callables by name, and then calls Contxt#reduce to calculate all dependent nodes and return a result.
Maps may be any object that, when iterated with each, gives name/callable pairs. Names may be any object that can serve as a hash key. Callables can be any object that responds to the call method.
Overview
Context orchestrates the reduction calculation. It is primarily used by callables invoked during computation to get access to their environment.
Instantiators of a Context typically only use the Context#reduce method.
Users of Reduxco should use Reduxco::Reduxer rather than directly consume Context directly.
Callable Helper Functions
Callables (objects that respond to call) are the meat of the Context. When their call method is invoked, it is passed a reference to the Context. Callables can use this reference to access a range of methods, including the following:
- Context#call
-
Given a refname, run the associated callable and returns its value. Usually invoked as Context#[]
- Context#include?
-
Introspects if a refname is available.
- Context#completed?
-
Instrospects if a callable has been called and returned.
- Context#after
-
Given a refname and a block, runs the contents of the block after the given refname, but returns the value of the callable accociated with the refname.
- Context#inside
-
Given a refname and a block, runs the callable associated with the refname, giving it access to running the block inside of it and getting its value.
Defined Under Namespace
Classes: AssertError, CallableTable, Callstack, CyclicalError, LocalJumpError, NameError, NotCallableError
Instance Method Summary collapse
-
#after(refname) ⇒ Object
Runs the passed block after calling the passed refname.
-
#assert_completed(refname) ⇒ Object
Raises an exception if
completed?
is false. -
#before(refname) ⇒ Object
Runs the passed block before calling the passed refname.
-
#call(refname = :app, &block) ⇒ Object
(also: #reduce, #[], #inside)
Given a refname, call it for this context and return the result.
-
#callstack ⇒ Object
Returns a copy of the current callstack.
-
#completed?(refname) ⇒ Boolean
Returns a true value if the given refname has been computed.
-
#current_frame ⇒ Object
Returns the top frame of the callstack.
-
#dup ⇒ Object
Duplication of Contexts are dangerous because of all the deeply nested structures.
-
#include?(refname) ⇒ Boolean
Returns a true value if the given refname is defined in this context.
-
#initialize(*callable_maps) ⇒ Context
constructor
Instantiate a Context with the one or more callalbe maps (e.g. hashes whose keys are names and values are callable) for calculations.
-
#super(&block) ⇒ Object
When invoked, finds the next callable in the CallableTable up the chain from the current frame, calls it, and returns the result.
-
#yield(*args) ⇒ Object
Yields to the block given to a #Context.call.
- #yield_frame ⇒ Object
Constructor Details
#initialize(*callable_maps) ⇒ Context
Instantiate a Context with the one or more callalbe maps (e.g. hashes whose keys are names and values are callable) for calculations.
The further to the right in the arguments that a map is, the higher the precedence of itsdefinition.
65 66 67 68 69 70 71 72 73 74 |
# File 'lib/reduxco/context.rb', line 65 def initialize(*callable_maps) @callable_maps = callable_maps @calltable = CallableTable.new(@callable_maps) @callstack = Callstack.new @cache = {} @block_association_cache = {} @yield_frame_depth = 0 end |
Instance Method Details
#after(refname) ⇒ Object
Runs the passed block after calling the passed refname. Returns the value of the call to refname.
197 198 199 200 201 |
# File 'lib/reduxco/context.rb', line 197 def after(refname) result = call(refname) yield if block_given? result end |
#assert_completed(refname) ⇒ Object
Raises an exception if completed?
is false. Useful for asserting weak dependencies (those which you do not need the return value of) have been met.
184 185 186 |
# File 'lib/reduxco/context.rb', line 184 def assert_completed(refname) raise AssertError, "Assertion that #{refname} has completed failed.", caller unless completed?(refname) end |
#before(refname) ⇒ Object
Runs the passed block before calling the passed refname. Returns the value of the call to refname.
190 191 192 193 |
# File 'lib/reduxco/context.rb', line 190 def before(refname) yield if block_given? call(refname) end |
#call(refname = :app, &block) ⇒ Object Also known as: reduce, [], inside
Given a refname, call it for this context and return the result.
This can also take CallableRef instances directly, however if you find yourself passing in static references, this is likely because of design flaw in your callable map hierarchy.
Call results are cached so that their values can be re-used. If callables have side-effects their side-effects are only invoked the first time they are run.
Given a block, Context#yield may be used by the callable to invoke the block. Depending on the purpose of the block, Context#inside may be the preferrable alias.
89 90 91 92 93 94 95 96 97 98 99 |
# File 'lib/reduxco/context.rb', line 89 def call(refname=:app, &block) # First, we resolve the callref and invoke it. frame, callable = @calltable.resolve( CallableRef.new(refname) ) # If the ref is nil then we couldn't resolve, otherwise invoke. if( frame.nil? ) raise NameError, "No reference for name #{refname.inspect}", caller else invoke(frame, callable, &block) end end |
#callstack ⇒ Object
Returns a copy of the current callstack.
150 151 152 |
# File 'lib/reduxco/context.rb', line 150 def callstack @callstack.dup end |
#completed?(refname) ⇒ Boolean
Returns a true value if the given refname has been computed.
If the given CallableRef, it returns a true if the reference has already been computed.
175 176 177 178 179 |
# File 'lib/reduxco/context.rb', line 175 def completed?(refname) callref = CallableRef.new(refname) key = callref.dynamic? ? @calltable.resolve(callref).first : callref @cache.include?(key) end |
#current_frame ⇒ Object
Returns the top frame of the callstack.
155 156 157 |
# File 'lib/reduxco/context.rb', line 155 def current_frame @callstack.top end |
#dup ⇒ Object
Duplication of Contexts are dangerous because of all the deeply nested structures. That being said, it is very tempting to try to use a well-constructed Context rather than save and reuse the callable maps used for instantiation.
To remedy this concern, dup acts as a copy constructor, making a new Context instance with the same callable maps, but is otherwise freshly constructed.
211 212 213 |
# File 'lib/reduxco/context.rb', line 211 def dup self.class.new(*@callable_maps) end |
#include?(refname) ⇒ Boolean
Returns a true value if the given refname is defined in this context.
If given a CallableRef, it returns a true value if the reference is resolvable.
167 168 169 |
# File 'lib/reduxco/context.rb', line 167 def include?(refname) @calltable.resolve( CallableRef.new(refname) ) != CallableTable::RESOLUTION_FAILURE end |
#super(&block) ⇒ Object
When invoked, finds the next callable in the CallableTable up the chain from the current frame, calls it, and returns the result.
This is primarily used to reference shadowed callables in their overrides.
Like Context#call, it may take a block that is yielded to with Context#yield. If no block is given but the current scope has a block, its block will be automatically forwarded.
118 119 120 121 122 123 124 125 126 127 128 129 |
# File 'lib/reduxco/context.rb', line 118 def super(&block) # First, we resolve the super ref. frame, callable = @calltable.resolve_super( current_frame ) # If the ref is nil then we couldn't resolve, otherwise invoke. if( frame.nil? ) raise NameError, "No super found for #{current_frame}", caller else block = block_for_frame(current_frame) if block.nil? invoke(frame, callable, &block) end end |
#yield(*args) ⇒ Object
Yields to the block given to a #Context.call
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 |
# File 'lib/reduxco/context.rb', line 132 def yield(*args) block = block_for_frame(yield_frame) if( block.nil? ) raise LocalJumpError, "No block given to yield to.", caller else begin # If the block call has a context yield inside, resolve that one frame up. # This turns out to be what we usually want as we are forwarding a # yielded value in the call. @yield_frame_depth += 1 block.call(*args) ensure @yield_frame_depth -= 1 end end end |
#yield_frame ⇒ Object
159 160 161 |
# File 'lib/reduxco/context.rb', line 159 def yield_frame @callstack.peek(@yield_frame_depth) end |