モジュール分割とグローバル変数のお作法

閲覧: 4,880 回
最初の未読メッセージにスキップ

たくと

未読、
2011/12/28 4:36:552011/12/28
To: nodejs_jp
はじめまして。takuto1981と申します。node.jsをはじめたばかりの初心者です。
モジュール分割とグローバル変数のお作法についてお聞きしたく、メールしました。
# まだ右も左も分からない状態なので、用語などに誤りがあれば申し訳ありません。

expressで自動生成される雛形(テンプレートはejs)をみる限り、
コールバック関数ごとにモジュール分割するのがお作法なのかなと思っています。

この場合、例えばメインのモジュールでグローバル変数を宣言して、
それぞれのモジュールから参照することは作法的に問題ないでしょうか?

例えばmongoDBのモデルを作成した時に、各モジュールからもアクセスしたいのですが、
当然ですがモジュールスコープの変数にしてしまうとアクセス出来ません。

いちおう動くことは確認しましたが、グローバル変数を使うことに少し抵抗があったので、
もしこういう場合の定石としての作法があるようであれば、ご教授いただけると嬉しいです。

よろしくお願い致します。

yusuke kagiwada

未読、
2011/12/28 5:03:212011/12/28
To: node...@googlegroups.com
Jxck です。

確かに express は現在 routes ディレクトリが出来ていて、
分割する方針をとっています。

しかし、「じゃあどう分割するか?」という話は、
express のドキュメントなどには、出ていません。
そして、案外面倒くさいこともわかってます。

あと、 TJ は「自由にやれよ」みたいなスタンスみたいですw
が、これをどう扱うかは大事な議論だと思います。


まず、前提を確認したいんですが。

> この場合、例えばメインのモジュールでグローバル変数を宣言して、
> それぞれのモジュールから参照することは作法的に問題ないでしょうか?

これってどうやってますか?
ちょっとたたき台になりそうなので、小さいコードでも見せてもらえると良いと思います。
(ここでも、 gist でも。雰囲気だけでもいいです。)

2011年12月28日18:36 たくと <takut...@gmail.com>:

兼平 卓史

未読、
2011/12/28 5:36:512011/12/28
To: node...@googlegroups.com
Jxckさん、


なるほど。まだ試行錯誤といった感じなのですね。

叩き台にすらならないレベルですが、一応以下にコードの一部を添付します。

####

[./app.js] : メイン
var express = require('express')
Events = require('./models/eventSchema');
var test = require('./routes/test');
 ・
 ・
 ・
app.get('/neworder/:id', test.index);
 ・
 ・
 ・

[./models/eventSchema.js] : mongoDB関連
var mongoose = require('mongoose');
var db = mongoose.connect('mongodb://XXX.XX.XX.XXX:XXXX/testdb');
var Schema = mongoose.Schema;
var EventsSchema = new Schema({
 ・
 ・
 ・
});
mongoose.model('Events',EventsSchema);
module.exports = db.model('Events');

[./routes/test.js] : モジュール化したコールバック関数
exports.index = function(req,res){
 ・
 ・
 ・
  var query = Events.find({});
 ・
 ・
 ・
}

Akitoshi Manabe

未読、
2011/12/28 6:32:272011/12/28
To: node...@googlegroups.com
このモジュール間を跨ぎつつ、共有したい変数の作法は、私も気になるところでした。

> あと、 TJ は「自由にやれよ」みたいなスタンスみたいですw
> が、これをどう扱うかは大事な議論だと思います。

モジュール間を跨ぐ変数の共有方法について作法は気になるところでしたが、未だ無いんですねぇ。


私自身は、未だに express のようなフレームワークに触れず、色々と自前コードを書きながら遊んでる段階なのですので、express
での扱い方は判りませんが、モジュールのスコープを律儀に守る手法ですと

server.js 内でこんな風に書いたものを
exports.HTDOCS_DIR = path.join( __dirname, 'htdocs' );

require() で呼ばれたコード内で参照する時は、こんな感じ。
var HTDOCS_DIR = module.parent.exports.HTDOCS_DIR;

この場合、モジュールオブジェクトが管理する構造を調査しないと、参照できずに面倒に感じていました。。

で、自前のモジュールを擬似的なグローバル変数として扱うというのはどうでしょう?
require() されたモジュールは、moduleオブジェクトで管理され、同じpathのモジュールがrequire()されると、メモリ上で管理中のmoduleを参照する。これを上手く利用すれば自前のモジュールを擬似的なグローバル変数として代用できると思います。

たとえば、MyObjects.js というモジュールを次のように1行だけ書いておき、

[MyObjects.js]
module.exports = {};


