poriweb

備忘録とか

vimでNERDTreeを使いつつもmksしたい

最近nvimを使っているんですが、NERDTreeを起動したまま

:mks

して

nvim -S

するとNERDTreeが復元できなくてエラーになります。
ちょっと調べたんですがあまり良い方法が見つからず・・・。
せっかく ~/.config/git/ignoreSession.vim を書いてglobalでgit ignoreできて他の人がvim使ってないプロジェクトでも優勝!と思ったのに。

しょうがないのでvim終了時に :mks! するようにしてその直前にNERDTreeは閉じることにしました。

autocmd VimLeave * NERDTreeClose
autocmd VimLeave * mks!

復元できるのが一番ですが、あまり設定周りを複雑にしたくない気持ちもあり。
何かいい方法あれば教えていただきたいです。

お試しIonic React

IonicFrameworkについて、最近よく耳にするので、自分も試したかったためプロジェクト作成→PWA→iOSビルドまでやってみました。

ionicframework.com

やったことは完全に ドキュメント (Installing Ionic - Ionic Documentation) の流れなので、内容については細かくは書きませんが、ざっくり説明すると

  1. npmでPCに @ionic/cli を入れる
  2. ionicコマンドでプロジェクトを作る
  3. npmで @ionic/react and @ionic/react-router を入れる
  4. ionic serve してweb開発する(まんまreact scriptsですね)
  5. githubにpushし、netlifyにデプロイしてPWAを実機(iPhone)で試す
  6. ionicコマンドで capacitor add ios する
  7. capacitor.config.json の appId(Bundle ID)をセットする(store公開しないので適当に)
  8. macOS最新化
  9. Xcode最新版インストール
  10. cocoapods最新版インストール
  11. Xcodeでプロジェクト開く
  12. エミュレータ&実機ビルド確認

をしました。 ただ、実際には8をやる前に実機ビルドしようとして、pod installがコケたり、ダークモードのコードがうまく動作しなかったりといったことで軽くハマっているので前後関係がちょっと違います。全部最新化されていればハマりポイントは少なくサクッといけそうだなぁという印象です。ネイティブのAPIなどはまだ触っていないので、その辺触り出したらどの程度ハマるか・・・。

リポジトリは下記です。

github.com

所感(触って10h程度です。前述の通りネイティブのAPIはまだ触っていません。)

  • ドキュメントしっかりしていたのでwebでの開発に入るまではサクッといけたので、単なるWebアプリでも採用して良いのではないかという印象です。(ただパッケージの依存性などの制約がどの程度あるかなどはわかりませんが、そこはもう使う以上はIonic Wayに乗っかるべきですね。最悪Ionicだけを剥がすこともできそうなのは👍)
  • ネイティブにビルドするためにどの程度コードの書き方への制約があるのかまだよくわかっていませんが、表面的な部分では普通にReact書いてそのままiOSアプリ作れる感覚がとてもGoodでした。
  • Xcode周りはSwift開発ほどの知識は不要とはいえ、CocoaPodsなどを全く知らない状態からやるとハマって抜け出せなくなる場合もありえるのかな?と思いましたが、逆にIonicからネイティブビルド環境に触れる足掛かりにするのもありかなと。この手の「ハイブリッドアプリやクロスプラットフォームフレームワークはそのネイティブアプリビルド環境の知識がないとキツイ問題」はどこでも同じだとは思いますので。

というわけで技術選定時に問題がなければプロダクションでも積極的に使っていきたいなーという気持ちは今のところ結構強いです。

@vue/composition-apiで縦と横のwindowサイズの変更検知する

最近 @vue/composition-api を触っていて、ウィンドウサイズを検知して値を取得する処理を書いたので備忘録として残します。 以下そのまま書いてもたぶん動きますが確認してないです。

import {
  defineComponent,
  reactive,
  onMounted,
  onBeforeUnmount,
} from '@vue/composition-api'

export default defineComponent({
  setup() {
    const state = reactive({
      width: window.innerWidth,
      height: window.innerHeight,
    })
    const resizeWidth = () => {
      state.width = window.innerWidth
    }
    const resizeHeight = () => {
      state.height = window.innerHeight
    }
    onMounted(() => {
      window.addEventListener('resize', resizeWidth)
      window.addEventListener('resize', resizeHeight)
    })
    onBeforeUnmount(() => {
      window.removeEventListener('resize', resizeWidth)
      window.removeEventListener('resize', resizeHeight)
    })
    return {
      state,
    }
  },
})

