くりーむわーかー

プログラムとか。作ってて ・試しててハマった事など。誰かのお役に立てば幸いかと。 その他、いろいろエトセトラ。。。

プログラム

Nuxtの開発環境をDockerで

DockerでNuxtのローカルの開発環境作りたくなった。

やってみたのですが、なかなか思い通りにいかなかったので記載。

やりたい事としては、nodeとかnode_modulesとかはdocker側において、ソースはローカル。

で、ローカルのソースを修正したら、nuxtというかVueのホットリロードを動かしたい。

ひとまずソースの構成。

.
├── app
│   ├── Dockerfile
│   └── webapp # nuxtのアプリ
└── docker-compose.yml

nuxtのアプリを作る

cd app
npx create-nuxt-app webapp # 質問は適当に動けばとりあえず。。。

で、ここでは「npm install」はしない。やった場合は後述。

Dockerfile

FROM node:14.1.0-alpine

WORKDIR /app

# 多分、nuxt動かすだけなら、@vue/cliとか@vue/cli-initはいらないと思う。
RUN apk update && \
    npm install -g npm && \
    npm install -g @vue/cli && \
    npm install -g @vue/cli-init && \
    npm install -g create-nuxt-app

ENV HOST 0.0.0.0

CMD ["npm", "run", "dev"]

Docker-compse

version: '3'
services:
  webapp:
    build: ./app
    ports:
      - 13000:3000
    volumes:
      - ./app/webapp:/app
    tty: true

ホスト側のPortは適当に。tty:trueはあっても無くてもどっちでも。docker内のコンソール出力を全部見たいのでひとまず付ける。

そしたら「docker-compose build」

build終わったら下記でコンテナ内で「npm install」する。

docker-compose run webapp ash
npm install
exit

そしたら「docker-compose up」

これで、上記のポートの割り当てだったら「http://localhost:13000」でnuxtのデモ画面が出る。

そのまま、ローカルのファイルを修正すればDocker側にも反映されてホットリロードされると。

全ソースはここに上げてます


で、ここから出来なかった事。

本当はbuildのところもdockerfile側に置きたい。↓の感じ。

COPY ./webapp/package.json /app/package.json
COPY ./webapp/package-lock.json /app/package-lock.json
RUN npm install

↑は上手くいくのですが、nuxtの実行をすると、「.nuxt」ってディレクトリ作られるじゃないですか?

こいつがかなりやっかいで、実行時に動的に作ってるらしく、ローカルのディレクトリと共有してると権限がおかしくなって動かなくなるのです。

dockerignoreとかマウントしてからvolumeで部分的に上書きして~とかローカルのuidと合わせて~とか色々やってみたのですが、どうにも上手くいかない。

今のところ上手くいった方法が上述のnpm installはbuild後にupする前にコンテナ内で実行しておくという手順。

んーカッコ悪いんだけど、開発環境としてローカル側にソース置きたい場合はこれしかやりようがなさそう。


途中でnpm installとかnpm run devしちゃって「node_modules」「.nuxt」出来ちゃってる場合はコンテナ内に入る前に全部消す。

sudo rm -rf .nuxt
sudo rm -rf node_modules

CSS 直前の要素のセレクタ & beforeとafterで一言

とある要素の直前の要素をCSSセレクタで指定したくなった。

<div>hoge</div> ←これを選択したい
<div class="fuga">fuga</div> ←ここから見て
<div>moge</div>

結論から言うとCSSセレクタではこれは無理っぽい。

兄弟要素(階層が同じ要素)の弟(後に出てくる要素)は選択できるんだけど、

兄(前に出てくる要素)を選択するセレクタはないらしい。。。

DOMの構造見直すか、JavaScript使ってどうにかするしかあるまい。

jQueryなら「.prev()」とかでいけるかな?jQuery使ってないけど。


で、ここから調べてる中で「コレ間違ってるんじゃないかな?」って思った話。

「CSS 直前の要素」でググって出てくるサイトは一杯あるのですが、

疑似要素のbeforeとafterでちょいちょいヒットしてる。

