@Satoh_D no blog

大分にUターンしたので記念に。調べたこととか作ったこととか食べたこととか

nodenv経由でインストールしたnodeでyarnをインストールしたけど動かなかった件

nodeenv経由のnpmyarnをインストールしたけどcommand not foundとエラーが出てしまい利用できなかった
この解決方法をメモしておく

環境

  • MacOS: 10.15.6
  • nodenv: 1.4.0+3.631d0b6
  • node.js: v14.12.0
  • yarn: 1.22.5

解決方法

nodenv rehash するだけ
yarnをインストールした直後はshimsが更新されておらずPATHが通っていないので、このコマンドを打つことでshimsを更新しPATHが通った状態になるとのこと。

$ npm installl -g yarn
$ yarn
# 何故かエラーとなる
bash: yarn: command not found

# 解決
$ nodenv rehash
$ yarn -v
1.22.5

参考サイト

Nuxt.jsでWebカメラで映像を撮影 & 画像の取得をしてみる

案件でブラウザからWebカメラにアクセスし、画像を取得する実装を刷ることになったのでサンプルを作ってみた。
Nuxt.js初めて使ったけど学習コスト低くていいですね。もっと勉強したい。

環境

  • MacOS: 10.15.6
  • node.js: v14.12.0
  • yarn: v1.22.5
  • Nuxt.js: v.2.14.6

手順

1. Nuxt.jsでプロジェクトを作成する

$ yarn create nuxt-app webrtc-sample

yarn create v1.22.5
[1/4] 🔍  Resolving packages...
[2/4] 🚚  Fetching packages...
[3/4] 🔗  Linking dependencies...
[4/4] 🔨  Building fresh packages...
success Installed "create-nuxt-app@3.3.0" with binaries:
      - create-nuxt-app

create-nuxt-app v3.3.0
✨  Generating Nuxt.js project in webrtc-sample
? Project name: webrtc-sample
? Programming language: JavaScript
? Package manager: Yarn
? UI framework: Buefy
? Nuxt.js modules: Axios
? Linting tools: 
? Testing framework: None
? Rendering mode: Single Page App
? Deployment target: Server (Node.js hosting)
? Development tools: (Press <space> to select, <a> to toggle all, <i> to invert selection)
? Version control system: Git

🎉  Successfully created project webrtc-sample

プロジェクトができたら yarn dev で起動 これで http://localhost:3000 でブラウザで表示ができるようになる この時点ではデフォルトのページが表示される

$ yarn dev

ソースコード

新規でページを作成し、そこにソースコードを記述する(index.vueを編集しても可)
今回は /pages/webrtc.vue を作成

<template>
  <section class="section">
    <div class="columns">
      <div class="column"><video id="localvideo" ref="localVideo" autoplay v-bind:srcObject.prop="localstream" width="500" height="500"></video></div>
    </div>
    <div class="columns">
      <div class="column"><canvas id="localPic" ref="localPicCanvas" width="640" height="350"></canvas></div>
    </div>
    <div class="buttons">
      <b-button @click="getUserMedia">映像を取得する</b-button>
      <b-button @click="getUserPic">写真を撮る</b-button>
    </div>
  </section>
</template>

<script>
export default {
  data: function() {
    return {
      localstream: undefined,
      canvas: undefined,
      movie: undefined,
      streamConst: {
        video: {
          width: 1280,
          height: 700
        }
      }
    }
  },
  mounted: function() {
    this.canvas = this.$refs.localPicCanvas;
    this.movie = this.$refs.localVideo;
  },
  methods: {
    getLocalMediaStream: function(mediaStream) {
      this.localstream = mediaStream;
    },
    handleLocalMediaStreamError: function(error) {
      console.error(`navigator.getUserMedia error: ${error}`);
    },
    getUserMedia: function() {
      navigator.mediaDevices
        .getUserMedia(this.streamConst)
        .then(this.getLocalMediaStream)
        .catch(this.handleLocalMediaStreamError);
    },
    getUserPic: function() {
      const ctx = this.canvas.getContext('2d');
      ctx.drawImage(this.movie, 0, 0, 640, 350);
    }
  }
}
</script>