template内では以下のようにして検知して値を取れます。これで横幅によって分岐ができて嬉しいかも。

<template>
  <div>
    {{state.width}}
    {{state.height}}
  </div>
</template>

もっと良い書き方あればぜひコメントください。

Nuxt.jsから始めるPWA生活

gunma.webでLTしてきました。
テーマフリーということで、PWAへの取っ掛かりの内容で話してきました。

gunmaweb.connpass.com

LT内容

  • スライド

speakerdeck.com

  • DEMOページ

elated-knuth-617fed.netlify.com

github.com

以下、LTの補足内容です。知っている人には大した情報ではないので、最後( PWA iOSつらい問題について )だけ読んで、解決策知ってる方いたら教えていただけると嬉しいです。

デモの環境について

f:id:t-pori418:20190601233835p:plain
gunmaweb-35-lt-demo
スマホでホーム画面に追加するとPWAアプリ化します。オフラインでも一度読み込むとページ遷移するので、読み込んだあと機内モードにしてみてください。

私が初めてPWAを実装を触った時(一年半くらい前?)にはもうNuxt.jsにpwaのモジュールがあり、ちらほらと記事をあげている人がいて、とっつきやすかったので、今回題材としてもNuxt.jsを使用しました。
静的ファイルをgenerateできるのも良いところで、以下コマンドでビルドしてnetlifyでホスティングしてサクッと見せられるのも良いところです。

$ yarn generate
// ディレクトリ・dist内にgenereteされるのでそれをホスティング

PWAの '実装' について

LT冒頭で、PWAは特定の実装を指すものではない、ということを少し強調して話したのですが、googleの指針があったり、lighthouseでPWA実装状況が表示されるようになってたりで、何に対応していたらPWAか、ということが具体的にわかるものがあります。
例えばMicrosoftPWABuilder を使用すればWebサイトのPWA実装状況を見ることができます。
ちなみに、デモサイトの結果は以下のような感じ。

f:id:t-pori418:20190601225828p:plain
PWABuilderGunmaWeb35DEMO

pushManagerが実装されてない旨が表示されています。

サービスワーカーの利用について

さらに踏み込んだキャッシュ戦略を行う場合、今回紹介した範囲だけでなく、直接cache APIを利用する処理やServiceWorkerとやりとりする処理を書く必要が出てくる場合あるかもしれません。
その時はworkboxも利用できる状態なので、ドキュメント見ながら頑張るって感じになると思います。
自分もチョットやったことはありますが教えられるほど詳しくない&いろいろ見てても泥臭い処理書いてる場合が多くてベストプラクティス、綺麗な書き方がわかっていない状態なので、むしろこの辺は細かく教わりたいですね......。

WebPushについて

今回のLTではあえてweb pushについてはあまり触れませんでしたが、OneSignal使うと比較的容易に実装できた気がします。お手伝いでやったけどうろ覚えです。
Nuxt.js pwaのドキュメントにも載ってるのでその通りにやるだけですが、iOSはまだ対応してないのでandroidとブラウザ/デスクトップPWAでしか通知はきません。

OneSignal Module | ⚡ Nuxt PWA

PWA iOSつらい問題について

言及している2つのポイント

  1. バージョンID情報アップデートしてもアプリが更新されない
  2. web pushがない

以外は個人的にはPWAとしてそこそこ使えるレベルにはなっていると思っています。
ただ、1の状況が直近なぜ起こっているのかわからず、androidでは正常に更新されるので、これが実装の問題じゃないのであればかなり致命的だなと。(業務アプリやる分にはOAuth情報保持されなくても画面reloadされる方がまだマシだった)
これ、だれかわかる人とか同じ状況起きたって人いたら教えてほしいです。

あとはスプラッシュが出ないとか細かいところはありますが、上記ができれば個人的にはより普及するんじゃないかと思っています。

最後に

