20191127のNode.jsに関する記事は10件です。

WSLのUbuntu環境でPC内音楽データをWeb操作する(おまけでyoutube音楽とGooglehomeで伝言)

はじめに

●PC内音楽データをWEBで操作し、PCスピーカで再生できるようにする。
(Googlehomeで声で操作するのWeb操作版です。声でキーワードがうまく入力できない場合に使用してます)
【操作例】WEBから「竹内まりや」と入力する
PC音楽ボタンを押すと次の曲が再生します。(複数のファイルが検索一致した場合)
コメント 2019-11-27 230824.png
●おまけ
youtube音楽:下記の記事と同じです。
 WSLのUbuntu環境でyoutube音楽をWeb操作する(おまけでradikoとサイマルラジオ)
伝言:Googlehomeで伝言を再生します。google-home-notifierを使用。

環境

●Windows10 HOMEのPCにWSLのubuntuをインストールする。
●ubuntuでapache2,npm,node.js,youtube-dl,mplayerをインストールする
●WSLのubuntuでpulseaudioでPCのスピーカを使うようにする
●Windows側でpulseaudioサーバのインストールが必要です
 https://www.cendio.com/thinlinc/download の 「Client Bundle」のリンクからダウンロードできます

PC内音楽データを再生するプログラム(pcplay.js)

●単体で起動する方法
$node pcplay.js 検索キー(例 竹内まりや)

const exec   = require('child_process').exec;

var limit = 1;
var items;
var item;
var title;
var id;

//値取得
const value = process.argv[2];
if (!value) return
console.log(value)

// キーワードから検索し、音楽データのパスを取得する。
play_pcmusic(value);
// 音楽ファイルのパスを取得し、再生する。
function play_pcmusic(keyword) {
  command = 'cat /mnt/j/music/htdocs/music/list.txt | grep "'  + keyword + '"';
  var exec = require('child_process').exec;
  exec(command, {maxBuffer: 40000*1024}, 
function(error, stdout, stderr) {
    if (error !== null) {
      command = 'curl -X POST -d "text=指定された曲はありません" http://192.168.1.82:8080/google-home-notifier'
      exec(command, {maxBuffer: 40000*1024}, function(error, stdout, stderr) {
        if (error !== null) {
          console.log('Exec error: ' + error);
        }
      });
    } else {
      console.log(stdout);
    }
 });
  command = 'cat /mnt/j/music/htdocs/music/list.txt | grep "'  + keyword + '">/mnt/j/music/htdocs/music/playlist';
  console.log(command)
  var exec = require('child_process').exec;
  exec(command, {maxBuffer: 40000*1024}, 
function(error, stdout, stderr) {
    if (error !== null) {
      console.log('Exec error: ' + error);
    }
  });
  command = 'mplayer -shuffle -playlist /mnt/j/music/htdocs/music/playlist';
  console.log(command)
  var exec = require('child_process').exec;
  exec(command, {maxBuffer: 40000*1024}, 
function(error, stdout, stderr) {
    if (error !== null) {
      console.log('Exec error: ' + error);
    }
  });
}

index.php

<?php
if(isset($_GET['comment1'])){
$comment = $_GET['comment1'];
exec("killall mplayer");
exec("node /home/ユーザ名/ytplay.js " .$comment);
}
if(isset($_GET['comment2'])){
$comment = $_GET['comment2'];
exec("killall mplayer");
exec("node /home/ユーザ名/pcplay.js " .$comment);
}
if(isset($_GET['comment3'])){
$comment = $_GET['comment3'];
exec("killall mplayer");
exec("curl -X POST -d 'text=".$comment."' http://192.168.1.82:8080/google-home-notifier");
}
if(isset($_GET['id'])){
$id = $_GET['id'];
if($id=="stop"){
exec("killall mplayer");
}
}
?>
<html>
<head>
<meta name="viewport" content="width=device-width">
</head>
<body>
<p><a href="index.php?id=stop">stop</a></p>
<form action="index.php" method="get">
<input type="text" name=comment1>
<input type="submit" value="youtube">
</form>
<form action="index.php" method="get">
<input type="text" name=comment2>
<input type="submit" value="PC音楽">
</form>
<form action="index.php" method="get">
<input type="text" name=comment3>
<input type="submit" value="伝言">
</form>
</body>
</html>

