Vue componentのevent handlerのテストはwrapper#triggerから

単純なメモ。

<template>
  <container>
    <button @click="handler">押して</button>
  </container>
</template>

<script>
export default {
  methods: {
    handler() {
      // これをテストから直接呼ぶとrender済んでない可能性大
    }
  }
}
</script>

こんな Vue component があるとする。

端折りまくったテストコードはこんな感じに書けるんだけど、ここで handler() を直接呼んではいけない。

import Komponent from 'komponent.vue'
..

describe('Komponent', () => {
  var wrapper

  function mountComponent(component) {
    return mount(component, {localVue, router, ..})
  }

  describe('condition', () => {
    beforeEach(() => {
      wrapper = mountComponent(Komponent)
    })

    it('wrong example', () => {
      wrapper.vm.handler()
    })

    it('good example', () => {
      wrapper.find('button').trigger('click')
    })
  })
})

どうも直接 handler() を呼ぶと component が render される前に handler() が呼ばれてしまうらしく、少なくとも DOM 上のデータは取得できない。

単純なロジックだけのメソッドならこれでもテスト可能だが、event handler としてのテストをする場合はちゃんと event から呼んであげるのがよいらしい。

で、event を起こすには、まず event を起こしたい要素を特定してあげる。wrapper.find() で特定してあげると DOM wrapper のようなオブジェクトが取得できるので、そいつで event を trigger する1。すると実際に Vue component が render されているような状態で handler() を動作させることができる。

※ ちなみに render という function も export されているのだが、これは @vue/test-utils ではなく @vue/server-test-utils に含まれるものであり、component のテストではなく、render された HTML に対するテストしかできない。mount や shallow の代わりには使えない。

  1. @vue/test-utils を使ったテストはとにかく wrapper を相手にする 

Dragonfly attachmentを持つレコードをコピーする場合には注意が必要

Dragonfly については以前紹介した通りで、普段触っているアプリではこれを使っている。表示のタイミングで変換が走るのは高負荷のサイトには向かないかもしれないけど、使い勝手には満足している。

で、今回はこの Dragonfly を使っている場合に気をつける必要のあるユースケースを見つけたのでまとめておこうと思う。

ActiveRecord + Dragonflyでの画像の削除のタイミング

Paperclip もそうかもしれないけど、Dragonfly はよくできていて

  • レコードの削除
  • レコード内の Attachment の変更

のタイミングで Attach していたファイルをちゃんと掃除してくれる。これによって無駄なファイルが残ってしまうといったことがなくなる。

そしてこの処理は ActiveRecord で言うと

before_save

のタイミングで実行される。

削除時にはファイルの参照はチェックしないので自前で

今回ハマったのは

  • コピーしたレコードで
  • 画像を変更したら
  • 他のレコードも参照していたファイルが消えてしまった!

というものです。

上の説明を読んでいればとても当たり前の話なんだけど、まぁそこはそれ。回避するには以下のいずれかの方法がありそう。

  1. 参照カウンタよろしく、同一の uid を持つレコードが before_save の段階で 2以上あったら削除を無効化
  2. コピーの段階で実体のファイルもコピー

今回使ったのは 1 の方法。

削除を無効化する具体的な方法

※ 以下のコードは dragonfly 0.9.12 のものであり、また回避用のコードは実際に動いているものとよく似ているけれど動作検証はしていません。

dragonfly の画像の削除は知らない間に行われるので、callback で処理しているに違いない。ということで、before_* で検索してみる。

すると

lib/dragonfly/active_model_extensions/class_methods.rb

           before_save :save_dragonfly_attachments
           before_destroy :destroy_dragonfly_attachments

こんな記述が見つかる。探すと今度は

lib/dragonfly/active_model_extensions/instance_methods.rb

     def save_dragonfly_attachments
       dragonfly_attachments.each do |attribute, attachment|
         attachment.save!
       end
     end

     def destroy_dragonfly_attachments
       dragonfly_attachments.each do |attribute, attachment|
         attachment.destroy!
       end
     end

という記述が見つかる。これはそれぞれ

lib/dragonfly/active_model_extensions/attachment.rb

の中にある

     def destroy!
       destroy_previous!
       destroy_content(uid) if uid
     end

     def save!
       sync_with_model
       store_job! if job && !uid
       destroy_previous!
       self.changed = false
       self.retained = false
     end

のようだ。destroy! は期待通りに動いているので、save! でも同様に呼び出されている

     def destroy_previous!
       if previous_uid
         destroy_content(previous_uid)
         self.previous_uid = nil
       end
     end

があやしい。そこでこんなことをしてみた。

class Photo < ActiveRecord::Base

  image_accessor :image

  after_validation do
    if self.image_uid_was and
       self.class.where(:image_uid => self.image_uid_was).count > 1
      self.image.instance_eval {
        def destroy_previous!
          ;
        end
      }
    end
  end

end
  • 例として Photo という Model を用意し
  • その中の image という attribute に画像を保存
  • ファイルを変更しようとしており(photo_uid_was が nil でなかったら)、同じ uid を参照しているものが他に存在していたら
  • destroy_previous! を無効化

している。

今まさに保存しようとしているインスタンスだけ destroy_previous! の中身を空にしてしまう黒魔術。

すでに入っている before_save の callback の前にこれを追加することができるならそれでいいと思う。やり方が分からなかったので、before_save よりも前に呼ばれる after_validation に突っ込んだ。

