赤き血のイレブン

自分の復習ネタを書いてます。

actionviewのヘルパー(date_selectってなんじゃこりゃ〜)

雑談

東アジアカップは女子は一矢報いたけど、男子は全然でしたね。。。 そんな中、我らが武藤くんは2ゴールとしっかり結果を出してくれました!

今後のワールドカップ予選も期待ですね!

本題

さてさて、最近date_selectというメソッドを見つけたのでちょっと試してみました!

利用シーン

ユーザの情報などで誕生日(birthday)のような日付データ(date型)を保持するモデルがあるとして フォームでその誕生日をセレクトボックスで作るときにいちいち年、月、日と3つも作るの大変ですよね! そんなのrailsでやりたくない!ってことで標準であるみたいです。(うれしいですね)

使い方

使い方ですが、ビューでこんなかんじですかね。

= date_select :person, :birth_date

とすると

<select id="person_birth_date_1i" name="person[birth_date(1i)]"> ... </select>
<select id="person_birth_date_2i" name="person[birth_date(2i)]"> ... </select>
<select id="person_birth_date_3i" name="person[birth_date(3i)]"> ... </select>

おぉー。ちゃんと分かれてる。

あれ、パラメータ名がperson[birth_date(1i)]、person[birth_date(2i)]、person[birth_date(3i)]に変わってるやん! まじか。。。

でも、ちゃんと保存されてるし、どーなってんの?

新たな刺客、マルチパラメータ

実は、activerecord内部でマルチパラメータというものがあって これが、birthdayをがっちゃんこしてくれているそうです!

github.com

この辺りのソースを読むと カラム名+数字+(i or f)という感じのパラメータにするとまとめてくれるそうな。

birth_date(1i)は * birth_dateは元々のパラメータ名で * 数字の1は一つ目 * iはinteger型に変換(日付は分解するとそれぞれ数値なので) に分解できる。

で、丸かっこもフォーマットとしては必要みたいです。

今回のヘルパーは、activerecordの機能を前提とした実装だということがわかってめでたしめでたしでした!

Effective Ruby②

雑談

昨日はなでしこと北朝鮮東アジアカップでしたが、結果は残念でした。 DFが全然ダメでしたね。。

でも、杉田のミドルはすごかった! 男子でもあんなシュートを決めるのは稀ですからね。。。

今日は男子、昨日のなでしこの借りを返してほしい。

本題

前回に続きEffective Rubyです。。

superって

親子関係になるときに子からオーバーライドした親のメソッドを呼び出すのにsuperを使うんですけどね。。 気をつけないと引数の数がダメ―と怒られます。。

例えばこんな感じ

[1] pry(main)> class A
[1] pry(main)*   def a1(a=nil)
[1] pry(main)*     puts a
[1] pry(main)*   end
[1] pry(main)* end
=> :a1
[2] pry(main)> class B < A
[2] pry(main)*   def a1(a,b)
[2] pry(main)*     super # 親のメソッドを呼び出すが。。。
[2] pry(main)*   end
[2] pry(main)* end
=> :a1
[3] pry(main)> B.new.a1('a','b')
ArgumentError: wrong number of arguments (2 for 0..1)
from (pry):2:in `a1'

イメージとしたら引数を省略して呼び出したいときにsuperって書くとダメみたいで superだと、同じメソッドが呼び出されるようで引数も同じ数だけ渡されるようです。

上の例だと super super(a,b) って呼び出されているようなので、 引数を2つ許容するメソッドがないためエラーになります。

じゃあ、省略して渡すにはどうするか そう()つけるんですね。。。

[4] pry(main)> class C < A
[4] pry(main)*   def a1(a,b)
[4] pry(main)*     super() # 引数なしの親メソッドを呼び出し
[4] pry(main)*   end
[4] pry(main)* end
=> :a1
[5] pry(main)> C.new.a1('a','b')

=> nil

super() だと引数の省略で呼べるので間違えないように覚えておかないといかんですね。。。

Effective Ruby①

雑談

今日から、東アジアカップですね。 思い起こせば前回大会、柿谷・山口・青山などのメンバーが結果を残し ブラジルワールドカップに出たので、今回もしっかり活躍して頑張って欲しい。

個人的にはやはり浦和の柏木が頑張って欲しかったけど、怪我で離脱と無念。。 レッズ・武藤にも頑張ってもらいたいです! あとは五輪世代の遠藤・浅野に頑張ってもらいましょう。

※今日はなでしこの方ですね。。。

本題

Effective Ruby

中級者〜向けでしょうか。。 ひと通り勉強した人がみたほうがいいですね。 ※でも自分はひと通り勉強しましたが、まだまだ初級ですけどね。。。

200ページ弱なのでそんなに分厚くないですし、 48の項目に分かれているので読みやすいですね。

定数って(第1章 項目4より)

先頭が大文字の変数らしいですよ。 (普通は全部大文字で書きますよね。。)

[1] pry(main)> Hoge = "a".freeze
=> "a"
[2] pry(main)> Hoge.capitalize!
RuntimeError: can't modify frozen String
from (pry):2:in `capitalize!'