まとめ

●お気に入りのPCスピーカでPC内の音楽データを再生します
●プログラムはラズパイでも同様に動作しますが、youtube-dlの動作が遅く、再生開始に少々時間がかかります。

参考

WSLのUbuntuでyoutube音楽をGoogleHomeで操作する
WSLのUbuntuでPC内の音楽データをGoogleHomeで操作する
WSLのUbuntu環境でyoutube音楽をWeb操作する

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

Axiosの前処理でAPIレスポンスのスネークケースをキャメルケースに変換する

はじめに

サーバサイドからのAPIレスポンスがsome_idのようにスネークケースで定義されていると、JS側で毎回キャメルケースに変換することになりますよね。

{
  some_id: 1,
  some_name: "name",
}

今回はそんな変換処理をAxiosの共通処理でまとめてみました。

レスポンスをキャメルケースに変換する

import { camelCase, snakeCase } from 'change-case';

const isObject = (target: any): boolean => Object.prototype.toString.call(target).slice(8, -1)
  .toLowerCase() === 'object';

const convertSnakeToCamel = (target: any): void => {
  if (Array.isArray(target)) {
    target.forEach((t) => convertSnakeToCamel(t));
  }
  if (isObject(target)) {
    Object.keys(target).forEach((key) => {
      if (isObject(target[key]) || Array.isArray(target[key])) {
        convertSnakeToCamel(target[key]);
      }
      // スネークケースをキャメルケースに変換する
      if (key.includes('_')) {
        target[camelCase(key)] = target[key];
        delete target[key];
      }
    });
  }
};

キャメル変換する部分はchange-case というライブラリを入れましたが、そこまで複雑ではないので自作でもよさそう

参考:javascriptでキャメルケースとスネークケースの相互変換

Axiosに組み込む

import axios, { AxiosInstance } from 'axios';

const Axios: AxiosInstance = axios.create();

Axios.interceptors.request.use((request) => {
  // リクエストの共通処理
  return request;
});

Axios.interceptors.response.use((response) => {
  if (response.data) {
    convertSnakeToCamel(response.data);
  }

  return response;
});

export default Axios;

interceptorsを使うことでできました。interceptorsを使いこなせば、他にも、リクエストに共通のヘッダを毎回差し込んだり、レスポンスでエラー処理をしたりと、何かと便利にできそうです。

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

Authorizationヘッダーを使ったPOSTリクエストのTypeScript / Node.jsサンプル各種

POSTの例を

Node.js上動くアプリから、POSTリクエストを投げるコードを書くことが最近多いので、パターンをいくつか書き残そうと思いました。モジュールによって変わる使用感やコードの量、デバッグ方法などを比較したかったため、複数の方法を残します。
なお、ここではコードサンプルを残すのみで、特に比較や分析は述べてません。

ここに記録しているコード

下の3つです。すべてTypeScriptに対応しているモジュールです。

  • Node.js標準のhttp/httpsモジュールを使った例。
  • requestモジュールを使った例.
  • gotモジュールを使った例

Nodeの標準に含まれているhttp, httpsモジュールを使った例

Node標準のhttp/httpsモジュール
import jsonDataImported from './data.json';

const ACCESS_TOKEN = `<<アクセストークンと置き換えます>>`;
const ENDPOINT_HOST = `<<エンドポイントホスト名と置き換えます 例) sample.endpoint.com>>`;
const ENDPOINT_PATH = `<<エンドポイントホスト下のパスと置き換えます 例) /sample/path`;


const postOptions: https.RequestOptions = {
  host: ENDPOINT_HOST,
  path: ENDPOINT_PATH,
  method: 'POST',
  headers: {
    'Content-Type': "application/json;charset=utf-8",
    Accept: 'application/json',
    Authrorization: ACCESS_TOKEN
  }
};

function postDataWithAccessToken(options: https.RequestOptions, postData: string): Promise<any> {
  return new Promise((resolve, reject)=>{
    const req = https.request(basePostOptions, (res)=>{
      let queue: Buffer[] = [];
      res.on('data', (chunk)=>{
        queue.push(chunk);
      });
      res.on('end', ()=>{
        const data = Buffer.concat(queue);
        resolve(data.toString());
      });
    });
    req.on('error', (e)=>{
      console.log(`request error:${JSON.stringify(e)}`);
      reject(e);
    });
    req.write(postData);
    req.end();
  });
}

