はじめてVuejsアプリケーションを本格的に作った時の参考文献履歴

Vuejsのプロジェクトテンプレートを作りたい

https://qiita.com/po3rin/items/3968f825f3c86f9c4e21

.vueファイルが意味不明すぎて怖い

https://qiita.com/po3rin/items/3968f825f3c86f9c4e21

clickなどのイベント駆動で何か処理したい

https://qiita.com/Sa2Knight/items/7e22307436a1e696729d

ループ描画したい

https://jp.vuejs.org/v2/guide/list.html

フォーム入力内容を変数にバインドしたい

https://jp.vuejs.org/v2/guide/forms.html

特定の変数が true の場合だけ
のようにクラスを付与したい

https://jp.vuejs.org/v2/guide/class-and-style.html

scssを使いたい(Sass-loader)

https://qiita.com/morocco/items/b1640cfdbe87817bd6a7

または<style lang=scss">コンパイルしたときのエラーメッセージに従ってnpm install

コンポーネント分割したい

https://jp.vuejs.org/v2/guide/components-registration.html#%E3%83%A2%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AB%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0

コンポーネントから子コンポーネントのメソッドを呼びたい

https://qiita.com/MasahiroHarada/items/80c2a60a77361f944953

WIP

【Docker】初めてdocker-composeでbuild+app runまでする構成を組もうとして挫折した

Go + VueなアプリをDockerでビルド用コンテナ+稼働用コンテナ+docker-composeするまでの過程で ハマったところをつらつらとログした記事。

世には素晴らしい日本語記事がいっぱいでているが、 今更やってみたの一例として、誰かの参考になればというモチベで進行。

環境

項目 内容
ホストOS Ubuntu 16.04
Docker 17.05.0-ce
Docker Compose 1.22.0
コンテナOS alpine 3.9
Golang 1.11.5
nodejs 10.14.2
vuecli 2.9.6

とりあえずできたやつ

https://github.com/pakuyuya/dbsql-meta-viewer/tree/master/docker

  • dep ensure + go build、vuecliで生成したテンプレートをnpm run build してパッケージング、ビルドしたコンテナ自身にデプロイして動かす簡単なスクリプト

セットアップ

Install Docker/Docker-compose to host server

https://docs.docker.com/install/ http://docs.docker.jp/compose/install.html

ビルドコンテナ

流れ

  • コンテナに必要なパッケージをインストールして環境変数もいい感じに初期化したイメージを、Dockerfileからビルドする
  • ビルドするときエントリポイントにシェルを仕込んでおき、コンテナを run したら git clone -> build のあと、コンテナ内 /var/distにビルド後のモジュールを出力。
  • コンテナをrunするときに/var/distに任意のディレクトリをマウント、ビルド結果を受け取る。

ハマり1:ARGには複数の値を指定できない

ビルドするときにパラメータガンガン渡せばいーじゃんと思ったけどなんかダメだそう。

下記は×

# Dockefile
FROM alpine:3.9

ARG REPOSITORY=https://github.com/pakuyuya/dbsql-meta-viewer.git \
        ENVTARGET=production

代替策

ハマリ2:ashで、シェルを呼んだ先のシェルを呼んだ先のシェルを呼ぶことができない

alpineの標準シェル

docker/build/Dockefile

FROM  alpine:3.9

ENV REPOSITORY=https://github.com/pakuyuya/dbsql-meta-viewer.git \
        # ... 省略 ...

ADD ./entrypoint.sh /
RUN chmod +x /entrypoint.sh

ENTRYPOINT ["/entrypoint.sh"]

docker/build/entrypoint.sh

#!/bin/sh

git clone ${REPOSITORY} /
cd /dbsql-meta-viewer

/bin/sh ./build.sh

build.sh

#!/bin/sh

/bin/sh ./client/build.sh # ここで file not found

対策として、シェルの呼び出し階層を泣く泣く削減した。

アプリケーションコンテナ

