You Got A Question? Ask    GNOME Community!


Writing GNOME Scripts With Javascript & NeDB

Sometimes in our scripts we need to store data. Usually in Linux, people keep their data either in custom format files, or XMLs, or JSON. In more complex scripts (or apps) devs are using SQLite, which is far from keeping things simple.

Here I’m going to use Node with NeDB to write a stupid script, just for demonstration. NeDB is a lightweight document database written in Javascript that uses MongoDB API. You can think it as what SQLite is for MySQL, and it is a very good and popular solution for Apps databases. After all you don’t want to force users to install a Mongo server just to run your app, do you?


Packaging

No reason to talk about the impact that Node has in Linux desktop world, so I am moving right ahead to the packaging. This is our package.json file.

{
  "name": "NeDB_Demo",
  "version": "0.0.1",  
  "description":"Just A demo",
  "dependencies": {
    "nedb": ">=0.10.6",
    "async": "^0.9.0",
    "coffee-script":"~1.7.1"
  },
	
  "scripts": {
    "start": "node start.js"
  }  
}

I kept that minimum. What says is that you need nedb, async and coffee-script as dependencies, and the start script is the start.js. “^” means compatible with this version and “~” means near to this version.

What’s amazing about Node packaging is that dependencies are isolated from the system, so you won’t get bothered with incompatibilities or you won’t care about distros and distros versions. It will just run everywhere.

To install the dependencies

 npm install

Actually I’m not using either Async or Coffee-Script. However you can’t do much without Async. Async is a module that permit us to easily put control flows in our scripts. Because every Node function runs asynchronously (unless is specified otherwise), we have to control the time of execution in series. For example we want to run FuncB after FuncA is finished.

I also reckon to use Coffee-Script in Node if you don’t want to run into a Callback Hell.


Scripting

For this example I will just read the extensions that are installed under

/usr/share/gnome-shell/extensions/

together with the name of each one as this is defined inside “metadata.json” file, included in every extension.

In start.js file

var fs = require('fs');
var extPath = "/usr/share/gnome-shell/extensions/";

fs.readdir(extPath, function(err, files) {
  if (err) throw(err);

  files.forEach(function(file) {		
	
    fileJSON = fs.readFileSync(extPath+file+"/metadata.json");
      extension= JSON.parse(fileJSON);
      console.log("Ext Dir: %s" + "\n" + "Ext Name: %s \n", file, extension.name);
    });    
});

The script is quite simple to understand and if we run it:

 npm start
Ext Dir: alternate-tab@gnome-shell-extensions.gcampax.github.com
Ext Name: AlternateTab 

Ext Dir: apps-menu@gnome-shell-extensions.gcampax.github.com
Ext Name: Applications Menu 

Ext Dir: launch-new-instance@gnome-shell-extensions.gcampax.github.com
Ext Name: Launch new instance 

Ext Dir: places-menu@gnome-shell-extensions.gcampax.github.com
Ext Name: Places Status Indicator 

Ext Dir: user-theme@gnome-shell-extensions.gcampax.github.com
Ext Name: User Themes 

Ext Dir: window-list@gnome-shell-extensions.gcampax.github.com
Ext Name: Window List 


Add The Database

Adding a database to store our data it will take just 3 lines of code and less than 30sec!

var fs = require('fs'),
    Nedb = require('nedb'),
     extensions = new Nedb({ filename: '/home/alex/data.db', autoload: true }), 	
    extPath = "/usr/share/gnome-shell/extensions/";


fs.readdir(extPath, function(err, files) {
  if (err) throw(err);

  files.forEach(function(file) {
    fileJSON = fs.readFileSync(extPath+file+"/metadata.json");
    extension= JSON.parse(fileJSON);		
    extensions.insert({ extDir: file, extName: extension.name }, function (err) {});
  });    
});

First I create a new database at /home/alex/data.db. Document databases are schema-less, meaning that the collections are dynamically created when we insert data.

 extensions = new Nedb({ filename: '/home/alex/data.db', autoload: true })

Then instead of printing data in screen, we insert them in the database

extensions.insert({ extDir: file, extName: extension.name }, function (err) {});

This is a document text (it could also be binary) database so we can open and edit with gEdit

 gedit /home/alex/data.db
{"extDir":"alternate-tab@gnome-shell-extensions.gcampax.github.com","extName":"AlternateTab","_id":"uLwt4ZBqEUj2Vc61"}
{"extDir":"apps-menu@gnome-shell-extensions.gcampax.github.com","extName":"Applications Menu","_id":"IL7yfLIAmxbIRrx2"}
{"extDir":"launch-new-instance@gnome-shell-extensions.gcampax.github.com","extName":"Launch new instance","_id":"oOkjH7LfATt9Qmu6"}
{"extDir":"places-menu@gnome-shell-extensions.gcampax.github.com","extName":"Places Status Indicator","_id":"OwD6d6Aayp9mJUtW"}
{"extDir":"user-theme@gnome-shell-extensions.gcampax.github.com","extName":"User Themes","_id":"cbZslvGYlKsSQAWM"}
{"extDir":"window-list@gnome-shell-extensions.gcampax.github.com","extName":"Window List","_id":"wkEWYDna3EYFI0nf"}

Of course since it is a database, it auto-adds a unique key (_id) in the data.


Searching

Querying the database is ridiculous easy.

Count all documents in the collection

extensions.count({}, function (err, count) {
  //count equals to 6
});

Searching for AlternativeTab

extensions.find({ extName: 'AlternateTab' }, function (err, docs) {
  //docs hold the data in an array
});

Obviously you can have all the standard functionality of databases, like paging, sorting and so on.

Docs at: https://github.com/louischatriot/nedb


Okay maybe this script doesn’t do much, but in what other way you can make something similar in less than 5 minutes? ;)


 
  We can't watch comments unless G+ provides an API or if you send a notification, e.g +World Of Gnome
     Sometimes is better to place your questions on GNOME Community