定数の破壊的な変更はできないみたいですが

[1] pry(main)> Hoge = "a"
=> "a"
[2] pry(main)> Hoge = "b"
(pry):2: warning: already initialized constant Hoge
(pry):1: warning: previous definition of Hoge was here
=> "b"

値の再代入はできるみたいで 警告が出ますね。。

あとコレクションはフリーズしても、

[1] pry(main)> HOGE = ["a","b"].freeze
=> ["a", "b"]
[2] pry(main)> HOGE.map{|hoge| hoge << "eee"}
=> ["aeee", "beee"]
[3] pry(main)> HOGE
=> ["aeee", "beee"]

中身が変更されちゃうので、中身もフリーズしましょうということで

[1] pry(main)> HOGE = ["a","b"].map!(&:freeze).freeze
=> ["a", "b"]
[2] pry(main)> HOGE.map{|hoge| hoge << "eee"}
RuntimeError: can't modify frozen String
from (pry):2:in `block in <main>'
[3] pry(main)> HOGE
=> ["a", "b"]

コレクションと要素をフリーズする必要があるらしい・・・ マップ、フリーズ、フリーズ・・・ 一回で出来たらいいのになぁ〜

RailsでDBの更新日だけ変更したい場合の話。〜touchしましょう〜

雑談

本日は平日ですが、J1セカンドステージ 第5節で浦和レッズはと言いますと引き分けでした。 セカンドステージに入り、てっきり勝てなくなりましたが代表戦のためリーグ戦は一時中断なので その間に立てなおして欲しいっす。。

本題

DBの更新日だけ更新せんかいや!

さて、表題の件ですが色々大人の事情があったりするわけで、 該当のレコードのデータに変更がなくても、更新日だけ変更したかったりすることもあるでしょう。。

updated_atに現在日時をぶっこんで、保存してもできますが。。。 もっとスマートにやりたいっす。 そんなときはこれ、 touch メソッドです。

実演

さて、実演です。

[1] pry(main)> hoge = Hoge.take
[2] pry(main)> hoge.touch
   SQL (0.4ms)  UPDATE `hoges` SET `hoges`.`updated_at` = '2015-07-29 14:25:38' WHERE `hoge`.`id` = 1

※クラスについてはフィクションです。

touch するとsaveがいらないんですね、これが。

さらに!

とはいっても、touchだけ使うというよりかは普通にデータの登録・更新の処理をするなかで一部だけ touch するというのが考えられるケースかなと。。。

なので、 changed? と一緒に使ったりするかなと。。 changed? は見たままですが、変更があれば(もしくは新規の場合も)trueで、なければfalseです。 今回の場合、変更がない場合に更新日を変更するので

if hoge.changed?
  hoge.save!
else
  hoge.touch
end

のような感じですかね。。 まぁ、更新日なので、更新しなかったら変更しないので普通なので 変更するに越したことないですが。。。

最後に

と、つらつら書いてきましたが 書いてる途中にupdated_atに現在日時をぶっこんでsaveしたほうが スマートなんちゃうんと思いました。。。