The basic method of writing extensions for other programs using Node.js _node.js

Source: Internet
Author: User
Tags garbage collection gtk wrapper

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:


$ NPM AddUser

Next, go back to your root directory code and run:

 
$ NPM Publish

That's it, your bag is now ready to be installed by anyone through NPM install your package name command.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.