[NEW] Servoy Tutorials

Home for older / inactive topics

Re: [NEW] Servoy Tutorials

Postby GaryDotzlaw » Tue Nov 26, 2013 8:53 pm

Thanks for the feedback.

We were looking for a solution to get code-completion working for the object's prototype; if you add functions to the prototype Servoy will not see them in code complete.

So, I agree, this works fine. I don't need anything special; Servoy can code complete everything in the constructor shown below including Person.fullName() (no JSDoc warnings either). I don't need any @constructor tag.
Code: Select all
function Person (firstName, lastName, title){
    this.firstName = firstName;
    this.lastName = lastName;
    this.title = title;
    this.fullName = function(){
        return this.firstName + " " + this.lastName;
    }
}


However, if you take the function and put it on the prototype for the object, then Servoy cannot see it (no code complete and you get JSDoc warning), but it still works fine. You have to do it in the prototype like this:
Code: Select all
var Person_proto = function(){
    Person.prototype = {
        fullName : function(){
            return this.firstName + " " + this.lastName;
        },
        fullNameTitle : function(){
            return this.fullName() + ", " + this.title;
        }
    }
}();


As well, Servoy cannot see functions that are in a Revealing Module pattern, an object constructor that hides private variables / functions and only exposes certain things to the external world, like an api. In this case, although the functions are in the object constructor, Servoy does not see the "type" or "getInfo" functions.
Code: Select all
function Fruit2(name, color, rating, price) {
    var _name = name,
        _color = color,
        _rating = rating,
        _price = price,
        type = function () {
            return 'I am a ' + _color + ' ' + _name;
        },
        getInfo = function(){
            return 'Rating: ' + _rating + ', Price: ' + _price
        };
        // Servoy does not see the public functions exposed here
        return {
            type: type,
            getInfo: getInfo
        };
}


I think the JSDoc @constructor tag is used more for documentation (let others know what it is or maybe auto-generate API docs). I don't think Servoy does anything with it.
Gary Dotzlaw
Dotzlaw Consulting
dotzlaw.com
Version: 7.3.0 - build 2018
Win 7, 64
User avatar
GaryDotzlaw
 
Posts: 38
Joined: Fri Jun 17, 2011 10:00 pm

Re: [NEW] Servoy Tutorials

Postby david » Tue Nov 26, 2013 9:18 pm

GaryDotzlaw wrote:The tutorial on Using an Object as a Cache demonstrates how to load the entire foundset one-time, store it, and retrieve it, whenever you need it. This further reduces load tremendously. It contains all the methods you need to get started and can easily be extended to cache more than just foundsets.


You can add "pseudo" data broadcasting functionality to cached objects. Add serialize(), save() and get() methods to store/retrieve the object, and fire whenever the object is accessed or changed. This allows you to share a cached object around to all clients.

We use this to keep our slickgrid component data up-to-date across all clients. A cached object maintains a list of "dirty" records by client ID. Any action on a client slickgrid checks this object for changes from other clients and updates the client-side datastore with just the changes. Client CRUD operations write to the cached object for other clients to pick up as needed.

This is how we get high performance (million+ records) grids in Servoy web client that stay updated. Same technique can be used for any client-side component (ie, jquery calendar, etc).

A couple of catches: this does not actively "push" to all clients like you're used to with Servoy's data broadcasting. And record locking is something you have to manage yourself (ie, timestamps). So some tradeoffs. When you start coding this functionality yourself you gain a new appreciation for how much complexity Servoy's foundsets implement.
David Workman, Kabootit

Image
Everything you need to build great apps with Servoy
User avatar
david
 
Posts: 1727
Joined: Thu Apr 24, 2003 4:18 pm
Location: Washington, D.C.

Re: [NEW] Servoy Tutorials

Postby david » Tue Nov 26, 2013 9:23 pm

GaryDotzlaw wrote:I think the JSDoc @constructor tag is used more for documentation (let others know what it is or maybe auto-generate API docs). I don't think Servoy does anything with it.


