TIBET Logo

TIBET Essentials

Essentials

Welcome to the second tutorial in our "Hello World!" project series.

This tutorial builds on the TIBET Quickstart Guide so if you haven't worked through that tutorial do that first. It's ok...we'll be here when you get back.


First Steps

Before we dive into the next phase of development make sure your hello project is running.

Open a terminal, cd to your project home, and execute tibet start:

cd ${project_home}
tibet start

                                  ,`
                            __,~//`
   ,///,_            .~////////'`
  '///////,       //////''`
         '//,   ///'`
            '/_/'
              `
    ////////////////////     ///////////////////  ////
    `//'````````````///      `//'```````````````  '''
     /`              //       /'
    /                //      '/
   ,/____             /'    ,/_____
  /////////;;,,_      //   ,//////////;,_
              `'/,_   '/              `'///,_
                 `'/,_ /                   '//,
                    '/,/,                    '/_
                      `/,                     `/,
                        '                      `/
                                               '/
                                                /

1504056886785 [7] TDS TIBET Data Server v5.0.0-pre.11 (development)
...
...
...
1504056888242 [7] TDS hello 0.1.0 @ http://127.0.0.1:1407 (production build) (build)
1504056888242 [7] TDS hello 0.1.0 @ http://127.0.0.1:1407#?boot.profile=development (dev)


Next, load http://127.0.0.1:1407#?boot.profile=development in Chrome.

If you haven't changed anything since wrapping up the Quickstart Guide your should see:

Project Home Page

A Whole Lotta Hello


With our project running it's time to tackle the next step in our tutorial process.


Compiled Tags

Precision


When you need more precision in rendering your tags than you can get with templating, TIBET offers another approach to tag authoring using 'compiled tags'.

A 'compiled' tag in TIBET is a tag whose rendered markup is produced by JavaScript rather than an external template file. There's no separate "compiler" or "compile" step per se.

hello:now

To create a simple example of a compiled tag let's build another custom tag, this time one that outputs the current date/time when it renders. We'll call this tag <hello:now/>.

We could do this task using handlebars-style substitution syntax in a templated tag but for purposes of this tutorial we'll create a compiled tag instead.

Create a new compiled tag by using --dna compiledtag with the tibet type command:

$ tibet type hello:now --dna compiledtag

In response to this command TIBET will generate a tag type and associated test file, hot patching them into our application. No template file will be created since this is a compiled tag.

To keep things simple let's add our new tag to the <hello:app/> template, adjusting it so we get both <hello:world/> and <hello:now/> output in our home page.

Edit the APP.hello.app.xhtml template to say hello once and tell the time.

<div tibet:tag="hello:app">
    <hello:world/>
    <hello:now/>
</div>
~app_tags/APP.hello.app/APP.hello.app.xhtml


Save your template with the new hello:now tag and you should see:

hello:now first try

<hello:now/> version 0


What just happened?

We created a new compiled tag, added it to our app tag template, and we get a link identifying the tag location in our UI. That link is an indication our new type is working as expected. What we're seeing is the default implementation of a compiled tag's tagCompile method.

Click that <hello:now/> link:

hello:now link alert

Newly created compiled tags running outside of the Sherpa™ will alert() you to update the tag's tagCompile method, a simple way of helping you to work iteratively from definition through refinement. (In the Sherpa you'd be taken directly to that method for editing).

As the link alert suggests, let's refine the hello:now tagCompile method next.

tagCompile

TIBET is a true framework in the sense that TIBET code calls you, not the other way around. The tagCompile method is a perfect example.

When TIBET processes tags it invokes a number of methods on the types which support those tags. The tagCompile method is perhaps the most commonly used of those since it's the method responsible for providing content to render.

We'll start by taking a look at the tagCompile stub generated by tibet tag in the source file ~app_tags/APP.hello.now/APP.hello.now.js.

Open ~app_tags/APP.hello.now/APP.hello.now.js, our hello:now type's source:

/**
 * @type {APP.hello.now}
 * @summary A compiled tag which...
 */

//  ------------------------------------------------------------------------

TP.core.CompiledTag.defineSubtype('APP.hello:now');

//  ------------------------------------------------------------------------

APP.hello.now.Type.defineMethod('tagCompile',
function(aRequest) {

    /**
     * @method tagCompile
     * @synopsis Convert instances of the tag into their XHTML form.
     * @param {TP.sig.Request} aRequest A request containing the tag element
     *     to convert along with other optional processing parameters.
     * @returns {Element|Array<Element>} The element(s) to replace the inbound
     *     element with in the final DOM.
     */

    return this.callNextMethod();
});

//  ------------------------------------------------------------------------
//  end
//  ========================================================================
APP.hello.now.js


Since this is our first deep look at TIBET code let's take it a line at a time:

Line 1
TP.core.CompiledTag.defineSubtype('APP.hello:now');

Much like our earlier look at the hello:app source file, the first line in many TIBET files will be a line that invokes defineSubtype on a supertype, providing the name of a new subtype. In this case we create a new TP.core.CompiledTag subtype.