require('path/to/MyObjects,js') を書いた全てのモジュールで共有するオブジェクトを保持できる。

var myObj = require( 'path/to/MyObjects.js' );
myObj.hoge = 'hogeValue';

とか、他のモジュールでも参照できる。
JavaSciptなので、生成したインスタンスのメンバですら変更可能という特徴を活かす感じです。
クライアントサイドではオブジェクト汚染などと言われる事を、しっかりと管理しつつ、
意図的に行うことで安全な変数の役割を持たせることができる。というコンセプトです。

他にも

[MyClass.js]
function MyClass(){}
MyClass.prototype.method1 = function(){}
MyClass.prototype.method2 = function(){}
exports.MyClass = MyClass;

のようなモジュールは、こんな風に利用されますが、

var MyClass = require('path/to/MyClass.js').MyClass;
var myClass = new MyClass();
myClass.method1();
myClass.method2();

ここで、

MyClass.hoge = (任意の値)

というように、MyClass.js で定義した MyClass
のスタティックなプロパティにアクセスすることで、モジュールオブジェクト内で管理されるMyClassの内容も変更されるため、他のモジュールからも
(任意の値) を参照できます。


2011年12月28日19:36 兼平 卓史 <takut...@gmail.com>:

yusuke kagiwada

未読、
2011/12/28 7:58:552011/12/28
To: node...@googlegroups.com
Jxck です。

takuto1981 さん。

一番重要なのは、ここですよね?

> [./app.js] : メイン
> var express = require('express')
> Events = require('./models/eventSchema');

これは、
var express = require('express');
Events = ...;


var express = require('express'),
Events = ..;

では大きく意味が変わってしまいますが、
恐らく前者の方で、 プロセス上のどっからでも Events が見えるからっていうのが、
最初に言ってた「グローバル変数」だと思って進めます。

このグローバル変数は、恐らくお行儀がいいとは言えないでしょう。
普通にバッドパーツと扱われるだろうし、
Ruby でも「グローバル変数($) は将来無くしたい」などと言われています。

それに、他のライブラリと組み合わせてバッティングなんかしたりしたら、
デバッグが地獄です。
モジュールの仕組みが有るから、そちらを使うべきだと自分は思います。


