«Fivebeans» и «Node js & Подключение модулей без явного использования require или аналог namespace»: разница между страницами

Материал из support.qbpro.ru
(Различия между страницами)
imported>Supportadmin
 
imported>Vix
Нет описания правки
 
Строка 1: Строка 1:
beanstalkd client & worker daemon for node
При работе с node.js часто приходится подключать файлы, расположенные не в
текущей директории, и даже не в поддереве текущего каталога. Поэтому часто в
модулях можно наблюдать что-то вроде


[https://www.npmjs.org/package/fivebeans оригинал]
var obj = require('../../../a/b/c/someModule');


==FiveBeansClient==


Heavily inspired by node-beanstalk-client, which is a perfectly usable client but somewhat dusty. I wanted more complete support of the beanstalkd protocol in a project written in plain javascript.
Для меня гораздо удобнее подключать файлы относительно корня проекта (модуля),
например


All client method names are the same case & spelling as the beanstalk text command, with hyphens replaced by underscore. The single exception is delete, which is renamed to destroy().
var obj = require(base_dir + '/a/b/c/someModule');


For complete details on the beanstalkd commands, see its protocol documentation.


===Creating a client===
Однако и в этом случае довольно много писанины и необходимо откуда-то получать
base_dir.
Для себя я нашел решение проблемы, позволяющее больше не писать require и не
узнавать base_dir:


The client constructor takes two arguments:
var obj = lib.a.b.c.someModule;


*host: The address of the beanstalkd server. Defaults to 127.0.0.1.
*port: Port to connect to. Defaults to 11300.


The client emits three events that you should listen for: connect, error, and close.


The client is not usable until you call its connect() method. Here's an example of setting up a client:
Идея решения


<nowiki>var fivebeans = require('fivebeans');


var client = new fivebeans.client('10.0.1.1', 11300);
Решение проблемы — создание хитрого «класса» (да простят меня гуру javascript за
client
такое слово), свойствами которого являются директории и файлы указанной при
    .on('connect', function()
создании класса директории. Если свойство-директория не является модулем, то
    {
данное свойство также является объектом нашего класса, свойствами которого
        // client can now be used
являются директории и файлы этой директории.
    })
    .on('error', function(err)
    {
        // connection failure   
    })
    .on('close', function()
    {
        // underlying connection has closed
    })
    .connect();</nowiki>


===Producing jobs===
Чтобы стало понятнее — приведу пример. Предположим у нас имеется следующая
====use====
структура проекта:


client.use(tube, function(err, tubename) {});


Use the specified tube. Reponds with the name of the tube being used.
Содержимое файлов:
\a\b\Foo.js
\a\c\Bar.js
\a\index.js


====list_tube_used====
var LazyLoader = require('./LazyLoader');
global.lib = new LazyLoader(__dirname); 
var foo = new lib.a.b.Foo();
console.log(foo.name);
console.log(foo.bar.name);


client.list_tube_used(function(err, tubename) {});


Responds with the name of the tube currently being used by the client.


====put====
Foo.js


client.put(priority, delay, ttr, payload, function(err, jobid) {});
module.exports = function () {
this.name = 'Foo';   
this.bar = new
lib.a.c.Bar(); };  


Submit a job with the specified priority (smaller integers are higher priority), delay in seconds, and allowed time-to-run in seconds. The payload contains the job data the server will return to clients reserving jobs; it can be either a Buffer object or a string. No processing is done on the data. Responds with the id of the newly-created job.


====peek_ready====


client.peek_ready(function(err, jobid, payload) {});
Bar.js


Peek at the data for the job at the top of the ready queue of the tube currently in use. Responds with the job id and payload of the next job, or 'NOT_FOUND' if there are no qualifying jobs in the tube. The payload is a Buffer object.
module.exports = function () { this.name = 'Bar'; };


====peek_delayed====


client.peek_delayed(function(err, jobid, payload) {});


Peek at the data for the delayed job with the shortest delay in the tube currently in use. Responds with the job id and payload of the next job, or 'NOT_FOUND' in err if there are no qualifying jobs in the tube. The payload is a Buffer object.
LazyLoader.js


====peek_buried====
var fs = require('fs');
var path = require('path'); 
function getter(key, dir,file) {
  delete this[key];
  var fullPath = path.join(dir, file);
    try
{require.resolve(fullPath);}
    catch (e) {
                if
            (fs.existsSync(fullPath)) {
                  this[key] = new LazyLoader(path.join(dir, file));
                }       
    return this[key];
    }   
  this[key] = require(fullPath);   
    return this[key]; } 
function LazyLoader(dir) {
      var loader = this;
      if (!fs.existsSync(dir)) {
        return;
    }
  fs.readdirSync(dir).forEach(function(file) {
        var key = file.replace(/\.js$/, '');
        loader.__defineGetter__(key, getter.bind(loader, key, dir, file))
    }); } 
module.exports = LazyLoader; 


client.peek_buried(function(err, jobid, payload) {});


Peek at the data for the next buried job in the tube currently in use. Responds with the job id and payload of the next job, or 'NOT_FOUND' in err if there are no qualifying jobs in the tube. The payload is a Buffer object.


===Consuming jobs===
При запуске node index.js в консоли распечатается (как и ожидалось)
===Server statistics===
 
==FiveBeansWorker==
Foo Bar
===API===
 
====constructor====
 
===Events===
 
===Jobs===
В данном примере я использовал глобальную переменную lib. Если вы противник
===Handlers===
глобальных переменных (или вы просто разрабатываете отдельный публичный модуль)
===Example===
— то можно отказаться от глобальной переменной — в Foo.js придется написать,
==FiveBeansRunner==
например, так:
===bin/beanworker===
 
===Configuration file===
var path = require('path');
var LazyLoader = require('../../LazyLoader');
var lib = new LazyLoader(path.join(__dirname, '../../'));
module.exports = function() {
    this.name = 'Foo';
    this.bar = new lib.a.c.Bar();
};
 
 
И если у вас всего 1-2 подключаемых файла, то возможно данный подход покажется
вам неуместным, но при большем количестве — вы должны ощутить существенное
облегчение.
Кроме того вы можете сохранять ссылки на директории для сокрашения записи,
например:
 
var lib = new LazyLoader(__dirname);
var logLig = lib.dir1.dir2.dir3.log;
var f1 = new logLib.ConsoleLogger();
var f2 = new logLib.FileLogger(); // вместо //
var f1 = new lib.dir1.dir2.dir3.log.ConsoleLogger(); //
var f2 = new lib.dir1.dir2.dir3.log.FileLogger();  //  и вместо //
var f1 = new require(__dirname + '/dir1/dir2/dir3/log/ConsoleLogger)(); //
var f2 = new require(__dirname + '/dir1/dir2/dir3/log/FileLogger)();
 
 
 
 
'''Заключение'''
 
1. Указанный подход позволяет имитировать аналог '''namespace''' в других языках.
 
2. Приведенный в примере '''LazyLoader''' легко расширить для работы с несколькими
директориями, например, для смешвания или переопределения модулей:
 
var lib = new LazyLoader('globalLibDir', 'applicationSpecificLibDir');
 
 
 
3. Если Вы нашли неточности, опечатки или у вас есть альтернативные решения
подключения модулей — пишите в ЛС.
 
ссылка на оригинал статьи http://habrahabr.ru/post/164665/
 
[http://savepearlharbor.com/?p=164665 взято тут...]

Версия от 17:22, 14 июня 2014

При работе с node.js часто приходится подключать файлы, расположенные не в текущей директории, и даже не в поддереве текущего каталога. Поэтому часто в модулях можно наблюдать что-то вроде

var obj = require('../../../a/b/c/someModule'); 


Для меня гораздо удобнее подключать файлы относительно корня проекта (модуля), например

var obj = require(base_dir + '/a/b/c/someModule'); 


Однако и в этом случае довольно много писанины и необходимо откуда-то получать base_dir. Для себя я нашел решение проблемы, позволяющее больше не писать require и не узнавать base_dir:

var obj = lib.a.b.c.someModule; 


Идея решения


Решение проблемы — создание хитрого «класса» (да простят меня гуру javascript за такое слово), свойствами которого являются директории и файлы указанной при создании класса директории. Если свойство-директория не является модулем, то данное свойство также является объектом нашего класса, свойствами которого являются директории и файлы этой директории.

Чтобы стало понятнее — приведу пример. Предположим у нас имеется следующая структура проекта:


Содержимое файлов:

\a\b\Foo.js
\a\c\Bar.js
\a\index.js
var LazyLoader = require('./LazyLoader'); 
global.lib = new LazyLoader(__dirname);  
var foo = new lib.a.b.Foo(); 
console.log(foo.name);
console.log(foo.bar.name); 


Foo.js
module.exports = function () {
this.name = 'Foo';     
this.bar = new
lib.a.c.Bar(); }; 


Bar.js
module.exports = function () { this.name = 'Bar'; }; 


LazyLoader.js
var fs = require('fs'); 
var path = require('path');  
function getter(key, dir,file) { 
 delete this[key];
 var fullPath = path.join(dir, file);
    try
{require.resolve(fullPath);} 
    catch (e) { 
               if
           (fs.existsSync(fullPath)) {
                 this[key] = new LazyLoader(path.join(dir, file));
               }         
    return this[key];
    }     
 this[key] = require(fullPath);     
   return this[key]; }  
function LazyLoader(dir) {
     var loader = this;
     if (!fs.existsSync(dir)) {
        return;
    } 
  fs.readdirSync(dir).forEach(function(file) {
        var key = file.replace(/\.js$/, );
        loader.__defineGetter__(key, getter.bind(loader, key, dir, file))
    }); }  
module.exports = LazyLoader;  


При запуске node index.js в консоли распечатается (как и ожидалось)

Foo Bar 


В данном примере я использовал глобальную переменную lib. Если вы противник глобальных переменных (или вы просто разрабатываете отдельный публичный модуль) — то можно отказаться от глобальной переменной — в Foo.js придется написать, например, так:

var path = require('path');
var LazyLoader = require('../../LazyLoader');
var lib = new LazyLoader(path.join(__dirname, '../../'));
module.exports = function() {
    this.name = 'Foo';
    this.bar = new lib.a.c.Bar();
}; 


И если у вас всего 1-2 подключаемых файла, то возможно данный подход покажется вам неуместным, но при большем количестве — вы должны ощутить существенное облегчение. Кроме того вы можете сохранять ссылки на директории для сокрашения записи, например:

var lib = new LazyLoader(__dirname);
var logLig = lib.dir1.dir2.dir3.log;
var f1 = new logLib.ConsoleLogger();
var f2 = new logLib.FileLogger(); // вместо //
var f1 = new lib.dir1.dir2.dir3.log.ConsoleLogger(); //
var f2 = new lib.dir1.dir2.dir3.log.FileLogger();  //  и вместо //
var f1 = new require(__dirname + '/dir1/dir2/dir3/log/ConsoleLogger)(); //
var f2 = new require(__dirname + '/dir1/dir2/dir3/log/FileLogger)(); 



Заключение

1. Указанный подход позволяет имитировать аналог namespace в других языках.

2. Приведенный в примере LazyLoader легко расширить для работы с несколькими директориями, например, для смешвания или переопределения модулей:

var lib = new LazyLoader('globalLibDir', 'applicationSpecificLibDir'); 


3. Если Вы нашли неточности, опечатки или у вас есть альтернативные решения подключения модулей — пишите в ЛС.

ссылка на оригинал статьи http://habrahabr.ru/post/164665/

взято тут...