Can datatype redefine getter/setter?

I have a lot of string properties, which need custom but similar Getters and Setters.

Currently I'm generating Getter and Setter in class generation code but that expands class definition.

Is there a way to specify custom datatype which would generate custom getters and setters?

  • 0
  • 0
  • 91
  • 15
  • 2

Answers

I could imagine inheriting standard data types for a customized data type and then 

adding methods LogicalToStorage and StorageToLogical  similar as LogicalToObs, ....
So your manipulations happen on the way from Global to the local variable.

Additional Parameters eventually may control the generated code.

It's just a vague idea.

Here's what I've coded.

Property:

Class Test.String Extends %String
{

ClassMethod StorageToLogical(%val As %String) As %String [ CodeMode = objectgenerator ]
{
	set property = $g(%member)
	do %code.WriteLine($c(9) _ "Quit """ _ property _ """")
	quit $$$OK
}

ClassMethod LogicalToStorage(%val As %String) As %String [ CodeMode = objectgenerator ]
{
	set property = $g(%member)
	do %code.WriteLine($c(9) _ "Quit """ _ property _ """")
	quit $$$OK
}

}

Class:

Class Test.Obj Extends %RegisteredObject
{

Property prop As Test.String;

}

However reference to property does not return anything (expected to return property name):

w obj.prop
zw obj.prop
>""

Any ideas?

Turns out it's much easier than I thought:

Class Test.String Extends %String
{

ClassMethod Get() As %String [ CodeMode = objectgenerator ]
{
    do %code.WriteLine($c(9) _ "Quit $g(^Test.String, 0)")
    quit $$$OK
}

ClassMethod Set(%val As %String) As %String [ CodeMode = objectgenerator ]
{
    do %code.WriteLine($c(9) _ "Set ^Test.String = %val")
    quit $$$OK
}

}

Example:

set obj = ##class(Test.Obj).%New()
write obj.prop
>0
set obj.prop=1
write obj.prop
>1

Okay, how do I make this work with methods?

On property get/set I need to call instance method.

I've tried this:

Class Test.String Extends %String
{

Method Get() As %String [ CodeMode = objectgenerator ]
{
    do %code.WriteLine($c(9) _ "Quit ..Test()")
    quit $$$OK
}

}

And class:

/// set obj = ##class(Test.Obj).%New()
/// w obj.prop
Class Test.Obj Extends %RegisteredObject
{

Property prop As Test.String;

Method Test()
{
    quit $random(100)
}

}

But it fails to compile.

instead of 

do %code.WriteLine($c(9) _ "Quit ..Test()")

use

do %code.WriteLine($c(9) _ "Quit zTest()")

It worked for me best

Can you share the code please?

Changed .. to z and compilation fails for me.

It doesn't work for me too, there is an error

#1056: Missing subscript : '}'

Try this

Class Test.String Extends %String
{

Method Get() As %String CodeMode = objectgenerator, NoContext ]
{
  %code.WriteLine("  q $this.Test()")
  q $$$OK
}

}

Thank you!

NoContext helped. What does it mean?

This works too btw:

d %code.WriteLine("  q ..Test()")

NoContext helped. What does it mean?

NoContext
With the same success it is possible to specify NoContext for the class:

Class Test.String Extends %String [ NoContext ]
{

Method Get() As %String CodeMode = objectgenerator ]
{
  %code.WriteLine("  q $this.Test()")
  q $$$OK
}

}
This works too btw:

It doesn't work for me:

MPP5376 : Method or Property 'Test' does not exist in this class.

That's because first code generation is run during Test.String compilation, which does not have Test method.

Add something like this at the beginning:

#; don't generate any code if it not for a property
quit:%mode="method" $$$OK

Then it's better:

Method Get() As %String CodeMode = objectgenerator, NoContext]
{
  d:%mode="propertymethod" %code.WriteLine("  q ..Test()")
  q $$$OK
}

Sorry,  I had more changes. 
My approach in details

Class DC.String Extends %String  {
ClassMethod Get() As %String [ CodeMode = objectgenerator ]
{    do %code.WriteLine($c(9) _ "Quit $g(^Test.String, 0)")
    quit $$$OK  }
ClassMethod Set(%val As %String) [ CodeMode = objectgenerator ] {
    do %code.WriteLine($c(9) _ "Set ^Test.String = %val")
    do %code.WriteLine($c(9) _ "do zTest()")
    quit $$$OK }

/// this generates Method propTest()
/// but satisfies the Compiler for this class
ClassMethod Test() { quit  ;; from Data definition }
}

compiles this routine:

 ;DC.String.1
 ;(C)InterSystems, generated for class DC.String. Do NOT edit. 10/04/2019 12:00:42PM
 ;;42345370;DC.String
 ;
zGet() public  Quit $g(^Test.String, 0) }
zSet(%val) public Set ^Test.String = %val
      do zTest() }
zTest() public  quit  ;; from Data definition }

and the  using class

Class DC.StringTest Extends %RegisteredObject  {
Property prop As DC.String;
Method Test()
 b   quit ;; from using Class }
}

compiled as 

 ;DC.StringTest.1
 ;(C)InterSystems, generated for class DC.StringTest. Do NOT edit. 10/04/2019 12:02:35PM
 ;;49614632;DC.StringTest
 ;
%NormalizeObject() public {
If '$system.CLS.GetModified() Quit 1
If m%prop Set:i%prop'="" i%prop=(..propNormalize(i%prop))
Quit 1 }
%ValidateObject(force=0,checkserial=1) public {
set sc=1
If '$system.CLS.GetModified() Quit sc
If m%prop Set iv=..prop If iv'="" Set rc=(..propIsValid(iv)) If ('rc) Set sc=$$EmbedErr^%occSystem(sc,rc,5802,"prop",iv)
Quit sc }
zTest() public {
 b   quit ;; from using Class
}
zpropGet() public {
Quit $g(^Test.String, 0) }
zpropSet(%val) public {
Set ^Test.String = %val
do zTest() }
zpropTest() public {
 quit  ;; from Data definition
}

and the test

 SAMPLES>s obj=##class(DC.StringTest).%New()
SAMPLES>set obj.prop=123
 b   quit ;; from using Class
 ^
<BREAK>zTest+1^DC.StringTest.1
SAMPLES 3d1>

Well, the code generator can be tricky.
It's not one of my favorites. But sometimes you have no choice.

OK.
StorageTo../..ToStorage only works in persistent classes where you move content from/to globals.
No chance without storage. 

It doesn't get called in Registered Classes and not when the object wasn't saved.
see my test with %Persistent

set obj=##class(Test.String).%New()
write 
obj.prop    ;nothing loaded yet

set obj.prop=77 write obj.prop   ; unchanged as neither stored nor loaded
77
do obj.%Save(),obj,%Reload() ; force reload to trigger StorageToLogical
write obj.prop  ; and here we go
prop


This proves that there is limited use of the approach.

Why do not use PropertyClass, so, you can generate own getter and setter for any property in a class?

Can you please provide an example?

I'm aware only of %XML.PropertyParameters but there's nothing but parameters.

Sorry, usually I use it to add custom methods to properties. Did not not, that it doesn't work for such methods as Set/Get. But you can add any other method.