Railsのモデルの更新系メソッドをどう書くか
Railsでアプリを作っていて、モデルの更新系メソッドをどう定義するのが良いのだろうかと試行錯誤していたのだが、もうこれでいいんじゃないかな、というパターンが固まってきたのでメモっておく。
処理とエラーチェックの分離
メソッド定義内ではエラーチェックをせず、すべてがうまくいっているかのようなコードを書く。 状態を変更して保存し、自分自身を返す、というのがパターン。
エラーチェックはもっぱらバリデーションでおこなう。 定義したメソッドの実行時に必要なチェックが全て実行されるようにバリデーションを定義する。
# メインの処理 def do_something self.status = 'new_status' save self end # do_something実行時に実行されるようバリデーションの条件を指定する validate :can_be_new_status, if: 'status_changed? && new_status?' # バリデーションの定義 def can_be_new_status unless can_be_new_status? errors.add :status, :cannot_be_new_status end end
バリデーションは必要に応じていくらでも増やせる。 チェック内容すべてを一つのバリデーションに詰め込んでもいいし、それらを個別のバリデーションに分けてもいい。 はじめはひとつのバリデーションに詰め込んでおいて、複数のメソッドで共通のチェック内容が出てきたら分離すればよいかと思う。
呼び出し側でのエラーチェック
メソッドの呼び出し側で処理の成否を確認するには、返されたオブジェクトのvalid?やerrors.any?、persisted?などのメソッドを使う。
何がうれしいのか
この方式だと、検出したいエラーの種類を増やしたいときにメソッド本体を修正する必要がなく、単にバリデーションを追加していけばよい。 戻り値として返されるオブジェクトにはバリデーションで引っかかった項目すべての情報が含まれているので、何がダメだったのかこれ以上ないほどに詳しい情報が含まれている。
これがメソッド本体でif文をならべて最初に引っかかったチェックでエラーを返す方式だと、たまたま最初に引っかかった問題点しかわからないので、その問題を修正して再度処理を実行しても、今度はまた別の問題で引っかかる可能性がある。
エラーを返す方法としてBool値や例外を使うことも可能だが、Bool値だとエラーの詳細がわからないし、例外を使う方法だと、そもそもどんな例外を定義するか考えたり、一貫性のある例外の使い方をするよう気をつけたりする必要があって、面倒くさい。
メソッド定義とエラーチェックが分離しているのはコードを追うときに若干わかりづらくなるかもしれないけど、コードはスッキリするし、エラーの検出も網羅的になるので、利点は大きい。
エラーを検出するのにいちいちバリデーションを追加するのは手間だと感じるかもしれないが、成功したか失敗したかぐらいの判別しかできない中途半端なエラー情報を返すメソッドを定義しても、後々自分が不便な思いをして修正するハメになりがちだ。 それだったら、決まりきったパターンとしてメソッドとバリデーションを定義して、バリデーション通過後の自分自身を返すメソッドを黙々と書いたほうが結局は時間の節約にもなるんじゃないかな。
ActiveRecordでカウンター
呼ばれるたびに前回返した値から+1したものを返すカウンターのクラスを書いたものの、出番がなくなったが、今後同じようなものを書きそうなのでメモ。 動作はMySQLで試した。
テーブルの定義
class CreateCounters < ActiveRecord::Migration def change create_table :counters do |t| t.string :key t.integer :count, :default => 0 t.timestamps end add_index :counters, :key end end
クラス定義
class Counter < ActiveRecord::Base class << self def next(key) find_or_create_by_key(key).next end end validates_presence_of :key, :count validates_uniqueness_of :key def next with_lock do increment! :count count end end end
使い方
# クラスメソッドで使う Counter.next('foo') # => 1 Counter.next('foo') # => 2 Counter.next('foo') # => 3 # 違うキーを使うと別のカウンター扱いになる Counter.next('bar') # => 1 # インスタンス化して使う counter = Counter.find_or_create_by_key 'foo' counter.next # => 4 # 同じキーで別のインスタンスを作ると互いに影響する counter2 = Counter.find_or_create_by_key 'foo' counter2.next # => 5 counter.next # => 6 counter2.next # => 7
Railsでモデルデータのクラスをサブクラスに変更する
Single Table Inheritanceで親子関係にある二つのクラスHoge, Fugaがあるとする。
class Hoge < ActiveRecord::Base end class Fuga < Hoge end
このときHogeクラスのオブジェクトaがあったとしてそれをFugaクラスに変換したいとする。 そもそもそういう状況がオブジェクト指向的にどうなのかというのは置いておく。
このとき、
a = Hoge.find(1) b = a.becomes(Fuga) b.save!
とすればいいのかと思って試してみるも、ロードしなおしたオブジェクトはHogeクラスのままだった。
オブジェクトを保存するときのクエリーを見ると次のようになっていた。
UPDATE `hoges` SET `type` = 'Fuga', `updated_at` = '2013-04-08 14:04:33' WHERE `hoges`.`type` IN ('Fuga') AND `hoges`.`id` = 1
type In ('Fuga')の制約のために、更新したいレコードが引っかからなかったのだ。 親クラスから子クラスに変更するには、次のように元のオブジェクトのtypeカラムを直接変更するのが無難か。
a = Hoge.find(1) a.update_attribute(:type, Fuga.name)
becomesというメソッドの存在を知って、これを使えばクラスの変換もナチュラルな感じに書けるのかと期待したけど、結局typeカラムを修正するという、STIの実装に依存した書き方になった。
ちなみに子クラスから親クラスへの変換はbecomesとsave!を使ったやり方でもうまくいく。
Working with Unix Processes メモ
プロセスが持っているもの
ID
Process.pid
用途
- シグナルを送る
- ログファイル内でプロセスを識別
親
Process.ppid
ファイルディスクリプタ
passwd = File.open('/etc/passwd') puts passwd.fileno
扱えるリソースの限度
Process.getrlimit(:NOFILE) # => [256, 9223372036854775807] Process.setrlimit(:NOFILE, 10000)
ri Process.getrlimit
引数
ARGV[0]
環境
ENV["PATH"]
名前
$PROGRAM_NAME
終了コード
exit
exit 10
at_exit{puts "bye!"} exit
abort # 終了コード1 abort "hoge" # 標準エラーに表示される
プロセスができること
fork
puts "親プロセスID: #{Process.id}" if fork puts "親プロセスID: #{Process.id}" else puts "子プロセスID: #{Process.id}" end
fork do # 子プロセス end
wait
Process.wait # => pid Process.wait2 # => [pid, status]
child_pid = fork do # 子プロセス exit 10 end pid, status = Process.waitpid2(child_pid) puts status.exitstatus
親なしになる
fork do while true sleep 1 puts "hello" end end abort "Bye!"
ゾンビになる
child_pid = fork do # 子プロセス end puts child_pid sleep 20
ps -ho pid,state -p {プロセスID}
child_pid = fork do # 子プロセス end Process.detach(child_pid) sleep 20
シグナルを受け取る
child_pid = fork do # 子プロセス sleep 3 end trap(:CHLD) do puts "Hey!" exit end loop do sleep 1 end
他のプロセスと通信する
パイプ
r, w = IO.pipe w.write("Hello") w.close puts r.read
r, w = IO.pipe fork do r.close 5.times do |i| sleep 1 w.puts i end end w.close while s = r.gets puts s end
パイプは一方向
ソケット
require 'socket' child_socket, parent_socket = Socket.pair(:UNIX, :DGRAM, 0) fork do parent_socket.close 5.times do |i| s = child_socket.recv(1000) puts s child_socket.send("CHILD: You said '#{s}'?", 0) end end child_socket.close 5.times do |i| parent_socket.send("Hello #{i}!", 0) end 5.times do puts parent_socket.recv(1000) end
ソケットは双方向
デーモンになる
Process.daemon 10.times do |i| system "say #{10 - i}" sleep 1 end
- 作者: Jesse Storimer
- 発売日: 2011/12/20
- メディア: Kindle版
- クリック: 1回
- この商品を含むブログ (1件) を見る
経済的価値がある理由を考える切り口
The Personal MBAの中で、おなじみマズローの欲求段階のお話などとともに、Core Human Drivesというのを紹介している。
- The Drive to Acquire
- The Drive to Bond
- The Drive to Learn
- The Drive to Defend
- The Drive to Feel
物が欲しい、愛されたい、知りたい、安全で居たい、刺激が欲しい、という欲求が人にはあり、これらの欲求が満たされない状態にある人は、それを求めるものだ、という話。
ビジネスで売りものを決めるときは、ちゃんと人の欲求を満たすものを作りましょうね、そして人の欲求ってのは大雑把にこれぐらいなものなので、自分が考えたものが人のどの欲求を満たすものなのかちゃんと押さえておきましょうね、どの欲求にも答えないものはどう頑張っても売れっこないですよ、ってこと。
この認識がどの程度妥当なのかはわからないけど、商品が売れる理由を考える際のチェックリストとして有用そうではある。
The Personal MBA: Master the Art of Business
- 作者: Josh Kaufman
- 出版社/メーカー: Portfolio Trade
- 発売日: 2012/08/28
- メディア: ペーパーバック
- 購入: 1人 クリック: 3回
- この商品を含むブログを見る
ビジネスが提供する価値の形
引き続きThe Personal MBAから。
この本の中で、ビジネスが提供する価値の典型的な形として12個挙げられている。
1. Product
車とかコンピュータなどの「モノ」を提供する。
複製(大量生産)可能な価値であることが良いところ。
2. Service
お金をとって助力を提供する。
医者、美容師、コンサルタントとか。
3. Shared Resources
各自では所有できない設備を提供する。
ジム、フィットネスクラブ、博物館、遊園地。
資金的に所有できない場合もあれば、スペース的に所有できないものもありそう。
4. Subscription
継続して価値を提供する。
ケーブルテレビ。
「継続して届ける」「いつでも利用できる状態に保っておく」ことが価値、か。
5. Resale
元の生産者が個別の消費者に販売する手間を引き受ける。
雑貨屋とかスーパーとか。
6. Lease
商品として買うと高価なものを、安く使えるようにする。
レンタカー、賃貸、貸しDVD。
7. Agency
元の価値提供者が個別の買い手を見つける手間を引き受ける。
不動産業。
Resaleと似ているが、元の価値提供者との間の取引が発生するタイミングが違うのか。
8. Audience Aggregation
人を集め、その人達へのアクセスを提供する。
雑誌と広告、広告で運営しているWeb。
9. Loan
自分で賄えない額のお金を提供する。
住宅ローン。
10. Option
特定の行動をとる権利を提供する。
映画のチケット。
11. Insurance
リスクを引き受ける。
生命保険。
12. Capital
ビジネスに資金を提供する。
こうして整理されると、「どう売るのか」を考えるときに網羅的に検討できそうでよい。
The Personal MBA: Master the Art of Business
- 作者: Josh Kaufman
- 出版社/メーカー: Portfolio Trade
- 発売日: 2012/08/28
- メディア: ペーパーバック
- 購入: 1人 クリック: 3回
- この商品を含むブログを見る
ビジネスについて学びはじめる
ちょっと前からThe Personal MBAという本をちょびちょびと読んでいる。
著者は大学のMBAコースを、大金払って現場では役に立たない知識を教えるところと批判し、自分で勉強したほうがいいという意見を持っている。
本の構成は、大きく「ビジネス」「人」「システム」に分けられ、それぞれいくつかの章が割り当てられている。 システムというのはITシステムとか生産システムなどではなく、それらを含む、ビジネスを回すのに伴う活動全般を指してるっぽい。
ビジネスについては、まずビジネスを構成するプロセスとして5つを挙げ、それぞれについて1つの章を割り当てている。 各章のタイトルは
- Value Creation
- Marketing
- Sales
- Value Delivery
- Finance
となっている。
このように、活動全体をビジネス、人、システムに分け、さらにビジネスであればそれを5つのプロセスに分類した上で、各分類に関連する言葉と考え方を1つずつ紹介する、というスタイルになっている。 ひとつの節がひとつコンセプトの説明になっていて、本全体として用語集のように見えなくもない。
ちなみに「人」についての章は
- Human Mind
- Working with Yourself
- Working with Others
「システム」に関する章のタイトルは
- Understanding Systems
- Analyzing Systems
- Improving Systems
となっている。
人はそもそも言葉を知らなければうまく考えることもできない。 この本はビジネスという分野に関して、そもそも何を考えればいいのかすらわからないという人が、考慮しなければならなくなるであろう範囲の全体と、知っておくべき、あるいは知っていると便利かもしれない概念、それらを使った考え方の最初の取っ掛かりを得るのに良い内容かと思う。
著者のそもそもの活動が、有益なビジネス書のリストをブログで紹介することから始まったということもあり、この本の中でも、トピックについてもっとくわしく知りたい人向けに別の書籍を紹介しているのも便利。
The Personal MBA: Master the Art of Business
- 作者: Josh Kaufman
- 出版社/メーカー: Portfolio Trade
- 発売日: 2012/08/28
- メディア: ペーパーバック
- 購入: 1人 クリック: 3回
- この商品を含むブログを見る