# -*- Tcl -*- package require nx package require nx::test nx::Class create M { :method mfoo {} {puts [self proc]} } nx::Class create M2 nx::Class create C C create c1 # # test mixin method # nx::test case mixin-method { ? {C info lookup method mixin} "::nsf::classes::nx::Class::mixin" ? {C mixin set M} ::M ? {C info precedence} "::nx::Class ::nx::Object" ? {C mixin get} "::M" ? {C info mixin classes} "::M" ? {c1 info precedence} "::M ::C ::nx::Object" ? {C mixin add M2} "::M2 ::M" ? {c1 info precedence} "::M2 ::M ::C ::nx::Object" ? {C mixin delete M2} "::M" ? {c1 info precedence} "::M ::C ::nx::Object" ? {C mixin delete M} "" ? {C info mixin classes} "" ? {C mixin set ::M} "::M" ? {C mixin clear} "::M" ? {C info mixin classes} "" ? {C mixin add ::M} "::M" ? {C mixin set {}} "" ? {C info mixin classes} "" } # # test nsf::mixin interface # nx::test case nsf-mixin { ? {::nsf::mixin C ::M} "::M" ? {C info mixin classes} "::M" ? {::nsf::mixin C ::M2} "::M2 ::M" ? {C info mixin classes} "::M2 ::M" ? {::nsf::mixin C ""} "" ? {C info mixin classes} "" } # # per-object mixins # nx::test case per-object-mixin { ? {c1 info precedence} "::C ::nx::Object" ? {c1 object mixin add M} ::M ? {::nsf::relation::get c1 object-mixin} ::M ? {catch {c1 object mixin UNKNOWN}} 1 ? {::nsf::relation::get c1 object-mixin} "::M" # add again the same mixin ? {c1 object mixin add M} {::M} ? {c1 info precedence} "::M ::C ::nx::Object" ? {c1 object mixin add M2} "::M2 ::M" ? {c1 info precedence} "::M2 ::M ::C ::nx::Object" ? {c1 object mixin delete M} "::M2" ? {c1 info precedence} "::M2 ::C ::nx::Object" ? {c1 object mixin delete M2} "" ? {c1 info precedence} "::C ::nx::Object" ? {c1 object mixin add M} {::M} ? {c1 info object mixin classes} {::M} ? {c1 object mixin clear} {::M} ? {c1 info object mixin classes} {} } # # adding, removing per-object mixins for classes through relation # "object-mixin" # nx::test case object-mixin-relation { ? {::nsf::relation::set C object-mixin M} ::M ? {C info precedence} "::M ::nx::Class ::nx::Object" ? {C info object mixin classes} "::M" ? {::nsf::relation::set C object-mixin ""} "" ? {C info precedence} "::nx::Class ::nx::Object" } # # adding, removing per-object mixins for classes through slot # "object-mixin" # # C object-mixin M # ? {C info precedence} "::M ::nx::Class ::nx::Object" # ? {C info object mixin classes} "::M" # C object-mixin "" # ? {C info precedence} "::nx::Class ::nx::Object" # # add and remove object mixin for classes via modifier "object" and # "mixin" # nx::test case class+mixin { ? {C object mixin set M} ::M ? {C info precedence} "::M ::nx::Class ::nx::Object" ? {C info object mixin classes} "::M" ? {C object mixin set ""} "" ? {C info precedence} "::nx::Class ::nx::Object" } # # add and remove object mixin for classes via object mixin add # nx::test case class+mixin-add { ? {C object mixin add M} ::M ? {C info precedence} "::M ::nx::Class ::nx::Object" ? {C info object mixin classes} "::M" ? {C object mixin set ""} "" ? {C info precedence} "::nx::Class ::nx::Object" ? {C object mixin add M} ::M ? {C info precedence} "::M ::nx::Class ::nx::Object" ? {::nsf::relation::get C object-mixin} ::M ? {catch {C object mixin add UNKNOWN}} 1 ? {::nsf::relation::get C object-mixin} "::M" ? {C object mixin set ""} "" ? {C info precedence} "::nx::Class ::nx::Object" ? {C object mixin set M} ::M ? {C info precedence} "::M ::nx::Class ::nx::Object" # forwarder with get ? {C object mixin get} "::M" } nx::test case mixin-add { nx::Class create M1 { :method mfoo {} {puts [current method]} } nx::Class create M11 nx::Class create C1 ? {C1 info lookup method mixin} "::nsf::classes::nx::Class::mixin" C1 object mixin set M1 ? {C1 info precedence} "::M1 ::nx::Class ::nx::Object" C1 create c11 ? {c11 info precedence} "::C1 ::nx::Object" C1 object mixin add M11 ? {C1 info precedence} "::M11 ::M1 ::nx::Class ::nx::Object" Object create o -object-mixin M1 ? {o info precedence} "::M1 ::nx::Object" nx::Class create O O object mixin set M1 ? {O info precedence} "::M1 ::nx::Class ::nx::Object" nx::Class create O -object-mixin M1 ? {O info precedence} "::M1 ::nx::Class ::nx::Object" } nx::test case filter-relation { nx::Class create CC { :public method filterA args {next} :public method filterB args {next} :public object method filterC args {next} :create cc { :public object method filterD args {next} } } ? {::nsf::relation::get cc object-filter} "" ? {cc info object filter methods} "" ? {::nsf::relation::set cc object-filter filterA} filterA ? {cc info object filter methods} "filterA" ? {cc object filter set filterB} "filterB" ? {::nsf::relation::get cc object-filter} "filterB" ? {cc info object filter methods} "filterB" ? {cc object filter add filterD} "filterD filterB" ? {::nsf::relation::get cc object-filter} "filterD filterB" ? {cc info object filter methods} "filterD filterB" ? {cc object filter delete filterB} "filterD" ? {::nsf::relation::get cc object-filter} "filterD" ? {cc info object filter methods} "filterD" ? {catch {::nsf::relation::set cc object-filter UNKNOWN}} 1 ? {::nsf::relation::get cc object-filter} "filterD" ? {cc info object filter methods} "filterD" ? {::nsf::relation::get CC object-filter} "" ? {CC info object filter methods} "" ? {::nsf::relation::set CC object-filter filterC} "filterC" ? {::nsf::relation::get CC object-filter} "filterC" ? {CC info object filter methods} "filterC" ? {CC object filter clear} "filterC" ? {::nsf::relation::get CC object-filter} "" ? {CC info object filter methods} "" ? {::nsf::relation::get CC class-filter} "" ? {CC info filter methods} "" ? {::nsf::relation::set CC class-filter filterA} "filterA" ? {::nsf::relation::get CC class-filter} "filterA" ? {CC info filter methods} "filterA" ? {CC filter add filterB} "filterB filterA" ? {::nsf::relation::get CC class-filter} "filterB filterA" ? {CC info filter methods} "filterB filterA" ? {CC filter delete filterA} "filterB" ? {::nsf::relation::get CC class-filter} "filterB" ? {CC info filter methods} "filterB" ? {catch {::nsf::relation::set CC class-filter UNKNOWN}} 1 ? {::nsf::relation::get CC class-filter} "filterB" ? {CC info filter methods} "filterB" ? {CC filter clear} "filterB" ? {::nsf::relation::get CC class-filter} "" ? {CC info filter methods} "" } nx::test configure -count 3 nx::test case "filter-and-creation" { nx::Class create Foo { :method myfilter {args} { set i [::incr ::count] set s [self] set m [current calledmethod] #puts stderr "$i: $s.$m" #puts stderr "$i: procsearch before [$s procsearch info]" set r [next] #puts stderr "$i: $s.$m got ($r)" #puts stderr "$i: $s.$m procsearch after [$s info lookup method info]" return $r } # method for testing next to non-existing shadowed method :public method baz {} {next} } ? {Foo create ob} ::ob # make sure, no unknown handler exists #? {::ob info lookup method unknown} "::nsf::classes::nx::Object::unknown" ? {::ob info lookup method unknown} "" ? {ob bar} {::ob: unable to dispatch method 'bar'} ? {ob baz} {} # define a global unknown handler ::nx::Object protected method unknown {m args} { error "[::nsf::current object]: unable to dispatch method '$m'" } ? {ob bar} {::ob: unable to dispatch method 'bar'} ? {ob baz} {} Foo filter set myfilter # create through filter ? {Foo create ob} ::ob # unknown through filter ? {ob bar1} {::ob: unable to dispatch method 'bar1'} ? {ob baz} {} # deactivate nx unknown handler in case it exists ::nx::Object method unknown {} {} # create through filter ? {Foo create ob2} ::ob2 # unknown through filter ? {ob2 bar2} {::ob2: unable to dispatch method 'bar2'} ? {ob2 baz} {} # create with filter ? {Foo create ob3 -object-filter myfilter} ::ob3 } nx::test configure -count 1 # # Test the next-path with just intrinsic classes in cases where a # method handle is used for method dispatch # nx::test case intrinsic+method-handles { nx::Class create A {:public method foo {} {return "A [next]"}} nx::Class create B -superclass A {:public method foo {} {return "B [next]"}} nx::Class create C -superclass B {:public method foo {} {return "C [next]"}} C create c1 ? {c1 foo} "C B A " ? {c1 [C info method definitionhandle foo]} "C B A " ? {c1 [B info method definitionhandle foo]} "B A " ? {c1 [A info method definitionhandle foo]} "A " # we expect same results via dispatch with fully qualified names ? {nsf::dispatch c1 foo} "C B A " ? {nsf::dispatch c1 [C info method definitionhandle foo]} "C B A " ? {nsf::dispatch c1 [B info method definitionhandle foo]} "B A " ? {nsf::dispatch c1 [A info method definitionhandle foo]} "A " # # check, whether the context of "my -local" is correct # A public method bar {} {nsf::my -local foo} B public method bar {} {nsf::my -local foo} C public method bar {} {nsf::my -local foo} ? {c1 bar} "C B A " ? {c1 [C info method definitionhandle bar]} "C B A " ? {c1 [B info method definitionhandle bar]} "B A " ? {c1 [A info method definitionhandle bar]} "A " } # # Test the next-path with mixin classes in cases where a # method handle is used for method dispatch # nx::test case mixins+method-handles { # # Just mixin classes # nx::Class create A {:public method foo {} {return "A [next]"}} nx::Class create B {:public method foo {} {return "B [next]"}} nx::Class create C {:public method foo {} {return "C [next]"}} nx::Class create X -mixin {C B A} X create c1 ? {c1 foo} "C B A " ? {c1 [C info method definitionhandle foo]} "C B A " ? {c1 [B info method definitionhandle foo]} "B A " ? {c1 [A info method definitionhandle foo]} "A " # # Intrinsic classes and mixin classes # nx::Class create Y {:public method foo {} {return "Y [next]"}} nx::Class create Z -superclass Y {:public method foo {} {return "Z [next]"}} Z create c1 -object-mixin {C B A} ? {c1 foo} "C B A Z Y " ? {c1 [C info method definitionhandle foo]} "C B A Z Y " ? {c1 [B info method definitionhandle foo]} "B A Z Y " ? {c1 [A info method definitionhandle foo]} "A Z Y " ? {c1 [Z info method definitionhandle foo]} "Z Y " ? {c1 [Y info method definitionhandle foo]} "Y " # # check, whether the context of "my -local" is correct # A public method bar {} {nsf::my -local foo} B public method bar {} {nsf::my -local foo} C public method bar {} {nsf::my -local foo} Y public method bar {} {nsf::my -local foo} Z public method bar {} {nsf::my -local foo} ? {c1 bar} "C B A Z Y " ? {c1 [C info method definitionhandle bar]} "C B A Z Y " ? {c1 [B info method definitionhandle bar]} "B A Z Y " ? {c1 [A info method definitionhandle bar]} "A Z Y " ? {c1 [Z info method definitionhandle bar]} "Z Y " ? {c1 [Y info method definitionhandle bar]} "Y " } # # Test the next-path with mixin classes in cases where a # method handle is used for method dispatch # nx::test case mixins+method-handles+intrinsic { # # Just mixin classes # nx::Class create A {:public method foo {} {return "A [next]"}} nx::Class create B {:public method foo {} {return "B [next]"}} nx::Class create C {:public method foo {} {return "C [next]"}} nx::Class create X -mixin {C B A} { :public method foo {} {return "X [next]"} } X create c1 ? {c1 foo} "C B A X " ? {nsf::dispatch c1 -intrinsic foo} "X " # # Intrinsic classes and mixin classes # nx::Class create Y {:public method foo {} {return "Y [next]"}} nx::Class create Z -superclass Y {:public method foo {} {return "Z [next]"}} Z create c1 -object-mixin {C B A} ? {c1 foo} "C B A Z Y " ? {nsf::dispatch c1 -intrinsic foo} "Z Y " # # check, whether the context of "my -intrinsic" is correct # A public method bar {} {nsf::my -intrinsic foo} B public method bar {} {nsf::my -intrinsic foo} C public method bar {} {nsf::my -intrinsic foo} Y public method bar {} {nsf::my -intrinsic foo} Z public method bar {} {nsf::my -intrinsic foo} ? {c1 info precedence} "::C ::B ::A ::Z ::Y ::nx::Object" ? {c1 bar} "Z Y " ? {c1 [C info method definitionhandle bar]} "Z Y " ? {c1 [B info method definitionhandle bar]} "Z Y " ? {c1 [A info method definitionhandle bar]} "Z Y " ? {c1 [Z info method definitionhandle bar]} "Z Y " ? {c1 [Y info method definitionhandle bar]} "Z Y " # # check, whether the context of "nsf::dispatch [self] -intrinsic" is correct # A public method bar {} {nsf::dispatch [self] -intrinsic foo} B public method bar {} {nsf::dispatch [self] -intrinsic foo} C public method bar {} {nsf::dispatch [self] -intrinsic foo} Y public method bar {} {nsf::dispatch [self] -intrinsic foo} Z public method bar {} {nsf::dispatch [self] -intrinsic foo} ? {c1 bar} "Z Y " ? {c1 [C info method definitionhandle bar]} "Z Y " ? {c1 [B info method definitionhandle bar]} "Z Y " ? {c1 [A info method definitionhandle bar]} "Z Y " ? {c1 [Z info method definitionhandle bar]} "Z Y " ? {c1 [Y info method definitionhandle bar]} "Z Y " } # # Test filter guards (define filter and guard separtely) # nx::test case filter-guard-separately { # # Define a room with occupancy and methods for entering and leaving # nx::Class create Room { :property name :variable occupancy:integer 0 :public method enter {name} {incr ::occupancy} :public method leave {name} {incr ::occupancy -1} # # We are interested, what happens with the room, so we define a # logging filter.... # :method loggingFilter args { lappend ::_ [current calledmethod] next } # # ... and we register it. # :filter add loggingFilter } set ::_ {} ? {Room create r} ::r r enter Uwe r leave Uwe r configure -name "Office" ? {set ::_} "__objectparameter init enter leave configure" # # Hmm, we not so much interested on all these calls. Just the # "enter" and "leave" operations are fine. We could have certainly # as well mixin for these two methods, but the guards are more # general since the can as well trigger on arbitrary patterns. # Room filter guard loggingFilter { [current calledmethod] in {enter leave} } r destroy set ::_ {} ? {Room create r} ::r r enter Uwe r leave Uwe r configure -name "Office" ? {set ::_} "enter leave" r destroy # Now we define a subclass DangerRoom, which refines the filter by # logging into a "dangerRoomLog". We want here entries for all # operations. set ::_ {} set ::dangerRoomLog {} nx::Class create DangerRoom -superclass Room { :method loggingFilter args { lappend ::dangerRoomLog [current calledmethod] next } :filter add loggingFilter } ? {DangerRoom create d} ::d d enter Uwe d leave Uwe d configure -name "Safe Room" ? {set ::_} "enter leave" ? {expr [llength $::dangerRoomLog] > 2} 1 d destroy } # # Test filter guards (define filter together with guard) # nx::test case filter-guard-separately { # # Define a room with occupancy and methods for entering and leaving # nx::Class create Room { :property name :variable occupancy:integer 0 :public method enter {name} {incr ::occupancy} :public method leave {name} {incr ::occupancy -1} # # We are interested, what happens with the room, so we define a # logging filter.... # :method loggingFilter args { lappend ::_ [current calledmethod] next } # # ... and we register it together with a guard. # :filter add {loggingFilter { [current calledmethod] in {enter leave} }} } set ::_ {} ? {Room create r} ::r r enter Uwe r leave Uwe r configure -name "Office" ? {set ::_} "enter leave" # Now we define a subclass DangerRoom, which refines the filter by # logging into a "dangerRoomLog". We want here entries for all # operations. set ::_ {} set ::dangerRoomLog {} nx::Class create DangerRoom -superclass Room { :method loggingFilter args { lappend ::dangerRoomLog [current calledmethod] next } :filter add loggingFilter } ? {DangerRoom create d} ::d d enter Uwe d leave Uwe d configure -name "Safe Room" ? {set ::_} "enter leave" ? {expr [llength $::dangerRoomLog] > 2} 1 d destroy } # # Local variables: # mode: tcl # tcl-indent-level: 2 # indent-tabs-mode: nil # End: