Skip to content
Open
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
120 commits
Select commit Hold shift + click to select a range
af9e537
不要なファイルを削除
taichihub Sep 6, 2024
3a6be0e
package管理
taichihub Sep 6, 2024
5cada90
eslint,prettier設定ファイル
taichihub Sep 6, 2024
9fb1338
Eslint,Prettier周り編集
taichihub Sep 6, 2024
615278a
package.json
taichihub Sep 10, 2024
b652d11
データベース関連の処理をするファイル
taichihub Sep 10, 2024
f6c4670
設定ファイルとログファイル
taichihub Sep 10, 2024
f884a7f
コマンドラインメモアプリの内部処理
taichihub Sep 10, 2024
b3e75c7
memo.js(このファイルでメモアプリ全体を動作させる)
taichihub Sep 10, 2024
86921e2
default exportからnamed exportに変更
taichihub Sep 11, 2024
e9a43b2
不必要なファイルを削除
taichihub Oct 4, 2024
36d8dd8
不必要に削除したファイルを復元
taichihub Oct 4, 2024
38245af
inquirerを最新バージョンに更新
taichihub Oct 24, 2024
bfa9e8d
readline-syncパッケージを削除した
taichihub Oct 24, 2024
2175c99
SQL文の不必要な余分/コメントアウトを削除
taichihub Oct 24, 2024
8fc5a52
メモテーブル作成SQL文が格納されている定数名を修正
taichihub Oct 24, 2024
f03ccb4
memo.dbをgit管理下から除外した
taichihub Oct 24, 2024
c403443
クラス名をDatabaseからMemoDatabaseに変更し具体化した
taichihub Oct 24, 2024
f92aa83
他クラスから参照されないメソッドをprivate化
taichihub Oct 24, 2024
93db65b
db取得のメソッドをgetterで再定義
taichihub Oct 24, 2024
89b6bde
DB関連処理をPromiseオブジェクトを用いて修正
taichihub Oct 24, 2024
20665ea
memo.jsの配置場所変更/import文修正
taichihub Oct 24, 2024
f3288c8
インスタンスメソッド名を具体化してわかりやすくした
taichihub Oct 24, 2024
c194940
データベースファイルパスを相対パスから絶対パスにした
taichihub Oct 24, 2024
86fe4a9
handleError関数を解消し、コールバックベースのメソッドに対してPromiseで非同期実装をした
taichihub Oct 25, 2024
f5ce052
ファイル名をappManagerからmemoAppに変更
taichihub Oct 25, 2024
748d963
ファイル名変更によるimport文の修正
taichihub Oct 25, 2024
9aac457
memoApp.jsで行なっていた無駄なカプセル化を解消し、memo.jsに責務を移行した
taichihub Oct 25, 2024
d5e0e7d
Ctrl+C/Dで強制終了した際に同じエラーメッセージが2回表示されてしまう問題を解決
taichihub Oct 25, 2024
f11b24f
memoHelpers.jsをhelpersフォルダの中に移動
taichihub Nov 12, 2024
5690b10
memoAppフォルダが無駄だったのでなくしてディレクトリ構造を修正
taichihub Nov 12, 2024
be2fa74
無駄な空配列の引数を削除
taichihub Nov 12, 2024
2b281ee
memoAppクラスを作成してオブジェクト指向を取り入れた
taichihub Nov 13, 2024
3164ab7
actions配下の処理をmemoApp.jsに統合
taichihub Nov 19, 2024
66e6ef1
ヘルパーファイ修正
taichihub Nov 19, 2024
0ca3b85
dbファイル
taichihub Nov 19, 2024
1ad1a24
メインの実行ファイル編集
taichihub Nov 19, 2024
bfd1058
gitignoreを修正してdbファイルをgit管理から外した
taichihub Nov 26, 2024
5c6bb39
クエリの定数名を全て名詞形にした
taichihub Nov 26, 2024
6d30a0c
不必要なテンプレート文字列を解消
taichihub Nov 26, 2024
874405b
全メモ取得のクエリを最新順で取得するようにした
taichihub Nov 26, 2024
3b820c0
MemoAppがMemoDatabaseのプロパティに依存しない実装にした
taichihub Nov 27, 2024
8051761
無駄なコメントアウトを削除した
taichihub Nov 27, 2024
19192a2
databaseプロパティをプライベートにした
taichihub Nov 27, 2024
b26f70b
content変数をelse文の中で定義した
taichihub Nov 27, 2024
c09c1a7
databaseプロパティをプライベートにした
taichihub Nov 27, 2024
5ce7476
ログファイルにログを追加
taichihub Nov 27, 2024
98b82e4
add/list/read/delete機能ごとにファイル分け
taichihub Nov 27, 2024
da3d426
memoAppクラスの責務が薄くなったので解消
taichihub Nov 27, 2024
8a9f10b
database.jsをmemoDatabase.jsにしてクラス名とファイル名を一致させた
taichihub Nov 27, 2024
1e9207f
ヘルパーファイル修正
taichihub Nov 27, 2024
bb2dfa5
メインのスクリプトファイルとして修正
taichihub Nov 27, 2024
6feccac
定数をクラス内で直接参照する読み込ませ方で無くした
taichihub Dec 10, 2024
b58dd99
コンストラクタで定義する値を修正
taichihub Dec 10, 2024
8417087
connectメソッドの例外処理を修正
taichihub Dec 10, 2024
67f9782
クラス上部をpublicメソッドに、下部をprivateメソッドを定義するようにした
taichihub Dec 10, 2024
a87ed58
テーブル作成SQL文を修正
taichihub Dec 10, 2024
a038466
logMessageとlogErrorを全ファイルから削除してprocessで修正
taichihub Dec 10, 2024
8b7d0dc
不必要なtry...catchを削除
taichihub Dec 10, 2024
8acbaa5
不必要なtry...catch文を削除
taichihub Dec 10, 2024
5806291
selectedMemoIdのチェックを外した
taichihub Dec 10, 2024
9401114
memoの存在のチェックを外した
taichihub Dec 10, 2024
d519f13
memosのundefinedの懸念が無いためundefined対策のチェックを無くした
taichihub Dec 10, 2024
adc2b7d
不必要なtry...catch、変数の値の存在チェックを無くした
taichihub Dec 10, 2024
5389332
TTYと非TTYのメモ追加ロジックを統一させた
taichihub Dec 10, 2024
5a9d3bf
addMemoの処理をPromiseベースで修正した
taichihub Dec 10, 2024
7b8b2bf
配列名を複数形に修正した 
taichihub Dec 10, 2024
ce0a070
checkIfEmptyをensureNotEmptyに命名を修正 
taichihub Dec 10, 2024
82ceffd
プロパティ名と値の変数が同じ場合に省略記法を使用して修正
taichihub Dec 10, 2024
176ed38
addMemoのエラーハンドリングを処理毎に分割した
taichihub Dec 11, 2024
5bac5d6
不必要なtry...catchを削除した
taichihub Dec 11, 2024
740f2ed
return値をつけた
taichihub Dec 11, 2024
cec5c9d
SIGINTイベントの制御を追加
taichihub Dec 11, 2024
2399ee4
表示するメモ・削除するメモ選択の際にCtrl+C/D入力時の挙動修正
taichihub Dec 11, 2024
e988392
メモが存在しない状態でのr・dオプション使用時の挙動修正
taichihub Dec 11, 2024
d9cc68e
NotTTYのメモ追加時のログ表示ロジックを修正
taichihub Dec 12, 2024
e407227
空白判定ロジックを修正
taichihub Dec 12, 2024
11101c5
queriesとlogMessagesのプロパティを削除した
taichihub Dec 18, 2024
e8603c7
カラム名をmemoからcontentに修正
taichihub Dec 18, 2024
547a645
connectメソッドで捕捉するエラーを明確化
taichihub Dec 18, 2024
6e42d3d
#databaseの初期値を無くした
taichihub Dec 18, 2024
7385187
logフォルダを解体して直接ファイルに埋め込み
taichihub Dec 18, 2024
9fbff4a
exitをreturnに置き換え
taichihub Dec 18, 2024
9e78123
exitをreturnにしたことによって処理が進んでしまう箇所のエラーハンドリング
taichihub Dec 18, 2024
3e0fdb7
processと改行文字を使用している箇所でconsoleを使用
taichihub Dec 18, 2024
ecb8ccc
メモ読み込み→出力時に改行をするように
taichihub Dec 18, 2024
d5aaae1
メモ削除時にメモの表示をしないようにした
taichihub Dec 18, 2024
68b2cc3
SIGINT/EOFの入力にはexitを使用するようにした
taichihub Dec 18, 2024
da5a39e
selectMemoでメモ自体を取得するようにして呼び出し側で再検索の処理を要らなくした
taichihub Dec 18, 2024
8578808
selectMemo内でensureNotEmptyの呼び出しを無くした
taichihub Dec 18, 2024
a1939c3
ensureNotEmpty関数を解体した
taichihub Dec 18, 2024
8ec8247
変数名を複数形にして配列が入ることを想起させるようにした
taichihub Dec 18, 2024
f0a4c37
terminalの設定ロジック修正
taichihub Dec 18, 2024
24d614a
メモがない場合のreturn処理を追加
taichihub Dec 18, 2024
a4c3d34
不必要なconsole.logを削除
taichihub Dec 18, 2024
29bc0c8
配列名をlinesに修正
taichihub Dec 18, 2024
7385c44
使用していない設定項目を削除
taichihub Dec 30, 2024
5753e3b
引数をオブジェクトではなく通常の渡し方にした
taichihub Dec 30, 2024
dbda513
エラーメッセージの出力責務とメソッドの責務の範囲を修正
taichihub Dec 30, 2024
5e37171
メソッド名を簡潔に
taichihub Dec 30, 2024
3da8156
import文を参照順に整列
taichihub Dec 30, 2024
04b26b5
returnの実行位置を調整
taichihub Dec 30, 2024
e4725e6
undefinedのパターンを無くした
taichihub Dec 30, 2024
3ba39f7
不必要な引数を削除
taichihub Dec 30, 2024
07372dd
異常終了時は終了ステータス1で終了させるようにした
taichihub Dec 30, 2024
91edcb8
変数名をcontentsからlinesに修正
taichihub Dec 30, 2024
4fb6530
元の変数名とループ処理内の変数の名前を統一
taichihub Dec 30, 2024
75d1487
resolve時に配列そのままを渡すのではなく文字列のみを渡すように修正
taichihub Dec 30, 2024
dfeb341
tryの範囲を狭めて例外が送出されることが想定される箇所を絞った
taichihub Dec 30, 2024
0d103b4
期待するエラーのみを捕捉するようにした
taichihub Dec 30, 2024
385c5a4
メッセージ出力する箇所の責務を修正
taichihub Dec 30, 2024
a7db3b7
tryの範囲を狭めた
taichihub Dec 30, 2024
4dfcc22
エラー捕捉ロジック修正
taichihub Jan 6, 2025
39c7dec
エラー捕捉の範囲を広げた
taichihub Jan 6, 2025
92760af
誤字修正
taichihub Jan 6, 2025
8cb0312
スローする例外のロジックを修正
taichihub Jan 6, 2025
fbcee09
変数の定義位置を修正
taichihub Jan 6, 2025
e5e4ecb
exitではなくreturnで処理終了を対応
taichihub Jan 6, 2025
219798e
closeイベント処理修正
taichihub Jan 6, 2025
e3b23f9
createInterface修正
taichihub Jan 14, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions 04.class/memoApp/actions/addMemo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Node.js標準モジュール
import { readFileSync } from "fs";
import { createInterface } from "readline";
import { stdin as input, stdout as output } from "process";

