Pragmatic Patterns of Ruby on Rails - Ruby Kaigi2009
description
Transcript of Pragmatic Patterns of Ruby on Rails - Ruby Kaigi2009
RubyKaigi2009
株式会社万葉
Pragmatic Patternsof Ruby on Rails
2009.7.17Yasuko OHBA(大場寧子)
現場で役立つ Ruby on Rails パターン
RubyKaigi2009
株式会社万葉
自己紹介what I do
• developer of Rails applicationsRailsアプリケーションを開発
•Akasaka.rb• Everyleaf Corporation株式会社万葉
提供
株式会社万葉Everyleaf Corp.
RubyKaigi2009
株式会社万葉
スポンサーブースsponsor booth
• first time to be a sponsorはじめてのスポンサー
• we serve candies飴あります
• you can play ‘Romantic Ruby’ !ギャルゲーあります
RubyKaigi2009
株式会社万葉
作ってるものmy products
• home accounting web serviceWeb家計簿「小槌」http://www.kozuchi.net
• http://github.com/everyleaf/kozuchi/tree
株式会社万葉
RubyKaigi2009
iCarta
株式会社万葉
RubyKaigi2009
Ruby on Rails逆引きクイックリファレンス
株式会社万葉
RubyKaigi2009
伝えたいこと
personal message
株式会社万葉
RubyKaigi2009
中学生くらいからプログラミング
I started programming when I
was 14 years old
株式会社万葉
RubyKaigi2009
女子校
school for girls
株式会社万葉
RubyKaigi2009
孤独なプログラマ
I was alone
株式会社万葉
RubyKaigi2009
就職
got the job
株式会社万葉
RubyKaigi2009
似た人々と出会う
find similar people
株式会社万葉
RubyKaigi2009
でも
however
株式会社万葉
RubyKaigi2009
エンタープライズ
‘enterprise’
株式会社万葉
RubyKaigi2009
社外に出ない
only activitiesinside the company
株式会社万葉
RubyKaigi2009
見積もりマネージメントアウトソージング
other things mount up
株式会社万葉
RubyKaigi2009
どうしたらいいのか
I was lost
株式会社万葉
RubyKaigi2009
実装がしたい
want to write codes
株式会社万葉
RubyKaigi2009
そんな時that’s when..
株式会社万葉
RubyKaigi2009
Rubyに出会った
Ruby was given
株式会社万葉
RubyKaigi2009
人生変わった(^o^)/
life becomes happier
株式会社万葉
RubyKaigi2009
Rubyでエンタープライズ
Ruby for enterprises
株式会社万葉
RubyKaigi2009
実装してて幸せになる道
the way to live happily writing codes
株式会社万葉
RubyKaigi2009
そして
And...
株式会社万葉
RubyKaigi2009
素晴らしいコミュニティ
great communities
株式会社万葉
RubyKaigi2009
仲間ができた
many friends
株式会社万葉
RubyKaigi2009
ありがとう!thank you !!
株式会社万葉
RubyKaigi2009
本題
today’s presentation
株式会社万葉
RubyKaigi2009
実装パターン
coding patternswith
Ruby on Rails
RubyKaigi2009
株式会社万葉
ターゲットthe target
• large & complicated applications複雑で大きなアプリ
•teamチームでの開発
RubyKaigi2009
株式会社万葉
大きなアプリの課題
problems of large applications
•different coding styles書き方がバラバラ
•nasty parts made品質が揃っていない
株式会社万葉
RubyKaigi2009
メンテナンスしにくくなる
becomes harderto maintenance
株式会社万葉
RubyKaigi2009
コードをいい状態に保つ
keep your codes nice
RubyKaigi2009
株式会社万葉
いいコードとは?• good designいい設計
• easy to understand読みやすい、わかりやすい
• easy to find something wrong 間違いにすぐ気づく
what is the ‘nice code’?
RubyKaigi2009
株式会社万葉
そこで、パターン
• make and share coding patterns共通パターンを作る
• add some DSL (not too much)DSLを作る(控えめに)
coding patterns work fine
株式会社万葉
RubyKaigi2009
もちろん効率も上がる
efficient, of course
RubyKaigi2009
株式会社万葉
このプレゼンで触れないことwhat I won’t say
• how to get the best performance極限のパフォーマンスの追求
• ActiveRecord’s alternativesAR以外のDB層の選択肢
株式会社万葉
RubyKaigi2009
O/R Mapper
ActiveRecord
株式会社万葉
RubyKaigi2009
て言うか、
frankly speaking,
株式会社万葉
RubyKaigi2009
ActiveRecord大好き
I love ActiveRecord
株式会社万葉
RubyKaigi2009
なぜなら
because
株式会社万葉
RubyKaigi2009
オブジェクト指向で書ける
OOP
株式会社万葉
RubyKaigi2009
複雑さに耐えられる
still able to maintenancewhen it gets complicated
株式会社万葉
RubyKaigi2009
実用的
pragmatic
株式会社万葉
RubyKaigi2009
Railsの中核
the heart of RoR
株式会社万葉
RubyKaigi2009
ActiveRecordを中心に話します
many AR features in this presentation
株式会社万葉
RubyKaigi2009
実例紹介
examples
株式会社万葉
RubyKaigi2009
権限のあるデータの表示
show permitted data
要件 ①
株式会社万葉
RubyKaigi2009
ARオブジェクトから始める検索
find starts froman AR object
パターン ①
株式会社万葉
RubyKaigi2009
IDでNoteを検索find a note by id
/note/3
def show @note = Note.find(params[:id])end
株式会社万葉
RubyKaigi2009
ログインユーザーのNoteを検索
find a note of the current user
@note = Note.find_by_id_and_user_id( params[:id], current_user.id)raise ActiveRecord::RecordNotFound unless @note
/note/3
株式会社万葉
RubyKaigi2009
restful_authentication
current_user?
株式会社万葉
RubyKaigi2009
named scope?
@note = Note.written_by( curret_user.id).find(params[:id])
/note/3
株式会社万葉
RubyKaigi2009
条件を忘れても気づきにくい
hard to notice the luck of condition!
@note = Note.find(params[:id])@note = Note.find_by_id_and_user_id( params[:id], current_user.id)@note = Note.written_by( current_user).find(params[:id])
株式会社万葉
RubyKaigi2009
そこで
so,
株式会社万葉
RubyKaigi2009
ARオブジェクトから始める検索
find starts froman AR object
pattern ①
株式会社万葉
RubyKaigi2009
オブジェクト起点で検索
starts from an object
def show @note = current_user.notes.find( params[:id])end
株式会社万葉
RubyKaigi2009
関連を使う
use the association
def show @note = current_user.notes.find( params[:id])end
株式会社万葉
RubyKaigi2009
関連
association
class User < ActiveRecord::Base has_many :notesend
株式会社万葉
RubyKaigi2009
問題に気づきやすい
easy to notice problems
@note = current_user.notes.find( params[:id])
@note = Note.find(params[:id])
株式会社万葉
RubyKaigi2009
データの権利者が一目でわかる
easy to see who can access
株式会社万葉
RubyKaigi2009
データの権利者
who can access
@note = current_user.notes.find( params[:id])
株式会社万葉
RubyKaigi2009
ARオブジェクトを得るFilter
filters to find the starting AR object
pattern ②
株式会社万葉
RubyKaigi2009
さっきのパターンで書くと
with the previouspattern
株式会社万葉
RubyKaigi2009
コントローラのアクションの多くの
起点が揃う
most actions willuse the AR object
株式会社万葉
RubyKaigi2009
例)グループのノートのCRUD
example;CRUD of the specified
group’s notes
RubyKaigi2009
株式会社万葉
URL
•ノートの帰属するグループがURL
の前に付く• /groups/15/notes• /groups/15/notes/3
RubyKaigi2009
株式会社万葉
class Note < ActiveRecord::Base belongs_to :groupend
Model• Note has group_idNoteが group_id を持つ
class Group < ActiveRecord::Base has_many :notesend
株式会社万葉
RubyKaigi2009
Controllerdef index @group = Group.find(params[:group_id]) @notes = @group.notes.paginate(:page => params[:page])enddef show @group = Group.find(params[:group_id]) @note = @group.notes.find(params[:id])end
株式会社万葉
RubyKaigi2009
重複したコード
the duplicated line
@group = Group.find(params[:group_id])
株式会社万葉
RubyKaigi2009
Filterで記述する
write that linein a filter
RubyKaigi2009
株式会社万葉
Filterとは?what’s the filter?
• the separated logic which would be called around actionアクションの前後に行いたい処理をアクションとは別に記述
• declarative宣言的
株式会社万葉
RubyKaigi2009
class GroupNotesController < ApplicationController
before_filter :find_group .......actions ...... private def find_group @group = Group.find(params[:group_id]) endend
Filterで@groupを得るfind the group in a filter
株式会社万葉
RubyKaigi2009
重複を避けられるDRY
before_filter :find_groupdef index @notes = @group.notes.paginate(:page => params[:page])enddef show @note = @group.notes.find(params[:id])end
株式会社万葉
RubyKaigi2009
アクセス制限を変えるのも楽
easy to changeaccess control
株式会社万葉
RubyKaigi2009
例) メンバーしか見られないようにする
example; change to allow access to members only
def find_group @group = current_user.groups.find( params[:group_id]end
株式会社万葉
RubyKaigi2009
それだけじゃない
other merits
株式会社万葉
RubyKaigi2009
securegroupでの
絞り込みを確実に
株式会社万葉
RubyKaigi2009
@groupで始まれば安心safe if it starts from @group
def index @notes = @group.notes.paginate(:page => params[:page])enddef show @note = @group.notes.find(params[:id])end
株式会社万葉
RubyKaigi2009
filter urges developersto use @group
Filterがあれば自然な強制力が
働く
株式会社万葉
RubyKaigi2009
and
さらに
株式会社万葉
RubyKaigi2009
読みやすい
readable
株式会社万葉
RubyKaigi2009
コントローラ名とFilterで
概要が分かる
you can understandthe controller’s summary
from name and filters
株式会社万葉
RubyKaigi2009
さっと把握できる
understand the summaryquickly
class GroupNotesController < ApplicationController before_filter :find_group
RubyKaigi2009
株式会社万葉
大事なことthe points
•a good controller name良いコントローラ名
•readable filters読みやすいフィルター
株式会社万葉
RubyKaigi2009
複雑なビジネスロジック
complicatedbusiness logics
要件 ②
株式会社万葉
RubyKaigi2009
ビジネスロジックはモデルに書きたい
want to writebusiness logics
in models
株式会社万葉
RubyKaigi2009
なぜなら
because
RubyKaigi2009
株式会社万葉
モデルに集めると嬉しい点
merits of writing business logics in models
• easy to testテストしやすい
• easy to reuse再利用しやすい
• readable, easy to find the target codesコードが分散せず、読みやすい
株式会社万葉
RubyKaigi2009
しかし
however
株式会社万葉
RubyKaigi2009
集め方が問題
‘how’ matters a lot
株式会社万葉
RubyKaigi2009
悪い例a bad example
class MyModel < ActiveRecord::Base def do_create(params) .... def do_destroy(params) ...end
RubyKaigi2009
株式会社万葉
どこが悪いか?why is it bad ?
• it breaks MVCMVCを壊している
• hard to reuse再利用性が低い
株式会社万葉
RubyKaigi2009
そこでnow
株式会社万葉
RubyKaigi2009
コントローラからモデルへ
うまくコードを移してみましょう
let’s move codesfrom controller to model
in good way
株式会社万葉
RubyKaigi2009
パラメータによる分岐ロジックの
移動
move the logicbranching on parameters
from C to M
pattern ③
株式会社万葉
RubyKaigi2009
パラメータによる処理の分岐branching on parameters
def update @note.attributes = params[:note] @tags = params[:auto_tagging] == '1' ? generate_tags(@note) : []
# do saving and tagging ...end
株式会社万葉
RubyKaigi2009
コントローラに書きがち
often written in controller
株式会社万葉
RubyKaigi2009
モデルに持っていくには
to move to model
株式会社万葉
RubyKaigi2009
制御条件をモデルの属性に
add an attribute for branching to the model
株式会社万葉
RubyKaigi2009
分岐条件
the condition
params[:auto_tagging] == '1'
株式会社万葉
RubyKaigi2009
モデルの属性にas an attribute of the model
class Note < ActiveRecord::Base attr_accessor :auto_tagging
end
株式会社万葉
RubyKaigi2009
パラメータ構造を変える
change parameters structure
{ :note => {.....}, :auto_tagging => '1' }
{ :note => { ....., :auto_tagging => '1' }}
株式会社万葉
RubyKaigi2009
そのためにビューでフィールド名を変更
change the view for it
<%= check_box_tag :auto_tagging %>
<% form_for :note, ... do |f |%> <%= f.check_box :auto_tagging %><% end %>
株式会社万葉
RubyKaigi2009
params[:note]で渡せるnow it’s in params[:note]
def update @note.attributes = params[:note] if params[:auto_tagging] == '1' generate_tags(@note) end .....
株式会社万葉
RubyKaigi2009
モデルで分岐できる
can branch in the model
class Note < ActiveRecord::Base .... if auto_tagging ... endend
株式会社万葉
RubyKaigi2009
分岐は移動できた
finished to movebranching
株式会社万葉
RubyKaigi2009
残るはgenerate_tagsロジックの移動
move generate_tags next
株式会社万葉
RubyKaigi2009
付随する他モデルへの処理
の移動
move the logic processing other models
from C to M
pattern ④
株式会社万葉
RubyKaigi2009
付随する他モデルへの処理def update generate_tags(@note)endprivatedef generate_tags(note) tags = Tag.extract(note.body) note.tag_list = tags.join(',')end
the logic processing other models
株式会社万葉
RubyKaigi2009
これもコントローラに
書きがち
also often written in controller
株式会社万葉
RubyKaigi2009
モデルのコールバックへput it into model’s callback
class Note < ActiveRecord::Base before_save :generate_taggings
private def generate_taggings return unless auto_tagging tags = Tag.extract(body) self.tag_list = tags.join(',') endend
RubyKaigi2009
株式会社万葉
モデルのコールバックとは?
what’s the model’s callback?
• methods called before/after save or destroysaveやdestroyなどの前後に呼び出される処理
RubyKaigi2009
株式会社万葉
Rails Model You
normal method call普通のメソッド呼び出し
savesave(validation)
before_save
after_save
do saving
株式会社万葉
RubyKaigi2009
自律的なモデル
self-directive models
RubyKaigi2009
株式会社万葉
目標を達成!now we’ve done it !
• easy to testテストしやすい
• easy to reuse再利用しやすい
• readable, easy to find the target codesコードが分散せず、読みやすい
RubyKaigi2009
株式会社万葉
話せなかったことother patterns
• multi levels for validation多レベルの検証
• design & coding policy for STISTIの原則
• use owner object’s attributes in association関連オブジェクトに属性情報を伝える
• how to make routes.rb a little readableroutes.rb の整理
株式会社万葉
RubyKaigi2009
最後に
the last topic
株式会社万葉
RubyKaigi2009
Rails実装パターンの見い出し方
how to findcoding patterns
株式会社万葉
RubyKaigi2009
私の場合
in my case
株式会社万葉
RubyKaigi2009
Railsの中で自然に感じる書き方の追究
try to choosethe most natural style
for RoR
株式会社万葉
RubyKaigi2009
Railsにおいて何が自然か
what is the most natural way
in Ruby on Rails?
株式会社万葉
RubyKaigi2009
1. オブジェクト指向
1. OOP
株式会社万葉
RubyKaigi2009
ビジネスロジックをモデルとして表現
express business logics as models
株式会社万葉
RubyKaigi2009
誰のすべき仕事かにこだわる
always think who should do that job
株式会社万葉
RubyKaigi2009
Note or User ?
@note = Note.find_by_id_and_user_id( params[:id], current_user.id)
@note = current_user.notes.find( params[:id])
株式会社万葉
RubyKaigi2009
必ずしもテーブルで決めない
you can’t always decide it from tables
株式会社万葉
RubyKaigi2009
オブジェクトの世界の中で決める
decide it in objects world
株式会社万葉
RubyKaigi2009
2. 原則に従う
2. follow the principles of RoR
RubyKaigi2009
株式会社万葉
Railsの原則principles of RoR
•DRY•CoC•RESTful
株式会社万葉
RubyKaigi2009
RESTful
株式会社万葉
RubyKaigi2009
RailsではRESTfulから逃げない方がいい
accept RESTfulin Ruby on Rails
株式会社万葉
RubyKaigi2009
コントローラをどう分けるか?
how to designcontrollers ?
株式会社万葉
RubyKaigi2009
“RESTful”
株式会社万葉
RubyKaigi2009
対象リソース1種類に
1つのコントローラ
one controller fora type of resources
株式会社万葉
RubyKaigi2009
リソースって何だ?
what’s the resources?
株式会社万葉
RubyKaigi2009
URLで示される
you can find it in URL
/companies/everyleaf/notes/3
株式会社万葉
RubyKaigi2009
コントローラの設計は
design of controllers
株式会社万葉
RubyKaigi2009
URL設計から始める
starts from design of URLs
株式会社万葉
RubyKaigi2009
ざっくり
roughly speaking,
株式会社万葉
RubyKaigi2009
Resourceリソース
Model Classモデルクラス
1
1..*
株式会社万葉
RubyKaigi2009
重要なのは
the point is...
株式会社万葉
RubyKaigi2009
複数リソースのCRUDはまずい
one controller shouldprovide one resource
type’s CRUD
株式会社万葉
RubyKaigi2009
こういうのはまずいmight have been derailed
class BlogsController < ApplicationController
def create_comment end def comments end def destroy_comment ...
株式会社万葉
RubyKaigi2009
もうひとつ
one more
株式会社万葉
RubyKaigi2009
モデルの基本的な流れに乗せる
write codes in model’s standard flows
RubyKaigi2009
株式会社万葉
standard flows
•find•new → save (create)•find → save (update)•find → destroy
標準的な流れの種類
株式会社万葉
RubyKaigi2009
流れの中身
inside of a flow
株式会社万葉
RubyKaigi2009
例) createnewbuild
attribute =属性代入
before_validation
validation検証
after_validation
before_save
creation作成処理 after_save
株式会社万葉
RubyKaigi2009
適切な場所に書く
write codes inappropriate place
株式会社万葉
RubyKaigi2009
newbuild
attribute =属性代入
before_validation
validation検証
after_validation
before_save
creation作成処理 after_save
where, what
株式会社万葉
RubyKaigi2009
Railsの中で自然に感じる書き方の追究
try to choosethe most natural style
for RoR
株式会社万葉
RubyKaigi2009
基本的な書き方から大きく離れない
don’t go too farfrom the basic styles
株式会社万葉
RubyKaigi2009
そうすれば
because
株式会社万葉
RubyKaigi2009
Railsプログラマにとって標準的
it’s normal to Rails programmers
株式会社万葉
RubyKaigi2009
誰でも読める
easy to readfor everyone
株式会社万葉
RubyKaigi2009
そのためには
for that purpose
株式会社万葉
RubyKaigi2009
Rubyの柔軟性を活用する
use Ruby’s flexibility
株式会社万葉
RubyKaigi2009
まとめ
summary
株式会社万葉
RubyKaigi2009
複雑で大きなアプリでは
in developinglarge & complicated
application
株式会社万葉
RubyKaigi2009
コードをいい状態に保つのが大事
it’s importantto keep codes nice
株式会社万葉
RubyKaigi2009
そのために
for that purpose
株式会社万葉
RubyKaigi2009
実装パターンを意識して共有しよう
be aware of coding patterns,
and share them
株式会社万葉
RubyKaigi2009
Railsでの自然な書き方を
目指せば
coding patternsfitting Ruby on Rails
株式会社万葉
RubyKaigi2009
読める人の多いコードができる
make source codes easy to read for
general developers
株式会社万葉
RubyKaigi2009
メンテナンスできる
it meanseasy to maintain
株式会社万葉
RubyKaigi2009
ご静聴ありがとうございました
thank you !