Webカメラで撮影する部分と写真を撮る部分は以下の通り

Webカメラで撮影する部分

<template>
  ...
      <div class="column"><video id="localvideo" ref="localVideo" autoplay v-bind:srcObject.prop="localstream" width="500" height="500"></video></div>
  ...
      <b-button @click="getUserMedia">映像を取得する</b-button>
  ...
</template>

<script>
export default {
  data: function() {
    return {
      localstream: undefined,
      streamConst: {
        video: {
          width: 1280,
          height: 700
        }
      }
    }
  },
  ...
  methods: {
    getLocalMediaStream: function(mediaStream) {
      this.localstream = mediaStream;
    },
    handleLocalMediaStreamError: function(error) {
      console.error(`navigator.getUserMedia error: ${error}`);
    },
    getUserMedia: function() {
      navigator.mediaDevices
        .getUserMedia(this.streamConst)
        .then(this.getLocalMediaStream)
        .catch(this.handleLocalMediaStreamError);
    },
    ...
  }
}
</script>

ブラウザからWebカメラにアクセスするには navigator.mediaDevices.getUserMedia() を利用する
navigator.mediaDevices.getUserMedia()promiseが利用できるのでthen(), catch()を利用することができる
ストリームが取得できた場合、video要素にバインドすると映像が表示できるようになる

このとき、ストリームをv-bind:srcで指定してもよいが(ストリームをURL.createObjectURLで変換する必要あり)、URL.createObjectURLが廃止予定のメソッドであるためv-bind:srcObject.propで指定するほうが良いらしい

写真を撮る部分

<template>
  ...
      <div class="column"><canvas id="localPic" ref="localPicCanvas" width="640" height="350"></canvas></div>
  ...
      <b-button @click="getUserPic">写真を撮る</b-button>
  ...
</template>

<script>
export default {
  data: function() {
    return {
      canvas: undefined,
      movie: undefined,
    }
  },
  mounted: function() {
    this.canvas = this.$refs.localPicCanvas;
    this.movie = this.$refs.localVideo;
  },
  methods: {
    ...
    getUserPic: function() {
      const ctx = this.canvas.getContext('2d');
      ctx.drawImage(this.movie, 0, 0, 640, 350);      
    }
  }
}
</script>

撮影する部分はcanvas要素を利用して描画するのみ setIntervalを利用すれば指定ミリ秒後に写真を取り続けるといったことも可能になる

参考サイト

Vue.js&Nuxt.js超入門

Vue.js&Nuxt.js超入門

【JavaScript】IE11では関数の引数に初期値を設定すると「’)’がありません」エラーと出る

今やってる案件(IE11も対応範囲)で、謎のエラーに悩まされてたのが解決出来たのでメモ。
(今もIE11対応が必要なのかというのはあるけれども。。)

エラーの現象

一部のページをIEで確認するとDevtoolのコンソールタブに

’)’がありません

というメッセージが表示される。

何度も該当箇所の括弧の数を確認しても問題ない…。

解消方法

少し調べてみたところ、IE固有のバグ とのこと。
IEでは関数の引数に次のように初期値を指定すると今回のようなエラーが出るそうで。。

// 今回エラーが起きていた該当箇所のソース(一部改変)
function foo ( bar = true ) {
}

エラーが出ないよう関数内で処理をするよう変更したことで解消できた。
本当にIEってやつは…

function foo ( bar ) {
    if (bar == undefined ) {
        bar = true;
    }
}

参考にしたサイト

Anker PowerPort Atom III Slimを買ってみた

Amazonの新生活セールで「Anker PowerPort Atom III Slim」が500円引きの2,000円になっていたのでポチってみた。
最近出張にMacbook Proを持っていくことが多く、ACアダプタを軽いものにすることで少しでも荷物を軽くするのが狙い。
ACアダプタって地味に重いんだよなぁ。。

