Class: Cheri::Builder::Context
- Inherits:
-
Object
- Object
- Cheri::Builder::Context
- Defined in:
- lib/cheri/builder/context.rb
Defined Under Namespace
Classes: ConnectionMinder
Instance Method Summary collapse
-
#[](k) ⇒ Object
call-seq: ctx -> value.
-
#[]=(k, v) ⇒ Object
call-seq: ctx = value -> value.
-
#active? ⇒ Boolean
call-seq: ctx.active? -> true/false Returns true if the context stack is not empty.
-
#als ⇒ Object
(also: #aliases)
call-seq: ctx.aliases -> aliases hash ctx.als -> aliases hash.
-
#auto ⇒ Object
def client @c end.
- #auto!(m) ⇒ Object
-
#bld(*r, &k) ⇒ Object
(also: #builder)
call-seq: ctx.builder(symbol, *args [, &block]) -> aBuilder or nil ctx.bld(symbol, *args [, &block]) -> aBuilder or nil.
- #bottom ⇒ Object
-
#call(b, *r, &k) ⇒ Object
call-seq: ctx.call(builder, *args, &block) -> result Pushes builder onto the stack, then calls the block/proc, passing the builder’s object and any other arguments supplied:
block.call(builder.object,*args)
Then pops the builder from the stack and returns the result of the call. - #cfg ⇒ Object
-
#crz(mod, c, a) ⇒ Object
(also: #resolve_ctor)
call-seq: ctx.resolve_ctor(clazz, args) -> resolved? ctx.crz(clazz, args) -> resolved? Attempts to resolve any
:Constant
symbols found inargs
for the specifiedclazz
. -
#csm(*r, &k) ⇒ Object
(also: #consume)
call-seq: ctx.consume(sym, *args, &block) -> consumed?, result ctx.csm(sym, *args, &block) -> consumed?, result.
-
#each ⇒ Object
(also: #each_frame)
:yields: frame.
-
#empty? ⇒ Boolean
(also: #stack_empty?)
call-seq: ctx.stack_empty? -> true/false ctx.empty? -> true/false Returns true if the context stack is empty.
-
#eval(i, b, &k) ⇒ Object
call-seq: ctx.eval(instance, builder, &block) -> result Pushes builder onto the stack, then calls
instance.instance_eval(&block)
. -
#exec(i, b, *r, &k) ⇒ Object
call-seq: ctx.exec(instance, builder, *args, &block) -> result.
- #ictx ⇒ Object
-
#initialize(ictx, client) ⇒ Context
constructor
call-seq: Context.new(instance_context,client) -> aContext – Key to the cryptic instance variable names:.
-
#inspect ⇒ Object
Overrides the default Object#inspect to prevent mind-boggling circular displays in IRB.
-
#mrz(mod, o, y, a) ⇒ Object
(also: #resolve_meth)
call-seq: ctx.resolve_meth(object, method_name, args) -> resolved? ctx.mrz(object, method_name, args) -> resolved?.
-
#msend(m, sym, *r, &k) ⇒ Object
call-seq: ctx.msend(builder_module, sym, *args, &block) -> result.
-
#nsend(m, ns, sym, *r, &k) ⇒ Object
call-seq: ctx.nsend(builder_module, namespace_sym, sym, *args, &block) -> result.
- #pop ⇒ Object (also: #pop_frame)
-
#ppd(ctr, bldr, obj, sym, props = nil) ⇒ Object
(also: #prepared)
call-seq: prepared(connecter,builder,obj,sym,props=nil) -> true ppd(connecter,builder,obj,sym,props=nil) -> true Called by a Connecter (normally) from its
prepare
method (normally) to indicate that it is prepared to connectobj
to the parent object hosted bybuilder
. - #props ⇒ Object
- #push(f) ⇒ Object (also: #push_frame)
-
#reach ⇒ Object
(also: #reverse_each, #reverse_each_frame)
:yields: frame.
-
#run(b) ⇒ Object
call-seq: ctx.run(builder) -> result of builder.run.
-
#send(sym, *r, &k) ⇒ Object
call-seq: ctx.send(sym, *args, &block) -> matched?, result.
-
#size ⇒ Object
(also: #length, #stack_size)
call-seq: ctx.stack_size -> Fixnum ctx.size -> Fixnum ctx.length -> Fixnum Returns the number of frames on the context stack.
- #top ⇒ Object (also: #peek)
-
#tos(t) ⇒ Object
(also: #type_on_stack?)
call-seq: ctx.type_on_stack?(class_or_module) -> true/false ctx.tos(class_or_module) -> true/false.
Constructor Details
#initialize(ictx, client) ⇒ Context
call-seq:
Context.new(instance_context,client) -> aContext
– Key to the cryptic instance variable names:
@a - any? hash - active builders (on the stack) for which any? -> true
@c - the client (normally instance of class that included Cheri builder modules)
@f - reference to the factories in the class's Config instance
@g - reference to the class's Config instance (all included Cheri builder modules)
@p - configuration properties hash, used for various purposes
@i - reference to the InstanceContext that created this Context
@l - reference to aliases from InstanceConfig
@m - the ConnectionMinder for this context, tracks pending (prepared) connections
@n - reference to the connecters in the class's Config instance
@r - array of ConstantResolver instances, lazily initialized
@s - the stack, holds active builders and builder frames
@u - auto-factories array, lazily initialized
@x - flag indicating whether instance_exec is supported, lazily initialized
++
140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
# File 'lib/cheri/builder/context.rb', line 140 def initialize(ictx,client) @i = ictx cfg = @g = ictx.cfg @f = cfg.factories @n = cfg.connecters if (u = ictx.auto) @u = u.dup end @l = ictx.aliases # @c = client @m = ConnectionMinder.new @s = [] # stack @p = {} # configuration properties end |
Instance Method Details
#[](k) ⇒ Object
call-seq:
ctx[key] -> value
582 583 584 |
# File 'lib/cheri/builder/context.rb', line 582 def [](k) @p[k] end |
#[]=(k, v) ⇒ Object
call-seq:
ctx[key] = value -> value
589 590 591 |
# File 'lib/cheri/builder/context.rb', line 589 def []=(k,v) @p[k]=v end |
#active? ⇒ Boolean
call-seq:
ctx.active? -> true/false
Returns true if the context stack is not empty.
403 404 405 |
# File 'lib/cheri/builder/context.rb', line 403 def active? !@s.empty? end |
#als ⇒ Object Also known as: aliases
call-seq:
ctx.aliases -> aliases hash
ctx.als -> aliases hash
597 598 599 |
# File 'lib/cheri/builder/context.rb', line 597 def als @l end |
#auto ⇒ Object
def client
@c
end
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 |
# File 'lib/cheri/builder/context.rb', line 163 def auto # same mods or both nil if (u = @u) == (iu = @i.auto) return u end # TODO: save ref to iu to compare? otherwise we'll do this every time if # thread-only auto is set for one mod and instance auto is set for another. if iu if u u.concat(iu - u) else u = @u = iu.dup end end u end |
#auto!(m) ⇒ Object
180 181 182 183 |
# File 'lib/cheri/builder/context.rb', line 180 def auto!(m) raise BuilderException,"not an included builder module: #{m}" unless @g.include?(m) (@u ||= []) << m unless @u && @u.include?(m) end |
#bld(*r, &k) ⇒ Object Also known as: builder
call-seq:
ctx.builder(symbol, *args [, &block]) -> aBuilder or nil
ctx.bld(symbol, *args [, &block]) -> aBuilder or nil
Searches the stack for a builder/frame whose associated factory can supply a builder for the specified symbol (usually originating in the client’s method_missing
method). If no stack frame can supply a builder, any auto-enabled builder modules are searched in the order they were enabled. Returns nil if no builder is found.
429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 |
# File 'lib/cheri/builder/context.rb', line 429 def bld(*r,&k) #puts "bld args: #{[r.join(',')]}" queried = nil # lazily-allocated array to hold factories we've already queried b = nil # builder atf = @f # search stack for a frame whose module's factory can supply a matching builder @s.reverse_each do |s| if (f = atf[s.mod]) unless queried && queried.include?(f) return b if (b = f.builder(self,*r,&k)) (queried ||= []) << f end end end # try auto-enabled builders if (u = auto) u.each do |m| if (f = atf[m]) unless queried && queried.include?(f) return b if (b = f.builder(self,*r,&k)) (queried ||= []) << f end end end end # no matches nil end |
#bottom ⇒ Object
362 363 364 |
# File 'lib/cheri/builder/context.rb', line 362 def bottom @s.first end |
#call(b, *r, &k) ⇒ Object
call-seq:
ctx.call(builder, *args, &block) -> result
Pushes builder onto the stack, then calls the block/proc, passing the builder’s object and any other arguments supplied: block.call(builder.object,*args)
Then pops the builder from the stack and returns the result of the call.
Context#call
ensures that the stack is always popped, even if an exception is raised in the called block. If you choose to call the block directly, be sure to do the same, otherwise failures and/or (possibly subtle) bugs will ensue.
292 293 294 295 296 297 |
# File 'lib/cheri/builder/context.rb', line 292 def call(b,*r,&k) push(b) k.call(b.object,*r) ensure pop end |
#cfg ⇒ Object
571 572 573 |
# File 'lib/cheri/builder/context.rb', line 571 def cfg @g end |
#crz(mod, c, a) ⇒ Object Also known as: resolve_ctor
call-seq:
ctx.resolve_ctor(clazz, args) -> resolved?
ctx.crz(clazz, args) -> resolved?
Attempts to resolve any :Constant
symbols found in args
for the specified clazz
. Note that args
must be passed as an array, not as *args
, as substitutions will be made directly in the array.
511 512 513 514 515 516 517 |
# File 'lib/cheri/builder/context.rb', line 511 def crz(mod,c,a) #puts "resolving: m=#{mod}, c=#{c}, a=[#{a.join(',')}]" if (z = @g.resolvers[mod]) return true if z.resolve_ctor(c,a) end false end |
#csm(*r, &k) ⇒ Object Also known as: consume
call-seq:
ctx.consume(sym, *args, &block) -> consumed?, result
ctx.csm(sym, *args, &block) -> consumed?, result
Attempts to consume the specified sym
, usually as a method on a built object (though not necessarily; the Java on_xxx handlers are implemented through a consumer). This is normally called when no builder is found matching sym
(which normally originates in the client’s method_missing method). Only the topmost stack frame is eligible to consume.
This method is primarily intended for internal use by Context.
471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 |
# File 'lib/cheri/builder/context.rb', line 471 def csm(*r,&k) # methods are not passed up the stack; if it can't be consumed # by the last builder/frame, it's an error return false,nil unless (b = @s.last) # TODO: error if !b # offer the builder/object the opportunity to consume the method directly #puts "csm b=#{b}, b.o=#{b.object}, b.mod=#{b.mod}" if b.respond_to?(:consume) return b.consume(*r,&k) # must have object for standard consumers elsif b.object && (c = @g.consumers[b.mod]) return c.consume(self,b,*r,&k) else return false, nil end end |
#each ⇒ Object Also known as: each_frame
:yields: frame
366 367 368 |
# File 'lib/cheri/builder/context.rb', line 366 def each #:yields: frame @s.reverse_each {|v| yield v } if block_given? end |
#empty? ⇒ Boolean Also known as: stack_empty?
call-seq:
ctx.stack_empty? -> true/false
ctx.empty? -> true/false
Returns true if the context stack is empty.
394 395 396 |
# File 'lib/cheri/builder/context.rb', line 394 def empty? @s.empty? end |
#eval(i, b, &k) ⇒ Object
call-seq:
ctx.eval(instance, builder, &block) -> result
Pushes builder onto the stack, then calls instance.instance_eval(&block)
. Then pops the builder from the stack and returns the result of the instance_eval
.
Context#eval
ensures that the stack is always popped, even if an exception is raised in the executed block. If you choose to do your own instance_eval, be sure to do the same, otherwise failures and/or (possibly subtle) bugs will ensue.
308 309 310 311 312 313 |
# File 'lib/cheri/builder/context.rb', line 308 def eval(i,b,&k) push(b) i.instance_eval(&k) ensure pop end |
#exec(i, b, *r, &k) ⇒ Object
call-seq:
ctx.exec(instance, builder, *args, &block) -> result
Pushes builder onto the stack, then calls instance.instance_exec(builder.object,*args,&block)
. Then pops the builder from the stack and returns the result of the instance_exec
.
Context#exec
ensures that the stack is always popped, even if an exception is raised in the executed block. If you choose to do your own instance_exec
, be sure to do the same, otherwise failures and/or (possibly subtle) bugs will ensue.
If instance_exec
is not supported on a system, the call is redirected to Context#eval
(instance_eval
). Note that parameters will not be passed to blocks in that case. – TODO: need a good default instance_exec, (the one in active_supportcore_extobjectextending.rb?) ++
331 332 333 334 335 336 337 |
# File 'lib/cheri/builder/context.rb', line 331 def exec(i,b,*r,&k) return eval(i,b,&k) unless @x ||= (defined?instance_exec) push(b) i.instance_exec(b.object,*r,&k) ensure pop end |
#ictx ⇒ Object
155 156 157 |
# File 'lib/cheri/builder/context.rb', line 155 def ictx @i end |
#inspect ⇒ Object
Overrides the default Object#inspect to prevent mind-boggling circular displays in IRB.
629 630 631 |
# File 'lib/cheri/builder/context.rb', line 629 def inspect to_s end |
#mrz(mod, o, y, a) ⇒ Object Also known as: resolve_meth
call-seq:
ctx.resolve_meth(object, method_name, args) -> resolved?
ctx.mrz(object, method_name, args) -> resolved?
Attempts to resolve any :Constant
symbols found in args
for the specified object
and method_name
. Note that args
must be passed as an array, not as *args
, as substitutions will be made directly in the array.
496 497 498 499 500 501 |
# File 'lib/cheri/builder/context.rb', line 496 def mrz(mod,o,y,a) if (z = @g.resolvers[mod]) return true if z.resolve_meth(o,y,a) end false end |
#msend(m, sym, *r, &k) ⇒ Object
call-seq:
ctx.msend(builder_module, sym, *args, &block) -> result
Version of send
intended for use by proxy objects. The supplied module’s factory must be able to resolve the symbol; otherwise, a NoMethodError will be raised.
235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 |
# File 'lib/cheri/builder/context.rb', line 235 def msend(m,sym,*r,&k) # ok for stack to be empty for this call, so no check raise Cheri.type_error(sym,Symbol) unless sym.instance_of?(Symbol) raise ArgumentError,"not a valid builder module: #{m}" unless (f = @f[m]) # substitute sym for alias, if any sym = @l[sym] if @l[sym] # check args against pending connections (see note at ConnectionMinder) @m.ck(sym,*r) # the factory _must_ be able to return a matching builder unless (b = f.builder(self,sym,*r,&k)) raise NoMethodError,"undefined method '#{sym}' for #{m}" end # run it res = b.run # connect it ctc(b) res end |
#nsend(m, ns, sym, *r, &k) ⇒ Object
call-seq:
ctx.nsend(builder_module, namespace_sym, sym, *args, &block) -> result
Version of send
intended for use by namespace objects. The supplied module’s factory must be able to resolve the symbol; otherwise, a NoMethodError will be raised. The resulting builder must supply a ns=
method; otherwise a TypeError will be raised.
260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 |
# File 'lib/cheri/builder/context.rb', line 260 def nsend(m,ns,sym,*r,&k) # ok for stack to be empty for this call, so no check raise Cheri.type_error(ns,Symbol) unless Symbol === ns raise Cheri.type_error(sym,Symbol) unless Symbol === sym raise ArgumentError,"not a valid builder module: #{m}" unless (f = @f[m]) # substitute sym for alias, if any sym = @l[sym] if @l[sym] # check args against pending connections (see note at ConnectionMinder) @m.ck(sym,*r) # the factory _must_ be able to return a matching builder unless (b = f.builder(self,sym,*r,&k)) raise NoMethodError,"undefined method '#{sym}' for #{m}" end raise Cheri.type_error(b,Markup) unless b.respond_to?(:ns=) b.ns = ns # run it res = b.run # connect it ctc(b) res end |
#pop ⇒ Object Also known as: pop_frame
349 350 351 352 353 354 |
# File 'lib/cheri/builder/context.rb', line 349 def pop f = @s.pop @m.po f @a.pop if f.any? f end |
#ppd(ctr, bldr, obj, sym, props = nil) ⇒ Object Also known as: prepared
call-seq:
prepared(connecter,builder,obj,sym,props=nil) -> true
ppd(connecter,builder,obj,sym,props=nil) -> true
Called by a Connecter (normally) from its prepare
method (normally) to indicate that it is prepared to connect obj
to the parent object hosted by builder
. The context will later call the connecter’s connect
method to perform the actual connection, provided the pending connection has not been invalidated. (A pending connection will be invalidated if obj
is passed as a parameter to a method or constructor invoked via Cheri; the assumption is that the receiver will have performed any connection necessary.)
Note that if you are using TypeConnecter (highly recommended where appropriate), this all takes place behind the scenes, so you don’t need to call this method directly.
619 620 621 622 623 624 625 |
# File 'lib/cheri/builder/context.rb', line 619 def ppd(ctr,bldr,obj,sym,props=nil) unless @ax @m.ppd(ctr,bldr,obj,sym,props) else @m.ppda(ctr,bldr,obj,sym,props) end end |
#props ⇒ Object
575 576 577 |
# File 'lib/cheri/builder/context.rb', line 575 def props @p end |
#push(f) ⇒ Object Also known as: push_frame
339 340 341 342 343 344 345 346 |
# File 'lib/cheri/builder/context.rb', line 339 def push(f) # TODO: more checks of f (frame/builder) raise ArgumentError,"invalid argument - can't push: #{f}" unless f @s << f @m.pu f (@a ||= []) << f if f.any? self end |
#reach ⇒ Object Also known as: reverse_each, reverse_each_frame
:yields: frame
371 372 373 |
# File 'lib/cheri/builder/context.rb', line 371 def reach #:yields: frame @s.each {|v| yield v } if block_given? end |
#run(b) ⇒ Object
call-seq:
ctx.run(builder) -> result of builder.run
TODO: this description sucks, rewrite.
Runs the specified builder. This method should be called if a builder was not created through the normal Cheri factory mechanism. This method should be called, rather than calling builder.run directly, to ensure that the builder’s object gets properly connected.
193 194 195 196 197 198 199 |
# File 'lib/cheri/builder/context.rb', line 193 def run(b) # run the builder res = b.run # connect it's object ctc(b) res end |
#send(sym, *r, &k) ⇒ Object
call-seq:
ctx.send(sym, *args, &block) -> matched?, result
Find and run a builder corresponding to sym
. If no match is found, try to consume sym
as a method on the topmost object on the stack. No exception is raised by this method if a match is not found (contrast with #msend), but normally an exception will subsequently be raised if this method returns to method_missing (the usual caller) empty-handed.
209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 |
# File 'lib/cheri/builder/context.rb', line 209 def send(sym,*r,&k) raise Cheri.type_error(sym,Symbol) unless sym.instance_of?(Symbol) # substitute sym for alias, if any sym = @l[sym] if @l[sym] # check args against pending connections (see note at ConnectionMinder) @m.ck(sym,*r) # first try for a builder matching sym if (b = bld(sym,*r,&k)) # run it res = b.run # connect it ctc(b) return true, res elsif @s.empty? return false, nil else # no matching builder, try to consume sym as a method csm(sym,*r,&k) end end |
#size ⇒ Object Also known as: length, stack_size
call-seq:
ctx.stack_size -> Fixnum
ctx.size -> Fixnum
ctx.length -> Fixnum
Returns the number of frames on the context stack.
383 384 385 |
# File 'lib/cheri/builder/context.rb', line 383 def size @s.length end |
#top ⇒ Object Also known as: peek
357 358 359 |
# File 'lib/cheri/builder/context.rb', line 357 def top @s.last end |
#tos(t) ⇒ Object Also known as: type_on_stack?
call-seq:
ctx.type_on_stack?(class_or_module) -> true/false
ctx.tos(class_or_module) -> true/false
Returns true if a stack frame matches the specified class_or_module, as evaluated by class_or_module === frame.
413 414 415 416 417 418 |
# File 'lib/cheri/builder/context.rb', line 413 def tos(t) @s.reverse_each do |f| return true if t === f end false end |