20200629のNode.jsに関する記事は8件です。

[Node.js]カバレッジレポートを出力しよう

現場で、なんとなくistanbulを使用してCI環境を構築しておったが、
Node.jsのスプレッド公文で半端なくエラーが出力されるという自体が発生。。。

なんで!?なんで!?

https://www.npmjs.com/package/istanbul

This package has been deprecated
Author message:

This module is no longer maintained, try this instead: npm i nyc Visit https://istanbul.js.org/integrations for other alternatives.

あー。なるほど。(公式は読みましょう。はいすみません。)

いい機会なので、nycを使ってちゃんと出力してみようと思った次第で。

環境を作ってみよう

Dockerじゃないと夜も眠れないので、Dockerで構築してみる。

# 適当に
$ mkdir CoverageNodejs

# Create Dockerfile
$ mkdir docker
$ touch docker/Dockerfile

Dockerfileを記載。Versionは適当に安定版を。
※ 内容はPJ毎によしなにどうぞ。

docker/Dockerfile.
FROM node:12.18.1

WORKDIR /src

ENV TZ Asia/Tokyo

CMD ["sh"]

docker-composeも。

$ touch docker-compose.yml
docker-compose.yml
version: '3'

services:
  coverage_nodejs:
    container_name: coverage_nodejs
    build: docker/.
    volumes: 
      - ./src:/src

とりあえず、srcを。

$ mkdir src
$ touch src/index.js
src/index.js
'use strict'

const execute = () => new Promise((resolve, reject) => {
  Promise.resolve()
    .then(() => {
      resolve('yamachita')
    })
    .catch(reject)
})

module.exports = {
  execute
}

dockerで。

$ docker-compose up -d --build
$ docker-compose run --rm coverage_nodejs npm init

とりあえず私がNode.jsでCI/CDする際に使用しているライブラリをinstall。

# 静的解析
# @see https://www.npmjs.com/package/standard
$ docker-compose run --rm coverage_nodejs npm install -D standard

# テスティングライブラリ
# @see https://www.npmjs.com/package/mocha
$ docker-compose run --rm coverage_nodejs npm install -D mocha

# カバレッジレポート
# @see https://www.npmjs.com/package/nyc
$ docker-compose run --rm coverage_nodejs npm install -D nyc

実際にテストをしてみよう

適当なテストフォルダを作成

$ mkdir src/test
$ touch src/test/index.test.js
test/index.test.js
/* eslint-disable no-undef */
const usecase = require('../index')
const assert = require('assert')

describe('# index', () => {
  it('## execute', async () => {
    let res
    await usecase.execute().then((result) => { res = result })
    assert.strictEqual(res, 'yamashita')
  })
})

テストスクリプト記載。

src/package.json
〜省略〜
  "scripts": {
    "test": "mocha --recursive",
    "test-coverage": "standard && nyc --reporter=html --reporter=text mocha --recursive"
  },
〜省略〜

テスト実行

$ docker-compose run --rm coverage_nodejs npm test
> src@1.0.0 test /src
> mocha --recursive



  # index
    1) ## execute


  0 passing (13ms)
  1 failing

  1) # index
       ## execute:

      AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
+ actual - expected

+ 'yamachita'
- 'yamashita'
       ^
      + expected - actual

      -yamachita
      +yamashita

      at Context.<anonymous> (test/index.test.js:8:12)



npm ERR! Test failed.  See above for more details.

あ。テストケースミスった。
修正してもう一回。

$ docker-compose run --rm coverage_nodejs npm test

> src@1.0.0 test /src
> mocha --recursive



  # index
    ✓ ## execute


  1 passing (10ms)

お次はカバレッジレポートを出力。

$ docker-compose run --rm coverage_nodejs npm run test-coverage

> src@1.0.0 test-coverage /src
> standard && nyc --reporter=html --reporter=text mocha --recursive



  # index
    ✓ ## execute


  1 passing (14ms)

----------|---------|----------|---------|---------|-------------------
File      | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
----------|---------|----------|---------|---------|-------------------
All files |     100 |      100 |     100 |     100 |                   
 index.js |     100 |      100 |     100 |     100 |                   
----------|---------|----------|---------|---------|-------------------

# カバレッジレポートを開く
$ open src/coverage/index.html 

こんな感じ。

スクリーンショット 2020-06-29 22.38.59.png
スクリーンショット 2020-06-29 22.39.06.png

まとめ

私の現職場では、このカバレッジレポートを社内専用サーバーにExpress(Node.js)の上に乗っけて、毎朝実行 + 実行結果をSlackに通知している。
時間があれば、その環境も記事にできたらいいな。

今回のGitPJ

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

nodejsを使ってウエブサーバーを作ろう - majidai

Build StatusnpmGitHub license

majidaiとは

majidaiとは、nodejs用のWebフレームワークです。
サードパーティーライブラリが使っていないため非常に軽いです。
データ量 < 50KB

インストール

nodejsがインストールはこの記事のスコープ外とします。

1. プロジェクトの初期化

タミナルから作業フォルダーに移動し、プロジェクトの初期化を行います。

npm init -y
2. majidaiインストール

以下のコマンドで「majidai」をインストールします。

npm install majidai
3. サーバーコード