届いてびっくりしたのがその小ささ。
名刺よりも一回り小さいくらいで、薄さもそれなり。
重さも56g(実測 / カタログでは59g)とめちゃくちゃ軽い。
Macbook ProのACアダプタが190gくらいだったので140gくらいは軽量化に成功したことになる。

これで次の出張が少し楽になる...かも?

【Laravel】カスタムバリデーションを作成する

最近初めてLaravelの案件をやることになりまして。
ひっさびさのプログラミングということもあってフレームワークの進化に驚くばかり...。

Laravelはバリデーション周りがデフォルトで充実してていいですね。
しかし、デフォルトでは実装できないバリデーションの要件があったので、
勉強がてらカスタムバリデーションを実装してみました。

前提

  • PHP7.0
  • Laravel 5.5.x

1. カスタムバリデーションルールを作成する

php artisanで雛形を作成する

$ php artisan make:rule HogeCustomRule

/app/Rules/HogeCusomRule.phpが出来上がる

<?php

namespace App\Rules;

use Illuminate\Contracts\Validation\Rule;

class HogeRule implements Rule
{
    /**
     * Create a new rule instance.
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

    /**
     * Determine if the validation rule passes.
     *
     * @param  string  $attribute
     * @param  mixed  $value
     * @return bool
     */
    public function passes($attribute, $value)
    {
        //
    }

    /**
     * Get the validation error message.
     *
     * @return string
     */
    public function message()
    {
        return 'The validation error message.';
    }
}

バリデーションルールはpasses()に記載する
戻り値はboolになるよう処理を書くこと
falseのときにmessage()に記載したエラーメッセージが表示される

public function passes($attribute, $value)
{
    if($value % 2 == 0) {
      return true;
    }
}

public function message()
{
    return ':attributeは偶数で入力してください';
}

2. フォームリクエストを作成する

php artisanで雛形を作成する

$ php artisan make:request HogeRequest

/app/Http/Requests/HogeRequest.phpが出来上がる

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class HogeRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return false;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
            //
        ];
    }
}

rules()にバリデーションルールを書いていく
カスタムバリデーションルールはuseで宣言した後、new HogeRule()で適用する

use App\Rules\HogeRule;

class HogeRequest extends FormRequest
{
  ...
  
  public function rules()
  {
    return [
      'hoge' => 'required', 'integer', new HogeRule()
    ];
  }
}

3. コントローラで利用する

コントローラー内で利用したいアクションの引数の型に先程作成したHogeRequestを利用する
引数に宣言することでアクション実行時にバリデーションチェックを自動的に行なってくれる

use Illuminate\Http\Request;
use App\Http\Requests\HogeRequest;

class HogeController extends Controller
{
  public function index()
  {
    ...
  }
  
  public function store(HogeRequest $request)
  {
    ...
  }
}

やってみて

わりと簡単に実装できるもんですね。
Controllerの引数に指定するだけで自動的にバリデーションチェックをやってくれるのマジ凄い。
最近のフレームワークってこういった挙動がトレンドなんですかね。

参考サイト

PHPフレームワーク Laravel入門

PHPフレームワーク Laravel入門

【Docker】MacにDockerをインストールしてみた & 少し操作してみた

必要に迫られたのでそろそろdockerを勉強してみた結果を記します。

今回の環境

やってみる

Docker for Macをインストールする

Docker for Mac公式サイトでもインストールできるみたいだけど、今回はhomebrewを利用してインストールしてみます。

$ brew search docker
==> Formulae
docker                              docker-compose                      docker-gen                          docker-machine-driver-hyperkit      docker-machine-parallels
docker-clean                        docker-compose-completion           docker-ls                           docker-machine-driver-vultr         docker-squash
docker-cloud                        docker-credential-helper            docker-machine                      docker-machine-driver-xhyve         docker-swarm
docker-completion                   docker-credential-helper-ecr        docker-machine-completion           docker-machine-nfs                  docker2aci

