@Satoh_D no blog

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

【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 試しに持って歩きましたが、適度に太く握りやすい印象でした。

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

【AWS】RDS(MySQL)で発生したIPブロックを解消する

今やっている案件で、急にRDSへの接続ができず以下のエラーを吐くようになりました。

Host 'xxx.xxx.xxx.xxx' is blocked because of many connection errors;
〜以下続く

調べたところ、MySQLのシステム変数であるmax_connect_errorsで設定した値以上接続失敗をするとIPブロックされてしまうようです。
解消方法は以下の通り。

  1. IPブロックされていないホストからMySQLに接続
$ mysql -u [username] -p -h [hostname]
  1. MySQLにログイン後、以下コマンドを実施
mysql> FLUSH HOSTS;
Query OK, 0 rows affected (0.01 sec)

これで解消できます。 解消したのはいいのですが、何故起きたのかを突き止めないとですね...(ヽ´ω`)

3ステップでしっかり学ぶ MySQL入門 [改訂2版]

3ステップでしっかり学ぶ MySQL入門 [改訂2版]

【Python】Pythonを使ってGoogle Spread Sheetを操作してみる

社内でやっているIoT案件にて、PythonからGoogle Spreadsheetにデータを送信する必要があったので試してみた。

環境

手順

1. Google Spread SheetのAPIキーを取得する

Google API コンソールのページに遷移する

「アプリケーションを登録するプロジェクトの選択」から任意のプロジェクトを選択する
プロジェクトが存在しない場合、「プロジェクトの作成」をクリックし、プロジェクトを作成する。

プロジェクトを作成した場合、認証用の鍵がダウンロードされる。
ダウンロードした認証用の鍵は「client_secret.json」というファイル名にリネームし、実行ファイルと同じ場所に置いておく。

2. Google API用のPythonパッケージをダウンロードする

pipを利用してgoogle-api-python-client, oauth2clientをダウンロードする
サンプルコードgoogle-api-python-clientだけでいいっぽいけど、うちの環境ではoauth2clientも必要だった。

$ pip install --upgrade google-api-python-client
$ pip install --upgrade oauth2client

3. サンプルコードを写経する

サンプルコードのコードをエディタに書き、動かしてみる。

from __future__ import print_function
import httplib2
import os

from apiclient import discovery
from oauth2client import client
from oauth2client import tools
from oauth2client.file import Storage

try:
    import argparse
    flags = argparse.ArgumentParser(parents=[tools.argparser]).parse_args()
except ImportError:
    flags = None

# If modifying these scopes, delete your previously saved credentials
# at ~/.credentials/sheets.googleapis.com-python-quickstart.json
SCOPES = 'https://www.googleapis.com/auth/spreadsheets.readonly'
CLIENT_SECRET_FILE = 'client_secret.json'
APPLICATION_NAME = 'Google Sheets API Python Quickstart'


def get_credentials():
    """Gets valid user credentials from storage.

    If nothing has been stored, or if the stored credentials are invalid,
    the OAuth2 flow is completed to obtain the new credentials.

    Returns:
        Credentials, the obtained credential.
    """
    home_dir = os.path.expanduser('~')
    credential_dir = os.path.join(home_dir, '.credentials')
    if not os.path.exists(credential_dir):
        os.makedirs(credential_dir)
    credential_path = os.path.join(credential_dir,
                                   'sheets.googleapis.com-python-quickstart.json')

    store = Storage(credential_path)
    credentials = store.get()
    if not credentials or credentials.invalid:
        flow = client.flow_from_clientsecrets(CLIENT_SECRET_FILE, SCOPES)
        flow.user_agent = APPLICATION_NAME
        if flags:
            credentials = tools.run_flow(flow, store, flags)
        else: # Needed only for compatibility with Python 2.6
            credentials = tools.run(flow, store)
        print('Storing credentials to ' + credential_path)
    return credentials

def main():
    """Shows basic usage of the Sheets API.

    Creates a Sheets API service object and prints the names and majors of
    students in a sample spreadsheet:
    https://docs.google.com/spreadsheets/d/1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms/edit
    """
    credentials = get_credentials()
    http = credentials.authorize(httplib2.Http())
    discoveryUrl = ('https://sheets.googleapis.com/$discovery/rest?'
                    'version=v4')
    service = discovery.build('sheets', 'v4', http=http,
                              discoveryServiceUrl=discoveryUrl)

    spreadsheetId = '1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms'
    rangeName = 'Class Data!A2:E'
    result = service.spreadsheets().values().get(
        spreadsheetId=spreadsheetId, range=rangeName).execute()
    values = result.get('values', [])

    if not values:
        print('No data found.')
    else:
        print('Name, Major:')
        for row in values:
            # Print columns A and E, which correspond to indices 0 and 4.
            print('%s, %s' % (row[0], row[4]))


if __name__ == '__main__':
    main()

ソースの流れを見る感じ、get_credentials()でOAuthの認証情報を取得し、ローカルにjson形式で保存。
json形式で保存することで、2回目以降の認証をスキップできるみたい。

credentials = get_credentials()
http = credentials.authorize(httplib2.Http())
discoveryUrl = ('https://sheets.googleapis.com/$discovery/rest?'
                'version=v4')
service = discovery.build('sheets', 'v4', http=http,
                          discoveryServiceUrl=discoveryUrl)

このあたりでGoogle SpreadsheetへアクセスするためのServiceを作成している。 Spreadsheetの内容を取得するのは以下コード部分。

spreadsheetId = '1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms'
rangeName = 'Class Data!A2:E'
result = service.spreadsheets().values().get(
    spreadsheetId=spreadsheetId, range=rangeName).execute()
values = result.get('values', [])

spreadsheetIdでアクセス先のSpreadsheetを設定し、rangeNameで取得する位置を設定している。
データの取得自体はservice.spreadsheets().values().get()を利用する。

ちなみにデータの書き込みは上記サンプルコードのうち、以下の部分を書き換える。

# SCOPES = 'https://www.googleapis.com/auth/spreadsheets.readonly'
SCOPES = 'https://www.googleapis.com/auth/spreadsheets'

# result = service.spreadsheets().values().get(...).execure()
ValueInputOption = 'USER_ENTERED'
body = {
  'values': [[1, 2, 3]]
}
# データの書き込みは service.spreadsheets().values().update() を利用する
# データの追記は service.spreadsheets().values().append() を利用する
# valueInputOption='USER_ENTERED`オプションを利用すると、「書式設定」が自動になり、通常入力と同じ状態になる
#   → 入力時に書式を自動判別してくれる
result = service.spreadsheets().values().update(
    spreadsheetId=spreadsheetId, range=rangeName,
    valueInputOption=ValueInputOption, body=body).execute()

参考サイト

入門 Python 3

入門 Python 3