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

Macでnodeのバージョンを切り替える〜少しめんどい〜

$
0
0

nodeのバージョンを切り替えるための処理です!
homebrewがインストールされている前提で話をすすめます。homebrewがインストールされてない方はインストールして下さい。

nodebrewがインストールされているか確認

$nodebrew-v
$node-v

でnodeのバージョンも確認できる

nodebrewをインストール(インストール済みであれば不要)

$brewinstallnodebrew
$nodebrew-v

でバージョン確認

nodebrewでインストール可能なnodeのバージョンを確認

$ nodebrew ls-remote

ローカルの状況確認(nodebrewを通さずにインストールされているnodeがあれば一旦、アンインストールが必要)

$brewls

→nodeが入っていれば一度アンイストールしなければいけない

今まで使っていたnodeをアンインストール

$brewuninstall--forcenode

→無理なら$ brew uninstall --ignore-dependencies node

pathを設定

$vi~/.bash_profile

vimでファイルを開き、以下を追加する。(iでインサートモード)

export PATH=$HOME/.nodebrew/current/bin:$PATH
bash_profile

→esc、:wqで保存する

$source~/.bash_profile

→更新して設定を反映させる。

セットアップ

$nodebrewsetup

→セットアップのためのコマンドで、これやらないとインストールできない。

インストール可能なバージョンを確認

$ nodebrew ls-remote

nodebrewを通して、使いたいバージョンを指定してnodeをインストール

$ nodebrew install-binary 8.11.1

使用するnodeのバージョンを選択

$ nodebrew use v10.15.0

nodeが正しく設定されているか確認

$ nodebrew ls
v8.11.1
v12.14.0

current: v12.14.0

こんな感じになれば正解。


Vueはどこへ消えた?

$
0
0

この物語は、Vue CLIに興味を持ち、Yarnを使ってインストールしてみたものの、パスを見失ってしまった哀れな開発者の奮闘の記録です。

第1話 WindowsにYarnでVue CLIをインストールする

事件編

まずはWindowsにNode.jsとYarnをインストールします。使うのは、Windows用のパッケージマネージャChocolatey

choco install nodejs
choco install yarn

で、Vue CLIの記述の通り、YarnでVue CLIをインストール。

yarn global add @vue/cli

ちゃんとインストールできたかか確認すると……

vue --version

……こんな無慈悲なメッセージが。

'vue' は、内部コマンドまたは外部コマンド、
操作可能なプログラムまたはバッチ ファイルとして認識されていません。

Vueはどこへ消えた?

解決編

Yarnでグローバルインストールしたパッケージは、C:\Users\me\AppData\Local\Yarn\binに隠れています。
なので、このパス(%AppData%\Local\Yarn\binでも可)を手動で環境変数に追加すれば、パスが通ります。
めでたしめでたし。

第2話 ChromebookにYarnでVue CLIをインストールする

事件編

次は、Chromebook(上のLinux)にもVue CLIをインストールしてみます。
Node.js 公式のバイナリディストリビューションを参考にNode.jsと、

curl -sL https://deb.nodesource.com/setup_13.x | sudo -E bash -
sudo apt-get install -y nodejs

Yarn公式サイトを参考にYarnをインストールします。

curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
sudo apt-get update && sudo apt-get install yarn

で、さっきと同じようにYarnでVue CLIをインストールし、確認すると……
……こんな無慈悲なメッセージが。

-bash: vue: command not found

Vueはどこへ消えた?

解決編

Yarnでグローバルインストールしたパッケージは、「Linux ファイル」からアクセスできるホームフォルダ内の.yarn/binに存在します。
同じディレクトリにある.profileファイルをテキストエディタとかで開き、

if [ -d "$HOME/.yarn/bin" ] ; then
    PATH="$HOME/.yarn/bin:$PATH"
fi

と末尾に追記して、ターミナルを再起動すると、無事パスが通ります。
めでたしめでたし。


ちなみに、Angular CLIngコマンドなんかも同じ要領で解決できますよ。

npmモジュールをインポートしてRollupでビルドしたときに○○ is not definedが出たときの対処

$
0
0

svelteを使ってちょっとしたWebアプリを作っているときにハマったのでメモ。

環境

  • macOS Catalina
  • Node v12.15.0
  • npm v6.13.4
  • rollup.js v1.20.0
  • Svelte v3.0.0

事象

svelteではクイックスタート用のテンプレートが用意されており、その中でモジュールバンドラとしてRollupを利用しています(参考)。
このテンプレートを元に開発を進めていたところ、特定のnpmモジュールをインポートして動かした際に下記のエラーに遭遇しました。

Uncaught ReferenceError: stream is not defined

streamというモジュールは自分が書いたプログラムの中では使っていません。

問題点

インポートしたnpmモジュールが内部でNodeのビルトインモジュールを利用しており、ブラウザ環境ではそんなモジュールないよ!となってエラーとなっていたようです。

解決方法

下記のRollupプラグインモジュールを導入する。

インストール

npm install--save-dev rollup-plugin-node-builtins rollup-plugin-node-globals

rollup.config.jsの設定

rollup.config.js
...+importbuiltinsfrom'rollup-plugin-node-builtins';+importglobalsfrom'rollup-plugin-node-globals';exportdefault{...plugins:[...resolve({browser:true,dedupe:['svelte'],}),commonjs(),+globals(),+builtins(),...],...}

まとめ

プラグインを利用することで、Nodeのビルトインモジュール依存の処理をRollupでビルドすることができます。

npmコマンド備忘録

$
0
0

確認

npm -–version
npm -v
npm version
npm help
npm root
npm bin
npm show パッケージ名

パッケージ検索

npm search パッケージ名

リリースされたパッケージのバージョン

npm info パッケージ名 versions

直接インストールしたパッケージ

npm list --depth=0

ローカルインストール済みのパッケージのバージョン

npm list (--depth=0)

グローバルインストール済みのパッケージのバージョン

npm list --depth=0) -g

未更新のパッケージを確認

npm outdated

package.jsonに記載されているパッケージのバージョンに更新

npm update

初期化

npm init

復元

npm install

インストール

グローバル

npm install –g パッケージ名

ローカル

npm install パッケージ名

package.json記述

package.json記述

npm install (-g) -save パッケージ名

package.json'(開発用)記述

npm install (-g) -save-dev パッケージ名

バージョン指定

npm install (-g) (-save) パッケージ名@x.x.x

グローバルインストールしたパッケージをアンインストール

npm uninstall -g パッケージ名

dependenciesから依存関係を削除

npm uninstall --save パッケージ名

ディレクトリ

ローカルインストール

カレントディレクトリの node_modules 配下にインストール

グローバルルインストール

buildを実行

npm run build

Electronで作成しているアプリケーションでfirebaseモジュールを使おうとした時に発生するエラーの解決[Failed to load gRPC binary module because it was not installed for the current system]

$
0
0

% npm i firebase
を実行して、プログラム内に

constfirebase=require('firebase');

を記述し、% electron .でアプリを起動した際に

Uncaught Error: Failed to load gRPC binary module because it was not installed for the current system

とgrpcモジュールに関するエラーが表示された。

% npm i grpc

を実行したが、今度はgrpcモジュール内にnode_modules/grpc/src/node/extension_binary/electron-v6.0-darwin-x64-unknown/grpc_node.nodeというファイルが無いぞとエラーで怒られる。

さらにnode-v72-darwin-x64-unknown/grpc_node.nodeってファイルならあったけどね!npm rebuildすると治るかも!と書かれていたので愚直に% npm rebuildを実行するも結果は変わらず。

拡張バイナリの中にnode-v72(以下略がありelectron-v6.0(以下略が無いならリビルド時にターゲットを指定してみようと思い、まずelectronのバージョンを確認した。% electron --version

使っていたのはv6.0.10であったため、ランタイムとバージョンを指定してリビルド。% npm rebuild --runtime=electron --target=6.0.10

再度electronアプリを起動すると、エラーも表示されず無事解決した。

Node.js の gRPC で Redis のデータを作成 (Create)

$
0
0

設定ファイル、サーバープログラム、クライアントプログラムの3つが必要です。

設定ファイル

redis_create.proto
こちらと同じ
Python の gRPC で Redis のデータを作成 (Create)

サーバープログラム

redis_create_server.js
#! /usr/bin/node
// ---------------------------------------------------------------//  redis_create_server.js////                  Feb/09/2020// ---------------------------------------------------------------varPROTO_PATH='redis_create.proto'vargrpc=require('grpc')varprotoLoader=require('@grpc/proto-loader')varpackageDefinition=protoLoader.loadSync(PROTO_PATH,{keepCase:true,longs:String,enums:String,defaults:true,oneofs:true});varredis_create_proto=grpc.loadPackageDefinition(packageDefinition).redis_create// ---------------------------------------------------------------functionredis_create_proc(key_in,str_json){constredis=require("redis")constclient=redis.createClient(6379,'localhost')client.on("error",function(err){console.log("Redis connection error to "+client.host+":"+client.port+" - "+err)})client.set(key_in,str_json,redis.print)client.quit()}// ---------------------------------------------------------------functionRedisCreate(call,callback){console.error("*** RedisCreate ***")constkey_in=call.request.keyconststr_json=call.request.str_jsonconststr_out='RedisCreate '+key_inconsole.error(str_json)redis_create_proc(key_in,str_json)callback(null,{key:call.request.key});}// ---------------------------------------------------------------functionmain(){varserver=newgrpc.Server()server.addService(redis_create_proto.Greeter.service,{RedisCreate:RedisCreate})server.bind('0.0.0.0:50051',grpc.ServerCredentials.createInsecure())server.start()}// ---------------------------------------------------------------main()// ---------------------------------------------------------------

クライアントプログラム

redis_create_client.js
#! /usr/bin/node
// ---------------------------------------------------------------//  redis_create_client.js////                      Feb/09/2020// ---------------------------------------------------------------varPROTO_PATH='redis_create.proto'vargrpc=require('grpc');varprotoLoader=require('@grpc/proto-loader');varpackageDefinition=protoLoader.loadSync(PROTO_PATH,{keepCase:true,longs:String,enums:String,defaults:true,oneofs:true})varredis_create_proto=grpc.loadPackageDefinition(packageDefinition).redis_create// ---------------------------------------------------------------functionmain(){varclient=newredis_create_proto.Greeter('localhost:50051',grpc.credentials.createInsecure())varkey_in=process.argv[2]varunit_aa={}unit_aa.name=process.argv[3]unit_aa.population=process.argv[4]unit_aa.date_mod=process.argv[5]conststr_json=JSON.stringify(unit_aa)console.error(unit_aa.name)client.RedisCreate({key:key_in,str_json:str_json},function(err,response){console.log('key:',response.key)})}// ---------------------------------------------------------------main()// ---------------------------------------------------------------

サーバープログラムの起動

export NODE_PATH=/usr/lib/node_modules
./redis_create_server.js

クライアントプログラムの実行

$ export NODE_PATH=/usr/lib/node_modules 
$ ./redis_create_client.js t0931 那須烏山 98735 2005-6-12
那須烏山
key: t0931

Node.js の gRPC で Redis のデータを読む (Read)

$
0
0

設定ファイル、サーバープログラム、クライアントプログラムの3つが必要です。

設定ファイル

redis_read.proto
こちらと同じ
Python の gRPC で Redis のデータを読む (Read)

サーバープログラム

redis_read_server.js
#! /usr/bin/node
// ---------------------------------------------------------------//  redis_read_server.js////                  Feb/08/2020// ---------------------------------------------------------------varPROTO_PATH='redis_read.proto'vargrpc=require('grpc')varprotoLoader=require('@grpc/proto-loader')varpackageDefinition=protoLoader.loadSync(PROTO_PATH,{keepCase:true,longs:String,enums:String,defaults:true,oneofs:true});varredis_read_proto=grpc.loadPackageDefinition(packageDefinition).redis_read// ---------------------------------------------------------------functionredis_read_proc(key_in,callback){constredis=require("redis")constclient=redis.createClient(6379,'localhost')client.get(key_in,function(err,reply){if(err){console.log("Get error: "+err)}elseif(reply!=null){conststr_json=replyconstdata=JSON.parse(str_json)varout_str=key_in+"\t"out_str+=data.name+"\t"out_str+=data.population+"\t"out_str+=data.date_modconsole.log(out_str)callback(null,{str_json:str_json})}client.quit()})}// ---------------------------------------------------------------functionRedisRead(call,callback){console.error("*** RedisDelete ***")constkey_in=call.request.keyconststr_out='RedisDelete '+key_inconsole.error(str_out)redis_read_proc(key_in,callback)//  callback(null, {key: key_in})}// ---------------------------------------------------------------functionmain(){varserver=newgrpc.Server()server.addService(redis_read_proto.Greeter.service,{RedisRead:RedisRead})server.bind('0.0.0.0:50051',grpc.ServerCredentials.createInsecure())server.start()}// ---------------------------------------------------------------main()// ---------------------------------------------------------------

クライアントプログラム

redis_read_client.js
#! /usr/bin/node
// ---------------------------------------------------------------//  redis_read_client.js////                      Feb/08/2020// ---------------------------------------------------------------varPROTO_PATH='redis_read.proto'vargrpc=require('grpc');varprotoLoader=require('@grpc/proto-loader');varpackageDefinition=protoLoader.loadSync(PROTO_PATH,{keepCase:true,longs:String,enums:String,defaults:true,oneofs:true});varredis_read_proto=grpc.loadPackageDefinition(packageDefinition).redis_read// ---------------------------------------------------------------functionmain(){varclient=newredis_read_proto.Greeter('localhost:50051',grpc.credentials.createInsecure())varkey_in=process.argv[2]client.RedisRead({key:key_in},function(err,response){//    console.log('str_json:', response.str_json)constdata=JSON.parse(response.str_json)varout_str=data.name+"\t"out_str+=data.population+"\t"out_str+=data.date_modconsole.log(out_str)})}// ---------------------------------------------------------------main()// ---------------------------------------------------------------

サーバープログラムの起動

export NODE_PATH=/usr/lib/node_modules
./redis_read_server.js

クライアントプログラムの実行

$ export NODE_PATH=/usr/lib/node_modules
$ ./redis_read_client.js t1852
敦賀  41295   2003-5-10

Node.js の gRPC で Redis のデータを更新 (Update)

$
0
0

設定ファイル、サーバープログラム、クライアントプログラムの3つが必要です。

設定ファイル

redis_update.proto
こちらと同じ
Python の gRPC で Redis のデータを更新 (Update)

サーバープログラム

redis_update_server.js
#! /usr/bin/node
// ---------------------------------------------------------------//  redis_update_server.js////                  Feb/09/2020// ---------------------------------------------------------------varPROTO_PATH='redis_update.proto'vargrpc=require('grpc')varprotoLoader=require('@grpc/proto-loader')varpackageDefinition=protoLoader.loadSync(PROTO_PATH,{keepCase:true,longs:String,enums:String,defaults:true,oneofs:true});varredis_update_proto=grpc.loadPackageDefinition(packageDefinition).redis_update// ---------------------------------------------------------------functionget_current_date_proc(){consttoday=newDate()varddx=(1900+today.getYear())+"-"+(today.getMonth()+1)ddx+="-"+today.getDate()returnddx}// ---------------------------------------------------------------functionredis_update_proc(key_in,population_in){constredis=require("redis")constclient=redis.createClient(6379,'localhost')client.on("error",function(err){console.log("Redis connection error to "+client.host+":"+client.port+" - "+err)})client.get(key_in,function(err,reply){if(err){console.log("Get error: "+err)}elseif(reply!=null){varjson_str=replyvarunit_aa=JSON.parse(json_str)unit_aa.population=population_inunit_aa.date_mod=get_current_date_proc()varjson_out=JSON.stringify(unit_aa)console.log(json_out)client.set(key_in,json_out,redis.print)client.quit()}})}// ---------------------------------------------------------------functionRedisUpdate(call,callback){console.error("*** RedisUpdate ***")constkey_in=call.request.keyconstpopulation_in=call.request.populationconststr_out='RedisUpdate '+call.request.keyconsole.error(str_out)redis_update_proc(key_in,population_in)callback(null,{key:call.request.key});}// ---------------------------------------------------------------functionmain(){varserver=newgrpc.Server()server.addService(redis_update_proto.Greeter.service,{RedisUpdate:RedisUpdate})server.bind('0.0.0.0:50051',grpc.ServerCredentials.createInsecure())server.start()}// ---------------------------------------------------------------main()// ---------------------------------------------------------------

クライアントプログラム

redis_update_client.js
#! /usr/bin/node
// ---------------------------------------------------------------//  redis_update_client.js////                      Feb/09/2020// ---------------------------------------------------------------varPROTO_PATH='redis_update.proto'vargrpc=require('grpc')varprotoLoader=require('@grpc/proto-loader')varpackageDefinition=protoLoader.loadSync(PROTO_PATH,{keepCase:true,longs:String,enums:String,defaults:true,oneofs:true})varredis_update_proto=grpc.loadPackageDefinition(packageDefinition).redis_update// ---------------------------------------------------------------functionmain(){varclient=newredis_update_proto.Greeter('localhost:50051',grpc.credentials.createInsecure())varkey_in=process.argv[2]varpopulation_in=process.argv[3]client.RedisUpdate({key:key_in,population:population_in},function(err,response){console.log('key:',response.key)})}// ---------------------------------------------------------------main()// ---------------------------------------------------------------

サーバープログラムの起動

export NODE_PATH=/usr/lib/node_modules
./redis_update_server.js

クライアントプログラムの実行

$ export NODE_PATH=/usr/lib/node_modules
$ ./redis_update_client.js t1857 8234500
key: t1857

Node.js の gRPC で Redis のデータを削除 (Delete)

$
0
0

設定ファイル、サーバープログラム、クライアントプログラムの3つが必要です。

設定ファイル

redis_delete.proto
こちらと同じ
Python の gRPC で Redis のデータを削除 (Delete)

サーバープログラム

redis_delete_server.js
#! /usr/bin/node
// ---------------------------------------------------------------//  redis_delete_server.js////                  Feb/08/2020// ---------------------------------------------------------------varPROTO_PATH='redis_delete.proto'vargrpc=require('grpc')varprotoLoader=require('@grpc/proto-loader')varpackageDefinition=protoLoader.loadSync(PROTO_PATH,{keepCase:true,longs:String,enums:String,defaults:true,oneofs:true});varredis_delete_proto=grpc.loadPackageDefinition(packageDefinition).redis_delete// ---------------------------------------------------------------functionredis_delete_proc(key_in){constredis=require("redis")constclient=redis.createClient(6379,'localhost')client.on("error",function(err){console.log("Redis connection error to "+client.host+":"+client.port+" - "+err)})client.del(key_in)client.quit()}// ---------------------------------------------------------------functionRedisDelete(call,callback){console.error("*** RedisDelete ***")constkey_in=call.request.keyconststr_out='RedisDelete '+call.request.keyconsole.error(str_out)redis_delete_proc(key_in)callback(null,{key:call.request.key});}// ---------------------------------------------------------------functionmain(){varserver=newgrpc.Server()server.addService(redis_delete_proto.Greeter.service,{RedisDelete:RedisDelete})server.bind('0.0.0.0:50051',grpc.ServerCredentials.createInsecure())server.start()}// ---------------------------------------------------------------main()// ---------------------------------------------------------------

クライアントプログラム

redis_delete_client.js
#! /usr/bin/node
// ---------------------------------------------------------------//  redis_delete_client.js////                      Feb/08/2020// ---------------------------------------------------------------varPROTO_PATH='redis_delete.proto'vargrpc=require('grpc');varprotoLoader=require('@grpc/proto-loader');varpackageDefinition=protoLoader.loadSync(PROTO_PATH,{keepCase:true,longs:String,enums:String,defaults:true,oneofs:true});varredis_delete_proto=grpc.loadPackageDefinition(packageDefinition).redis_delete// ---------------------------------------------------------------functionmain(){varclient=newredis_delete_proto.Greeter('localhost:50051',grpc.credentials.createInsecure())varkey_in=process.argv[2]client.RedisDelete({key:key_in},function(err,response){console.log('key:',response.key)})}// ---------------------------------------------------------------main()// ---------------------------------------------------------------

サーバープログラムの起動

export NODE_PATH=/usr/lib/node_modules
./redis_delete_server.js

クライアントプログラムの実行

$ export NODE_PATH=/usr/lib/node_modules
$ ./redis_delete_client.js t1855
key: t1855

FirestoreでCollectionを削除する

$
0
0

Firestoreではコレクションを完全に削除するにはその下に紐づいているドキュメントを一個一個全部削除してからそのあと、コレクションを削除しないと、完全に削除できません。

公式ドキュメント↓
https://firebase.google.com/docs/firestore/manage-data/delete-data?hl=ja

また、FirestoreのBatchでは一度に500個までしかできないので、500以上まとめて削除する場合はそれぞれを500個ずつ分割して削除する必要があります。

公式ドキュメント↓
https://firebase.google.com/docs/firestore/manage-data/transactions?hl=ja

なので、それを考慮して削除するコードを記載します。

実装

const batchArray: admin.firestore.WriteBatch[] = [];
    batchArray.push(db.batch());
    let operationCounter = 0;
    let batchIndex = 0;

    const roomId = chatQuery.docs[0].id;
    const deleteDoc = await db
      .collection("test")
      .doc(testId)
      .collection("docs")
      .get();
    deleteDoc.forEach(doc => {
      batchArray[batchIndex].delete;
      operationCounter++;

      if (operationCounter === 499) {
        batchArray.push(db.batch());
        batchIndex++;
        operationCounter = 0;
      }
    });

    batchArray.forEach(async batch => await batch.commit());

    await db
      .collection("chat_room")
      .doc(roomId)
      .delete();
  }

Node.jsでGoogle Slides内のテキストの書き換え

$
0
0

Google Slides APIをNode.jsから触ってみてます。

の記事の続きです。

batchUpdate()で更新

presentations.batchUpdateでどうやら更新ができそうです。

presentations.batchUpdate()の

presentations.batchUpdate()の使い方がいまいち分からなかったけど、stackoverflowの投稿を参考に

presentationId:プレゼンテーションIDを指定,resource:{requests:{//ここにリクエストする内容を入れる}}

みたいな使い方というのが分かりました。

app.js
省略functionlistSlides(auth){constslides=google.slides({version:'v1',auth});slides.presentations.batchUpdate({presentationId:presentationId,resource:{requests:{replaceAllText:{containsText:{text:"Node.js"//置き換え前のテキスト},replaceText:"Hoge"//置き換え後のテキスト}}}},(err,res)=>{if(err)returnconsole.log('The API returned an error: '+err);console.log(res.data);});}

Node.jsというテキストをHogeに書き換えます。

実行

こんな感じで書き換えができました。

所感

今回はreplaceAllTextというリクエストをしましたが、他のリクエスト方法もあるので試していきたいです。

/v1/presentations/request

bullで管理するNodeJSの分散job

$
0
0

bullとは

NodeJSで分散ジョブとメッセージを処理するためのキューパッケージです。redisをベースに動作します。
kueの後継的なライブラリです。

確認環境

express-generator にてプロジェクト作成

ここでは、play_node_bullというプロジェクトで作成します。

npx express-generator play_node_bull
cd play_code_bull
npm install

bullのインストール

npm install bull

app.js の編集

app.jsに以下の行を追記します。

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

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

var app = express();

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

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

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

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

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

jobs.js の追加

1ブロック目

routesフォルダに、jobs.jsを追加します。
routerとBullを準備します。

routes/jobs.js
constexpress=require('express');constrouter=express.Router();constBull=require("bull")

2ブロック目

重い処理のスタブを準備します。

routes/jobs.js
// 重い処理のスタブconstheavyJob=async()=>{returnnewPromise((resolve)=>{setInterval(()=>{resolve();},10000);})}

3ブロック目

bullオブジェクトと処理名を定義します。
処理名は、キューの送信と受信を紐づけるキーとなります。

routes/jobs.js
constqueue=newBull('bulltest',{redis:{port:6379,host:'127.0.0.1'}});constprocessor='processor_name';// 処理名

4ブロック目

エンキューするエンドポイントを定義します。
エンキューするqueue.add()には、データオブジェクトを処理名を指定します。

routes/jobs.js
router.post('/',(req,res,next)=>{constdata={greetingTime:(newDate()).toString()}// jobのデータ(object)queue.add(processor,data);res.send('ok');});module.exports=router;

5ブロック目

デキューするハンドラを記述します。
queue.process()に処理名と並行処理数、ハンドラ関数を指定します。

routes/jobs.js
constconcurrency=2;// 並行処理数queue.process(processor,concurrency,async(job,done)=>{try{awaitheavyJob();done();}catch(e){done(e);}});

実行

アプリケーションを実行します。デフォルトでは3000番ポートで待機します。

npm start

APIの呼び出し

curlコマンドで、作成したjobsAPIを呼び出します。

curl -X POST 'http://127.0.0.1:3000/jobs'

jobのオプション

interfaceJobOpts{priority:number,// オプションの優先度。1(最高の優先度)~MAX_INT(最低の優先度)を指定する。パフォーマンスにわずかな影響を与えるため、必要でない限り使用しない。delay:number,// このジョブを処理できるようになるまで待機するミリ秒数。正確に遅延するためには、サーバーとクライアントの時刻を同期する必要がある。attempts:number,// ジョブが完了するまでのリトライ回数repeat:RepeatOpts,// cron仕様に従ってジョブを繰り返す。backoff:number|BackoffOpts,// ジョブが失敗した場合の自動再試行設定。遅延時間を設定するか、{type: 'fixed' or 'exponential')を指定。lifo:boolean,// 後入れ先出しにする (default false)timeout:number,// タイムアウトエラーでジョブが失敗するまでのミリ秒数jobId:number|string,// ジョブIDを上書きする。既に存在するIDを持つジョブを追加しようとしても、追加されない。removeOnComplete:boolean|number,// trueの場合、正常に完了したときにジョブを削除します。falseの場合は`completed`セットに保持される。removeOnFail:boolean|number,// trueの場合、処理に失敗したときにジョブを削除します。falseの場合は`faild`セットに保持される。stackTraceLimit:number,// スタックトレースに記録されるスタックトレース行の量を制限。}

【動画付き】 draw.io 使い方まとめ 〜エンジニアでなくても使えるTips集〜

$
0
0

title

draw.ioはブラウザを使用してフローチャート、プロセス図、組織図、UML 図、ER モデル、ネットワーク図などを作成できる優れたツールです。作成した図は xml ファイルとして保存でき、GitHub との連携もシームレスに行われます。3 年ほど愛用しているツールですが、隠された使い方がたくさんあります。すぐに忘れてしまうので取りまとめておきます。

「こんな使い方あるよ!オススメだよ!!」という方はぜひ編集リクエストをいただければ追記していく予定です 😊

※ 主に参照している文献は以下、公式ブログは非常に分かりやすいのでオススメです。

ショートカット

ショートカット集です。机の上に置いて覚えましょう。

shortcut

Line / 線

まずは最も頻繁に使う Line(線)の使い方からご紹介します。

矢印をまっすぐに揃える

ちまちまと矢印の線をドラッグして微調整していませんか? 右メニューから簡単に直線に揃えることができますよ。

adjastarrow.gif

線にラベルを挿入する

線上のどこでもいいのでダブルクリックをするとラベルを挿入できます。テキストボックスを作成してから矢印を繋げるよりもずっと早いですね。

arrowlabel.gif

Shape をコピーしつつ、矢印もつける(Alt+Shift+十字キー)

Shape をコピーしてから、矢印を引っ張って繋げていませんか?  Alt+Shift を押しながら十字キーを押してみましょう。これなら 10 倍は効率がいいですね。

copyobjecttoline.gif

Shape をコピーしつつ、矢印もつける(ctrl+drag)

Ctrl キーを押しながら、Shape から引っ張れる青い線を引いてみましょう。あら不思議、Shape が複製されました。Ctrl キーを押しながら青い線をダブルクリックすると線は引かれずに Shape だけが複製されます。

ctrlclick.gif

Shape の接続

Shape 間から伸ばす線が勝手に他の Shape にくっついてしまって困ることはありませんか? そんな時は Ctrl+Shift を押しながら線を引っ張ってみましょう。ちょっとだけ幸せになれますね。その他、Shift キーだけを押しながら線を引っ張ると接続点を無視することもできます。

connectpoint.gif

曲線

デフォルト設定だと直線ですね。曲線ももちろん使えますよ。

curve.gif

Shape / 図形

この章では Shape に関連した Tips をご紹介します。Shape とは1つの図だと思ってください。リソースやオブジェクトなどと呼ばれたりしますが、公式の呼び方は Shape のようです。

画像の切り抜き

画像を丸や四角い形に切り抜きたい場合があります。画像のワンちゃんのかわいさが際立ちますね 🐶

ssr.gif

要素の置き換え Shift+click

Shape を一度置いてみたものはいいものの、もっと適切な形があるので変えたいという時に便利です。Shift を押しながら左メニューの中から Shape をクリックしてみましょう。簡単に置き換えられます。

shiftclick.gif

その他の図形

もっと他の図形も見てみたい、使いたいという方はこちらから。AWS リソースのアイコンなんかもありますよ。

moreobject.gif

系統図

Shape 間の関係を維持したまま、系統図を自動整形できます。とりあえず線を繋げまくったけど、関係性がわからなくなってきた、ドラッグで1つずつ移動させるのはしんどいなぁという時に便利です。

organic.gif

プレースホルダ(文字埋め込み)

ほとんど知られていませんが、プレースホルダが使えます。特定の条件にあわせて文字列を動的に組み換えることができます。IP アドレスが振られた構成図や、小さな Shape を寄せ集めた図を書く時に重宝しそうですね。公式ブログ ~How to work with placeholders?~にて詳しく解説されています。

placeholder

placeholder.gif

比率を維持したままリサイズ Cmd(Mac) または Ctrl(Windows)

右メニューから比率を固定することもできますが、Cmd キーを押しながらドラッグするだけでリサイズできるショートカットは便利です。

resize.gif

スクラッチパッドにグループを追加する

左メニューのスクラッチバッドに図を追加できます。

scratchboard.gif

Shape をコミカルにする

図の形を漫画風にできます。

importfromspreadsheet.gif

フローチャート

様々なフローチャートの書き方を覚えておくと、適切なフォーマットで情報を伝える力が身につきます。以下のブログを読んでおけばよいでしょう。テンプレートも用意されているので、すぐにそれなりのものが出来上がります。

公式ブログ ~Creating different types of flowcharts with draw.io
~

flowachart

Settings / その他操作、設定など

意外と知られていない設定や操作が存在します。この章ではそんな隠れた Tips をご紹介します。

ズームイン/アウト(Alt or Option) + Mouse

Alt または Option を押しながらカーソルを動かしてみましょう。拡大縮小ができます。ちなみに Ctrl+プラスまたはマイナスでサイズを変更している方は Ctrl+0 を押してみましょう。きっと幸せになれるはずです。

zoom.gif

ダークモード

最近流行りのダークモード、もちろんできます。Extras > Themeから設定しましょう。

darkmord.gif

その他のフォントを使用する

標準のフォント以外にもシステムフォント、Google フォント、Web フォントが使用できます。

font.gif

設定の共有

draw.io の設定を Json 形式で保存して共有できます。公式ブログ ~How to configure draw.io?~を参照しましょう。

config.gif

PlantUML

PlantUML をインポートして描画できます。PlantUML の書き方はこちらの記事によくまとまっていました。

plantuml.gif

ルーラー

細かい微調整にはルーラーが欠かせません。

ruler.gif

リンクを作成

作成した図は公開できます。リンクを作成すると画像ファイルの形式で閲覧できます。

link.gif

埋め込み HTML の作成

作成した図をブログなどに挿入する場合は埋め込み HTML がオススメです。

https://about.draw.io/publish-link-and-embed-html/

CSV から読み込む

Google のスプレッドシートなどで CSV ファイルを管理しておいて、draw.io に読み込ませることができます。
こちらのツールを使用してスプレッドシートと draw.io を連携しましょう。スプレッドシートを更新すると draw.io の図も更新されます。公式ブログ ~Automatically create draw.io diagrams from CSV files~に詳しい説明があります。

importfromspreadsheet.gif

プラグイン / 拡張

draw.io には様々なプラグインが用意されています。この章では、その一部をご紹介します。

現在使用できるプラグインの一覧

※ ちなみにプラグインは JavaScript で書かれていました。

Line をアニメーション化

Flow プラグインを使用することで、Line にアニメーションを付与できます。詳細は公式ブログ ~Connector styles and animations in draw.io~を参照してください。

anime.gif

SQL を読み込んで ERD を作成する

SQL プラグインを使用することで CREATE 文から ERD を生成します。

sql.gif

さいごに

ざっと draw.io さん公式から情報を集めてみました。
他にも良い使い方を知っている方は、ぜひ教えてください 🙏

nodebrewからnodenv with anyenvに乗り換える

$
0
0

概要

ディレクトリごとにnodeのバージョンを変えたい状況になって、
nodebrewだと難しそうだからnodenvに乗り換えるついでにanyenv入れちゃおうって話。

nodebrewのアンインストール

  • brew uninstall nodebrew
  • .nodebrewフォルダを削除する。
  • PATHからnodebrewを削除する。

参考

Nodebrew本体を削除する方法 - Qiita

anyenvのインストール

# homebrew
brew install anyenv

# .bash_profile
anyenv init
echo 'eval "$(anyenv init -)"' >> ~/.bash_profile
exec $SHELL -l

# initialize manifest
anyenv install --init

参考

nodeのインストール

# install nodenv
anyenv install nodenv

# install node
nodenv install [version]

エラー発生

nodenv: default-packages file not foundと出た。
下記コマンドを実行して再度インストールで解決。

echo yarn >> $NODENV_ROOT/default-packages

参考

nodenv install時にdefault-packages file not foundが出た時のメモ | 7me

利用バージョンの設定

localでバージョンを指定するとそのディレクトリより下の階層では設定したバージョンが自動的に利用される。尊い。

# global
nodenv global [version]

# local
nodenv local [version]

おわりに

ディレクトリごとにいちいちnodeのバージョンを切り替える必要がなくなって嬉しい!

Vue.js勉強会に参加してみた

$
0
0

はじめに

WantedlyでVue.jsの勉強会を見つけたので、参加してきました。
Vue.jsは前々から勉強しようと思っていたのですが、後回しになっていたので、これを機会に本腰を入れよう!

環境構築

今回、事前に環境構築をしてきてとお願いされたのは以下のもの

  • Visual Studio Code
  • 拡張機能でVetur(Vue.js用の拡張機能)もインストール
  • githubアカウント
  • Node.js
    • versionは10以上
  • git
    • versionは2.20以上

このうち、(僕のパソコンはWindowsなこともあり)Node.jsとgitのインストールを行いました。

nodistのインストール

WindowsでNode.jsのバージョンを管理するには、nodistを使うらしい。
インターン先ではnodebrewを使っていたので、「あれ?」と思ったが、nodebrewは基本的にmac環境で使っているそうです。

以下のサイトの手順通りに進めました。
[Node.js] Node.js の導入(Windows編)

1. 公式ページにアクセス

2. インストーラーをダウンロード

実行時のversionは0.9.1

3. インストーラーに従ってインストール

ここでインストール終了時に「PATH not updated, original length 1183 > 1024」というエラーが。
取り敢えず、「OK」を押すと、何事もなかったかのように「Install Finished」と出る。

しかしコマンドプロンプトを開いて、$ nodist -vと打っても

'nodist'は、内部ファイルまたは~

というエラーが返ってくる。これはPATHが通ってないときに出てくるやつですね。
まぁ、さっきの時点でそんな気はしましたが、、、

4. nodistのPATHを通す

こちらの方の記事を参考にしたら、うまくいきました!
node.js インストール備忘録(windows7)

5. nodistのversion確認

$ nodist -v
0.9.1

無事インストールできました!

Node.jsのインストール

続いてNode.jsをインストールしていきます。

1. インストール可能なNode.jsのバージョンを確認

$ nodist dist

めちゃくちゃ出てきてビックリ(笑)。

2. 任意のバージョンをインストール

「偶数バージョンの方がいい」みたいなことを聞いたことがあったので、調べてみると以下のような記事が。
node.jsのバージョンごとの違い

今回は安全に使いたいので、偶数バージョンの最新版(12.14.1)をインストール。

$ nodist + v12.14.1

3. インストールされている Node.js を確認

$ nodist
  (x64)
> 12.14.1

4. Node.js のバージョンを切り替える

$ nodist v12.14.1

5. 現在使用可能なNode.jsのバージョンを確認

$ node -v
v12.14.1

6. npmのアップデート

npmはNode.jsのパッケージを管理するもの。

$ npm -v
$ npm install npm -g

以上でNode.jsのインストールは終了!

Gitのインストール

今までGitを使いたいときはSourseTreeを使っていたのですが、インターン先ではGitをコマンドで打つので、自分のパソコンにも入れたいと思いつつ、後回しにしていた。。。
今回やっとGitをインストール!

以下の記事を参考にしました。
Git インストール for Windows

特に問題はなく成功

$ git --version
git version 2.25.0.windows.1

Vueとは

  • オープンソース
  • WebアプリケーションのUIを構築
  • SPAを高速に構築することが可能
  • 学習コスト低い

Gitを使ってみる

ここは今回のメインではないので、箇条書きで進めます

  1. GitHubでリポジトリを作る
  2. リモートに適当なディレクトリを作る
  3. README.mdを作成し、# Hello, GitHub!と入力して保存
  4. git initをして、gitの初期化
  5. git remote add origin git@github.com:xxxx/yyyy.git
    • git remote -vでリモートを確認できる
  6. git add .をして、先ほど編集した差分をstage (commit待ちのファイル群) に乗せる
  7. git statusで、stageに乗ったか確認 (緑色になってたらstaged)
  8. git commit -m "first commit"で、差分をコミット
  9. git push origin masterで、作ったコミットをoriginリモートのmasterブランチ (デフォルトのブランチ) にpush

Vue.jsについて

Vue.jsはドキュメントがとても優秀、わからなくなったらここを読めばほとんど全て理解できる
- https://jp.vuejs.org/v2/guide/index.html
- そのドキュメントの日本語がよく分からず後回しにした人間がここに1人

Vueで書いてみよう

今回はscriptタグで、CDNからVueを読みます。
Todoリストを作成して、練習していきます。

sample.html
<!DOCTYPE html><htmllang="ja"><head><metacharset="UTF-8"><metaname="viewport"content="width=device-width, initial-scale=1.0"><metahttp-equiv="X-UA-Compatible"content="ie=edge"><title>Step1 ToDo Application</title><script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script></head></html>

Vueインスタンスの作成

ではbody要素を足していきます。
まずはVueのインスタンスを作ります。更に、elプロパティでappというidの要素を指定すると、それ以下をVueが管理してくれます。

sample.html
<body><divid="app"><!-- ここをvueが管理 --></div><script>constapp=newVue({el:'#app'// "id=app"を指定})</script></body>

データを持つ

動的に変化するデータを扱うには、data プロパティに宣言します。
そして{{}}内に囲むことで、そのデータを表示できます。

sample.html
<body><divid="app">
    {{ hoge }}
  </div><script>constapp=newVue({el:'#app',data:{hoge:"sample"}})</script></body>

ここでVueの特徴の一つreactive性が出てきます。
これは、変更されたdataが直ちに再描画されるというものです。

フォームデータの束縛(バインディング)

v-model="~"の形で記述された場合、~の変数にデータを格納します。

sample.html
<body><divid="app"><label>ToDoの内容<inputtype="text"v-model="todoText"></label>
    {{ todoText }}
  </div><script>constapp=newVue({el:'#app',data:{todoText:"sample"}})</script></body>

こうすると、inputタグのデータがtodoTextという変数と結びつくため、フォーム内に初めから"sample"という文字列が表示されます。

リストの表示

リストを表示するにはv-forを使います。

sample.html
<body><divid="app"><label>ToDoの内容<inputtype="text"v-model="todoText"></label><ul><liv-for="todo in todoList">{{ todo }}</li></ul></div><script>constapp=newVue({el:'#app',data:{todoText:"sample",todoList:['掃除','洗濯','料理']}})</script></body>

算出プロパティ

テンプレートで表示するのに計算が必要な場合は算出プロパティcomputedを使用します。

sample.html
<body><divid="app"><label>ToDoの内容<inputtype="text"v-model="todoText"></label><p>あと{{ todoCount }}個のtodo</p><ul><liv-for="todo in todoList">{{ todo }}</li></ul></div><script>constapp=newVue({el:'#app',data:{todoText:"sample",todoList:['掃除','洗濯','料理']},computed:{todoCount(){returnthis.todoList.length}}})</script></body>

メソッド

DOMイベントに対して何かのアクションをするには、v-onディレクティブを使用します。
これにより、呼び出したいメソッドを呼ぶことができます。

sample.html
<body><divid="app"><label>ToDoの内容<inputtype="text"v-model="todoText"></label><buttonv-on:click="register">登録</button><p>あと{{ todoCount }}個のtodo</p><ul><liv-for="todo in todoList">{{ todo }}</li></ul></div><script>constapp=newVue({el:'#app',data:{todoText:"",todoList:[]},computed:{todoCount(){returnthis.todoList.length}},methods:{register:function(){this.todoList.push(this.todoText);this.todoText="";}}})</script></body>

より本格的な開発のために

コンポーネントシステム

コンポーネントとは、ざっくり言うと「部品」のこと。
ページをコンポーネント化して、各部品として管理することで、再利用性や保守性が高まる。

Vueでは.vueファイルとして保存します。
更にVueの特徴は、.vueファイル一枚にtemplate,style,scriptを書けることです。(単一ファイルコンポーネント)
このおかげで、ファイル管理が楽になります。

Vuex

状態管理のためのライブラリ(+パターン)。
共通の状態やデータを複数のコンポーネントで管理する場合に活用できる。

参考:ざっくり理解、Vuexって何?VuexとAPIの関係を図解してみた

Nuxt.jsについて

Nuxt.jsとは

Nuxtは、モダンな web アプリケーションを作成する Vue.js に基づいたプログレッシブフレームワークです。Vue.js 公式ライブラリ(vue、vue-router や vuex)および強力な開発ツール(webpack、Babel や PostCSS)に基づいています。 Nuxt の目標は、優れた開発者エクスペリエンスを念頭に置き、Web 開発を強力かつ高性能にすることです。

先ほど説明したものや、cssフレームワーク、SSRなどもろもろのベストプラクティスを適用したプロジェクトを一瞬で構築可能。
本格的なWebアプリケーション作成ができる。

Nuxt.jsをセットアップする

npxのインストール

npxを使うと、ローカルにインストールしたnpmパッケージを、npxコマンドだけで実行できるようになります。
これにより、面倒なコマンドを打たなくて済みます。
言わば"便利コマンド集"のようなもの。

普通はnpmが入れば自動的にインストールされる。
しかしnodist経由だと、npxが自動でインストールされないらしい...

そこで個別にインストールします。

$ npm install -g npx

プロジェクト作成

npm create-nuxt-app <プロジェクト名>でNuxt.jsのプロジェクトを作成可能です。
ただし、プロジェクトを作る場所はカレントディレクトリではなく、キャッシュに残っている場所になってしまいます。

しかもその場所のパスにスペースが入っていると、

operation not permitted, mkdir '~'

のエラーが出てプロジェクトを作れない。。。

そこで、エラーが出る場合は以下のコマンドでキャッシュの場所を変えます。

$ npm config set cache <プロジェクトを作りたいディレクトリのパス> --global

その上で改めて

$ npm create-nuxt-app <プロジェクト名>

を実行します。

プロジェクト作成中、UIフレームワークなど、設定を聞かれるので、適宜選択してください。

プロジェクトのディレクトリ構成

  • layouts
    • 全ページで使用されるテンプレートファイルを規定する。ページの外観を変更するために使用される(例えばサイドバーなど)。
    • page の layoutプロパティで変更可能
  • pages
    • アプリケーションのビュー及びルーティングファイルを入れる。
    • Nuxt.js フレームワークはこのディレクトリ内のすべての .vue ファイルを読み込み、Vue Routerによって自動的にルーティングされる
      • pages/index.vue/
      • pages/hoge/fuga.vue/hoge/fuga
  • components
    • pages から利用するコンポーネントを入れておく
    • components から components も使ったりする
  • stores
    • Vuex ストアのファイルを入れる
    • デフォルトではVuexストアは無効。このディレクトリに index.js ファイルを作成するとストアが有効になる。

Nuxt.jsでカレンダーアプリを作ってみる

サンプルとしてカレンダーアプリ(と言えるかすら微妙なもの)を作ってみました。

index.vueの作成

まずはindex.vueです。pagesディレクトリ内に作ります。
コンポーネントとして作ったCalender.vueをscript内で読み込みます。
これにより、Calender.vueの内容を、<calender />タグの部分にはめ込むことができます。

index.vue
<template><divclass="container"><calendar/></div></template><script>importCalendarfrom'~/components/Calendar'exportdefault{components:{Calendar,}}</script><style></style>

コンポーネントの作成(Calendar.vueとCalendarDay.vue)

Calendar.vueにカレンダーの全体像、CalendarDay.vueにカレンダーの一日の部分を入れます。

Calendar.vue
<template><divclass="calendar"><calendar-dayv-for="day in dayList":key="day":day="day"/></div></template><script>importCalendarDayfrom'~/components/CalendarDay'exportdefault{components:{CalendarDay,},data(){return{dayList:[]}},created(){for(leti=1;i<=31;i++){this.dayList.push(i)}}}</script><style scoped>.calendar{display:flex;flex-direction:row;flex-wrap:wrap;}</style>
CalendarDay.vue
<template><divclass="calendar-day">
    {{ day }}
    <inputtype="text"v-model="text"><button@click="addSchedule({ day, text })">追加</button><ul><liv-for="schedule in scheduleList":key="schedule.id">{{ schedule.text }}</li></ul></div></template><style scoped>.calendar-day{border:solid1pxblack;width:150px;height:150px;}</style><script>import{mapGetters,mapMutations}from'vuex'exportdefault{data(){return{text:'',}},props:{day:0,},computed:{...mapGetters({getScheduleListByDay:'schedule/getScheduleListByDay',}),scheduleList(){returnthis.getScheduleListByDay(this.day)}},methods:{...mapMutations({addSchedule:'schedule/addSchedule'}),}}</script>

storeの作成(schedule.js)

storeディレクトリにschedule.jsを作成し、予定データを保存・加工します。

schedule.js
exportconststate=()=>({scheduleList:[],// Array<{ id: Number, day: Number, text: String }>lastScheduleId:0,})exportconstgetters={getScheduleListByDay:(state)=>(day)=>{returnstate.scheduleList.filter(schedule=>schedule.day===day)},}exportconstmutations={addSchedule(state,{day,text}){state.scheduleList.push({id:++state.lastScheduleId,day,text,})},}

おわりに

Vueを使うと、コンポーネントとしての管理がしやすいなぁと思いました。

ただ、アロー関数などJSの記法をしっかり覚えないと...


Node12系 AWS LambdaでHTMLをPDFに変換しようとしたらいろいろハマった

$
0
0

Node8系でwkhtmktopdfを使ってHTMLをPDFに変換するLambdaを使っていたのだが、Node8系で動いていた。
Node8系がサポートされなくなるということで、12系にそのままあげたら動かなくなってしまったのでNode12系でHTMLをPDFに変換するLambdaを作り直す必要が出てきた。

HTMLをPDFに変換するLambdaについては結構多くの記事が見つかったが、なかなか上手くいかなかった。

やりたかったこと

Lambdaで日本語を含むHTMLをPDFに変換し、S3に保存する

試したが上手くいかなかった方法

  • wkhtmlpdf
    • 自分が見つけられなかっただけかもしれないが、Node12系でも問題なく動くソースを見つけられなかった
  • html-pdf
    • phantomjsの128エラーでちっとも動かなかった
  • puppeteer
    • 動きそうな気配はあったが、node_modulesのサイズが大きすぎてLambdaの最大ソースサイズをオーバーしてしまった

上手く行った方法

使用モジュール

chrome-aws-lambdaを使った。
puppeteer-coreもnpm installする必要があるが、puppeteerだと大きすぎるから必要なcoreだけ使ってるっぽい。

注意点

  • Lambdaのメモリ設定を512MBにする必要があった
    • 最初256MBにしていて、browserがlaunchできなかったいうようなエラーが出て、chrome-aws-lambdaもダメかあと思っていたところBUG報告で512MBにしたらできたで、って書いてあった
  • .fontsをちゃんとzipファイルに含める
    • Lambda環境に日本語フォントはない。自分で.fontsとして読み込ませる必要があるが、日本語が反映されへんな〜と思ったら.fontsをzipファイルに含むのが漏れていたというオチだった

コード

構造

┣ pdfGenerator.js
┣ package.json
┣ package-lock.json
┣ .fonts
  ┣ ipaexg.ttf
  ┣ ipaexm.ttf

コード

pdfGenerator.js
/* Lambda環境でも日本語フォントを使えるようにするには.fontsを読み込ませるためにHOMEを設定する必要ある */process.env['HOME']="/var/task";process.env['PATH']=process.env['PATH']+':'+process.env['LAMBDA_TASK_ROOT'];constAWS=require("aws-sdk");constS3=newAWS.S3({signatureVersion:"v4"});constchromium=require("chrome-aws-lambda");exports.handler=asyncfunction(event,context,callback){try{if(!event.html){callback("unable to get html");return;}constfileName=event.filename,tmpFileName=`/tmp/${Math.random().toString(36).slice(2)}.pdf`,bucket=event.bucket,pageSize=event.pageSize||"A4",html=event.html;constexecutablePath=awaitchromium.executablePath,browser=awaitchromium.puppeteer.launch({args:chromium.args,defaultViewport:chromium.defaultViewport,executablePath,headless:chromium.headless,ignoreHTTPSErrors:true});constpage=awaitbrowser.newPage();awaitpage.setContent(html);constpdf=awaitpage.pdf({path:tmpFileName,format:pageSize});browser.close();S3.putObject({Bucket:bucket,Key:fileName,Body:pdf,ContentType:"application/pdf"},(error)=>{callback(error);});}catch(e){callback(e);}};

参考リンク

https://github.com/alixaxel/chrome-aws-lambda/issues/82

Knexあれこれ(雑多メモ)

$
0
0

はじめに(次の見出しまで飛ばしてOK)

最近仕事に対する楽しみ方を意識するようになり、今のつまらない仕事が続くのも嫌だったので、6月で辞めますと退職連絡を済ませてきた。
コーディングしてる最中はプライベートでも仕事でも楽しいので、コードをびっしり書くような会社に転職したいと思ったが、個人での活動実績はない。
とりあえず就活に繋がる作品を何か作ってみようと思い立ちこれ1本で大体何とか済ませちゃうNode.jsの勉強を1週間前に始めた。
が、DB処理があまり好きになれなかった。

適当に買った参考書に書いてたDB処理は以下のようなもの

sql.js
connection.query('SELECT * FROM hoge ',function(error,results,fields){//取得結果に対する処理 });

新卒配属された闇プロジェクトのシステム構成がこれに近い形式で実装されていたことが大体の原因ではあるが。

便利なnpmパッケージ Knex

PHP/Laravelあたりに言語/フレームワークを変更しようかなとこっそり考えつつ、何かいいnpmパッケージはないものかと探してみた。
あった(Knex公式)
記事を書いてくれている方もいた

個人的に良いなと思った点

  • チェーン形式で処理を書ける
  • マイグレーションにも対応してる

軽く記事を流し読みるするだけでも結構便利に思えたのでリファレンス読みつつ色々試してみた。
で、ちょっと一々リファレンスを見に行くのもあれなのでちょっとメモを残そうと思いこの記事を書き始めた。正直公式リファレンスのは読みづらかった
割と参考記事とかぶってる点が多いので、多分knex回りを調べてこの記事を開いてくれた方は↑の参考記事を見ることをお勧めします。

マイグレーションのあれこれメモ(本題)

実行前準備

初期構築 マイグレーションをするためのアレコレの準備

$ knex init 

マイグレーション実行に当たる必要となる設定

module.exports={// 開発環境の設定(デフォルトで参照される設定 NODE_ENVを書き換えればデフォルトではなくなる)development:{// データベースの種類client:'mysql',// DB接続設定connection:{host:'127.0.0.1',user:'root',password:'',database:'node_app'},// コネクションプールの設定pool:{min:2,max:10},// マイグレーション設定migrations:{// マイグレーションファイルの配置先(knexfile.jsからの相対)directory:'./db/migrations',// マイグレーションを管理するテーブル名 (マイグレーションの実行と同時にDBに作成される)tableName:'knex_migrations'}},// 開発環境とは異なる環境のマイグレーション実行設定定義 (本番環境,検証環境の差別化等)staging:{...},production:{...},};

マイグレーションファイルの作成

<タイムスタンプ>_<ファイル名>.jsというファイルが、migrations.directoryで設定したパス配下に配置される

$ knex migrate:make <ファイル名>

マイグレーションファイルの中身の設定

細かく解説すると助長になるので、コメント参照

20200208161057_items.js
// 後述する実行コマンドで呼び出されるメソッドexports.up=function(knex,Promise){// connection.databaseで設定しているスキーマに引数で渡したテーブルがあるかチェックreturnknex.schema.hasTable('テーブル名')// hasTableのチェーンメソッド 判定結果を引数existsに渡して無名関数をコールバック.then(function(exists){if(!exists){// 接続先のスキーマに指定した名前でテーブルを作成するreturnknex.schema.createTable('テーブル名',// 作成したテーブルにカラムを作成するfunction(table){// テーブルの要素設定 別途記載table.increments('id').primary();table.string('name',100);table.integer('price');});}else{returnnewError("The table already exists. 2");}});};// 後述する切り戻しコマンドで呼び出されるメソッドexports.down=function(knex,Promise){returnknex.schema.hasTable('items').then(function(exists){if(exists){// 指定したテーブルを削除するreturnknex.schema.dropTable('items');}});};

接続先データベース/スキーマに対して実行するメソッド各種 (使いそうな奴だけ)

function(knex,Promise){// テーブル作成メソッド コールバック内でカラム設定を忘れずにknex.schema.createTable(tableName,callback)// 指定したテーブル名を変更するknex.schema.renameTable(from,to)// 指定したテーブルを削除する           knex.schema.dropTable(tableName)// 指定したテーブルが存在しないか確認する 上例のように.then()でチェーンするとスマートknex.schema.hasTable(tableName)// 指定したテーブルが存在すれば、そのテーブルを削除する. has,dropをチェーンしなくて済むknex.schema.dropTableIfExists(tableName)// 指定したテーブルに関する変更処理を実行するknex.schema.table(tableName,callback)};

接続先のデータベース/スキーマが持つテーブルに対して実行するメソッド各種

createTableや、table関数のコールバック内で利用する

knex.schema.createTable('posts',function(table){// 指定したカラムを削除するtable.dropColumn(name)// 指定した複数のカラムを削除table.dropColumns(*columns)// 指定したカラム名を変更table.renameColumn(from,to)// オートインクリメント形式のカラムを追加table.increments(name)// 文字列型のカラムを追加 オプション引数で長さも指定できるtable.string(name,[length])// 数値型のカラムを追加table.integer(name)});

主キーとか外部キーとかindexとかの使い方

カラム設定の関数にチェーンする

knex.schema.createTable('posts',function(table){// 主キーの設定table.increments(name).primary()// knexインスタンスがMySQLとPostgreSQLの場合のみ使えるtable.index(columns,[indexName],[indexType])// 外部キーの設定 table.foreign(カラム名).references('参照テーブル.カラム名')table.string(name)table.foreign(name).references(table.name)});

実行手順

未実行のマイグレーションファイルのupメソッドを、タイムスタンプの古い順に全ファイル実行

$ knex migrate:latest

envオプション: 実行対象の環境を指定してマイグレーションを実行する

$ knex migrate:latest --env production

直前に実行した全てのマイグレーションファイルのdownメソッドを、タイムスタンプの新しい順に実行する

Latestでバッチ実行したなら、すべてのファイルをタイムスタンプの新しい順に実行する。
Upで個別実行したなら、そのマイグレーションファイルのみを実行する

$ knex migrate:rollback

allオプション:過去に実行した全てのマイグレーションファイルのdownメソッドを実行する

$ knex migrate:rollback --all

タイムスタンプが最も古い未実行のマイグレーションファイルのUpメソッドを実行する

$ knex migrate:up 

マイグレーションファイルを指定してそのUpメソッドを実行する

$ knex migrate:up <ファイル名>.js

タイムスタンプが最も新しい実行済のマイグレーションファイルのdownメソッドを実行する

$ knex migrate:down

マイグレーションファイルを指定してそのdownメソッドを実行する

$ knex migrate:down <ファイル名>.js

マイグレーションファイルを一覧表示する

実行済みのものと未実行のものに分割してリスト表示してくれる

$ knex migrate:list
Using environment: development
Found 1 Completed Migration file/files.
20200208145923_items.js 
Found 1 Pending Migration file/files.
20200208161057_items.js 

作成日の異なるマイグレーションファイルがある場合のmigrate:latestの挙動

タイムスタンプの古い方から実行される

/project
|--migration
|  |--20200208161057_items.js
|  |--20200208161542_items.js
20200208161057_items.js
exports.up=function(knex,Promise){console.log('items oldest');}
20200208161542_items.js
exports.up=function(knex,Promise){console.log('items lastest');}
実行結果
$ knex migrate:latest
Using environment: development
items oldest
items latest

作成日の異なるマイグレーションファイルがある場合のmigrate:rollbackの挙動

タイムスタンプの新しい方から実行される

/project
|--migration
|  |--20200208161057_items.js
|  |--20200208161542_items.js
20200208161057_items.js
exports.up=function(knex,Promise){console.log('items oldest');}
20200208161542_items.js
exports.up=function(knex,Promise){console.log('items lastest');}
実行結果
$ knex migrate:rollback
Using environment: development
items latest
items oldest

オプション引数類は一例で上げてるだけになります。詳細は公式へ

migrate:latestの後にもう1回migrate:latestをした場合のmigrate:rollbackの挙動

上述した通り、migrate:rollback は直前の1回の実行を戻す
2回に分けた場合は2回実行するか、--allオプション を使う

ぼやき

結構前からあるパッケージなのにknexの日本語記事少ないってことは、
割とNode.jsを書いてる人からは不人気なのだろうか。

Nodejs(TypeScript)とcacとinquirer.jsでサクッとCLIを作成する

$
0
0

はじめに

NodejsでCLIツールを作成するときに良さそうなパッケージを見つけて、実際に使ってみたら良かったので紹介していきます。

使ったパッケージ

cac

シンプルでパワフルなコマンドラインインターフェイスを作るためのフレームワーク

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

inquirer.js

対話型のインターフェースを組み込むのに便利

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

インストール

$npm i cac inquirer
$npm i --save-dev @types/inquirer

サンプル

cacを使ってCLIの骨組みを書いていきます。
下記の例では cli hello hogeみたいなコマンドを入力されたときに処理が実行されるようにしています。

cli.ts
importindexfrom'../src/index'importcacfrom'cac'constcli=cac()cli.command('hello [name]','Enter your name').action(()=>{index()})cli.help()cli.parse()

inquirerを使って対話型のインタフェースを定義していきます。
下記の例では、入力とリストとチェックボックスの対話型インターフェースを定義しています。

index.ts
import{prompt,Separator,QuestionCollection}from'inquirer'exportdefaultasync():Promise<void>=>{constname=process.argv[3]if(!name){console.error('Please pass one argument!!')process.exit(1)}constmsg=`
    Hello!! ${name} !!!
  `console.log(msg)// InputconstinputQuestions:QuestionCollection=[{type:'input',message:"What's your name",name:'name'}]awaitprompt(inputQuestions).then((answers:any)=>{console.log(JSON.stringify(answers,null,''))})// ListconstlistQuestions:QuestionCollection=[{type:'list',name:'color',message:'What do you like color?',choices:['black','red',{name:'orange',disabled:'disabled'},'green']}]awaitprompt(listQuestions).then((answers:any)=>{console.log(JSON.stringify(answers,null,''))})// CheckboxconstcheckboxQuestions:QuestionCollection=[{type:'checkbox',message:'select',name:'select',choices:[newSeparator(' = Choise A = '),{name:'hoge'},{name:'fuga'},{name:'foo'}],validate:(answer:any):boolean|string=>{if(answer.length<1){return'You must choose'}returntrue}}]awaitprompt(checkboxQuestions).then((answers:any)=>{console.log(JSON.stringify(answers,null,''))})}

実行する

ビルドするのが面倒なので ts-nodeで実行してきます。

$npx ts-node bin/cli.ts hello test
    Hello!! test !!!

? What's your name is_ryo
{
  "name": "is_ryo"
}
? What do you like color? red
{
  "color": "red"
}
? select hoge, fuga, foo
{
  "select": [
    "hoge",
    "fuga",
    "foo"
  ]
}

さいごに

サクッと対話型のCLIを作ることができました。
これでCLIを作るときの雛形はできるので、自作CLIを作っていきましょう!
ではまた!!!

Windowsでgulpの環境構築。yarn編

$
0
0

yarnでgulpの環境構築

gulpの環構築の時にサラッと出来るように自分用に投稿。
完璧に初心者なのであしからず

※ちなみにyarnもgulpもnodeもインストール済みとして書きます。

環境

・yarn
1.21.1
・gulp
CLI version: 2.2.0
Local version: 4.0.2 `
・node(楽だからnodistで管理したほうがいいかも)
v11.13.0

手順

test(任意)フォルダの中に移動します

 C:\Users\(ユーザー名) > CD test
 C:\Users\(ユーザー名)\test >

ちなみにtestフォルダの中は下記になります

 test─┬─css
      ├─scss
      │  └─main.scss
      └─index.html

package.jsonを生成しないとダメなので下記を実行

 C:\Users\(ユーザー名)\test > yarn init

実行すると色々聞かれるけどエンター連打。
そうするとtest直下にpackage.jsonが生成される。

 test─┬─css
      ├─scss
      │  └─main.scss
      ├─package.json
      └─index.html

macはこの時にgulpのローカルインストールをする

 C:\Users\(ユーザー名)\test > yarn add gulp

パッケージをインストールします。なのでとりあえず下記を実行。
(後で色々変更できるけどとりあえず)

 C:\Users\(ユーザー名)\test > yarn add --dev gulp
 C:\Users\(ユーザー名)\test > yarn add --dev gulp-sass
 C:\Users\(ユーザー名)\test > yarn add --dev gulp-autoprefixer
 C:\Users\(ユーザー名)\test > yarn add --dev gulp-plumber

次にgulpfile.jsの生成。これは自分で作らないとだめなやーつ。
作ったらtestフォルダに放り投げといて下さい。
中身はざっくりこんな感じ。

const { watch, series, task, src, dest } = require('gulp');
const sass                               = require('gulp-sass');
const autoprefixer                       = require('gulp-autoprefixer');
const plumber                            = require('gulp-plumber');

const convertToCss = () =>
    src('scss/*.scss')
      .pipe(plumber())
      .pipe(sass())
      .pipe(autoprefixer())
      .pipe(dest('css/'));

const watchscss = () =>
    watch('scss/*.scss', convertToCss);

exports.default = watchscss;

.pipe(dest('css/'));の部分を
.pipe(dest('css'));にしててそれに気づかず大変でした。

最後に下記を実行して終了

 C:\Users\(ユーザー名)\test > yarn gulp

これでちゃんとコンパイルが出来ているはず。
何故かこれだけの作業なのに結構時間がかかった。

【個人メモ】Node.js LINEBOT雛形

$
0
0

プロジェクト作成とハローワールド

$mkdir linebot
$cd linebot 
$npm init -y
$npm i @line/bot-sdk express 
$npm install dotenv --save
$touch index.js $$ .env
$code .
index.js
'use strict';constexpress=require('express');constline=require('@line/bot-sdk');constPORT=process.env.PORT||3000;constdotenv=require('dotenv')dotenv.config()constconfig={channelAccessToken:process.env.CHANNEL_ACCESS_TOKEN,channelSecret:process.env.CHANNEL_SECRET};constapp=express();app.get('/',(req,res)=>res.send('Hello(GET)'));app.post('/webhook',line.middleware(config),(req,res)=>{console.log(req.body.events);if(req.body.events[0].replyToken==='00000000000000000000000000000000'&&req.body.events[1].replyToken==='ffffffffffffffffffffffffffffffff'){res.send('Hello!(POST)');console.log('疎通確認用');return;}Promise.all(req.body.events.map(handleEvent)).then((result)=>res.json(result));});constclient=newline.Client(config);functionhandleEvent(event){if(event.type!=='message'||event.message.type!=='text'){returnPromise.resolve(null);}returnclient.replyMessage(event.replyToken,{type:'text',text:event.message.text});}app.listen(PORT);console.log(`Server running at ${PORT}`);
.env.sample
CHANNEL_ACCESS_TOKEN = "xxxxxxxxxxxxxxxxxxx"
CHANNEL_SECRET = "xxxxxxxxxxxxxx"
BASE_URL = 'xxxxxxxxxxxxx.ngrok.io'
$ngrok http 3000

BASE_URLの書き換え

$node index.js

webhookURLの設定

https://xxxxxxxxxxxxxxxxxx.ngrok.io/webhook

Viewing all 8926 articles
Browse latest View live