Line 2
APP.hello.now.Type.defineMethod('tagCompile',

We begin the actual implementation by defining a method on our new hello:now type to handle tagCompile messages.

TIBET methods are always defined using the defineMethod function. Where the method ends up depends on the object messaged.

Using method methods is mentioned in JavaScript: The Good Parts

In this case we want our tagCompile method to be a type method so we add a .Type qualifier to our tag name to target the type. If we wanted an instance method we'd add .Inst. If we wanted a 'local' method, a method unique to a single object, we leave off the 'Type' or 'Inst' qualifier and invoke defineMethod directly on the targeted instance.

Line 3
function(aRequest) {

Simple enough. With defineMethod's second parameter we're providing the method body, the function that will do the work of compiling the tag. Virtually all tag processing methods take a single parameter, a TIBET Request object, shown here.

Line 4
return this.callNextMethod();

Now things get a little more interesting.

TIBET is classically Object-Oriented. As a result we often need to invoke the supertype version of a method. In this case we're asking our stub to do just that, to call 'the next method' up the inheritance/lookup chain without requiring hard-coded references or duplicating the argument list (TIBET handles that automatically).

Since we subtyped TP.core.CompiledTag that type will be checked for tagCompile and the search will continue up through the type hierarchy until an implementation is found.

If you're used to C++, Java, or other JS libraries that statement should give you pause.

Many popular OO languages don't have type inheritance, they have 'static methods' which work more like TIBET's 'local methods' with respect to types. That's true for TypeScript, ECMA6/7, and most JavaScript OO implementations.

TIBET's OO infrastructure is modeled on Smalltalk, with a dash of Traits thrown in to support composition in a predictable, controllable fashion. The result is that TIBET lets types inherit methods and attributes, just like instances, a feature which enables TIBET's tag system to be more efficient.

But we digress....

Line 5
});

Close method body, close defineMethod parameter list, end statement.

Done.

With our review of the stub implementation complete our next task is to create a real one.

tagCompile v2

In the version of our tagCompile method below we've replaced our stub's callNextMethod() logic with a full implementation.

Note that there's no constraint on what the `tagCompile` method does, only that it return whatever element(s) form the replacement content.

Edit your version of APP.hello.now.js to include this implementation:

APP.hello.now.Type.defineMethod('tagCompile',
function(aRequest) {
    var elem;

    //  Get the initiating element.
    if (!TP.isElement(elem = aRequest.at('node'))) {
        return;
    }

    //  Set its content to the date.
    TP.nodeSetContent(elem, (new Date()).toString());

    //  Return the element.
    return elem;
});
APP.hello.now.js


Let's look at this chunk by chunk.

Get the initiating element

The first step is to access the node in the document representing our tag. The request parameter's at method can give us that by requesting the node property. We verify we get a valid element from the request using the TP.isElement() primitive:

// Get the initiating element.
if (!TP.isElement(elem = aRequest.at('node'))) {
    return;
}
Set the content of the element

Next we set the content of the element to be the String representation of a new Date:

// Set its content to the date.
TP.nodeSetContent(elem, (new Date()).toString());
Return the element to be rendered.

Finally, we return the node (typically an Element or DocumentFragment) to be rendered in place of the original node. In this case we return the original element since we don't need a replacement node, we just want to update the current element's content in place.

//  Return the element.
return elem;

Save these changes and our screen should now display:

hello:now implementation

<hello:now/>


Success!

Let's test! This time however, let's just test our new tag...

Change to an available terminal and enter tibet test APP.hello.now:

$ tibet test APP.hello.now
# Loading TIBET via PhantomJS 2.1.1 at July 15, 2017 at 16:44:18 MDT
# TIBET loaded in 4066 ms. Starting execution.
# Running Type tests for APP.hello.now
# TIBET starting test run
# 1 suite(s) found
1..1
#
# tibet test APP.hello.now.Type --suite='APP.hello:now suite'
ok - Is a TP.core.CompiledTag tag.
# pass: 1 total, 1 pass, 0 fail, 0 error, 0 skip, 0 todo, 0 only.
#
# PASS: 1 total, 1 pass, 0 fail, 0 error, 0 skip, 0 todo, 0 only.
# Running Inst tests for APP.hello.now
# TIBET starting test run
0..0
# PASS: 0 pass, 0 fail, 0 error, 0 skip, 0 todo.
# Running Local tests for APP.hello.now
# TIBET starting test run
0..0
# PASS: 0 pass, 0 fail, 0 error, 0 skip, 0 todo.

# Finished in 4270 ms w/TSH exec time of 204 ms.

Wow. That worked?! We just tested a specific type. But how?

Like virtually all of TIBET, TIBET's test harness isn't page-based...it's object-based. In TIBET all tests are associated with an object. The tests TIBET generates when we create a new type are associated with that type. When we run tibet test {typename} TIBET loads and uses reflection to run any tests it finds. By associating tests with objects you can keep your testing focused, improve cycle times, and support smarter forms of code coverage analysis.

