n_hachiのメモ

メモです。

Express web framework (Node.js/JavaScript)の勉強 part4、 directory-structure

The generated projectを読み進める。
日本語版の資料をかいつまんだような内容なので、詳細を知りたい人はこっちを見ることを勧める。

ディレクトリ構造

expressおよび関連するコマンドを実行することで以下のようなディレクトリ構造になる。
注意:一部コマンドの出力結果は省略している。

$ express express-locallibrary-tutorial --view=pug --git

   create : express-locallibrary-tutorial/
   create : express-locallibrary-tutorial/public/
   create : express-locallibrary-tutorial/public/javascripts/
   create : express-locallibrary-tutorial/public/images/
   create : express-locallibrary-tutorial/public/stylesheets/
   create : express-locallibrary-tutorial/public/stylesheets/style.css
   create : express-locallibrary-tutorial/routes/
   create : express-locallibrary-tutorial/routes/index.js
   create : express-locallibrary-tutorial/routes/users.js
   create : express-locallibrary-tutorial/views/
   create : express-locallibrary-tutorial/views/error.pug
   create : express-locallibrary-tutorial/views/index.pug
   create : express-locallibrary-tutorial/views/layout.pug
   create : express-locallibrary-tutorial/.gitignore
   create : express-locallibrary-tutorial/app.js
   create : express-locallibrary-tutorial/package.json
   create : express-locallibrary-tutorial/bin/
   create : express-locallibrary-tutorial/bin/www

   change directory:
     $ cd express-locallibrary-tutorial

   install dependencies:
     $ npm install

   run the app:
     $ DEBUG=express-locallibrary-tutorial:* npm start

$ cd express-locallibrary-tutorial
$ npm install

up to date, audited 123 packages in 2s

6 packages are looking for funding
  run `npm fund` for details

3 low severity vulnerabilities

To address all issues, run:
  npm audit fix --force

Run `npm audit` for details.

$ tree -F
.
├── app.js
├── bin/
│   └── www*
├── node_modules/
│   ├── たくさんのサブディレクトリおよびファイル(表示は省略)
│   ︙
│
├── package.json
├── package-lock.json
├── public/
│   ├── images/
│   ├── javascripts/
│   └── stylesheets/
│       └── style.css
├── routes/
│   ├── index.js
│   └── users.js
└── views/
    ├── error.pug
    ├── index.pug
    └── layout.pug

405 directories, 4784 files

nodemon

今後express-locallibrary-tutorial以下を編集していくのだが、編集するたびアプリを起動し直すのは面倒である。
この煩わしさを解消するnodemonというツールがある
下記コマンドを実行し、nodemonをインストールする。

npm install -g nodemon

もし開発環境以下(?)にインストールしたい場合は-gの代わりに--save-devを使う。
nodemonを使ってアプリを起動すると、ファイル編集時に自動で反映してくれるためアプリの再起動の手間が省ける。

nodemon ./bin/www

package.json

package.jsonはアプリの依存情報を保持する
nodemonをインストールした直後のpackage.jsonの内容を以下に示す。
タイミングによってバージョン番号は変化するので、次同じコマンドを実行しても下記と同じ結果になるとは限らない(と思う)。

{
  "name": "express-locallibrary-tutorial",
  "version": "0.0.0",
  "private": true,
  "scripts": {
    "start": "node ./bin/www",
  },
  "dependencies": {
    "cookie-parser": "~1.4.4",
    "debug": "~2.6.9",
    "express": "~4.16.1",
    "http-errors": "~1.6.3",
    "morgan": "~1.9.1",
    "nodemon": "^2.0.7",
    "pug": "2.0.0-beta11"
  }
}

npm run-script

参考:npm-run-script | npm Docs
npm run-scriptコマンドによってpackage.json"scripts"オブジェクトに記載されたコマンドを実行できる。
上記の場合、"scripts"というオブジェクトの中にキー"start"、バリュー"node ./bin/www"が存在しているため、npm run-script startと入力するとnode ./bin/wwwが実行される。
引数なしの場合は、以下のように利用可能なコマンドが表示される。