==> Casks
docker                                                       docker-toolbox                                               homebrew/cask-versions/docker-edge

$ brew cask install docker
Updating Homebrew...
==> Satisfying dependencies
==> Downloading https://download.docker.com/mac/stable/28905/Docker.dmg
######################################################################## 100.0%
==> Verifying SHA-256 checksum for Cask 'docker'.
==> Installing Cask docker
==> Moving App 'Docker.app' to '/Applications/Docker.app'.
🍺  docker was successfully installed!

Dockerがインストールされたか念の為確認

インストールしたDocker.appを起動し、ステータスが「Docker Desktop is running」となれば起動完了です。
念の為Terminalからコマンドの確認がてらバージョンを表示してみます。

$ docker --version
Docker version 18.09.0, build 4d60db4
$ docker-machine version
docker-machine version 0.16.0, build 702c267f
$ docker-compose version
docker-compose version 1.23.1, build b02f1306
docker-py version: 3.5.0
CPython version: 3.6.6
OpenSSL version: OpenSSL 1.1.0h  27 Mar 2018

Hello Worldしてみる

公式サイトにある手順に沿い、Terminalでhello-worldというDocker Imageをインストールします。
docker runで起動するDocker Imageがローカルに無い場合、Docker Hubから自動的にPullしてくれるようですね、便利。

$ docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
d1725b59e92d: Pull complete 
Digest: sha256:0add3ace90ecb4adbf7777e9aacf18357296e799f81cabc9fde470971e499788
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (amd64)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/get-started/

次はnginxのDocker Imageをインストールします

$ docker run -d -p 80:80 --name webserver nginx
Unable to find image 'nginx:latest' locally
latest: Pulling from library/nginx
a5a6f2f73cd8: Pull complete 
67da5fbcb7a0: Pull complete 
e82455fa5628: Pull complete 
Digest: sha256:31b8e90a349d1fce7621f5a5a08e4fc519b634f7d3feb09d53fac9b12aa4d991
Status: Downloaded newer image for nginx:latest
11b1835537b3da82eeb655a4c43ad5010eeb4fcb3fff626501ba795e9469fa0d

【構文】 docker run -d -p 80:80 --name {エイリアス名} nginx

コマンド, オプション 内容
docker run Docker Imageからコンテナを起動する
-d コンテナをデタッチド・モードで起動する
-p 80:80 ポートフォワーディングの設定を行う。この例ではホスト側80番ポートの通信をコンテナ側80番ポートに転送する
--name コンテナのエイリアス名を設定する
nginx この部分はDocker Image名を設定する

nginxが起動したらブラウザを開き、http://localhostにアクセスしてみます。
「Welcome to nginx!」が表示されれば完了です。

f:id:Satoh_D:20181120132431p:plain

ちなみにdocker container lsを利用することで現在コンテナが起動しているかも確認できるぽい。

$ docker container ls
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                NAMES
11b1835537b3        nginx               "nginx -g 'daemon of…"   About an hour ago   Up About an hour    0.0.0.0:80->80/tcp   webserver

コンテナを確認したり止めたりイメージを削除したり

コンテナの確認

基本はdocker container lsを利用します。
-aオプションをつけることで起動していないコンテナも一覧表示します。

$ docker container ls
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                NAMES
11b1835537b3        nginx               "nginx -g 'daemon of…"   About an hour ago   Up About an hour    0.0.0.0:80->80/tcp   webserver
$
$ docker container ls -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                   PORTS                NAMES
11b1835537b3        nginx               "nginx -g 'daemon of…"   About an hour ago   Up About an hour         0.0.0.0:80->80/tcp   webserver
2a79441c51b8        hello-world         "/hello"                 2 hours ago         Exited (0) 2 hours ago                        adoring_volhard

コンテナの停止

