domingo, 14 de agosto de 2016

Smart Mobile Studio - Variant

Variant data type

The Variant data type provides a flexible general purpose data type. It can hold basically anything. You can use variants when interacting with native javascript objects or external libraries.

We can also use variants in external classes. The External classes are the glue between Smart Pascal and the javascript world. It's important to say these external classes are case sensitive, in fact, the external code is only interfaced – this is not a real class, but an external class. This means that we only need the definition while the implementation is done by the external JavaScript code.

The Variants are treated as "unknown" by the smart pascal codegen. Variants are primarily used to transport native values and objects between methods dealing with javascript objects or browser objects. So Smart Mobile Studio compiler deals with variants as unknown values. Use it with care - there are potentials for run time errors and poor code clarity when using variants. For instance, the compiler does not check if a method of an object actually exists.

this

In this example, I'm going to define an external global variable. Note that, external variables must be declared in global scope. We're going to use this in global context. The this keyword behaves differently in JavaScript compared to other language. In Object Oriented languages, like smart pascal, the this keyword refers to the current instance of the class (aka Self).

In JavaScript the value of this is determined mostly by the invocation context of function and where it is called. Se, we can interface with native javascript objects with object pascal and create powerful components.

We can declare this variable using two ways:

{ using external variable }
var this external "this" : variant;
 { or using external function }
function this: variant; external 'this' property;

then we can use this external variable, such as:

procedure HandleCallback(a: variant);
begin
 console.log(a);
end;

procedure InvokeByVariant;
var mSrc: variant;
begin
mSrc := this.document;

(* invoke our method *)
HandleCallback(mSrc);
end;

Variant parameter

Let's suppose we have a method which looks like:

procedure HandleCallback(data: variant);
begin
  //
end;

In the sample code, it remains unclear the argument data. I'd call them a better name because that gives you more of a chance of understanding at a glance what your code means. You can customize it, so the method looks like this now:

type
 TJSONData = Variant;

procedure HandleCallback(data: TJSONData);
begin
//
end;

Literal object using the keyword Class

I'd like to create a JS literal object in smart pascal, like this:

var object1 = {
  "child" : {
    "Callback" : function () {
       console.log(this);
       return this;
    }
  }
};

As has been said: variants are treated as "unknown" by the smart pascal codegen. The smart compiler does no check if a method f.i. actually exists! Use it with care.






var object1 := CLASS
  that: variant = class external;

  child = class
    Callback := function(): variant
    begin
     console.log(this);
     Result := this;
    end;
  end;
END;

procedure CallMe(unknown: variant);
begin
  unknown.Name := "MyName";
  unknown.Age  := 102;
  unknown.Callback(unknown);
end;

CallMe(Object1.child);

CallMe(CLASS
  Callback := lambda
    console.log('this is a test');
  end;
END);

JS Emitted:

function CallMe(unknown) {
 unknown.Name = "MyName";
 unknown.Age = 102;
 unknown.Callback(unknown);
};

var object1 = {
 "child" : {
 "Callback" : function () {
    var Result = undefined;
    console.log(this);
    Result = this;
    return Result
 }
 }
 ,"that" : {
 }
};
CallMe(object1.child);
CallMe({
 "Callback" : function () {
 console.log("this is a test");
 }
});   

Array of Array of Integer

Now I'd like an object such as: {"values":[[0,100],[1000,200],[2000,300],[3000,400],[4000,500]] }

I've got weird result using this approach:

var preloaded = CLASS
  values : array [0..4] of array [0..1] of Integer = [ [0, 100],[1000, 200],[2000, 300],[3000, 400],[4000, 500] ];
END;

console.log(JSON.stringify(preloaded));
{"values":[[4000,500],[1000,200],[2000,300],[3000,400],[4000,500]]}

an easy workaround would be defining a local variable like this:

var valor : array [0..4] of array [0..1] of Integer = [ [0, 100],[1000, 200],[2000, 300],[3000, 400],[4000, 500] ];

var preloaded2 = CLASS
  values := valor;
END;

console.log(JSON.stringify(preloaded2));
{"values":[[0,100],[1000,200],[2000,300],[3000,400],[4000,500]]}

JSON Array Object

Now I would like to create a JSON object like this:

[{"id":1,"name":"Item#1"},{"id":2,"name":"Item#2"},{"id":3,"name":"Item#3"},{"id":4,"name":"Item#4"},{"id":5,"name":"Item#5"},{"id":6,"name":"Item#6"},{"id":7,"name":"Item#7"},{"id":8,"name":"Item#8"},{"id":9,"name":"Item#9"},{"id":10,"name":"Item#10"}]