やりたかったこと

  • build コンテナを runして、完了したらappコンテナがmountしたvolumeからアプリを受け取ってrunするはずだった

ハマり3:docker-composeの依存関係解決が貧弱

Docker Composeにはコンテナの起動順序を制御するオプションがいくつかあるが、プロセス間のシグナルな同期はサポートしていない。

  • depends_on
    • 依存するコンテナのimagesからのコンテナへマウントを待ってから、自身のコンテナのマウントを始める。runが終わるのは待たない。
  • deploy関連コンフィグ
    • update-config: delay などの微妙に使えそうな設定がある。
    • docker stack deployなどを使ったswarm modeでのみ有効になるらしく、docker-compose up や run では無視されるらしい。

結局のところビルドコンテナとアプリコンテナを泣く泣く統合することで解決したが、 シグナル制御や、volumesを共有して wait for file するのがなんだかんだいって便利なのかもしれない。

(ググって見つけた)

【Docker】Dockerfile RUNで、BASHな環境変数を展開してコマンド実行する

恐らく禁じ手

ハマった事象

DockerFileを書くとき、ARGやENVな変数をコマンド内で使うときは $ENVNAME で書くが、 少なくともDocker verions 17.05.0-ceでは、Dockerfile中の$ENVNAMEプリプロセッサ的に事前に展開してコマンド実行するようだ。

FROM alpine

ARG SOMEARG=1
ENV SOMEENV=hehehe
RUN CMDENV=foo

RUN echo \"$SOMEARG\" \"$SOMEENV\" \"$CMDENV\"

実行結果

yu@yu:~/test$ docker build --rm .
Sending build context to Docker daemon  4.096kB
Step 1/5 : FROM alpine
 ---> caf27325b298
Step 2/5 : ARG SOMEARG=1
 ---> Using cache
 ---> 1efffbdfdb5c
Step 3/5 : ENV SOMEENV hehehe
 ---> Using cache
 ---> 0b585450b6db
Step 4/5 : RUN CMDENV=foo
 ---> Using cache
 ---> 1e28e889dfdf
Step 5/5 : RUN echo \"$SOMEARG\" \"$SOMEENV\" \"$CMDENV\"
 ---> Running in f3083830fbf4
"1" "hehehe" ""
 ---> 5648473c7075
Removing intermediate container f3083830fbf4
Successfully built 5648473c7075
yu@yu:~/test$

echoした結果に注目。 RUN CMDENV=foo がきいていない。

"1" "hehehe" ""

恐らく?だが、RUN echo \"$SOMEARG\" \"$SOMEENV\" \"$CMDENV\" の箇所でコマンドラインで実行されるのはecho "1" "hehehe" "" になっているからだと思う。(Dockerのコード見る気力がないのでこれ以上は勘弁)

それでも実行したい

バージョン変わるとつぶされるかもしれないが、いったんは下記でhackできた。

RUN `echo 実行したいコマンド \$環境変数名`
yu@yu:~/test$ cat
Dockerfile        env.sh            LoadConfigScript
yu@yu:~/test$ cat Dockerfile
FROM alpine

ARG SOMEARG=1
ENV SOMEENV=hehehe

RUN CMDENV=hoo \
    && `echo echo \$SOMEARG \$SOMEENV \$CMDENV` \
    && `echo mkdir \$SOMEARG \$SOMEENV \$CMDENV` \
    && `echo ls -l \$SOMEARG  \$SOMEENV  \$CMDENV`
yu@yu:~/test$
yu@yu:~/test$ docker build --rm .
Sending build context to Docker daemon  4.096kB
Step 1/4 : FROM alpine
 ---> caf27325b298
Step 2/4 : ARG SOMEARG=1
 ---> Using cache
 ---> 1efffbdfdb5c
Step 3/4 : ENV SOMEENV hehehe
 ---> Using cache
 ---> 0b585450b6db