docker container stop {コンテナ名}でコンテナを停止することができます。

$ docker container stop webserver
webserver
# 停止したか確認
$ docker container ls -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                      PORTS               NAMES
11b1835537b3        nginx               "nginx -g 'daemon of…"   2 hours ago         Exited (0) 20 seconds ago                       webserver

コンテナの削除

docker container rm {コンテナ名}でコンテナを削除することができます。

$ docker container rm webserver
webserver
# webserverコンテナが削除されていることを確認
$ docker container ls -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                   PORTS               NAMES
2a79441c51b8        hello-world         "/hello"            2 hours ago         Exited (0) 2 hours ago                       adoring_volhard

イメージの確認

docker image lsでローカルにあるイメージを一覧表示します。

Owner-no-MacBook-Air:docker_sample owner$ docker image ls
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
nginx               latest              e81eb098537d        3 days ago          109MB
hello-world         latest              4ab4c602aa5e        2 months ago        1.84kB

イメージの削除

docker image rm {イメージ名}でローカルにあるイメージを削除できます。

$ docker image rm nginx
Untagged: nginx:latest
Untagged: nginx@sha256:31b8e90a349d1fce7621f5a5a08e4fc519b634f7d3feb09d53fac9b12aa4d991
Deleted: sha256:e81eb098537d6c4a75438eacc6a2ed94af74ca168076f719f3a0558bd24d646a
Deleted: sha256:7055505a92c39c6f943403d54a1cda020bfeb523b55d9d78bfe1dad0dd32bb2d
Deleted: sha256:378a8fcc106dc4d3a9f2dc0b642b164e25de3aab98a829e72b2d8c0cf0bad8ee
Deleted: sha256:ef68f6734aa485edf13a8509fe60e4272428deaf63f446a441b79d47fc5d17d3
# nginxが削除されていることを確認
$ docker image ls
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
hello-world         latest              4ab4c602aa5e        2 months ago        1.84kB

やってみて

初心者でも割とすんなり出来ました。 これだけでは案件に全然使えないので、もっと色々調べなきゃですね...。

参考サイト

Docker/Kubernetes 実践コンテナ開発入門

Docker/Kubernetes 実践コンテナ開発入門

enkeeoのカーボン製トレッキングポールを買ってみた

表題の通り、enkeeoのトレッキングポールを購入しました。
Amazonのタイムセールで¥3,280でした。

似たようなトレッキングポールは何個かあったのですが、決め手になったのは以下の3通り。

  • カーボン製であること
  • 伸縮の際の固定ははレバー式であること
  • かっこいいこと

登山利用なのでなるべく軽いことを重要視していたので、カーボン製なのはとても助かります。
他にこの価格帯でカーボン製のポールがあまりなかったんですよね...。

早速開けてみたところです。
f:id:Satoh_D:20181025083154j:plain

ポールの他に接地面の付属品が4セットほど入っています。
雪山等を歩くつもりは今のところ無いのですが、あって困るものではないのでいいですね。
f:id:Satoh_D:20181021150832j:plain

伸縮の際のジョイント部分はレバーで固定する形となっています。
f:id:Satoh_D:20181021150909j:plain 一緒にトレッキングしている方から「スクリューロック式はやめておけ、使ってると緩む」という話を聞いていたのでこの部分は譲れませんでした。
購入時は下のレバーについているネジがキツく伸ばしづらいので予めネジを緩めて伸ばすよう注意してください。
ちなみに最短65cm、最長135cmまで伸縮ができるようです。

取っ手の部分は人工光学に基づいて設計されているらしく、かっこいいだけでなく持ちやすいです。 f:id:Satoh_D:20181021150850j:plain 試しに持って歩きましたが、適度に太く握りやすい印象でした。

まだトレッキングに利用はしていないですが、ざっくり触った感じ価格以上の価値があるんじゃないかと思いました。
早くこのポールを使ってトレッキングがしてみたい...してみたい...!