https://wiki.servoy.com/display/SERV61/Function :wink:

(edit)
Just to be clear, Servoy does do something with the @constructor jsdoc tag: "Specifies the function that creates an object's prototype." So no need to add methods on the prototype anymore.

Way back I argued against the use of jsdoc tags for more than just documentation (I think it came up over the "@AllowToRunInFind" tag) specifically because of the potential that people wouldn't naturally assume that some tags do something. But in the end, it's better than separate system for IDE/compiler/parser directives.
Last edited by david on Tue Nov 26, 2013 9:52 pm, edited 7 times in total.
David Workman, Kabootit

Image
Everything you need to build great apps with Servoy
User avatar
david
 
Posts: 1727
Joined: Thu Apr 24, 2003 4:18 pm
Location: Washington, D.C.

Re: [NEW] Servoy Tutorials

Postby david » Tue Nov 26, 2013 9:28 pm

And then just use this wherever you need code completion:

Code: Select all
/** @type {SomeConstructor} */


In this example, variable "res" gets code completion based on the constructor function:

Code: Select all
for (var i = 0; i < map[type].length; i++) {
   /** @type {Resolution} */
   var res = map[type][i]
   if (res instanceof Resolution) {
      if (res.name == _resizeSelected) {
         item = menu.addCheckBox(res.getName())
         item.selected = true
      }
      else {
         item = menu.addMenuItem(res.getName())
      }
      
      item.setMethod(ACTION_resize)
      item.methodArguments = [res.name,res.getDimHoriz(),res.getDimVert()]
   }
}


It's a pretty slick feature.
David Workman, Kabootit

Image
Everything you need to build great apps with Servoy
User avatar
david
 
Posts: 1727
Joined: Thu Apr 24, 2003 4:18 pm
Location: Washington, D.C.

Re: [NEW] Servoy Tutorials

Postby david » Tue Nov 26, 2013 9:32 pm

And I bet if you tag a function/property in a constructor with "@private" (possibly "@ignore" too?) it wouldn't show up in code completion.
David Workman, Kabootit

Image
Everything you need to build great apps with Servoy
User avatar
david
 
Posts: 1727
Joined: Thu Apr 24, 2003 4:18 pm
Location: Washington, D.C.

Re: [NEW] Servoy Tutorials

Postby GaryDotzlaw » Wed Nov 27, 2013 12:22 am

Okay, thanks David. I will have to ponder your comments over a whiskey.
Gary Dotzlaw
Dotzlaw Consulting
dotzlaw.com
Version: 7.3.0 - build 2018
Win 7, 64
User avatar
GaryDotzlaw
 
Posts: 38
Joined: Fri Jun 17, 2011 10:00 pm

Re: [NEW] Servoy Tutorials

Postby david » Wed Nov 27, 2013 12:57 am

GaryDotzlaw wrote:Okay, thanks David. I will have to ponder your comments over a whiskey.


Another proven programming technique :D
David Workman, Kabootit

Image
Everything you need to build great apps with Servoy
User avatar
david
 
Posts: 1727
Joined: Thu Apr 24, 2003 4:18 pm
Location: Washington, D.C.

Re: [NEW] Servoy Tutorials

Postby GaryDotzlaw » Wed Nov 27, 2013 1:08 am

David,

Perhaps we will both need a whiskey and discuss it offline. Its a very complicated subject, that's for sure.

What we are trying to do, in Sebastian's example, is to put the functions on the prototype of his Person constructor. We want to do this so that when we create a lot of person objects using the constructor, we are not consuming memory by having a copy of the functions in every object we create. So, in the example we have as follows:

Code: Select all
// The constructor
function Person (firstName, lastName, title){
     this.firstName = firstName;
    this.lastName = lastName;
    this.title = title;
}


Next we add the functions to the prototype like this:

Code: Select all
// Adding to the prototype
var Person_proto = function(){
    Person.prototype = {
        fullName : function(){
            return this.firstName + " " + this.lastName;
        },
        fullNameTitle : function(){
            return this.fullName() + ", " + this.title;
        }
    }
}();