(async ()=>{
  try {
    const result = await postDataWithAccessToken(postOptions, JSON.stringify(jsonDataImported));
    console.log(`${JSON.stringify(result)}`);
  } catch(e) {
    console.log(`ERROR - non-200/202 code returned from the endpoint:\n${JSON.stringify(e)}`);
  }
})();

ポピュラーなrequestモジュールを使った例

よく見つかるのはrequestモジュールを使用するコードかと思います。実際に私も今まではrequestを使ってましたが、最近開発がメンテナンスモードに入ってしまったとのことで、代わりを探さなくてはいけないと思ってました。

requestモジュール
import request from 'request';
import _ from 'lodash';
import jsonDataImported from './data.json';

const ACCESS_TOKEN = `<<アクセストークンと置き換えます>>`;
const ENDPOINTT_TO_POST = '<<エンドポイント 例)https://sample.com/sample/path >>';

const postOptions: request.Options = {
  uri: ENDPOINTT_TO_POST,
  method: 'POST',
  headers: {
    'Content-Type': "application/json;charset=utf-8",
    Accept: 'application/json',
    Authorization: `Bearer ${ACCESS_TOKEN}`
  },
  json: true,
  body: jsonDataImported
};

function postDataWithAccessToken(options: request.Options): Promise<any> {
  return  new Promise((resolve, reject)=>{
    const req = request.post(options, (err, res, body)=>{
        if (err) {
          console.log(`${JSON.stringify(err)}`);
        } else if (res.statusCode === 200 || res.statusCode === 202) {
          resolve(res);
        } else {
          console.log(`status code was not 200 or 202`);
          reject(res);
        }
    });
  });
}

(async ()=>{
  try {
    const result = await postDataWithAccessToken(postOptions);
    console.log(`${JSON.stringify(result)}`);
  } catch (e) {
    console.log(`ERROR - non-200/202 code returned from the endpoint:\n${JSON.stringify(e)}`);
  }
})();

gotモジュールを使った例

requestモジュールの代わりを選択肢を持っておこうと考えていて、今のところしばらくはgotを使ってみます。今日使ってみた中では、一番簡潔に書けますし、十分通常の用途をカバーするモジュールだと思います。

gotモジュール
import got from 'got';
import jsonDataImported from './data.json';

const ACCESS_TOKEN = `<<アクセストークンと置き換えます>>`;
const ENDPOINTT_TO_POST = '<<エンドポイント 例)https://sample.com/sample/path >>';

(async () => {
  try {
    const body = await got.post(ENDPOINTT_TO_POST, {
      body: jsonDataImported,
      json: true,
      headers: {
        'Content-Type': "application/json;charset=utf-8",
        Authorization: `Bearer ${ACCESS_TOKEN}`
      }
    });
  } catch(e) {
    console.log(`ERROR:\n${JSON.stringify(e)}`);
  }
})();

今後

特にgotについてはこれからも使ってみて、便利な使い方など見つけたらアップデートしていこうと思います。

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

知ってるとかっこよくなれるJS小技集2019

はじめに

コードレビューでさらっと
「こんな書き方もできますよ」って言ってくれる先輩ってかっこいいですよね。
いつかそんな先輩になれるように、メモ化しておこうという試みです。クリスマスのお供にどうぞ。

記法

変数名が同じなら省略できる

const params = {
   id: id
   name: name
}
const params = {
   id,
   name
}

{}で分割代入

似てるものでこういう省略系もあります。分割代入と言うらしいです。

const id = response.id;
const {id} = response;

複数もOK

const {id, name} = response;

実務でよくみる使い方だと、Reactでpropsから値取り出すときとかでしょうか。

const {width, height} = props;
return (
  <div style={{ width, height }} />
)

スプレッド演算子...で展開できる

const hoge = {
    id: fuga.id,
    name: fuga.name,
    type: fuga.type
}
const hoge = {
    ...fuga
}

展開してさらに追加してもOK

const hoge = {
    ...fuga,
    date: Date.now()
}

