雑草SEの備忘録

東大を卒業し、SEとして働くことになった。備忘録的に作業を綴る。

ruby 1.9系のラムダ記法

今時rubyの1.9系を使っている人なんていないと思いますが、私は仕事の都合上、1.9系を使うときがあります。
しかも、同じリポジトリでサーバーによってrubyのバージョンが2.1系だったり、1.9系だったりするので厄介です。

今回、仕事でrubyのラムダ記法で2系ならOKなのに1.9系だったらだめだった書き方があったので、備忘録的に紹介します。

まずは、アロー関数ではなくlambdaと書いた場合は問題ありません。

$ ruby -v
ruby 1.9.3p551 (2014-11-13 revision 48407) [x86_64-darwin15.2.0]
$ irb
irb(main):001:0> function = lambda do |x|
irb(main):002:1* puts x * 2
irb(main):003:1> end
=> #<Proc:0x007fabc195d9d0@(irb):1 (lambda)>
irb(main):004:0> function.(3)
6
=> nil

そして、アロー関数での書き方。

irb(main):005:0>
irb(main):006:0* function = ->(x) {
irb(main):007:1* puts x * 2
irb(main):008:1> }
=> #<Proc:0x007fabc1963560@(irb):6 (lambda)>
irb(main):009:0> function.(3)
6
=> nil

このように書くとうまくいきます。
しかし、