以下のコマンドで「majidai」をインストールします。

server.js
// majidaiの読み込み
const majidai = require("majidai");

// インスタンス化
const server = new majidai();

// 受付開始
server.start();
4. serverの起動

以下のコマンドでウエブサーバーの起動します。

node server.js

そして、ブラウザを起動し、http://localhostにアクセスします。以下の画面が表示されればOKです。

デフォルトでは80ポートで受け付けしますが、以下のようにポートを指定する事が可能です。

const config = {
    http: {
        port: 8000
    }
};
const server = new majidai(config);

静的コンテンツの配信

デフォルトでは静的コンテンツの配信が無効にしてますが、以下のように「Document Root」指定する事で指定のフォルダーは以下のものを配信する事が可能です。

const config = {
    http: {
        documentRoot: './public'
    }
};
const server = new majidai(config);

使い方

majidaiの書き方は以下のようになります。

server.METHOD(PATH, CALLBACK)
METHODとは

以下の3種類があります。
- get : http GETリクエストのみ受け付けます
- post : http POSTリクエストのみ受け付けま
- listen :複数のhttpメソッドを一つのPATHで受け付けます
※詳細についてはこちら

PATHとは

"/" , "/top", "/user/dakc",..みたいにどこでリクエストを受け付けるかの事です。

CALLBACKとは

これはコールバック関数の事です。指定のPATHにリクエストを受け付けたら、この関数が実行されます。関数の引数が以下の2つです。
- request : <http.IncomingMessage>
- response : <http.ServerResponse>
majidaiが両方のオブジェクトに「mj」オブジェクトを追加します。

request.mj

requestに追加したmjオブジェクトがクライアントから送られて来たデータを操作するために以下の2つの関数(メソッド)を有します。

  • getParams : GETで送られて来た情報をJSONオブジェクトとして返します。キーを引数として指定することで特定のキーに対する値のみを取得する事が可能です。
  • postParams: POSTで送られて来た情報をJSONオブジェクトとして返します。キーを引数として指定することで特定のキーに対する値のみを取得する事が可能です。※x-www-form-urlencoded, application/json, form-dataどれでデータが送られて来てもJSONオブジェクトとして返します。
response.mj

クライアントにレスポンスを返すために以下のメソッドを有します。

  • text : plain/textとしてレスポンス
  • json : application/jsonとしてレスポンス
  • static : 静的コンテンツのレスポンス
  • redirect : 別のURLへリダイレクト
  • error : HTTPエラーとしてレスポンス

※ 詳細についてはこちら

samples

サンプルを見ながらどのようなことできるかやってみましょう!

1. GET受付
get-sample.js
const server = new majidai();

server.get("/home", (request,response) => {
    // リクエストがhttp://〇〇/home?price=230)
    var price = request.mj.getParams("price");
    console.log(price);
    // 230

    // OR
    // 全データをJsonオブジェクトとして取得
    var getData = request.mj.getParams();
    console.log(getData);
    // { price: '230' }

    // 「price」の値を取得
    var price_ = getData.price;
    console.log(price);
    // 230


    return "ここで返すものがクライアントへのレスポンスになります";
});

server.start();
2. POST受付
post-sample.js
const server = new majidai();

server.post("/login", (request,response) => {
    // リクエストがhttp://〇〇/login
    // <form action="/login" method="POST">
    //   <input name="id" type="text" value="abc">
    //   <input name="pass" type="password" value="p@ss">
    // </form>

    // 「id」の値を取得
    var id= request.mj.postParams("id");
    console.log(id);
    // abc

    // OR
    // 全データをJsonオブジェクトとして取得
    var postData = request.mj.postParams();
    console.log(postData);
    // { id: 'abc', pass: 'p@ass' }

    // 「id」の値を取得
    var id_ = postData.id;
    console.log(id_);
    // abc


    return "ここで返すものがクライアントへのレスポンスになります";
});

server.start();
3. 複数HTTPメソッド受付
multiple-methods.js
const server = new majidai();

server.listen({ method: ["GET", "POST"], path: "/profile" }, (request, response) => {
    // GETでもPOSTでもこのの処理を実行されます。
    // DO SOMETHING

    return "something";
});

server.start();

Parameterized URL

どのような表現が分かりやすいかよくわからなかったため、上記の表記にしました。ごめんなさい。何か気付くところがありましたら是非コメントに残してやってください。

この機能がどうしても実現したかった機能です。
「http://〇〇/user?id=abdf&command=edit」より
「http://〇〇/user/abdf/edit」
ほうが好きだったからです。

parameterized-url.js
// {}の中のものがその名前のキーでデータを取得する事ができます。
server.get("/books/{year}/{price}", function (req, res) {
    // リクエストがhttp://〇〇/books/2020/5000

    // {}の中のものがその名前のキーでデータを取得する事ができます。
    // 「year」の値を取得
    var year = req.mj.getParams("year");
    console.log(year );
    // 2020

    // 「price」の値を取得
    var price= req.mj.getParams("price");
    console.log(price);
    // 5000

    // GETと受けたものをレスポンスとして返す
    return req.mj.getParams();
});

※post,listenの時もParameterizedURL機能が使えます。

Docker