実例だと、ReduxでStateを一部更新するときなんかに使えますね

  [HogeAction.SOMETHING]: (state: IHogeState, action: any): IHogeState => ({
    ...state,
    fuga: action.payload
  })

オブジェクトをコピーするためにも使えますが、Object.assign同様ディープコピーではないので注意です。

const original = {
   id: 1,
   obj: {
      name: "shwan"
   }
};

const copy = { ...original };
console.log(copy.obj.name); // "shwan"
original.obj.name = "changed";
console.log(copy.obj.name); // "changed"

オブジェクトのディープコピー技だとこんなのもありますが、undefindを扱えない罠があったりするので、できるだけ避ける人生を歩みたいですね。

const copy = JSON.parse(JSON.stringify(original));

スプレッド演算子に話をもどすと、引数でも展開できたりします。使う場面はあまりなさそうですが。

doSomething(fuga.id, fuga.name, fuga.type);
doSomething(...fuga);

条件分岐

論理演算子 || で条件分岐

let name;
if (response.name) {
    name = response.name
} else {
    name = 'Guest';
}
const name = response.name || 'Guest';

nullかもしれない配列を空配列にするときによく使います

response.names.forEach() // response.namesがnullやundefindだとエラー
const arr = response.names || [];
arr.forEach()

論理演算子 && で条件分岐

if (isGuest) {
    doSomething();
}
isGuest && doSomething();

この記法、 if( A && B ) if( A || B ) なときと違う動きをしてるようにも見えますが
&&左が偽なら左の値を、真なら右の値を返す
||左が真なら左の値を、偽なら右の値を返す
という共通の仕組みです。

const name = response.name || 'Guest';

の例ではresponse.name'shwan'などの文字列であれば左が真とみなせるので、左の値が返り、''nullであれば、偽なのでGuestが返ります。

キャスト

!! でのbooleanキャスト

const isSelected = this.state.type ? true : false;
const isSelected = !!this.state.type;

この記法も!を2つつなげると if(!isSelected) なときと違う動きをしているようにみえますが、
!値を真偽値とみなしたうえで真偽を反転する という動きでどちらも共通です。

+ でのnumberキャスト

const id = '1'
console.log(+id); // 1
console.log(new Date()); // Wed Nov 27 2019 21:00:38 GMT+0900 (日本標準時)
console.log(+new Date()); // 1574856056863

キャストできなければNaN になるので使い所は見きわめましょう。
ちなみにnew DateはDate.now()を使えばキャストせずとも数値で出せます。

console.log(Date.now()); // 1574856056863

letしない

三項演算子でletの回避

let type = 'a';
if (isB) {
    type = 'b';
}
const type = isB ? 'b' :'a';

即時関数でletの回避

複雑な分岐なら即時関数を使って回避することもできますね。このあたりは可読性とトレードオフでしょうか。

const type = ((flag) => {
  if (flag) {
    return 'a';
  } else {
    return fukuzatuNaSomething();
  }
})(isB);

その他Tips

関数の引数に初期値を設定する

初期値をつけておけば条件分岐を減らせます

const doSomething = (a, b = 1) => a * b;
doSomething(1); // 1
doSomething(1, 2); // 2

someで繰り返し処理を途中で抜ける

arr.forEach((obj) => {
  doSomething
  if(shouldBreak){
    //break; ←できない
  }
})
arr.some((obj) => {
  doSomething
  if (shouldBreak) {
    return true;
  }
  return false;
})

someで配列の中を調べる

includes代わりに使えます

const arr = ['a', 'b', 'c'];
arr.includes('b') // true
const arr = [{
  name: 'shwan'
},{
  name: 'taro'
}];
arr.some((person) => person.name === 'shwan') // true

対象が配列なのか調べる

Array.isArray(target)

対象がオブジェクトなのか調べる

Object.prototype.toString.call(target).slice(8, -1).toLowerCase() === 'object';

ここでいうオブジェクトは {id: 1, name:'a'}こういうのです。配列のようにスッキリは書けないので一工夫必要になります。

そろそろネタがなくなって極小ネタになってきたので、また来年に向けて溜め直してきます!

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

Technology Radar 2019のピックアップ

Technology Radarから気になったものをピックアップし、軽く説明を添えてみました。
社内で共有したところ、反響が良かったのでQiitaにも投稿します。