// プロジェクト内からのインポート
import { INSERT_MEMO } from "../db/queries.js";
import { FILE_ENCODING } from "../config/settings.js";
import { checkIfEmpty } from "./memoHelpers.js";
import {
ADD_MEMO_LOG_MESSAGES,
COMMON_LOG_MESSAGES,
logMessage,
handleError,
} from "../config/log.js";

export function addMemo(db) {
if (!input.isTTY) {
const inputContent = readFileSync(0, FILE_ENCODING);
saveMemo(db, inputContent);
} else {
const rl = createInterface({ input, output });
logMessage(ADD_MEMO_LOG_MESSAGES.PROMPT);

const content = [];
rl.on("line", (line) => {
content.push(line);
});

rl.on("close", () => {
checkIfEmpty(content, logMessage, ADD_MEMO_LOG_MESSAGES.EMPTY);
const memoContent = content.join("\n");
saveMemo(db, memoContent);
});
}
}

function saveMemo(db, memoContent) {
db.run(INSERT_MEMO, [memoContent.trim()], (err) => {
if (handleError(err, COMMON_LOG_MESSAGES.ERROR)) return;
logMessage(ADD_MEMO_LOG_MESSAGES.SUCCESS);
});
}
26 changes: 26 additions & 0 deletions 04.class/memoApp/actions/appManager.js

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ファイル名とクラス名を一致させてください。他にも処理が定義されているのなら appManager.js という名前でよい可能性もありますが、現状は MemoApp クラスしか定義されていないからです。