$ npm run-script
Lifecycle scripts included in express-locallibrary-tutorial:
  start
    node ./bin/www

以下npm run-script -hの実行結果からわかるようにrun-scriptrunというエイリアスを持っている。
入力文字の短さからnpm runを使ったほうが良いだろう(npm run-scriptのほうがnpm runより良いケースってあるのだろうか?)。

$ npm run-script -h
npm run-script <command> [-- <args>]

aliases: run, rum, urn

scripts

package.json内のscriptsを以下のように編集する

"scripts": {
  "start": "node ./bin/www",
  "devstart": "nodemon ./bin/www",
  "serverstart": "DEBUG-express-lcallibrary-tutorial:* npm run devstart"
}

"start"で始まる行の末尾にカンマ(,)が追記されていることに注意する。
以降はnpm run serverstartと実行することでnodemonでアプリが実行される

/bin/www

ファイル/bin/wwwはアプリケーションのエントリポイントである。
下記コードによって、後述するapp.jsを呼び出す。

#!/usr/bin/env node

/**
 * Module dependencies.
 */

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

残りのコードでapp.jsを使用するHTTPサーバの設定、ポートの設定、リスニングやサーバエラーのレポートを開始するらしい。

/app.js

expressアプリケーションオブジェクト(appという名前が慣習である)を生成、アプリケーションおよびミドルウェアの設定、appをエクスポートする。
上記した/bin/wwwはエクスポートされたappを使用する。

var express = require('express');
...
var app = express();
...
module.exports = app;

app.jsの詳細を見ていく。
最初にいくつかのnodeライブラリを取り込む。

var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');

その後require()を使い、routesディレクトリからモジュールを取り込む。
これらファイルには、特定のURLへのリクエストを受け取った場合の処理が書かれている。

var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');

次にappオブジェクトをexpressモジュールを使い作成し、view(template)エンジンを設定する。
エンジンの設定は2段階に分かれる。
最初に値'views'を使ってテンプレートが保存されているフォルダを指定する。
その後、値'view engine'を使いテンプレートライブラリを指定する。
参考:Using template engines with Express

var app = express();

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');

続いてapp.use()を使いミドルウェアライブラリをリクエストハンドリングの連鎖に加える。

app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

更に特定のパスへの要求を別のルータへ対応付ける。

app.use('/', indexRouter);
app.use('/users', usersRouter);

最後はエラーとHTTP404レスポンスを返却するためのミドルウェアを追加する。

// catch 404 and forward to error handler
app.use(function(req, res, next) {
  next(createError(404));
});

// error handler
app.use(function(err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render('error');
});

以上でexpressアプリケーションオブジェクト(app)の設定は完了する。
最後にmodule.exportsappを追加することで、/bin/wwwからアクセスできるようにする。

module.exports = app;

Router

/routes以下にはusers.jsindex.jsがあるが基本的に処理が同じなので、ここではindex.jsのみを見ていく。
最初にexpressモジュールをロードしexpress.routerオブジェクトを取得する。

var express = require('express');
var router = express.Router();

/* GET users listing. */
router.get('/', function(req, res, next) {
  res.send('respond with a resource!!');
});

module.exports = router;

Views(templates)

views(template)は/viewsディレクトリに格納され、拡張子.pugを持つ。
Response.render()メソッドは特定のテンプレートを描画するために使われ、レスポンスとして結果を送信する。
以下/routes/index.jsの例ではルートレンダーはテンプレートindex(index.pug)を使い、テンプレート変数titleを渡している。

/* GET home page. */
router.get('/', function(req, res, next) {
  res.render('index', { title: 'Express' });
});

上記コードに対応するテンプレート/views/index.pugを以下に示す。
ここでは数変titleの箇所に値Expressが挿入される。

extends layout

block content
  h1= title
  p Welcome to #{title}

まとめ