(自分と似た技術スタックの方に刺さるのではないかと考えています)

ピックアップの観点

「自社の技術スタックとマッチしてるか」「自分の技術スタックとマッチしているか」の観点からピックアップしています。

筆者は現在web系の企業のSREチームに所属しており、業務や趣味で触れる技術/言語としては下記のとおりです。

  • Ruby on Rails
  • Node.js / Vue.js / Nuxt.js
  • AWS + Terraform
  • Go/python/Firebase/GCP

一方で下記技術は興味が無い/専門じゃない等の理由でスルーしていますのでご注意ください。

  • モバイルアプリ系
  • ML系
  • JVM系

Technology Radarとは

要は今年のイケてる技術の紹介です。
4段階で導入のおすすめ度合いが評価されているので、毎年重宝しています。

TECHNOLOGY RADERとはThoughtWorks社が発表している技術トレンド分析の調査結果になります。 年1-2回発表しており、2016年は4月と11月に発表されました。

ユニークな点は、技術トレンドの分析を
Techniques(開発手法) / Tools(ツール) / Platforms (プラットフォーム) / Languages & Frameworks(言語とフレームワーク)
の4分野に分けているところです。
また、評価結果も ADOPT / TRIAL / ASSESS / HOLDの4つに分けているところです。

ADOPT : プロジェクトにマッチするならば、採用を強くおすすめしている。
TRIAL : プロジェクトでリスクを管理できればやる価値はある。
ASSESS: どのような影響をあたえるか理解するために採用するときがある。(今後のために採用するときがある)
HOLD : 採用する場合は慎重に進める必要がある。

引用元:https://allabout-tech.hatenablog.com/entry/2017/02/15/093000

気になった技術

それぞれの領域ごとに概要と所感を記載しています。

Techniques

Container security scanning (ADOPT)

もはやコンテナのセキュリティスキャンは必須の時代です。
CI/CDパイプライン内でスキャンを実施しましょう。

所感:ECRのscan on pushやtrivyで簡単に実現できそうなのでやっていきたい。

Pipelines for infrastructure as code (ADOPT)

ソフトウェアのCI/CDパイプラインによるデプロイが主流になってきました。
Chef/Puppet/Ansible、Packer、Terraform等の登場により、インフラレイヤーもCI/CDを実現することが可能です。
インフラレイヤーもCI/CD化することで、実行元の一元管理や、実行前のエラー検知ができるようになるので、是非やりましょう。

所感:Lint/validationやplan結果の表示もできるのでやりたい。stg/prdのapplyタイミングを踏まえて設計する必要があるのでちょっと大変かも。Dockerfileも現在はCIにかかってないので回すようにしたい。

security policy as code (TRIAL)

セキュリティポリシーをコード化し管理していきましょう。
Open Policy AgentIstio等であれば、ポリシー定義と実施メカニズムを提供しています。

所感:明文化してGit管理を開始したので、適用を強制したり検知する仕組みもあわせて実施していけるといい感じかも。

Tools

Commitizen (ADOPT)

http://commitizen.github.io/cz-cli/
対話形式でGitのコミットメッセージをサポートしてくれるツールです。
コミットの種類とか関連するIssueの有無とかbreaking changeの有無とか聞いてくれていい感じにメッセージを組み立ててくれます。
commitizen

所感:個人のリポジトリはコミットメッセージ英語なので、これを導入することで良さげなリポジトリに見えるようになりそう。

jib (TRIAL)

https://github.com/GoogleContainerTools/jib
Java用のコンテナイメージ作成ツールです。
MavenやGradleに対応しており、DockerfileやDockerデーモンを必要とせずにイメージをビルドします。

所感:使うことはまあ無いだろうけど、デーモン無しにイメージを作成できる技術は気になる。

Trivy (TRIAL)

https://github.com/aquasecurity/trivy
コンテナイメージのセキュリティスキャンツールです。

所感:今ではかなり有名なやつ。使っていきたい。

Twistlock (TRIAL)

https://www.twistlock.com/
Twistlockは、コンテナ環境向けセキュリティ製品です。開発環境から実行環境まで、包括的なセキュリティを提供します。
NIST/CISベンチマークなど、業界のベストプラクティスに沿った対策が可能です。
twistlock