@taichihub taichihub Oct 25, 2024

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cafedomancer

修正内容

ファイル名をappManager.jsからmemoApp.jsに変更しました。

質問

  • 他ファイルの命名をキャメルケースで行なっているのでmemoApp.jsにしたのですが、クラス名と全く同じMemoAppのようにパスカルケースにした方が良いでしょうか?
  • また、JavaScriptを用いての開発においてファイル名・変数名・クラス名の命名ケースのデファクトスタンダードを教えていただきたいです。

Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { addMemo } from "./addMemo.js";
import { listMemos } from "./listMemos.js";
import { readMemo } from "./readMemo.js";
import { deleteMemo } from "./deleteMemo.js";

export class MemoApp {
constructor(database) {
this.db = database.getDb();
}

addMemo() {
addMemo(this.db);
}

listMemos() {
listMemos(this.db);
}

readMemo() {
readMemo(this.db);
}

deleteMemo() {
deleteMemo(this.db);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

これらのコードが Database クラスのインスタンスに依存しているのならまだしも、Database クラスのフィールドに依存しているのはカプセル化の原則に反していると思います。db フィールドの API にもし変更があれば、これらの処理はすぐに壊れてしまいます。

@taichihub taichihub Oct 25, 2024

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cafedomancer
こちら、どのような修正を依頼されているのかを正確に汲み取ることができなかったのですが以下のように推測しました。

指摘内容の推測

  • memoApp.js(修正前はappManager.js。以後memoApp.jsと記述する)で無駄なカプセル化・クラス化を行なっている。
  • memoApp.jsの責務がないに等しく、存在している理由が無い。

自分が推測した上記2点の理由から、以下のような対応を行いました。

推測をもとに行った修正

  • memoApp.jsで行なっていた処理をmemo.jsに移行した。
    • addMemo``listMemos``readMemo``deleteMemoをinport
    • DBInstanceMemoDatabaseのインスタンスを格納する
    • connectメソッドでSQLiteデータベースとメモテーブルの作成を行う
    • ゲッターメソッドdatabaseConnectionの実行でconnectメソッドの処理で作成されたデータベースを取得。それをdatabseに格納
    • listMemos(database);のように、それぞれの機能で作成したメソッドの引数にdatabaseを渡して実行

memo.js

#!/usr/bin/env node

import { MemoDatabase } from "./memoApp/db/database.js";
import { addMemo } from "./memoApp/actions/addMemo.js";
import { listMemos } from "./memoApp/actions/listMemos.js";
import { readMemo } from "./memoApp/actions/readMemo.js";
import { deleteMemo } from "./memoApp/actions/deleteMemo.js";
import { OPTIONS } from "./memoApp/config/settings.js";

const DBInstance = new MemoDatabase();
await DBInstance.connect();
const database = DBInstance.databaseConnection;
const args = process.argv.slice(2);

switch (args[0]) {
  case OPTIONS.LIST:
    listMemos(database);
    break;
  case OPTIONS.READ:
    readMemo(database);
    break;
  case OPTIONS.DELETE:
    deleteMemo(database);
    break;
  default:
    addMemo(database);
    break;
}

そもそもなぜ無駄なカプセル化を行ったのか?

現状はメモ作成・メモリスト表示・各メモ内容表示・メモ削除の4つの機能だが、もし今以上に機能が増えることを仮定すると、このメモアプリがどのような機能で構成されているものなのかが見えずらいと感じ、MemoAppというクラスを作成してその中に各機能ごとに関数を呼び出して「メモアプリ内の機能の一覧化」を行いたかったということになります。

質問・依頼

  • このフィードバックに対する修正は自分の推測をもとに行ったので、求められている修正かどうかがわからないのでまずはその点の評価を行なっていただきたいです。
  • また、db フィールドの API にもし変更があれば、これらの処理はすぐに壊れてしまいます。とコメントしていただいたと思うのですが、なぜdb フィールドの API にもし変更があれば、これらの処理はすぐに壊れてしま雨のかが検討もつかない状態なので、根本の部分から解説していただきたいです。

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

import している関数に処理を委譲するような形で実装しているのはなぜでしょう? 理由を教えてください。また、これだとクラスから単に他の関数を呼び出しているだけで、オブジェクトが状態を持っていないので、クラスを定義する意味がないですね。

@taichihub taichihub Oct 25, 2024

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cafedomancer
以下のコメントでこちらのフィードバックに対する返信も一緒にしてますのでご確認よろしくお願いいたします🙇‍♂️
#5 (comment)

}
12 changes: 12 additions & 0 deletions 04.class/memoApp/actions/deleteMemo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { DELETE_MEMO_BY_ID } from "../db/queries.js";
import { DELETE_MEMO_LOG_MESSAGES } from "../config/log.js";
import { handleMemoAction } from "./memoHelpers.js";

export async function deleteMemo(db) {
await handleMemoAction(
db,
DELETE_MEMO_LOG_MESSAGES.PROMPT,
DELETE_MEMO_BY_ID,
() => DELETE_MEMO_LOG_MESSAGES.SUCCESS,
);
}
19 changes: 19 additions & 0 deletions 04.class/memoApp/actions/listMemos.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { SELECT_ALL_MEMOS } from "../db/queries.js";
import { checkIfEmpty } from "./memoHelpers.js";
import {
LIST_MEMOS_LOG_MESSAGES,
COMMON_LOG_MESSAGES,
logMessage,
handleError,
} from "../config/log.js";

export function listMemos(db) {
db.all(SELECT_ALL_MEMOS, [], (err, rows) => {
if (handleError(err, COMMON_LOG_MESSAGES.ERROR)) return;
checkIfEmpty(rows, logMessage, LIST_MEMOS_LOG_MESSAGES.NOT_FOUND);
logMessage(LIST_MEMOS_LOG_MESSAGES.TITLE);
rows.forEach((row) => {
logMessage(`・${row.memo.split("\n")[0]}\n`);
});
});
}
69 changes: 69 additions & 0 deletions 04.class/memoApp/actions/memoHelpers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// 外部ライブラリ
import inquirer from "inquirer";

// プロジェクト内からのインポート
import { SELECT_ALL_MEMOS } from "../db/queries.js";
import {
COMMON_LOG_MESSAGES,
MEMO_HELPERS_LOG_MESSAGES,
logMessage,
handleError,
} from "../config/log.js";

export async function handleMemoAction(
db,
promptMessage,
query,
successMessage,
) {
const selectedMemoId = await selectMemo(db, promptMessage);

if (selectedMemoId) {
db.get(query, [selectedMemoId], (err, result) => {
if (handleError(err, COMMON_LOG_MESSAGES.ERROR)) return;
if (result) {
logMessage(successMessage(result));
}
});
}
}

export async function selectMemo(db, message) {
return new Promise((resolve, reject) => {
db.all(SELECT_ALL_MEMOS, [], async (err, rows) => {
if (handleError(err, COMMON_LOG_MESSAGES.ERROR)) return reject(err);
checkIfEmpty(
rows,
logMessage,
MEMO_HELPERS_LOG_MESSAGES.NO_MEMOS,
resolve,
);

const choices = rows.map((row) => ({
name: row.memo.split("\n")[0],
value: row.id,
}));

const answer = await inquirer.prompt([
{
type: "list",
name: "selectedMemo",
message: message,
choices: choices,
},
]);

resolve(answer.selectedMemo);
});
});
}

export function checkIfEmpty(array, logMessage, message, resolve = null) {
if (array.length === 0) {
logMessage(message);
if (resolve) {
return resolve(null);
}
return;
}
}
12 changes: 12 additions & 0 deletions 04.class/memoApp/actions/readMemo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { SELECT_MEMO_BY_ID } from "../db/queries.js";
import { READ_MEMO_LOG_MESSAGES } from "../config/log.js";
import { handleMemoAction } from "./memoHelpers.js";

export async function readMemo(db) {
await handleMemoAction(
db,
READ_MEMO_LOG_MESSAGES.PROMPT,
SELECT_MEMO_BY_ID,
(row) => `\n${row.memo}\n`,
);
}
61 changes: 61 additions & 0 deletions 04.class/memoApp/config/log.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// ログ出力関数(標準出力)
export function logMessage(message) {
process.stdout.write(message);
}

// ログ出力関数(エラー出力)
export function logError(message) {
process.stderr.write(message);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

console.logconsole.error をそのまま使えばよいだけな気がしたので、これらの関数が何のために定義されているのか分かりませんでした。理由を教えてください。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cafedomancer
ただただprocess.stderr.write(message);が何回も出てくると煩わしいかな、と思いからこのような行いました。


export function handleError(err, logMessage) {
if (err) {
logError(`${logMessage}${err.message}\n`);
return true;
}
return false;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

非同期処理のプラクティスで例外処理について触れているのだから、エラーは boolean ではなく例外として取り扱うべきでしょう。handleError という関数名から boolean の戻り値を持つことも推測できないです。

@taichihub taichihub Oct 25, 2024

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cafedomancer
以下2点の理由からhandleError関数を無くし、元々handleError関数が呼び出されていた箇所についてはtry/catchで例外処理を実装したり、エラーメッセージを出力させてreturnするように修正しました。

  • 現状handleError関数でエラーをbooleanで扱っていることがまず不適切
  • この関数を呼び出しても、それが例外処理をしているかどうかは呼び出す側では読み取りづらい

また、runメソッド・allメソッド・getメソッドについてはコールバックベースのメソッドなのでPromiseでラップしawaitを使用してPromiseに基づく非同期実装をする形に修正いたしました。


// 共通ログメッセージ
export const COMMON_LOG_MESSAGES = {
ERROR: "エラー: ",
};

// database.js 専用のログメッセージ
export const DATABASE_LOG_MESSAGES = {
SETUP_ERROR: "テーブル作成中にエラーが発生しました: ",
CONNECTION_ERROR: "データベース接続エラー: ",
SUCCESS: "データベースのセットアップが完了しました。\n",
};

// addMemo.js 専用のログメッセージ
export const ADD_MEMO_LOG_MESSAGES = {
PROMPT:
"メモの内容を入力してください(終了するにはCtrl + Dを押してください):\n",
EMPTY: "メモの内容が空です。保存されませんでした。\n",
SUCCESS: "メモを追加しました。\n",
};

// listMemos.js 専用のログメッセージ
export const LIST_MEMOS_LOG_MESSAGES = {
TITLE: "メモ一覧:\n",
NOT_FOUND: "メモが存在しません。\n",
};

// readMemo.js 専用のログメッセージ
export const READ_MEMO_LOG_MESSAGES = {
PROMPT: "表示するメモを選んでください:",
NOT_FOUND: "メモが見つかりません。\n",
};

// deleteMemo.js 専用のログメッセージ
export const DELETE_MEMO_LOG_MESSAGES = {
PROMPT: "削除するメモを選んでください:",
SUCCESS: "メモを削除しました。\n",
NOT_FOUND: "削除するメモが見つかりません。\n",
};

// memoHelpers.js 専用のログメッセージ
export const MEMO_HELPERS_LOG_MESSAGES = {
NO_MEMOS: "メモが存在しません。\n",
};
12 changes: 12 additions & 0 deletions 04.class/memoApp/config/settings.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// データベースのパス
export const DATABASE_PATH = "./db/memo.db";

// ファイルのエンコーディング
export const FILE_ENCODING = "utf-8";

// コマンドラインオプション設定
export const OPTIONS = {
LIST: "-l",
READ: "-r",
DELETE: "-d",
};
26 changes: 26 additions & 0 deletions 04.class/memoApp/db/database.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// 外部ライブラリ
import sqlite3 from "sqlite3";

// プロジェクト内からのインポート
import { CREATE_TABLE_MEMOS } from "./queries.js";
import { DATABASE_LOG_MESSAGES, handleError } from "../config/log.js";
import { DATABASE_PATH } from "../config/settings.js";

export class Database {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Database というクラス名だと、データベースに関する内容を広く扱うクラスのように見えます。しかし、実装を見る限りはメモアプリの内容に特化しているので、Database という名前はちょっと抽象的すぎると思います。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cafedomancer
DatabaseMemoDatabaseに変更し、具体化しました。

constructor() {
this.db = new sqlite3.Database(DATABASE_PATH, (err) => {
if (handleError(err, DATABASE_LOG_MESSAGES.CONNECTION_ERROR)) return;
this.setupDatabase();
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

非同期処理の課題で、コールバックベースのものを Promise として扱うことを学んでいるのだから、Promise として扱うようにしてほしいです。

@taichihub taichihub Oct 24, 2024

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cafedomancer
MemoDatabaseクラス内の処理を以下のように修正しました。

constructor

  • dbにnullを格納市初期化
  • nullを格納することでundefinedの状態をなくすことができたりTypeErrorになる可能性をなくすことができる

connect

  • このインスタンスメソッドでデータベースの作成と、メモテーブルの作成の処理を行なっている。
  • async/awaitで実行

openDatabase

  • SQLiteのデータベースを作成するメソッド
  • 他クラスから参照しないのでprivate化

createMemosTable

  • memosテーブルを作成するメソッド
  • 他クラスから参照しないのでprivate化
export class MemoDatabase {
  constructor() {
    this.db = null;
  }

  async connect() {
    try {
      this.db = await this.#openDatabase(DATABASE_PATH);
      await this.#createMemosTable();
    } catch (err) {
      handleError(err, DATABASE_LOG_MESSAGES.CONNECTION_ERROR);
    }
  }

  #openDatabase(path) {
    return new Promise((resolve, reject) => {
      const db = new sqlite3.Database(path, (err) => {
        if (err) {
          reject(err);
        } else {
          resolve(db);
        }
      });
    });
  }

  #createMemosTable() {
    return new Promise((resolve, reject) => {
      this.db.run(CREATE_MEMOS_TABLE, (err) => {
        if (err) {
          reject(err);
        } else {
          resolve();
        }
      });
    });
  }

  get databaseConnection() {
    return this.db;
  }
}

memo.js

インスタンス作成後connectメソッドをawaitを用いて呼び出すことで、SQLiteデータベース・memosテーブルが正常に作成された後にメモアプリが起動する流れになるようになりました。

const database = new MemoDatabase();
await database.connect();

}

setupDatabase() {
this.db.run(CREATE_TABLE_MEMOS, (err) => {
if (handleError(err, DATABASE_LOG_MESSAGES.SETUP_ERROR)) return;
});
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

他のクラスから実行されないメソッドは可視性を private にしてください。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cafedomancer
メソッドの先頭に#をつけることでprivateメソッド化しました。
参考記事:https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Classes/Private_properties#%E6%A7%8B%E6%96%87

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

node-sqlite3 の処理がコールバックベースで扱われているので、現状の実装だと CREATE TABLE の処理の完了を待つことなく、後続の処理が実行されてしまうと思いますよ。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cafedomancer
下記の返信内で同時に対応しました。
#5 (comment)


getDb() {
return this.db;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getter を定義する仕組みがあるのでそれを使ってください。

@taichihub taichihub Oct 24, 2024

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cafedomancer
getterを定義して使用する形にしました。

database.js修正対象箇所

  get databaseConnection() {
    return this.db;
  }

appManager.js修正対象箇所

  constructor(database) {
    this.db = database.databaseConnection;
  }
参考記事

}
Binary file added 04.class/memoApp/db/memo.db

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

データベースファイルはコミットしないでください。データベースのデータに変更があるたびに git の差分が生じるからです。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cafedomancer
以下の流れでmemo.dbをgitの追跡対象から除外しました。

  • .gitignoreファイルを作成しmemoApp/db/memo.dbを記載
  • git rm --cached memo.dbを実行し、gitの追跡対象から除外
  • コミット

Binary file not shown.
27 changes: 27 additions & 0 deletions 04.class/memoApp/db/queries.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// メモのテーブルを作成するクエリ

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

コードを読めば分かることをコメントとして残すのはやめましょう。コードやコメントの変更にともなって両者の乖離が発生すると、どちらの内容が正しいのか分からなくなってしまうからです。コードからは分からないことのみ、コメントとして残すようにしてください。

@taichihub taichihub Oct 24, 2024

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cafedomancer
今回使用するSQL文においては全て読めばわかる範囲のものだと判断しましたので、コメントアウトについては全て削除しました。

export const CREATE_TABLE_MEMOS = `CREATE TABLE IF NOT EXISTS memos (id INTEGER PRIMARY KEY AUTOINCREMENT, memo TEXT)`;
export const INSERT_MEMO = `INSERT INTO memos (memo) VALUES (?)`;
export const SELECT_ALL_MEMOS = `SELECT id, memo FROM memos`;
export const SELECT_MEMO_BY_ID = `SELECT * FROM memos WHERE id = ?`;
export const DELETE_MEMO_BY_ID = `DELETE FROM memos WHERE id = ?`;

コードを読めば分かることをコメントとして残すのはやめましょう。

コードからは分からないことだったり、どうしても可読性が低くなってしまう場合や視認性が低いコードを書かなければいけない場合にのみ使用していきます。

export const CREATE_TABLE_MEMOS = `

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

この定数だけ、他の定数と名前付けが揃っていないです。他と合わせるのなら、CREATE_MEMOS_TABLE でしょう。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cafedomancer
命名時、品詞等に気をつけて命名していきます🙇‍♂️

CREATE TABLE IF NOT EXISTS memos (
id INTEGER PRIMARY KEY AUTOINCREMENT,
memo TEXT
)
`;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

テンプレート文字列を使って複数行の文字列を定義し、かつそれをインデントすると、インデント分の余分な空白が先頭に挿入されてしまいます。SQL に余分な空白が含まれていて実行上の問題はないものの、不要なものなので好ましくはないです。

@taichihub taichihub Oct 24, 2024

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cafedomancer
全てのSQL文を1行にまとめて余分な空白が含まれないように修正しました。

export const CREATE_TABLE_MEMOS = `CREATE TABLE IF NOT EXISTS memos (id INTEGER PRIMARY KEY AUTOINCREMENT, memo TEXT)`;
.......


// メモを作成するクエリ
export const INSERT_MEMO = `
INSERT INTO memos (memo) VALUES (?)
`;

// メモを全て取得するクエリ
export const SELECT_ALL_MEMOS = `
SELECT id, memo FROM memos
`;

// メモをIDで取得するクエリ
export const SELECT_MEMO_BY_ID = `
SELECT * FROM memos WHERE id = ?
`;

// メモをIDで削除するクエリ
export const DELETE_MEMO_BY_ID = `
DELETE FROM memos WHERE id = ?
`;
22 changes: 22 additions & 0 deletions 04.class/memoApp/memo.js

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

このファイルは 04.class ディレクトリの下に置いてください。また、$ ./memo.js の形式で実行できるようになっていないです。

@taichihub taichihub Oct 24, 2024

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cafedomancer
以下のような対応をしました。

  • memoApp配下から04.class配下に位置を修正しました。それに伴いmemo.js内のimport文も修正
  • memo.jsの最上行にシェバン行を追加しました。#!/usr/bin/env node(このプログラムがNode.jsで実行すると認識させるため)
  • chmod +x memo.jsを実行し、memo.jsに実行権限を与えることで./memo.jsで実行できるようになった

Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Database } from "./db/database.js";
import { MemoApp } from "./actions/appManager.js";
import { OPTIONS } from "./config/settings.js";

const database = new Database();
const memoApp = new MemoApp(database);
const args = process.argv.slice(2);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

理解度を確認するために slice(2) を実行している理由を教えてください。また、カレンダーの提出物では、コマンドライン引数を処理するためにパッケージが使われていたはずですが、ここではそうしていない理由を教えてください。

@taichihub taichihub Oct 24, 2024

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cafedomancer
カレンダーのプラクティスでは、年と月の指定オプションで最大4要素含まれる仕様でしたが、今回のメモアプリはオプションが-l-r-dのように1つで固定だったため、sliceメソッドでオプション以外の部分を削ることで簡単にオプションだけを抜き取ろうとしました。

こちら、カレンダーのプラクティスと同様にオプション周りの実装はcommanderを使用した方が良いでしょうか?


switch (args[0]) {
case OPTIONS.LIST:
memoApp.listMemos();
break;
case OPTIONS.READ:
memoApp.readMemo();
break;
case OPTIONS.DELETE:
memoApp.deleteMemo();
break;
default:
memoApp.addMemo();
break;
}
Loading