深夜3時から4時頃にスライドの冒頭とデモ作ったので若干寒いネタが入ってるんですが、話す時はいかに寒いと思っても臆しないかが重要だということが勉強になったLTでした。(いったい何の勉強会なのか)
他の方の面白いLTも結構あったので、そのうちconnpass上に資料があがると思うので是非見てみてください。

Gunma.web #34 スキーマ駆動開発に参加しました/LTしました

gunmaweb.connpass.com

Web + DB pressで執筆された中野さんに講演をしていただき、OpenAPI Generatorめちゃめちゃ使いたいな!?となりました。

https://github.com/OpenAPITools/openapi-generator

スキーマ駆動開発、言葉としては新しいですが、思想としては今までみなさんやりたい/やっているものだと思うので、今後APIの実装は定義して、コード生成するという流れが加速していくのではないかなと感じています。

今回、前日の夜に「LTせよ」という天の声を受け取ったため、5分LTしてきました。

speakerdeck.com

ネタとしてはAWSの標準機能であるAPI Gateway Swagger インポートをしてみたという話です。
Open APIの3に対応していて、初めてインポートしてみたのでどんな感じなのか手探りでやってみたけどインポートだけなら簡単だったという話です。
実務で使っていくにはどこまでできるかの調査と、いろいろ仕組み作りは必要だなと思いましたが、可能性は感じました。
スライド内でREST Clientから実行してみている箇所がありますが、もちろんswagger-uiからの実行もうまくいきました。(なぜか最初はswagger-uiの存在が頭から抜け落ちていた)

APIはドキュメントとの乖離をなくしていく、単純なものはコードを書かずに生成するという方向で実務でも使っていけたら幸せになれるのではないかと感じた会でした。

gunma.web #34 全LT資料はこちらにアップロードされています。

gunmaweb.connpass.com

中野さんのスライド

平静を保ち、コードを生成せよ 〜 OpenAPI Generator誕生の背景と軌跡 〜 / gunmaweb34 - Speaker Deck

中野さんの講演の中で話題に出てきたと思われるスキーマの参考記事

スキーマファースト開発のススメ - onk.ninja

JAWS-UG 群馬 #2 でLTしました->AWSサーバーレスアーキテクチャでWebサイトを構築してみた

2018/12/18にJAWS-UG 群馬支部のLT会が開かれました。
テーマは「今年AWSでこれやったよ!」

jawsug-gunma.connpass.com

JAWS-UG 群馬は10月に発足したばかりなので現在手探りでいろいろやっている状況です。(私は運営メンバーではありませんが。)

発表内容

今回LTということでサーバレス関係でLTをしました。

speakerdeck.com

