Index: TODO =================================================================== diff -u -rdcee5f2f7bac79f3fb2b966d68360cdfcef8002a -r76571cce036b3d02efa2943454acfed2bc2f3351 --- TODO (.../TODO) (revision dcee5f2f7bac79f3fb2b966d68360cdfcef8002a) +++ TODO (.../TODO) (revision 76571cce036b3d02efa2943454acfed2bc2f3351) @@ -3029,7 +3029,7 @@ - reduce verbosity - reduce scope of variables -- reamed ObjectParameterSlot attribute from nosetter => accessor +- renamed ObjectParameterSlot attribute from nosetter => accessor (positive formulation) - nsf.c: make sure to always initialize variables @@ -3047,18 +3047,37 @@ - generalized "nsf::object::initialized" to nsf::object::property objectName initialized|class|rootmetaclass|rootclass|slotcontainer +- nx: factor out method createFromParameterSpec +- method variable: + * check default value + * added shortcut, when no slot object is needed + * extended regression test TODO: + - provide warning, when method variable is a noop + (e.g. no value provided and no accessor is wanted) + - call user defined setter in object parameters? + - set value always in per-object variable method? probably yes. + - set value always in per-object attribute method? probably yes. + - should we change interface for default value in attribute? + probably not, same interface is used in methodparameters as well + - we could rename "attribute" to "property" to make distinction between + "variable" and "attribute/property" as well ("a property is a + variable with accessors") + - maybe use (position == -1) instead of (objectparameter == false) to save common vars - optimization of plain variable in per-object case - cleanup variable/attribute - testing variable/attribute - maybe change default createBootstrapAttributeSlots ::nx::Attribute {accessor true} -> false +#::nsf::var::exists ?-array? object varName +#::nsf::var::import object ?arg ...? +#::nsf::var::set ?-array? object varName ?value? +#::nsf::var::unset ?-array? object varName - - Revise callstack introspection/intercession, i.e., [current activelevel] vs. [current callinglevel] vs. uplevel()/upvar(): Index: library/nx/nx.tcl =================================================================== diff -u -rc6057c18970d5bc19fe0f1f760ef0d29898ecfdd -r76571cce036b3d02efa2943454acfed2bc2f3351 --- library/nx/nx.tcl (.../nx.tcl) (revision c6057c18970d5bc19fe0f1f760ef0d29898ecfdd) +++ library/nx/nx.tcl (.../nx.tcl) (revision 76571cce036b3d02efa2943454acfed2bc2f3351) @@ -736,23 +736,21 @@ } } - MetaSlot public class method createFromParameterSpec { - target - -per-object:switch + MetaSlot public class method parseParameterSpec { {-class ""} - {-initblock ""} {-defaultopts ""} - value + spec default:optional } { set opts $defaultopts - set colonPos [string first : $value] + set colonPos [string first : $spec] if {$colonPos == -1} { - set name $value + set name $spec + set parameterOptions "" } else { - set properties [string range $value [expr {$colonPos+1}] end] - set name [string range $value 0 [expr {$colonPos -1}]] - foreach property [split $properties ,] { + set parameterOptions [string range $spec [expr {$colonPos+1}] end] + set name [string range $spec 0 [expr {$colonPos -1}]] + foreach property [split $parameterOptions ,] { if {$property in [list "required" "convert" "substdefault" "noarg"]} { if {$property in "convert" } { set class [:requireClass ::nx::Attribute $class] @@ -787,7 +785,22 @@ if {$type eq "switch"} {error "switch is not allowed as type for object parameter $name"} lappend opts -type $type } + return [list $name $parameterOptions $class $opts] + } + MetaSlot public class method createFromParameterSpec { + target + -per-object:switch + {-class ""} + {-initblock ""} + {-defaultopts ""} + spec + default:optional + } { + + lassign [:parseParameterSpec -class $class -defaultopts $defaultopts $spec] \ + name parameterOptions class opts + if {[info exists default]} { lappend opts -default $default } @@ -1356,6 +1369,10 @@ ::nx::Attribute protected method checkInstVar {} { if {${:per-object} && [info exists :default] } { if {![::nsf::var::exists ${:domain} ${:name}]} { + set options [:getParameterOptions -withMultiplicity true] + if {[llength $options] > 0} { + ::nsf::is -complain [join $options ,] ${:default} + } ::nsf::var::set ${:domain} ${:name} ${:default} } } @@ -1572,52 +1589,76 @@ # Define method "attribute" for convenience ###################################################################### - Class method attribute {spec {-class ""} {initblock ""}} { - set r [::nx::MetaSlot createFromParameterSpec [::nsf::self] \ - -class $class -initblock $initblock {*}$spec] - if {$r ne ""} { - set o [::nsf::self] - ::nsf::method::property $o $r call-protected \ - [::nsf::object::dispatch $o __default_attribute_call_protection] - return $r - } - } + # Class method attribute {spec {-class ""} {initblock ""}} { + # set r [::nx::MetaSlot createFromParameterSpec [::nsf::self] \ + # -class $class -initblock $initblock {*}$spec] + # if {$r ne ""} { + # set o [::nsf::self] + # ::nsf::method::property $o $r call-protected \ + # [::nsf::object::dispatch $o __default_attribute_call_protection] + # return $r + # } + # } - Object method attribute {spec {-class ""} {initblock ""}} { - set r [::nx::MetaSlot createFromParameterSpec [::nsf::self] \ - -class $class -per-object -initblock $initblock {*}$spec] - if {$r ne ""} { - set o [::nsf::self] - ::nsf::method::property $o -per-object $r call-protected \ - [::nsf::object::dispatch $o __default_attribute_call_protection] - } - return $r - } + # Object method attribute {spec {-class ""} {initblock ""}} { + # set r [::nx::MetaSlot createFromParameterSpec [::nsf::self] \ + # -class $class -per-object -initblock $initblock {*}$spec] + # if {$r ne ""} { + # set o [::nsf::self] + # ::nsf::method::property $o -per-object $r call-protected \ + # [::nsf::object::dispatch $o __default_attribute_call_protection] + # } + # return $r + # } nx::Object method variable { - {-class ""} - {-initblock ""} - {-objectparameter false} - {-accessor false} - spec - default:optional - } { - set r [::nx::MetaSlot createFromParameterSpec [::nsf::self] \ - -per-object \ - -class $class \ - -initblock $initblock \ - -defaultopts [list -accessor $accessor -objectparameter $objectparameter] \ - $spec \ - {*}[expr {[info exists default] ? [list $default] : ""}]] - return $r + {-class ""} + {-initblock ""} + {-array:switch} + {-accessor:boolean false} + spec + value:optional + } { + # + # when do we need a slot + # currently: + # - when accessors are needed + # (serializer uses slot object to create accessors) + # in general: + # - when initblock is non empty + # + + #puts stderr "Object variable $spec accessor $accessor" + + if {$initblock eq "" && !$accessor} { + lassign [::nx::MetaSlot parseParameterSpec -class $class $spec] \ + name parameterOptions class opts + + if {[info exists value]} { + if {$parameterOptions ne ""} { + #puts stderr "::nsf::is $parameterOptions $value" + ::nsf::is -complain $parameterOptions $value + } else { + set name $spec + } + set :$name $value + } + return + } + return [::nx::MetaSlot createFromParameterSpec [::nsf::self] \ + -per-object \ + -class $class \ + -initblock $initblock \ + -defaultopts [list -accessor $accessor -objectparameter false] \ + $spec \ + {*}[expr {[info exists value] ? [list $value] : ""}]] } Object method attribute {spec {-class ""} {initblock ""}} { set r [[self] ::nsf::classes::nx::Object::variable \ -class $class \ -initblock $initblock \ -accessor true \ - -objectparameter true \ {*}$spec] return $r } @@ -1630,6 +1671,7 @@ spec default:optional } { + #puts stderr "Class variable $spec" set r [::nx::MetaSlot createFromParameterSpec [::nsf::self] \ -class $class \ -initblock $initblock \ Index: tests/parameters.test =================================================================== diff -u -rc6057c18970d5bc19fe0f1f760ef0d29898ecfdd -r76571cce036b3d02efa2943454acfed2bc2f3351 --- tests/parameters.test (.../parameters.test) (revision c6057c18970d5bc19fe0f1f760ef0d29898ecfdd) +++ tests/parameters.test (.../parameters.test) (revision 76571cce036b3d02efa2943454acfed2bc2f3351) @@ -1775,42 +1775,69 @@ [o info slots a] reconfigure ? {o eval {info exists :a}} 1 ? {o a} anothervalue +} - # - # Object parameters (specified e.g. via attributes) are defined to - # configure fresh objects (therefore, the definition is on the class - # level). Therefore, object-level object parameter do not fulfill - # this purpose, since they can only be defined, *after* the object - # is created. - # - # In general, object parameters have creational aspects (providing - # configurations for the object creation, such as e.g. defaults, and - # configurations) and object-lifetime aspects (valid through the - # lifetime of objects, such as e.g. setters/checkers). - # - # Object-level attributes cannot be used for the creational aspects +# +# Object parameters (specified e.g. via attributes) are defined to +# configure fresh objects (therefore, the definition is on the class +# level). Therefore, object-level object parameter do not fulfill +# this purpose, since they can only be defined, *after* the object +# is created. +# +# In general, object parameters have creational aspects (providing +# configurations for the object creation, such as e.g. defaults, and +# configurations) and object-lifetime aspects (valid through the +# lifetime of objects, such as e.g. setters/checkers). +# +# Object-level attributes cannot be used for the creational aspects # of object parameters. - # - # Strengths of object-level parameters: - # - same interface as class-level attributes - # - can use same meta-data mechanisms as for class-level attributes - # (e.g database types, attribute name in the database, persistence - # information, ...) +# +# Strengths of object-level parameters: +# - same interface as class-level attributes +# - can use same meta-data mechanisms as for class-level attributes +# (e.g database types, attribute name in the database, persistence +# information, ...) # - can use same setters/checkers as for class-level attributes - # - can use as well incremental as for class-level attributes - # - # Shortcomings of object-level parameters: - # - no nice introspection: - # "info parameter ...." is defined on cls, not on obj - # - default handling is not the same as for classes level attributes - # (we have already some special mechanisms to set instance - # attributes, if they do not exist) - # - object-level parameters cannot be used in a "configure" - # of the object, since configure allows the same signature - # as on object creation, all object parameters are cached - # on the class level - # - Since configure does not include object-level parameters, - # positional object level parameters do not make sense, since they - # cannot be called. - +# - can use as well incremental as for class-level attributes +# +# Shortcomings of object-level parameters: +# - no nice introspection: +# "info parameter ...." is defined on cls, not on obj +# - default handling is not the same as for classes level attributes +# (we have already some special mechanisms to set instance +# attributes, if they do not exist) +# - object-level parameters cannot be used in a "configure" +# of the object, since configure allows the same signature +# as on object creation, all object parameters are cached +# on the class level +# - Since configure does not include object-level parameters, +# positional object level parameters do not make sense, since they +# cannot be called. + +nx::Test case variable { + + nx::Object create ::enterprise { + # set 2 variables, one via variable, one via attribute + ? [list [self] variable captain1 "James Kirk"] "" + ? [list [self] attribute [list captain2 "Jean Luc"]] "::enterprise::captain2" + + # in both cases, we expect instance variables + ? [list [self] eval {set :captain1}] "James Kirk" + ? [list [self] eval {set :captain2}] "Jean Luc" + + # just for the attribute, we have accessors + ? [list [self] info lookup method captain1] "" + ? [list [self] info lookup method captain2] "::enterprise::captain2" + + # set variable with a value checker + ? [list [self] variable x1:int 1] "" + ? [list [self] attribute [list x2:int 2]] "::enterprise::x2" + + # set variable with a value checker and an invalid value + ? [list [self] variable y1:int a] {expected integer but got "a"} + ? [list [self] attribute [list y2:int b]] {expected integer but got "b"} + + # more tests, e.g. multiplicity and user-defined type + } + } \ No newline at end of file