所感:金額次第だが、セキュリティ要件厳しいサービスを運用する際にはいいかも。ただし、融通が利くかどうかは重要。

asdf-vm (ASSESS)

https://asdf-vm.com/#/
複数の言語のバージョンを管理できるコマンドラインツールです。
RVMやnvmのようにバージョンを管理できますが、複数の言語をこのツール1つで管理できるのが特徴です。

所感:ruby以外もバージョン固定して利用するプロジェクトでは良さそう。個人の環境は全部これに乗せ替えたい。

AWSume (ASSESS)

https://github.com/trek10inc/awsume
AssumeRoleをいい感じにやってくれるCLIツール
AWSume

所感:社内のエンジニアには基本的にassum roleして利用するIAMユーザーを配ってるので、これを標準の手順にしてみてもよさそう。(現在、MFAを利用していることもあり、AssumeRoleするユーザーでawscliを使う手順がやや面倒)

Pumba (ASSESS)

https://github.com/alexei-led/pumba
PumbaはDockerのためのchaos testingとネットワークエミュレーションのツールです。
ネットワークをエミュレートし、遅延、パケット損失、帯域幅レート制限などのさまざまなネットワーク障害をシミュレートすることもできます。

所感:chaos testingはやったこと無いので挑戦してみたい。

Platforms

Crux (ASSESS)

https://opencrux.com/
bitemporal graph queryを備えたドキュメントデータベースです。
bitemporalとは、履歴を持ったデータのこと

不変のトランザクションレコードも保持しながら、ビジネスの真の履歴を記録します。これがバイテンポラリティの本質です。開発者およびアプリケーションユーザーとして、時間をかけて効率的にクエリを実行する機能のロックを解除します。 Cruxを使用すると、遡及修正を作成し、履歴データの移行を簡素化し、異常なイベントデータの統合ビューを構築できます。

所感:特許で見たことあるような。履歴を持ち、かつそれを遡及して修正するシステムは本当に辛いので、これがマッチするなら使うのが良さそう。

Hydra (ASSESS)

https://www.ory.sh/hydra/
OAuth2、OpenID connect providerを簡単にホストすることができるOSSです。

所感:micro serviceをk8s上で構築する際に使えそう。認証系の自前メンテは辛いので、こういうものを活用していきたい。

Teleport (ASSESS)

https://gravitational.com/teleport/

『Gravitational Teleportは、SSHまたはKubernetes API経由でLinuxサーバーのクラスタへのアクセスを管理するためのゲートウェイです。従来のOpenSSHの代わりに使用することを目的としています。』

参考記事

所感:中規模以上のk8sを利用している場合に効果を発揮しそう。自前でteleportのクラスタを構築し、ライセンスも必要なプロダクトなので、導入するのはけっこうな大事。GitHubや任意のSSO連携可能なサービスのアカウントで、ロールに基づいたsshができるのはまさに我々が求めていたものである。

Languages & Frameforks

jest-when (TRIAL)

https://www.npmjs.com/package/jest-when
when(fn).calledWith(args).thenReturn(value)のような感じでモック関数の引数に対するレスポンスを定義できるプラグインです。

所感:地味に便利で記述量を少なくできるので良さそう。

NestJS (ASSESS)

https://nestjs.com/
TypeScript製のserver side Node.jsフレームワークです。
GraphQL、Websocket、ORMライブラリなどのプロトコルをサポートしています。

参考記事:Nest.jsは素晴らしい

所感:発想はRailsに近いように思える(レールに乗ることでスタイルが統一され楽に開発できる等)。expressとの2択になりそう。

Paged.js

https://www.pagedmedia.org/paged-js/
HTMLで書籍等の印刷物を作る場合に必要なページカウンターやヘッダー、フッター等を描画できるポリフィルとCSSモジュールを生成してくれるライブラリです。

所感:本を作る機会があれば使ってみたいかも。前職であれば仕様書は謎に文書形式である必要があったので、Markdownで書いた場合でもヘッダーやフッターを頑張ってつけてたから、それにも使えそう。

まとめ

個人的に気になった技術をいくつかピックアップして紹介してみました。

よくあるツール紹介でごめんなさい。
でも、元が英語だから許してね。

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