Step 4/4 : RUN CMDENV=hoo     && `echo echo \$SOMEARG \$SOMEENV \$CMDENV`     && `echo mkdir \$SOMEARG \$SOMEENV \$CMDENV`     && `echo ls -l \$SOMEARG  \$SOMEENV  \$CMDENV`
 ---> Running in f482c9fbcd9c
1 hehehe hoo
1:
total 0

hehehe:
total 0

hoo:
total 0
 ---> f19485b7afb9
Removing intermediate container f482c9fbcd9c
Successfully built f19485b7afb9
yu@yu:~/test$

\はDockefileのエスケープ文字だが、$に対して実施してもコマンドラインで渡しても$はただの文字として機能する。 しかし、などで一度コマンド置換すると、環境変数として評価するみたいだ。

制約

DockefileのbuildはStepごとに中間結果をキャッシュし、Dockefileを修正したときキャッシュのある部分から再ビルドする。 ・・・が、上記例でいうところのRUN CMDENV=hoo環境変数が残留になるスコープは、Step実行して評価されたbuild回でのみ。 (プロセスが一旦落ちるから?)

下記のDockerFileがあったとして、RUN FOO=var のStepがキャッシュによりスキップされた場合、Step2の出力は環境変数FOOが未設定の扱いとなる。

FROM alpine

RUN FOO=var
RUN `echo echo \$FOO`

多分Dockerの中の人たちには、どうしようもないバグとして思われているんだろうなぁ。

Linuxのbashシェルスクリプト/Windows batスクリプトの別スクリプトを呼んだときのカレントディレクトリと環境変数スコープ遷移

ハマった

Windows batスクリプトの知見

  • setlocal / endlocal しない限り、環境変数のスコープが共有される

実験

カレントディレクトリを指す環境変数 CD で例にとる

構成

|`- main.bat
 `- subdir
     `- sub.bat

main.bat

@echo off
echo [main.bat] current directory: %CD%
call .\subdir\sub.bat
echo [sub.bat] current directory: %CD%

sub.bat

@echo off
echo [sub.bat] current directory: %CD%

rem sub.batがいるディレクトリまで移動
cd %~dp0

echo [sub.bat] current directory: %CD%

実行結果

E:\work\example>main.bat
[main.bat] current directory: E:\_work\example
[sub.bat] current directory: E:\_work\example
[sub.bat] current directory: E:\_work\example\subdir
[main.bat] current directory: E:\_work\example\subdir

E:\work\example\subdir>

子供のbatでcdした結果はコマンドプロンプト含む呼び出し元にも影響。

Windows batで元のCDを残す

【1: setlocal / endlocalで環境変数のローカルスコープをリセット】

sub.bat

@echo off
setlocal
echo [sub.bat] current directory: %CD%

rem sub.batがいるディレクトリまで移動
cd %~dp0

echo [sub.bat] current directory: %CD%
endlocal

実行結果

E:\work\example>main.bat
[main.bat] current directory: E:\_work\example
[sub.bat] current directory: E:\_work\example
[sub.bat] current directory: E:\_work\example\subdir
[main.bat] current directory: E:\_work\example

E:\work\example>

【2: CDを記憶し帰り際にCDしなおす】

sub.bat

@echo off
echo [sub.bat] current directory: %CD%
set PARENT_CD=%CD%

rem sub.batがいるディレクトリまで移動
cd %~dp0

echo [sub.bat] current directory: %CD%

cd %PARENT_CD%

実行結果

E:\work\example>main.bat
[main.bat] current directory: E:\_work\example
[sub.bat] current directory: E:\_work\example
[sub.bat] current directory: E:\_work\example\subdir
[main.bat] current directory: E:\_work\example

E:\work\example>

Linux bashシェルスクリプトの知見

  • /bin/bashで別プロセスで コールされる(別プロセスが立ち上がる)ときは親プロセスのスコープを引き継ぎ、プロセスを戻るときに環境変数破棄
  • .source で呼び出されたら親プロセス内で処理されたことになり、共有スコープとなる(戻っても変更内容引継ぎ)

実験

ディレクトリ構成

|`- main.sh
 `- subdir
     `-sub.sh