見てみると、beforeの説明でこんな内容になってる。

:before 要素の直前

とか

before / afterでそれぞれ要素の前後に[擬似要素]を追加することができます。

ん?って感じ。

↑の説明だと、構造的に下記をイメージしますよね?

<::before />
<div />
<::after />

要素の直前だからね。実際↑書いてるサイトの構造のイメージはそうなってた。

でも、MDNにはこう書いてある

In CSS, ::before creates a pseudo-element that is the first child of the selected element.

CSS における ::before は、選択した要素の最初の子要素として擬似要素を作成します。

子要素として 擬似要素を作成します

ですよね。

chromeあたりの開発ツールで見ると分かるのですが、どう見ても指定してる要素の

子要素として作ってるよね?

構造的には下記。


<div class="hoge">
  <.hoge::before />
  <something.... />
  <.hoge::after />
</div>

直前の要素って説明は間違ってると思うのです。

書くなら「before:直下の最初の子要素」とか「after:直下の最後の子要素」が正確な表現かしら?

.net バージョンの確認

公式にはいちをこんなページがあるのでそれを見た方がよいかも。

昔、MSDNにこのコマンドファイル載ってたのですが、そのページ見つけられなくなった。。。

いちを↑のコマンドファイル実行でインストールされてるバージョンの確認はできる。

で、実行PG側から今動いてるフレームワークのバージョンが何かを見たい場合は下記。

string clrVersionBuildtime = System.Reflection.Assembly.GetExecutingAssembly().ImageRuntimeVersion;
string clrVersion = System.Environment.Version.ToString();
Console.WriteLine(String.Format("ビルドバージョン:{0}", clrVersionBuildtime));
Console.WriteLine(String.Format("動作時バージョン:{0}", clrVersion));

v-calendarのdate-pickerで外から表示する月を変える

Vueのコンポーネントで、v-calendarっていうやつがある。

相当色んな事が出来て、今まで見てきたカレンダー的モジュールの中では一番じゃないだろうかと個人的に思ってる。

中のソースもめっちゃ勉強になる。

で、これのDatePickerを使う場合に、外から日付を渡して表示する場所を変えたかったりする。

一筋縄でいかなかったのでちょっと記載。


多分普通に使う分には↓の感じ。

<vc-date-picker v-model="dateval" />

適当なdataをv-modelで指定してあげれば、それに応じた日付にはしてくれると思う。

ただ、このdatevalを外で変えた場合に、カレンダーのページがその日付の月になってない。

これをやるのに、shoPageRangeっていうの呼ぶらしい。公式のドキュメントにもそんな感じで書いてある。

これのissue

まー上手くいかないと。popoverの場合は上手くいかない。is-inlineだとうまくいくっぽい。

どうもpopoverの場合、$refs.calendarの$refsがcalendarじゃなくてpopoverになってるみたいですね。

で、ソース色々見てたら、少なくとも最新のバージョンではどうも違うっぽい。正解はこっちだと思う。

<vc-date-picker v-model="dateval" ref="calendar"/>

methods:{
  changePage(){
    this.dateval = "何かの日付"
    this.$refs.calendar.adjustPageRange()//コッチ
  }
}

datepicker使う場合は少なくとも「adjustPageRange」っぽいですね。

内部的なカレンダーの日付のページにこれで合わせてくれる模様。

コード的にはここ

あと、カレンダーが非表示の状態だと動かないっぽい?

なので、最終的にはこんな感じにしておく必要がありそう。

<vc-date-picker v-model="dateval" ref="calendar" @popoverDidShow="handleShow"/>

methods:{
  changePage(){
    this.dateval = "何かの日付"
  },
  handleShow(){
    カレンダーが表示されたときに呼ぶ
    this.$refs.calendar.adjustPageRange()
  }
}

Python import thisの裏

import thisは色んな人が解説書いてるので少し違った個人の視点をば。

import thisのソースってこんな感じ。