NOTE that for this tutorial we're sticking with command-line testing via PhantomJS. TIBET tests also run in `karma` via the karma-tibet package, a fully-supported part of the TIBET ecosystem. TIBET tests can span everything from units to UI.

Recap

Using the tibet type command (with a --dna compiledtag option) we've created a new tag that renders based on a JavaScript method on the tag type, the tagCompile method.

Our current implementation simply outputs the current Date.now() value but complex logic is easy to support using this approach.

With the "look" in place, the next step is to add behavior ("feel") to our tag.


Tag Behavior

Tag Behavior


When we think of behavior in JavaScript terms we're typically thinking about how an object responds to events. Event-driven development is central to web development, particularly client-side web development.

A key feature of TIBET is its signaling subsystem, a set of components which unify how events work across browsers while also supporting non-native events. In TIBET you work with Signal instances for DOM events, Exceptions, state changes, etc.

For this tutorial our goal is to add reusable behavior to our hello:now tag so it will update its date/time display any time we activate (click) it. We do that via signal handling.

defineHandler

To define a signal handler we use the defineHandler method, a variant of defineMethod specific to signal handlers. As with defineMethod the first parameter is the handler name and the second parameter is the handler body (function).

Because we want to add behavior to hello:now tags we will define our event handler in the JavaScript source file specific to that tag. This is just one more way TIBET helps to organize and modularize your code.

Edit ~app_tags/APP.hello.now/APP.hello.now.js and add the following handler:

APP.hello.now.Inst.defineHandler('UIActivate', function(aSignal) {
    alert('UIActivate');
});
APP.hello.now.js


In the code above we define a signal handler for UIActivate signals on instances of hello:now tags (thanks to the Inst qualifier).

Recall that type methods in TIBET use `Type`, instance methods use `Inst`, and 'local' methods use no qualifier. The targeted object receives the method.

If you're wondering what UIActivate is, it's a TIBET signal which generalizes click and keyup and is one of a number of 'UI Signals' TIBET provides to enhance accessibility and testability across browsers and input devices.

Note that we don't need to set up or tear down observations or do any other boilerplate coding. All hello:now tags will automatically respond to UIActivate simply by virtue of our defining a handler for that signal in our tag implementation.

Save your changes, then click on the text of the hello:now tag in your UI:

UIActivate

hello:now UIActivate stub


With this simple action we've confirmed our event handler is operational...and notice that we didn't reload...TIBET hot-patched our tag implementation with the new functionality and all rendered instances of the tag automatically updated.

Signal Data

We still need to make our tag update its content in response to activation. We do that by leveraging data in the Signal instance provided to the signal handler.

Edit ~app_tags/APP.hello.now/APP.hello.now.js again, updating the handler to match:

APP.hello.now.Inst.defineHandler('UIActivate', function(aSignal) {
    var target,
        tag;

    target = aSignal.getTarget();
    tag = TP.wrap(target);

    tag.set('value', new Date().toString());
});
APP.hello.now.js


In the code above we access the signal's target via getTarget. This is the low-level node that received the initial event, much as we'd expect from a normal DOM event handler.

The next line wraps that low-level element in the best-fit TIBET type, in this case an instance of our APP.hello.now type, granting access to the tag's instance methods.

The final line leverages TIBET's 'getter/setter' syntax via set(), ultimately triggering a call to setValue on our tag. Note that this method relies on polymorphism in that setting the value of different tag types may cause different behavior. For an input field, this would set its .value, property but because our target here is an inline, non-form element, setValue instead sets its content (innerHTML in this case).

Save the new handler definition and then click on the date generated by the hello:now tag.

The hello:now tag should display the current time each time we click. It's that easy.

Recap

In this section we used the defineHandler method to add a signal handler to our hello:now tag. Our new signal handler updates the tag's time display whenever it receives a UIActivate signal, expanding the value of our reusable component.

As with our previous efforts we didn't have to reload the page to activate this behavior, we simply edited the source file and saved our changes and TIBET did the rest.

Of special interest is that clicking on a previously rendered tag "just worked". There was no need to set up/tear down listeners or do other boilerplate coding for events.

Summary

Summary


This "essentials" guide built upon the foundation created in the TIBET Quickstart Guide.

In particular, we added a new custom hello:now tag which leverages a JavaScript method to render the current date and time.

Next we expanded our hello:now tag's functionality by adding behavior to all hello:now tags so they update their current date/time display whenever they receive a UIActivate signal. In particular, we did this without any requirement that they add/remove listeners, helping reduce boilerplate and the potential for memory leaks, duplicate, or dangling listener registrations.

TIBET has a lot more to offer obviously but hopefully this further exploration of a simple hello application has helped reinforce one of the core things that makes TIBET special, namely a development process focused on tags, not code.

We invite you to continue to explore TIBET by checking out the entire TIBET platform, particularly the TIBET Sherpa, our revolutionary web development IDE.


The Pup

Contact us for more information or to discuss how we can assist you.