Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Added checks in oometa to prevent errors for classes that define no metadata Added better handling of ensembles for cases of mixins and morphs Added an automatic rebuild of metadata prior to building dynamic methods for a class Added tests for these new behaviors in tool |
---|---|
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
6cda9ff384ba979bf460bb054ec012fb |
User & Date: | hypnotoad 2016-10-19 12:31:34 |
Context
2016-10-19
| ||
16:12 | Added a hook to ensure ancestors classes for every instanted object also cement their ensembles Added tests to ensure that ancestor ensembles are accessible from [next] and also that ensemble methods inherit in a method-like order check-in: f3b84e3415 user: tne tags: trunk | |
12:31 | Added checks in oometa to prevent errors for classes that define no metadata Added better handling of ensembles for cases of mixins and morphs Added an automatic rebuild of metadata prior to building dynamic methods for a class Added tests for these new behaviors in tool check-in: 6cda9ff384 user: hypnotoad tags: trunk | |
2016-10-18
| ||
15:02 | Added checks in oometa to prevent errors for classes that define no metadata Added better handling of ensembles for cases of mixins and morphs Added an automatic rebuild of metadata prior to building dynamic methods for a class Added tests for these new behaviors in tool check-in: 592f264a47 user: tne tags: odie | |
2016-10-17
| ||
16:44 | Cron: Added error handling OOdialect: Fixed tabs and indentation (no code change) oometa: Added a mechanism for frameworks to intercept and detect when the metadata for a class has changed tool: Delays ensemble creation until object creation. Utilizes the new metadata modified method from oometa to invalidate the method ensembles for classed and their decendents. check-in: 182b1361cf user: hypnotoad tags: trunk | |
Changes
Changes to modules/oometa/oometa.tcl.
︙ | ︙ | |||
110 111 112 113 114 115 116 | branchset { ::oo::meta::rebuild $class foreach {field value} [lindex $args end] { ::dict set ::oo::meta::local_property($class) {*}[lrange $args 0 end-1] [string trimright $field :]: $value } } leaf_add { | > | > > | 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | branchset { ::oo::meta::rebuild $class foreach {field value} [lindex $args end] { ::dict set ::oo::meta::local_property($class) {*}[lrange $args 0 end-1] [string trimright $field :]: $value } } leaf_add { if {[::info exists ::oo::meta::local_property($class)]} { set result [dict getnull $::oo::meta::local_property($class) {*}[lindex $args 0]] } ladd result {*}[lrange $args 1 end] dict set ::oo::meta::local_property($class) {*}[lindex $args 0] $result } leaf_remove { if {![::info exists ::oo::meta::local_property($class)]} return set result {} forearch element [dict getnull $::oo::meta::local_property($class) {*}[lindex $args 0]] { if { $element in [lrange $args 1 end]} continue lappend result $element } dict set ::oo::meta::local_property($class) {*}[lindex $args 0] $result } |
︙ | ︙ | |||
177 178 179 180 181 182 183 184 185 186 187 188 189 190 | foreach dclass $dirty_classes { foreach {cclass cancestors} [array get ::oo::meta::cached_hierarchy] { if {$dclass in $cancestors} { unset -nocomplain ::oo::meta::cached_property($cclass) unset -nocomplain ::oo::meta::cached_hierarchy($cclass) } } if {[dict getnull $::oo::meta::local_property($dclass) classinfo type:] eq "core"} { if {$dclass ni $::oo::meta::core_classes} { lappend ::oo::meta::core_classes $dclass } } } set dirty_classes {} | > | 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 | foreach dclass $dirty_classes { foreach {cclass cancestors} [array get ::oo::meta::cached_hierarchy] { if {$dclass in $cancestors} { unset -nocomplain ::oo::meta::cached_property($cclass) unset -nocomplain ::oo::meta::cached_hierarchy($cclass) } } if {![::info exists ::oo::meta::local_property($dclass)]} continue if {[dict getnull $::oo::meta::local_property($dclass) classinfo type:] eq "core"} { if {$dclass ni $::oo::meta::core_classes} { lappend ::oo::meta::core_classes $dclass } } } set dirty_classes {} |
︙ | ︙ |
Changes to modules/tool/ensemble.tcl.
︙ | ︙ | |||
15 16 17 18 19 20 21 | } if {$class ni $::tool::dirty_classes} { lappend ::tool::dirty_classes $class } } } | | < < < < < | | > | < | | > | < | < < > | | 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | } if {$class ni $::tool::dirty_classes} { lappend ::tool::dirty_classes $class } } } proc ::tool::ensemble_build_map args { set emap {} foreach thisclass $args { foreach {ensemble einfo} [::oo::meta::info $thisclass getnull method_ensemble] { foreach {submethod subinfo} $einfo { dict set emap $ensemble $submethod $subinfo } } } return $emap } proc ::tool::ensemble_methods emap { set result {} foreach {ensemble einfo} $emap { #set einfo [dict getnull $einfo method_ensemble $ensemble] set eswitch {} set default standard if {[dict exists $einfo default:]} { set emethodinfo [dict get $einfo default:] set arglist [lindex $emethodinfo 0] set realbody [lindex $emethodinfo 1] |
︙ | ︙ | |||
88 89 90 91 92 93 94 | set body [lindex [dict get $einfo _preamble:] 1] } else { set body {} } append body \n [list set methodlist $methodlist] append body \n "set code \[catch {switch -- \$method [list $eswitch]} result opts\]" append body \n {return -options $opts $result} | > > > > > > > > > > > > > > > > > > > > > > > > > > | | > < | 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 | set body [lindex [dict get $einfo _preamble:] 1] } else { set body {} } append body \n [list set methodlist $methodlist] append body \n "set code \[catch {switch -- \$method [list $eswitch]} result opts\]" append body \n {return -options $opts $result} append result \n [list method $ensemble {{method default} args} $body] } return $result } ### # topic: fb8d74e9c08db81ee6f1275dad4d7d6f ### proc ::tool::dynamic_object_ensembles {thisobject thisclass} { variable trace set ensembledict {} foreach dclass $::tool::dirty_classes { foreach {cclass cancestors} [array get ::oo::meta::cached_hierarchy] { if {$dclass in $cancestors} { unset -nocomplain ::tool::obj_ensemble_cache($cclass) } } } set ::tool::dirty_classes {} ### # Only go through the motions for classes that have a locally defined # ensemble method implementation ### if {[info exists ::tool::obj_ensemble_cache($thisclass)]} return set emap [::tool::ensemble_build_map $thisclass] set body [::tool::ensemble_methods $emap] oo::define $thisclass $body # Define a property for this ensemble for introspection foreach {ensemble einfo} $emap { ::oo::meta::info $thisclass set ensemble_methods $ensemble: [lsort -dictionary [dict keys $einfo]] } set ::tool::obj_ensemble_cache($thisclass) 1 } ### # topic: ec9ca249b75e2667ad5bcb2f7cd8c568 # title: Define an ensemble method for this agent ### ::proc ::tool::define::method {rawmethod args} { |
︙ | ︙ |
Changes to modules/tool/metaclass.tcl.
︙ | ︙ | |||
146 147 148 149 150 151 152 153 154 155 156 157 158 159 | return $result } ### # topic: a92cd258900010f656f4c6e7dbffae57 ### proc ::tool::dynamic_methods class { set metadata [::oo::meta::metadata $class] foreach command [info commands [namespace current]::dynamic_methods_*] { $command $class $metadata } } ### | > | 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 | return $result } ### # topic: a92cd258900010f656f4c6e7dbffae57 ### proc ::tool::dynamic_methods class { ::oo::meta::rebuild $class set metadata [::oo::meta::metadata $class] foreach command [info commands [namespace current]::dynamic_methods_*] { $command $class $metadata } } ### |
︙ | ︙ | |||
310 311 312 313 314 315 316 317 318 319 320 321 322 323 | # This class is inherited by all classes that have options. # ::tool::define ::tool::object { # Put MOACish stuff in here variable signals_pending create variable organs {} constructor args { my Config_merge [::tool::args_to_options {*}$args] } destructor {} | > | 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 | # This class is inherited by all classes that have options. # ::tool::define ::tool::object { # Put MOACish stuff in here variable signals_pending create variable organs {} variable mixins {} constructor args { my Config_merge [::tool::args_to_options {*}$args] } destructor {} |
︙ | ︙ | |||
449 450 451 452 453 454 455 | } } } set dat [dict getnull $public option_info] if {$integrate} { my meta rmerge [list option $dat] } | < > > > > > > | 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 | } } } set dat [dict getnull $public option_info] if {$integrate} { my meta rmerge [list option $dat] } my variable option_canonical array set option_canonical [dict getnull $public option_canonical] set dictargs {} foreach {var getcmd} [dict getnull $public option_default_command] { if {[dict getnull $dat $var class:] eq "organ"} { if {[my organ $var] ne {}} continue } if {[dict exists $config $var]} continue dict set dictargs $var [{*}[string map [list %field% $var %self% [namespace which my]] $getcmd]] } foreach {var value} [dict getnull $public option_default_value] { if {[dict getnull $dat $var class:] eq "organ"} { if {[my organ $var] ne {}} continue } if {[dict exists $config $var]} continue dict set dictargs $var $value } ### # Apply all inputs with special rules ### foreach {field val} $dictargs { |
︙ | ︙ | |||
484 485 486 487 488 489 490 | # Provide a default value for all options and # publically declared variables, and locks the # pipeline mutex to prevent signal processing # while the contructor is still running. # Note, by default an odie object will ignore # signals until a later call to <i>my lock remove pipeline</i> ### | | > > | > > > > > > > > | > > > > | 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 | # Provide a default value for all options and # publically declared variables, and locks the # pipeline mutex to prevent signal processing # while the contructor is still running. # Note, by default an odie object will ignore # signals until a later call to <i>my lock remove pipeline</i> ### method mixin args { ### # Mix in the class ### my variable mixins set mixins $args ::oo::objdefine [self] mixin {*}$args ### # Build a compsite map of all ensembles defined by the object's current # class as well as all of the classes being mixed in ### set emap [::tool::ensemble_build_map [::info object class [self]] {*}[lreverse $args]] set body [::tool::ensemble_methods $emap] oo::objdefine [self] $body foreach class $args { my ClassPublicApply $class } } method morph newclass { if {$newclass eq {}} return set class [string trimleft [info object class [self]]] set newclass [string trimleft $newclass :] if {[info command ::$newclass] eq {}} { error "Class $newclass does not exist" } if { $class ne $newclass } { my Morph_leave my variable mixins oo::objdefine [self] class ::${newclass} my graft class ::${newclass} # Reapply mixins my mixin {*}$mixins my InitializePublic my Morph_enter } } ### # Commands to perform as this object transitions out of the present class |
︙ | ︙ | |||
530 531 532 533 534 535 536 | return {} } if { $stub eq "all" } { return $organs } return [dict getnull $organs $stub] } | | < < < < < < < < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < | < < | 551 552 553 554 555 556 557 558 559 560 | return {} } if { $stub eq "all" } { return $organs } return [dict getnull $organs $stub] } } |
Changes to modules/tool/tool.test.
︙ | ︙ | |||
392 393 394 395 396 397 398 399 400 401 402 403 404 405 | test tool-option_class-002 {Test option class} { ObjectOptionTest5 organ master } GNDN test tool-option_class-003 {Test option class} { ObjectOptionTest5 <master> puts FOO } {puts FOO} # ------------------------------------------------------------------------- testsuiteCleanup # Local variables: # mode: tcl | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 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 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 | test tool-option_class-002 {Test option class} { ObjectOptionTest5 organ master } GNDN test tool-option_class-003 {Test option class} { ObjectOptionTest5 <master> puts FOO } {puts FOO} ### # Second round of testing # Make sure the various and sundry ways to generate # dynamic methods follow through morphs, mixins, # and class method definitions ### tool::class create WidgetClass { class_method unknown args { set tkpath [lindex $args 0] if {[string index $tkpath 0] eq "."} { set obj [my new $tkpath {*}[lrange $args 1 end]] $obj tkalias $tkpath return $tkpath } next {*}$args } constructor {TkPath args} { my variable hull set hull $TkPath my graft hull $TkPath } method tkalias tkname { set oldname $tkname my variable tkalias set tkalias $tkname set self [self] set hullwidget [::info object namespace $self]::tkwidget my graft tkwidget $hullwidget #rename ::$tkalias $hullwidget my graft hullwidget $hullwidget ::tool::object_rename [self] ::$tkalias #my Hull_Bind $tkname return $hullwidget } } test tool-class-method-001 {Test Tk style creator} { WidgetClass .foo .foo organ hull } {.foo} tool::class create WidgetNewClass { superclass WidgetClass } test tool-class-method-002 {Test Tk style creator inherited by morph} { WidgetNewClass .bar .bar organ hull } {.bar} tool::class create DummyClass { method i_am_here {} { return DummyClass } } tool::class create OrganClass { option db {class organ default ::noop} constructor args { my config set $args } } DummyClass create ::DbObj OrganClass create OrganObject db ::DbObj test tool-constructor-args-001 {Test that organs passed as options map correctly} { OrganObject organ db } {::DbObj} test tool-constructor-args-002 {Test that organs passed as options map correctly} { OrganObject cget db } {::DbObj} tool::object create MorphOrganObject#1 tool::object create MorphOrganObject#2 MorphOrganObject#2 graft db ::DbObj MorphOrganObject#1 morph OrganClass test tool-constructor-args-003 {Test that a default for an organ option is applied after a morph} { MorphOrganObject#1 organ db } {::noop} MorphOrganObject#2 morph OrganClass test tool-constructor-args-004 {Test that a default for an organ option is NOT applied if the graft exists following a morph} { MorphOrganObject#2 organ db } {::DbObj} tool::object create MorphOrganObject#3 tool::object create MorphOrganObject#4 MorphOrganObject#4 graft db ::DbObj MorphOrganObject#3 mixin OrganClass test tool-constructor-args-005 {Test that a default for an organ option is applied during a mixin} { MorphOrganObject#3 organ db } {::noop} MorphOrganObject#4 mixin OrganClass test tool-constructor-args-006 {Test that a default for an organ option is NOT applied if the graft exists during a mixin} { MorphOrganObject#4 organ db } {::DbObj} # ------------------------------------------------------------------------- testsuiteCleanup # Local variables: # mode: tcl |
︙ | ︙ |