s = """Gur Mra bs Clguba, ol Gvz Crgref

Ornhgvshy vf orggre guna htyl.
Rkcyvpvg vf orggre guna vzcyvpvg.
Fvzcyr vf orggre guna pbzcyrk.
Pbzcyrk vf orggre guna pbzcyvpngrq.
Syng vf orggre guna arfgrq.
Fcnefr vf orggre guna qrafr.
Ernqnovyvgl pbhagf.
Fcrpvny pnfrf nera'g fcrpvny rabhtu gb oernx gur ehyrf.
Nygubhtu cenpgvpnyvgl orngf chevgl.
Reebef fubhyq arire cnff fvyragyl.
Hayrff rkcyvpvgyl fvyraprq.
Va gur snpr bs nzovthvgl, ershfr gur grzcgngvba gb thrff.
Gurer fubhyq or bar-- naq cersrenoyl bayl bar --boivbhf jnl gb qb vg.
Nygubhtu gung jnl znl abg or boivbhf ng svefg hayrff lbh'er Qhgpu.
Abj vf orggre guna arire.
Nygubhtu arire vf bsgra orggre guna *evtug* abj.
Vs gur vzcyrzragngvba vf uneq gb rkcynva, vg'f n onq vqrn.
Vs gur vzcyrzragngvba vf rnfl gb rkcynva, vg znl or n tbbq vqrn.
Anzrfcnprf ner bar ubaxvat terng vqrn -- yrg'f qb zber bs gubfr!"""

d = {}
for c in (65, 97):
    for i in range(26):
        d[chr(i+c)] = chr((i+13) % 26 + c)

print("".join([d.get(c, c) for c in s]))

文字列表示してるだけなのですが、カエサル式(シーザー式)暗号っていうのやってます。

65がasciiコードでいう「A」で97が「a」。でそれぞれ26文字分繰り返して13文字分ずらした文字に直して出力。みたいな。


こういうの見ると「遊び心」があるー。って感じするじゃん?

何か洒落てる感じするじゃないですか。

「遊び心」が大事だよねって思うじゃないですか。

で、遊ぶってすごいなと思うわけです。

例えば将棋とかチェスとか、野球とかサッカーとかバスケみたいなスポーツでもいいし、大工さんだったりF1だったり、別にゲームでもいい、例えば地球防衛軍とかバイオハザードとか。

何でもそうだと思うのですが、本気で遊べるためには、その何かに精通してなきゃ無理だと思うんですよ。

それなりに精通してないと遊ぶ余裕が全くないし、動かすだけで精一杯。

遊び心が出せるって事はそれに精通してるって事。

そのくらい知識とか経験とかちゃんと身に着けなよって言われた気がしたのです。

まー気のせいだ。。。

Pythonのクラス変数を定数(再代入禁止の変数)として扱う - 名前空間を分けたい

Pythonには他の言語でいう定数がない。

慣習的に定数扱いにしたい場合は大文字で書くみたいなのはある。

FOO = 100

みたいな。。。

ちんまりしたアプリだったら別に上記のくらいのルールでもいいかもだけど、

そこそこ大きいアプリになって色んな開発者が長期に渡って触る可能性がある場合。

定数を定数として扱わなくなる危険性があるので、ちゃんと再代入は禁止できるようにしたい。

で、これをやるのによく見るのって下の感じ。

# 「const.py」ってファイルで作成

class _const:
    def __setattr__(self, name, value):
        if name in self.__dict__:
            # 定義されたら例外出す
            raise TypeError(f'Can\'t rebind const ({name})')
        self.__dict__[name] = value

import sys
sys.modules["const"]=_const()

# ↑を作ったら↓

import const

const.FOO = 'hoge'
const.FOO = 'fuga' # 例外でる

これで再代入を禁止した定数っぽいものが作れると。参考元


ただ、これって「const」クラスの「インスタンス変数」として突っ込んでるだけ。ってそれがダメなわけじゃないですが、大量に定義したい時なんかはちょっと・・・。

あと、試してないのですが、名前空間一つになっちゃうと思うので、色んなPGで使うと変数名かぶったりした時にやられたりしないのかしら?