irb(main):010:0> function = -> (x) {
irb(main):011:1* puts x * 2
irb(main):012:1> }
SyntaxError: (irb):10: syntax error, unexpected tLPAREN_ARG, expecting keyword_do_LAMBDA or tLAMBEG
function = -> (x) {
               ^
(irb):10: syntax error, unexpected tLAMBEG, expecting $end
	from /Users/username/.rbenv/versions/1.9.3-p551/bin/irb:12:in `<main>'

このように書くとSyntaxErrorになります。何が違うのかというと、ラムダの矢印のあとの引数のカッコが矢印にくっついているかいないか。1.9系だと、くっついていないとSyntaxErrorになります。

2系以上だと、離れていてもSyntaxErrorにはなりません。

$ ruby -v
ruby 2.0.0p648 (2015-12-16 revision 53162) [x86_64-darwin15.2.0]
$ irb
irb(main):001:0> function = -> (x) {
irb(main):002:1* puts x * 2
irb(main):003:1> }
=> #<Proc:0x007faf791cb288@(irb):1 (lambda)>
irb(main):005:0> function.(3)
6
=> nil

今は1.9系を使うことなんてないかもしれませんが、行けると思って知らないとはまるのでご紹介でした。

unless A && Bを止めたい

タイトルの通りである。unless A && Bはわかりにくい。

例えばRuby on Railsで開発している時。

array1 = ["text1", "text2"]

array2 = []

という二つの配列があったとする。

puts "TEST1" unless array1.present? && array2.present? #1

puts "TEST2" unless array1.present? && array2.blank? #2

puts "TEST3" unless array1.blank? && array2.present? #3

puts "TEST4" unless array1.blank? && array2.blank? #4

puts "TEST5" unless array1.present? || array2.present? #5

puts "TEST6" unless array1.present? || array2.blank? #6

puts "TEST7" unless array1.blank? || array2.present? #7

puts "TEST8" unless array1.blank? || array2.blank? #8

この8つのうちどれがputsで文字列が表示されるのか?頭の中だけで考えてみると私は混乱する。

例えば#1だと、array1は存在するからarray1.present?はtrue でarray2は存在しないからarray2.present?はfalse。true && falseなのでtrue。そのunlessなのでこれは表示されない??

いや違う。これは表示される。

pry(main)> puts "TEST1" unless array1.present? && array2.present?
TEST1

これはわかりにくい・・・。unless A && Bは非常にわかりにくいのだ!(私には)

そこでunlessをifで書き換える。

puts "TEST1" if array1.blank? || array2.blank? #1

puts "TEST2" if array1.blank? || array2.present? #2

puts "TEST3" if array1.present? || array2.blank? #3

puts "TEST4" if array1.present? || array2.present? #4

puts "TEST5" if array1.blank? && array2.blank? #5

puts "TEST6" if array1.blank? && array2.present? #6

puts "TEST7" if array1.present? && array2.blank? #7

puts "TEST8" if array1.present? && array2.present? #8

そうすると#1はarray1はblank?そうじゃないね。じゃあarray?はblank?そうだね。TEST1が表示されるね。

とわかる。個人的にはこちらの方がわかりやすい。

unless A && B

if !A || !B

にしたほうがわかりやすいと思う。
これは高校の数学Aで習うド・モルガンの法則というやつだ。

\overline{A \land B} = \overline{A} \lor \overline{B}
個人的には、unless A && Bとなるときはif !A || !Bとするようにルールを決めても良いくらいだと思っている。

Ruby on Rails初心者が一歩進むために~2~

タイトルの通り。Ruby on Railsを勉強していてこの使い方知ってるとだいぶ幅が広がるなというのがあったので、メモ。

今回は、reduceです。エイリアスでinjectというのもあって、両者は同じ挙動をします。

1.reduce, inject

http://ref.xaio.jp/ruby/classes/enumerable/inject

reduceやinjectの使い方ですが、上記サイトには、次のように書かれている。

injectメソッドは、ブロックを使って繰り返し計算を行うのに使います。ブロックに順に「要素1、要素2」、「ブロックの前回の戻り値、要素3」、「ブロックの前回の戻り値、要素4」、...を渡します。メソッドの戻り値はブロックが最後に返した値になります。

うん。。よくわからない。
それで、Exampleを見てみる。

numbers = [4, 3, 9, 8, 5, 6, 1, 7, 2]
puts numbers.inject {|sum, n| sum + n }
puts numbers.inject(100) {|diff, n| diff - n }

これですぐ理解できるという人はすごい。私は少し悩んだ。
numbersという配列があって、その要素を一つ一つをとりだすのが、

numbers.inject {|sum, n| sum + n }

のnの部分。それで、sumはどこからでてくるのかというと、injectの引数。一つ目の例は、引数が省略されているが、引数が無い時は、

numbers.inject(0) {|sum, n| sum + n }

と同じ。sumはこのinjectの引数(この場合は0)を受ける。injectはブロックをとるので、その中で処理を書く。今回の例の場合は、
0に4を足して4、
4に3を足して9、
・・・
43に2を足して45
という感じ。つまり、各要素に対して繰り返し同じ処理をしたいときに使える。
引数を二つとってこんな書き方もできる。

numbers.inject(0, :+)

一つ目の引数は初期値。二つ目の引数はメソッド名。今回の場合は足し算。
Rubyはメソッドをシンボルにして呼び出すことが良くある。public_sendなんかはその最たる例であろう。

2.each_with_object

http://ref.xaio.jp/ruby/classes/enumerable/each_with_object

tags = [
  {href: "http://example1.com", tag: "tag1", style: "class1"},
  {href: "http://example2.com", tag: nil, style: "class2"},
  {href: nil, tag: "tag3", style: "class3"},
  {href: "http://example4.com", tag: "tag4", style: "class4"}
]

このような配列があったとする。hrefやtagがnilのない配列を作りたいとする。
そういうときは、each_with_objectを使ってこのように書くことができる。

tags.each_with_object([]) do |tag, array|
  array << { href: tag[:href], tag: tag[:tag], style: tag[:style] } unless tag[:href].nil? || tag[:tag].nil?
end

結果はこちら。

=> [{:href=>"http://example1.com", :tag=>"tag1", :style=>"class1"},
 {:href=>"http://example4.com", :tag=>"tag4", :style=>"class4"}]

each_with_objectではブロックの第2引数の処理結果が返り値となる。
今回の場合だと、arrayという変数だ。初期値は[]で空の配列。
次のように書くのと同じ動きをする。

# 変数の初期化
array = []
tags.each do |tag|
  next if tag[:href].nil? || tag[:tag].nil?
  array << { href: tag[:href], tag: tag[:tag], style: tag[:style] }
end

変数arrayを見ると、

[25] pry(main)> array
=> [{:href=>"http://example1.com", :tag=>"tag1", :style=>"class1"},
 {:href=>"http://example4.com", :tag=>"tag4", :style=>"class4"}]

こんな感じだ。
このやり方だと変数を初期化してループでぐるぐるまわすというのがいかにもrubyっぽくない。そこでeach_with_objectの登場というわけだ。
reduceで書くこともできる。

tags.reduce([]) do |array, tag|
  array << tag unless tag[:href].nil? || tag[:tag].nil?
  array
end

ただし、reduceの場合、ブロックの最後にarrayを返さないといけない。またdoのあとの引数もeach_with_objectとはちょっと違うの注意が必要だ。

The Philosophy of Rubyを読んでみた。

Rubyを使い始めて4か月目に入りました。Rubyを勉強していくうちに2003年に行われたRubyの生みの親であるまつもとゆきひろ氏のインタビュー記事があり、日本語訳が見当たらなかったので、勉強がてら和訳してみました。

 

 以下、長いですが、引用です。

Rubyの哲学

www.artima.com

要約

まつもとゆきひろRubyの生みの親)はRubyの設計哲学についてBill Vennersと対談した。設計の不完全性、直行性の危険性、コンピューターの努力における人間の重要性について話した。

まつもとゆきひろ(ネット上ではMatzと呼ばれている)はRubyの生みの親だ。Rubyは、日々プログラミングをするのにもフルスケールのアプリケーションを作るのにも両方に適したオブジェクト指向の言語だ。MatzのRuby開発は1993年にまで遡る。書くのが楽しく、生産性を向上させる言語が欲しかったからだ。最初は日本で人気になったが、Rubyは世界中のプログラマーのハートをつかんだ。

2003年9月24日、Bill VennersはデンマークのAarhusで開かれたJAOO会議でMatzと対談した。この対談で、MatzはRubyの設計哲学、Rubyの特徴、より良いプログラマーになることについて論じた。この記事ではMatzは設計の不完全性、直行性の危険性、ある程度の道しるべを与えながらも自由を与えること、驚き最小限の原則、コンピューターの努力における人間の重要性について述べる。

 

完璧な言語なんてない

Bill: Dave Thomas(プログラミングRuby の共著者)は、あなたが言語の設計が完璧であるはずはないと思っていると言った。なぜだ?

Matz: 言語設計者は完璧な言語を設計したいと思う。言語設計者はできるなら、「私の言語は完璧だ。なんでもできる」と言いたい。だが、完璧な言語を設計するのはただただ不可能だ。なぜなら言語を見るのに二つの側面があるからだ。一つはその言語で何を成すことができるか。もう一つは、その言語を使ってどう感じるか、つまり、プログラミングの最中に人間がどう感じるかだ。

チューニング完全理論のために、チューニング完全言語でできることはすべて、理論上は他のチューニング完全言語でもできる。しかし、コストが異なる。アセンブリ言語でもすべてできる。だが、もはやだれもアセンブリ言語でプログラミングをしたいとは思わない。何が出来るかという観点で考えれば言語は異なる。ただ、違いは限られている。例えば、PythonRubyプログラマーにとってほとんど同じ力を提供してくれる。

私は「何」の側面ではなく、「どのように」の側面を強調したい。つまり、プログラミングするときにどう感じるかだ。それがRubyの他の言語との主な違いだ。私はフィーリング、特にRubyを使って私がどう感じるかを強調したい。私はRubyをすべての人にとって完璧にするために頑張りたくはない。私が感じることとあなたが感じることは違う。すべての人にとって完璧な言語なんてない。私はRubyを私にとって完璧にしたい。おそらくそれはあなたにとっては完璧ではないだろう。Guido van Rossumにとって完璧な言語はPythonであるように。

直行性VS調和性

Bill: Dave Thomasはもし私が直行性のある特徴を加えるようにあなたにお願いするなら、あなたはそれをしないだろうとも言った。あなたがしたいのは調和のとれたことである。調和とは何を意味するのか?

Matz: 私は、一貫性と直行性は設計の道具にすぎず、設計の最優先の目的ではないと思っている。

Bill: ではこの文脈で直行性とは何を意味する?

Matz: 直行性の一例はちょっとした特徴やシンタックスを結び付けれるようにすることだ。例えば、C++は関数に対し、デフォルトパラメータ値とパラメーターに基づいた関数名のオーバーロードすることの両方ができる。両方とも一つの言語の持つ良い特徴だが、それらは直行性があるので、同時に適用することはできない。コンパイラはそれらを同時に適用する術を知っている。もしそれが少し曖昧なら、コンパイラはエラーを起こすだろう。しかし、コードを見ると私は私の頭でそのルールを適用する必要がある。つまり、私はコンパイラがどう動作するかを推測する必要がある。もしその推測が正しいなら私は十分に賢い。そういうときは問題はない。しかし、私があまり賢くなければ、そして私は実際に賢くないが、それは混乱の元となる。その結果は普通の人間にとっては予想のしないことだ。これがどのように直行性が良くないかを示す一例だ。

Bill: つまり、直行性は書き手がそれらを理解して作用させたとき、うまく動作すると。けれど、プログラマーにとっては、それを見たときに理解するのは難しい。コンパイルされるから。そして、私はこれらの二つの事象がどう一緒に動くか判断しなければならないから。

Matz: 直行性という特徴は、結び付けられた時とても複雑になる。

Bill: では、代替のものは何か?さらなる調和か。

Matz: 言語に組み込むために、それら二つのうち一つをピックアップする。あなたは考えているすべてをする必要はない。それらのうち一つを選べば良い。両方とも良かったとしても。

自由と安らぎ

Bill: Pythonコミュニティの設計哲学の一つは、一つだけ提供すること。ものごとをするのに一つだけやり方を提供すること。もしあなたが、同じことをするのに50の異なる方法を提供したら、書き手には便利さを提供することになる。みんな好きな方法で書くことができる。トレードオフは読み手だ。例えば、私がある人のコードを読むとき、その人はある一つの方法で書く。次に別人のコードを読むとき、その人は別の方法で書く。だから読み手として、私はタスクを完了させるために結局すべての方法に精通する必要がある。私の好きな方法だけじゃなくて。それが設計のトレードオフだ。Pythonコミュニティは一つの方法を好むようだ。たった一つの。しかし、Rubyは同じことをするのに複数の方法を提供しているように見える。

Matz: RubyPerlの哲学を継承している。それは同じことをするのに複数個提供するということだ。わたしは、Larry Wallからその哲学を受け継いだ。Larry Wallは私のヒーローだ。私は、Rubyユーザーに自由になってほしい。私は彼らに選択の自由を与えたい。人間はみな違う。人間はみな様々な基準を持っている。しかし、多くの代替方法の中にはより良い方法がある。私は快適にすることによってその方法を奨励したい。だから、それが私が挑戦してきたことだ。おそらくPythonのコードはRubyより読みやすいだろう。みんな同じスタイルで書く。だからたぶん読みやすい。しかし、人と人との違いはとても大きい。たとえPythonを使っているとしても、一つのやり方だけを提供することはほとんど助けにはならない。私はそう思う。私は可能ならばたくさんの方法を提供したい。けれども、良い方法を推奨するユーザーや道しるべを提示してくれるユーザがより良い方法を選んでくれるだろう。

Rubyの楽しみ

Bill: あなたは、Ruby導入記事で、「私にとって、人生の目的の一部は喜びを持つことだ。プログラマーはプログラミングという創造に集中することができると、よく楽しみを感じる。だから、Rubyプログラマーを幸せにするために設計されている」と書いた。Rubyはどのようにプログラマを幸せにできますか?

Matz: あなたは人生を幸せにしたいよね。もしあなたが仕事を素早くできたら、あなたの仕事は楽しい。良いと思わないかい?それが人生の目的の一つだ。あなたの人生はより良くなる。

私はコンピュータを使うことで日々の生活で直面する問題を解決したい。だから私はプログラムを書く。Rubyを使うことで、その言語の摩訶不思議な規則ではなく、私がすることに集中したい。"print hello world"と言うためにpublic void なんちゃらという呪文から始めるのではなく、私は単に"print this!"と言いたい。私は周囲の摩訶不思議なキーワードはいらない。私はタスクに集中したいだけなのだ。それが基本的な考え方だ。だから私はRubyコードを簡潔にすることに挑戦してきた。

Bill: 簡潔なコードが書けることは、プログラマーを幸せにする方法の一つだ。

Matz: そうだ。結果、彼らは解決しなければならない課題そのものに集中できる。プログラマはときどき紙に疑似コードを書くことがある。もしその疑似コードが直接パソコンで動いたらすごいと思わないかい?Rubyはそうできるようになることを目指している。Pythonがそう言われるように。

Bill: そう、Pythonは実行可能な疑似コードだとみんな言っている。Rubyで他にプログラマを幸せにすることは何だい?

Matz: プログラマとしての日々の生活の中で、私たちはたくさんのテキスト文字列を処理する。だから私はテキスト処理に一生懸命取り組もうとしている。つまり、Stringクラスと正規表現だ。正規表現は言語にビルトインされていて、使用に適したチューニングが施されている。私たちは何度もOSを呼び寄せる必要もある。RubyUnix上のあらゆるシステムコールWindows APIのほとんどを呼ぶことができる。これで、OSの力や機能を言語環境の解釈に割ける。だからあなたは日々のシステム管理やテキスト処理プログラミングができる。それが少なくとも私の人生の中の主な領域だ。だから私はそれを良くするために一生懸命活動する。

Bill: だから基本的に、自分の仕事を早くそして楽しく終わらせるのに役立つことで、Rubyは私の生活を楽しませてくれると?

Matz: 私にとってはね。あなたにとってそうかどうかは分からない。そうであることを望むけど。

ヒューマンファクター

Bill: インタビューの中で、あなたは次のように述べた。「ヒューマンファクターを過小評価してはいけない。たとえパソコンの前にいたとしても、パソコンは機械だ。私たちは人間のために人間と一緒に働く。」これはどういうことを意味する?

Matz: メールを書いているときを想像してほしい。そのときあなたはパソコンの前にいる。パソコンを操作している。マウスをクリックしたり、キーボードを打ったりしてね。だけれども、メールのメッセージはインターネットの先の人間に届く。だからあなたはパソコンの前で働いていると言ってもそのパソコンの後ろには人がいる。私たちがしていることのほとんどは人間のためにやっていることだ。例えば税金計算によって政府は私の財布からお金を引き出す。政府は人間で構成される。

私たちの仕事のほとんどは結局人間に関係している。だからプログラミングによって、私たちは、パソコンに人間のために働くよう求めたり、コンピュータが実行できるレベルのとても明確な方法で私たちの考えを表現することができる。
一つ目のケースとしては、人間のために働くコンピュータを作ることだ。ターゲットはコンピュータの向こうの人間だ。もう一つのケースは、コンピュータによって理解され、実行される程度に考えを明確に表現することで、私たちは自分の脳から意図を表現し、結果的にコンピュータに実行される。両方のケースでの対象はやはり人間だ。

Bill: そのように考えるのは何が重要なのか。あなたはヒューマンファクターを過小評価するなと言った。なぜか。

Matz: コンピュータは、コンピュータとコミュニケーションをとろうと努力しなければいけないかどうかやコミュニケーションがとりやすいかどうかは気にしない。コンピュータは私がファイル内のバイト列の命令番号を入力し、実行させるためにそれを送信しなければいけないかなんて気にしない。あるいは、とても高級な言語が命令を生成したかなんて気にしない。コンピュータは気にしないのだ。私たち人間は私達がする努力を気にする。人々、特にコンピュータエンジニアは、機械に焦点を当てる。「これをすることで、そのマシンはもっと早くなる。これをすることでそのマシンはもっと効率よく動く。これをすることで、そのマシンは・・・」と考える。彼らは機械にフォーカスする。しかし実際には私たちは人間に、そして、人間がどのようにプログラミングをするかや、どのようにアプリケーションを操作するかに焦点を当てる必要がある。私たちが支配者で、機械が奴隷なのだ。

Bill: しばらくの間はね。

Matz: しばらくの間、そう、ターミネータ世代のうちは。

驚き最小の原則

Bill: インタビューの中で、あなたはこう述べた。「私は自分の驚きを最小限にするためにRubyを設計した。私は、世界中の人々がRubyが驚きを減らし、プログラミングの喜びを高めたと教えてくれた時とても驚いた。今や私はプログラマーの心は世界中で似ていると確信している。」

なぜ驚き最小の原則なのか。

Matz: 実際には、私はRubyが驚き最小の原則に従うと主張してはいなかった。何人かの人がRubyの設計がその哲学に従っていて、そう言いだした。実際は私はそれを持ち出してはいない。

私はプログラミング中の自分のフラストレーションを最小にしたかった。だから私はプログラミングにおける努力を最小にしたい。それがRubyを設計する上での私の主な目標だ。私はプログラミングでもっと楽しみたい。Rubyをリリースしてから、世界中のたくさんの人がRubyを知るようになって、私が感じるように彼らも感じると言った。彼らは驚き最小の原則というフレーズを思いついた。しかし実際にはしばしば誤解される。

Bill: どう誤解される?

Matz: 人々はみな個々人のバックグラウンドを持っている。ある人はPythonからきたかもしれないし、またほかのある人はPerlからきたかもしれない。そして言語のさまざまな側面に驚くだろう。そして私の所に来てこう言うのだ。「私はRubyのこの特徴に驚いた。だからRubyは驚き最小の原則に反する」と。ちょっと待ってほしい。驚き最小の原則はあなただけのものじゃない。驚き最小の原則は私の驚き最小の原則を意味する。そしてそれはあなたがRubyをしっかりと勉強してからわかる。例えば、私はRubyの設計を開始する前、C++プログラマだった。私は2~3年間、C++だけでプログラミングをしてきた。そしてC++のプログラミングを2年間やってもまだ私は驚いていた。

Ruby on Rails初心者が一歩進むために~1~

タイトルの通り。Ruby on Railsを勉強していてこの使い方知ってるとだいぶ幅が広がるなというのがあったので、メモ。

今回は、define_methodとdefine_singleton_methodです。キーワードはメソッドの動的生成。

1.define_method

define_method (Module) - Rubyリファレンス

メソッドを定義することができる。
同じ処理を何度もしたい!けどいちいち一つずつ定義するのは面倒だ!なんてときに使います。繰り返し似たようなメソッドを作るときには売ってつけです。
たとえば、次のようなCurrencyモデルがあったとします。

Currencyモデル

id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL
code varchar(255)
appelation varchar(255)

currenciesテーブルのデータ

id code appellation
1 JPY
2 USD ドル
3 TWD 台湾ドル

そして、例えば、一つデータをとってくる。

[2] pry(main)> currency = Currency.first
  Currency Load (0.4ms)  SELECT  "currencies".* FROM "currencies"   ORDER BY "currencies"."id" ASC LIMIT 1
=> #<Currency id: 1, code: "JPY", appelation: "円">

ここで、とってきたデータがJPYかどうかを調べたいというとき。Currencyモデルにjpy?というメソッドを作る。

class Currency < ActiveRecord::Base
  def jpy?
    code == 'JPY'
  end
end

こんな感じでjpy?というメソッドを追加します。
さて、同様にusd?とかtwd?とかいうメソッドも追加したい。だけれども、今後通貨が増えてくるし、いちいちメソッドを定義するのは面倒だ!という時に、一気にメソッドを定義してくれるのがdefine_method。
例えば、CODESという通貨コードをもった配列を用意して、くるくるまわす。

class Currency < ActiveRecord::Base
  CODES = ['JPY', 'USD', 'TWD']

  CODES.each do |currency_code|
    define_method("#{currency_code.downcase}?".to_sym) { code == currency_code }
  end

  # 上のブロック内のdefine_methodによって以下が定義されたのと同じ。
  #def jpy?
  #  code == 'JPY'
  #end
  #def usd?
  #  code == 'USD'
  #end
  #def twd?
  #  code == 'TWD'
  #end
end

結果がこちら。きちんとメソッドが定義されていて、期待通りの動きになっていることが分かる。

[1] pry(main)> currency = Currency.find(2)
  Currency Load (0.8ms)  SELECT  "currencies".* FROM "currencies"  WHERE "currencies"."id" = ? LIMIT 1  [["id", 2]]
=> #<Currency id: 2, code: "USD", appelation: "ドル">
[2] pry(main)> currency.jpy?
=> false
[3] pry(main)> currency.usd?
=> true
[4] pry(main)> currency.twd?
=> false
2.define_singleton_method

define_singleton_method (Object) - Rubyリファレンス

こちらも基本的には、define_methodと同じです。ただし、特異メソッドを作ってくれます。Railsだと、例えば、モデルにクラスメソッドを動的に作成したいときに威力を発揮します。
今回の例ですと

Currency.twd

とやると、台湾ドルのデータをとってこれるようにしたいという場合を考えます。
単純にクラスメソッドを定義してやればいいです。

class Currency < ActiveRecord::Base
  def self.twd
    where(code: 'TWD').first
  end
end

ただ、これも同様に日本円やUSドルも同様に作りたいですよね。define_methodと同じようにすればいいです。define_singleton_methodをつかうと、クラスメソッドをはやすことができます。

class Currency < ActiveRecord::Base
  CODES = ['JPY', 'USD', 'TWD']

  CODES.each do |currency_code|
    define_singleton_method("#{currency_code.downcase}".to_sym) do
      where(code: currency_code).first
    end
  end

  # 上のブロック内のdefine_singleton_methodによって以下が定義されたのと同じ。
  #def self.jpy
  #  where(code: 'JPY').first
  #end
  #def self.usd
  #  where(code: 'USD').first
  #end
  #def self.twd
  #  where(code: 'TWD').first
  #end
end

rails consoleで動作を確認してみます。

[1] pry(main)> Currency.jpy
  Currency Load (0.2ms)  SELECT  "currencies".* FROM "currencies"  WHERE "currencies"."code" = 'JPY'  ORDER BY "currencies"."id" ASC LIMIT 1
=> #<Currency id: 1, code: "JPY", appelation: "円">
[2] pry(main)> Currency.usd
  Currency Load (0.5ms)  SELECT  "currencies".* FROM "currencies"  WHERE "currencies"."code" = 'USD'  ORDER BY "currencies"."id" ASC LIMIT 1
=> #<Currency id: 2, code: "USD", appelation: "ドル">
[3] pry(main)> Currency.twd
  Currency Load (0.4ms)  SELECT  "currencies".* FROM "currencies"  WHERE "currencies"."code" = 'TWD'  ORDER BY "currencies"."id" ASC LIMIT 1
=> #<Currency id: 3, code: "TWD", appelation: "台湾ドル">

GemfileとGemfile.lockの簡単なお話

Ruby on Railsをやり始めたときだと、Gemfileって何?そんでbundle installするとつくられるGemfile.lockって何?みたいな感じですよね。
今日は、GemfileとGemfile.lockの簡単なお話です。

gemっていうのは、Javaでいうところのライブラリみたいなものです。結構頻繁に使われるあるまとまった処理があって、それを毎回開発するたびに書いているのは大変なのでgemという形にしてみんなが使えるようにして配布しているわけですね。

開発者は、この機能を作りたい!そのためには、このgemが必要だ!って思った時は、Rails.rootにあるGemfileに追加します。
だけど、gemの中にはさらに別のgemを使っているgemもあったりして、さらにそのgemも別のgemを使っていて…とかが往々にしてあります。
さらに、Aというgemを使うためにはZというgemのバージョン1.2.1が必要だけど、Bというgemを使うためには、Zというgemを使うんだけど、1.3.0のバージョンが必要で…みたいな話もあります。

そこで、Railsだと、bundle installするときにそのあたりの依存性をよしなに解決してくれるわけですね。そして、その解決した依存性をGemfile.lockというファイルに記録します。
Gemfile.lockをリポジトリに入れて管理しておけば、そのリポジトリを別の環境に持っていったときも依存性は解決されているので、Gemfile.lockに従ってgemをインストールすればいいことになって、便利です。

ただ、たまに、Gemfile.lockそのままじゃだめなときがあります。私の過去の経験の話を少しします。
当時、開発は、AWS上のEC2で行ったり、Macのローカルの環境で行ったりしていました。gemの中には、なぜかEC2では難なくインストールできたのに、Macではうまくインストールされないというものがありました。
仕方がないので、私は、そのgemをGemfile.lockから消し、もう一度bundle installを行うとよしなに依存性が解決され、別のバージョンのgemがインストールされました。それで、無事めでたくbundle installを行うことが出来ました。

もちろん、本番環境とローカルでは、gemのバージョンが異なってくるわけですが、デプロイ時は本番環境に近い環境でbundle installするので、当時は問題は発生していませんでした。

私も含め初学者はこのあたりブラックボックスのまま突き進むことがあると思いますが、ちょっと振り返ってみました。


詳しい話は、(ベタですが)次の記事にお任せします。
www.rubylife.jp

当たり前だけど、ちゃんとgitのstatusを見てからaddしようね。

当たり前の事なんだけど、gitでcommitする前はきちんとgit statusして変更したファイルを確認し、必要なファイルだけをコミットするということをきちんとしないといけない。

当たり前の事なんだけど。

だけど、余りわかっていない初心者はとりあえず

git add .

して、

git commit -m 'commit message'

しちゃう。あるいは、この二つのコマンドを一気に

git commit -am 'commit message'

みたいなことをする。

もちろんstatusみたりもするけど、ふ~んみたいなかんじでスルーしちゃう。
まあ全部私の事なんですけど。

なんでかっていうと、言い訳だけど、初心者がやるチュートリアル的なもの
 Ruby on Rails Tutorial
とか
 RSpec の入門とその一歩先へ~RSpec 3バージョン~
とかがそんな感じでやってるから。
もちろん、これらはgitの使い方を学ぶものではないから、仕方がないんだけど、きちんと一回gitの勉強はしないとねと。

最近は学習して、

git status

でmodifiedファイルを確認。Ruby on Railsをやってたりすると、bundle installしたときにGemfile.lockとかも書き換わってる時があるので、それらはaddしないようにする。

git add file.rb

する。
それで、私の場合、一旦diffを見る。
addしない場合だと、

git diff

だけで見れるけど、addしたもののdiffを見るときは、

git diff --cached

で見ることができる。ここで間違いがないかを確認する。間違いがあればそのファイルを修正してもう一度addする。
間違いがないことを確認したら、

git commit -m 'commit message'

でコミットする。-amはなるべく使わない。
一旦addしたものを取り消したいなら、

git reset

を使う。commitする前だったらこれでaddを取り消せる。
commitしてしまったら、

git reset --soft HEAD^

を使って直前のコミットを取り消す。resetのオプションに--softならファイルの変更は取り消されない。コミットだけ取り消される。
オプションが--hardの場合はファイルの変更まで取り消されるので注意。ただ、gitにはこれもまた戻す方法がある。
自分の場合、今の所--softしか使う機会はないけど。