以下の1行のコマンドを実行する事でブラウザーからmajidaiの動作を確認できます。

docker run -it --rm -p 80:80 dakc/majidai npx /data/server.js

最後に

nodejsの標準の機能のみで動く軽量なフレームワークを欲しかったため、majidaiを開発しました。MITライセンスでリリースしておりますのでご自由にお使いください。

DOCUMENTATION - https://dakc.github.io/majidai.html

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

CLS とは?ブラウザとNode.jsで CLS を実装してみます

資料を調べる際に、CLS の存在を知りました。エンジニアリングで結構いいデカップリングのやり方と感じまして、シェアしたいと思います。

シチュエーション

ブラウザか、サーバーのNode.jsか、どっちでもエラーハンドリング、ユーザートラッキングのニーズは日常茶飯事。例えユーザーを特定しなくても、id をつけて、ユーザーの行為を追跡して、エラーの再現にも重要し、プロダクトの改善にも役たちます。

仮に今エラーハンドリングを書こうと思って、このエラーハンドリングはすべてのエラーを処理しますが、どのリクエストから生み出したエラーを知りたいと

log.error("Error occured", req);

このハンドリングは req と結合しちゃった

仮に今このエラーどのユーザーから出たエラー、ユーザーが何をやったかを知りたいと

log.info("User has done xxx", user);
log.error("Error occured by", user);

ユーザーとも結合しちゃった

この2つの例は一見するとそんなに大きいな問題ではなさそう、ただ2つのパラメータが増えただけじゃ。
だけど、大型サービスを作る時、どんどん増えた機能に対して、関数の引数と関数の長さと共にどんどん伸びちゃって気持ち悪くてリファクタリングしようとしょうもないこと、少なくありませんでしょうか?

解決してみよう

関数が同期のであれば、グローバルで変数につけたらいいじゃんー

const global = {};
$("button").click((event) => {
  global.event = event;
  log("button clicked");
});

function log(...args) {
  console.log(global.event, ...args); // { x: xxx, y: xxx, target: xxx } 'button clicked'
  // other logic
}

だが、非同期関数のであれば

const global = {};
$("button").click((event) => {
  global.event = event;
  setTimeout(() => {
    log("button clicked");
  }, 1000);
});

function log(...args) {
  console.log(global.event, ...args);
  // other logic
}