CircleCIのnode imageでnpm installに失敗する

CircleCI2系で、公式ドキュメントを参照してセットアップしたらnpm installがコケた。
circleci/node:6.17.1のイメージを使用してる環境。

https://circleci.com/docs/ja/2.0/language-javascript/

Error: Cannot find module 'strip-ansi'

こういうエラーでnpm installが停止する。
package-lockの中を見るとstrip-ansiは入ってるし……と思ってたがプロジェクトのパッケージが問題ではなくて、原因としては、公式ドキュメントにある sudo がだめ。

.circleci/config.yml
- run:
    name: update-npm
    command: 'sudo npm install -g npm@latest'

npmがsudoでインストールされるとそのあとのnpmコマンドが通らなくなる(別にinstallに限らずnpmコマンド全部落ちる)。
sudoを外すとパーミッションでコケるので、以下のように変更するとよい

.circleci/config.yml
- run:
    name: set-global-prefix
    command: npm config set prefix '$HOME/.npm-global/'
- run:
    name: update-npm
    command: npm install -g npm@latest

グローバルパッケージのインストール場所をパーミッションのいらないところに変更すると通る。
もしかしたらnodeのバージョンにもよるのかもしれないけど、公式通りにやるとハマるのは困った。

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

結局Firebaseの無料プランSparkでは何ができるのか

Firebaseの制限

Google Cloud Platform(GCP)の提供するFirebaseでは料金の一切かからないプランsparkが存在する。
諸兄等には十分ご存じのことと思うが、そのプランではいくつか機能に制限がかけられている。
その制限はどのぐらい許容できるのもなのだろうか。

はじめに

私は仕事でFirebaseのBlazeを利用して開発を行っているが、
ふと強みの一つである無料プランが存在する利点を享受できていないのではと思い当たった。
そのため、この度Sparkプランで簡単なアプリを作成し、どのようなことが可能なのか、
また、その限界はどこなのかの一端を調べてみることにした。

検証した内容

limit.png
今回はこちらの「100秒当たりの制限」はどのぐらいで使いつくしてしまうかについて調べた。

開発環境

Firebase
nodejs(v8.16.2)
typescript(3.7.2)
vuejs(2.6.10)

ソース

https://github.com/tecoKodera/firebase_spark_test_othello
書きなぐったコードで申し訳ないが、リポジトリは上記にある

動作の様子

main.png
単純なオセロ+チャットBOT

結果

複数回呼び出す場合もあるような冗長なつくりにしたが、5秒ごとに1/10の確率で話すBOTと、
オセロ10クリック程度で制限を使い切ってしまった。
log.png
やはり複数回呼び出しかねない構造のFunctionsはよろしくないようだ。
今回チャット部の発言時間を自動で記録する処理が特に呼び出し回数を消費してしまっているようだった。
sparkプランで運用するには呼び出し自体を減らすよう設計する必要がある。

終わりに

Firebaseには他にも制限があり、思いがけないところでコツが必要になってくる。
慣れるまでは大変だが、やはりこの速さでwebアプリを作れることは大きな魅力に違いない。
これからも皆様におかれましては素晴らしいFirebaseライフを。

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

Node.jsのfsモジュールの使い方

fsモジュールとは

fsモジュールはファイルを扱うためのモジュールで、ファイルから書き出したり、ファイルに書き込んだりするときに役立ちます。
Node.jsがはじめから提供しているモジュールなので、Node.jsのインストールがしてあれば、fsモジュールのインストールの必要はありません。

ファイルの読み込み

const fs = require('fs');

//fs.readFileSync(ファイルのパス, 文字コード, コールバック関数)
fs.readFileSync('./text.txt', utf-8, (err, data) => {
//dataがファイルの中身、errは読み込み時のエラー
  if(data) {
    console.log(data);
  } else {
    console.log(err);
  }
});

ファイルへの新規書き込み

const fs = require('fs');

//fs.writeFileSync(ファイルのパス, 書き込む文字, 文字コード, コールバック関数)
fs.writeFileSync('./text.txt', "こんにちは", utf-8, (err) => {
  if(err) {
    console.log(err);
  }
});

ファイルへの追加書き込み

const fs = require('fs');

