Ready to start
First we use the following directory structure to create a node notification (node-notify) folder.
Copy Code code as follows:
.
|--Build/# This is where we extension is built.
|--demo/
| '--Demo.js # This are a demo Node.js script to test our extension.
|--src/
| '--Node_gtknotify.cpp # is the where we do the mapping from C + + to Javascript.
'--WScript # This is our builds configuration used by Node-waf
This seemingly beautiful tree is built with a common tree.
Now let me create a test script demo.js and decide that our extended API upfront should look like this:
This loads we extension on the notify variable.
It'll only load a constructor function, Notify.notification ().
var notify = require (".. /build/default/gtknotify.node "); Path to our extension
var notification = new notify.notification ();
Notification.title = "notification title";
Notification.icon = "Emblem-default"; see/usr/share/icons/gnome/16x16
notification.send ("notification message");
Write our Node.js extension
Init method
To create a node.js extension, we need to write a C + + class that inherits Node::objectwrap. Objectwrap implements a public approach that makes it easier to interact with JavaScript
Let's start by writing the basic framework of the class:
#include <v8.h>//V8 is the Javascript engine used by Qnode #include <node.h>//We'll need the following Libraries for our GTK + notification #include <string> #include <gtkmm.h> #include <libnotifymm.h> usin
G namespace V8; Class Gtknotify:node::objectwrap {private:public:Gtknotify () {} ~gtknotify () {} static void Init (Handle<ob Ject> target) {//This is what Node would call when we load the extension through require (), and the Code be
Low.
}
};
* * Warning:boilerplate code ahead. * * https://www.cloudkick.com/blog/2010/aug/23/writing-nodejs-native-extensions/& http://www.freebsd.org/ Cgi/man.cgi?query=dlsym * * thats it for actual interfacing with V8, and finally we need to let node.js know how to Dynamic
Ally load our code. * Because a node.js extension can is loaded at runtime from a shared object, we need a symbol that the DLSYM function can Find, * So we do the following: * * V8::P Ersistent<fuNctiontemplate> gtknotify::p ersistent_function_template; extern "C" {//Cause of name mangling in C + +, we use extern C-here static void Init (handle<object> target) {GTK
Notify::init (target);
}//@see http://github.com/ry/node/blob/v0.2.0/src/node.h#L101 node_module (gtknotify, init);
}
Now we have to write the following code into our init () method:
Declare the constructor and bind it to our target variable. var n = require ("notification"), binding notification () to N:n.notification ().
Wrap our C + + new () method so that it's accessible from Javascript//This is called by the new operator in Java
Script, for example:new notification ();
v8::local<functiontemplate> local_function_template = v8::functiontemplate::new (New);
Make it persistent and assign it to persistent_function_template which are a static attribute of our class.
Gtknotify::p ersistent_function_template = V8::P ersistent<functiontemplate>::new (local_function_template);
Each JavaScript object keeps a reference to the C + + object for which it's a wrapper with a internal field. Gtknotify::p ersistent_function_template->instancetemplate ()->setinternalfieldcount (1); 1 since a constructor function only references 1 object//Set a ' class ' name for objects created with our Constructo
R gtknotify::p ersistent_function_template->setclassname (V8::string::newsymbol ("Notification")); Set the "notification" property's our target variable and assign it To our constructor function Target->set (String::newsymbol ("Notification"), Gtknotify::p ersistent_function_
Template->getfunction ());
Declaring properties: N.title and N.icon.
Set Property Accessors
//Setaccessor Arguments:javascript property name, C + + method that'll act as the getter, C + + method, that'll act as the setter
gtknotify::p ersistent_function_template->instancetemplate ()-> Setaccessor (String::new ("title"), GetTitle, settitle);
Gtknotify::p ersistent_function_template->instancetemplate ()->setaccessor (string::new ("icon"), GetIcon, SetIcon);
For instance, N.title = "Foo" would now call Settitle ("foo"), N.title'll now call GetTitle ()
Declaring a prototype method: N.send ()
This is a Node macro to help bind C + + methods to Javascript methods (for the HTTPS://GITHUB.COM/JOYENT/NODE/BLOB/V0.2.0/SRC /NODE.H#L34)
//Arguments:our constructor function, Javascript method name, C + + method name
Node_set_prototype_ Method (gtknotify::p ersistent_function_template, "send", send);
Now our init () method should look like this:
Our constructor static V8::P ersistent<functiontemplate> persistent_function_template; static void Init (handle<object> target) {v8::handlescope scope;//used by V8 to garbage collection//Our con
Structor v8::local<functiontemplate> local_function_template = v8::functiontemplate::new (New);
Gtknotify::p ersistent_function_template = V8::P ersistent<functiontemplate>::new (local_function_template); Gtknotify::p ersistent_function_template->instancetemplate ()->setinternalfieldcount (1); 1 Since this is a constructor function gtknotify::p ersistent_function_template->setclassname (v8::string::
Newsymbol ("Notification")); Our getters and setters gtknotify::p ersistent_function_template->instancetemplate ()->setaccessor (String::
New ("title"), GetTitle, Settitle); Gtknotify::p ersistent_function_template->instancetemplate ()->setaccessor (string::new ("icon"), GetIcon,
SetIcon); Our Methods Node_set_prototype_method (gtknotify::p ERsistent_function_template, "send", send); Binding our constructor function to the target variable Target->set (string::newsymbol ("Notification"), Gtknotify::
Persistent_function_template->getfunction ());
}
All that remains to be done is to write the C + + method we used in the Init method: New,gettitle,settitle,geticon,seticon,send
Constructor method: New ()
The new () method creates an instance of our custom class (a Gtknotify object), sets some initial values, and then returns the JavaScript processing for that object. This is the expected behavior of JavaScript invoking the constructor using the new operator.
std::string title;
std::string icon;
New notification ()
static handle<value> new (const arguments& args) {
handlescope scope;
gtknotify* gtknotify_instance = new Gtknotify ();
Set some default values
Gtknotify_instance->title = "Node.js";
Gtknotify_instance->icon = "Terminal";
Wrap our C + + object as a Javascript object
gtknotify_instance->wrap (args. This ());
return args. This ();
}
Getters and Setters:gettitle (), Settitle (), GetIcon (), SetIcon ()
The following are mainly boilerplate code, which can be summed up as a value conversion between C + + and JavaScript (V8).
This.title static v8::handle<value> GetTitle (v8::local<v8::string> property, const V8::accessorinfo
& info) {//Extract the C + + Request object from the JavaScript wrapper. gtknotify* gtknotify_instance = node::objectwrap::unwrap<gtknotify> (info.
Holder ());
Return V8::string::new (Gtknotify_instance->title.c_str ()); }//this.title= static void Settitle (Local<string> property, local<value> Value, const accessorinfo& INF O) {gtknotify* gtknotify_instance = node::objectwrap::unwrap<gtknotify> (info.
Holder ());
V8::string::utf8value v8str (value);
Gtknotify_instance->title = *v8str; }//This.icon static v8::handle<value> GetIcon (v8::local<v8::string> property, const V8::accessorinfo
& info) {//Extract the C + + Request object from the JavaScript wrapper. gtknotify* gtknotify_instance = node::objectwrap::unwrap<gtknotify> (info.
Holder ());
Return V8::string::new (Gtknotify_instance->icon.c_str ()); }//This.icon= static void SetIcon (Local<string> property, local<value> Value, const accessorinfo& info) {gtknotify* Gtknotify_instance = node::objectwrap::unwrap<gtknotify> (info.
Holder ());
V8::string::utf8value v8str (value);
Gtknotify_instance->icon = *v8str;
}
Prototype method: Send ()
First we extract the This reference for the C + + object, and then use the object's properties to construct the notification and display it.
This.send ()
static v8::handle<value> send (const arguments& args) {
v8::handlescope scope;
Extract C + + object reference from ' This '
gtknotify* gtknotify_instance = node::objectwrap::unwrap<gtknotify > (args. This ());
Convert argument to V8 String
v8::string::utf8value v8str (args[0));
For further info on the Notify library:http://library.gnome.org/devel/libnotify/0.7/notifynotification.html
Notify::init ("Basic");
Arguments:title, Content, Icon
notify::notification N (gtknotify_instance->title.c_str (), *v8str, Gtknotify_instance->icon.c_str ()); *v8str points to the C string it wraps
//Display the Notification
n.show ();
Return value return
v8::boolean::new (true);
}
Compiling extensions
Node-waf is a build tool for compiling Node extensions, which is the basic encapsulation of WAF. The build process can be configured with a file named WScript.
def set_options (opt):
opt.tool_options ("Compiler_cxx")
def Configure (conf):
Conf.check_tool (" Compiler_cxx ")
conf.check_tool (" Node_addon ") # This'll tell the compiler to link we extension with the
gtkmm a D libnotifymm Libraries.
Conf.check_cfg (package= ' gtkmm-2.4 ', args= '--cflags--libs ', uselib_store= ' libgtkmm ')
conf.check_cfg (package= ' libnotifymm-1.0 ', args= '--cflags--libs ', uselib_store= ' Libnotifymm ')
def Build (bld):
obj = Bld.new_task_gen ("Cxx", "Shlib", "Node_addon")
Obj.cxxflags = ["G", "-d_file_offset_bits=64", "-d_largefile_source", "-wall"] # This is the name of our
extension.< C12/>obj.target = "Gtknotify"
obj.source = "Src/node_gtknotify.cpp"
obj.uselib = [' libgtkmm ', ' libnotifymm ' ]
Now that we are ready to start building, run the following command in the top-level directory:
Node-waf Configure && Node-waf Build
If everything works, we'll get the compiled extension, located at:./build/default/gtknotify.node, to try:
$ node
> var notif = require ('./build/default/gtknotify.node ');
> n = new notif.notification ();
{icon: ' Terminal ', Title: ' Node.js '}
> N.send ("Hello world!");
True
The above code will display a notification message at the top right of your screen.
Make a npm bag.
This is really cool, but how do you share your efforts with the node community? This is the main purpose of NPM: making it easier to expand and distribute.
It is very simple to play NPM's expansion pack. All you have to do is create a file in your top-level directory that contains your extended information Package.json:
{
//Extended name (do not include node or JS in the name, which is an implicit keyword).
This is the name of the extension that was imported by require ().
Name ":" Notify ",
//version should be http://semver.org/compliant
" version ":" v0.1.0 "
// These scripts will run when the NPM installation and NPM uninstall are invoked.
, "scripts": {
"preinstall": "Node-waf Configure && Node-waf build"
, " Preuninstall ":" Rm-rf build/* "
}
//This is the relative path to build our extension.
," main ":" Build/default/gtknotify.node "
// The following are optional fields:
, "description": "Description of the extension ..."
, "homepage": "https://github.com/olalonde/ Node-notify "
," author ": {
" name ":" Olivier Lalonde "
," email ":" olalonde@gmail.com "
," url ":" Htt p://www.syskall.com/"
}
," repository ": {
" type ":" Git "
," url ":" https://github.com/olalonde/ Node-notify.git "
}
}
For more details on the Package.json format, you can get the document through NPM help JSON. Note that most of the fields are optional.
You can now install your new NPM package in your top-level directory by running NPM install. If all goes well, you should be able to simply load your extended var notify = require (' Your package name ');. Another useful command of NPM link allows you to create a link to your development directory that doesn't have to be installed/unloaded every time you change your code.
Let's say you wrote a cool extension that you might want to post on the web in a central NPM library. First you have to create an account:
Next, go back to your root directory code and run:
That's it, your bag is now ready to be installed by anyone through NPM install your package name command.