すべての global.event は同じイベントになちゃった(´;ω;`)!それはだめですね。

我々必要なのは非同期呼び出しチェーンに最初から最後まで持続的なストレージ、
もしくは今走ってる非同期関数の呼び出しの唯一の識別子。

CLS が登場

他の言語では、Thread-local storageと呼ばれるものがあります。が JavaScript はマルチスレッドはありません(Web Workerなどはメインと関係ないし、自分でもマルチスレッドしない)。CLS という名前は TLS みたいに関数型プログラミングからの Continuation-passing style 名前をもらって、Continuation-local Storage、そのチェインの呼び出しの中で持続的データストレージをメンテナンスする。

ブラウザの解決 Zone.js

どうのように解決したかちょっと見てみましょう

$('button').click(event => {
  Zone.current.fork({
    name: 'clickZone',
    properties: {
      event
    }
  }).run(
    setTimeout(() => {
      log('button clicked');
    }, 1000);
  );
});

function log(...args) {
  console.log(global.event, ...args);
  // other logic
}

Zone.js は Angular 2.0 から誕生したもので、もちろん他の機能も持ってる。

この方法は残念なところがあります

考えてみましょう、 Zone.js はどうやってこれを実現しました。ブラウザは呼び出しに対して唯一の識別子を提供するAPIがなければ、すべての非同期関数をリライトしかできなく、そうすれば非同期が入る時と出る時 hook できて、この効果が実装できますね。

自分も書いてみました。

zone-simulation.js

const Zone = {
  _currentZone: {},
  get current() {
    return {
      ...this._currentZone,
      fork: (zone) => {
        this._currentZone = {
          ...this._currentZone,
          ...zone,
        };
        return this;
      },
      set: (key, value) => {
        this._currentZone[key] = value;
      },
    };
  },
};

(() => {
  const _setTimeout = global.setTimeout;
  global.setTimeout = (cb, timeout, ...args) => {
    const _currentZone = Zone._currentZone;
    _setTimeout(() => {
      const __after = Zone._currentZone;
      Zone._currentZone = _currentZone;
      cb(...args);
      Zone._currentZone = __after;
    }, timeout);
  };
})();

for (let i = 0; i < 10; i++) {
  const value = Math.floor(Math.random() * 100);
  console.log(i, value);
  Zone.current.fork({ i, value });
  setTimeout(() => {
    console.log(Zone.current.i, Zone.current.value);
  }, value);
}

また問題なさそうだけど、

angular with tsconfig target ES2017 async/await will not work with zone.js

ブラウザ今では完璧の解決方法はありません

実験をやってみましょう、console で下のコードを打ったら、

const _promise = Promise;
Promise = function () { console.log('rewrite by ourselves') };
new Promise(() => {}) instanceof Promise
// rewrite by ourselves
// true

async function test() {}
test() instanceof Promise
// false
test() instanceof _promise
// true

async function test() { return new Promise() }

test() instanceof Promise
// rewrite by ourselves
// false
test() instanceof _promise
// rewrite by ourselves
// true

ブラウザは、async 関数のリターンをネイティブの Promise で再ラッピングします。ネイティブ文法なので、async 関数はリライトできない。
もちろん transpiler で async 関数を generator もしくは Promise にすることは可能ですが、完璧とは言わないでしょう。

Node.js の解決 async_hooks

Node.js バージョン 8 以降出た async_hook モジュール、バージョン 14 の今でも Experimental ステータスから脱却してない。出たごろ性能に関しての議論もあったが、今はどうなってるかまだわからない状態ですが

Experimental ステータスにしても安定性としては問題なさそう、大量な Node.js のトラッキング / APM が依存していて、問題があったら issue が立てられるはずです。

性能に関する問題はここは展開しない、コードの低結合と少しパフォーマンスの低下を交換するかしないかによりますね。

使い方

async_hookscreateHook という関数を提供した、これが非同期関数のライフサイクルに hook できます、しかも唯一識別子も提供してくれますので、CLS を簡単に作れます。

cls.js

const {
  executionAsyncId,
  createHook,
} = require("async_hooks");

const { writeSync: fsWrite } = require("fs");
const log = (...args) => fsWrite(1, `${args.join(" ")}\n`);

const Storage = {};
Storage[executionAsyncId()] = {};

createHook({
  init(asyncId, _type, triggerId, _resource) {
    // log(asyncId, Storage[asyncId]);
    Storage[asyncId] = {};
    if (Storage[triggerId]) {
      Storage[asyncId] = { ...Storage[triggerId] };
    }
  },
  after(asyncId) {
    delete Storage[asyncId];
  },
  destroy(asyncId) {
    delete Storage[asyncId];
  },
}).enable();

class CLS {
  static get(key) {
    return Storage[executionAsyncId()][key];
  }
  static set(key, value) {
    Storage[executionAsyncId()][key] = value;
  }
}


// --- seperate line ---

function timeout(id) {
  CLS.set('a', id)
  setTimeout(() => {
    const a = CLS.get('a')
    console.log(a)
  }, Math.random() * 1000);
}

timeout(1)
timeout(2)
timeout(3)

Node.js バージョン 13 からオフィシャルの実装も

コミュニティの中でたくさんの CLS ライブラリーがあった上に、Node.js 13.10 から AsyncLocalStorage の API がありました。

https://nodejs.org/api/async_hooks.html#async_hooks_class_asynclocalstorage

実はこれはすでにすぐに使える CLS です。

const {
  AsyncLocalStorage,
} = require("async_hooks");

const express = require("express");
const app = express();

const session = new AsyncLocalStorage();

app.use((_req, _res, next) => {
  let userId = Math.random() * 1000;
  console.log(userId);
  session.enterWith({ userId });
  setTimeout(() => {
    next();
  }, userId);
});

app.use((_req, res, next) => {
  const { userId } = session.getStore();
  res.json({ userId });
});

app.listen(3000, () => {
  console.log("Listen 3000");
});


const fetch = require('node-fetch')

new Array(10).fill(0).forEach((_, i) => fetch('http://localhost:3000/test', {
  method: 'GET',
}).then(res => res.json()).then(console.log))

// Output:
// Listen 3000
// 355.9573987560112
// 548.3773445851497
// 716.2437886469793
// 109.84756385607896
// 907.6261832949347
// 308.34659685842513
// 407.0145853469649
// 525.820449114568
// 76.91502437038133
// 997.8611964598299
// { userId: 76.91502437038133 }
// { userId: 109.84756385607896 }
// { userId: 308.34659685842513 }
// { userId: 355.9573987560112 }
// { userId: 407.0145853469649 }
// { userId: 525.820449114568 }
// { userId: 548.3773445851497 }
// { userId: 716.2437886469793 }
// { userId: 907.6261832949347 }
// { userId: 997.8611964598299 }

参照

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Javascript】ワンライナーで現在時刻をフォーマットした日付で表示する

getDay()、getHours()とかやってから繋げるのめんどくさい:confounded:

コード:writing_hand:

ワンライナー
new Date().toLocaleString(undefined, { month: "short", day: "numeric", hour: "2-digit", minute: "2-digit" });
↑改行するとこう
new Date().toLocaleString(undefined, {
  month: "short",
  day: "numeric",
  hour: "2-digit",
  minute: "2-digit"
});
結果
8月10日 19:19

別のやり方

new Intl.DateTimeFormat(undefined, { [ここにオプション] }).format(new Date());

使い方

dateObj.toLocaleString([locales[, options]])

詳細:Date.prototype.toLocaleString()

  • locales "ja"とかが入ります。undefinedなら、OSのデフォルトです
  • options フォーマットを指定できます(自由度は低い)。詳しくはMDNで

※ localesが日本だと"2-digit"で表示されないことがあります:innocent:

まとめ

moment.jsのが楽です:hugging:

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

laravel+vueプロダクトのローカル開発環境

書いてあること

Laravelでバックエンド、Vueでフロントエンドの構成のプロダクトをWindowsクライアントで開発する際のクライアントのローカル環境の構築方法。

手順

1.「Visual Studio Code(VS Code)」のインストール

公式サイトからダウンロードしてインストール

2.「VS Code」拡張機能のインストール

「VS Code」を起動する。
アクティビティバーの「Extensions(拡張)」アイコンをクリック。
image.png

以下の検索ボックスを選択。
image.png

以下の拡張機能名を入力し、拡張機能をインストールしていく。

拡張機能名 説明
.ejs EJSファイル(HTMLのテンプレートファイル)に対して構文をハイライトしてくれる拡張機能。
Bracket Pair Colorizer 対となるカッコ色付けして見やすくしてくれる拡張機能。
ESLint 構文チェックするために必要な拡張機能。
Japanese Language Pack for Visual Studio Code 日本語化するために必要な拡張機能。
php cs fixer PHPのコード整形するために必要な拡張機能。「PHP CS Fixer」や「PHP-CS-Fixer」など、似たような名前の拡張機能が存在するため、間違えないようにインストールしてください。
PHP Intelephense PHPのコード補完をしてくれる拡張機能。
Vetur 「.Vue」ファイルを扱うために必要な拡張機能。
GitLens Gitで管理しているファイルの変更点をショートカットキー等で開ける拡張機能。
Debugger for Chrome VSCodeでフロントサイドのデバッグするための拡張機能。

インストールを実行するには以下の「Install」ボタンを実行する。
image.png

3.「Node.js」のインストール

公式サイトからダウンロードしてインストール。

4.phpインストール

公式サイトから「php-7.3.17-Win32-VC15-x64.zip」をダウンロードし解凍して任意の場所に配置。
配置したフォルダを環境変数PATHに追加
配置したフォルダに入り”php.ini-development”を”php.ini"でコピー。
php.iniを開き以下編集

[php.ini]
-;extension_dir = "ext"
-;extension=fileinfo
-;extension=mbstring
-;extension=gd2
+extension_dir = "ext"
+extension=fileinfo
+extension=mbstring
+extension=gd2

・・・

+extension=php_openssl
+extension=php_pdo_mysql

5.「Composer(コンポーザー)」のインストール

公式サイトからダウンロードしてインストール。
Developer modeはチェック入れる
phpの場所は手順4の配置フォルダ中のphp.exeを指定
update this php.iniはチェック外してスキップ
proxyは未設定のままスキップ

6.「SourceTree」のインストール

公式サイトから「SourceTreeSetup-2.6.10.exe」をダウンロードしてインストール。
求められるアカウント認証は作るかグーグルアカウントがあればそれでできます。

7.「SourceTree」を使ってソースの取得

以下の「Clone」アイコンをクリックする。
image.png

以下のような画面が表示される。
image.png

以下のとおり設定し、「クローン」ボタンを実行する。

《設定内容》 《設定値》
元のパス/URL https://develop.fan-technology.com/gitlab/InHouseDev/xxx.git
保存先のパス {SourceTreeのワークパス}{リポジトリ名}
名前 リポジトリ名
Local Folder [ルート]
詳細オプション ※デフォルトのまま

「Git Flow」メニューを実行。
image.png

デフォルト設定のまま「OK」ボタンを実行。
image.png

「develop」のブランチが作成されたことを確認する。
image.png

8.ライブラリで利用するモジュールをインストール

「Node.js」で使用している拡張機能の物理ファイル群(「node_modules」の中身)をインストールする作業です。
「laravel」で使用している拡張機能の物理ファイル群(「vendor」の中身)をインストールする作業です。

「コマンドプロンプト」を起動します。
以下のコマンドを入力して実行(Enterキー押下)し、「xxx」のソースがあるフォルダにカレントフォルダを設定します。

cd C:\Work\SourceTree\xxx

以下のコマンドを入力して実行(Enterキー押下)します。

npm install

初回はネットから落としてくるファイルが多いので10分ほどかかる場合があります。
つづいて以下のコマンドを入力して実行(Enterキー押下)します。

composer install

初回はネットから落としてくるファイルが多いので10分ほどかかる場合があります。

9.DBセットアップ

resourceフォルダのmariadb-10.4.10-winx64.msiでデフォルト設定にて。

rootユーザのパスワードは"root"で。

※「HeidiSQL」というソフトが入ってしまうようですが、使わないので無視してください。

10.DBマイグレーション

リポジトリ名のフォルダにコマンドプロンプトで入り以下実行

php artisan migrate 

11.初期データ投入

php artisan db:seed 

※「class not found」などのエラーが出る場合は「composer dump-autoload」のコマンドを実行して再度試してください。

12.実行

コマンドプロンプトを立ち上げてxxxのフォルダに移動

php artsan serve    

コマンドプロンプトを立ち上げてxxxのフォルダに移動

npm run watch

※ビルドが終わると自動でブラウザでサイトを開いてくれます。
http://localhost:3000/

また、browserSyncというホットリロードの仕組みを取り入れていますので、ソース系のファイル変更を検知してリビルドしてブラウザリロードしてくれます。

もしリロードしてほしくない場合は以下のURLでアクセスしてください。
http://localhost:8000/

13.「Postman」のインストール

「Postman」はAPIの動作確認を行う際に有用なツール。
GUIで動くcurl。
公式サイトからダウンロードしてインストール。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Node.js 開発環境構築

はじめに

Node.js 未経験者が初めて開発環境を構築したときの備忘録です。
Homebrew や git はインストール済みの前提です。

環境

macOS Catalina version 10.15.5
Homebrew 2.4.2
git version 2.22.0
fish, version 3.0.2

nodenv

anyenv

こちらはnodenv以外にもあるpyenv等、様々なenv系のツールをまとめてくれるもの。
今回はこちらを経由して nodenv をインストールする。
https://github.com/anyenv/anyenv

インストール

$ brew install anyenv

初期化します。

$ anyenv init
# Load anyenv automatically by adding
# the following to ~/.config/fish/config.fish:

status --is-interactive; and source (anyenv init -|psub)

なんか出るので従います。

$ vi ~/.config/fish/config.fish

# 以下を追記
status --is-interactive; and source (anyenv init -|psub)

config を読み直します

$ source ~/.config/fish/config.fish
ANYENV_DEFINITION_ROOT(/Users/mkitaya/.config/anyenv/anyenv-install) doesn\'t exist. You can initialize it by:
> anyenv install --init

またなんかでるので従います。

$ anyenv install --init
Manifest directory doesn\'t exist: /Users/mkitaya/.config/anyenv/anyenv-install
Do you want to checkout ? [y/N]: y
Cloning https://github.com/anyenv/anyenv-install.git master to /Users/mkitaya/.config/anyenv/anyenv-install...
Cloning into '/Users/mkitaya/.config/anyenv/anyenv-install'...
remote: Enumerating objects: 48, done.
remote: Total 48 (delta 0), reused 0 (delta 0), pack-reused 48
Unpacking objects: 100% (48/48), done.

Completed!

nodenv インストール

https://github.com/nodenv/nodenv

インストール

$ anyenv install nodenv

# shell 再起動
$ exec $SHELL -l

Node.js インストール

https://nodejs.org/ja/

# 一覧を確認
$ nodenv install -l

# バージョンを指定してインストール
# https://nodejs.org/ja/download/ を確認して、最新のLTSバージョンを選ぶ
$ nodenv install 12.18.1

# デフォルトで使用するバージョンを設定
$ nodenv global 12.18.1

# shell 再起動
$ exec $SHELL -l

# バージョンを確認
$ node -v
v12.18.1

ちなみに、 nodenv local を使うと特定のディレクトリで使用するバージョンを設定できます。

$ cd hoge/
$ nodenv local 12.18.0
# 上記を実行すると .node-version というファイルが作られる

$ node -v
v12.18.0

ここまでで Node.js を使えるようになりました。

Visual Studio Code

Node.js の開発を行う際に便利なエディタ

以下よりzipをダウンロード
https://code.visualstudio.com/
解凍するとappファイルが出てくるのでApplicationsディレクトリに移動してインストール完了。



:white_check_mark: 環境構築完了です。

参考

https://qiita.com/kyosuke5_20/items/eece817eb283fc9d214f

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

今すぐ使いたいスプレッド構文、"Three-dots" Tip 集

Screen Shot 2020-06-17 at 6.40.18 PM.png

こんにちは。また転職しますので、以前仕事で使っていたアカウントから切り離して新しいアカウントからこれを書いています。(なので現在フォロワー0からの再出発!)

この記事は10日ほど前に私が Dev.to で投稿したものと同じ内容なのですが、Must-read に選ばれるほどの好評でしたので日本語でも書いてみることにしました。


ES6 (ECMAScript 2015, the 6th edition) が標準化されて5年経ちました。この通称 ES6 は多くの新機能や、シンタックティカル・シュガーが追加され、簡潔で明瞭な構文で記述できるようになりました。

たとえば class 構文、let / const、アロー関数などは、みなさんもすでに普段から使っていると思います。では、通称 "three-dots" とも呼ばれる Spread operator / スプレッド構文はどうでしょうか。あまり積極的に使っていない方が多いのではないでしょうか。

私は個人的はとても便利だと感じていますので、コードを書いてて見つけた便利な使い方を紹介してみようと思います。もちろん普段はコードを書くのに StackOverflow 参考にすることも多いことは隠すつもりはないので、そうして誰かがシェアしたものの中で便利だと思ったコードは自分で普段使ってクセにしていって、そのうち自分でもいろいろ見つけた、というのが本当のところです。

Three Dots って?

ES6 で追加された "three-dots" シュガー には、可変長引数を配列で受け取る Rest parameter / 残余引数と、シンタックスが似ていますが、逆に配列を展開するスプレッド構文があります。

この記事ではスプレッド構文の方の紹介をしていきます。この定義の説明を読んで理解するよりも、これから紹介する実際の使えるコードをみてもらった方がわかりやすいと思います。

? ? ?

Concat (配列の結合)

"Con*cat*" ということで猫の毛色を含む2つの配列があります。?

const arr1 = ['solid', 'bicolor', 'tabby'];
const arr2 = ['calico', 'tortoiseshell'];

ES6 以前では、元来の方法で concat() を使ってこう書いてきました:

var conCats = arr1.concat(arr2);
// ['solid', 'bicolor', 'tabby', 'calico', 'tortoiseshell']

ES6 スプレッド構文ではこう書くことができます:

const conCats = [...arr1, ...arr2]; 
// ['solid', 'bicolor', 'tabby', 'calico', 'tortoiseshell']

文字列 → 配列 変換

文字を反転にせよ、もしくは、回文を作れというような問題はソフトウェア・エンジニアの面接のあるあるなのではないでしょうか。実際の問題はもっと複雑なものかもしれませんが、似たような問題で、問題を解決するには、まず文字列を配列に変換する、というようなものが結構多いかと思います。

まず文字列があります:

const str = 'kitty';

ES6 以前では split() を使って一文字づつ配列におさめることができました:

var newArr = str.split(''); // ['k', 'i', 't', 't', 'y'];

ES6 スプレッド構文を使えばもっと楽に:

const newArr = [...str]; // ['k', 'i', 't', 't', 'y'];

Max または Min の値

まず、このような値が与えられたとします。

10, 9, 6, 12 

この中から最大値(または最小値)を見つけるのは Math.max() (or Math.min()) を使い、上の数値を引数としてあたえます:

var max = Math.max(10, 9, 6, 12);

ES6 スプレッド構文を使うとこう書くことができます:

const nums = [10, 9, 6, 12];
const max = Math.max(...nums); // 12

配列のコピー

スプレッド構文と使って配列のシャローコピーを返すことができます。

例えば、こういう配列があります。

const allCatNames = ['chewie', 'leia', 'yoda', 'chewie', 'luke', 'leia'];

シャローコピーを得る一つの方法として、 slice() があります:

var allCatNamesCopy = allCatNames.slice();

これを ES6 スプレッド構文を使うとこうなります:

const allCatNamesCopy = [...allCatNames];

配列から重複を除く

上の例の配列、 allCatNames ではいくつかの重複 (chewieleia が2つづつ)があります。重複を取り除いた配列を得るには ES5 では、何行もあるコードを書く必要があったかと思います。たとえば、配列をループし、それぞれの値をマッピングして同じ値があるか調べつつ新しい配列に push して最終的に重複を除いた新しい配列を返す、といったオペレーションをする必要がありました。

これを、スプレッド構文を使うと一行で書くことができます。

const catNames = [...new Set(allCatNames)]; 
// ['chewie', 'leia', 'yoda', 'luke'];

HTML エレメントを配列に

もしあなたが DOM 使いなフロントエンドの JavaScript ディベロッパーだったら次の例は特に便利かもしれません。

例えば、.cat というクラス名のついたエレメントを集めたいとします。 この時、querySelectorAll() で DOM ノードを収集するでしょう。

ただこの時 document.querySelectorAll('.cat') はノードの集合 NodeList であり、似てはいますが配列とは異なります。

ではこの NodeList を配列にしたい時はどうしますか?

ES5 では、このように一見わかりにくいハック的な方法をとっていたかもしれません:

var catElementArray = [].slice.call(document.querySelectorAll('.cat'));

これを、スプレッド構文を使うとこう書くことができます:

const catElementArray = [...document.querySelectorAll('.cat')];

さて、私の記事であなたが three-dots ノーテーションが気に入ったかどうかはわからないですが、今から使ってみたい、という気になった方がいましたら幸いです。

もし他に便利な使い方を見つけた方はぜひ私にもシェアしてくれると嬉しいです。

ES.Next をもっと知りたい(さらに猫が好きな)方へ

この夏に ECMeowScript - What’s new in JavaScript Explained with Cats というトークを、7月に Forward JS (San Francisco) そして9月に Web Directions (Sydney) でする予定。コロナウイルスの影響で両方ともリモート・カンフェランスですので興味のある方が是非参加してください ?

では。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

mongoose で docker でたてた MongoDB に接続するのにはまった話

はじめに

Python でせっせと実装していたものが、npm install すれば、あっという間に出来てしまうことを知り、少し落ち込んだりもしたけど、Node.js で書き直している私です。Node.js は超初心者です。

開発は1人でやっていて、まだ試しに作ってるだけの状態で、Mac にあれこれ DB をたてるのが嫌なので、DB だけ docker でやっちゃおう!とやってみたところ、ちっとも繋がりませんでした。

mongoose の Top ページ (ココ)に書いてある

const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:27017/test', {useNewUrlParser: true, useUnifiedTopology: true});

const Cat = mongoose.model('Cat', { name: String });

const kitty = new Cat({ name: 'Zildjian' });
kitty.save().then(() => console.log('meow'));

これのコピペが実行できないという絶望的な状況からのスタート。

動作環境

OS: MacOS Catalina Version 10.15.5 (19F101)
node: v12.18.1
mongoose: 5.9.20
MongoDB: 4.2.8
Docker: version 19.03.8, build afacb8b
docker-compose: version 1.25.5, build 8a1c60f6

docker-compose

docker-compose.yml
version: "3.1"

services:
  mongo:
    image: mongo
    restart: always
    environment:
      MONGO_INITDB_ROOT_USERNAME: user
      MONGO_INITDB_ROOT_PASSWORD: secret1234
    ports:
      - 27017:27017
    volumes:
      - ./configdb:/data/configdb
      - mongo_local_marketing:/data/db

  mongo-express:
    image: mongo-express
    restart: always
    ports:
      - 8081:8081
    environment:
      ME_CONFIG_MONGODB_ADMINUSERNAME: user
      ME_CONFIG_MONGODB_ADMINPASSWORD: secret1234

volumes:
  mongo_local_marketing:
    driver: local

ほぼ、公式(docker hub: mongo)のまま。永続化しただけ。express はなくても良いけど、MongoDB にも不慣れで便利なのでありがたく。

で、sandbox という名前の database を作っておく。

うまくいかなかったやり方

├── app.js
└── config
    └── db.js
config/db.js
module.exports = {
  url: "mongodb://user:secret1234@0.0.0.0:27017/sandbox",
};
app.js
const mongoose = require("mongoose");
const dbConfig = require("./config/db");

// connect mongodb
mongoose
  .connect(dbConfig.url, {
    useNewUrlParser: true,
    useUnifiedTopology: true,
  })
  .then(() => {
    console.log("successfully connected to the database");
  })
  .catch((err) => {
    console.log("error connecting to the database");
    console.log(err);
    process.exit();
  });

実行するとエラー

$ node app.js
error connecting to the database
MongooseServerSelectionError: Authentication failed.
    at NativeConnection.Connection.openUri (.../node_modules/mongoose/lib/connection.js:830:32)
    at Mongoose.connect (.../node_modules/mongoose/lib/index.js:335:15)
    at Object.<anonymous> (.../app.js:10:4)
    at Module._compile (internal/modules/cjs/loader.js:1138:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1158:10)
    at Module.load (internal/modules/cjs/loader.js:986:32)
    at Function.Module._load (internal/modules/cjs/loader.js:879:14)
    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:71:12)
    at internal/main/run_main_module.js:17:47 {
  reason: TopologyDescription {
    type: 'Single',
    setName: null,
    maxSetVersion: null,
    maxElectionId: null,
    servers: Map { '0.0.0.0:27017' => [ServerDescription] },
    stale: false,
    compatible: true,
    compatibilityError: null,
    logicalSessionTimeoutMinutes: null,
    heartbeatFrequencyMS: 10000,
    localThresholdMS: 15,
    commonWireVersion: null
  }
}

reason... ナニコレ、さっぱり分からん。認証に失敗していることだけ分かった。これ、慣れたら reason 見て理由が分かるもの?!
user/password の typo か?と思ったけど、そうじゃない。user/password の渡し方がいかんのか?と思って以下のように変更。

config/db.js
module.exports = {
  url: "mongodb://0.0.0.0:27017/sandbox",
  user: "user",
  pwd: "secret1234",
};
app.js
const mongoose = require("mongoose");
const dbConfig = require("./config/db");

// connect mongodb
mongoose
  .connect(dbConfig.url, {
    useNewUrlParser: true,
    useUnifiedTopology: true,
    user: dbConfig.user,
    pass: dbConfig.pwd,
  })
...

ダメ。0.0.0.0localhost に変えてみたり、docker-compose に書いた monogo に変えてみたりしたけど、当然ダメ。

dbName を取ってみたら接続はできた

config/db.js
module.exports = {
  url: "mongodb://0.0.0.0:27017",
  user: "user",
  pwd: "secret1234",
};

こうすると、なんか接続はできた。しかし、MongoDB 上に Database は、

  • admin
  • config
  • local
  • sandobox

の4つが存在しており、どれにつながってるのか分からん状態。Database は増やせるし。
sandbox に最初からつながりたいんですよ、私は。たまたまつながってる状態じゃなくて、ちゃんと明示した通りにつながっていて欲しいんですよ、私は。

dbName をオプションで渡す

https://mongoosejs.com/docs/connections.html#options

ここを見ると、dbName もオプションで渡せるみたいなので、そのようにしてみた。

config/db.js
module.exports = {
  url: "mongodb://0.0.0.0:27017",
  user: "user",
  pwd: "secret1234",
  dbName: "sandbox",
};
app.js
const mongoose = require("mongoose");
const dbConfig = require("./config/db");

// connect mongodb
mongoose
  .connect(dbConfig.url, {
    useNewUrlParser: true,
    useUnifiedTopology: true,
    user: dbConfig.user,
    pass: dbConfig.pwd,
    dbName: dbConfig.dbName,
  })
  .then(() => {
    console.log("successfully connected to the database");
  })
  .catch((err) => {
    console.log("error connecting to the database");
    console.log(err);
    process.exit();
  });

const Cat = mongoose.model("Cat", { name: String });

const kitty = new Cat({ name: "Zildjian" });
kitty.save().then(() => console.log("meow"));

実行

$ node app.js
successfully connected to the database
meow

にゃー。
"sandbox" database の "cats" collection にジルジャン入ってました。シンバルか!

{
    _id: ObjectId('5ef8b261164eb0103f341ef6'),
    name: 'Zildjian',
    __v: 0
}

つながらなかった理由

わかりまsn笑

同じ症状の人が見当たらなかったので、私の環境のせい?今度暇な時にゆっくり mongoose のソースコードを追ってみるかも知れない。

今日はいったん、ここまで。あと MongoDB に突っ込めばいったん開発おしまいなので、そっちをやってから。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む