Skip to content

Building blocks of Amber application (and library)

Herbert Vojčík edited this page Mar 3, 2016 · 5 revisions

Lego blocks view at Amber

Note: In this article, we only take into account browser-based Amber projects.

Definition 1: Amber application is a piece of a code written in Amber, possibly using pieces of non-amber code, that can be run in the browser when properly served.

Fact 1: In best tradition of Smalltalk, you must run your project to be able to convey an IDE on top of it.

Conclusion 1: Any Amber project which you want to be able to develop using an Amber IDE, is an Amber application.

Library - a main bulding block

Beginning with version 0.13, Amber applications are highly focused on easy composability off the Amber and non-Amber pieces. These pieces are called libraries. One of them is shown at this picture:

Why is this main building block called library? Well, for the default case (using bower to fetch and publish libraries), this is the only part you should publish if you decided your project should be a reusable library. Later you will see there is more pieces of different colours on the board, but this is the only part you should publish (and so, the only part which you fetch if you use the library).

In the picture above, the base is the Amber project, and the 2x4 blue brick with some small blocks on top of it represents a library. The 2x4 blue brick itself is the payload - the actual code of the library. The small skyblue block with a pin is the list of dependencies - it points to other libraries that should be fetched and put into project (that is, on the board).

Definition 2: Library is a bunch of files and/or directories, coming from one source, put into Amber project. A library contains the payload (the code and the resources) and metainformation. Metainformation can provide a list of dependencies, which are also libraries.

As was already told, the key point here is composability. You can put many libraries (2x4 blue blocks, some of them with additional small blocks on top of them) to the board. Just as the base of the Lego model is flat, so are libraries residing in an Amber project. For an Amber application, there is just a bunch of blue blocks there with some helper blocks here and there. Neither of the blue blocks is "master" or "slave", in the end they are just there, side-by-side, all pinned on the ground level.

This also means that:

Fact 2: The project's own code is also one of the libraries.