var obj := CLASS
  that: variant = class external;

  child = class
    Callback := function(): variant
    begin
     Result := this;
    end;
  end;

  "JSONArray1": array of variant;

  "JSONArray2": array of variant;

  "JSONArray3": array of variant;

  "JSArray": array of variant;

  "name": string = "obj";

  "f"   : TFunc = function(): variant
          begin
            result := this.that;
          end;
END;



for var x:=1 to 10 do
begin
  obj.JSONArray1.add(CLASS
    name := "Item#" + IntToStr(x);
    id := x;
  END);
end;

Javascript Array

...and a simple javascript array list like:
["Item#1", "Item#2", "Item#3", "Item#4", "Item#5", "Item#6", "Item#7", "Item#8", "Item#9", "Item#10"]


for var x:=1 to 10 do
begin
  obj.JSArray.add("Item#" + IntToStr(x));
end;



Array of JSON Array

Playing more, with smart pascal, how about to create this weird array of array of objects?

[[{"age":1,"nome":"Nome#1"},{"cep":"35700-001","address":"Addr#1"},{"country":"Country 1","city":"City#1"}],[{"age":2,"nome":"Nome#2"},{"cep":"35700-002","address":"Addr#2"},{"country":"Country 2","city":"City#2"}],[{"age":3,"nome":"Nome#3"},{"cep":"35700-003","address":"Addr#3"},{"country":"Country 3","city":"City#3"}],[{"age":4,"nome":"Nome#4"},{"cep":"35700-004","address":"Addr#4"},{"country":"Country 4","city":"City#4"}],[{"age":5,"nome":"Nome#5"},{"cep":"35700-005","address":"Addr#5"},{"country":"Country 5","city":"City#5"}],[{"age":6,"nome":"Nome#6"},{"cep":"35700-006","address":"Addr#6"},{"country":"Country 6","city":"City#6"}],[{"age":7,"nome":"Nome#7"},{"cep":"35700-007","address":"Addr#7"},{"country":"Country 7","city":"City#7"}],[{"age":8,"nome":"Nome#8"},{"cep":"35700-008","address":"Addr#8"},{"country":"Country 8","city":"City#8"}],[{"age":9,"nome":"Nome#9"},{"cep":"35700-009","address":"Addr#9"},{"country":"Country 9","city":"City#9"}],[{"age":10,"nome":"Nome#10"},{"cep":"35700-0010","address":"Addr#10"},{"country":"Country 10","city":"City#10"}]]

In my experiments, 
for var x:=1 to 10 do
begin
  var objeto :=
    [CLASS
      nome   : string  = "Nome#" + IntToStr(x);
      age    : integer =  + x;
    END,
    CLASS
      address: string  = "Addr#" + IntToStr(x);
      cep    : variant = '35700-00'+ IntToStr(x);
    END,
    CLASS
      city   : string  = "City#" + IntToStr(x);
      country: string  = "Country " + IntToStr(x);
    END];
obj.jsonarray2.push(objeto);
end;

 The codegen generated weird pusha javascript instruction. I had to replace to push to get working.

var obj = null,
 x$32 = 0;
var objeto = [null,null,null];
/// anonymous TClassSymbol
/// anonymous TClassSymbol
TApplication.InitApp(Self);
obj = {
 "that" : {
 }
 ,"name" : "obj"
 ,"JSONArray3" : []
 ,"JSONArray2" : []
 ,"JSONArray1" : []
 ,"JSArray" : []
 ,"f" : function () {
 var Result = undefined;
 Result = this.that;
 return Result
 }
 ,"child" : {
 "Callback" : function () {
    var Result = undefined;
    Result = this;
    return Result
 }
 }
};
for(x$32=1;x$32<=10;x$32++) {
 /// anonymous TClassSymbol
 /// anonymous TClassSymbol
 /// anonymous TClassSymbol
 objeto = [{
 "age" : x$32
 ,"nome" : "Nome#"+x$32.toString()
 }, {
 "cep" : "35700-00"+x$32.toString()
 ,"address" : "Addr#"+x$32.toString()
 }, {
 "country" : "Country "+x$32.toString()
 ,"city" : "City#"+x$32.toString()
 }];
 obj.JSONArray2.pusha(objeto.slice(0));
}

to be continued...

It would be handy in SMS, in this instance, to have a kind of TDataModule - collection of data stores. How to use in-memory data stores in Smart. This TDataModule should have agnostic functions like findById, findByName, etc..
  
I ended up spending a decent amount of time trying different things to get this TW3DataSet  as in memory dataStore. To save some headaches, I decided to create my own data store, it’s ugly, unfinished, I find it more flexible to use it through the units. I don't know if the right stack, anyway, stay tuned.

Nenhum comentário:

Postar um comentário