Read-only Properties

It is often nec­es­sary for a cer­tain prop­er­ty in a giv­en script to be a read-only one. This can­not be achieved by the com­mon prop­er­ties, pro­posed by Lin­go, so we usu­al­ly use acces­sors meth­ods by the means of which we manip­u­late these prop­er­ties in the way we like. This works out, of course. But if we want (for our con­ve­nience) to use syn­tax of the fol­low­ing type:

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

we can­not do it by the use of acces­sors meth­ods in Lingo.
Let us imag­ine Bul­let script has two prop­er­ties — #type and #speed. Sup­pos­ed­ly #type prop­er­ty is read-only and can be defined only through Bul­let con­struc­tor. Assume that val­ues for #speed can be changed, but can­not be less than 0. We could use the (undoc­u­ment­ed) pos­si­bil­i­ty to inher­it a Javascript func­tion in which #type and #speed prop­er­ties are defined. We use the fol­low­ing methods:

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

So, let us return to Bul­let 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 func­tion, defined in a sep­a­rate movie scipt:

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

This is an aux­il­iary func­tion which cre­ates and returns an instance of Bul­let­Base, for in Lin­go we can­not use direct­ly the code below:

ancestor = new BulletBase( atype )

This is Bul­let­Base 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: Read­on­ly Prop­er­ties (361 downloads) 

Try the next things in the mes­sage 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 tech­nique described above leads to worse per­for­mance com­pared to the usage of acces­sors meth­ods in Lingo.

 
Comments

This might be triv­ial, but just in case: an alter­na­tive approach to achieve about the same result with lin­go only is to go com­plete­ly with­out dot syn­tax for getting/setting prop­er­ties, but instead exclu­siv­ly use brack­et syn­tax. Then your script can over­write the default imple­men­ta­tions of getAt/setAt to cre­ate such cus­tom 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