Think of that for a while. If Amber application is a flat Lego base, the code of your app itself is one of the blue blocks. Other blue blocks (and purple blocks on top of them, I'll get to them later) can come from amber itself, jQuery and other dependencies. Lots of blue blocks on the board. In the end, when you deploy the application, all blue blocks are packed into all-in-one output file.


Now, to the purple blocks. Amber-aware library is known by the presence of them. They are important for they allow actual use of the blue blocks. Every blue block with the payload needs the corresponding purple block, like an access key.

Fact 3: For every blue block in the board, an Amber application needs to find its corresponding purple block somewhere on the board to be able to use it. Purple block gives Amber application an access to the blue block.

"Somewhere" is emphasized for a reason. Non-Amber-aware libraries (like jQuery) do not bring purple blocks with them. So someone must bring the "foreign" purple block for such non-behaving blue block.

Now to disclose the puzzle:

  • Blue bricks are actual code. For Amber library, it is the src/ folder. For jQuery, it is bunch of files (that gets installed by bower install jquery). It is the main payload, that's why they are so big.
  • Sky-blue bricks with a pin is bower.json's dependencies and devDependencies sections. Any library can specify which libraries it depends upon. Those must be brought in and put into project's space. FYI, bower puts all dependencies in a project in flat fashion - it finds all dependencies in the whole subtree, resolves version constraints, and put all of them side-by-side.
  • And finally, purple blocks. They are *.amd.json files. Non-Amber-aware library does not have those. And without them, requirejs cannot load the corresponding code (nor, ultimately, pack it in a deploy artifact).

So, to reread the previous paragraphs, only things that Amber library needs to publish, is: src/, bower.json and *.amd.json. Nothing else is needed to publish for a library, that is: no index.html, no Gruntfile.js, no deploy.js, no devel.js, no config.js and no the.js (all of these will appear later as different bricks).

Amber application machinery

A library in itself is just a dead piece of code and metadata. To make use of it, you must have an Amber application that contains the machinery bringing the collective of libraries to life.

To illustrate it, let us assume we have an Amber project out there, and we just make a fresh clone of it. What we get is this:

A lot of other blocks is on the board.

The central strangely shaped black one is the true black box of an Amber application: Gruntfile.js. It contains instructions on many bookkeeping tasks that are vital for the life-cycle of the project. You may want not to touch this piece for a while, but just keep it there.

The 2x2 yellow brick with the light is index.html. As index.html is used to actually display the application, I chose the brick with the light. It is also a full-height piece, as it can contain lots of your HTML if you wish so - it is not merely a helper file, it is part of the application (but not part of the library; but see Conclusion 1 at the beginning).

And those small flat 1x2 pieces are the most neglected pieces of the puzzle: deploy.js and devel.js (EDIT: nowadays, there are three of them: deploy.js, testing.js and devel.js). They are list of packages to load: without them, yellow brick would show no light. Yellow brick asks green pieces which of the purple access keys should it use to get to the payload. If you give it the wrong list, application just does not load. BTW, the one at the bottom is deploy.js and the one at the top is devel.js (EDIT: imagine there is one more in the middle, testing.js), as devel list contains everything that deploy list contains plus pieces usable only at development time (EDIT: now the distinction is more subtle, testing contains all the deploy contains plus pieces needed to run tests; devel contains everything testing contains plus development-only pieces, like tooling helpers, example code and whatnot).

No other files are shown, because they can be safely .gitignored - they could be regenerated by proper use of grunt. For example, let's run grunt devel. And what we get is: ERROR. The project was just cloned from git. It misses all the dependencies. So first let's run npm install and we get:

Those L-shaped brown flat pieces are libraries brought in by npm: grunt, grunt task modules, amber-dev etc. Those are all needed to be present (in node_modules) so Gruntfile.js's instructions can actually be used. So let's run grunt devel now. And what we get is: ERROR. But this error happens later: grunt actually tried but failed. We miss a few more dependencies. After running bower install we get to this:

Ah, more 2x4 blue bricks (in fact, many more of them would appear there, but for the illustration in this picture, I added only one). The sky-blue brick with pin specified some dependency, so it was brought in and pinned at the ground level. That dependency is non-Amber-aware, so it did not bring its own purple block. But the library from the back of the board, which required the new one as the dependency in the first place, knew this little nuisance beforehand, and along with its own one (read: local.amd.json) it brought the corresponding purple block for the dependency, too (read: extlibdir.amd.json).

Again, imagine there is lot of blue blocks there. Some of them may have sky-blue pinned blocks on top of them (read: they have their own dependencies). If your project uses some Amber-aware libraries, some of them have some purple blocks pinned at them.

Now we can finally run grunt devel and succeed:

Nice! A silo has been built on top of the yellow brick! True, and that silo contains everything that is needed to actually run the libraries, inside index.html (on top of yellow brick).

The bottom purple half of the silo is generated into the config.js file. It just takes all the purple access keys and pours them into common container. Thus, a keychain to access all of the blue pieces is created (in more implementation point of view, config.js is the complete configuration of the RequireJS AMD loader).

The complete silo tower (include the bottom purple part) is then generated in the.js: the code that actually allows to run all the code in blue bricks, in one file (the.js is the only script loaded directly in index.html). The flat green middle part is the proper mapping of the application entry point 'app', using information from the green blocks. The upper blue part is anything that enables blue blocks to load and run. In devel mode, it's just the loader that loads those files directly; in deploy mode not just the loader but all the contents of all blue blocks is included so everything is inlined.

That said, config.js is not needed to run the app, as everything is in the.js, but config.js being overall access keychain to all blue blocks helps external pieces of code to work as well. In particular, when in devel mode, Helios IDE needs it to load correctly.

In deployment mode, after running grunt deploy, it suffices to just take the yellow brick off the board (with the silo attached to it), and you have everything you need to run the deployed app: index.html and the.js.

Lessons

Any Amber project is an application and the library at the same time. It is an application because without it, it cannot be run, thus cannot be developed. It is a library, as it contains src/, *.amd.json and bower.json.

It is easy to reuse the code of the Amber project: just publish its library part (the blue brick with additions) and include it in another project (pin it to the other board). Everything will get composed automagically (if you don't forget to update the green pieces).

An Amber application is the machinery plus a pack of libraries. That's all. And amber init pre-creates the machinery (and a seed library) for you.

Same as you can put blue pieces on any free place on the base, the same you can use any tool and layout to bring pieces to compose together if they are all in your project's file tree. bower is not the requirement, but it is the best solution out there (as its cares for subdependencies, version constraints and all such things).

Clone this wiki locally