//fs.appendFileSync(ファイルのパス, 書き込む文字, 文字コード, コールバック関数)
fs.appendFileSync('./text.txt', "こんにちは", utf-8, (err) => {
  if(err) {
    console.log(err);
  }
});

ファイルの削除

index.js
const fs = require('fs');

//fs.unlink(ファイルのパス, コールバック関数)
fs.unlink(process.argv[2], (err) => {
  if (err) {
    console.error(err);
  } else {
    console.log('削除が終わりました');
  }
});

process.arg[2]はコマンドの第三引数を指すので、以下のようにファイルをコマンドの第三引数に書き、ファイルの実行を行います。

console
$ node index.js ./text.txt

ファイルの作成

const fs = require('fs');

//fs.open(新規作成ファイルのパス, 書き込む文字, コールバック関数)
fs.open('./text.txt', 'こんにちは', (err) => {
  if(err) {
    console.log(err);
  } else {
    console.log('ファイルが作成されました');
  }
});

ストリーム

当初、ストリームを使う理由が分からなかったのですが、ストリームだとデータを必要な分だけ受け取ることができるようになるため、メモリを多く使わずに済むそうです。

参考:Node.js Stream を使いこなす

const fs = require('fs')

//読み込みストリームを作成
const rs = fs.createReadStream('./text.html');
//dataイベントが発生したら(データを受け取ったら)、chunkに'./text.html'のデータが入ります。
rs.on('data', (chunk) => {
  res.write(chunk)
});
普通のファイル読み込みで同じ処理をしてみると、

readFileSyncだと、同期的な読み込みになるため、すごく読み込みが遅いです。
そのため、readFileで非同期的な読み込みをしています。

const fs = require('fs');

//fs.readFile(ファイルのパス, コールバック関数)
fs.readFile('./text.html', (err, data) => {
//dataがファイルの中身、errは読み込み時のエラー
  if(data) {
    res.write(data);
  } else {
    console.log(err);
  }
});

今回は、./text.htmlファイルも大きくなく、この程度のファイルならばスピードに大きな違いはありませんでした。
ですが、これが大規模な開発になると違いが出てくると思われます。

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

ファイルをセーブしたら、npmスクリプトを走らせる」というライブラリを1行で作る。

「ファイルをセーブしたら、npmスクリプトを走らせる」というライブラリがある日突然欲しくなりました。

onchangeというライブラリが見つかりました。npmライブラリです。

使い方は簡単で、onchangeをグローバルにインストールした後、grobパターンで、監視するファイルとnpmスクリプトを記述するだけです。

npm install -g onchange
onchange 'app/**/*.js' 'test/**/*.js' -- npm test

しかしです...

globパターンを記述するのが地味に面倒い!
ルートフォルダ以下で変更があれば、npmスクリプトを走らせるだけでいいのに...

そこでよりシンプルなライブラリを自作することにしました

作ったのが「save-run」というライブラリ。

名前は、save-runとして、npmに登録してあります。
githubリポジトリはこちら

使い方は簡単で、グローバルにインストールしてからrun-saveコマンドの後にnpmスクリプトを渡すだけ。

npm install -g run-save
run-save npm test

幸せになれました〜〜〜

私は、react-nativeエンジニアなのですが、
run-save npm start
run-save npm test
などに使っています。

実はこのライブラリは1行で実装されているので、興味のある方は読んでみてください。

#!/usr/bin/env node

const { watch } = require('chokidar')
const { execSync } = require('child_process')

watch('.').on('change', () => execSync(process.argv.slice(2).join(' '), { stdio: 'inherit' }))

完!

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

Node.js で無限ループしつつ、一定周期で処理をしたい

メモ。
Node.js で無限ループしつつ、一定周期で処理をしたいという要件があった。
(具体的には SIGTERM シグナルを受けてから1秒毎に標準出力に文字列を出力したい)
上記について Node.js のサイトにずばり書いてあった。

"Infinite Loop" Execution ~ setInterval()

node.js
function intervalFunc() {
  console.log('Cant stop me now!');
}

setInterval(intervalFunc, 1500);

この場合、1.5秒毎に intervalFunc() が実行される。
必要に応じて時間や処理内容を変えれば良い。

Node.js v10.17.0 で動作確認済み。

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