私の個人サイト(https://www.poriweb.com/)はnetlifyでホスティングしているのですが、別件でWebサイトを構築する件がありまして、S3でのホスティングを選びました。

この構成です↓ aws.amazon.com

この構成、比較的容易ではあるんですが、デプロイなどの環境を整えなくてはならないので、個人的にはAmplify consoleに期待しています。
あと、サーバーレス関係ないですが、EKS Tokyoはよ・・・! (追記: はよって言ってる時点でもう来てました笑)

他の方の発表内容

jawsug-gunma.connpass.com

  • AWS re:Invent 2018参加報告
    現地に行かれたそうです!
    現地ならではの雰囲気もあると思うので一回行ってみたいですね〜。

  • 5分でS3にサイトをアップする方法をやろうと思ったら90分ハマり倒した話
    WebコンソールのUIが変わったり機能が変わったり、っていうの結構あるので本を参考にやっていてもハマることがあるんですよね。 公式のドキュメントが取っつき辛いというのもあります。

  • 今年AWSでこれやったよ!
    特に気になったのが、Alexaスキルについて。
    元々興味はあってecho dot持っているんですが、なかなか重い腰が上がらなかったので、実際にやってる人の話を聞いてやりたくなりました。

最後に

発表内容にあるサーバサイドのLambdaの詳しい内容については前回の記事(AWS Lambda(python) / API Gateway / DynamoDB / SES でWebサイトのお問い合わせAPIを作る(DB保存,メール,slack連携) - poriweb)に載せています。

AWS Lambda(python) / API Gateway / DynamoDB / SES でWebサイトのお問い合わせAPIを作る(DB保存,メール,slack連携)

サーバーレスでwebサイトを構築をすることがあったので、Lambdaネタです。
ググればすぐ出てくるような話が主ですが、ユースケースが合う人の参考になれば。
作成部分は簡単にしか書いていませんので詳しくはドキュメントを参照願います。
また、pythonで書いているので他言語は読み替えていただければと思います。

LambdaとAPI Gatewayを作成する

  • Lambda関数を作成する
    AWS WebコンソールよりLambda関数を作成します。
    ロールに権限が必要になるのでDynamoDBとSESにアクセスできるようにしておきます。

  • API Gatewayを作成する
    コンソールよりAPI Gatewayに行き新しいAPIの作成を押下し、API名とエンドポイントタイプ(リージョン)を入力/選択して作成します。
    空のAPIにリソースを設定します。アクションからメソッドの作成を選択し、POSTを作ります。Lambda関数の欄に先ほど作成したLambda関数を入力して保存。

f:id:t-pori418:20181213184253p:plain
API Gateway

また、CORSは有効化しておきます。POSTメソッドを選択し、アクションからCORSの有効化を行います。

f:id:t-pori418:20181213184510p:plain
API Gateway

あとはAPIをデプロイすればAPI Gateway側の基本設定は終了で、APIを実行できるようになります。

  • 最終的にLambda側のDesinerはこんな感じ(CloudWatchとS3にも権限を与えています)

f:id:t-pori418:20181213184527p:plain
Lambda

前提:お問い合わせのPOST内容

下記のようなjsonが送られてくるとします。関数のテストイベントの中身をこれにしておきます。

{
    "item": {
        "company_name": "テスト株式会社",
        "name": "テスト名前",
        "tel": "000-0000-0000",
        "email": "hogehoge@test",
        "content": "内容"
    }
}

1. Lambdaで入力されたものをDynamoDBに保存する

まず、DaynamoDBに contacts というテーブルを作成しておきます。

lambdaのコードは元々はこんな感じ

import json

def lambda_handler(event, context):
    # TODO implement
    return {
        'statusCode': 200,
        'body': json.dumps('Hello from Lambda!')
    }

event で入力を受け取ります。APIの場合POSTされてきた中身がここに入ってきます。
POSTされてきたデータをlambdaに登録する処理を入れたコードはこんな感じ。

import json
from datetime import datetime
import boto3
from boto3.dynamodb.conditions import Key, Attr

dynamodb = boto3.resource('dynamodb')

def lambda_handler(event, context):
    item = event['item']

    # 対象テーブル
    table = dynamodb.Table('contacts')

    # 実行時間を登録しておく
    item['received_at'] = datetime.now().strftime("%Y/%m/%d %H:%M:%S")

    # テーブル登録
    table.put_item(
        Item=item
    )

    # 成功
    return {
        'statusCode': 200,
        'body': json.dumps('success!')
    }

登録された時間をテーブル内のデータとして持っておきたかったので datetime.now を使用しています。注意点としては日本時間にする場合はLambda関数の環境変数タイムゾーンを設定しておく必要がある点です。

f:id:t-pori418:20181213184552p:plain
Lambda

2. Lambdaでお問い合わせ内容をメールで管理者に知らせる

先にSESでドメインから送信できるように設定しておく必要があります。
仮に test.com というドメインを持っていて送信できる状態とします。

メール送信用に以下のような関数を用意します。

import boto3

MAIL_REGION = "us-east-1"

def send_email(source, to, subject, body):
    client = boto3.client('ses', region_name=MAIL_REGION)
 
    response = client.send_email(
        Source=source,
        Destination={
            'ToAddresses': [
                to,
            ]
        },
        Message={
            'Subject': {
                'Data': subject,
            },
            'Body': {
                'Text': {
                    'Data': body,
                }
            }
        }
    )
     
    return response

呼び出しは send_email("no-reply@test.com", "admin@test.com", "タイトル", ”本文”) といった感じです。
これを踏まえて先ほどのDB登録を行うコードに付け加えてみます。

import json
from datetime import datetime
import boto3
from boto3.dynamodb.conditions import Key, Attr

dynamodb = boto3.resource('dynamodb')

MAIL_REGION = "us-east-1"

def send_email(source, to, subject, body):
    client = boto3.client('ses', region_name=MAIL_REGION)
 
    response = client.send_email(
        Source=source,
        Destination={
            'ToAddresses': [
                to,
            ]
        },
        Message={
            'Subject': {
                'Data': subject,
            },
            'Body': {
                'Text': {
                    'Data': body,
                }
            }
        }
    )

    return response

def lambda_handler(event, context):
    item = event['item']

    # 対象テーブル
    table = dynamodb.Table('contacts')

    # 実行時間を登録しておく
    item['received_at'] = datetime.now().strftime("%Y/%m/%d %H:%M:%S")

    # テーブル登録
    table.put_item(
        Item=item
    )

    # メール送信
    title = "お問い合わせが来ました"
    message = "会社: " + item['company_name']\
                + "\n名前: " + item['name']\
                + "\nメールアドレス: " + item['email']\
                + "\n電話番号: " + item['tel']\
                + "\n内容:" + item['content']\

    src = "no-reply@test.com"
    dst = "admin@test.com"
    rsponse = send_email(src, dst, title, message)

    # 成功
    return {
        'statusCode': 200,
        'body': json.dumps('success!')
    }

3. Lambdaでお問い合わせ内容をslackにも連携する

slack連携はwebhookを使用してPOSTすれば良いので urllib.request でなげちゃいます。これだけのためにRequestsを入れるのは面倒そうだったのでurllibを使用しています。

先ほどのコードにさらに追記しちゃいます。

import json
from datetime import datetime
import boto3
from boto3.dynamodb.conditions import Key, Attr
import urllib.request

dynamodb = boto3.resource('dynamodb')

MAIL_REGION = "us-east-1"

def send_email(source, to, subject, body):
    client = boto3.client('ses', region_name=MAIL_REGION)
 
    response = client.send_email(
        Source=source,
        Destination={
            'ToAddresses': [
                to,
            ]
        },
        Message={
            'Subject': {
                'Data': subject,
            },
            'Body': {
                'Text': {
                    'Data': body,
                }
            }
        }
    )

    return response

def lambda_handler(event, context):
    item = event['item']

    # 対象テーブル
    table = dynamodb.Table('contacts')

    # 実行時間を登録しておく
    item['received_at'] = datetime.now().strftime("%Y/%m/%d %H:%M:%S")

    # テーブル登録
    table.put_item(
        Item=item
    )

    # メール送信
    title = "お問い合わせが来ました"
    message = "会社: " + item['company_name']\
                + "\n名前: " + item['name']\
                + "\nメールアドレス: " + item['email']\
                + "\n電話番号: " + item['tel']\
                + "\n内容:" + item['content']\

    src = "no-reply@test.com"
    dst = "admin@test.com"
    rsponse = send_email(src, dst, title, message)

    # slack連携
    url = 'https://hooks.slack.com/services/ slackから発行されたwebhooks url' # 自分のワークスペース,チャンネルのものに置き換えてください

    data = {
        'text': '<!here>\nお問い合わせが来ました。\n *会社* \n' + item['company_name'] + '\n *名前* \n' + item['name'] + '\n *メールアドレス* \n' + item['email'] + '\n *電話番号* \n' + item['tel'] + '\n *内容* \n' + item['content']
    }
    headers = {
        'Content-Type': 'application/json'
    }
    req = urllib.request.Request(url, json.dumps(data).encode(), headers)
    with urllib.request.urlopen(req) as res:
        body = res.read()

    # 成功
    return {
        'statusCode': 200,
        'body': json.dumps('success!')
    }

これで以下のようなメッセージが飛びます

@here
お問い合わせが来ました。
*会社*
テスト株式会社
*名前*
テスト名前
*メールアドレス*
hogehoge@test
*電話番号*
000-0000-0000
*内容*
内容

最後に

例として挙げたソースコードは各機能をくっつけただけかつエラーハンドリングまともにしてないなど修正、リファクタリングする必要はありますが、ただ実装するだけなら簡単にできました。
メールの送信実行以外は lambda_handler にほぼ処理を書いていてごちゃごちゃしてしまったのでこれから機能単位で分離していこうと思います。

また、実行しながら上記コードを書いているわけではないのでおかしいところなどありましたらご連絡していただけると助かります。