NodeSchoolのlearnyounodeを解いてみる #1

# はじめに

node.jsを勉強すべく、[NodeSchool](http://nodeschool.io/)の[learnounode](https://github.com/workshopper/learnyounode)をやってみました。長くなりそうなのでいくつかに分けて投稿します。

NodeSchoolについてはこちらの記事を参照ください。

- [NodeSchoolでJS力をアップしよう！](http://qiita.com/kenfdev/items/277ce7a7310ac246b3b3)

![image](https://qiita-image-store.s3.amazonaws.com/0/37467/48d4c1f7-0902-aa41-f379-5362cc271563.png)


# HELLO WORLD
- コンソールにHello Worldと表示する、それだけです。

```program.js
console.log("Hello World");
```

# BABY STEPS
- 1つ以上の整数をコマンドライン引数として受けとり、それらを足し合わせてコンソールに出力する
- コマンドライン引数を読むためには`process`というオブジェクトの`argv`というプロパティにアクセスする
- 配列の0番目、1番目はnodeのパス、実行ファイルのパスが表示されるので、入力したコマンドライン引数は`process.argv[2]`から

```program.js
var result = 0;

for (var i =2; i < process.argv.length; i++) {
   result = result + Number(process.argv[i]);
}

console.log(result);
```

# MY FIRST I/O!
- `fs`モジュールを使ってファイルを読みこみ、ファイルにある改行文字数を出力する
- `readFileSync()`はBufferオブジェクトを返すので、`toString()`メソッドでString型に変換
- `split()`関数で改行文字ごとにファイルを分割、lengthでその数を取る
   - -1するのは最後の改行文字が含まれてしまうため

```program.js
var fs = require("fs");
var file = fs.readFileSync(process.argv[2]);

//Buffer型をString型に変換後、改行文字で分割
var result = file.toString().split("\n").length - 1;

console.log(result);
```

- 実行時に上記エラーが出た。理由はコマンドライン引数に値を入れてなかったから
  - `program.js`で`process.argv[2]`を設定したが、コマンドでそこの引数を入力していなかったため、上記エラーが発生した
  - `node program.js ./program.js`と引数を与えることで正常に実行できるように

```
node program.js
fs.js:262
  binding.open(pathModule._makeLong(path),
          ^
TypeError: path must be a string
    at TypeError (native)
    at Object.fs.readFile (fs.js:262:11)
    at Object.<anonymous> (/Users/01006524/Development/src/localhost/learnyounode/program.js:21:4)
    at Module._compile (module.js:398:26)
    at Object.Module._extensions..js (module.js:405:10)
    at Module.load (module.js:344:32)
    at Function.Module._load (module.js:301:12)
    at Function.Module.runMain (module.js:430:10)
    at startup (node.js:141:18)
    at node.js:980:3
```



# MY FIRST ASYNC I/O!

- 上記を非同期で書くパターン。
- 非同期には`readFileSync()`ではなく、`readFile()`メソッドを用いて、コールバック関数でつなぐ。

```program.js
var fs = require("fs");
var file = process.argv[2];

fs.readFile(file, function(err, data) {
  var result = data.toString().split("\n").length - 1;
  console.log(result);
});
```

# FILTERED LS
- 拡張子によってフィルタしたファイルのリストをコンソールに出力する
- コマンドライン引数の第1引数はフォルダのパス、第2引数は.なしの拡張子名とする
  - 例：`node program.js hoge/hogehoge txt`
- `readdir(path, callback)`関数を用いて指定したパスのファイルを読み込む
- コマンドライン第2引数で指定した拡張子とディレクトリ内にファイル名の拡張子が一致していたらconsole.logに表示

```program.js
var fs = require("fs");
//pathモジュールのメソッドを使うために読み込み
var path = require("path");

var dir = process.argv[2];
var extension = process.argv[3];

fs.readdir(dir, function(err, list) {
  list.forEach(function(file) {

  if (path.extname(file) === '.' + extension) {
    console.log(file);
    }
  });
});
```

# MAKE IT MODULAR
- 内容は前回と同じ、ただし今回は`modules`の概念を使う
- 今まで書いていた処理をモジュール化する
- モジュール化する上での4つの制約
    - 正しい引数を取る関数を定義してください。
    - エラー、もしくはデータを引数に取るコールバック関数を一度だけ呼び出してください。 
    - 2つ目の制約以外には何も変えないでください（グローバル変数や標準出力など）。
    - 発生する可能性のあるエラーは全てコールバック関数に渡してください。 

```program.js
//自作モジュール呼び出し
var filter = require("./filter.js");
var dir = process.argv[2];
var fileEx = process.argv[3];

filter(dir, fileEx, function(err, list) {
  if(err) throw err;

  list.forEach(function(file) {
    console.log(file);
  });

});
```

```filter.js
var fs = require("fs");
var path = require("path");

module.exports = function(dir, fileEx, callback) {

  fs.readdir(dir, function(err, list) {
    var files = [];
    if(err) return callback(err);
      for(var i in list) {
        if(path.extname(list[i]) == '.' + fileEx) {
          files.push(list[i]);
        }
      }

    callback(null, files);
  });
}
```