これでできた
stripe.accounts.update('acct_1GP38OKO0yOOiwW9',{legal_entity:{type:'individual'}},function(err,account){console.log("アカウント編集");console.log(err);console.log(account);});
これでできた
stripe.accounts.update('acct_1GP38OKO0yOOiwW9',{legal_entity:{type:'individual'}},function(err,account){console.log("アカウント編集");console.log(err);console.log(account);});
唐突に「BLEACH」の登場人物の解号・始解・卍解をパッと確認できるやつ欲しい!と思ったので作りました。
「blch」コマンド。 blch - npm
nodeによるcli作成〜公開まで。
npm install blch
で!
できること
*全ての登場人物を網羅していません
*基本的に破面篇までの登場人物を扱いますが、一部卍解は千年血戦篇以降で発現したものも扱っています
*十刃(エスパーダ)の場合は始解・卍解ではなく帰刃を扱います
人物追加・編集・修正はこちらから自由にしていただいて構いません。
https://docs.google.com/spreadsheets/d/1e7Ms9sX2m1xu_4r20AgyIZHFgByRQp1s93ZE2PZlEaM/edit#gid=0
このデータからCSVを作成し、該当するものを整形して表示するという仕組みになっています。
*スプレッドシート更新されたらコマンドも勝手に更新するような仕組みにはなってないです
*CLIを作って公開までをやってみたかっただけなので作りは荒いです(レスポンス速度など考慮してなかったり)
キャラクターをリスト表示します。
オプション指定により、護廷十三隊、仮面の軍勢(ヴァイザード)、十刃(エスパーダ)などのリスト表示ができます。
blch human --gotei13
登場人物の解号・斬魄刀・卍解を表示します。
blch tldr 黒崎一護
非常にお世話になっているtldr
コマンドから着想を得ています。
shellコマンドの説明と使用方法を数行で紹介してくれます。brew install tldr
で!
https://github.com/tldr-pages/tldr
echoコマンドにより、始解または卍解を行います。
blch echo--shikai朽木白哉
blch echo--bankai朽木白哉
blch human -a | fzf | xargs blch tldr
# もしくは(*fish記法です)
blch tldr (blch human -a | fzf)
ちなみにログインシェルはfishを使っていますが超おすすめです。
ログインシェルをfishにしてみる - Qiita
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 - npm
ターミナル出力に色をつけたり太字にしたりできます。
chalkを使うことで味のある出力になります。
今回この2つくらいしかCLI用のパッケージは使ってないですが、NodeのCLIには他にも使えそうなものは色々あるんだなということを知れました。
こちらはgitのpackage解決周りでバグってビルドできなかったけど参考になりました。
Build a JavaScript Command Line Interface (CLI) with Node.js — SitePoint
ほぼこれに沿って行ったので参照のみ貼ります。
npmへの公開は思っていたより簡単でした。
https://qiita.com/TsutomuNakamura/items/f943e0490d509f128ae2
Node.jsの開発スピードが速すぎて、公開しているNode-REDのライブラリがいつの間にかnpm installできなくなっていました。このライブラリはC++のドライバにアクセスするためNode.jsのC++ Addon(V8エンジン1)を利用していますが、npm install時のnode-gypで失敗しているようでした。V8 APIについては検索してもなかなか情報が見つからず参考になれば幸いです。
ちなみに、本家のサイト2によるとライブラリの開発には
の3つがあると書かれていますが、ここで扱うのは3つ目のタイプです。
10.xまでは問題なかったのですが、11.xからエラーが出るようになりました。現在最新のv13.11を対象としています。ちなみに、11.xからV8のMajor versionが6から7に上がっています。3
Maybe versionになったため、引数にcontextを追加で渡せとのエラーです。Maybe versionの場合は.ToChecked()で返します。4
../nodes/***/***_wrap.cc:286:46:error:nomatchingfunctionforcallto‘v8::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();
別パターンですが、Maybe versionに関するエラーです。同様にcontextを渡して、.ToChecked()で返します。String::NewFromUtf8()の引数、戻り値の型もMaybeLocalに変わったので、引数にNewStringType::kNormalを追加し、.ToLocalChecked()で返します。
../nodes/***/***_wrap.cc:292:83:error:nomatchingfunctionforcallto‘v8::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の引数が空なので、Isolate* isolateを引数で渡せとのエラーです。
※V8 7.4の前後でエラーの内容が変わるので両対応しています。
../nodes/***/***_wrap.cc:196:33:error:nomatchingfunctionforcallto‘v8::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->Set()と同様。Maybe versionに関するエラー。
../nodes/***/***_wrap.cc:449:26:error:nomatchingfunctionforcallto‘v8::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の文法に慣れないので読み解くのに苦労しました。
v8.dev (https://v8.dev) ↩
C++ Addons (https://nodejs.org/api/addons.html#addons_c_addons) ↩
Previous Releases (https://nodejs.org/en/download/releases/) ↩
Use Maybe version of V8 APIs (https://github.com/joyeecheung/node/blob/v8-maybe-doc/CPP_STYLE_GUIDE.md#use-maybe-version-of-v8-apis)
v8/node deprecated APIs and how to handle them #7 (https://github.com/bcoin-org/bcrypto/issues/7) ↩
V8 API changes (https://docs.google.com/document/d/1g8JFi8T_oAE_7uAri7Njtig7fKaPDfotU6huOa1alds/edit) ↩
普段、バックエンドに引きこもっているデータエンジニアだが、たまにはフロントエンドに出ていってみようかと思い、最速JSに近しい存在という爆速(=>死語?)なSvelteいじって三日目。そろそろ(無料で)デプロイなるものをしてみたい今日このごろ。
...ということで、nodejs系を始めいろいろと無料でお気楽にデプロイできそうなZeit NOWを試してみた。
参考 Now でクラウドの複雑さから解放されよう、今すぐに
github(かgitlabなど)の個人アカウントを持っているならば、たしかに、すぐさまデプロイできたのでメモを残しておく。Zeit now初体験だったが、10分ほどでデプロイをできた。
デプロイしたもの(カス):
アクセス先:
https://svelte1.now.sh
・・・デプロイしたものは、Svelteの公式チュートリアルの一部を改悪しただけのカス。
『zeit svelte』でググると出てくる以下から、
https://zeit.co/guides/deploying-svelte-with-zeit-now
自分が使っているgitリポジトリのアカウントを選ぶだけ。githubの場合
https://zeit.co/import/git?tab=github
あとは、SSOの認証を行うとディフォルトでprivateリポジトリにfolkしてくれる。
公開すると恥ずかしいサービスを作りたいとか、一山当てたいサービスを作りたい際には、privateリポジトリがディフォルトなのはありがたい限り。
folkたら当然、git cloneの類を持ってきて編集することになる。
フォルダ構成は以下の通り。ほぼSvelteのフォルダ構成通りだが.nowフォルダ配下がZeit NOWとの周りの調整を担ってくれているらしい。
とりあえず、お試ししたい場合は、↑の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ってどんなもんなんだろう?」ってところから始まり。
↓
「へぇ〜APIとか簡単にできるじゃん」となり。
↓
「ログイン認証とかも割と簡単なんじゃね」と思ったのでやってみました。
npm
を使ってExpressをインストールする。
entry pointはapp.js
に設定する。
$ mkdir myapp
$ cd myapp
$ npm init
$ npm install express
myappに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
ログイン認証に必要なものをインストールします。
(詳しく説明しませんが、ググれば大丈夫)
$ 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.html
、ok.html
、ng.html
を作成する
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)})// ...
<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.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 uno
・SparkFun midiシールド
・leapmotion
・node.js
・CASIO キーボード CTK-530
参考サイト:
ArduinoとNode.jsでシリアル通信で文字列を送受信するスクリプト
ArduinoとLeapmotionのシリアル接続
まずはleapmotion側(node.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からこのセンサーに変更します。
記事は着手次第アップします。
Artilleryは yaml ファイルに宣言的にシナリオを記述し、シンプルなインタフェースで負荷をかけることができる Nodejs 製の負荷テストツールです。
本記事では Artillery を使用して簡単に WebSocket サーバの負荷テストを実行する方法を紹介します。
まずはじめに WebSocket サーバを実装しましょう。今回は Node.js を使用します。
必要最小限の機能だけを提供します。wsライブラリを使用して簡単に実装しましょう。
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 は npm でインストールできます。
$ npm install-g wscat
WebSocket サーバを起動し、wscat で接続したら任意のメッセージを送信してみましょう。1つのクライアントからの送信を受けて、他のクライアントへ broadcast していることがわかります。
さて、ようやく本題です。Artillery を使用して負荷テストをかけてみましょう。
最小限のシナリオファイルのサンプルです。シナリオファイルは senario.yml
のような名前をつけておきます。
config:target:"ws://localhost:3000"phases:-duration:20arrivalRate:10scenarios:-engine:"ws"flow:-send:"hello"
以下、コマンドで実行します。
$ artillery run senario.yml
実際に WebSocket を用いたアプリケーションでは、常に多くのコネクションが張られていることが一般的です。上記のシナリオでは hello というメッセージを送ったらすぐにコネクションを切断してしまうので、常時アクティブなコネクションが少ない状態であまり現実的ではありません。まずは、think
を指定してアクティブなコネクション数が増えるように調整しましょう。また、同じメッセージを繰り返し送信する loop
も指定できます。
scenarios:-engine:"ws"flow:-send:"hello"-think:1# pause for 1 second-loop:-send:"world"count:5
さて先ほどまでは string 形式のデータだけに限定していましたが、{"name":"john", "age":24}
のようにオブジェクト形式で送信する方が良いこともあるでしょう。以下のように記述すれば、送信時に Stringify してくれます。
scenarios:-engine:"ws"flow:# the following will be stringified and sent as '{"name":"john","age":24}'-send:name:"john"age:24
また、送信したタイムスタンプを付与したいこともあります。このようなケースに対応するためにはカスタムコードを使用して柔軟に値を差し込むことができます。
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 超入門(第2版)を読んでいたところ、バリデーションの箇所が本の通り書いているにも関わらずエラーが発生。 正誤表を確認したところ、以下のような説明がありました。
本書で使用している Express Validator は、現在 ver.6 となり、仕様が変更されているため、本書の記述の通りでは正常に動作しなくなっています。
ver.5 にバージョンダウンすることでサンプルコードのまま実行することが出来たのですが、折角なので最新バージョンに修正して実行をしてみました。
本記事では、express-validator公式ドキュメントを参考に行った修正内容について紹介したいと思います。
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();}});});
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引数に記載req.getValidationResult().then()
でresult
を引数としてコールバック関数で処理していたが、ver.6ではvalidationResult(req)
を定数result
に代入し、関数の中でその値を使って処理notEmpty()
はなくなっているので、not().isEmpty()
のように宣言withMessage()
をメソッドチェーンにして使用し設定以上のように修正することで、記事作成時の最新バージョン(ver.6.4.0)のexpress-validatorでもバリデーションを実行することができました。
よかったら参考にしてみて下さい。
TypeORMのcreateConnectionメソッドを呼ぶ際の設定値は、色々な指定方法があります。
色々あって迷っちゃったので、整理して考えた結果、ormconfig.jsに書くのが分かりやすいのではという結論に至りました。
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の節の後半で、そう説明されています)。
1つ目は、何らかの手段によって、所定の名前の環境変数を設定する方法です。
TYPEORM_CONNECTION
やTYPEORM_HOST
など、TYPEORM_XXX
という名前の環境変数を設定しておくと、TypeORMはその値を参照します。参照される環境変数名は、以下のURLにまとめられています。
https://typeorm.io/#/using-ormconfig/using-environment-variables
この方法の亜種として、環境変数を設定する代わりに、ormconfig.envというファイルにそれらの設定を書く方法もあります。
2つ目は、ormconfig.xxx(ただし、xxx ≠ env)というファイルをpackage.jsonと同じ階層に置き、そこに設定を書く方法です。拡張子はjs、json、ymlなどから選べます。
module.exports={"type":"mysql","host":"localhost","port":3306,"username":"test","password":"test","database":"test"}
下記のURLで述べられている通り、上記2種類の設定方法を併用することはできません。環境変数TYPEORM_XXXが設定されていると、ormconfigファイルは無視されます。
https://typeorm.io/#/using-ormconfig/which-configuration-file-is-used-by-typeorm
TypeORMの設定値の中には、環境変数で指定したいものもあるでしょうし(例えばDBのパスワード)、分かりやすくどこかにハードコードしたいものもあるでしょう。ところがあいにく、環境変数TYPEORM_XXXとormconfigファイルの併用はできません。
そこで、以下のようにすればちょうど良い落とし所になるのではと考えました。
この時、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です。
Progateの無料版のレッスンはこれで終わります。
以前のレッスンで既にインストール済みです。
・ExpressとはNode.jsでWebアプリを開発するためのフレームワーク。
RubyでいうところのRailsになんでしょうね。C#でいうところのASP.NETみたいな。
・おなじみのパッケージ。便利な機能、メソッドが揃った塊、ライブラリの事。
・npm
コマンドでExpressをインストールする。npm
とはNode Package Manager
その名の通り、Node.jsのパッケージを管理するツール。これを使ってパッケージを取得する。
環境
最近こればっかりですが、VSCode使っていきます。
適当なデイレトリを作成し
VSVodeで開きます。
ターミナルを開きます。
まずinitします。
npm init
package name: (node)
と聞かれた作成するpackage.jsonの名前をいれます。任意な値。nodeにしました。
次に、以下を実行してExpressをインストールします。
npm install express
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
があります。
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
にアクセスすると以下のエラーとなった
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
にアクセス。
表示できました!
・見た目部分には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
にアクセス
・「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
にアクセスして
・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
にアクセスして
・ejs
とは「EJSは、HTMLとJavaScriptのコード両方を記述できるNode.jsのパッケージ」らしい。
Embedded javaScript
HTMLの中にJavaScriptを埋め込む
らしい。ReactはJSにHTMLを埋め込むイメージでしたよね。
・なんとnpm install ejs
を最初にやる必要があったらしい。
紛らわしい。なぜその手順を最初に解説しなかったのか・・・。
・JavaScritpを埋め込むには<% %>
または<%= %>
を使用するらしい。Rubyと一緒かな?
・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
にアクセスして
・比較的簡単にWebが作れました。
DBから引っ張ってくるところとかはおそらく有料の上級編でやっているんだろうなぁ・・・。
・Node.jsに言えたことではないが、今まで無料レッスンやってきた言語のWeb系のデプロイってどうやるんだろうw
今回でProgate無料版はすべてこなしました。
いつも書いているように、無料版なので、基礎や基本的なことしかやらないので、即何かアプリを作れるかと言ったら、この内容だけじゃ難しいですが、他無料サイトをみながらアプリ作成を始めてみる際にはこのレッスンの経験が少しは生きてくると思いました。
次回はReactのチュートリアルの三目並べゲームをやってみたいと思います。
最近のWebアプリを作るために様々な技術が登場しています。
結局どれで作るのが良いのだろうとふと疑問に思った時、思わず目に入ってきたサイトを見つけました。それがScaffoldHubでした。
より多くの人にScaffoldHubを知ってもらいたいと思い
(実は既に知っている人大多数説あり)、恥ずかしながら記事の初投稿を決意しました。
この記事はScaffoldHubの公式サイトに書いてあることを記載しているだけなので、詳しくは以下のURLの公式サイトや公式ブログを参照してください。
https://scaffoldhub.io/
ScaffoldHubとは、JavaScriptで書かれたWebアプリのコード自動生成サービスです。選べるものとして、フロントエンドには御三家(React, Vue, Angular)、バックエンドはNodeJSのみ、DBにはSQL, MongoDB, Firebase Firestoreが選べるようです。Webアプリ生成した後は、生成されたソースを基に機能拡張ができるので、自分好みに改良できます。一からWebアプリを作らなくて良いのは魅力的です。
サンプルとしてWebアプリを動かすのは無料ですが、ソースコードのダウンロードはもちろん有料です。気になる価格ですが、なんと39ドル!!
これは安いのでは!?と思いました。公式サイトを見る限り、これ以上かかる費用はなさそうですが、購入を検討されている方は購入前に必ず詳細を確認してください。
いくつか抜粋して、ScaffoldHubの特徴を記載します。
認証機能
ユーザ名、パスワード認証やfacebook, Twitter, Google認証が実装されているようで、これらの認証機能を一から実装する必要がないようです。
ユーザ・ロール、パーミッション管理
それぞれを管理でき、権限制御ができるようです。
監査ログ
ユーザのアクション(ログイン、ページ遷移等)がログに記録されるようです。
モバイルとの親和性
ScaffoldHubから生成したWebアプリはモバイル用のデザインも用意されているようです。
まだまだ、ありますが公式サイトを見ると全容がわかるので、詳細はそちらを参照しください、
ScaffoldHubは、2019年5月からサービスを開始していたようですが、
ScaffoldHubに関する記事が見当たらなかったので、記事投稿をしてみました。(今回の記事がQiitaデビュー)
まだ、次回の記事はScaffoldHubをしっかり見れていないのでこれからデモを動かしたときの使用感を試していきたいと思います。
ScaffoldHubについて、知っている方や気になる方がいらっしゃればこの記事を機会にぜひ公式サイトをみて欲しいです。また、気軽なコメントもぜひお待ちしております。
https://scaffoldhub.io/
ローカルの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できない、とのことだった
// スリープconstsleep=delay=>newPromise(resolve=>setTimeout(resolve,delay));(async()=>{// 5秒待機awaitsleep(5000);})();
ヾ(・ω<)ノ" 三三三● ⅱⅲ コロコロ♪
------------------- ↓ 余談はここから ↓-------------------
lockファイルでインストールを制御ってのは、
割と理想論だと思っている。
まぁ、それはいいとして、
lockファイルをコミットすると、
ほぼ間違いなくコンフリクトする。
そんなときの対処法を残しておこう。
------------------- ↓ 本題はここから ↓-------------------
コンフリクトを適当に解消して以下のコマンドを打つ
$ composer update --lock
コンフリクトした状態のまま以下のコマンドを打つ
$ npm install
下記2点で情報入手
①Webサイト
cheerio-httpcli - Node.js用WEBスクレイピングモジュール
②書籍(P53~P67)
Webクローラー/ネットエージェント開発テクニック
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
このシステムではスクリプトの実行が無効になっているため、~~~
というエラーが出るとき突然ですが、絵を描く用の画像資料が10GBを超えてしまいました。
何かいい画像ビューアないかなと探していたのですがイマイチだったので、自分で作ってみようと思いました。
HTML/CSSは少しやったことがあったので、ちょうどいいやということでelectronを始めてみました。🤯
あとこの記事ではwindows10のみを対象にしています。
macとかlinuxは使ったことがないので分かりません。すみません。
もちろんプログラム初心者です。間違いがあったら教えてください。
さて、googleでelectronについて検索してもヒットするのは古い情報が多いですよね。(そのせいで合計2日分くらい無駄にしてしまいました)
私みたいな人を増やさないために、今の時点で分かったことを初心者目線でまとめておきます。
(あと頭の中身を整理して今抱えているエラーを解決するため)
画像ビューアを作ろうとしたとき、まずはこちらの記事(簡単なウインドウアプリ(GUI)開発のまとめ(Windows偏))を参考にさせていただきました。
まずは一番上にあったHTA(WSH?)を試してみたのですが、
次にNW.jsを使ってみたのですが、
で、electronに至りました。electronは、
みたいな感じです。
個人的に積極的に使いたい子ではないです。
他のはウーンって感じだしC#はハードル高そうだしってなって消極的に採用しました。
他に何かよさげなのあったら教えてください!
まずはNode.jsをインストールしてください。
次におおまかにelectron開発の流れをなぞるとこんな感じです。
moge
)を作るmoge
の中に必要なフォルダとファイルを作る(main.js
, index.html
, src/package.json
など)moge/package.json
を編集する次で詳しく触れていきます。
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.json
、moge
直下の方をmoge/package.json
と呼ぶことにします。
(これ何で名前同じの2つ作らせる仕様なんですかね?ややこしい!)
色んなの資料読んでるとどっちのpackage.json
を指してるのか分かりにくくて初心者的には大変です。
体感ですが、moge/package.json
の方を指してることが多いと思います。ていうか多分そう。
まずは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を使ってみました。
<!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
も、どのプロジェクトでもほとんど同じようなものになるらしいです。
{"main":"main.js"}
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のことらしいです。
これでファイルの準備はおしまいです!
エクスプローラでmoge
フォルダの余白をshift + 右クリック
して、PowerShell ウィンドウをここで開く(S)
を選択してください。
(どうでもいいですけどPowerShell ウィンドウ「で」ここ「を」開く(S)
じゃないですかね?)
そしたら紺色のウィンドウが出てくるはずです。
これはPowerShellというもので、コマンドを打ち込むとwindowsに命令を下せます。
electronの母体であるNode.jsは、モジュール?をインストールするのにこのPowerShellを使う必要があります。
よく見る.exeのインストーラーは使わないらしいです。
そしたら、まずはnode -v
を実行してください。
下みたくなったらNode.jsのインストールに成功していることが確認できます。
PSD:\electron\moge>node-vv12.16.0
確認できたら、npm i -D electron
とnpm i -D electron-builder
を実行してください。
どちらも少し時間がかかります。
これは、electronとelectron-builderをmoge
フォルダにインストールするという工程です。-D
を-g
にすることで、グローバルなインストールができるらしいですが、electronはローカルにインストールするのがいいらしいです?
下みたくなったらOKです。
PSD:\electron\moge>npmi-Delectron+electron@8.0.2added85packagesfrom93contributorsandaudited102packagesin10.474s
PSD:\electron\moge>npmi-Delectron-builder+electron-builder@22.3.2added138packagesfrom107contributorsandaudited770packagesin15.356s
このとき、プロジェクトフォルダは次みたくなってるはずです。
追加されたnode_modules
とpackage-lock.json
は何もしなくてOKです。
moge
├ node_modules
├ src
│ ├ main.js
│ ├ index.html
│ └ package.json
├ package.json
└ package-lock.json
確認できたら、npm start
を実行してください。
PSD:\electron\moge>npmstart>moge@1.0.0startD:\electron\moge>electron.
テンション上がりますね!うおおお😭
このままだとNode.jsやelectronがインストールされている環境でPowerShellから呼び出さないと起動できませんが、ビルドするとダブルクリックひとつ(?)で起動できるようになります!
次でビルドしていきます。
ここによると、PowerShellはセキュリティの都合でよく分からないコマンドを実行できないような設定になってるらしいです。
electron-builderはその「よく分からないコマンド」に属しているらしく、初期設定のままだと実行できません。
なので、さっきの記事に従って設定を変更しましょう。
ちなみに私はSet-ExecutionPolicy RemoteSigned
を実行しました。
そしたら、electron-builder build --win
を実行しましょう。
この処理は1~2分くらいかかります。
下みたくなったら成功です。
PSD:\electron\moge>electron-builderbuild--win・electron-builderversion=22.3.2os=10.0.17763・loadedconfigurationfile=package.json("build"field)・descriptionismissedinthepackage.jsonappPackageFile=D:\electron\moge\package.json・writingeffectiveconfigfile=dist\builder-effective-config.yaml・packagingplatform=win32arch=x64electron=8.0.2appOutDir=dist\win-unpacked・defaultElectroniconisusedreason=applicationiconisnotset・buildingtarget=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
と同じウィンドウが出てきましたね!
成功です👺
以上がelectron開発の流れになります。
(長かった…)
単純にもう一回electron-builder build --win
を実行するとエラーが出ることがあります。
そのときは、
dist
フォルダを削除するC:/Users/(Username)/AppData/Roaming/moge
を削除するで再ビルドできます。
もしフォルダを削除しようとして使用中なので削除できませんというエラーが出たときは、
moge 1.0.0.exe
を閉じるをするとフォルダを削除できるようになります。
(electron-rebuilderとかいうやつがあるとかないとか聞きますが、使い方が分かりません🤷♀️)
私が分かっていることは以上です。
こうして頭の中を整理してみましたが、今抱えている
「ビルドしたあとに外部ファイルを読み込ませるにはどうしたらいいか」
を解決する糸口は全くつかめませんでした💥
どなたか助けていただけると幸いです…
「公式ページのここに書いてありそう」とかでも構わないので教えてください…
で、初心者の方にも分かりやすい構成を目指して書いてみたのですが、どうだったでしょうか(冗長すぎたかも)。
こんな記事でしたが、最後まで読んでいただいてありがとうございました!🍣
.env
ファイルに定義された値を環境変数として使うことができます.env
を配置し、本番ではホスティングサービスの機能で環境変数として設定するといった使い方をすることでリポジトリ内のファイルを変更せずに実行することができますindex.js
があってnode index.js
で実行するようなケースですnpm i dotenv
index.js
を作成します
require('dotenv').config();
としておくことでdotenvが適用されるようになりますprocess.env
で環境変数を取得できますTEST_VALUE
という環境変数の値を取得しコンソールに出力するサンプルですrequire('dotenv').config();constvalue=process.env.TEST_VALUE;console.log(value);
.env
ファイルを作成します# 環境変数を定義
TEST_VALUE=.envで値を設定しています
node index.js
.envで値を設定しています
と出力されているはずです.env
の値をシステム環境変数で上書きできることも確認しておきましょうTEST_VALUE="コマンドで値を設定しています" node index.js
コマンドで値を設定しています
と出力されているはずですnpm i webpack webpack-cli dotenv-webpack
constDotenv=require('dotenv-webpack');module.exports={plugins:[// { systemvars: true } を設定するとシステム環境変数も読み込まれるようになるnewDotenv({systemvars:true}),],};
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
コマンドで値を設定しています
とログが出力されるはずです本記事は、2020年3月6日 (米国時間) にて、Azure Cosmos DB に新しく Free Tier (無償利用枠) が登場したことに伴い、改めて Azure Cosmos DB を色々と触っていく試みの 4 回目です。
今回も、前回記事同様、 Microsoft Azure Cosmos JavaScript SDKについて見ていきたいと思います。
実際に、Microsoft Docsの内容を元に、JavaScript SDK (SQL API) の中身を見ていきます。
今回はデータベースの読み取り、または削除
を行う 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
メソッドは、どうなっているのか、実際に中身を確認してみます。
publicdatabase(id:string):Database{returnnewDatabase(this,id,this.clientContext);}
はい、Database クラスのコンストラクタが動いています。
まあ Database 型の定数(const)に値を代入しているのだから当たり前ですよね。
このコンストラクタ、プログラミング初心者にとってはすごく理解が難しい引数this
がありますが、一旦置いておいて、実際のコンストラクタの中身を見ていきます。
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 ファイルも見てみます。
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 って本当に便利ですね。
さて、本題のコンストラクタ処理に戻ります。
exportclassDatabase{publicreadonlycontainers:Containers;publicreadonlyusers:Users;constructor(publicreadonlyclient:CosmosClient,publicreadonlyid:string,privateclientContext:ClientContext){this.containers=newContainers(this,this.clientContext);this.users=newUsers(this,this.clientContext);}
コンストラクタのパラメータプロパティ宣言を除くと、Containers
とUsers
のクラスを生成する処理があります。
実際に、この 2 つのコンストラクタの中身をみていきます。
exportclassContainers{constructor(publicreadonlydatabase:Database,privatereadonlyclientContext:ClientContext){}}
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
クラスについて見ていこうと思います。
以前ndenv
を使っていたのですが、気づいたらdeprecated
になっていたので転職時に新しい端末になったのを機にnodenv
へ移行しました。anyenv
使って入れるのがステップ数多くてだるいなと思っていたのですが、homebrew
であれば割とサクッと入れられそうだったので今回はこちらで導入してみるので、備忘録として残しておきます。
https://github.com/nodenv/nodenv
導入済み前提で進めるので、導入方法については割愛します。
各々の環境で異なっている所だと思うので適時環境にあった方法にて対応ください。Macなら大体当該ディレクトリを消せば済むと思います。
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
打てば全部丁寧に教えてはくれるのですが、代表的なのを抜粋しておきます。
nodenv install -l
nodenv install {{ バージョン }}
nodenv versions
よくlistって打って怒られてます…
nodenv version
node -v
でもいいんですが、こちらで表示するとNode.jsのバージョン選定理由が併記されます。
12.16.0 (set by /Users/ユーザ/.nodenv/version)
nodenv global {{ バージョン }}
nodenv local {{ バージョン }}
ターミナルでの現在位置に.node-version
というファイルが作られます。このファイルがあると、当該ディレクトリでNode.js実行時に自動的に指定バージョンのものを使おうとし、指定バージョンが導入されてなければ当該バージョンをインストールするようにエラーが出ます。
nodenv: version `12.16.0' is not installed (set by /Users/ユーザ/プロジェクト/.node-version)
npm
がないと怒られる場合PHPStormなどのJetBrains製IDEを使っていると、過去にndenv
を利用していた時にも同じことを書いていますが、この状態でプロジェクトを開いてNPMスクリプトを実行しようとした際にパッケージマネージャが見つからないと怒られると思います。
非常にだるいのでなんとかします。
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タブも無事叩き放題になります。
昔と比べると導入するのも楽になった気がします。多大な労力を割くような場所ではないと思うので、楽になるに越したことはないですね。