Rails 4.1のenumの挙動(2)
前回、キーの項目と数値を定義したのだけど、実はキーの項目だけ指定しても数値として保存されるようだ(キーだけだと文字列で保存されるのかと思っていた……)。
class Product < ActiveRecord::Base enum status: %w(normal sale empty) end
こんな感じで定義してから ./bin/rails c
して確認する。
まずはどのようにデータが保存されるか。
Loading development environment (Rails 4.1.0.rc2) irb(main):001:0> Product.create(status: 1) (0.2ms) begin transaction SQL (0.9ms) INSERT INTO "products" ("created_at", "status", "updated_at") VALUES (?, ?, ?) [["created_at", "2014-04-06 02:53:47.039739"], ["status", 1], ["updated_at", "2014-04-06 02:53:47.039739"]] (1.3ms) commit transaction => #<Product id: 1, name: nil, status: 1, created_at: "2014-04-06 02:53:47", updated_at: "2014-04-06 02:53:47"> irb(main):002:0> Product.create(status: "sale") (0.2ms) begin transaction SQL (0.4ms) INSERT INTO "products" ("created_at", "status", "updated_at") VALUES (?, ?, ?) [["created_at", "2014-04-06 02:53:57.177845"], ["status", 1], ["updated_at", "2014-04-06 02:53:57.177845"]] (1.0ms) commit transaction => #<Product id: 2, name: nil, status: 1, created_at: "2014-04-06 02:53:57", updated_at: "2014-04-06 02:53:57"> irb(main):003:0> Product.create(status: :sale) (0.2ms) begin transaction SQL (0.6ms) INSERT INTO "products" ("created_at", "status", "updated_at") VALUES (?, ?, ?) [["created_at", "2014-04-06 02:54:05.389452"], ["status", 1], ["updated_at", "2014-04-06 02:54:05.389452"]] (1.3ms) commit transaction
ついでに、前回は書かなかったんだけど述語メソッドも生えているのでそっちも確認しておく。ここで保存したのはsaleの値なので、sale?で問い合わせすることができる。
irb(main):013:0> pro = Product.where(status: 1).first Product Load (0.3ms) SELECT "products".* FROM "products" WHERE "products"."status" = 1 ORDER BY "products"."id" ASC LIMIT 1 => #<Product id: 1, name: nil, status: 1, created_at: "2014-04-06 02:53:47", updated_at: "2014-04-06 02:53:47"> irb(main):014:0> pro.sale? => true
ここではどうでも良いけど、empty
ってステータスはメソッド呼び出しだとちょっと誤解を招きそうなので辞めたほうが良いですね。
検索する場合、ステータス毎のクラスメソッドがある。当然、メソッドチェインでwhere区等もつなげることができる。
irb(main):003:0* Product.sale Product Load (3.3ms) SELECT "products".* FROM "products" WHERE "products"."status" = 1 => #<ActiveRecord::Relation [#<Product id: 1, name: nil, status: 1, created_at: "2014-04-06 02:53:47", updated_at: "2014-04-06 02:53:47">, #<Product id: 2, name: nil, status: 1, created_at: "2014-04-06 02:53:57", updated_at: "2014-04-06 02:53:57">, #<Product id: 3, name: nil, status: 1, created_at: "2014-04-06 02:54:05", updated_at: "2014-04-06 02:54:05">]> irb(main):004:0> Product.sale.where('id > 2') Product Load (0.4ms) SELECT "products".* FROM "products" WHERE "products"."status" = 1 AND (id > 2) => #<ActiveRecord::Relation [#<Product id: 3, name: nil, status: 1, created_at: "2014-04-06 02:54:05", updated_at: "2014-04-06 02:54:05">]>
ただ、where区に文字列やシンボルで渡しても想定通りの動作にはならない。シンボルはともかく、文字列を渡すと0で検索しちゃうのはウッカリハマる可能性がありそう……。
irb(main):008:0> Product.where(status: 'sale') Product Load (0.3ms) SELECT "products".* FROM "products" WHERE "products"."status" = 0 => #<ActiveRecord::Relation []> irb(main):009:0> Product.where(status: :sale) Product Load (0.4ms) SELECT "products".* FROM "products" WHERE "products"."status" = 'sale' => #<ActiveRecord::Relation []>
私からは以上です。
Rails 4.1のenumの挙動
みんな大好きenum型
DBのカラムでステータスとか使うとき、ぼくは数値派なんですけど、enumで掛けたら楽で良いですよねみたいのあります。ありますよね。
で、Rails 4.1ではそういうのが導入されたっぽいので試してみました。
rails 4.1をインストールしてプロジェクト作ったりする
$ gem install rails --pre --no-ri --no-rdoc $ rails new sample $ cd sample $ ./bin/rails g scaffold product name:string status:integer $ ./bin/rake db:migrate
rails c
でフツーに登録してみる(ここは何の変哲も無い)。
irb(main):005:0> Product.create!(name: "hoge", status: 99) (0.1ms) begin transaction SQL (0.2ms) INSERT INTO "products" ("created_at", "name", "status", "updated_at") VALUES (?, ?, ?, ?) [["created_at", "2014-03-26 01:25:21.026233"], ["name", "hoge"], ["status", 99], ["updated_at", "2014-03-26 01:25:21.026233"]] (1.9ms) commit transaction => #<Product id: 2, name: "hoge", status: 99, created_at: "2014-03-26 01:25:21", updated_at: "2014-03-26 01:25:21">
statusの値が99でも保存できちゃう。そのままでも良いんだけど、validatesのおさらいとして例えば0〜2までに制限してみようか。
class Product < ActiveRecord::Base validates :status, inclusion: {in: 0..2} end
ためしてみよう
Loading development environment (Rails 4.1.0.rc2) irb(main):001:0> Product.create!(name: "hoge", status: 99) (0.1ms) begin transaction (0.1ms) rollback transaction ActiveRecord::RecordInvalid: Validation failed: Status is not included in the list irb(main):003:0* Product.create!(name: "hoge", status: 2) (0.1ms) begin transaction SQL (0.4ms) INSERT INTO "products" ("created_at", "name", "status", "updated_at") VALUES (?, ?, ?, ?) [["created_at", "2014-03-26 01:27:51.238248"], ["name", "hoge"], ["status", 2], ["updated_at", "2014-03-26 01:27:51.238248"]] (1.6ms) commit transaction => #<Product id: 3, name: "hoge", status: 2, created_at: "2014-03-26 01:27:51", updated_at: "2014-03-26 01:27:51">
数値の範囲指定ができましたね。
それではenumを使ってみよう
enumの設定をしてみよう。ややこしくなるので、validatesは消します。ついでに書いておくと、enumの項目自体はテキトウなのでスルー推奨です。
class Product < ActiveRecord::Base enum status: { normal: 0, # 通常 sale: 1, # 特売 empty: 2, # 在庫切れ } end
これで試してみよう。まずは正常系で。
irb(main):005:0* Product.create!(name: "hoge", status: 2) (0.1ms) begin transaction SQL (0.2ms) INSERT INTO "products" ("created_at", "name", "status", "updated_at") VALUES (?, ?, ?, ?) [["created_at", "2014-03-26 01:33:06.501462"], ["name", "hoge"], ["status", 2], ["updated_at", "2014-03-26 01:33:06.501462"]] (1.6ms) commit transaction => #<Product id: 6, name: "hoge", status: 2, created_at: "2014-03-26 01:33:06", updated_at: "2014-03-26 01:33:06"> irb(main):006:0> Product.create!(name: "hoge", status: :sale) (0.1ms) begin transaction SQL (0.6ms) INSERT INTO "products" ("created_at", "name", "status", "updated_at") VALUES (?, ?, ?, ?) [["created_at", "2014-03-26 01:33:13.877152"], ["name", "hoge"], ["status", 1], ["updated_at", "2014-03-26 01:33:13.877152"]] (1.5ms) commit transaction => #<Product id: 7, name: "hoge", status: 1, created_at: "2014-03-26 01:33:13", updated_at: "2014-03-26 01:33:13"> irb(main):005:0> Product.create!(name: "hoge", status: 'sale') (0.1ms) begin transaction SQL (0.6ms) INSERT INTO "products" ("created_at", "name", "status", "updated_at") VALUES (?, ?, ?, ?) [["created_at", "2014-03-26 01:57:26.957196"], ["name", "hoge"], ["status", 1], ["updated_at", "2014-03-26 01:57:26.957196"]] (1.4ms) commit transaction => #<Product id: 15, name: "hoge", status: 1, created_at: "2014-03-26 01:57:26", updated_at: "2014-03-26 01:57:26">
このように、シンボルや文字列、そして数値でも保存してくれるようになった。便利。
では範囲外の値を入力すると?
irb(main):007:0> Product.create!(name: "hoge", status: 99) ArgumentError: '99' is not a valid status irb(main):009:0* Product.create!(name: "hoge", status: :hoge) ArgumentError: 'hoge' is not a valid status irb(main):011:0* Product.create!(name: "hoge", status: 'hogehoge') ArgumentError: 'hogehoge' is not a valid status
ことごとくArgumentErrorとなる。create!
で試しているけど、create
やnew
でも試してみよう。
irb(main):007:0> Product.create(name: "hoge", status: 99) ArgumentError: '99' is not a valid status irb(main):009:0* Product.new(name: "hoge", status: 99) ArgumentError: '99' is not a valid status
create!だけに限らず、newメソッドとかでも引数に異なる範囲の値が入るとArgumentErrorになる。
普通のvalidatesに条件を書く場合は ActiveRecord::RecordInvalid: Validation failed: Status is not included in the list
みたいにActiveRecord::RecordInvalidの例外クラスが送出されるけど、そうではない(例外クラス的にも、発生タイミング的にも)ので気をつけた方が良さそう。
validatesではないから挙動が異なるのは当然といえば当然だけど、使い勝手としては同じような挙動のほうが嬉しかったんだけどそうではなかったという備忘録エントリーです。
Rails詳しくないのでよくわかりませんが、ここら辺、正式リリースまでに挙動が変わったりするかしら?
書かなくてはならない事柄(追記)
こちらも何卒よろしくお願い致します。
Webアプリエンジニア養成読本[しくみ、開発、環境構築・運用…全体像を最新知識で最初から! ] (Software Design plus)
- 作者: 和田裕介,石田絢一(uzulla),すがわらまさのり,斎藤祐一郎
- 出版社/メーカー: 技術評論社
- 発売日: 2014/03/11
- メディア: 大型本
- この商品を含むブログ (3件) を見る
SJISでもgit diffしたい
SJISのファイルでもgit diff
したい。いじわるしないで;;
どうするか
追記:NKF使わなくても良かった(後述)
$ git diff | nkf -u
でdiffの文字列をUTF-8にすれば良い。今日日NKFもそうそう使わないしMac OSXにはデフォルトで入ってないっぽいのでHomebrewでインストールすると良いでしょう。
$ brew install nkf
これでgit diff
できて生きる希望が湧いてきますね。
NKFじゃなくてiconvで良かった
@sugamasao nkf はデフォルトで入ってないけど iconv は入ってる気がします。追加ツールなしでできるかも。
— TOYODA Tetsuya (@aql) 2014, 3月 14
アドバイス通り、Mac OSXにはiconvがデフォルトで入ってるので、NKFを使わずとも実現できるのであった。
$ git diff | iconv -f SJIS
やった便利!!
書かざるをえない事柄
3/11に共著で本を書きましたのでよろしくお願いいたします。
Webアプリエンジニア養成読本[しくみ、開発、環境構築・運用…全体像を最新知識で最初から! ] (Software Design plus)
- 作者: 和田裕介,石田絢一(uzulla),すがわらまさのり,斎藤祐一郎
- 出版社/メーカー: 技術評論社
- 発売日: 2014/03/11
- メディア: 大型本
- この商品を含むブログ (2件) を見る
あと、以下の日程でトークセッションをやる予定ですので、こちらも興味あれば是非(残席わずからしい)
SimpleMagick v1.1.0をリリースした
オプションの使い方を(内部的に)変更した
いままで、明示的にresizeやquality等いくつかのメソッドを実装し、他は足りなければ additional_option
で直接呼んでねっていうつもりで書いてたんだけど、それはあんまりだろうってことでmethod_missing使ってImageMagickで定義されてるオプションだったらそのオプション名を使うことにした。
一番でかい変更はImageMagickのオプションを定義するの毎回コピペするのだるいのでRakeタスクにして、Rubyのファイル自体作っちゃってる所ですね。使う分には互換性は保てているハズ。たぶん。
ところで今日は3/11ですね
3/11といえば、今年からはWebアプリエンジニア養成読本の発売日という意味が含まれるようになりました。よろしくお願いいたします。
Webアプリエンジニア養成読本[しくみ、開発、環境構築・運用…全体像を最新知識で最初から! ] (Software Design plus)
- 作者: 和田裕介,石田絢一(uzulla),すがわらまさのり,斎藤祐一郎
- 出版社/メーカー: 技術評論社
- 発売日: 2014/03/11
- メディア: 大型本
- この商品を含むブログ (2件) を見る
Ruby編、PHP編で書かれているサンプルソースはGitHub上で公開されていますので、学びがありそうでしたら本書を手にとっていただけると嬉しいです。
ImageMagickをラップするSimpleMagickっていう薄いライブラリを作った
なぜか
MiniMagickが遅すぎたのだった。
MiniMagickはナウなヤングが使うイケテルツールとのことだったのだけど、コマンドにオプションを渡す度に mogrify
コマンドを実行するため、でかい画像であればあるほど、前処理の為に時間が掛かる。まー初回にresizeを噛ませばちょっとはましになるところだけど、それにしても毎回実行してほしくない感じなのだった。
で、作ったのがこれ
使い方はREADMEにある通りだけど、こんな感じ。
require 'simple_magick' if SimpleMagick.imagemagick_installed? image = SimpleMagick::Image.new('/path/to/src_image.jpg') image.resize '150x150' image.convert! '/path/to/dest_image.jpg' end
実行するには ImageMagick 自体がインストールしてある必要がある(mogrify
を直接実行しているため)。
ベンチマークを取ると、ひとまずMiniMagickよりは早いことがわかる(これもREADMEに書いてある)。サンプルで使う画像次第で、如実に結果は変わるんだけど、自分が必要に迫られている現実ではもっと大きな差が出た(というか、ちょっと実用に耐えられなかったので自分で作ったのだけど)。
% bundle exec ruby benchmark.rb user system total real simple_magick 0.080000 0.270000 28.530000 ( 29.687068) mini_magick 0.410000 0.790000 31.300000 ( 37.115507) ImageMagick 0.030000 0.170000 28.620000 ( 29.094316)
どうしても書かなくてはいけない宣伝
ぼくも一部執筆をした書籍が発売されます!!ジュンク堂等で先行発売しているところもあるようですが、3/11が正式な発売日です。
Webアプリエンジニア養成読本[しくみ、開発、環境構築・運用…全体像を最新知識で最初から! ] (Software Design plus)
- 作者: 和田裕介,石田絢一(uzulla),すがわらまさのり,斎藤祐一郎
- 出版社/メーカー: 技術評論社
- 発売日: 2014/03/11
- メディア: 大型本
- この商品を含むブログ (2件) を見る
内容に関しては、献本しがいのある素晴らしい感想が書かれているエントリを見て頂ければ良いかなと思います。
出版記念イベントがあります!!!
電話で予約するという高難度イベントですが、まだ席に空きはあるそうなので、もしお時間があればぜひ。 - https://www.junkudo.co.jp/mj/store/event_detail.php?fair_id=4314
毎年忘年会に参加し続けた結果www
執筆する機会を得ることができまして、「Webアプリエンジニア養成読本」という本が近日発売する運びとなりました(ごめんね、ごめんね、タイトルは釣りです……)。
Webアプリエンジニア養成読本[しくみ、開発、環境構築・運用…全体像を最新知識で最初から! ] (Software Design plus)
- 作者: 和田裕介,石田絢一(uzulla),すがわらまさのり,斎藤祐一郎
- 出版社/メーカー: 技術評論社
- 発売日: 2014/03/11
- メディア: 大型本
- この商品を含むブログを見る
著者紹介の文章、テンションおかしくて胸が熱くなります。
この本ってどんな本なの
今までWebアプリを書いたことがない人向けです。例えば、学校でプログラミング言語自体はならったけどWebアプリ作ったことないわーみたいな人とか。
正直な所、この本をもってプログラミング言語が理解できるような深い内容ではないです。まずは写経して、「なるほど、わからん」という部分はその他の入門書やWebサイトなどで補完してもらうという形です。
その代わり、HTTPの通信の仕組みやサーバーを構築するための基礎知識や運用監視についてまで、抑えておくと良い点が揃っていると思います。ようするに、Webアプリを作りたい!!だがどうすれば?という時に、この本に書かれている要素を頼りにしてより深く調べて行ってもらえると良いのかなと思います。
また、本の出版イベントも行う予定ですので、もし興味のある方はいらしてください。
三行で執筆経緯を
- ハチイチ忘年会に毎年参加してたら仲の良い人ができた
- 毎年朝までいるもんで幹事をやることに
- そんなこんなでゆーすけべーさんからお声がけ頂くことに
所感
履歴を見ると、11/14に声を掛けてもらって、その後編集者さんと顔合わせや構成についての意識合わせをしたのが11月末くらいです。なので、2〜3ヶ月で執筆や校正をした感じですね。 こういうスプリント戦は(書くのが決まれば)楽で良いです……*1
執筆陣のスピード感が非常に良く、レスポンスの良さだったり締め切りにきちんと合わせてくるであったり、なんというか息のあった良いチームでした。
Webアプリを作ろうという2章の構成ではPHP編とRuby編*2という別の言語それぞれ同じような題材を扱うという話なんですけど、PHP編を書いてる @uzulla さんが超絶文章書きまくってて*3本当にスゴいと思いました。スゴいんですよ。いやースゴい。
ささたつ会議(あるいは新年会)に行ってきました
去る1/26、ささたつ会議が行われました。
ノリで参加したのですが、ワイワイしながら料理を作るというリア充臭あふれるイベントでした。
記憶に残っているメモ
- 2kgあった鶏肉が一瞬で無くなった
- 酒もドンドンなくなっていく
- すぎゃーんさんの将来が心配です
- 一流の人間は何をやっても*1一流になるのがわかったし心配いらないよ
- 料理も酒もうまくてヤバい感じだった
- 料金も2000円くらい?でコスパ厨歓喜
- というか手作り料理って滅多に食べないので感動もひとしおでしたね
写真で振り返るささたつ会議
写真NGの人もいるかもしれないので、とりあえず大丈夫そうな人だけ写ってるヤツを乗っけてみます。
これはしめ鯖という食べ物で、とてもおいしい
これは福井県民が涙を流しながら食べるという逸話のあるおあげ。おいしい
「たーっちゃん」って呼ばれて照れてる世界一の幸せ者
いわゆる唐揚げと呼ばれる食べ物なのですが、皿がテーブルに置かれた瞬間に食べられしまう
ラーメン屋のポーズ
どあきちゃん(CV: @do_aki)
イケメンはネギを持ってもイケメンであると暗に自慢している図
ちなみに、世界一の幸せ者タスキは @kamipo に継承されました
かみぽさん、ささたつから幸せ継承した。2014年本気出すらしい。 #299kaigi pic.twitter.com/zF3biqSBHT
— Yosuke FURUKAWA (@yosuke_furukawa) 2014, 1月 26
唐揚げの作り方を学ぶことは出来ませんでしたが、とても楽しかったですし、料理も美味しかったです。料理を作ってくれた方、お酒を持ってきてくれた方、企画・実行をしてくれたささたつさん、ありがとうございます!!1
関連リンク:ささたつ会議(新年会?) - Togetterまとめ
*1:ドルヲタでも