main.sh

#!/bin/bash
echo [main]pwd: `pwd`
echo

cd ~/test
/bin/bash ./subdir/sub.sh
echo [main]pwd: `pwd`
echo

cd ~/test
. subdir/sub.sh
echo [main]pwd: `pwd`
echo

cd ~/test
source subdir/sub.sh
echo [main]pwd: `pwd`

sub.sh

#!/bin/bash
echo [sub]pwd: `pwd`
cd ~/test/subdir
echo [sub]pwd: `pwd`

実行結果

yu@yu:~/test$ ./main.sh
[main]pwd: /home/yu/test

[sub]pwd: /home/yu/test
[sub]pwd: /home/yu/test/subdir
[main]pwd: /home/yu/test

[sub]pwd: /home/yu/test
[sub]pwd: /home/yu/test/subdir
[main]pwd: /home/yu/test/subdir

[sub]pwd: /home/yu/test
[sub]pwd: /home/yu/test/subdir
[main]pwd: /home/yu/test/subdir

/bin/bashで蹴られたときだけcdしたものが自動で破棄。

また、$0 で見えるキックされたスクリプト名も、プロセスを共有していると親のものが参照される。

【vuecli】src配下にapplication configファイルを追加する

ハマった。

環境

$ npm list -g vue-cli

E:\node_modules
+-- @vue/cli-init@3.0.0-rc.10
| `-- vue-cli@2.9.6
`-- vue-cli@2.9.6

$ npm -version
6.4.1

やりたいこと

  • vue cli を使ってテンプレート生成したプロジェクトに、vue用の application コンフィグファイルを作ってRestAPIの接続先サーバーを記述し、アプリケーションで利用したい。
  • npm run から起動するビルドスクリプトなどで、NODE_ENVがdevelopmentならdevlopment.jsを読み込み、productionならproduction.jsを読み込みたい。

構成は以下のとおり

