論理削除Gem、 Kakurenbo をアップデートしました。
様々なわけがあってkakurenboは非推奨です。新規のプロジェクトではkakurenbo-putiの使用をおすすめします。
Kakurenboとは
Rails4で論理削除を実現するためのGemです。実装に至った経緯は Rails4と3で論理削除を行うためのGem Kakurenbo の紹介と今更論理削除Gemを実装した理由。 - 波打際のブログさん を見てください。
アップデート
今回のアップデートの目的は、ActiveRecord4.0.2以降との親和性を向上させることにあります。主な変更点は次のとおりです。
メソッド名のコンフリクト解消
yusabana (Yuji Takaesu) · GitHub さんから、 destroy! メソッドのコンフリクト修正とRspec3対応のプルリクエストをいただき、マージしました。
従来の Kakurenbo で物理削除を行う場合、物理削除用のメソッド destroy! を呼び出していました。しかし、Rails4.0.2にdestroy!メソッドが追加されたため、メソッドがコンフリクトしてしまいました。解決策として、物理削除のメソッドを廃止してオプションで物理削除を指定する設計に変更しました。
この変更に伴い全ての物理削除メソッドを廃止し、オプションで物理削除を指定する設計に変更しました。
リレーショナルの論理削除に完全対応
従来の Kakurenbo はリレーショナルの論理削除に一部未対応でしたが、今回のアップデートで完全対応しました。
Rails3サポートの打ち切り
Rails4の対応に十分な時間を割り当てるために0.2以降のバージョンでは、Rails3の対応を打ち切ります。
アソシエーションの強化(7月23日追記)
belongs_toやhas_manyなどでレコードに関連付けされた他テーブルのレコードの読み込みを、近日リリース予定のバージョン0.2.1で強化します。具体的には、読み込み元のレコードが論理削除されていた場合に、関連付けされたレコードのスコープに依存削除されたレコードを含めるようにします。(完全対応はRails4.1以降のみで、Rails4.0.xではThroughアソシエーションには未対応となります。)
リファレンス
今回のアップデートで Kakurenbo のインターフェイスは次のようになりました。
論理削除
# コールバックあり model.destroy model.destroy! Model.destroy_all(hoge: 'fuga') Model.where(hoge: 'fuga').destroy_all # コールバックなし model.delete Model.delete_all(foo: 'bar') Model.where(foo: 'bar').delete_all
復元
destroy!メソッドの挙動に合わせてrestore!メソッドを修正しました。コールバックなどで復元がキャンセルされた場合に例外が発生するようになります。
# コールバックあり # before_restore, after_restore コールバックが利用可能です。 model.restore model.restore! Model.where(hoge: 'fuga').restore_all
物理削除
物理削除は、削除系のメソッドにオプションで指定するようになりました。
# コールバックあり model.destroy(hard: true) model.destroy!(hard: true) Model.destroy_all(hoge: 'fuga', hard: true) Model.where(hoge: 'fuga').destroy_all(nil, hard: true) # コールバックなし model.delete(hard: true) Model.delete_all(foo: 'bar', hard: true) Model.where(foo: 'bar').delete_all(nil, hard: true)
その他のメソッドや使い方は alfa-jpn/kakurenbo · GitHub を確認してください。
謝辞
今回のアップデートに関して、プルリクエスト、バグレポートを送信していただいた下記の方々に心より御礼申し上げます。
Rails4のバリデータをテストまでしっかり書いた備忘録
はじめに
Rails4には様々なバリデータが用意されていますが、プロダクトを実装する過程で自作のバリデータを定義して使いたくなることが稀によくあります。稀によくある事なので、その都度使い方を調べていていますが、バリデータの実装からロケールファイルの定義、テストの書き方までまとまっているページがないので備忘録程度にまとめておきます。
バリデータ
今回作るバリデータについて
バリデーション対象のカラムの内容が hoge であるかチェックするHogeValidatorを作成します。バリデーションのオプションで hoge が連続する回数を指定できるようにします。
# text が `hoge` であることのバリデーション validates :text, :hoge => {:count => 1} # text が `hogehoge` であることのバリデーション validates :text, :hoge => {:count => 2}
こんな感じで使えるバリデータを作ります。
バリデータの定義
`app/validators` フォルダに `hoge_validator.rb` を作ります。
class HogeValidator < ActiveModel::EachValidator def validate_each(record, attribute, value) unless ('hoge' * options[:count]) == value record.errors.add(attribute, :hoge, options) end end end
テスト
`spec/validators/` フォルダに `hoge_validator_spec.rb` を作ります。
require 'spec_helper' describe HogeValidator do let :validator do HogeValidator.new({:attributes => [attribute]}, :count => 1) end let :errors do ActiveModel::Errors.new(mock_class) end let :attribute do :text end let :mock_class do double('model').tap do |mock| allow(mock).to receive(:class) { ActiveRecord::Base } allow(mock).to receive(:errors) {errors} allow(mock).to receive(:read_attribute_for_validation) {attribute_value} end end subject do validator.validate_each(mock_class, attribute, attribute_value) errors.empty? end context 'ただしくHogeってる時' do let :attribute_value do 'hoge' end it 'バリデーションが通ること' do expect(subject).to be_truthy end end context 'Hogeってないとき' do let :attribute_value do 'fuga' end it 'バリデーションが通らない' do expect(subject).to be_falsey end end end
今回は省略しましたが、エラーメッセージに関するテストを行うとさらに良いと思います。
クラウドIDEを使ってみた Nitrous.io & Codeanywhere
はじめに
お久しぶりです。昨年末から関ってきた某プロジェクトを5月にリリースし、最近やっと落ち着いてきました。落ち着いてきたついでに先週末ごろから開発環境を0から作り直しているのですが、開発環境の構築って面倒くさいですよね。検証環境なんかはVagrantを使えば良いかもしれません、しかし、開発環境となると話は別です。IDEをインストールしてエディタの設定をして...etc 面倒くささの極み!!!
比較
双方一番安い有料プランに登録しての比較です。
仮想環境について
Codeanywhere
- Redhat系
- sudo権限あり
- ブラウザ上のターミナル以外から接続する方法なし
IDEについて
Codeanywhere
- 自動でオートコンプリートリストが開く
パフォーマンスについて
Codeanywhere
- 非常に不安定
料金について
Codeanywhere
- $2から
- マシンの設置可能台数がプランによって変化
まとめ
「リソースいっぱい使って、いくつも環境作りたい!」という人は料金体系的にCodeanywhereをお勧めしたいところですが、前述したとおり動作が非常に不安定です。日本国内から使用するなら安定性とパフォーマンスの問題から、現時点では Nitrous.io しか選択肢はなさそうです。
nginxモジュールのテストを書く
はじめに
唐突ですが、仕事でシステムの構成を考えていると、nginxを拡張したいなーとか思うことってあると思います。nginxはC言語で割と簡単に拡張モジュールを書くことができます。(今回は拡張モジュールの書き方については触れませんが・・・)
だがしかし、仕事なんだからテストコードは必須だよね(迫真)。既存のnginx用のモジュールのコードを眺めてみると、謎の`t`フォルダにテストコードが入っています。
`t`ってなんだよ`t`って!中身もhogehoge.tとかなってるし、t言語なんかあるの? というところから始まり、なんとかテストを書いて実行できるようになったので方法をまとめておきます。
具体的にはperlのテストフレームワークと agentzh/test-nginx · GitHub を利用してテストを書いていきます。
テストの準備
必要なもの
nginxのテストをするために必要なソフトウェアやライブラリです。URLを掲載していないものはUbuntu13.10の公式リポジトリからSynapticを使ってインストールしました。
nginxのコンパイルに必要なもの
- nginx (http://nginx.org/download/)
- echo-nginx-module (https://github.com/agentzh/echo-nginx-module)
- memc-nginx-module (https://github.com/agentzh/memc-nginx-module)
テストを実行する上でecho-nginx-moduleとmemc-nginx-moduleは不可欠でした。
※`/home/develop` 下にecho-nginx-moduleとmemc-nginx-moduleをcloneしていると想定して記事を書いています。
perlでテストを実行するために必要なもの
- perl
- prove
- libtest-base-perl
- libtest-longstring-perl
- test-nginx (https://github.com/agentzh/test-nginx)
test-nginxは適当なディレクトリにcloneしておき、テスト実行時にディレクトリを指定します。
※`/home/develop' 下にtest-nginxをcloneしていると想定して記事を書いています。
テストコードの書き方
nginxモジュールのプロジェクトディレクトリに`t/`というディレクトリを作り、その中に`{任意の名前}.t`という形でテストコードを書いていきます。
use Test::Nginx::Socket; # テストの実行回数の設定。 repeat_each(3); plan tests => repeat_each() * 2 * blocks(); # 文字の表示をわかりやすくする。 no_long_string(); # テストの実行 run_tests(); __DATA__ === TEST 1: ハローワールドのテスト --- config location /hello { echo hello_world; } --- request GET /hello HTTP/1.0 --- response_body hello_world --- error_code: 200
`--- config` にnginxの設定を記述し、`---request` でテストとして実行するリクエストを記述します。
次に、リクエストに対して`--- response_body` で期待するレスポンスを記述し、 `--- error_code` に期待するレスポンスコードを記述します。
こんなに簡単にかけるテストは見たことはありません!幸せ。
テストの実行
大まかな流れ
nginxのテストの大まかな流れは次のとおりです。
nginxのコンパイル
テストを実行するために、自分のモジュールを組み込んだnginxをコンパイルします。私の場合は次のようなオプションでコンパイルしました。
CFLAGS="-O1 -Wall -g" ./configure --add-module=[自分のモジュール] --add-module=/home/develop/echo-nginx-module --add-module=/home/develop/memc-nginx-module --prefix=/home/develop/nginx --with-debug
特筆すべき点は下記2点です。
- CFLAGSはvalgrindを利用してプロファイリングを行うことを想定しています。
- プレフィックスをつけて通常使用するnginxとは分けました。(当然といえば当然ですね。)
proveでテストの実行
まず、先ほどコンパイルしたnginxにパスを通します。
export PATH=/home/develop/nginx/sbin:$PATH
そしてテストを実行します。
prove -I/home/develop/test-nginx/lib -r t
proveを実行する際にtest-nginxをincludeのがポイントです。
valgrindでプロファイリングする場合
メモリリークや実行速度についての解析をvalgrindを用いて行う場合にはTEST_NGINX_USE_VALGRINDという環境変数にオプションを記述します。
TEST_NGINX_USE_VALGRIND="{オプション}" prove -I/home/develop/test-nginx/lib -r t
メモリリークのテストをする場合にはこんなかんじでしょうか。
TEST_NGINX_USE_VALGRIND="-v --tool=memcheck --leak-check=full" prove -I/home/develop/test-nginx/lib -r t
RailsのEnumと相性が悪い人向けのInumをバージョンアップさせた話。
はじめに
Railsの列挙型事情
Rails4.1以前では公式に列挙型が実装されておらず、列挙型を実現するために様々なgemが生み出されてきましたが、Rails4.1でついにActiveRecord::Enumという列挙型のクラスがやっと実装され、長い列挙型宗教戦争が終わろうとしています。
どうせI18n対応とかじゃないの?
もちろんI18n対応も理由の一つですが、I18n対応だけで良ければ brainspec/enumerize · GitHub あたりを使うのがベストだと思います。それだけでは足りないほしがりさん向けです。
Inumができること
モデルにロジックを食わせてコレ以上肥満体型にしたくない!
そんなあなたにInumダイエット。Inumでは列挙型を独立したクラスとして定義するので、モデルに列挙型に関するロジックを書いて太らせるなんてことはありません。(メンテナンス継続理由の大半を占めています。)
class AnimeType < Inum::Base define :NYARUKOSAN, 0 define :NONNONBIYORI, 1 define :KILLMEBABY, 2 # セリフを表示する def serif case self when NYARUKOSAN "いつもニコニコあなたの隣に這いよる混沌、ニャルラトホテプ、です!" when NONNONBIYORI "にゃんぱすー" when KILLMEBABY "キルミーベイベーは復活する、絶対にだ!!" end end end p AnimeType::KILLMEBABY.serif # => "キルミーベイベーは復活する、絶対にだ!!"
各位@ActiveRecordとなかよく
バージョン3.0.0には ActiveRecord support. by alfa-jpn · Pull Request #4 · alfa-jpn/inum · GitHub super friendly ActiveRecord. という内容のプルリクがマージされています。超仲いいです。
class Anime < ActiveRecord::Base bind_inum :column=> :anime_type, :class => AnimeType end
この1行書くだけで、Animeモデルのanime_typeカラム(integer)に列挙型がバインドされ便利なメソッドが定義されます。
anime = Anime.new # getter/setterで列挙型にラップされます。 anime.anime_type = AnimeType::KILLMEBABY p anime.anime_type.value # => 2 # 判定用メソッドが追加されます。プレフィックスにカラム名が入りますが、bind_inumのオプションで消せます。 p anime.anime_type_nonnonbiyori? # => false
仲がいいものの依存はしておらず、ActiveRecordは必須ではありません。
I18nで表示名を付けたい
I18nのyamlでinum下に記述することでtranslateできます。
ja: inum: anime_type: nyarukosan: '這いよれ!ニャル子さん' nonnonbiyori: 'のんのんびより' killmebaby: 'キルミーベイベー'
translate メソッドもしくは t メソッドで翻訳できます。
p AnimeType::NONNONBIYORI.t # => "のんのんびより"
パワフルにパーズできる
わりと何でもパーズしてくれるメソッドが内部にいるので、代入や比較ができます。
# パーサー p AnimeType.parse(0).t # => "這いよれ!ニャル子さん" p AnimeType.parse('NONNONBIYORI').t # => "のんのんびより" p AnimeType.parse(:KILLMEBABY).t # => "キルミーベイベー" p AnimeType.parse(999) # => nil # ActiveRecordのsetterでもパーサーが働くのでこんなことや anime = Anime.new anime.anime_type = "KILLMEBABY" # わりと色々な箇所に仕組んでいるのでこんな風にも使えます。 p (AnimeType::NONNONBIYORI + 1 ).t # => "キルミーベイベー"
EnamerableとComparebleをinclude
わりと無双な感じ
p AnimeType::KILLMEBABY == 2 # => true p AnimeType::NONNONBIYORI < AnimeType::KILLMEBABY # => true AnimeType.each do |enum| p enum.t end
参考サイト
Rails4と3で論理削除を行うためのGem Kakurenbo の紹介と今更論理削除Gemを実装した理由。
様々なわけがあってkakurenboは非推奨です。新規のプロジェクトではkakurenbo-putiの使用をおすすめします。
Kakurenboとは
Rails4及びRails3で論理削除を行うためのGemです。paranoia及びacts_as_paranoidと互換性があるので、gemを置き換えるだけでそのまま動きます。(動かなかったらissue投げてくだしあ><)
なんで今更論理削除のGemを実装したのか
今更開発しなくても論理削除のGemは山ほど出てきますが、なぜ開発しなくてはならなくなってしまったのか経緯に触れておきます。
acts_as_paranoid
論理削除の本家と言ってもいいレベルの知名度です、だがしかしいつまで立ってもRails4に対応しない。
rails4_acts_as_paranoid
既存のバグほったらかしで使えない。(メモリ関連のバグがありSQLクエリが永遠に結合されていきます。やばみ。プルリク送ったのに放置されてます。)
paranoia
今Rails4で論理削除と言ったらコレ!ってくらい定番です。私もつい最近まではコレを利用していました。コールバック関連のバグを修正するプルリク送ったりして結構便利に使っていました。
しかし、とある作業中にアソシエーションに深刻な問題があることに気付き、プルリクを送りましたが、仕様なのでマージする気はないとプルリク蹴られてしまいました。
具体的にはリレーショナルで1対多の関係を設定している場合、親を論理削除して復元を行った場合、親が論理削除される以前に論理削除されている子まで復元されてしまうのです。例えば掲示板で、掲示板を本体を論理削除した後、何かの手違いだとわかり復元する時に、問題があって削除してた書き込みも全て復元されてしまうのです。
「そもそも間違えて削除しなければいい話だろ」なんて運用でカバーするスタイルはRails、Rubyらしくありません。だからこれは深刻な問題です。大切なことなのでもう一度言います。これは深刻な問題です。
インストール方法
いつものようにGemfileに追加するとか、インストールするとか。
gem "kakurenbo"
使い方
論理削除で管理したいモデルに deleted_at カラムを datetime 型で追加するだけで使えます。
rails generate migration add_deleted_at_to_models deleted_at:datetime
クラスの初期化時にカラムを読み取って、deleted_atがあれば論理削除と判別するので論理削除のモデルでいちいちacts_as_paranoidメソッドを呼び出す必要はありません。
(deleted_at以外のカラムを利用する際には、acts_as_paranoidをcolumnオプション付きで呼び出して設定してください。)
基本的な使い方
paranoiaやacts_as_paranoidと互換性があるので説明はいらないかもしれませんが基本的な使い方です。
この記事は古くなっています、最新バージョン 0.2.x の使い方はこちらを参照してください。
論理削除Gem、 Kakurenbo をアップデートしました。 - 波打際のブログさん
復元
論理削除したモデルはrestoreで復活できます。
model.restore! # 次の方法も使えます。id指定、もしくはidの配列で一括で復元できます。 Model.restore(id)
before_restore, after_restoreコールバック使えます。
物理削除
destroy!で物理削除できます。
model.destroy!
スコープ
# 削除されたモデルも取得 Model.with_deleted # 削除されたモデルのみ取得 Model.only_deleted # 削除されていないモデルのみ取得(デフォルトスコープです。) Model.without_deleted
関連リンク
修正履歴
- 2014年01月29日 誤字脱字を修正しました。
Ubuntu13.10でiBusを捨ててfcitxを使う
はじめに
Ubuntu13.10がリリースされて3ヶ月ほど立ちました。Ubuntuのサポート期間はLTS版を除き、9ヶ月というとても短い設定となっています。今まで使用してたUbuntu13.04のサポート期限は去年の12月いっぱいなので、継続して使用するためにUbuntuのバージョンを13.10にアップグレードしたところ、インプットメソッドのiBusが1.5にアップグレードされ、非常に使いにくいものとなっていました。
はじめのうちはiBusと仲良くなる努力はしましたが不可能であることを悟り、今話題のfcitxを利用することにしました。これは導入に関するまとめです。
fcitxの良い所
- iBus1.4と同様の感覚で日本語入力ができる。
- iBusよりもカスタマイズできる箇所が多い。(候補ウィンドウの配色やフォント、テーマなど)
- Mozcのユーティリティへのアクセスがしやすい。(設定ツールや辞書ツールをワンクリックで呼び出せる)
fcitxの悪い所
- 半角/全角キーを長めに押さないとIMが切り替わらない事がある。(もしかしたら設定の問題?)
導入
パッケージのインストール
Ubuntuソフトウェアセンターか、apt-getでfcitx-mozcをインストールすると、依存関係が設定されているので必要なものがすべて芋づる式でインストールされます。
デフォルトでfcitxを使用する設定にする
パッケージのインストールが完了したら システム設定の言語サポートから、「キーボード入力に使うIMシステム」の設定をfcitxに設定します。設定が完了したらコンピュータを再起動させます。
iBusのインジケータを非表示にする
再起動するとfcitxのインジケータと、iBusのインジケータが表示されていると思うので、システム設定のテキスト入力から、「メニューバーに現在の入力ソースを表示」のチェックボックスを外し、iBusのインジケータ表示を無効にします。
fcitxの設定
この設定が完了すると、fcitxは既に使用できる状態になってますが、デフォルトでは半角/全角に割り当てられているキーマップが、mozcの半角全角と競合してしまっているために、おかしな挙動になってしまいます。そこで、mozcに割り当てられている半角/全角キーのキーマップを全て削除します。
メニューバーのfcitxのアイコンをクリックし、Mozcツール(表示されていない場合はテキスト入力画面で半角/全角キーを押して、IMをMozcに切り替えてください)から設定ツールをクリックします。Mozc設定ツールが起動したら、一般タブの中のキー設定項目右側にある編集ボタンをクリックするとキー設定ウィンドウが表示されます。
キー設定ウィンドウの上部にある入力キーのタブをクリックし、入力キーの名前順にリストを並び替え、Hankaku/Zenkakuの設定4つ全てを削除します。
以上で設定は完了です、設定が完了したら念の為に再起動を行います。