個人的にこの章は今までメインでやってきたこと(組み込み寄り)と比べて多くの差異を感じた。
特に重要に違いを感じた点を以下にまとめる。

  • npm expressでアプリケーションの雛形が作成されること。
  • app.jsではルーターやビューの設定、その他ライブラリのインポートなどを行うこと。
  • routesディレクトリ以下にはルーター(router)が配置され、ルーターは特定のパスへの要求を受理したときに対応する処理へのルーティングを担当すること。
  • 変数resrenderメソッドを使うとテンプレートを使って描画する。
  • viewsディレクトリにはビュー(view)のテンプレートが配置され、これらは描画の際に使われること。

これらは繰り返し実装しないと身につかなさそうだ。

Express web framework (Node.js/JavaScript)の勉強 part3、 express-generator

Express Tutorial Part 2: Creating a skeleton website を読み進める

ジェネレータのインストール

はじめに下記コマンドを実行しジェネレータをインストールする。

npm install express-generator -g

ジェネレータのオプション

ジェネレータは下記オプションを持つ

$ express -h

  Usage: express [options] [dir]

  Options:

        --version        output the version number
    -e, --ejs            add ejs engine support
        --pug            add pug engine support
        --hbs            add handlebars engine support
    -H, --hogan          add hogan.js engine support
    -v, --view <engine>  add view <engine> support (dust|ejs|hbs|hjs|jade|pug|twig|vash) (defaults to jade)
        --no-view        use static html instead of view engine
    -c, --css <engine>   add stylesheet <engine> support (less|stylus|compass|sass) (defaults to plain css)
        --git            add .gitignore
    -f, --force          force on non-empty directory
    -h, --help           output usage information

プロジェクトの生成

ジェネレータを使いプロジェクトの雛形をつくる。
参考元 の例では express-locallibrary-tutorial をプロジェクト名としているので、それに倣う。

$ express express-locallibrary-tutorial --view=pug --git

   create : express-locallibrary-tutorial/
   create : express-locallibrary-tutorial/public/
   create : express-locallibrary-tutorial/public/javascripts/
   create : express-locallibrary-tutorial/public/images/
   create : express-locallibrary-tutorial/public/stylesheets/
   create : express-locallibrary-tutorial/public/stylesheets/style.css
   create : express-locallibrary-tutorial/routes/
   create : express-locallibrary-tutorial/routes/index.js
   create : express-locallibrary-tutorial/routes/users.js
   create : express-locallibrary-tutorial/views/
   create : express-locallibrary-tutorial/views/error.pug
   create : express-locallibrary-tutorial/views/index.pug
   create : express-locallibrary-tutorial/views/layout.pug
   create : express-locallibrary-tutorial/.gitignore
   create : express-locallibrary-tutorial/app.js
   create : express-locallibrary-tutorial/package.json
   create : express-locallibrary-tutorial/bin/
   create : express-locallibrary-tutorial/bin/www

   change directory:
     $ cd express-locallibrary-tutorial

   install dependencies:
     $ npm install

   run the app:
     $ DEBUG=express-locallibrary-tutorial:* npm start

第1引数で指定した名前のディレクトリがカレントディレクトリ以下に作成される。
--gitオプションで.gitignoreも同時に作られる。
この.gitignore は中身が充実しており、これを使うのが好ましいと判断したため--gitオプションを付加して実行した。
以下に expressが作る.gitignore を残す。

expressが作成する.gitignore

# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage

# nyc test coverage
.nyc_output

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Bower dependency directory (https://bower.io/)
bower_components

# node-waf configuration
.lock-wscript

# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules/
jspm_packages/

# Typescript v1 declaration files
typings/

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity

# dotenv environment variables file
.env

# next.js build output
.next

まとめ

expressを使うことでアプリケーションの雛形を作成できる
引数によってビューアなどを切り替えることが可能
--gitオプションによって.gitignoreも同時に作られる

Express web framework (Node.js/JavaScript)の勉強 part2

Creating route handlersを読みすすめる

前回、特定のパスへのリクエストを受け取り、対応するコールバック関数を実行する方法を学んだ。 今回は express.Router についてを学ぶ

Router

express.Router を使うことで、特定のパスへのリクエストをまとめて扱うことが可能になる。
ここの例では http://localhost:3000/wiki/ 以下へのリクエストを解釈するrouterインスタンスを作成している。
具体例として以下に wiki.js というファイルを示す

wiki.js

// wiki.js - Wiki route module

const express = require('express');
const router = express.Router();

// Home page route
router.get('/', function(req, res) {
  res.send('Wiki home page');
});

// About page route
router.get('/about', function(req, res) {
  res.send('About this wiki');
});

module.exports = router;

4行目 const router = express.Router()

express.Router を使うことでモジュール化されたのマウント可能なルートハンドラを作成できる。
別ファイルで特定のパス(今回は /wiki プレフィックスとする)を指定することで、そのパスへ以下へのリクエストをルーティングするとが可能になる。

7〜9行目 router.get('/', function(req, res){ ...以下略

特定のパス以下の / へのリクエストを第二引数の関数へルーティングする
今回の例では/wikiへのリクエストをルーティングする

12〜14行目 router.get('/about', function(req, res){ ...以下略

特定のパス以下の /about へのリクエストを第二引数の関数へルーティングする 今回の例では/wiki/aboutへのリクエストをルーティングする

16行目 module.exports = router;

ここまでで作成したrouterオブジェクトを別ファイルで使用できるようにエクスポートする

app.js

const express = require('express');
const app = express();
const port = 3000;

const wiki = require('./wiki.js');

app.get('/', (req, res) => {
  res.send('Hello World!')
});

app.listen(port, () => {
  console.log(`Example app listening on port ${port}!`)
});

app.use('/wiki', wiki);

5行目 const wiki = rquire('./wiki.js');

nodejsはCommonJSのモジュールシステムを採用している。
* 参考:What is require? | Node.js
CommonJSではrequireを使うことでモジュールを取り込む。
requireはモジュールの名前もしくはパスを引数にとる。

15行目 app.use('/wiki', wiki);

appは関数以外にも、特定のミドルウェアの機能を特定のパスに関連付けることができる。
今回の例ではパス/wikiとオブジェクトwikiが、それぞれapp.useの第一、第二引数に使われている。
/wiki/aboutへの要求はwikiオブジェクトへ渡り、/wiki/aboutからプレフィックス/wikiを除いたパス/aboutを使いwikiオブジェクト内でマッチングが行われる。
結果wiki.jsの12行目で定義されたパスにマッチし、対応する関数が実行される。

まとめ

  • routerは別ファイルにまとめることで、特定のパス以下の処理を分割することができる

Express web framework (Node.js/JavaScript)の勉強 part1

webアプリの知識がないことに対しての危機感と興味からExpress web framework (Node.js/JavaScript) - Learn web development | MDN を読んでみることにした。
個人的に気になったところや手順を残す。

私の開発環境は以下の通り

$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 20.04.1 LTS
Release:        20.04
Codename:       focal

Nodeのインストール

はじめに node をインストールする
私の場合は nodenv を使ってインストールした

Hello World

まずは導入のための章であるHelloworld Express の内容を理解する

const express = require('express');
const app = express();
const port = 3000;

app.get('/', (req, res) => {
  res.send('Hello World!')
});

app.listen(port, () => {
  console.log(`Example app listening on port ${port}!`)
});

1行目 const express = require('express');

一行目の const express = require('express'); はモジュールをインポートするためのものである。
自作のファイルや、 node_modules ディレクトリ以下に存在するモジュールをインポートできる。
自作ファイルをインポートする場合には、require(./path/to/myLocalModule);のように、現在のファイルが存在するディレクトリからの相対パスで表現する。
この表記方法はUnixだけでなくWindowsでも可能と記載されている(手元にWindows環境がないため未確認)

参考

2行目 const app = express();

Expressアプリケーションを作成する。
関数express()はモジュールexpressが提供するトップレベルの関数とのことである。
appオブジェクトは下記の目的のためのメソッドを持つ

この章では"httpリクエストのルーティング"機能を使用する
ちなみに変数名がappという名前は慣習らしい

参考

5〜7行目 app.get('/', (req, res) => { ...以下略

特定のパスへのHTTPのGETリクエストを、特定のコールバック関数へと誘導する。
今回の例ではhttp://localhost:3000/に対してGETメソッドが呼ばれた場合に、無名関数(req, res) => { res.send('Hello World!')} が呼ばれる

reqはHTTP Requestを、resはHTTP Responseを表す。
res.send([body])によってResponseのボディ部を作成(内容を登録)するようだ

参考

9〜11行目 app.listen(port, () => { ...以下略

3行目で指定されたport=3000を使ってbindし外部からの接続を待つ
第二引数はおそらくサーバが構築したばあいに実行されるコンソールへの出力と予想する

参考

補足:文字列中に変数の値を埋め込む方法

バッククォートをつかうとできるようだ
bashなどになれているため最初はダブルクォートで囲んでしまい、意図通り動かなくて悩んだ

参考

まとめ

  • expressではappというオブジェクト作成し、そこに対して例えばgetメソッドを使うことで、特定のメソッドへのルーティングを実現する

device treeにおけるアンパサンド(&)の意味

はじめに

device treeを触っているときの疑問を解消する内容が下記リンクにかかれていた。
linux kernel - Meaning of an ampersand prefix in a device tree - Unix & Linux Stack Exchange
本記事は上記ページを備忘録を兼ねて翻訳しようと思う。
※英語が得意なわけではないので意訳を含みます。誤りがあれば指摘ください。

翻訳

質問

異なるノードを指定しようとするDTSファイルを眺めていたときに、興味深い、いくつかのノードの命名規則を見つけた。

/ {
    model = "TI AM335x BeagleBone Black";
    compatible = "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx";
};

&ldo3_reg {
    regulator-min-microvolt = <1800000>;
    regulator-max-microvolt = <1800000>;
    regulator-always-on;
};

&mmc1 {
    vmmc-supply = <&vmmcsd_fixed>;
};

&mmc2 {
    vmmc-supply = <&vmmcsd_fixed>;
    pinctrl-names = "default";
    pinctrl-0 = <&emmc_pins>;
    bus-width = <8>;
    status = "okay";
};

/ {
    hdmi {
        compatible = "ti,tilcdc,slave";
        i2c = <&i2c0>;
        pinctrl-names = "default", "off";
        pinctrl-0 = <&nxp_hdmi_bonelt_pins>;
        pinctrl-1 = <&nxp_hdmi_bonelt_off_pins>;
        status = "okay";
    };
};

ノードの先頭に&がついている場合どのような影響があるのでしょうか?

  • 上の例における&ldo3_reg&mmc1&mmc2のケースです

ノードはルートノードの中に存在できる一方で、ルートノードから分離されている必要性は何でしょうか?

  • device treeでは / の中がルートノードとして表現されます
  • 上の例ではルートノードの中に、ノードhdmiが存在している一方で、&から始まるノードは/{ };の外側に記述されていることを述べていると予想します

興味深いことに、上の例では2つのルートノードが存在します。これは問題ないのでしょうか?

  • 先頭と末尾の方に存在する2つの/を示していると予想します

回答

https://developer.toradex.com/device-tree-customization より
ノードはampersand(&)とラベルを用いて参照可能です

プロパティの上書き

プロパティを上書きする場合、ノードはampersandとラベルを使って参照される必要があります
後に記述されたdevice treeのエントリーは、それより以前に記述されたエントリーを上書きします(エントリの順番は重要であり、したがってインクルードの順番も重要です)
一般的に高いレイヤ(キャリアが作成するボードのためのdevice tree)は、低いレイヤ(例えばSoCのdevice tree)を上書きします。

  • 高いレイヤ = dtsファイル、 低いレイヤ = dtsiファイル と考えれば良いかもしれません。

これを実現するために高いレイヤ(のdevice tree)は低いレイヤ(のdevice tree)を最初に取り込みます(includeします)

例えば、デバイスもしくはホストになれる(デュアルロールな)USBコントローラは、dr_modeをプロパティを使い明示的にデフォルトモードを上書きします

&usbdev0 {
    dr_mode = "host";
};

まとめ

プロパティの上書きのためには&labelの形式が必要ということを知った

ただ、上記がdevice treeの公式のページか不明なので、公式なページで明言されているならば知りたい