vueproject
 |`- .babelrc
 |`- .editorconfig
 |`- .eslintignore
 |`- .eslintrc.js
 |`- .gitignore
 |`- .postcssrc.js
 |`- build
 |`- config
 |`- dist
 |`- index.html
 |`- node_modules
 |`- package-lock.json
 |`- package.json
 |`- README.md
 |`- src
 |    |`- config
 |    |    |`- development.js   ←
 |    |     `- production.js    ←
 |     `- componentsとか色々なやつ。この中のコードからconfigを参照
 |`- static
  `- test

設定追加に至るまで

結果的にWebpackにalias を設定する方法を採用。

qiita.com qiita.com

webpack.base.conf.js

build/webpack.base.confに下記を追記

module.exports = {
  // ...
  resolve: {
    extensions: ['.js', '.vue', '.json'],
    alias: {
      'vue$': 'vue/dist/vue.esm.js',
      '@': resolve('src')
    }
  },
  // ...
}

↓

module.exports = {
  // ...
  resolve: {
    extensions: ['.js', '.vue', '.json'],
    alias: {
      'vue$': 'vue/dist/vue.esm.js',
      '@': resolve('src'),
      'config': resolve(`src/config/${process.env.NODE_ENV}.js`) // 追記
    }
  },
  // ...
}

npm run devnpm run build 時に展開されてwebpackに渡される寸法。

npm run devだけだとでNODE_ENV未設定で解決

どうも vuecli で生成したスクリプトでは、npm run dev はNODE_ENVにdevelopmentが自動設定される前にwebpackをrunしてしまうらしい。

事前に環境変数 NODE_ENV にdevelopment を設定しておくだけでも解決するが、 自前用のコードであったしものぐさなので、webpack.dev.conf.jsの下記にハードコードで上書きするようにした。

const devWebpackConfig = merge(baseWebpackConfig, {
  // 下記追記
  resolve: {
    alias: {
      config: resolve(`src/config/development.js`)
    }
  },
  // ...
})

これで、下記コードでコンフィグが参照できるようになる。

development.js

export default {
  apiurl: '{protocol}//{host}/api'
}

main.js

import Vue from 'vue'
import App from './App'
import router from './router'
import config from 'config' // これ
import services from './services/entry'

Vue.config.productionTip = false

const configMixin = Vue.mixin({
  created: function () {
    this.$config = config

    for (const key in services) {
      var tagservice = '$' + key
      if (this[tagservice]) {
        console.error(`module name conflicted with $vue.${tagservice}! we take overwrite to ./services/modules.`)
      }
      this['$' + key] = services[key]
    }
  }
})

/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  configMixin,
  components: { App },
  template: '<App/>'
})

【Golang】CSVのDecode

[][]string へのDecodeで我慢できるのであれば、encoding/csv パッケージがそれなりに使える。

  • ファイル終端にいくとEOFする
  • 引用符ありなしを自動解釈
  • 引用符中のダブルクォート2つ重ねを1つにしてくれる
  • 引用符中なら改行文字を何いれてもいい
  • \はエスケープ文字としてみなさない

Decode

package textdata

import (
    "encoding/csv"
    "fmt"
    "io"
    "time"
)

func DecodeCSV(r io.Reader) ([]Textdata, error) {
    rc := csv.NewReader(r)

    datas := make([]Textdata, 0)

    i := 0
    for {
        row, err := rc.Read()
        i = i + 1
        if err != nil {
            if err == io.EOF {
                break
            }
            return nil, err
        }
        if len(row) < 2 {
            return nil, fmt.Errorf("invalid row. too less columns num. [position]: row %d [data]: %s", i, row)
        }
        data := Textdata{}
        data.Caption = row[0]
        data.Body = row[1]
        datas = append(datas, data)
    }

    return datas, nil
}

test

package textdata

import (
    "strings"
    "testing"
)

// Search is a function search textdata in shared repository
func TestDecodeCSV(t *testing.T) {
    gotest := func(csv string, expect []Textdata) {
        r := strings.NewReader(csv)
        datas, err := DecodeCSV(r)
        if err != nil {
            msg := "error occured"
            t.Error(msg)
        }

        if len(expect) != len(datas) {
            t.Errorf("expect %d records, but returns %d.\r\n[expect]:%s\r\n[result]:%s",
                len(expect), len(datas), expect, datas)
            return
        }
    }

    genExpect := func(caption string, body string) Textdata {
        return Textdata{Caption: caption, Body: body, CreateAt: ""}
    }

    gotest(`a,b`, []Textdata{genExpect("a", "b")})
    gotest(`"a",b`, []Textdata{genExpect("a", "b")})
    gotest(`a,"b"`, []Textdata{genExpect("a", "b")})
    gotest(`"""a""","b"`, []Textdata{genExpect("\"a\"", "b")})
    gotest(`"""a\r\nb\rc\nd""","b"`, []Textdata{genExpect("\"a\r\nb\rc\nd\"", "b")})
}

【Postgresql】マテリアライズドビュー

マテリアライズドビューとは

  • 実行結果をテーブルと同じようにキャッシュして、普通のビューなら選択などのコストを欠けて実行するものの既に計算済みとしてデータを読むだけの状態にする機能。
  • インデックスなどが張れる
  • 一度キャッシュしたら、`REFLESH

マテビューを作る

CREATE MATERIALIZED VIEW mate_view AS
SELECT
  dept.dept_id, dept.dept_name, emp.emp_id, emp.emp_name
FROM
  dept
  INNER JOIN emp USING (dept_id)
;

マテビューをリフレッシュする

手動実行

www.postgresql.jp

-- 占有ロックあり
REFRESH MATERIALIZED VIEW mate_view;

-- 占有ロックなし、更新/参照されたら都度都度リフレッシュとまる
REFRESH MATERIALIZED VIEW CONCURRENTLY mate_view;

Oracleには自動更新するオプションはあるが、Postgresql10.x現在そういうのはないらしい。