という事で、定数をある程度まとまった役割(名前空間)で分けたい場合に例えば、、、

class Moge:
   FOO = 100

# ↑こういう風にまとめて、
# ↓こういう風に使いたい

do_something(Moge.FOO)

って感じにしたいのですが、前述のやり方だと

class Moge:
   const.FOO = 100

みたいになっちゃうので無理。

何かやり方ないかなと思ってたら、PythonのClass定義は、それ自体もインスタンスだったなと思い出しまして、こんな風にしてみたら意外と出来た。


# クラス定義そのものに対してのsetter制御用メタクラス
class ConstMeta(type):
    def __setattr__(self, name, value):
        if name in self.__dict__:
            raise TypeError(f'Can\'t rebind const ({name})')
        else:
            self.__setattr__(name, value)

# 定数定義
class MyConstClass(metaclass=ConstMeta):
    X = 10
    Y = 20

MyConstClass.X # 10
MyConstClass.X = 30 # 例外
MyConstClass.Z = 40 # これは別の例外

これで定数を役割なんかでまとめた分類が出来るという寸法です。

Metaクラスってクラス定義に対しての特殊メソッドを定義出来るので、

↑の感じでクラス変数(属性)へのsetterをねじ込んだ感じ。

ただ、この場合は本当に定数だけのクラスじゃないとダメだと思う。

StoryBookのVisualTest(StoryShotsとStorycap)

以前書いたやつの続き。

StoryBookでビジュアルリグレッションテストしたいときの構成。

前のはURL単位にテストファイルを作っていたのですが、一括で全URL対象にした方が楽。

storyshotsを使うかscreencapを使うか。

両方やってみてscreencapを使うようにしたのでその辺の話。


StoryShots

GitHubのReadmeを見れば導入はそのままでOK。

で、実際に使う時に設定したこと。

除外したいstoryの指定

実際のコンポーネントを考えると、ずっとアニメーションしてるものの場合、同じスクリーンショットとるとか不可能なので、テストを外したい。Readmeにも書いてあるけどその場合は↓。

import initStoryshots from '@storybook/addon-storyshots';

initStoryshots({
  storyKindRegex:/^((?!.*?DontTest).)*$/
});
スナップショット取る前にちょっと待機させたい場合

開いた瞬間アニメーションしてるやつが落ち着くまで待機させたい場合。

import initStoryshots from '@storybook/addon-storyshots'
import { imageSnapshot } from '@storybook/addon-storyshots-puppeteer'


const beforeScreenshot = (page, { context: { kind, story }, url }) => {
  return new Promise(
    (resolve) =>
      setTimeout(() => {
        resolve()
      }, 1000) // waitのミリ秒
  )
}

initStoryshots({
  suite: 'Image',
  storyKindRegex: /^(?!.*?nvis).*$/,
  test: imageSnapshot({
    storybookUrl: 'http://localhost:9009',
    beforeScreenshot,
  }),
})

で、見逃してるだけのような気もするのですが、story毎に待機時間を決められないように思うので、待機時間つけちゃうと全部遅くなっちゃう。。。

あと、除外する名前の正規表現ってURLのところになってると思うのですが、どうにも上手くいかない。名称の見方がなんか違うっぽい。どう書けばいいんだろうか。正規表現的に色々試してみたのですが、どうもやりたい事が出来ない。


StoryCap

GitHubのリポジトリ。

で、StoryShotsの代わりにこれを使うようにした。スターの数だと圧倒的にStoryShotsなんだけど、

こっちの方が望む結果にすぐたどり着いたので。導入もすごく楽。

動き的にHTMLの変化を監視して、画面のアニメーションが落ち着くのを待ってからスクリーンショットを撮ってるぽい。賢い。

もちろんずっとアニメーションしてるコンポーネントは↑のWaitのタイムアウトでエラーになるけど。

で、そういうコンポーネントを除外したい場合は↓の感じで実行コマンドに渡す。

storycap http://localhost:9009 -e "**/DontTest/**"