そして実は、そもそも「グローバル変数」やったこと無かったし、出来ること知りませんでした(汗
言われてみれば、できますよね。でも最初「意味上のグローバル」のことだと思ってました。。


Manabe さん

上で言った意味上のグローバルは、こちらで書いて頂いた意味合いです。

「共有モジュール」をどっかで export しておいて、みんなが require する。
みたいな。


ただ、これって(index.js はどっかで使われているとして)

$ tree
.
├── path
│   └── to
│       └── my
│           └── module
│               └── index.js
└── share.js

だった場合、

[share.js]
exports.num = 10;

[index.js]
var share = require('../../../../share');
console.log(share);

となってしまうんですよね。
相対パス指定しない場合は、コアモジュールか node_modules しか呼んでくれません。

$ tree 
.
├── node_modules
│   └── share.js
└── path
    └── to
        └── my
            └── module
                └── index.js

これなら、index.js 内で require('share'); できます。
でも、 node_modules は npm install したもののみ、という暗黙の了解が有るし、
基本 .gitignore に足されます。

だから、もしそれを解決するには

$ tree                                                                                                                                             
.
├── lib
│   └── share.js
└── path
    └── to
        └── my
            └── module
                └── index.js

こうして、
node コマンドに NODE_PATH='lib' をつけて

>NODE_PATH node server.js

としないといけなくて、
それって毎回やるのめんどいということで、
npm には、「どうやって起動するか?」を指定する機能が有ります。


package.json を足して
$ tree
.
├── lib
│   └── share.js
├── package.json
└── path
    └── to
        └── my
            └── module
                └── index.js


中身はこう。name, version は必須だから。本題は script のところです。
ここでは、 index.js を直で呼ぶようになってますが、 index.js はどっかで使われているとして。
つまり想定は NODE_PATH='lib' node server.js とか。

{
  "name": "sample",
  "version": "0.0.0",
  "scripts": {
    "start": "NODE_PATH='lib' node ./path/to/my/module/index.js"
   }
}

すると
$ npm start

> sam...@0.0.0 start /private/tmp/sample
> NODE_PATH='lib' node ./path/to/my/module/index.js

{ num: 10 }

こんな感じにできます。

つまり、「そろそろみんな npm start で起動しない?」
というww


自分が思うのは「require('../../../../') はちょっと。。」という意味で、こんな感じです。
それ以外、Manabe に同意です。(あまり外からガシガシ変更するのは怖いけど。。)

takuto1981 のグローバル変数案には、キッパリ反対します。

他の意見も是非!

2011年12月28日20:32 Akitoshi Manabe <akitosh...@gmail.com>:

Shin Suzuki

未読、
2011/12/28 11:31:432011/12/28
To: node...@googlegroups.com
こんにちは。鈴木晋といいます。
非同期シングルスレッド万歳派というよりはJSの柔軟さが好きなゆえにNodeを触ってます。
独自ライブラリを沢山つくっています。(のでexpressとかはあまり知りません...)



とても面白い話題なので投稿させてください。

JSが他のスコープに変数を渡す方法は3つあって

1. (定義時での)外のスコープに置く
2. 引数
3. this


で今の問題は
app.get("hoge", test.index)
のtest.indexという関数にEventなる変数を渡したい、と。



まずtest.indexは別ファイルで定義されているので1は使えません.
もしくは最外スコープたるglobalスコープを使うしかない...と。


2番目のアイデア、引数で渡すというもの。

expressの場合はcallback functionに渡される引数が決まっているから無理だと。

といいつつ
app.get("hoge", function(req, res) {
  test.index.call(this, req, res, share); // share.Event とかに渡したいのを入れておいて。
})

とかやればいけますからね。これを毎回書くのが嫌ならapp.getをwrapしてしまえばいける。
app.getWithShareObj = function() {
  var callback = Array.prototype.pop.call(arguments);
  Array.prototype.push.call(arguments, function(req, res) {
    callback.call(this, req, res, share);
  });  
  return app.get.apply(this, arguments);
};



3のthisもシンプル。

app.get("hoge", test.index.bind({
  Event: Event
}))

もしtest.indexのなかでexpress式のthisを使っていないようならこれだけで十分。
こう書くだけでtest.indexの中で this.Event が使えるようになります.

しかし、express式のthisと一緒にしたいというのなら、またwrapしてあげましょう...

app.getWithShareObj = function() {
  var callback = Array.prototype.pop.call(arguments);
  Array.prototype.push.call(arguments, function(req, res) {
    Object.keys(share).forEach(function(k) { // getOwnPropertyなんちゃらも可
      this[k] = share[k];
    }, this);
    callback.call(share, req, res);
  });  
  return app.get.apply(this, arguments);
};



しかし、お作法のことは分かりません。
そういうのは大体LearnBoostなんかのスーパーエンジニアのbiasがかかりまくって、
必ずしもベストプラクティスではないこともあるんでは...。


あと, Object.freeze()をすることで共有するプロパティの変更を阻止できます。
methodの上書きなんかを避けたいときにはexportsにそれをしてあげるといいと思います。
再帰的にはやってくれないので注意.

ただ以前の独自調査でObject.freeze()がメモリかなり食うということが分かって以来、
僕自身は大事なもの以外にはあんまりやらなくなりました。
この辺もお作法があるかも。


鈴木晋







2011年12月28日21:58 yusuke kagiwada <block.rxc...@gmail.com>:

rika

未読、
2011/12/28 12:08:342011/12/28
To: nodejs_jp
Jxckさん、みなさん
こんばんは。node.js勉強中のrikaと申します。
本題とはずれてしまいますが、「require('../../../../') はちょっと。。」の対処方法として
わたしは、こんな感じで書いてます。
require.paths.unshift(process.cwd() + '/lib');
var const_val = require('const').const_val;
作法としてどうなのか分からないですが、とりあえず、これでどのモジュールからも、この書き方でアクセスできてます。

On 12月28日, 午後9:58, yusuke kagiwada <block.rxckin.be...@gmail.com>
wrote:


> Jxck です。
>
> takuto1981 さん。
>
> 一番重要なのは、ここですよね?
>
> > [./app.js] : メイン
> > var express = require('express')
> > Events = require('./models/eventSchema');
>
> これは、
> var express = require('express');
> Events = ...;
>
> と
>
> var express = require('express'),
> Events = ..;
>
> では大きく意味が変わってしまいますが、
> 恐らく前者の方で、 プロセス上のどっからでも Events が見えるからっていうのが、
> 最初に言ってた「グローバル変数」だと思って進めます。
>
> このグローバル変数は、恐らくお行儀がいいとは言えないでしょう。
> 普通にバッドパーツと扱われるだろうし、
> Ruby でも「グローバル変数($) は将来無くしたい」などと言われています。
>
> それに、他のライブラリと組み合わせてバッティングなんかしたりしたら、
> デバッグが地獄です。
> モジュールの仕組みが有るから、そちらを使うべきだと自分は思います。
>

> そして実は、そもそも「グローバル変数」やったこと無かったし、出来ること知りませんでした(汗http://twitter.com/#!/hakobera/status/151971594359685120

> 2011年12月28日20:32 Akitoshi Manabe <akitoshi.man...@gmail.com>:

> > 2011年12月28日19:36 兼平 卓史 <takuto1...@gmail.com>:

> > > 2011年12月28日18:36 たくと <takuto1...@gmail.com>:


>
> > >> はじめまして。takuto1981と申します。node.jsをはじめたばかりの初心者です。
> > >> モジュール分割とグローバル変数のお作法についてお聞きしたく、メールしました。
> > >> # まだ右も左も分からない状態なので、用語などに誤りがあれば申し訳ありません。
>

> ...
>
> もっと読む ≫

yusuke kagiwada

未読、
2011/12/28 12:35:102011/12/28
To: node...@googlegroups.com
Jxck です。

rika さん。

require.paths.unshift() というか、 require.paths 自体が、
v0.5.2 で無くなっています。

Express なんかでも昔は使っていたので、古いサンプルなんかだとまだ残ってるかもしれませんが、
これの代替というか、統一のために出来たのが node_modules と NODE_PATH なんです。
repl で下記をすると分かります。

> require.paths;
Error: require.paths is removed. Use node_modules folders, or the NODE_PATH environment variable instead.


2011年12月29日2:08 rika <rika....@gmail.com>:

kysnm

未読、
2011/12/28 13:34:452011/12/28
To: node...@googlegroups.com
Jxck さん、皆様、

kysnm と申します。

> [index.js]
> var share = require('../../../../share');
> console.log(share);
>
> となってしまうんですよね。
> 相対パス指定しない場合は、コアモジュールか node_modules しか呼んでくれません。
>
> $ tree
> .
> ├── node_modules
> │ └── share.js
> └── path
> └── to
> └── my
> └── module
> └── index.js
>
> これなら、index.js 内で require('share'); できます。
> でも、 node_modules は npm install したもののみ、という暗黙の了解が有るし、
> 基本 .gitignore に足されます。


この間 npm についてまとめた勢いでの発言ですが、
npm install では package.json を含んだディレクトリも
インストール対象に出来るので以下の様な解決方法もあ
るかと思います。ただファイル構成が冗長なのであまり
きれいではないかと思いますが、、、


$ tree
.
├── mymodule
│ ├── app.js
│ └── package.json
└── test_ml
├── app.js
└── package.json

2 directories, 4 files
$ cat mymodule/app.js
exports.hello = function (msg) {
console.log('Hello, ' + msg);
}
$ cat mymodule/package.json
{
"name": "mymodule",
"version": "0.0.0",
"main": "app.js",
"private": "true"
}
$ cat test_ml/app.js
var socket = require('socket.io');
var mymodule = require('mymodule');
mymodule.hello('Jxck');
$ cat test_ml/package.json
{
"name": "test_ml",
"version": "0.0.0",
"private": "true",
"dependencies": {
"socket.io": "*"
}
}
$ npm install mymodule
mymo...@0.0.0 ./node_modules/mymodule
$ tree
.
├── mymodule
│ ├── app.js
│ └── package.json
├── node_modules
│ └── mymodule
│ ├── app.js
│ └── package.json
└── test_ml
├── app.js
└── package.json

4 directories, 6 files
$ cd test_ml/
$ npm install
npm http GET https://registry.npmjs.org/socket.io
npm http 200 https://registry.npmjs.org/socket.io
npm http GET https://registry.npmjs.org/redis/0.6.7
npm http GET https://registry.npmjs.org/socket.io-client/0.8.7
npm http GET https://registry.npmjs.org/policyfile/0.0.4
npm http 304 https://registry.npmjs.org/redis/0.6.7
npm http 304 https://registry.npmjs.org/socket.io-client/0.8.7
npm http 304 https://registry.npmjs.org/policyfile/0.0.4
npm http GET https://registry.npmjs.org/uglify-js/1.0.6
npm http GET https://registry.npmjs.org/websocket-client/1.0.0
npm http GET https://registry.npmjs.org/xmlhttprequest/1.2.2
npm http 304 https://registry.npmjs.org/uglify-js/1.0.6
npm http 304 https://registry.npmjs.org/websocket-client/1.0.0
npm http 304 https://registry.npmjs.org/xmlhttprequest/1.2.2
sock...@0.8.7 ./node_modules/socket.io
├── polic...@0.0.4
├── re...@0.6.7
└── socket.i...@0.8.7
$ tree -L 4 ../
../
├── mymodule
│ ├── app.js
│ └── package.json
├── node_modules
│ └── mymodule
│ ├── app.js
│ └── package.json
└── test_ml
├── app.js
├── node_modules
│ └── socket.io
│ ├── History.md
│ ├── Makefile
│ ├── Readme.md
│ ├── benchmarks
│ ├── examples
│ ├── index.js
│ ├── lib
│ ├── node_modules
│ ├── package.json
│ ├── support
│ └── test
└── package.json

12 directories, 11 files
$ node app.js
Hello, Jxck

socket.io は一応インストール出来て、require
出来る事を確認する為だけに利用しています。


これはこれで管理もめんどくさそうですね。
参考程度にお考えください。


2011年12月28日21:58 yusuke kagiwada <block.rxc...@gmail.com>:

--
twitter: https://twitter.com/kysnm
email: tokyoinc...@gmail.com

Kazuhito Hokamura

未読、
2011/12/28 21:14:252011/12/28
To: node...@googlegroups.com
hokacchaです。
おそらくtakuto1981さんが聞きたかったことはJxckが答えたと思うので実際に私がどんな感じでモデルを定義しているか参考までにさらしてみようと思います。
最小のディレクトリ構成はこんな感じです。
. ├── app.js └── lib └── model ├── index.js
└── models ├── entry.js └── user.js
実行時にNODE_PATHでlibにパスを通します。
(運用時はnpm startとかに書くといいですね)
$ NODE_PATH=lib node app.js
model/index.jsはこうなっていて、models以下のファイルを探して呼ばれたらrequireして返すみたいな感じです。connectのコードを
パクってます。
var fs = require('fs'); fs.readdirSync(__dirname +
'/models').forEach(function(filename){ if (/\.js$/.test(filename))
{ var name = filename.substr(0, filename.lastIndexOf('.'));
exports.__defineGetter__(name, function(){ return
require('./models/' + name); }); } });
model/models/*.jsにはmongooseとかで定義したモデルが返るように書きます。で、app.jsからはこんな感じで使います。
var model = require('model'); app.get('/', function() {
model.entry.find(...); model.user.find(...); });
なにかの参考になれば。

2011年12月29日3:34 kysnm <tokyoinc...@gmail.com>:

--
----------------------------------------
Name : 外村 和仁
Email : k.hok...@gmail.com
URL : http://webtech-walker.com/
----------------------------------------

Kazuhito Hokamura

未読、
2011/12/28 21:23:262011/12/28
To: node...@googlegroups.com
hokacchaです。

すいません。なんか改行がバグったので再送します。

おそらくtakuto1981さんが聞きたかったことはJxckが答えたと思うので
実際に私がどんな感じでモデルを定義しているか参考までにさらして
みようと思います。

最小のディレクトリ構成はこんな感じです。

  .
  ├── app.js
  └── lib
      └── model
          ├── index.js
          └── models
              ├── entry.js
              └── user.js

実行時にNODE_PATHでlibにパスを通します。(運用時はnpm startとかに書きます)

  $ NODE_PATH=lib node app.js

model/index.jsはこうなっていて、models以下のファイルを探して
呼ばれたらrequireして返すみたいな感じです。

rika

未読、
2011/12/28 22:07:072011/12/28
To: nodejs_jp
Jxckさん

うげっ!ほんとだ。。。ご指摘いただいてありがとうございます!
こんなにバージョンアップされてたなんて、、、ちゃんとチェックしないと駄目なんですね。
バージョンあげたら、とりあえず
var const_val = require(process.cwd() + '/const').const_val;
こんなかんじで、絶対パスで動かそうかな。。。

On 12月29日, 午前2:35, yusuke kagiwada <block.rxckin.be...@gmail.com>
wrote:
> Jxck です。
>
> rika さん。
>
> require.paths.unshift() というか、 require.paths 自体が、
> v0.5.2 で無くなっています。https://github.com/joyent/node/blob/master/ChangeLog#L514
>
> Express なんかでも昔は使っていたので、古いサンプルなんかだとまだ残ってるかもしれませんが、
> これの代替というか、統一のために出来たのが node_modules と NODE_PATH なんです。
> repl で下記をすると分かります。
>
> > require.paths;
>
> Error: require.paths is removed. Use node_modules folders, or the NODE_PATH
> environment variable instead.
>
> 2011年12月29日2:08 rika <rika.dev...@gmail.com>:
> ...
>
> もっと読む ≫

Shunsuke Watanabe

未読、
2011/12/29 2:48:492011/12/29
To: nodejs_jp
こんにちは、渡辺といいます。

app.jsでrequireされるモジュールであれば、
グローバル変数の他に、app.setを使うこともできます。

app.jsで以下のように値を設定しておき
app.set('hogehoge', 'hogehoge is fugafuga')

requireされるモジュールで以下のように読みだして使えます
var app = module.parent.exports
var value = app.set('hogehoge')
console.log(value) // hogehoge is fugafuga


それから、各イベントハンドラモジュールのメソッドは一つ一つexportsする他に
module.exports = {
index: function(req,res){} ,
new: function(req,res){},
del: function(req,res){}
}
というふうにも書けます。
個人的にはこちらのほうが好みです。

兼平 卓史

未読、
2011/12/29 22:59:512011/12/29
To: node...@googlegroups.com
皆様、様々なアドバイスありがとうございます。
そもそも基本的な部分が理解出来ていなかったのですが、ようやく飲み込めました。

ポイントとしては、モジュール間でシェアしたい変数をセットするためのモジュールを作成して、
必要に応じてrequireすることで、(C言語でいうところの)共有メモリ的に使うということですね。

また、expressの場合はそもそも以下のように、各モジュールでexpress.createServer()のインスタンス(?)にアクセスすることが多いので、
express.createServer()を上記の共有用モジュールに置き換えて使用してもいいということですね。

 [app.js]
  var app = module.exports = express.createServer();
  
 [appを使う各モジュール]
  var app = module.exports = module.parent.exports;

手軽さの面ではapp.setのように感じるので、当面はこれを使いながら様子をみてみようと思います。
以下、参考までに両方のパターンで書いたコードの一部を添付します。

まず、app.setを使うパターンです。

 [./app.js]


  var test = require('./routes/test');

  var app = module.exports = express.createServer();
  ・
  ・
  app.set('Events', require('./models/eventSchema'));
  ・
  ・


  app.get('/neworder/:id', test.index);
  ・
  ・

 [./routes/test.js]
  exports.index = function(req,res){
   var app = module.exports = module.parent.exports;
   var Events = app.set('Events');


   ・
   ・
   var query = Events.find({});
   ・
   ・
  }

次に、シェア用のモジュールを使うパターンです。
 [./app.js]


  var test = require('./routes/test');

  var app = module.exports = express.createServer();
  ・
  ・
  var share = require('share.js');
  share.Events = require('./models/eventSchema');


  ・
  ・
  app.get('/neworder/:id', test.index);
  ・
  ・

 [./routes/test.js]
  exports.index = function(req,res){
   var app = module.exports = module.parent.exports;
   var share = require('share.js');
   share.Events = 'Events';
   ・
   ・
   var query = share.Events.find({});
   ・
   ・
  }

 [./lib/share.js]
  // Node.js : exports と module.exports の違い(解説編)
  // http://d.hatena.ne.jp/jovi0608/20111226/1324879536
  // これを読む限り"module.exports = {};"は明示的に書く必要無さそうだったので、
  // 書かずにファイルだけ作成して試してみたところ、問題なさそうです。

とても勉強になりました。皆様、ありがとうございました。

Jxck

未読、
2011/12/30 2:41:072011/12/30
To: node...@googlegroups.com
Jxck です。

確かに Express の場合は、 app.set() も使えますね。
ただこれの目的は、簡単な変数(リダイレクト先のパスとか、簡単な文字列とか)は全然良いと思うんですが、
DB コネクションとかまで乗せるのはどうなんでしょう。。


あと、もし Express 依存の場合は、似たような方法でミドルウエアなんかにする方法もあります。
ただ、ミドルウエアはどちらかというと、リクエストのたびに毎回 req, res に対する
中間処理を実行するような感じで使われています。


個人的には app などの標準オブジェクトに、DB コネクションとか色々と付けるのは、
どうなのかなという気がしたりしますがどうなんだろう。。
共有するモジュールは、 Node がもつモジュールの仕組みを使った方が良いと思います。
これは例えば jQuery の $ を(プラグイン以外で)がつがつ拡張しちゃってるイメージ。
(そして Express じゃない場合が解決してない。)


あと、

var app = module.exports = module.parent.exports;


はちょっと構造依存(という意味で密結合?)な気がします。
require する側が、される側にオブジェクトを渡したいなら、

// myLib.js
module.exports = function(app) {
  return function() {
    // app を使う処理。
  }
};

// app.js
var Lib = require('/path/to/myLib');
var app = express.createServer();
var lib = Lib(app);


で渡す方が構造的には良さそうな気がします。

これが stream の場合は、
  • writableStream を渡して、書き込んでもらう。
  • readableStream を渡して、読み込んでもらう。

で階層構造ではなく、インタフェースで分離できます。


まとめると、

  • Express 使う時は app に足すのは、できなくはない。でも Express じゃない場合はまた考えないといけない。
  • app や socket.io の io みたいなのを、あまりいじりすぎるのは行儀が良く無いと思う。(jQ の $ にプラグインじゃない拡張をするのと同じ)
  • この場合は module.parent.exports より、クロージャを使う方が良いと思う。
  • 結果、モジュールの分離は Node が持つモジュールの仕組みを使う方が良いと思う。
いかがでしょう?

(まあ、モジュールの仕組みを使うと、モジュールを色々分割した際に package.json の scripts が長くなりそうなんでそれもどうかという。。)

Shunsuke Watanabe

未読、
2012/01/02 4:05:492012/01/02
To: nodejs_jp
こんばんは、渡辺です。

そうですね、私もmongooseまでapp.setにいれたりはしてません。
各ルートファイルで必要なものだけrequireしています。

例に挙がっていたのがExpressだったので、
Expressではapp.setも使えるよ、程度の話でした。
> - writableStream を渡して、書き込んでもらう。
> - readableStream を渡して、読み込んでもらう。
>
> で階層構造ではなく、インタフェースで分離できます。
>
> まとめると、
>
> - Express 使う時は app に足すのは、できなくはない。でも Express じゃない場合はまた考えないといけない。
> - app や socket.io の io みたいなのを、あまりいじりすぎるのは行儀が良く無いと思う。(jQ の $
> にプラグインじゃない拡張をするのと同じ)
> - この場合は module.parent.exports より、クロージャを使う方が良いと思う。
> - 結果、モジュールの分離は Node が持つモジュールの仕組みを使う方が良いと思う。

Muddy Dixon

未読、
2012/01/03 4:06:142012/01/03
To: nodejs_jp
muddydixonです

あけましておめでとうございます

expressのみの場合ですが、

app.me = {}

のようなオブジェクトを用意して、ここにいろいろぶら下げてモジュール間のやり取りをしたりしています。

app.me.models = require('./models');
app.me.routes = require('./routes');app.me.locales = require('./
locales');

例えば、modelsやlocalesの場合は、こんな感じになっています
=== models/index.js ===
// 設定等引き回し用
var app = module.exports = module.parent.exports;
var models = {};
models['User'] = require('./User'); // models/User.js で mongoose を使っている
場合は、 mongooseからModelを組み立ててexportsしています。
models['Entry'] = require('/Entry');

// exports 上書き
module.exports = models;

===locales/index.js ====
module.exports = {
ja: require('./ja')
, en: require('./en')
};

// app.jsで、locales.jaやlocales.enで簡単に変更できるようにしています
======
routesも似たような感じです。

expressじゃない場合は・・・ASでも見られるような、com.hoge.localesとかしてglobalを使うのがいいのかな・・・?

また、index.jsを使うべきではない論もあるので、なんとも言えませんが、こんな感じでやっています。
ご意見いただければ幸いです。

Yoshihiro Sugi

未読、
2012/01/10 7:19:172012/01/10
To: node...@googlegroups.com
杉(id:sugyan)です。

ちょうど似たようなところで どうするのが良いのか考えていたところだったので、興味深かった方法を書いてまとめてみました。
http://d.hatena.ne.jp/sugyan/20120110/1326197416
何か間違い、補足、ご指摘ありましたらお願いします。


2012年1月3日18:06 Muddy Dixon <muddy...@gmail.com>:

--
杉 義宏
sugi...@gmail.com

Koichi Kobayashi

未読、
2012/01/10 10:00:432012/01/10
To: node...@googlegroups.com
小林 (koichik) です.

主題じゃないけど,require() は .json も読めるんだぜ?

var config = JSON.parse(require('fs').readFileSync('config.json'));

var config = require('./config');

http://nodejs.jp/nodejs.org_ja/docs/v0.6/api/modules.html#file_Modules

Java では普通になった DI (Dependency Injection) とか
IoC (Inversion of Control) ってやっぱり便利だよなーと
このスレを見てて思いました.


--
{
name: "Koichi Kobayashi",
mail: "koi...@improvement.jp",
blog: "http://d.hatena.ne.jp/koichik/",
twitter: "@koichik"
}

Koichi Kobayashi

未読、
2012/01/10 10:12:382012/01/10
To: node...@googlegroups.com
小林 (koichik) です.

> var宣言を外すことで、変数がグローバルになる。

global オブジェクト (クライアントサイドの window みたいな) を
明示的に使うこともできます.

config = JSON.parse(require('fs').readFileSync('config.json'));

global.config = JSON.parse(require('fs').readFileSync('config.json'));

http://nodejs.jp/nodejs.org_ja/docs/v0.6/api/globals.html#global

所詮はグローバルだけど,var 付け忘れたんじゃないんだぜ?感が
少しだけあるかも.かも.

Yoshihiro Sugi

未読、
2012/01/10 22:42:412012/01/10
To: node...@googlegroups.com
杉(sugyan)です。

koichikさん、ご指摘ありがとうございます!
requireで普通にjson読めたんですね…知りませんでした orz  v0.6からですか。
globalオブジェクトを明示的に書くことで、というのもよりハッキリ意図が伝わりますね。

追記しておきます。ありがとうございました。


2012年1月11日0:12 Koichi Kobayashi <koi...@improvement.jp>:

--
杉 義宏
sugi...@gmail.com

Muddy Dixon

未読、
2012/01/11 0:08:582012/01/11
To: nodejs_jp
muddydixonです

jslintでも「assume Node.js」を選べば、
globalが「varついてないよ!」って怒られないです。

http://www.jslint.com/

On Jan 11, 12:42 pm, Yoshihiro Sugi <sugi1...@gmail.com> wrote:
> 杉(sugyan)です。
>
> koichikさん、ご指摘ありがとうございます!
> requireで普通にjson読めたんですね…知りませんでした orz  v0.6からですか。
> globalオブジェクトを明示的に書くことで、というのもよりハッキリ意図が伝わりますね。
>
> 追記しておきます。ありがとうございました。
>
> 2012年1月11日0:12 Koichi Kobayashi <koic...@improvement.jp>:
>
>
>
>
>
>
>
>
>
> > 小林 (koichik) です.
>
> >> var宣言を外すことで、変数がグローバルになる。
>
> > global オブジェクト (クライアントサイドの window みたいな) を
> > 明示的に使うこともできます.
>
> > config = JSON.parse(require('fs').readFileSync('config.json'));
>
> > ↓
>
> > global.config = JSON.parse(require('fs').readFileSync('config.json'));
>
> >http://nodejs.jp/nodejs.org_ja/docs/v0.6/api/globals.html#global
>
> > 所詮はグローバルだけど,var 付け忘れたんじゃないんだぜ?感が
> > 少しだけあるかも.かも.
>
> > On Wed, 11 Jan 2012 00:00:43 +0900, Koichi Kobayashi <koic...@improvement.jp> wrote:
>
> >> 小林 (koichik) です.
>
> >> 主題じゃないけど,require() は .json も読めるんだぜ?
>
> >> var config = JSON.parse(require('fs').readFileSync('config.json'));
>
> >> ↓
>
> >> var config = require('./config');
>
> >>http://nodejs.jp/nodejs.org_ja/docs/v0.6/api/modules.html#file_Modules
>
> >> Java では普通になった DI (Dependency Injection) とか
> >> IoC (Inversion of Control) ってやっぱり便利だよなーと
> >> このスレを見てて思いました.
>
> >> On Tue, 10 Jan 2012 21:19:17 +0900, Yoshihiro Sugi <sugi1...@gmail.com> wrote:
>
> >> > 杉(id:sugyan)です。
>
> >> > ちょうど似たようなところで どうするのが良いのか考えていたところだったので、興味深かった方法を書いてまとめてみました。
> >> >http://d.hatena.ne.jp/sugyan/20120110/1326197416
> >> > 何か間違い、補足、ご指摘ありましたらお願いします。
>
> >> > 2012年1月3日18:06 Muddy Dixon <muddydi...@gmail.com>:
> >> > sugi1...@gmail.com
>
> >> --
> >> {
> >> name: "Koichi Kobayashi",
> >> mail: "koic...@improvement.jp",
> >> blog: "http://d.hatena.ne.jp/koichik/",
> >> twitter: "@koichik"
> >> }
>
> > --
> > {
> > name: "Koichi Kobayashi",
> > mail: "koic...@improvement.jp",
> > blog: "http://d.hatena.ne.jp/koichik/",
> > twitter: "@koichik"
> > }
>
> --
> 杉 義宏
> sugi1...@gmail.com

小林秀和

未読、
2012/01/11 8:23:382012/01/11
To: node...@googlegroups.com
KOBA789 です。
蒸し返し失礼します。

エントリポイントのモジュール(app.js)で、各種インスタンス(instanceA)を exports し、
require.main.exports.instanceA でアクセスする、という手法を思いついたのですがいかがでしょう。

テストするときにエントリポイントが変わってしまって面倒、という欠点はあるのですが。

2012年1月11日14:08 Muddy Dixon <muddy...@gmail.com>:

全員に返信
投稿者に返信
転送
新着メール 0 件