Quantcast
Channel: Node.jsタグが付けられた新着記事 - Qiita
Viewing all 8862 articles
Browse latest View live

Stripeでbusiness_typeをindividualにしたい - Node

$
0
0

これでできた

stripe.accounts.update('acct_1GP38OKO0yOOiwW9',{legal_entity:{type:'individual'}},function(err,account){console.log("アカウント編集");console.log(err);console.log(account);});

Node製CLIで「BLEACH」の始解・卍解を確認したい!

$
0
0

唐突に「BLEACH」の登場人物の解号・始解・卍解をパッと確認できるやつ欲しい!と思ったので作りました。
「blch」コマンド。 blch - npm
nodeによるcli作成〜公開まで。

TL;DR

npm install blchで!

できること

  • 登場人物のリスト表示
  • 指定した人物の解号・斬魄刀・卍解の表示
  • 始解
  • 卍解

*全ての登場人物を網羅していません
*基本的に破面篇までの登場人物を扱いますが、一部卍解は千年血戦篇以降で発現したものも扱っています
*十刃(エスパーダ)の場合は始解・卍解ではなく帰刃を扱います

人物追加・編集・修正はこちらから自由にしていただいて構いません。
https://docs.google.com/spreadsheets/d/1e7Ms9sX2m1xu_4r20AgyIZHFgByRQp1s93ZE2PZlEaM/edit#gid=0
このデータからCSVを作成し、該当するものを整形して表示するという仕組みになっています。
*スプレッドシート更新されたらコマンドも勝手に更新するような仕組みにはなってないです
*CLIを作って公開までをやってみたかっただけなので作りは荒いです(レスポンス速度など考慮してなかったり)

CLI紹介篇

人物リスト表示 - human

キャラクターをリスト表示します。
オプション指定により、護廷十三隊、仮面の軍勢(ヴァイザード)、十刃(エスパーダ)などのリスト表示ができます。

blch human --gotei13

output.gif

概要表示 - tldr

登場人物の解号・斬魄刀・卍解を表示します。

blch tldr 黒崎一護

output2.gif

非常にお世話になっているtldrコマンドから着想を得ています。
shellコマンドの説明と使用方法を数行で紹介してくれます。
brew install tldrで!
https://github.com/tldr-pages/tldr

始解・卍解 - echo

echoコマンドにより、始解または卍解を行います。

blch echo--shikai朽木白哉
blch echo--bankai朽木白哉

output3.gif

コマンド組み合わせ

blch human -a | fzf | xargs blch tldr
# もしくは(*fish記法です)
blch tldr (blch human -a | fzf)

output4.gif

ちなみにログインシェルはfishを使っていますが超おすすめです。
ログインシェルをfishにしてみる - Qiita

CLI作成篇

commander

node.jsにおけるCLIの完璧なソリューションです。
と書いてあります。実際使いやすかったです。↓
commander - npm

commanderの使い方をblch humanコマンドを例にとって説明してみます。
Typescriptです。

index.ts

import*asprogramfrom'commander';=====// バージョン情報の登録// blch -V or blch --version でバージョンが確認できるprogram.version('0.0.1','-V, --version')// blch humanコマンドの作成program.command('human')// コマンド名の登録 ここで登録したものを`blch ~`の形で使える.alias('hu')// alias登録により、`blch hu`と打つことも可能.description('Output human names')// help用// optionの登録(省略可能).option("-a, --all","List all").option("-g, --gotei13","List gotei 13").option("-e, --espada","List espada").option("-v, --visored","List visored").option("-o, --other","List other").action(async(cmd,options)=>{/**
        `blch human --gotei13`を実行した場合
        `cmd`に`gotei13`のBooleanパラメータがtrueで追加される
        `gotei13`は上記optionで設定した`--gotei13`の部分に該当する

        `blch human --gotei13 4`を実行した場合
        `blch human --gotei13`を実行した時とcmdの内容は同じ
        optionsは['4']となっている
        つまり`--gotei13 4`で指定したものが文字列として配列に入る

        今回のblchコマンドでは考慮していないが、
        オプションの重ねがけも可能。
        `blch -g -e`とするとcmdの`gotei13`も`espada`もtrueになる
        また、`--gotei13 4 5`とオプションに入れると
        optinosは、`['4', '5']`となる

        以下から`cmd`, `options`の値で条件分岐し出力する処理
      */// csvファイルから全リストを取得constdataList:Human[]=awaitfiles.getHumanDataList()lettargetCode:GroupCode='all'if(cmd.gotei13){targetCode='gotei13'}if(cmd.espada){targetCode='espada'}if(cmd.visored){targetCode='visored'}if(cmd.other){targetCode='other'}// オプションから対象を取得consthumans=findHumansByGroupCode(dataList,targetCode,options)if(humans.length===0){console.log('No matching')return}// ターミナルへ出力humans.forEach(human=>console.log(human.name))})// helpの定義.on('--help',function(){console.log('\n  Examples:')console.log()console.log('    $ blch human --gotei13')console.log('    $ blch hu -g')console.log()})// blch humanコマンド以外を作成する場合は同じように追加していくprogram.command('tldr <target>')======// 最後にターミナル引数をparseする処理program.parse(process.argv)

サクッとコマンド作れて良い。

chalk

chalk - npm
ターミナル出力に色をつけたり太字にしたりできます。

  • chalk未使用 image.png
  • chalk使用 image.png

chalkを使うことで味のある出力になります。

今回この2つくらいしかCLI用のパッケージは使ってないですが、NodeのCLIには他にも使えそうなものは色々あるんだなということを知れました。
こちらはgitのpackage解決周りでバグってビルドできなかったけど参考になりました。
Build a JavaScript Command Line Interface (CLI) with Node.js — SitePoint

CLI公開篇

ほぼこれに沿って行ったので参照のみ貼ります。
npmへの公開は思っていたより簡単でした。
https://qiita.com/TsutomuNakamura/items/f943e0490d509f128ae2

まとめ

  • nodeでのCLI作成〜公開を行った。意外とすんなりできた。
    • goとかrubyとかrustとかpythonでのCLI作成はどうなんだろう
  • 完全に個人用のCLIだが作るきっかけができ、「BLEACH」に感謝。
    • 網羅できていない部分は申し訳ありません
    • (追記)千年血戦篇アニメめっちゃ楽しみ

node-gyp rebuildのerrorを解決する

$
0
0

Node.jsの開発スピードが速すぎて、公開しているNode-REDのライブラリがいつの間にかnpm installできなくなっていました。このライブラリはC++のドライバにアクセスするためNode.jsのC++ Addon(V8エンジン1)を利用していますが、npm install時のnode-gypで失敗しているようでした。V8 APIについては検索してもなかなか情報が見つからず参考になれば幸いです。

ちなみに、本家のサイト2によるとライブラリの開発には

  • N-API
  • nan
  • direct use of internal V8, libuv and Node.js libraries

の3つがあると書かれていますが、ここで扱うのは3つ目のタイプです。

Node.jsのversion

10.xまでは問題なかったのですが、11.xからエラーが出るようになりました。現在最新のv13.11を対象としています。ちなみに、11.xからV8のMajor versionが6から7に上がっています。3

Object->Set()でエラー

Maybe versionになったため、引数にcontextを追加で渡せとのエラーです。Maybe versionの場合は.ToChecked()で返します。4

ログ

