Read-only Properties

It is often necessary for a certain property in a given script to be a read-only one. This cannot be achieved by the common properties, proposed by Lingo, so we usually use accessors methods by the means of which we manipulate these properties in the way we like. This works out, of course. But if we want (for our convenience) to use syntax of the following type:

myBullet = script( "Bullet" ).new( 5.45 )
myBullet.speed = 5200
put myBullet.speed
–5200

we cannot do it by the use of accessors methods in Lingo.
Let us imagine Bullet script has two properties – #type and #speed. Supposedly #type property is read-only and can be defined only through Bullet constructor. Assume that values for #speed can be changed, but cannot be less than 0. We could use the (undocumented) possibility to inherit a Javascript function in which #type and #speed properties are defined. We use the following methods:

YourFunctionName.prototype.__defineGetter__( "propertyname", function );
YourFunctionName.prototype.__defineSetter__( "propertyname", function );

So, let us return to Bullet script:

property ancestor
property container

on new( me, atype )
ancestor = getInstance( "BulletBase", atype )
container = list()
return me
end me

on report( me )
trace( "type:" && me.type )
trace( "speed:" && me.speed )
end report

getInstance() is a javascript function, defined in a separate movie scipt:

function getInstance( aClassname, args )
{
  var inst = new this[ aClassname ]( args ); 
  return inst;
}

This is an auxiliary function which creates and returns an instance of BulletBase, for in Lingo we cannot use directly the code below:

ancestor = new BulletBase( atype )

This is BulletBase function:

show code

function BulletBase( atype )
{
  var p = BulletBase.prototype;
  // SET DEFAULT VALUES
  if( atype == 5.45 || atype == 7.62 )
  {
    this._type  = atype;
  }
  else
  {
    _player.alert( "The property 'type' can accept only values 5.45 and 7.62" );
    _movie.halt();
  }
  this._speed = 0;
  
  // PROPERTIES
  
  // speed
  p.__defineGetter__( "speed",
  function()
    { 
      return this._speed;
    }
  );
  
  p.__defineSetter__( "speed", 
  function( n )
    { 
      if( ( n > 0 ) )
        { 
          this._speed = n;
        }
      else
        {
          _player.alert( "The property 'speed' can accept only values greater than 0." );
          _movie.halt();
        }
    }
  );
  
  // type
  p.__defineGetter__( "type",
  function()
    { 
      return this._type
    }
  );
} 


Here is the source code:

Readonly Properties (573 downloads)

Try the next things in the message window:

myBullet = script( "Bullet" ).new( 3.5 ) — "The property type can accept only values 5.45 and 7.62"
myBullet = script( "Bullet" ).new( 5.45 ) — OK
myBullet.type = 7.62 — "Script error: setting a property that has only a getter."
trace( myBullet.type ) — 5.4500
myBullet.speed = 1200 — OK
trace( myBullet.speed ) — 1200
myBullet.speed = -10 — "The property ‘speed’ can accept only values greater than 0."

Note: Please, have in mind that the technique described above leads to worse performance compared to the usage of accessors methods in Lingo.

 
Comments

This might be trivial, but just in case: an alternative approach to achieve about the same result with lingo only is to go completely without dot syntax for getting/setting properties, but instead exclusivly use bracket syntax. Then your script can overwrite the default implementations of getAt/setAt to create such custom getter/setter functions:

— parent script "Bullet"
property _speed
property _type

on new me, aType
if (aType<> 5.45 AND aType<>7.62) then
_player.alert( "The property ‘type’ can accept only values 5.45 and 7.62" )
_movie.halt()
end if
me._type = aType
return me
end

on setAt me, p, v
–put "setAt", p, v
case p of
#speed:
if (v<=0) then
_player.alert( "Error: The property ‘speed’ can accept only values greater than 0." )
_movie.halt()
end if
otherwise:
_player.alert( "Error: The property ‘"&p&"’ has no setter" )
_movie.halt()
end case
me.setProp("_"&p, v)
end

on getAt me, p
–put "getAt", p
return me.getProp("_"&p)
end

— test in message window
myBullet = script( "Bullet" ).new( 3.5 ) — "Error: The property ‘type’ can accept only values 5.45 and 7.62"
myBullet = script( "Bullet" ).new( 5.45 ) — OK
myBullet[#type] = 7.62 — "Error: The property ‘type’ has no setter"
trace( myBullet[#type] ) — 5.4500
myBullet[#speed] = 1200 — OK
trace( myBullet[#speed] ) — 1200
myBullet[#speed] = -10 — "Error: The property ‘speed’ can accept only values greater than 0."

Cheers,
Valentin

Leave a Reply