after_validation から呼ぶなら before_save そのものを skip するという手も使えそうだけど、他に callback をセットしてるとそれもややこしいので、いちばん影響なさそうで手っ取り早い黒魔術で片付けることにした。

これでコピーしたレコードの画像を差し替えても他のレコードでロストしない。

hgでgit log --name-onlyのようなもの

git に慣れた身からしてちょっとつらいのは

git log --name-only

相当のものが hg にないこと。

templateで解決

と思っていたけど、以下のようにするとどうにかなります。

hg log --stat --template '{file}\n'

see

hg help log
hg help template

マニュアルに載っているのは files なんだけど、'{file}\n' の方が後処理が楽です。

styleに保存もできる

上の

{file}\n

をファイルに保存しておけば

hg log --stat --style FILENAME

で呼び出すことができるそうです。ということは

hg log --stat --style name-only

のようにも書けるわけです。

ただし、どこから探すのかは hgbook を読んでも書いてないので、自分で試すかソース読むかしてください。

..hgrc に書けるのがいちばん便利だと思うんですが、そういう機能は今のところないっぽいです。

参考

Chapter 11. Customizing the output of Mercurial

Fastladder 試してみた

Fastladder : RSS / Atom feed reader

なぜなら livedoor ID をあまり取りたくなかった&ほしい ID はどうせ取れなかったので livedoor Reader は使ったことなかったのです。

で、初めて livedoor Reader を1使ってみた感想は、

  • 確かに閲覧は速い
    • でもあのアイテムを移動する時の「うにょん」ていう効果がなくて寂しい
  • うっかりフィードを開いてしまっても新着アイテムがいきなりなくなってしまわないのが嬉しい(Reload するまでは既読にマークされても開くことができるのかな?)
  • ハイライトがちょっとまぶしい
  • 閲覧中のフィードに対して share の状態の変更がすごくやりにくい
    • 基本 public でほとんどいじる必要ないっちゃないんですが
  • フィードリストの方の文字のサイズが変わらないのでバランスが悪くなってしまう
  • フィードリストの幅って変更できないの?

といった感じ。bloglines を使っているので基本的に bloglines との比較になります。

見た目については最終的には user css が炸裂すればなんとでもなるんだけど、フィード管理の部分はちょっとどうにかしてほしい気もする。まぁ優先順位が低いってことなんだろうけど。subscribe のときに設定できるし、そんなにいじらないしね。まとめて opml import した初回くらいしかこんなことは感じないのかも。

  1. チガイマス 

ADSL復活

長かった。予定より2日早いけど、それでも10日も常時接続が切れていたのか。まぁ平日の昼間は自分自身は IP reachable なわけだけど、その成果を自宅サーバやそれ経由の HDD レコーダに落とせないのがかなり不便だった。ダイヤルアップを共有したり、ルータを自分で作れるようにならないとダメかなぁとちょっと本気で考えてしまった。

まぁそれでもネットに繋がらないとやれることが減るので、家計簿整理したり住所録整理しようとしたり、なんか翻訳とかしてみたり、それはそれでなかなか充実した時間の使い方をするようになるもんだな、とは思った。調べ物とか手軽にできないのは痛いけど。

テキストサイト

ウェブサイト・アーカイブ

ちょっと勉強してみっか。でもなぁ。なんか狭い世界をさらに狭く細切れにしたいっつー話なんだよね?

SkyPe

相変わらず yasuii 氏のアンテナの方向と感度は自分とは大違いで面白い。

moinmoin

Zope はアイディアと実際にできることを知っているし、きらいではないが、やはり手軽に使うという感じはあまりしない。そこで ZWiki 以外の Python による Wiki 実装を試してみようと思い立った。まず白羽の矢が立ったのは超有名。

すげー。

なんか Python 関係の実装ってシンプルじゃないよなぁ。一個一個のファイルや一個一個のステートメントはシンプルかもしれないけど、全体の構造はあまりシンプルじゃないと言うかなんと言うか。ツールが便利すぎてそれ依存になれば全体をシンプルに保つ努力をしなくなるというか、そんな感じか。

どーも Python 関係にはいい印象を持てんぞ。

というわけで挫折中。

Zope 2.7 + ZWiki 日本語版 0.29

cmail は依存するものがいっぱいあって面倒くさくなったので以前から気になっていた reStrucedText を扱うプラットフォームとして、もう一度 ZWiki を立ててみようと思い、Zope 2.7 を試した。

Win32版は Python 2.3.3 の dll が入っているので別途 Python は不要。

Zope のシステムとインスタンス(ZODB とか log とか)をセットアップ段階で分離できるようになっていた。

いいね、やっと手軽にまともな環境が作れるようになってきたぞ。

LocalFSJP動かず
ZWiki日本語版ok

docutils

Python 用の各種ドキュメンテーション用ツール。reStructuredText はこのモジュールの中の1つ。

cmail

  • mime は別パッケージが必要
Emacs 18, 19tm
Emacs 20, 21SEMI

ということでまず SEMI を入れる

  • SEMI には FLIM が必要
  • FLIM も SEMI も APEL に依存している。
    • したがって最初にすべきは APEL のインストール

なんかやたらと使いにくいサイトなんですが。

About

例によって個人のなんちゃらです