../nodes/***/***_wrap.cc:286:46:error:nomatchingfunctionforcalltov8::Array::Set(int&,v8::Local<v8::Integer>)dst_addr->Set(i,Integer::New(isolate,tmp));^Infileincludedfrom/home/***/.cache/node-gyp/13.11.0/include/node/node.h:67:0,from../nodes/***/***_wrap.cc:29:/home/***/.cache/node-gyp/13.11.0/include/node/v8.h:3547:37:note:candidate:v8::Maybe<bool>v8::Object::Set(v8::Local<v8::Context>,v8::Local<v8::Value>,v8::Local<v8::Value>)V8_WARN_UNUSED_RESULTMaybe<bool>Set(Local<Context>context,^~~/home/***/.cache/node-gyp/13.11.0/include/node/v8.h:3547:37:note:candidateexpects3arguments,2provided/home/***/.cache/node-gyp/13.11.0/include/node/v8.h:3550:37:note:candidate:v8::Maybe<bool>v8::Object::Set(v8::Local<v8::Context>,uint32_t,v8::Local<v8::Value>)V8_WARN_UNUSED_RESULTMaybe<bool>Set(Local<Context>context,uint32_tindex,^~~/home/***/.cache/node-gyp/13.11.0/include/node/v8.h:3550:37:note:candidateexpects3arguments,2provided

コード

// 修正前Local<Array>dst_addr=Array::New(isolate,4);...dst_addr->Set(i,Integer::New(isolate,tmp));// 修正後Local<Context>context=isolate->GetCurrentContext();Local<Array>dst_addr=Array::New(isolate,4);...dst_addr->Set(context,i,Integer::New(isolate,tmp)).ToChecked();

Object->Set()でエラー その2

別パターンですが、Maybe versionに関するエラーです。同様にcontextを渡して、.ToChecked()で返します。String::NewFromUtf8()の引数、戻り値の型もMaybeLocalに変わったので、引数にNewStringType::kNormalを追加し、.ToLocalChecked()で返します。

ログ

../nodes/***/***_wrap.cc:292:83:error:nomatchingfunctionforcalltov8::Object::Set(v8::MaybeLocal<v8::String>,v8::Local<v8::Integer>)obj->Set(String::NewFromUtf8(isolate,"header"),Integer::New(isolate,mac.header));^Infileincludedfrom/home/***/.cache/node-gyp/13.11.0/include/node/node.h:67:0,from../nodes/***/***_wrap.cc:29:/home/***/.cache/node-gyp/13.11.0/include/node/v8.h:3547:37:note:candidate:v8::Maybe<bool>v8::Object::Set(v8::Local<v8::Context>,v8::Local<v8::Value>,v8::Local<v8::Value>)V8_WARN_UNUSED_RESULTMaybe<bool>Set(Local<Context>context,^~~/home/***/.cache/node-gyp/13.11.0/include/node/v8.h:3547:37:note:candidateexpects3arguments,2provided/home/***/.cache/node-gyp/13.11.0/include/node/v8.h:3550:37:note:candidate:v8::Maybe<bool>v8::Object::Set(v8::Local<v8::Context>,uint32_t,v8::Local<v8::Value>)V8_WARN_UNUSED_RESULTMaybe<bool>Set(Local<Context>context,uint32_tindex,^~~/home/***/.cache/node-gyp/13.11.0/include/node/v8.h:3550:37:note:candidateexpects3arguments,2provided

コード

// 修正前obj->Set(String::NewFromUtf8(isolate,"header"),Integer::New(isolate,mac.header));// 修正後obj->Set(context,(String::NewFromUtf8(isolate,"header",NewStringType::kNormal)).ToLocalChecked(),Integer::New(isolate,mac.header)).ToChecked();

BooleanValue()でエラー

BooleanValueの引数が空なので、Isolate* isolateを引数で渡せとのエラーです。

※V8 7.4の前後でエラーの内容が変わるので両対応しています。

ログ

../nodes/***/***_wrap.cc:196:33:error:nomatchingfunctionforcalltov8::Value::BooleanValue()latest=args[0]->BooleanValue();^Infileincludedfrom/home/***/.cache/node-gyp/13.11.0/include/node/node.h:67:0,from../nodes/***/***_wrap.cc:29:/home/***/.cache/node-gyp/13.11.0/include/node/v8.h:2771:8:note:candidate:boolv8::Value::BooleanValue(v8::Isolate*)constboolBooleanValue(Isolate*isolate)const;^~~~~~~~~~~~/home/***/.cache/node-gyp/13.11.0/include/node/v8.h:2771:8:note:candidateexpects1argument,0provided

コード

// 修正前staticvoidfoo(constFunctionCallbackInfo<Value>&args){Isolate*isolate=args.GetIsolate();latest=args[0]->BooleanValue();args.GetReturnValue().Set(Boolean::New(isolate,true));return;}// 修正後staticvoidfoo(constFunctionCallbackInfo<Value>&args){Isolate*isolate=args.GetIsolate();#if (V8_MAJOR_VERSION >= 7) && (V8_MINOR_VERSION >= 4)
latest=args[0]->BooleanValue(isolate);#elif (V8_MAJOR_VERSION >= 7)
Local<Context>context=isolate->GetCurrentContext();latest=args[0]->BooleanValue(context).ToChecked();#else
latest=args[0]->BooleanValue();#endif
args.GetReturnValue().Set(Boolean::New(isolate,true));return;}

Object->Get()でエラー

Object->Set()と同様。Maybe versionに関するエラー。

ログ

../nodes/***/***_wrap.cc:449:26:error:nomatchingfunctionforcalltov8::Array::Get(int)dst_addr[0]=arr->Get(0)->NumberValue(context).FromMaybe(0);^Infileincludedfrom/home/***/.cache/node-gyp/13.11.0/include/node/node.h:67:0,from../nodes/***/***_wrap.cc:29:/home/***/.cache/node-gyp/13.11.0/include/node/v8.h:3594:43:note:candidate:v8::MaybeLocal<v8::Value>v8::Object::Get(v8::Local<v8::Context>,v8::Local<v8::Value>)V8_WARN_UNUSED_RESULTMaybeLocal<Value>Get(Local<Context>context,^~~/home/***/.cache/node-gyp/13.11.0/include/node/v8.h:3594:43:note:candidateexpects2arguments,1provided/home/***/.cache/node-gyp/13.11.0/include/node/v8.h:3597:43:note:candidate:v8::MaybeLocal<v8::Value>v8::Object::Get(v8::Local<v8::Context>,uint32_t)V8_WARN_UNUSED_RESULTMaybeLocal<Value>Get(Local<Context>context,^~~/home/***/.cache/node-gyp/13.11.0/include/node/v8.h:3597:43:note:candidateexpects2arguments,1provided

コード

// 修正前dst_addr[0]=arr->Get(0)->NumberValue(context).FromMaybe(0);// 修正後Local<Context>context=isolate->GetCurrentContext();dst_addr[0]=arr->Get(context,0).ToLocalChecked()->NumberValue(context).FromMaybe(0);

まとめ

Node.jsのv10.x(V8 6.8)からv11.x(V8 7.0)でV8 APIに大きな変更が入り5、全体的にMaybe versionが適用されているAPIでnode-gypエラーが出ていました。V8の文法に慣れないので読み解くのに苦労しました。

最速Svelteを速攻デプロイするなら、Zeit NOW一択というメモ。

$
0
0

Svelteで何かデプロイしたい時には。

普段、バックエンドに引きこもっているデータエンジニアだが、たまにはフロントエンドに出ていってみようかと思い、最速JSに近しい存在という爆速(=>死語?)なSvelteいじって三日目。そろそろ(無料で)デプロイなるものをしてみたい今日このごろ。

...ということで、nodejs系を始めいろいろと無料でお気楽にデプロイできそうなZeit NOWを試してみた。
参考 Now でクラウドの複雑さから解放されよう、今すぐに

github(かgitlabなど)の個人アカウントを持っているならば、たしかに、すぐさまデプロイできたのでメモを残しておく。Zeit now初体験だったが、10分ほどでデプロイをできた。

デプロイしたもの(カス):
sve.PNG
アクセス先:
https://svelte1.now.sh

・・・デプロイしたものは、Svelteの公式チュートリアルの一部を改悪しただけのカス。

SvelteをNOWにデプロイする手法(2020年版)

① NOW用Svelteをfolk

『zeit svelte』でググると出てくる以下から、
https://zeit.co/guides/deploying-svelte-with-zeit-now
自分が使っているgitリポジトリのアカウントを選ぶだけ。githubの場合
https://zeit.co/import/git?tab=github
あとは、SSOの認証を行うとディフォルトでprivateリポジトリにfolkしてくれる。
公開すると恥ずかしいサービスを作りたいとか、一山当てたいサービスを作りたい際には、privateリポジトリがディフォルトなのはありがたい限り。

② NOW用Svelteをcloneして編集

folkたら当然、git cloneの類を持ってきて編集することになる。
フォルダ構成は以下の通り。ほぼSvelteのフォルダ構成通りだが.nowフォルダ配下がZeit NOWとの周りの調整を担ってくれているらしい。

sv.PNG

とりあえず、お試ししたい場合は、↑のApp.svelteを編集する。今回の場合。

<script>letcount=1$:doubled=count*2$:tripled=count*3lethandleClick=()=>{count=tripled+3}</script>
<style>p{color:purple;font-family:'Comic Sans MS',cursive;font-size:2em;}</style>
<main><h1>滑る手おぢさん(仮)。</h1>
<h2><ahref="https://zeit.co/docs"target="_blank"rel="noreferrer noopener">ZEITNow</a>
Svelteしてみる。突然だが、おい、オマイラ、掛け算という奴をしてみないか。すごく大きくなるぞ。</h2>
<br/><p>元の数{count}しかして、け奴の×2は、{doubled}然らば、×3{tripled}。けだし大きな数なり。</p>
<buttonon:click={handleClick}>クリックしてみて只今の元の数:{count}</button>
</main>

③ デプロイ

node/npm入っている人ならば、ひとまず、npm i -g nowした後に、メール認証などを済ませた後は、
編集しているフォルダにて、
now --prod
するだけで、git add/git commit / git push、そしてNOWを介して本番デプロイという手順を一気に済ませられる。
セキュリティ云々を考えないなら、ほんとにお気軽。

終わりに

少なくとも個人開発でのnodejsでの開発工程ならば、Zeit NOWは現時点では文句なしの存在ということを確認した。Svelteのバックエンドは数十分で始めた、Svelte+Firebaseのアプリ生活。で、firebaseが良さげと分かっている。
フロントエンド素人として悩ましいのが、node上でSvelteを動かす方法。絶賛開発中のSapperを試してはみたが、(英語でも)文書がなさすぎて素人にはつらすぎる(セキュリティ云々を気にするとfirebaseをバックエンドにしたアプリを公開できるは先になりそう、、)。

どなたかSvelte/Sapperのベストプラクティスを紹介してくださらぬものかのぅ.(コーダー老人的堕ち)。とりあえず、Svelteお試しする分には楽しいので良しとするが。

Node.js+Express+Passportでログイン認証をしたい

$
0
0

はじめに

「Node.jsってどんなもんなんだろう?」ってところから始まり。
  ↓
「へぇ〜APIとか簡単にできるじゃん」となり。
  ↓
「ログイン認証とかも割と簡単なんじゃね」と思ったのでやってみました。

環境

  • macOS Mojave

Express

npmを使ってExpressをインストールする。
entry pointはapp.jsに設定する。

$ mkdir myapp
$ cd myapp
$ npm init
$ npm install express

myappにapp.jsを作成し、以下を実装する。

app.js
constexpress=require('express')constapp=express()app.get('/',(req,res)=>{res.send('Hello World')})app.listen(3000,()=>{console.log('Listening on prot 3000')})

普通にnode app.jsで起動しても良いが、変更に対して自動的に起動するようにしてほしいのでnodemonをインストールする。
起動したら、http://localhost:3000にアクセスし期待通りの動きをしているか確認する。

$ npm install nodemon -g
$ npx nodemon app.js
    [nodemon] starting `node app.js`
    Listening on prot 3000

Passport

ログイン認証に必要なものをインストールします。
(詳しく説明しませんが、ググれば大丈夫)

$ npm install body-parser
$ npm install cookie-parser
$ npm install express-session
$ npm install passport
$ npm install passport-local
$ npm install connect-ensure-login

publicフォルダを作成し、login.htmlok.htmlng.htmlを作成する

app.js
constexpress=require('express')constapp=express()constpath=require('path')constsession=require('express-session')constpassport=require('passport')constbodyParser=require('body-parser')constcookieParser=require('cookie-parser')constLocalStrategy=require('passport-local').Strategy;app.use(express.static(path.join(__dirname,'public')))app.use(cookieParser())app.use(bodyParser.urlencoded({extended:true}))app.use(session({secret:'secret',resave:false,saveUninitialized:false,}))app.use(passport.initialize())app.use(passport.session())passport.use(newLocalStrategy((username,password,done)=>{if(username!=='user'||password!=='passwd'){return// ログイン失敗}returndone(null,username)// ログイン成功}))passport.serializeUser((user,done)=>{done(null,user)})passport.deserializeUser((user,done)=>{done(null,user)})// ...
login.html
<html><headlang="ja"><metacharset="UTF-8"><title>ログイン</title></head><body><formaction="/login"method="post"><div><label>ユーザID:</label><inputtype="text"name="username"></div><div><label>パスワード:</label><inputtype="password"name="password"></div><div><inputtype="submit"value="Login"></div></form></body></html>

その他

ログインに成功したら/login/okへリダイレクト、失敗したら/login/ngへリダイレクトするような実装をします。
ただ、普通にやってしまうとログインに成功していないにも関わらず、http://localhost:3000/login/okと叩けばアクセスできてしまいます。。。
そこで便利なものがあります!
require('connect-ensure-login').ensureLoggedIn('/login')を挟んでやれば、ログインしていない場合は指定した/loginにリダイレクトされるようにできます。

app.js
// ...app.get('/',(req,res)=>{res.redirect('/login')})app.get('/login',(req,res)=>{res.sendFile('login.html',{root:path.join(__dirname,'public')})})app.post('/login',passport.authenticate('local',{failureRedirect:'/login/ng',session:true}),(req,res)=>{res.redirect('/login/ok')})app.get('/login/ng',(req,res)=>{res.sendFile('ng.html',{root:path.join(__dirname,'public')})})app.get('/login/ok',require('connect-ensure-login').ensureLoggedIn('/login'),// <--- こいつ!!(req,res)=>{res.sendFile('ok.html',{root:path.join(__dirname,'public')})});app.get('/logout',(req,res)=>{req.logout()res.redirect('/login')})// ...

最後に

ログイン認証のセッション管理などをreq.isAuthenticated()で制御させてみたんですけど、想定通りの動きはしなかった。。。

これからはconnect-ensure-loginを使っていこーっと
一応、ソースコードを載せておきます。

arduino + leapmotion でmidi ドラム Play!

$
0
0

ドラムマシーンを足で演奏することを目指しています。
手で演奏するプロトタイプを作成しました。

・arduino uno
・SparkFun midiシールド
・leapmotion
・node.js
・CASIO キーボード CTK-530


leapmotionとarduinoでシリアル通信

参考サイト:
ArduinoとNode.jsでシリアル通信で文字列を送受信するスクリプト
ArduinoとLeapmotionのシリアル接続

まずはleapmotion側(node.js)
準備物は↑を参照願います。

leap-arduino.js
"use strict"constserialport=require('serialport');// arduinoポート名指定(arduino IDEから取得)varportName='/dev/cu.usbmodem1421';/* jsライブラリ読込 */varLeap=require("leapjs");varfive=require('johnny-five');varyPosi=0;//座標varG_KICK_TIME=newDate();//センサーキック時刻varC_WAITE_TIME=100;//ミリ秒//Leap Motionコントローラー作成varcontroller=newLeap.Controller();//leapmotionと接続開始controller.connect();//手を認識controller.on('hand',function(gesture,e){//y軸取得yPosi=gesture.palmPosition[1];yPosi=parseInt(yPosi);varss=newDate();//前回のarduino送信からの時間比較if(ss.getTime()-G_KICK_TIME.getTime()>=C_WAITE_TIME){//C_WAITE_TIME経過した場合//arduino通信発生時刻G_KICK_TIME=newDate();console.log("ON");//arduinoへ送信する関数write(yPosi);}});constReadline=serialport.parsers.Readline;constparser=newReadline();constspp=newserialport(portName,{baudRate:31250,//midiのボーレートが31250dataBits:8,parity:'none',stopBits:1,flowControl:false,parser:parser});//シリアル通信開始spp.on('open',function(){console.log('Serial open.');});//arduinoから受信した場合の処理(今回は未使用)//spp.on('data', function (data) {//  //console.log('spp.on-data:' + data);//});//arduinoへ送信functionwrite(data){//y軸座標の範囲で値を設定;varsendData;if(data>0&&data<=160){//スネアドラムsendData='a';}elseif(data>160&&data<=200){//ハイハットclosesendData='b';}elseif(data>200&&data<=250){//ハイハットopensendData='c';}else{//上記以外sendData='0';}data=sendData;//送信データ表示console.log('Mac:'+data);//arduinoへ値送信!spp.write(newBuffer.from(data),function(err,results){if(err){//エラーメッセージ表示console.log('Err: '+err);}});}

続いてarduino側です。
参考サイト
Arduino MIDI Library の使い方

手をかざすとC_WAITE_TIMEの間隔でCASIOキーボードのドラムを鳴らします。

//MIDIライブラリ使用のためのヘッダファイル読み込み#include <MIDI.h> 
// MIDIクラスのインスタンスとして"MIDI"を生成する。MIDI_CREATE_DEFAULT_INSTANCE();#define LED 13
intrecieveByte=0;StringbufferStr="";StringokStr="OK";//送信ONフラグconstboolC_ON=true;constboolC_OFF=false;//ノート情報constintC_NOTE_VEROCITY=40;constintC_NOTE_CHANNEL=1;boolbLedState=false;//受信状態 true:on false:offintpitchNo=0;voidsetup(){Serial.begin(31250);//midiのボーレートが31250MIDI.begin();// MIDIインスタンスの初期化}voidloop(){bufferStr="";while(Serial.available()>0){recieveByte=Serial.read();if(recieveByte==(int)'\n')break;bufferStr.concat((char)recieveByte);}//受信信号実行中の場合無視して次へ if(bLedState==C_OFF){//受信データによりCASIOキーボードへMIDI送信if(bufferStr.length()>0){if(bufferStr=="a"){bLedState=C_ON;pitchNo=38;//スネア}elseif(String(bufferStr)=="b"){bLedState=C_ON;pitchNo=44;//ハイハットclose}elseif(String(bufferStr)=="c"){bLedState=C_ON;pitchNo=46;//ハイハットopen}else{bLedState=C_OFF;pitchNo=36;//バスドラ}//ノートオン(pitch, velocity, channel)MIDI.sendNoteOn(pitchNo,C_NOTE_VEROCITY,C_NOTE_CHANNEL);//待機後信号受付状態にするbLedState=C_OFF;MIDI.sendNoteOff(pitchNo,C_NOTE_VEROCITY,C_NOTE_CHANNEL);}}}

MIDIのボーレートの既定値が31250のため、シリアル通信もそれに合わせています。

そうしないとarduino側で受信データが文字化けしてしまいます。
これでハマりました。

スイッチサイエンスで距離計測センサーを購入したので、届き次第leapmotionからこのセンサーに変更します。

記事は着手次第アップします。

WebSocket の負荷テストは Artillery でシュッと簡単に実行しよう

$
0
0

Artilleryは yaml ファイルに宣言的にシナリオを記述し、シンプルなインタフェースで負荷をかけることができる Nodejs 製の負荷テストツールです。
本記事では Artillery を使用して簡単に WebSocket サーバの負荷テストを実行する方法を紹介します。

最小構成の WebSocket サーバ

まずはじめに WebSocket サーバを実装しましょう。今回は Node.js を使用します。
必要最小限の機能だけを提供します。wsライブラリを使用して簡単に実装しましょう。

server.js
constWebSocket=require("ws");constwss=newWebSocket.Server({port:3000},()=>{console.log("server is now listening localhost:3000");});letconnections=[];wss.on("connection",ws=>{connections.push(ws);console.log(`new connection established. connections: ${connections.length}`);ws.on("close",()=>{console.log(`connection closeed: ${connections.length}`);connections=connections.filter((conn,i)=>conn!==ws);});ws.on("message",message=>{broadcast(JSON.stringify(message));});});constbroadcast=message=>{connections.forEach((con,i)=>{con.send(message);});};

起動して以下のメッセージが表示されれば準備完了です。

$ node server.js
server is now listening localhost:3000

wscat で接続を確認する

サーバを起動したらまず wscatを使用して動作の確認をしましょう。wscat は npm でインストールできます。

$ npm install-g wscat

WebSocket サーバを起動し、wscat で接続したら任意のメッセージを送信してみましょう。1つのクライアントからの送信を受けて、他のクライアントへ broadcast していることがわかります。

wscat

Artillery を使用して負荷テストを実行する

さて、ようやく本題です。Artillery を使用して負荷テストをかけてみましょう。
最小限のシナリオファイルのサンプルです。シナリオファイルは senario.ymlのような名前をつけておきます。

senario.yaml
config:target:"ws://localhost:3000"phases:-duration:20arrivalRate:10scenarios:-engine:"ws"flow:-send:"hello"

以下、コマンドで実行します。

$ artillery run senario.yml

run

アクティブなコネクション数を調整する

実際に WebSocket を用いたアプリケーションでは、常に多くのコネクションが張られていることが一般的です。上記のシナリオでは hello というメッセージを送ったらすぐにコネクションを切断してしまうので、常時アクティブなコネクションが少ない状態であまり現実的ではありません。まずは、thinkを指定してアクティブなコネクション数が増えるように調整しましょう。また、同じメッセージを繰り返し送信する loopも指定できます。

senario.yaml
scenarios:-engine:"ws"flow:-send:"hello"-think:1# pause for 1 second-loop:-send:"world"count:5

オブジェクトの送信に対応する

さて先ほどまでは string 形式のデータだけに限定していましたが、{"name":"john", "age":24}のようにオブジェクト形式で送信する方が良いこともあるでしょう。以下のように記述すれば、送信時に Stringify してくれます。

senario.yaml
scenarios:-engine:"ws"flow:# the following will be stringified and sent as '{"name":"john","age":24}'-send:name:"john"age:24

カスタムコードを使用してタイムスタンプを付与する

また、送信したタイムスタンプを付与したいこともあります。このようなケースに対応するためにはカスタムコードを使用して柔軟に値を差し込むことができます。

senario.yaml
config:target:"ws://localhost:3000"processor:"./custom.js"scenarios:-engine:"ws"flow:# custom code for timestanp-function:"createTimestampedObject"-send:"{{data}}"

非常にシンプルに記述し、シュッと実装することができました。Artillery は WebSocket だけではなく、HTTPのプロトコルもサポートしています。本記事は公式ドキュメントから参照していますので、詳細に理解されたい方は一度ドキュメントに目を通してみるのも良いでしょう。

また、CircleCIで継続的に負荷テストを実行するTipsをこちらの記事で紹介していますのでぜひご参照ください。

『Node.js 超入門』の express-validation を最新の仕様に修正する

$
0
0

概要

Node.js 超入門(第2版)を読んでいたところ、バリデーションの箇所が本の通り書いているにも関わらずエラーが発生。 正誤表を確認したところ、以下のような説明がありました。

本書で使用している Express Validator は、現在 ver.6 となり、仕様が変更されているため、本書の記述の通りでは正常に動作しなくなっています。

ver.5 にバージョンダウンすることでサンプルコードのまま実行することが出来たのですが、折角なので最新バージョンに修正して実行をしてみました。
本記事では、express-validator公式ドキュメントを参考に行った修正内容について紹介したいと思います。

修正内容

サンプルコード(ver.5 の書き方)

  • P.334~335 リスト6-4 より、修正の必要があるPOST時の処理について抜粋
    ※修正後と形式を統一するためフォーマッタで一部書き方等を修正してます
hello.js
router.post("/add",(req,res,next)=>{req.check("name","NAME は必ず入力して下さい。").notEmpty();req.check("mail","MAIL はメールアドレスを記入して下さい。").isEmail();req.check("age","AGE は年齢(整数)を入力下さい。").isInt();req.getValidationResult().then(result=>{if(!result.isEmpty()){varre='<ul class="error">';varresult_arr=result.array();for(varninresult_arr){re+="<li>"+result_arr[n].msg+"</li>";}re+="</ul>";vardata={title:"Hello/Add",content:re,form:req.body};res.render("hello/add",data);}else{varnm=req.body.name;varml=req.body.mail;varag=req.body.age;vardata={name:nm,mail:ml,age:ag};varconnection=mysql.createConnection(mysql_setting);connection.connect();connection.query("insert into mydata set ?",data,function(error,results,fields){res.redirect("/hello");});connection.end();}});});

修正後コード(ver.6 の書き方)

  • express-validationの最新バージョン(記事作成時は6.4.0)で実行できるようPOST処理を修正
    ※バリデーション設定箇所以外にも今時のJavaScriptの書き方に変更している箇所もあります(varでなくconstかletで変数を宣言するなど)
hello.js
const{check,validationResult}=require("express-validator");router.post("/add",[check("name").not().isEmpty().withMessage("NAME は必ず入力して下さい。"),check("mail").isEmail().withMessage("MAIL はメールアドレスを記入して下さい。"),check("age").isInt().withMessage("AGE は年齢(整数)を入力して下さい。")],(req,res,next)=>{constresults=validationResult(req);if(!results.isEmpty()){letre="<ul class='error'>";constresult_arr=results.array();for(consterrofresult_arr){re+=`<li>${err.msg}</li>`;}re+="</ul>";constdata={title:"Hello/Add",content:re,form:req.body};res.render("hello/add",data);}else{constname=req.body.name;constmail=req.body.mail;constage=req.body.age;constdata={name,mail,age};constconnection=mysql.createConnection(mysql_setting);connection.connect();connection.query("INSERT INTO mydata SET ?",data,(error,results,fields)=>{if(error===null){res.redirect("/hello");}});connection.end();}});

修正概要

今回でexpress-validationを使った処理に関しては以下の部分を修正しています

  • requireによるモジュールのロードをapp.jsではなくhello.jsの冒頭で行う
    • サンプルの通りapp.jsで宣言した場合には『TypeError: check is not a function』が発生するため変更
    • エラー原因は調査しましたがわからなかったので、ご存知の方がいましたらコメントで教えていただけたら幸いです
  • check内容をpostメソッドの第2引数に記載
  • ver.5 ではエラー情報をreq.getValidationResult().then()resultを引数としてコールバック関数で処理していたが、ver.6ではvalidationResult(req)を定数resultに代入し、関数の中でその値を使って処理
  • notEmpty()はなくなっているので、not().isEmpty()のように宣言
  • エラーメッセージはwithMessage()をメソッドチェーンにして使用し設定

まとめ

以上のように修正することで、記事作成時の最新バージョン(ver.6.4.0)のexpress-validatorでもバリデーションを実行することができました。
よかったら参考にしてみて下さい。


TypeORMのconnection設定はormconfig.jsに書くのがおすすめ

$
0
0

はじめに

TypeORMのcreateConnectionメソッドを呼ぶ際の設定値は、色々な指定方法があります。
色々あって迷っちゃったので、整理して考えた結果、ormconfig.jsに書くのが分かりやすいのではという結論に至りました。

createConnectionメソッドの引数として直接渡す方法は避けたかった

createConnectionメソッドでは、引数としてconnectionの設定値を直接渡す方法があります。

https://typeorm.io/#/connection/creating-a-new-connection

メソッド引数として直接渡す例(公式ドキュメントの引用)
import{createConnection,Connection}from"typeorm";constconnection=awaitcreateConnection({type:"mysql",host:"localhost",port:3306,username:"test",password:"test",database:"test"});

バックエンドのアプリケーションにおいて、createConnectionを呼ぶのは大抵の場合、サーバー起動時に一回だけでしょう。そのため、一見するとこの方法でもさほど問題なさそうに思えます。

ところがTypeORMは、バックエンドアプリケーション用の本番コードからだけでなく、何らかのテストコードからとか、DBマイグレーション用のCLIコマンドから呼ばれる場合もあるはずです。
それらを視野に入れると、createConnectionの引数として直接渡すだけではカバーしにくい場面がでてきそうです。似たような設定を何箇所かに重複して書く羽目になるかもしれません。

引数として直接渡す以外の方法は色々ある

では他の方法はというと、大別すると2種類があります。このあと紹介するそれらの方法で設定した上で、引数なしでcreateConnectionメソッドを呼べば、その設定が参照されます(Creating a new connectionの節の後半で、そう説明されています)。

環境変数TYPEORM_XXXを設定する

1つ目は、何らかの手段によって、所定の名前の環境変数を設定する方法です。

TYPEORM_CONNECTIONTYPEORM_HOSTなど、TYPEORM_XXXという名前の環境変数を設定しておくと、TypeORMはその値を参照します。参照される環境変数名は、以下のURLにまとめられています。

https://typeorm.io/#/using-ormconfig/using-environment-variables

この方法の亜種として、環境変数を設定する代わりに、ormconfig.envというファイルにそれらの設定を書く方法もあります。

ormconfigファイルを作る

2つ目は、ormconfig.xxx(ただし、xxx ≠ env)というファイルをpackage.jsonと同じ階層に置き、そこに設定を書く方法です。拡張子はjs、json、ymlなどから選べます。

jsにおける例(公式ドキュメントの引用)
module.exports={"type":"mysql","host":"localhost","port":3306,"username":"test","password":"test","database":"test"}

環境変数TYPEORM_XXXとormconfigの併用はできない

下記のURLで述べられている通り、上記2種類の設定方法を併用することはできません。環境変数TYPEORM_XXXが設定されていると、ormconfigファイルは無視されます。

https://typeorm.io/#/using-ormconfig/which-configuration-file-is-used-by-typeorm

TypeORMの設定値の中には、環境変数で指定したいものもあるでしょうし(例えばDBのパスワード)、分かりやすくどこかにハードコードしたいものもあるでしょう。ところがあいにく、環境変数TYPEORM_XXXとormconfigファイルの併用はできません。

そこで

そこで、以下のようにすればちょうど良い落とし所になるのではと考えました。

  • ormconfig.jsファイルを用意する。
  • ormconfig.jsの中で必要に応じて、環境変数を参照する。

この時、ormconfig.jsに書く環境変数名はTYPEORM_XXXではなく、TypeORMが予約していない何らかの独自の名前にします。そうすれば、「環境変数TYPEORM_XXXが設定されていると、ormconfigファイルは無視される」という制約を気にせず、柔軟に設定をかけるようになります。
jsファイルなので、環境変数の値に応じたちょっとした分岐を書きやすい利点もあります。

まとめ

TypeORMは、バックエンドアプリケーション用の本番コードからだけでなく、何らかのテストコードからとか、DBマイグレーション用のCLIコマンドから呼ばれる場合もあるので、1個の設定用ファイルにまとめて書いておけば、どこから呼んだ場合でも対応できます。

そして、設定用ファイルの種類としてormconfig.jsを使えば、jsファイルの中で環境変数(TYPEORM_XXXでない独自名)を参照させたり、ちょっとした分岐を書いたりと、柔軟に設定を書くことができます。

その環境変数はどこでどう設定するんかっていう点は残りますが、それはTypeORMの専用知識とは切り離した汎用的な問題として、独立して検討しやすいでしよう。また、TypeORM設定の入り口がormconfig.jsという一箇所に集約されるので、(jsファイルの中で、どこかで設定されている環境変数を参照しているとはいえ、)見通しもそこそこ良くなります。

Progate無料版をやってみる【Node.js】

$
0
0

前回に引き続きProgate無料レッスンをやっていこうと思います。

今回はNode.jsです。
Progateの無料版のレッスンはこれで終わります。
以前のレッスンで既にインストール済みです。

Node.js

公式レッスン

Expressの導入

・ExpressとはNode.jsでWebアプリを開発するためのフレームワーク。
RubyでいうところのRailsになんでしょうね。C#でいうところのASP.NETみたいな。
・おなじみのパッケージ。便利な機能、メソッドが揃った塊、ライブラリの事。
npmコマンドでExpressをインストールする。
npmとはNode Package Manager その名の通り、Node.jsのパッケージを管理するツール。これを使ってパッケージを取得する。

環境
最近こればっかりですが、VSCode使っていきます。

適当なデイレトリを作成し
image.png
VSVodeで開きます。
image.png
ターミナルを開きます。
image.png
まずinitします。

npm init

package name: (node)と聞かれた作成するpackage.jsonの名前をいれます。任意な値。nodeにしました。

次に、以下を実行してExpressをインストールします。

npm install express

こんな感じになるかと思います。
image.png

Progateのレッスンにもどります。

Progateのディレクトリ構成を再現します。

C:\job\Node
│  app.js
│  package-lock.json
│  package.json
│
├─node_modules
└─public
    └─images

app.js等の中身もコピペします。

app.js

constexpress=require('express');constapp=express();

サーバーの起動

app.listen(3000);と記述することで、ポート3000のhttp通信を受けつけることになるらしい。

app.js

constexpress=require('express');constapp=express();app.get('/',(req,res)=>{res.render('hello.ejs');});// サーバーを起動するコードを貼り付けてくださいapp.listen(3000);

getのところは、TOP/GETリクエストがあった場合にhello.ejsをレンダリングする意味だと思います。
ejsってなんすかw

Progateを真似すると、Viewsディレクトリが新たにできていて、その中にhello.ejsがあります。
image.png
hello.ejs

<!DOCTYPE html><html><head><metacharset="utf-8"><title>Hello World</title><script src="/send_url.js"></script></head><body><h1>Hello World</h1></body></html>

/send_url.jsってなんだ?w
ターミナルで

node app.js

と打って実行します。

http://localhost:3000にアクセスすると以下のエラーとなった
image.png
package.jsonの内容がちがったから、そのせいかな?
一緒にしてみる。

package.json

{
  "name": "nodejs_lesson",
  "version": "1.0.0",
  "description": "",
  "main": "app.js",
  "scripts": {
    "eslint": "eslint ./",
    "start": "nodemon -r './.module' ./app.js"
  },
  "author": "Progate",
  "license": "ISC",
  "dependencies": {
    "ejs": "^2.6.1",
    "express": "^4.16.4"
  },
  "devDependencies": {
    "nodemon": "^1.18.10"
  }
}

でも同じエラー。
あ、書き換えたpackage.jsonを適用するのかな?と思い

npm update

これで、package.jsonに記載した"ejs": "^2.6.1"が適用されずはず・・・。

実行後にhttp://localhost:3000にアクセス。
image.png
表示できました!

ページの表示の仕組み

・見た目部分にはejsという形式のファイルを使い、viewsディレクトリの下に配置するらしい。HTMLと同じと思えとのこと。

演習
・新たにtop.ejsが追加になる。

app.js

constexpress=require('express');constapp=express();app.get('/',(req,res)=>{res.render('hello.ejs');});// トップ画面を表示するルーティングを作成してくださいapp.get('/top',(req,res)=>{res.render('top.ejs');});app.listen(3000);

top.ejs

<!DOCTYPEhtml><html><head><metacharset="utf-8"><title>LISTAPP</title>
<scriptsrc="/send_url.js"></script>
</head>
<body><divclass="top-wrapper"><divclass="top-detail"><h2class="subtitle">買い物リストアプリ</h2>
<h1class="title">LISTAPP</h1>
<pclass="description">LISTAPPは、買い物をリストアップするサービスです。<br>買いたいものをリストに追加してみましょう。</p>
<aclass="index-button">一覧を見る</a>
</div>
<divclass="top-image"></div>
</div>
</body>
</html>

npm app.jsしてからhttp://localhost:3000/topにアクセス
image.png

CSSの適用と画像の表示

・「Expressでは、CSSや画像などのファイルがどこに置かれているかを指定する必要があります」らしい。
publicにある。
・app.jsでapp.use(express.static('public'));を行う必要がある。
・Progateの説明の通り、style.cssを追加する。top.ejsで読み込む。

app.js

constexpress=require('express');constapp=express();// CSSや画像ファイルを置くフォルダを指定するコードを貼り付けてくださいapp.use(express.static('public'));app.get('/',(req,res)=>{res.render('hello.ejs');});app.get('/top',(req,res)=>{res.render('top.ejs');});app.listen(3000);

public/css/style.css

/* reset ================================ */*{box-sizing:border-box;}html{font-size:100%;font-family:'Hiragino Sans',sans-serif;line-height:1.7;letter-spacing:1px;}body{margin:0;background-color:#f6faff;color:#6c7686;}ul,li{list-style-type:none;padding:0;margin:0;}a{display:block;text-decoration:none;color:#2d3133;font-size:14px;}a:hover{transition:all0.3sease;}h1,h2,h3,h4,h5,h6,p{margin:0;}h1{font-weight:600;}/* top ================================ */.top-wrapper{max-width:1200px;min-width:920px;display:flex;justify-content:center;align-items:center;margin:0auto;padding:60px;}.top-detail{width:36%;min-width:320px;margin-top:-40px;}.top-detail.subtitle{font-size:14px;font-weight:600;margin-bottom:4px;}.top-detail.title{font-size:54px;line-height:66px;letter-spacing:2px;margin-bottom:24px;}.top-detail.description{font-size:14px;line-height:28px;letter-spacing:0.5px;margin-bottom:40px;}.top-detail.index-button{width:184px;height:48px;text-align:center;line-height:45px;font-weight:600;color:#42cea9;;background-color:#ffffff;border:2pxsolid#58d2b2;border-radius:2px;}.top-detail.index-button:hover{color:#ffffff;background-color:#58d2b2;}.index-button{cursor:pointer;}.top-image{width:64%;text-align:center;overflow:hidden;}.top-imageimg{width:88%;}/* header ================================ */header{height:56px;background-color:#ffffff;border-bottom:1pxsolid#f0f4f9;}.header-logo{margin-left:56px;font-weight:600;font-size:20px;line-height:56px;color:#6c7686;display:inline;}.header-logo:hover{color:#58d2b2;}/* container ================================ */.container{width:80%;min-width:360px;max-width:720px;margin:0auto;margin-top:56px;}.container-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:12px;}.container-headerh1{font-size:24px;}/* index ================================ */.table-head{display:flex;background-color:#b7cadc;border-radius:2px2px00;height:44px;font-size:16px;line-height:46px;color:#ffffff;}.id-column{width:72px;text-align:center;}.table-body{background-color:#ffffff;}.table-bodyli{height:72px;border:1pxsolid#f0f4f9;border-top:none;line-height:74px;display:flex;}.table-body.id-column{font-size:16px;color:#bac6d3;}.table-body.name-column{font-size:14px;font-weight:500;color:#8491a5;}

top.ejs

<!DOCTYPE html><html><head><metacharset="utf-8"><title>LIST APP</title><!-- CSSファイルを読み込んでください --><linkrel="stylesheet"href="/css/style.css"><script src="/send_url.js"></script></head><body><divclass="top-wrapper"><divclass="top-detail"><h2class="subtitle">買い物リストアプリ</h2><h1class="title">LIST APP</h1><pclass="description">
          LIST APPは、買い物をリストアップするサービスです。
          <br>買いたいものをリストに追加してみましょう。
        </p><aclass="index-button">一覧を見る</a></div><divclass="top-image"><!-- 画像ファイルを読み込んでください --><imgsrc="/images/top.png"></div></div></body></html>

http://localhost:3000/topにアクセスして

実行結果
image.png

一覧画面の作成

ndex.ejsというファイルで一覧画面を作成する。

/indexのGETをapp.jsに実装する。
app.js

constexpress=require('express');constapp=express();app.use(express.static('public'));app.get('/',(req,res)=>{res.render('hello.ejs');});app.get('/top',(req,res)=>{res.render('top.ejs');});// 一覧画面を表示するルーティングを作成してくださいapp.get('/index',(req,res)=>{res.render('index.ejs');});app.listen(3000);

index.ejs

<!DOCTYPE html><html><head><metacharset="utf-8"><title>LIST APP</title><linkrel="stylesheet"href="/css/style.css"><script src="/send_url.js"></script></head><body><header><aclass="header-logo">LIST APP</a></header><divclass="container"><divclass="container-header"><h1>買い物リスト</h1></div><divclass="index-table-wrapper"><divclass="table-head"><spanclass="id-column">ID</span><span>買うもの</span></div><ulclass="table-body"><li><spanclass="id-column">1</span><spanclass="name-column">じゃがいも</span></li><li><spanclass="id-column">2</span><spanclass="name-column">にんじん</span></li><li><spanclass="id-column">3</span><spanclass="name-column">たまねぎ</span></li></ul></div></div></body></html>

http://localhost:3000/topにアクセスして
image.png

EJSを使って値を表示しよう

ejsとは「EJSは、HTMLとJavaScriptのコード両方を記述できるNode.jsのパッケージ」らしい。
Embedded javaScript
HTMLの中にJavaScriptを埋め込む
らしい。ReactはJSにHTMLを埋め込むイメージでしたよね。
・なんとnpm install ejsを最初にやる必要があったらしい。
 紛らわしい。なぜその手順を最初に解説しなかったのか・・・。

・JavaScritpを埋め込むには<% %>または<%= %>を使用するらしい。Rubyと一緒かな?

forEachを使ったHTMLの表示

ejs上でforEachできる。
 ただし、forEachの閉じは<% });%>とかなり独特。

ページ間リンク

・リンクはaタグのhref=""のところにパスを記載する。

app.js

constexpress=require('express');constapp=express();app.use(express.static('public'));// 下記のルーティングを削除してください// 削除ここまで// ルートURLで表示されるように変更してくださいapp.get('/',(req,res)=>{res.render('top.ejs');});app.get('/index',(req,res)=>{res.render('index.ejs');});app.listen(3000);

index.js

<!DOCTYPEhtml><html><head><metacharset="utf-8"><title>LISTAPP</title>
<linkrel="stylesheet"href="/css/style.css"><scriptsrc="/send_url.js"></script>
</head>
<body><header><!--href属性を追加してください--><aclass="header-logo"href="/">LISTAPP</a>
</header>
<divclass="container"><divclass="container-header"><h1>買い物リスト</h1>
</div>
<divclass="index-table-wrapper"><divclass="table-head"><spanclass="id-column">ID</span>
<span>買うもの</span>
</div>
<%constitems=[{id:1,name:'じゃがいも'},{id:2,name:'にんじん'},{id:3,name:'たまねぎ'}];%><ulclass="table-body"><%items.forEach((item)=>{%><li><spanclass="id-column"><%=item.id%></span>
<spanclass="name-column"><%=item.name%></span>
</li>
<%});%></ul>
</div>
</div>
</body>
</html>

top.ejs

<!DOCTYPEhtml><html><head><metacharset="utf-8"><title>LISTAPP</title>
<linkrel="stylesheet"href="/css/style.css"><scriptsrc="/send_url.js"></script>
</head>
<body><divclass="top-wrapper"><divclass="top-detail"><h2class="subtitle">買い物リストアプリ</h2>
<h1class="title">LISTAPP</h1>
<pclass="description">LISTAPPは、買い物をリストアップするサービスです。<br>買いたいものをリストに追加してみましょう。</p>
<!--href属性を追加してください--><aclass="index-button"href="/index">一覧を見る</a>
</div>
<divclass="top-image"><imgsrc="/images/top.png"></div>
</div>
</body>
</html>

http://localhost:3000/topにアクセスして
ダウンロード.gif

クリアしました
image.png

感想

・比較的簡単にWebが作れました。
 DBから引っ張ってくるところとかはおそらく有料の上級編でやっているんだろうなぁ・・・。
・Node.jsに言えたことではないが、今まで無料レッスンやってきた言語のWeb系のデプロイってどうやるんだろうw

今回でProgate無料版はすべてこなしました。
いつも書いているように、無料版なので、基礎や基本的なことしかやらないので、即何かアプリを作れるかと言ったら、この内容だけじゃ難しいですが、他無料サイトをみながらアプリ作成を始めてみる際にはこのレッスンの経験が少しは生きてくると思いました。

次回はReactのチュートリアルの三目並べゲームをやってみたいと思います。

ScaffoldHubってすごいのでは?

$
0
0

はじめに

最近のWebアプリを作るために様々な技術が登場しています。
結局どれで作るのが良いのだろうとふと疑問に思った時、思わず目に入ってきたサイトを見つけました。それがScaffoldHubでした。
より多くの人にScaffoldHubを知ってもらいたいと思い
(実は既に知っている人大多数説あり)、恥ずかしながら記事の初投稿を決意しました。

この記事はScaffoldHubの公式サイトに書いてあることを記載しているだけなので、詳しくは以下のURLの公式サイトや公式ブログを参照してください。
https://scaffoldhub.io/

ScaffoldHubとは

ScaffoldHubとは、JavaScriptで書かれたWebアプリのコード自動生成サービスです。選べるものとして、フロントエンドには御三家(React, Vue, Angular)、バックエンドはNodeJSのみ、DBにはSQL, MongoDB, Firebase Firestoreが選べるようです。Webアプリ生成した後は、生成されたソースを基に機能拡張ができるので、自分好みに改良できます。一からWebアプリを作らなくて良いのは魅力的です。

ScaffoldHubの価格

サンプルとしてWebアプリを動かすのは無料ですが、ソースコードのダウンロードはもちろん有料です。気になる価格ですが、なんと39ドル!!
これは安いのでは!?と思いました。公式サイトを見る限り、これ以上かかる費用はなさそうですが、購入を検討されている方は購入前に必ず詳細を確認してください。

ScaffoldHubの特徴

いくつか抜粋して、ScaffoldHubの特徴を記載します。

  • 認証機能
    ユーザ名、パスワード認証やfacebook, Twitter, Google認証が実装されているようで、これらの認証機能を一から実装する必要がないようです。

  • ユーザ・ロール、パーミッション管理
    それぞれを管理でき、権限制御ができるようです。

  • 監査ログ
    ユーザのアクション(ログイン、ページ遷移等)がログに記録されるようです。

  • モバイルとの親和性
    ScaffoldHubから生成したWebアプリはモバイル用のデザインも用意されているようです。

まだまだ、ありますが公式サイトを見ると全容がわかるので、詳細はそちらを参照しください、

終わりに

ScaffoldHubは、2019年5月からサービスを開始していたようですが、
ScaffoldHubに関する記事が見当たらなかったので、記事投稿をしてみました。(今回の記事がQiitaデビュー)
まだ、次回の記事はScaffoldHubをしっかり見れていないのでこれからデモを動かしたときの使用感を試していきたいと思います。

ScaffoldHubについて、知っている方や気になる方がいらっしゃればこの記事を機会にぜひ公式サイトをみて欲しいです。また、気軽なコメントもぜひお待ちしております。
https://scaffoldhub.io/

GlitchでPuppeteerを使う際の注意(--no-sandbox)

$
0
0
  • Glitch:Webサイト・APIなどをデプロイできるサービス
  • Puppeteer:ヘッドレスブラウザを立ち上げてスクレイピングなどに使えるNodeライブラリ。GoogleのChrome DevToolsチームが開発してる

ローカルのNodeで開発したPuppeteer利用APIをGlitchにデプロイして起きたエラーの解消メモ

コード(失敗時)

一般的な以下のような書き方で実行すると…

constpuppeteer=require('puppeteer')(async()=>{constbrowser=awaitpuppeteer.launch()constpage=awaitbrowser.newPage()awaitpage.goto('https://example.com')awaitpage.screenshot({path:'example.png'})awaitbrowser.close()})()

エラー

次のようなエラーがログに吐き出される

UnhandledPromiseRejectionWarning: Error: Failed to launch the browser process!
No usable sandbox! Update your kernel or see https://chromium.googlesource.com/chromium/src/+/master/docs/linux_suid_sandbox_development.md for more information on developing with the SUID sandbox. If you want to live dangerously and need an immediate workaround, you can try using --no-sandbox.

コード(成功時)

エラーメッセージに出ているように、--no-sandboxオプションをつけてpuppeteer.launch()すればOKなので

(async()=>{constbrowser=awaitpuppeteer.launch({args:['--no-sandbox']})constpage=awaitbrowser.newPage()awaitpage.goto('https://example.com')awaitpage.screenshot({path:'example.png'})awaitbrowser.close()})()

で無事実行可能に。Glitchのディスカッションをみると、Glitchの各プロジェクトはDockerコンテナ内で実行されるためsandboxをrunできない、とのことだった

async/await スリープ関数

$
0
0
// スリープconstsleep=delay=>newPromise(resolve=>setTimeout(resolve,delay));(async()=>{// 5秒待機awaitsleep(5000);})();

lockファイルがコンフリクトしたときの対処法

$
0
0

ヾ(・ω<)ノ" 三三三● ⅱⅲ コロコロ♪


------------------- ↓ 余談はここから ↓-------------------

lockファイルでインストールを制御ってのは、
割と理想論だと思っている。

まぁ、それはいいとして、
lockファイルをコミットすると、
ほぼ間違いなくコンフリクトする。
そんなときの対処法を残しておこう。


------------------- ↓ 本題はここから ↓-------------------

composer.lock

コンフリクトを適当に解消して以下のコマンドを打つ

$ composer update --lock

package-lock.json

コンフリクトした状態のまま以下のコマンドを打つ

$ npm install

cheerio-httpcli - Node.js用WEBスクレイピングモジュールについて


Node.js: printf風の文字列フォーマットをする方法

$
0
0

Node.jsでprintf風の文字列フォーマットをするには、util.format関数を使うと良い。

const{format}=require('util')constmessage=format('値は %s のうちのどれかにしてください',[1,2,3])console.log(message)//=> 値は [ 1, 2, 3 ] のうちのどれかにしてください

公式ドキュメント: Util | Node.js v13.11.0 Documentation

初心者がElectron(NW.js)を始めてみて分かったことまとめ

$
0
0

目次

  1. はじめに
  2. 個人的にHTA(WSH?)とNW.jsはオススメしません
  3. electron開発の流れ
    1. プロジェクトフォルダ(名前は何でもOK:ここではmoge)の準備
    2. 中身を編集する
    3. moge/package.jsonを編集する
    4. electronとelectron-builderのインストール
    5. ビルド
  4. electron-builderでビルドしようとするとこのシステムではスクリプトの実行が無効になっているため、~~~というエラーが出るとき
  5. 1つのプロジェクトを再ビルドしたいとき
  6. おわりに

はじめに

突然ですが、絵を描く用の画像資料が10GBを超えてしまいました。
何かいい画像ビューアないかなと探していたのですがイマイチだったので、自分で作ってみようと思いました。
HTML/CSSは少しやったことがあったので、ちょうどいいやということでelectronを始めてみました。🤯

あとこの記事ではwindows10のみを対象にしています。
macとかlinuxは使ったことがないので分かりません。すみません。
もちろんプログラム初心者です。間違いがあったら教えてください。

さて、googleでelectronについて検索してもヒットするのは古い情報が多いですよね。
(そのせいで合計2日分くらい無駄にしてしまいました)
私みたいな人を増やさないために、今の時点で分かったことを初心者目線でまとめておきます。
(あと頭の中身を整理して今抱えているエラーを解決するため)

個人的にHTA(WSH?)とNW.jsはオススメしません

画像ビューアを作ろうとしたとき、まずはこちらの記事(簡単なウインドウアプリ(GUI)開発のまとめ(Windows偏))を参考にさせていただきました。

まずは一番上にあったHTA(WSH?)を試してみたのですが、

  • 古い技術だからサポート切ってるブラウザが多いらしい?(要検証)
    • 私が試したときはIEでだけ動きました。
    • しかも毎回ポップアップで出てくるやつを許可しなきゃダメでちょっとメンドイ
  • ネット文献はそこそこヒットするけど、どれも文が硬く初心者向けではない

次にNW.jsを使ってみたのですが、

  • electronに比べユーザーが少ない
    • つまり文献も少ない(こういうエラーが出ました、など)
  • electronに比べてお手軽とあるが、やってみるとほとんど同じくらいの労力がかかる
  • なぜかビルドできない(これは個人の能力不足)

で、electronに至りました。electronは、

  • そこそこ文献がある
    • あるけどどれもレベルが高くて初心者が理解するには苦労する必要がある(でもないよりマシ!)
    • あと書籍としての文献もそこそこあるらしい(少し古いけど)
  • バージョンによって有効な書き方が違うことがあって少し大変

みたいな感じです。
個人的に積極的に使いたい子ではないです。
他のはウーンって感じだしC#はハードル高そうだしってなって消極的に採用しました。

他に何かよさげなのあったら教えてください!

electron開発の流れ

まずはNode.jsをインストールしてください。

  1. ダウンロードする(私はWindows Installer (.msi)の64-bitをダウンロードしました)
  2. インストーラの指示に従ってインストールする
  3. 完了!

次におおまかにelectron開発の流れをなぞるとこんな感じです。

  1. プロジェクトフォルダ(名前は何でもOK:ここではmoge)を作る
  2. mogeの中に必要なフォルダとファイルを作る(main.js, index.html, src/package.jsonなど)
  3. moge/package.jsonを編集する
  4. electronとelectron-builderをインストールする
  5. ビルドする

次で詳しく触れていきます。

プロジェクトフォルダの準備(名前は何でもOK:ここではmoge

適当なとこにmogeファイルを作ります。
mogeの中にsrcという名前のフォルダとpackage.jsonを作ります。
この段階では中身は空でOKです。
srcの中に、main.js, index.html, package.jsonを作ります。
これも中身は空でOKです。

プロジェクトフォルダ
moge
 ├ src
 │ ├ main.js
 │ ├ index.html
 │ └ package.json
 └ package.json

package.jsonが2つありますが、src直下の方をsrc/package.jsonmoge直下の方をmoge/package.jsonと呼ぶことにします。
(これ何で名前同じの2つ作らせる仕様なんですかね?ややこしい!)

色んなの資料読んでるとどっちのpackage.jsonを指してるのか分かりにくくて初心者的には大変です。
体感ですが、moge/package.jsonの方を指してることが多いと思います。ていうか多分そう。

中身を編集する

まずはmain.jsからです。
main.jsは、どのプロジェクトでもほとんど同じようなものになるらしいです。
必要に応じて書き足したりするらしいですが、私にはよく分かりません。

公式ドキュメントの読み方が分からない...!🤷‍♀️

main.js
const{// アプリ作成用のモジュールを読み込むapp,BrowserWindow,Menu}=require('electron');letmainWindow;// メインウィンドウfunctioncreateWindow(){mainWindow=newBrowserWindow({// メインウィンドウを作成するwidth:960,// 横幅height:540,// 縦幅alwaysOnTop:true,// 常に最前面webPreferences:{nodeIntegration:true// 必要なものらしい}});process.env.ELECTRON_DISABLE_SECURITY_WARNINGS='1';// 警告(Electron Security Warning)が出なくなるmainWindow.loadFile('./src/index.html');// メインウィンドウに表示するやつを指定するMenu.setApplicationMenu(null);// メニューバー削除mainWindow.webContents.openDevTools();// デベロッパーツールの起動mainWindow.on('closed',function(){mainWindow=null;// ×が押されたらメインウィンドウを閉じる});}app.on('ready',createWindow);// 初期化が完了したらcreateWindow()を呼び出すapp.on('window-all-closed',function(){// 全てのウィンドウが閉じたらif(process.platform!=='darwin')app.quit();// macでない限りアプリを終了させる});app.on('activate',function(){// アプリがアクティブになったらif(mainWindow===null)createWindow();// メインウィンドウが消えてたらもう一回メインウィンドウを作る});// In this file you can include the rest of your app's specific main process// code. You can also put them in separate files and require them here.

次はindex.htmlです。
今回はCSSで装飾してJSのログとHTMLのpを使ってみました。

html
<!DOCTYPE html><html><head><title>moge</title><style>body{background-color:#e5ddb0;padding:20px;width:100%;max-width:800px;margin:0auto;}</style><script>// メインfunctionmain(){console.log("JSのテスト");}</script></head><bodyonLoad="main()"><p>HTMLのテスト</p></body></html>

最後にsrc/package.jsonです。
エッこれだけ!?って感じですよね。
src/package.jsonも、どのプロジェクトでもほとんど同じようなものになるらしいです。

src/package.json
{"main":"main.js"}

moge/package.jsonを編集する

基本的にはこんな感じらしいです。

moge/package.json
{"name":"moge","version":"1.0.0","description":"","main":"./src/main.js","scripts":{"start":"electron ."},"keywords":[],"author":"","license":"ISC","build":{"productName":"moge","appId":"moge","win":{"target":"portable"}}}

"target": "portable"は、ビルドするときはポータブルアプリになるようにしてねって意味らしいです。
ポータブルアプリは、インストールしなくても使える.exeのことらしいです。

これでファイルの準備はおしまいです!

electronとelectron-builderをインストールする

エクスプローラでmogeフォルダの余白をshift + 右クリックして、PowerShell ウィンドウをここで開く(S)を選択してください。
(どうでもいいですけどPowerShell ウィンドウ「で」ここ「を」開く(S)じゃないですかね?)

そしたら紺色のウィンドウが出てくるはずです。
これはPowerShellというもので、コマンドを打ち込むとwindowsに命令を下せます。
electronの母体であるNode.jsは、モジュール?をインストールするのにこのPowerShellを使う必要があります。
よく見る.exeのインストーラーは使わないらしいです。

そしたら、まずはnode -vを実行してください。
下みたくなったらNode.jsのインストールに成功していることが確認できます。

node -v
PSD:\electron\moge>node-vv12.16.0

確認できたら、npm i -D electronnpm i -D electron-builderを実行してください。
どちらも少し時間がかかります。
これは、electronとelectron-builderをmogeフォルダにインストールするという工程です。
-D-gにすることで、グローバルなインストールができるらしいですが、electronはローカルにインストールするのがいいらしいです?
下みたくなったらOKです。

npm i -D electron
PSD:\electron\moge>npmi-Delectron+electron@8.0.2added85packagesfrom93contributorsandaudited102packagesin10.474s
npm i -D electron-builder
PSD:\electron\moge>npmi-Delectron-builder+electron-builder@22.3.2added138packagesfrom107contributorsandaudited770packagesin15.356s

このとき、プロジェクトフォルダは次みたくなってるはずです。
追加されたnode_modulespackage-lock.jsonは何もしなくてOKです。

プロジェクトフォルダ
moge
 ├ node_modules
 ├ src
 │ ├ main.js
 │ ├ index.html
 │ └ package.json
 ├ package.json
 └ package-lock.json

確認できたら、npm startを実行してください。

npm start
PSD:\electron\moge>npmstart>moge@1.0.0startD:\electron\moge>electron.

少し待つとこんな感じのウィンドウが出てくるはずです!
無題.png

テンション上がりますね!うおおお😭
このままだとNode.jsやelectronがインストールされている環境でPowerShellから呼び出さないと起動できませんが、ビルドするとダブルクリックひとつ(?)で起動できるようになります!
次でビルドしていきます。

ビルドする

ここによると、PowerShellはセキュリティの都合でよく分からないコマンドを実行できないような設定になってるらしいです。
electron-builderはその「よく分からないコマンド」に属しているらしく、初期設定のままだと実行できません。
なので、さっきの記事に従って設定を変更しましょう。
ちなみに私はSet-ExecutionPolicy RemoteSignedを実行しました。

そしたら、electron-builder build --winを実行しましょう。
この処理は1~2分くらいかかります。
下みたくなったら成功です。

electron-builder build --win
PSD:\electron\moge>electron-builderbuild--winelectron-builderversion=22.3.2os=10.0.17763loadedconfigurationfile=package.json("build"field)descriptionismissedinthepackage.jsonappPackageFile=D:\electron\moge\package.jsonwritingeffectiveconfigfile=dist\builder-effective-config.yamlpackagingplatform=win32arch=x64electron=8.0.2appOutDir=dist\win-unpackeddefaultElectroniconisusedreason=applicationiconisnotsetbuildingtarget=portablefile=dist\moge1.0.0.exearchs=x64

このシステムではスクリプトの実行が無効になっているため、~~~みたいなエラーが出た方は、上の記事に従って再度PowerShellの設定を変更してみてください。

ビルドに成功したら、mogeフォルダにdistというフォルダが追加されているはずです。

プロジェクトフォルダ
moge
 ├ dist
 ├ node_modules
 ├ src
 │ ├ main.js
 │ ├ index.html
 │ └ package.json
 ├ package.json
 └ package-lock.json

そしたらdistの中のmoge 1.0.0.exeを起動してみましょう!
さっきのnpm startと同じウィンドウが出てきましたね!
ddba20001abfb7f026aa13144984f2de.jpg

成功です👺

以上がelectron開発の流れになります。
(長かった…)

1つのプロジェクトを再ビルドしたいとき

単純にもう一回electron-builder build --winを実行するとエラーが出ることがあります。
そのときは、

  • distフォルダを削除する
  • C:/Users/(Username)/AppData/Roaming/mogeを削除する

で再ビルドできます。
もしフォルダを削除しようとして使用中なので削除できませんというエラーが出たときは、

  • moge 1.0.0.exeを閉じる
  • タスクマネージャーでelectronアイコンのやつを全て閉じる

をするとフォルダを削除できるようになります。
(electron-rebuilderとかいうやつがあるとかないとか聞きますが、使い方が分かりません🤷‍♀️)

おわりに

私が分かっていることは以上です。

こうして頭の中を整理してみましたが、今抱えている
「ビルドしたあとに外部ファイルを読み込ませるにはどうしたらいいか」
を解決する糸口は全くつかめませんでした💥

どなたか助けていただけると幸いです…
「公式ページのここに書いてありそう」とかでも構わないので教えてください…

で、初心者の方にも分かりやすい構成を目指して書いてみたのですが、どうだったでしょうか(冗長すぎたかも)。
こんな記事でしたが、最後まで読んでいただいてありがとうございました!🍣

【Node】dotenvで環境変数を設定する

$
0
0

概要

  • Nodeでプログラムを実行する時に環境(dev/stag/prodなど)ごとに値が異なる部分はコードを修正せずに実行するため環境変数として埋め込むことがよくあると思います
  • セットする環境変数が1つ2つであればコマンド実行時に設定すればよいですが規模が大きくなってきたらファイルでまとめて定義したくなるでしょう
  • そんな時はdotenvを使うと便利です
    • dotenvを使うと.envファイルに定義された値を環境変数として使うことができます
    • また、システムの環境変数として値が設定されていればそちらを優先して使うということもできます
    • なので、開発時はローカルで.envを配置し、本番ではホスティングサービスの機能で環境変数として設定するといった使い方をすることでリポジトリ内のファイルを変更せずに実行することができます

Nodeスクリプトの実行時にdotenvを使う場合

  • シンプルな形でいうとindex.jsがあってnode index.jsで実行するようなケースです
  • まずはdotenvをinstallします
npm i dotenv
  • 次にサンプル用のindex.jsを作成します
    • require('dotenv').config();としておくことでdotenvが適用されるようになります
    • process.envで環境変数を取得できます
    • TEST_VALUEという環境変数の値を取得しコンソールに出力するサンプルです
index.js
require('dotenv').config();constvalue=process.env.TEST_VALUE;console.log(value);
  • 最後に値を定義する.envファイルを作成します
.env
# 環境変数を定義
TEST_VALUE=.envで値を設定しています
  • 実行してみましょう
node index.js
  • ログに.envで値を設定していますと出力されているはずです
  • .envの値をシステム環境変数で上書きできることも確認しておきましょう
TEST_VALUE="コマンドで値を設定しています" node index.js
  • ログにコマンドで値を設定していますと出力されているはずです

webpackでビルド時にdotenvを使う場合

  • webpackなどでビルドしてから実行するケースも多くあると思います
  • ここではWebpackでdotenvを使う場合の手順を紹介します
  • dotenv-webpackというライブラリを使用します
npm i webpack webpack-cli dotenv-webpack
  • webpackの設定ファイルを作成します
webpack.config.js
constDotenv=require('dotenv-webpack');module.exports={plugins:[// { systemvars: true } を設定するとシステム環境変数も読み込まれるようになるnewDotenv({systemvars:true}),],};
  • index.jsを修正します
    • dotenvはビルド時に適用されるため設定が消えてスッキリしました
index.js
constvalue=process.env.TEST_VALUE;console.log(value);
  • ビルドして実行してみましょう
# ビルド(dist/main.jsが出力される)
npx webpack -p index.js
# 実行
node dist/main.js
  • 実行すると.envで値を設定していますとログが出力されるはずです
  • システムの環境変数で上書く場合ビルド時に環境変数を設定します
# 環境変数を設定してビルドTEST_VALUE="コマンドで値を設定しています" npx webpack -p index.js
# 実行
node dist/main.js
  • 実行するとコマンドで値を設定していますとログが出力されるはずです

まとめ

  • dotenvを使った環境変数の設定のしかたを紹介しました
  • Nodeを使ったアプリでもこういった技術を活用してCI/CD回していきましょう!

2020年から始めるAzure Cosmos DB - JavaScript SDK (SQL API)を見てみる (Part.2)

$
0
0

th.jpeg

この記事について

本記事は、2020年3月6日 (米国時間) にて、Azure Cosmos DB に新しく Free Tier (無償利用枠) が登場したことに伴い、改めて Azure Cosmos DB を色々と触っていく試みの 4 回目です。
今回も、前回記事同様、 Microsoft Azure Cosmos JavaScript SDKについて見ていきたいと思います。

対象読者

  • Azure Cosmos DB について学習したい方
  • Node.js で Azure Cosmos DB への CRUD 操作を行いたい方
  • Microsoft Azure Cosmos JavaScript SDK の動作について理解したい方
  • TypeScript のパラメータプロパティ宣言を知らない方

Microsoft Azure Cosmos JavaScript SDK

実際に、Microsoft Docsの内容を元に、JavaScript SDK (SQL API) の中身を見ていきます。
今回はデータベースの読み取り、または削除を行う Databaseについて確認します。

Database

TypeDoc の記載は、以下の通りです。

Operations for reading or deleting an existing database.
既存のデータベースの読み取りまたは削除の操作。

constclient:CosmosClient=newCosmosClient({endpoint,key});constdatabase:Database=client.database(databaseId);

実際に使用する際は、CosmosClient クラスを生成した後、CosmosClient クラス内にあるdatabaseメソッドを使用して、Database クラスを生成する手順が必要になります。
CosmosClient クラスのdatabaseメソッドは、どうなっているのか、実際に中身を確認してみます。

Azure/azure-sdk-for-js/sdk/cosmosdb/cosmos/src/CosmosClient.ts
publicdatabase(id:string):Database{returnnewDatabase(this,id,this.clientContext);}

はい、Database クラスのコンストラクタが動いています。
まあ Database 型の定数(const)に値を代入しているのだから当たり前ですよね。

このコンストラクタ、プログラミング初心者にとってはすごく理解が難しい引数thisがありますが、一旦置いておいて、実際のコンストラクタの中身を見ていきます。

Azure/azure-sdk-for-js/sdk/cosmosdb/cosmos/src/client/Database/Database.ts
exportclassDatabase{publicreadonlycontainers:Containers;publicreadonlyusers:Users;constructor(publicreadonlyclient:CosmosClient,publicreadonlyid:string,privateclientContext:ClientContext){this.containers=newContainers(this,this.clientContext);this.users=newUsers(this,this.clientContext);}

コンストラクタの第一引数にCosmosClientがあります。
つまり、先ほどの this とは、このコンストラクタの引数に自分自身への参照(CosmosClient)を渡していた、ということです。
ところで、 (私みたいな) TypeScript 初心者がこのコードをみた際、きっと下記のような疑問を持つのではないかと思います。(実は前回の内容の中にも同じものがあったんですけどね、、)

あれ、、先ほど引数で渡してきた CosmosClinet と databaseId、全然使われていないように見えるけど、どうなっているの...!?

安心してください、もちろん渡した引数には意味があります。ここで注目するのはコンストラクタの引数に public/private/readonlyの修飾子がついているところです。

TypeScript には、パラメータプロパティ宣言というものがあります。これは、コンストラクタの引数の中でプロパティの作成および初期化が行えるようになるというものです。

つまり、上記で記載した Database クラスのコンストラクタの内容は

exportclassDatabase{publicreadonlyclient:CosmosClient;publicreadonlyid:string;privateclientContext:ClientContext;publicreadonlycontainers:Containers;publicreadonlyusers:Users;constructor(client:CosmosClient,id:string,clientContext:ClientContext){this.client=client;this.id=id;this.clientContext=clientContext;this.containers=newContainers(this,this.clientContext);this.users=newUsers(this,this.clientContext);}

と同じ内容ということになります。
実際にコンパイルされた後の JavaScript ファイルも見てみます。

Azure/azure-sdk-for-js/sdk/cosmosdb/cosmos/dist/index.js(5988行目付近)
classDatabase{constructor(client,id,clientContext){this.client=client;this.id=id;this.clientContext=clientContext;this.containers=newContainers(this,this.clientContext);this.users=newUsers(this,this.clientContext);}}

内容が同じであることを確認できました。
TypeScript って本当に便利ですね。

さて、本題のコンストラクタ処理に戻ります。

Azure/azure-sdk-for-js/sdk/cosmosdb/cosmos/src/client/Database/Database.ts(再掲)
exportclassDatabase{publicreadonlycontainers:Containers;publicreadonlyusers:Users;constructor(publicreadonlyclient:CosmosClient,publicreadonlyid:string,privateclientContext:ClientContext){this.containers=newContainers(this,this.clientContext);this.users=newUsers(this,this.clientContext);}

コンストラクタのパラメータプロパティ宣言を除くと、ContainersUsersのクラスを生成する処理があります。
実際に、この 2 つのコンストラクタの中身をみていきます。

Azure/azure-sdk-for-js/sdk/cosmosdb/cosmos/src/client/Container/Containers.ts
exportclassContainers{constructor(publicreadonlydatabase:Database,privatereadonlyclientContext:ClientContext){}}
Azure/azure-sdk-for-js/sdk/cosmosdb/cosmos/src/client/User/Users.ts
exportclassUsers{constructor(publicreadonlydatabase:Database,privatereadonlyclientContext:ClientContext){}

また出たな!パラメータプロパティ!※決してどこかの企業のフレーズを真似したわけではありません笑

ここでもコンストラクタのパラメータプロパティ宣言を使い、ContainersクラスとUsersクラスを生成していますね。このあたりのクラスについては、次回以降、確認していきたいと思います。

さいごに

今回は、Azure Cosmos DB に JavaScript/Node.js で接続する際に CosmosClient クラスを使って生成する Database クラスについて、中身を確認してみました。
今回の内容も、以前、実際に CRUD アプリを作成 (Qiita 記事) した時はたったの 1 行で終わってしまった内容です。

前回、

普段のアプリ開発では、あまり意識しない世界なのかもしれませんが、実際にコンストラクタの中で何が行われているのかを確認することは、Azure Cosmos DB の仕組みや使用するライブラリへの深い知見を得る ためには必要な事かな、と思いました。

と思っていた自分ですが、Microsoft Azure Cosmos JavaScript SDK の中身を見ていくことは、ライブラリの理解だけでなく、TypeScript の勉強もできるという一石二鳥なことではないか、と感じました。他のライブラリについてもこれは同じように言えることだと思います。

次回は、Container/Containersクラスについて見ていこうと思います。

関連リンク

前回記事

参考資料

Microsoft Docs

TypeScript Handbook

Qiita

MacにHomebrew経由でnodenvを導入する(+PHPStorm対策)

$
0
0

 以前ndenvを使っていたのですが、気づいたらdeprecatedになっていたので転職時に新しい端末になったのを機にnodenvへ移行しました。anyenv使って入れるのがステップ数多くてだるいなと思っていたのですが、homebrewであれば割とサクッと入れられそうだったので今回はこちらで導入してみるので、備忘録として残しておきます。

https://github.com/nodenv/nodenv

Homebrewの導入

導入済み前提で進めるので、導入方法については割愛します。

既存のNode.jsまたはNode.jsバージョン管理の削除

各々の環境で異なっている所だと思うので適時環境にあった方法にて対応ください。Macなら大体当該ディレクトリを消せば済むと思います。

nodenvのインストール

brew install nodenv

まぁまぁ時間がかかると思うので、のんびり待ちます。

初期化コマンドをシェルプロファイルに追記

eval "$(nodenv init -)"を追記します。最近のMacならzshがデフォルトだと思うので~/.zshrcでしょうか。

nodenv initを打つとどのファイルに追記すればよいのかを教えてくれるようです。

% nodenv init
#Load nodenv automatically by appending
#the following to ~/.zshrc:
eval "$(nodenv init -)"

追記したらターミナルを再起動します。

インストール確認

nodenvにはnodenv-doctorという診断ツールが用意されています。以下のコマンドを叩くとnodenvが正常にインストールされているかと、利用状況が出力されます。

% curl -fsSL https://github.com/nodenv/nodenv-installer/raw/master/bin/nodenv-doctor | bash
Checking for `nodenv' in PATH: /usr/local/bin/nodenv
Checking for nodenv shims in PATH: OK
Checking `nodenv install' support: /usr/local/bin/nodenv-install (node-build 4.8.0)
Counting installed Node versions: none
  There aren't any Node versions installed under `/Users/ユーザ/.nodenv/versions'.
  You can install Node versions like so: nodenv install 2.2.4
Auditing installed plugins: OK

いたれりつくせりですね。

nodenvの利用方法

ターミナルにnodenv打てば全部丁寧に教えてはくれるのですが、代表的なのを抜粋しておきます。

利用出来るNode.jsのバージョン一覧

nodenv install -l

指定したバージョンのNode.jsをインストール

nodenv install {{ バージョン }}

インストール済みのNode.js一覧

nodenv versions

よくlistって打って怒られてます…

現在のNode.jsバージョン

nodenv version

node -vでもいいんですが、こちらで表示するとNode.jsのバージョン選定理由が併記されます。

12.16.0 (set by /Users/ユーザ/.nodenv/version)

グローバルで使用するNode.jsのバージョンを指定

nodenv global {{ バージョン }}

プロジェクトで使用するNode.jsのバージョンを指定

nodenv local {{ バージョン }}

ターミナルでの現在位置に.node-versionというファイルが作られます。このファイルがあると、当該ディレクトリでNode.js実行時に自動的に指定バージョンのものを使おうとし、指定バージョンが導入されてなければ当該バージョンをインストールするようにエラーが出ます。

該当バージョンが見つからない場合
nodenv: version `12.16.0' is not installed (set by /Users/ユーザ/プロジェクト/.node-version)

PHPStormなどのJetBrains製IDE利用時にnpmがないと怒られる場合

PHPStormなどのJetBrains製IDEを使っていると、過去にndenvを利用していた時にも同じことを書いていますが、この状態でプロジェクトを開いてNPMスクリプトを実行しようとした際にパッケージマネージャが見つからないと怒られると思います。

スクリーンショット 2020-03-24 11.15.38.png

非常にだるいのでなんとかします。

jetbrains-npmをインストール

https://github.com/nodenv/jetbrains-npm

nodenvのインストール先によって導入方法が異なるようですが、今回はデフォルトパスにインストールしているので推奨されている方法で導入します。

mkdir -p "$(nodenv root)/lib/node_modules"
git clone https://github.com/nodenv/jetbrains-npm "$(nodenv root)"/lib/node_modules/npm

導入が終わるとPHPStormがNPMを見つけてくれるようになり、NPMタブも無事叩き放題になります。

スクリーンショット 2020-03-24 11.28.09.png

昔と比べると導入するのも楽になった気がします。多大な労力を割くような場所ではないと思うので、楽になるに越したことはないですね。

Viewing all 8862 articles
Browse latest View live