CLI版メモアプリを作成(Node.js)

今回は、Node.jsでメモの追加・一覧表示・参照・削除ができるメモアプリ(CLI版)を作成しました。

データの保存先にはsqlite3、JavaScriptのクラス構文を使って作成しました。

要件

1. メモの追加

標準入力に入ってきたテキストを新しいメモとして追加する。

$ echo 'メモの内容' | app.js
2. メモの一覧を表示

それぞれのメモの最初の行のみを表示する。

$ memo.js -l
メモ1
今日の日記
晩ご飯のレシピ
3. メモの参照

選択したメモの全文が表示される。
(カーソルを合わせた時点もしくはメモを選択した時にメモの全文が表示される)

$ memo.js -r
Choose a note you want to see:
  メモ1
  今日の日記
> 晩ご飯のレシピ

晩ご飯のレシピ
カレー
豚肉
じゃがいも
人参
タマネギ
カレールー
4. メモの削除

選択したメモが削除される。

$ memo.js -d
Choose a note you want to delete:
  メモ1
  今日の日記
> 晩ご飯のレシピ

必要なパッケージをインストール

$ npm install --save enquirer minimist sqlite3
  • enquirer: ユーザからの入力を受け取るためのライブラリ。
  • minimist: コマンドライン引数を解析するライブラリ。
  • sqlite3: SQLite3データベースと連携するためのライブラリ。

memoDB.js

  • SQLite3データベースとのインタラクション(相互作用)を処理するクラス。
  • serialize()
    • 指定したテーブルが存在しない場合は、新しいテーブルを作成。
      テーブルは、自動的に増加する主キーのidとテキストデータを保持するtextの2つのカラムを持っている。

memo.js

  • メモ操作に関連するクラス。
    MemoDBクラスのインスタンスを引数として受け取り、Memoクラス内でデータベースの操作を行う。

menu.js

  • メモ選択のメニューを表示するクラス。
  • コンストラクタにchoices(選択肢)を引数として渡す。(メモの配列を表す)
    配列の各要素に対して、最初の行を名前(name)、idを値(value)とするオブジェクトを作成し、その結果を新しい配列として保存。

  • chooseMemoId()

    • ユーザーにメモを選択させるためにprompt関数を使用。
    • promptはenquirerライブラリを使用し、メモの選択メニューを表示し、ユーザーからの選択を受け取る。以下のキーを持たせる。
      • typeは入力の種類を指定し、ここでは"select"を指定。
      • nameはプロンプトの名前を指定。
      • messageはプロンプトのメッセージを指定。
      • choicesには先程作成したchoices配列を指定。
      • result関数では、選択した選択肢の値(この場合、メモのID)を返す。

cli.js

  • main関数を定義
    • MemoDBMemoクラスのインスタンスを作成し、minimistコマンドライン引数をパース。
    • process.argv.slice(2)は最初の2つの引数(nodeのパスと実行ファイルのパス)を除いた残りの引数を取得。
  • process.stdin.isTTYは、現在のプロセスがTTY(端末)からの入力を受け取っているかどうかをチェック。否定演算子(!)を前に付けているため、このif文はTTYからの入力がない場合、つまりパイプやリダイレクションからの入力がある場合に実行される。その入力を読み取り、新しいメモを作成。作成が完了したら、その内容をコンソールに表示。
  • 最後に、標準入力からデータを非同期に読み込むreadStdin関数と、メインの関数を実行するmain()を定義。readStdin関数はPromiseを返すため、データが全て読み込まれるまで待機し、その後処理を進めることができる。

JavaScriptのclass構文について

1. そもそもクラスとは?
  • プログラミングにおける「クラス」とは、オブジェクトを作るための設計図のようなもの。
    オブジェクト指向プログラミングでは、クラスを使ってデータとそれを操作するメソッド(関数)を一緒にまとめる。
2. JavaScriptのクラスはシンタックスシュガー
  • JavaScriptにおいても、クラスはデータとメソッドを一緒にまとめる役割を果たす。
  • JavaScriptのクラスは、他の言語のクラス構造と同じように見えるが、内部的には「プロトタイプベースのオブジェクト指向」を使用。
    • 「プロトタイプベース」...オブジェクトが他のオブジェクトを元にして作られることを意味する。これはクラスベースの言語と異なり、クラスを必要としないのが特徴。クラス構文はこのプロトタイプベースのオブジェクト指向を扱いやすくするための糖衣構文。(シンタックスシュガー

まとめ✏️

  • 非同期処理は、一部の時間のかかる操作(通常はI/O操作、たとえばデータベースへのクエリ、ファイルの読み書き、ネットワークリクエストなど)をバックグラウンドで実行し、それが完了するのを待つことなく他の操作を続けるために使用。

    • 上記のソースコードでは、データベースの操作(メモの作成、表示、削除など)と標準入力からの読み取りが非同期処理として行われている。こういった操作は、それぞれ時間がかかる可能性があるため、非同期処理を行う。特にデータベースの操作は、データベースエンジンが操作を完了するまでに時間がかかる場合がある。

    • 非同期処理を行うことで、アプリケーションは操作が完了するのを待つことなく、他の処理を行うことができる。これにより、アプリのパフォーマンスが向上、レスポンス時間が短縮され、ユーザーエクスペリエンスが向上。

    • 上記のソースコードの場合、たとえばメモの作成や表示を行うときに、それが完了するまで他の操作(たとえば別のメモの表示や削除)がブロックされることはない。これは非同期処理のおかげ。

【参考】
クラス - JavaScript | MDN
クラス(Class) - とほほのWWW入門
Node.jsでのCLIの作り方と便利なライブラリまとめ - Qiita
mapbox/node-sqlite3: Asynchronous, non-blocking SQLite3 bindings for Node.js