Then we create a new person object using the Person constructor like this:

Code: Select all
var person = new Person('Some','Buddy','JavaScript Servoy Blogger');
application.output(person.fullNameTitle()); //Some Buddy, JavaScript Servoy Blogger


Now Servoy gives us a JSDoc warning at the application.output() statement; it does not see the method "fullNameTitle()" because it is on the prototype of the Person constructor (no code completion for it either). However, look what we see if we do a breakpoint at this line:

Image

Notice that Person, the constructor, has the prototype with the two functions, and that person, the object we created, does not. This is what we want. When we create 10,000 person objects using the Person constructor, there is only one copy of the two functions in memory, and every person object has access to it. This is classical inheritance and what Sebastian wanted to do in his scenario.

On the other hand, the link (https://wiki.servoy.com/display/SERV61/Function) to the docs shows something different, I believe.

I can try to re-write the prototype in Sebastian's example using the suggested approach there:

Code: Select all
function Person_proto(firstName, lastName, title){
    this.fullName = function(){
      return this.firstName + " " + this.lastName;
   };
   this.fullNameTitle = function(){
      return this.fullName() + ", " + this.title;
   };
   Person.apply(this, arguments);
}
Person_proto.prototype = new Person();


and then do this:

Code: Select all
var oPerson2 = new Person_proto('SomeOther','Buddy','JavaScript Servoy Blogger');
application.output(oPerson2.fullNameTitle()); //SomeOther Buddy, JavaScript Servoy Blogger


It looks the same on the surface (I get the same output), but look at what I see if I set a breakpoint again:
<edit> Forgot to mention that this approach works great with code completion and no JSDoc warnings (just like the final approach shown at the end)

Image

Now the functions are on the person object we created and not on the Person constructor. That means you have 10,000 copies of the functions in memory, definitely not what Sebastian wanted. The functions are not on the Person constructor, but rather on the Person_proto. All this approach is doing is prototype chaining; its copying the Person constructor and making a new constructor called Person_proto, augmenting it with two functions, and preserving the prototype chain back to Person. It looks identical to just doing this:

Code: Select all
function Person (firstName, lastName, title){
   this.firstName = firstName;
    this.lastName = lastName;
    this.title = title;
   this.fullName = function(){
      return this.firstName + " " + this.lastName;
   };
   this.fullNameTitle = function(){
      return this.fullName() + ", " + this.title;
   };
}


Anyway, I still don't know how to get Servoy to see the prototype functions using the classical inheritance approach (first image). Clearly it is there in the Expressions tab, but only when I am running in the client and pause at a breakpoint. I think most editors look for the "prototype" keyword and the resolve it that way.

Again, I think we should probably take this offline. It could be we are misunderstanding each other and really need that drink first.
Gary Dotzlaw
Dotzlaw Consulting
dotzlaw.com
Version: 7.3.0 - build 2018
Win 7, 64
User avatar
GaryDotzlaw
 
Posts: 38
Joined: Fri Jun 17, 2011 10:00 pm

Re: [NEW] Servoy Tutorials

Postby pbakker » Wed Nov 27, 2013 10:11 am

Hi Gary,

Great write-ups, tnx for sharing.

Servoy support the following:
Code: Select all
/**
* @constructor
* @extends {personProto}
*/
function Person() {

}

function personProto() {
  function privateMember(){}

  this.name = function(){}
  /**
   * @protected
   */
  this.protectedMember = function(){}
}

var initPerson = (function(){
   Person.prototype = new personProto()
   Person.prototype.constructor = Person //Just some extra code to properly set the constructor property, needed for some code constructions
}())

function test() {
   var x = new Person()
   x.name() //CodeCompleting for .name() and no builder markers
}


This will create one instance of the .name() function, shared between all instances of Person. The trick however is when the constructor takes arguments that should also be applied to the prototype. Then you need to do personProto.apply(this, arguments) as first thing inside the Person constructor function. The big downside of this is that all the logic defined inside the personProto function is applied against the Person instance, which means that new instances get created of all functions defined inside personProto, which sort of defies the whole purpose.

So, if you have want to have shared instances of all the function objects from the prototype AND you need to also apply constructor logic, you're best off making it three layers: Person > personProtoLogic > personProtoFunctions (or something like that).

Note: the reason to do the "personProto.apply(this, arguments)" call first thing inside the Person constructor is because otherwise if inside Person you override methods already defined inside personProto, they will get overwritten by the "personProto.apply(this, arguments)" call.

As for encapsulation: on public members of constructor functions you can set the @protected JSDoc tag, which will make the member only visible inside subclasses and not to the outside world.

As you already noticed, we don't support the Revealing Module pattern currently. Please file a feature request for that if you deem it important.

We also don't support proper CodeCompletion/builder markers when using prototyping to alter objects, either through setting the prototype property with an Object or adding/overwriting members on the prototype object. Please vote for the cases related to this in our support system: SVY-5532

As for the suggestion to load big FoundSets into memory in one go: I think your reasoning is a bit flawed: FoundSets load PK's and only get the rest of the record info when needed. So if you get a FS and then get the last record, a lot of PK's will be loaded into memory and the full records at the end of the FS. If you would instead loop through the entire FS and get all the records, Servoy will start removing record info of records at the beginning of the FS when your loop progresses, to preserve memory usage. So, if you want to go for optimal performance caching-wise, you're better of using a SQL/QueryBuilder statement to get the entire set of data as JSDataSet. Offcourse, if it's a million records, you need to make sure there's enough memory to deal with that.

Paul
pbakker
 
Posts: 2822
Joined: Wed Oct 01, 2003 8:12 pm
Location: Amsterdam, the Netherlands

Re: [NEW] Servoy Tutorials

Postby david » Wed Nov 27, 2013 11:02 am

GaryDotzlaw wrote:What we are trying to do, in Sebastian's example, is to put the functions on the prototype of his Person constructor. We want to do this so that when we create a lot of person objects using the constructor, we are not consuming memory by having a copy of the functions in every object we create.


Ah crap, inheritance. You are absolutely correct, I completely missed your point. And now I see what you are trying to solve. So a moment to properly reflect on my embarrassment............ :oops:

I started playing around with options and made it half-way through Paul's approach but couldn't figure it out (I'm not surprised now seeing his code). Then I started poking around with this (link1, link2) approach and I think it may be a winner:

Code: Select all
/**
* @properties={typeid:35,uuid:"9E900714-C455-4156-BA3D-57285C38BB47",variableType:-4}
*/
var Line = {
   // constructor
    create: function (x1, y1, x2, y2) {
       // type expression with instance and prototype items to get full code completion on instantiated objects
       /**@type {{x1:Number,y1:Number,x2:Number,y2:Number, length:function(),seed:Number}} */
        var line = Object.create(this);
        // instance
        line.x1 = x1;
        line.y1 = y1;
        line.x2 = x2;
        line.y2 = y2;
        return line;
    },
   // prototype
    length: function () {
        var dx = this.x2 - this.x1;
        var dy = this.y2 - this.y1;
        return Math.sqrt(dx * dx + dy * dy);
    },
   // prototype
   seed: 50
};

/**
* @properties={typeid:24,uuid:"8B81F0CA-B902-4291-861B-AC7513FC54B2"}
*/
function test() {
   
   // init objects
   var obj1 = Line.create(0,0,0,100)
   var obj2 = Line.create(0,100,0,100)
   var obj3 = Line.create(100,0,0,0)
   
   application.output("\nTest 1")
   application.output("obj1: " + obj1.seed) // expect 50
   application.output("obj2: " + obj2.seed) // expect 50
   application.output("obj3: " + obj3.seed) // expect 50
   
   // override prototype property "seed" for obj1
   obj1.seed = 100
   application.output("\nTest 2")
   application.output("obj1: " + obj1.seed) // expect 100
   application.output("obj2: " + obj2.seed) // expect 50
   application.output("obj3: " + obj3.seed) // expect 50
   
   // change prototype property "seed"
   Line.seed = 0
   application.output("\nTest 3")
   application.output("obj1: " + obj1.seed) // expect 100
   application.output("obj2: " + obj2.seed) // expect 0
   application.output("obj3: " + obj3.seed) // expect 0
   
   // create new prototype property (code completion doesn't work and get warning markers)
   Line.added = "I was added to prototype"
   application.output("\nTest 4")
   application.output("obj1: " + obj1.added) // expect "I was added to prototype"
   application.output("obj2: " + obj2.added) // expect "I was added to prototype"
   application.output("obj3: " + obj3.added) // expect "I was added to prototype"
   
   // create new prototype function (code completion doesn't work and get warning markers)
   Line.addition = function () {
      return this.x1 + this.x2 + this.y1 + this.y2
   }
   application.output("\nTest 5")
   application.output("obj1: " + obj1.addition()) // expect 100
   application.output("obj2: " + obj2.addition()) // expect 200
   application.output("obj3: " + obj3.addition()) // expect 100
   
}


It doesn't follow Servoy's conventions for @constructor and @protected (don't add protected items to the type expression declaration instead) and you create object instances without the "new" approach. And as expected code completion doesn't work for:

We also don't support proper CodeCompletion/builder markers when using prototyping to alter objects, either through setting the prototype property with an Object or adding/overwriting members on the prototype object. Please vote for the cases related to this in our support system: SVY-5532


Otherwise, I like it for its clarity and code completion on initial defined items. Be interested to see if it passes your object memory test. And Paul, thoughts?
David Workman, Kabootit

Image
Everything you need to build great apps with Servoy
User avatar
david
 
Posts: 1727
Joined: Thu Apr 24, 2003 4:18 pm
Location: Washington, D.C.

Re: [NEW] Servoy Tutorials

Postby rgansevles » Wed Nov 27, 2013 12:44 pm

ptalbot wrote:if you cache getSize() it will return 200 for a foundset that can have millions of records.
So your loop will stop at 200 and not iterate upon the whole foundset... quite a performance enhancement though! ;)

You should do
var iMax = databaseManager.getFoundsetCount($fs);
to get the real number of records if you want that cached...


This is true,
Gary,

If people use the sample code on the tutorial blindly and take the foundset.getSize() outside of the loop they will get strange results as Patrick mentioned.

Note that databaseManager.getFoundsetCount($fs) is also fairly expensive (it runs a select-count query).

Can you make this more explicit in the tutorial to prevent common mistakes?


Thanks,

Rob
Rob Gansevles
Servoy
User avatar
rgansevles
 
Posts: 1927
Joined: Wed Nov 15, 2006 6:17 pm
Location: Amersfoort, NL

Re: [NEW] Servoy Tutorials

Postby rgansevles » Wed Nov 27, 2013 12:49 pm

On 4. Servoy lookups

The duplicate find as you mentioned should never be needed.
Code: Select all
if (fs.find() || fs.find()) {

If the first find() returned false, the second should also.

If you can show this in a small solution then please file a case in our support system: https://support.servoy.com/

Rob
Rob Gansevles
Servoy
User avatar
rgansevles
 
Posts: 1927
Joined: Wed Nov 15, 2006 6:17 pm
Location: Amersfoort, NL

Re: [NEW] Servoy Tutorials

Postby GaryDotzlaw » Wed Nov 27, 2013 4:18 pm

Paul, thank you for the feedback.
  • I will digest the inheritance information and post my findings.
  • I will also take a look at the loop performance using a dataset and see how it compares to the entire foundset.

Rob, thank you for the feedback.
  • I agree databaseManager.getFoundsetCount() is an expensive operation. However, the performance test results show that using it to preload a large foundset before a loop, rather than stepping through them in 200 record chunks, is still the way to go.
  • The sample code in my tutorial is correct; it preloads the entire foundset before the loop, and uses a cache. Patrick was referring to the discussion in this forum thread(I believe). I have modified the thread to make it clear that one needs to use the entire foundset in the loop.
  • Regarding the find(), I would agree with you in theory. However, after running into this difficult to locate bug several times in different solutions, I am convinced it happens, have modified my Eclipse Javascript template to include it, and it will stay. I do not have a sample solution to demonstrate the issue, as I do not know why it happens when it happens. I originally ran into the problem several years ago and was shown the workaround by a Servoy engineer.

Thanks again everyone for your feedback.
Gary Dotzlaw
Dotzlaw Consulting
dotzlaw.com
Version: 7.3.0 - build 2018
Win 7, 64
User avatar
GaryDotzlaw
 
Posts: 38
Joined: Fri Jun 17, 2011 10:00 pm

Re: [NEW] Servoy Tutorials

Postby GaryDotzlaw » Thu Nov 28, 2013 7:49 am

Paul, the performance tip to use a dataset for the loop instead of the foundset was a good one; it is 2x as fast. That makes it 68x as fast as looping through records in 200 record chunks. Here is what the results look like (110,000 records):

Image

Regarding the Object examples for code completion; Sorry, I am at a loss; it shouldn't be this hard. I'm creating objects and the prototype using conventional methods (classical inheritance), and they work properly, except for code completion and build markers. Yes, you get code completion on the "methods" with your objects, but there are other problems.

Paul, I was unable to get your example to work at all. It throws a parse error at

Code: Select all
Person.prototype.constructor = Person


I must be doing something wrong, because I had to modify your example to get it to run.

Code: Select all
/**
* @constructor
* @extends {personProto}
*/
function Person(first, last, title) {
   this.first = first;
   this.last = last;
   this.title = title;
}

function personProto() {
     function privateMember(){}

   this.fullName = function(){
      return this.firstName + " " + this.lastName;
   };
   this.fullNameTitle = function(){
      return this.fullName() + ", " + this.title;
   };

   /**
   * @protected
   */
   this.protectedMember = function(){
   };
}

var initPerson = (function(){
   Person.prototype = new personProto();
   Person.prototype.constructor = Person;
})() // <-- I think I had to move the bracket here from your example as well

// and then later
var x = new Person('SomeOther','Buddy','JavaScript Servoy Blogger');
application.output(x.fullName());
// outputs undefined undefined


If I set a breakpoint and inspect the object, here is what I see:

Image

The object looks correct, as the methods are on the prototype of Person, as I showed in my approach earlier in the thread. However, I could not get the prototype method to output the name of the person, just unidefined (I tried to follow your explanation but was unable to produce the required result) You will have to show us how to do that. I have to say, I suspect your approach is far more complicated then the classical inheritance approach I used earlier.

David, you do not get build markers, but your object does not look right when I inspect it. There is no prototype showing up at all, so although obj1.length outputs the right value, I have no clue where it is coming from. Your "seed" property for sure is on the object, and not the prototype. I ran the example you provided as is:

Image

Paul, let me know if you can finish off my example using your approach. I would like to see it working.

Thanks guys,
Gary Dotzlaw
Dotzlaw Consulting
dotzlaw.com
Version: 7.3.0 - build 2018
Win 7, 64
User avatar
GaryDotzlaw
 
Posts: 38
Joined: Fri Jun 17, 2011 10:00 pm

Re: [NEW] Servoy Tutorials

Postby Harjo » Thu Nov 28, 2013 10:22 am

Gary,

are you really doing these test, every time again, after a fresh restart?
Because when you do the first test, Servoy itself does also a bunch of caching.
Last edited by Harjo on Thu Nov 28, 2013 10:28 am, edited 1 time in total.
Harjo Kompagnie
ServoyCamp
Servoy Certified Developer
Servoy Valued Professional
SAN Developer
Harjo
 
Posts: 4321
Joined: Fri Apr 25, 2003 11:42 pm
Location: DEN HAM OV, The Netherlands

PreviousNext

Return to Archive

Who is online

Users browsing this forum: No registered users and 1 guest