単純なメモ。
<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 の代わりには使えない。
@vue/test-utils を使ったテストはとにかく wrapper を相手にする ↩
Dragonfly については以前紹介した通りで、普段触っているアプリではこれを使っている。表示のタイミングで変換が走るのは高負荷のサイトには向かないかもしれないけど、使い勝手には満足している。
で、今回はこの Dragonfly を使っている場合に気をつける必要のあるユースケースを見つけたのでまとめておこうと思う。
ActiveRecord + Dragonflyでの画像の削除のタイミング
Paperclip もそうかもしれないけど、Dragonfly はよくできていて
- レコードの削除
- レコード内の Attachment の変更
のタイミングで Attach していたファイルをちゃんと掃除してくれる。これによって無駄なファイルが残ってしまうといったことがなくなる。
そしてこの処理は ActiveRecord で言うと
before_save
のタイミングで実行される。
削除時にはファイルの参照はチェックしないので自前で
今回ハマったのは
- コピーしたレコードで
- 画像を変更したら
- 他のレコードも参照していたファイルが消えてしまった!
というものです。
上の説明を読んでいればとても当たり前の話なんだけど、まぁそこはそれ。回避するには以下のいずれかの方法がありそう。
- 参照カウンタよろしく、同一の uid を持つレコードが before_save の段階で 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 をセットしてるとそれもややこしいので、いちばん影響なさそうで手っ取り早い黒魔術で片付けることにした。
これでコピーしたレコードの画像を差し替えても他のレコードでロストしない。
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 に書けるのがいちばん便利だと思うんですが、そういう機能は今のところないっぽいです。
参考
Fastladder : RSS / Atom feed reader
なぜなら livedoor ID をあまり取りたくなかった&ほしい ID はどうせ取れなかったので livedoor Reader は使ったことなかったのです。
で、初めて livedoor Reader を1使ってみた感想は、
- 確かに閲覧は速い
- でもあのアイテムを移動する時の「うにょん」ていう効果がなくて寂しい
- うっかりフィードを開いてしまっても新着アイテムがいきなりなくなってしまわないのが嬉しい(Reload するまでは既読にマークされても開くことができるのかな?)
- ハイライトがちょっとまぶしい
- 閲覧中のフィードに対して share の状態の変更がすごくやりにくい
- 基本 public でほとんどいじる必要ないっちゃないんですが
- フィードリストの方の文字のサイズが変わらないのでバランスが悪くなってしまう
- フィードリストの幅って変更できないの?
といった感じ。bloglines を使っているので基本的に bloglines との比較になります。
見た目については最終的には user css が炸裂すればなんとでもなるんだけど、フィード管理の部分はちょっとどうにかしてほしい気もする。まぁ優先順位が低いってことなんだろうけど。subscribe のときに設定できるし、そんなにいじらないしね。まとめて opml import した初回くらいしかこんなことは感じないのかも。
チガイマス ↩
長かった。予定より2日早いけど、それでも10日も常時接続が切れていたのか。まぁ平日の昼間は自分自身は IP reachable なわけだけど、その成果を自宅サーバやそれ経由の HDD レコーダに落とせないのがかなり不便だった。ダイヤルアップを共有したり、ルータを自分で作れるようにならないとダメかなぁとちょっと本気で考えてしまった。
まぁそれでもネットに繋がらないとやれることが減るので、家計簿整理したり住所録整理しようとしたり、なんか翻訳とかしてみたり、それはそれでなかなか充実した時間の使い方をするようになるもんだな、とは思った。調べ物とか手軽にできないのは痛いけど。
ちょっと勉強してみっか。でもなぁ。なんか狭い世界をさらに狭く細切れにしたいっつー話なんだよね?
相変わらず yasuii 氏のアンテナの方向と感度は自分とは大違いで面白い。
Zope はアイディアと実際にできることを知っているし、きらいではないが、やはり手軽に使うという感じはあまりしない。そこで ZWiki 以外の Python による Wiki 実装を試してみようと思い立った。まず白羽の矢が立ったのは超有名。
すげー。
なんか Python 関係の実装ってシンプルじゃないよなぁ。一個一個のファイルや一個一個のステートメントはシンプルかもしれないけど、全体の構造はあまりシンプルじゃないと言うかなんと言うか。ツールが便利すぎてそれ依存になれば全体をシンプルに保つ努力をしなくなるというか、そんな感じか。
どーも Python 関係にはいい印象を持てんぞ。
というわけで挫折中。
cmail は依存するものがいっぱいあって面倒くさくなったので以前から気になっていた reStrucedText を扱うプラットフォームとして、もう一度 ZWiki を立ててみようと思い、Zope 2.7 を試した。
Win32版は Python 2.3.3 の dll が入っているので別途 Python は不要。
Zope のシステムとインスタンス(ZODB とか log とか)をセットアップ段階で分離できるようになっていた。
いいね、やっと手軽にまともな環境が作れるようになってきたぞ。
LocalFSJP | 動かず |
ZWiki日本語版 | ok |
docutils
Python 用の各種ドキュメンテーション用ツール。reStructuredText はこのモジュールの中の1つ。