ルールとしてstorybookに表示されるサブディレクトリの階層で、DontTestとか階層を作って、テスト除外したい奴はそこに配置する感じ。ちょっとカッコ悪いけど、urlをminimatchしてるのかな?

ディレクトリ階層で切り分けないとうまくいかなかった。

ただし、StoryCapはスクリーンショットを撮るだけでテストまでは入ってないので、

そこは自分で書いておかないとダメ。ロードマップ見ると将来的には対応してくれるようす。

テストは↓の感じで書けばよいのではないかと。

const fs = require('fs')

function getFiles(dir, files_) {
  files_ = files_ || []
  const files = fs.readdirSync(dir)
  for (const i in files) {
    const name = dir + '/' + files[i]
    if (fs.statSync(name).isDirectory()) {
      getFiles(name, files_)
    } else {
      files_.push(name)
    }
  }
  return files_
}

const files = getFiles('storycapで画像が出来るディレクトリ')

describe.each(files)('ビジュアルテスト', (filepath) => {
  test(`file: ${filepath}`, () => {
    const data = fs.readFileSync(filepath)
    expect(data).toMatchImageSnapshot({
      customSnapshotsDir: 'テスト用のスナップショット周りが出来るディレクトリ',
      failureThreshold: 0.10,
      failureThresholdType: 'percent',
    })
  })
})

CentOS7 yum updateでエラー

久しぶりに更新しようかと思ったら、変なエラーにあったので記載。

Failing package is: *****.el7.ius.x86_64
GPG Keys are configured as: file:///etc/pki/rpm-gpg/IUS-COMMUNITY-GPG-KEY

↑のエラーで落ちる。鍵がおかしいみたいな感じ?

で、これの対応は↓。

sudo yum install \
https://repo.ius.io/ius-release-el7.rpm \
https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm

公式に書いてある通りに実行。

いちをこれでupdate通るようにはなった。。。

WebPackでライブラリをUMDにした時のエラー

またWebPackでハマった。もーこれ疲れますね。。。

output:{
  library: 'hoge',
  libraryTarget: 'umd',
}

って、設定したライブラリ用の設定でビルドした奴を

nodeでrequireすると下のエラーが出る。

ReferenceError: window is not defined

で、これを回避するのに↓にした方が良いってさ。

output:{
  library: 'hoge',
  libraryTarget: 'umd',
  globalObject: 'this'  <---これ
}

↑の設定でビルドして出来上がる奴のwindowのところがthisになる。

うーん。webpackは素晴らしいとは思うのですが、

もうちょっとこう何かなーって疲れません?

参考:

https://github.com/webpack/webpack/issues/6522

Vue Render使う時に引数有のClickイベントを定義したい

VueでClickイベントのハンドラを書く時は大体↓の感じでかく。

<button v-on:click="handleClick">click me</button>

methods:{
  handleClick: function(arg){
    this.count++
    this.args = arg
  },
}

で、引数に何か渡したい場合は↓

<button v-on:click="handleClick(1, $event)">click me</button>

methods:{
  handleClick: function(arg, event){
    this.count++
    this.args = arg
  },
}

通常のeventオブジェクトも↑の感じで渡せる。

で、これをRender使ったときにどう書くかちょっとハマったので記載。

調べても出てこないのよね。みんなこの程度の事にはハマってすらいないんだろうなーと

ちょっと寂しい気持ちになりました。頭悪くてごめんなさい。

で、renderで書く場合、onのハンドラは下の感じでcreateElementに渡す。

createElement('button', {on: {click: handleClick}}, 'ボタン')

普通に引数付きで書くとこう書いちゃう。

createElement('button', {on: {click: handleClick(1)}}, 'ボタン')

コレだとダメ。

オブジェクトはあくまで関数を渡さないといけないので、↑だと関数を呼んじゃってる。

なので、下の感じにする。

createElement('button', {on: {click: (x) => this.handleClick(1, x)}}, '引数とイベント有')

ラムダ式の引数がイベントオブジェクトになってるのでそれをそのまま渡すと。

ハマったハマった。。。

サンプルは↓。

問合せ