浜松Rails3道場 其の弐 Model編

42
浜松Rails3道場 其の弐 ModelHamamatsurb#5 2011.07.13 @mackato 2011713日水曜日

Transcript of 浜松Rails3道場 其の弐 Model編

Page 1: 浜松Rails3道場 其の弐 Model編

浜松Rails3道場其の弐 Model編

Hamamatsurb#5 2011.07.13 @mackato

2011年7月13日水曜日

Page 2: 浜松Rails3道場 其の弐 Model編

浜松Rails3道場の心得

習うより慣れろ

理論より実用

未知を恐れない

2011年7月13日水曜日

Page 3: 浜松Rails3道場 其の弐 Model編

実際にRailsアプリを作ってみる

Wiki

全5回位で

2011年7月13日水曜日

Page 4: 浜松Rails3道場 其の弐 Model編

Wikiの仕様

ページの編集と削除ができる

ページにコメントできる

ページの表示ができる

2011年7月13日水曜日

Page 5: 浜松Rails3道場 其の弐 Model編

Wikiの仕様

ページの編集履歴が確認できる

ユーザー認証ができる

2011年7月13日水曜日

Page 6: 浜松Rails3道場 其の弐 Model編

Wikiの仕様

ページの一覧が表示できる

2011年7月13日水曜日

Page 7: 浜松Rails3道場 其の弐 Model編

Wikiの仕様ページが作成できる

2011年7月13日水曜日

Page 9: 浜松Rails3道場 其の弐 Model編

開発環境

Ruby Version 1.9.2

Rails Version 3.0.7

.rvmrcrvm ruby-1.9.2-p180@rails-3_0_7

2011年7月13日水曜日

Page 10: 浜松Rails3道場 其の弐 Model編

サンプルコードの取得GitHubから其の弐用のコードを取得

git clone git://github.com/hamamatsu-rb/rails3dojo.git

git checkout -b working 2-model_pre

開始地点のタグ作業ブランチ

2011年7月13日水曜日

Page 11: 浜松Rails3道場 其の弐 Model編

テスト環境の準備FactoryGirl(テストデータ作成)、Spork(テスト高速化)の導入

Gemfileに追加group :test do gem "factory_girl_rails" gem "spork", ">= 0.9.0.rc2"end

.rspecに追加--drb

spork --bootstrap

Sporkの初期化

2011年7月13日水曜日

Page 12: 浜松Rails3道場 其の弐 Model編

モデルの作成今回開発するWikiで必要なモデル

User: Wikiの編集をおこなうユーザーPage: タイトルや本文をもつWikiページComment: Pageに対するコメントHistory: Pageの更新履歴

2011年7月13日水曜日

Page 13: 浜松Rails3道場 其の弐 Model編

Userモデルの作成Wikiの編集をおこなうユーザー

rails g model user name:string

rake db:migrate

2011年7月13日水曜日

Page 14: 浜松Rails3道場 其の弐 Model編

UserのFactoryspec/factories.rb

Factory.define :user do ¦f¦ f.name "bob"end

spec/spec_helper.rbdef create_user(params = {}) Factory.create(:user, params)end

def build_user(params = {}) Factory.build(:user, params)end

2011年7月13日水曜日

Page 15: 浜松Rails3道場 其の弐 Model編

UserのSpecspec/models/user_spec.rb

describe User do describe "#name" do it "必須" do user = build_user(:name => "") user.should_not be_valid end ... end describe "#find_or_create_by_name" do context "同じ名前のユーザーが存在する場合" do it "存在しているユーザーを取得する" do ... end end context "同じ名前のユーザーが存在しない場合" do it "新しいユーザーを作成する" do ... end end endend

クラス

メソッド

コンテキスト

スペック

2011年7月13日水曜日

Page 16: 浜松Rails3道場 其の弐 Model編

Userクラスapp/models/user.rb

class User < ActiveRecord::Base validates :name, :presence => true, :length => { :maximum => 50 }, :uniqueness => trueend

SexyValidation!

validates_presence_of :namevalidates_length_of :name, :maximum => 50validates_uniqueness_of :name

Legacy Style Validation

2011年7月13日水曜日

Page 17: 浜松Rails3道場 其の弐 Model編

テスト実行bundle exec rspec spec/models/user_spec.rb

User #name 必須 50文字以内 重複不可 #find_or_create_by_name 同じ名前のユーザーが存在する場合 存在しているユーザーを取得する 同じ名前のユーザーが存在しない場合 新しいユーザーを作成する

2011年7月13日水曜日

Page 18: 浜松Rails3道場 其の弐 Model編

Pageモデルの作成タイトルや本文をもつWikiページ

rails g model page title:string body:text \ user_id:integer

rake db:migrate

2011年7月13日水曜日

Page 19: 浜松Rails3道場 其の弐 Model編

PageのFactoryspec/factories.rb

spec/spec_helper.rbdef create_page(params = {}) Factory.create(:page, params)end

def build_page(params = {}) Factory.build(:page, params)end

Factory.sequence :page_title do ¦n¦ "Page #{n}"end

Factory.define :page do ¦f¦ f.title { Factory.next(:page_title) } f.body "Lorem ipsum dolor sit amet, ..." f.association :userend

2011年7月13日水曜日

Page 20: 浜松Rails3道場 其の弐 Model編

PageのSpecspec/models/page_spec.rb(抜粋)

describe Page describe "#user" it "ページを作成したユーザー" it "必須" describe "#title" it "必須" it "255文字以内" it "重複不可" describe "#body" it "文字数は実質無制限" describe "#recent" it "最新の10件" it "作成日の降順でソート"

2011年7月13日水曜日

Page 21: 浜松Rails3道場 其の弐 Model編

Pageクラスapp/models/page.rb

class Page < ActiveRecord::Base belongs_to :user

validates :user, :presence => true validates :title, :presence => true, :length => { :maximum => 255 }, :uniqueness => true scope :recent, limit(10).order("created_at DESC")end

2011年7月13日水曜日

Page 22: 浜松Rails3道場 其の弐 Model編

Commentモデルの作成Pageに対するコメント

rails g model comment body:text \ page_id:integer user_id:integer

rake db:migrate

2011年7月13日水曜日

Page 23: 浜松Rails3道場 其の弐 Model編

CommentのFactoryspec/factories.rb

spec/spec_helper.rbdef create_comment(params = {}) Factory.create(:comment, params)end

def build_comment(params = {}) Factory.build(:comment, params)end

Factory.define :comment do ¦f¦ f.body "Lorem ipsum dolor sit amet, ..." f.association :page f.user { ¦c¦ c.page.user }end

2011年7月13日水曜日

Page 24: 浜松Rails3道場 其の弐 Model編

CommentのSpecspec/models/comment_spec.rb(抜粋)

describe Comment describe "#page" it "コメント対象のページ" it "必須"

describe "#user" it "コメントを投稿したユーザー" it "必須"

describe "#body" it "必須" it "1000文字以内"

2011年7月13日水曜日

Page 25: 浜松Rails3道場 其の弐 Model編

Commentクラスapp/models/comment.rb

class Comment < ActiveRecord::Base belongs_to :page belongs_to :user validates :page, :presence => true validates :user, :presence => true validates :body, :presence => true, :length => { :maximum => 1000 }end

2011年7月13日水曜日

Page 26: 浜松Rails3道場 其の弐 Model編

PageにCommentへの関連を追加

spec/models/page_spec.rb (抜粋)

app/models/page.rb

has_many :comments

describe Page ... describe "#comments" it "ページへのコメント" it "作成日の昇順でソート" it "Pageと一緒に全て削除"

2011年7月13日水曜日

Page 27: 浜松Rails3道場 其の弐 Model編

Historyモデルの作成Pageの更新履歴

rails g model history \page_id:integer user_id:integer

rake db:migrate

2011年7月13日水曜日

Page 28: 浜松Rails3道場 其の弐 Model編

HistoryのFactoryspec/factories.rb

spec/spec_helper.rbdef create_history(params = {}) Factory.create(:history, params)end

def build_history(params = {}) Factory.build(:history, params)end

Factory.define :history do ¦f¦ f.association :page f.user { ¦c¦ c.page.user }end

2011年7月13日水曜日

Page 29: 浜松Rails3道場 其の弐 Model編

HistoryのSpecspec/models/history_spec.rb

# -*- coding: utf-8 -*-require 'spec_helper'

describe History do describe "#page" do it "履歴の対象のページ" do create_history.page.should be end end describe "#user" do it "履歴を更新したユーザー" do create_history.user.should be end endend

2011年7月13日水曜日

Page 30: 浜松Rails3道場 其の弐 Model編

Historyクラスapp/models/history.rb

class History < ActiveRecord::Base belongs_to :page belongs_to :userend

2011年7月13日水曜日

Page 31: 浜松Rails3道場 其の弐 Model編

PageにHistoryへの関連を追加spec/models/page_spec.rb (抜粋)

app/models/page.rb

has_many :histories

describe Page ... describe "#histories" it "ページの更新履歴" it "作成日の降順でソート" it "Pageと一緒に全て削除"

2011年7月13日水曜日

Page 32: 浜松Rails3道場 其の弐 Model編

Pageの更新時に履歴を追加するspec/models/page_spec.rb (抜粋)

app/models/page.rb

describe Page ... describe "#save_by_user" it "保存の成否を返す" it "Historyを1つ追加"

def save_by_user(updater) if self.save self.histories.create(:user => updater) true else false end end

2011年7月13日水曜日

Page 33: 浜松Rails3道場 其の弐 Model編

今回の開発はここまで

git checkout 2-model

作業内容をmasterにマージgit checkout mastergit merge working

作業後のコードはGitHubにありますgit clone git://github.com/hamamatsu-rb/rails3dojo.git

git branch -d working

2011年7月13日水曜日

Page 34: 浜松Rails3道場 其の弐 Model編

More Rails Models

2011年7月13日水曜日

Page 35: 浜松Rails3道場 其の弐 Model編

データベース構造の管理

Migrations

class CreatePages < ActiveRecord::Migration def self.up create_table :pages do ¦t¦ t.string :title t.text :body t.integer :user_id

t.timestamps end end

def self.down drop_table :pages endend

rake db:migrate [VERSION=YY..]rake db:rollback [STEP=N]rake db:migrate:redo [STEP=N]

db/migrate/20110712154821_create_pages.rbpageテーブル作成

pageテーブル削除

フィールド定義

2011年7月13日水曜日

Page 36: 浜松Rails3道場 其の弐 Model編

データ検証

Validations

Validation Helpers vs SexyValidation

validates_acceptance_of :terms_of_servicevalidates_associated :friendsvalidates_confirmation_of :passwordvalidates_exclusion_of :password, :in => %w(0000)validates_format_of :login, :with => /^[a-z]+/ivalidates_inclusion_of :answer, :in => %w(Y N)validates_length_of :password, :in => 4..8validates_numericality_of :age, :only_integer => truevalidates_presence_of :login, :passwordvalidates_uniqueness_of :login

validates :terms_of_service, :acceptance => truevalidates :password, :confirmation => true, :exclusion => { :in => %w(0000) }, :length => { :in => 4..8 }, :presence => truevalidates :login, :format => { :with => /^[a-z]+/i }, :presence => true, :uniqueness => truevalidates :answer, :inclusion => { :in => %w(Y N) }

2011年7月13日水曜日

Page 37: 浜松Rails3道場 其の弐 Model編

データ関連

Associations

class  Order  <  ActiveRecord::Base    belongs_to  :customerend

class  Supplier  <  ActiveRecord::Base    has_one  :accountend

The belongs_to Association

The has_one Association

The has_many Association

class  Customer  <  ActiveRecord::Base    has_many  :ordersend

The has_many :through Association

class  Physician  <  ActiveRecord::Base    has_many  :appointments    has_many  :patients,  :through  =>  :appointmentsend  class  Appointment  <  ActiveRecord::Base    belongs_to  :physician    belongs_to  :patientend  class  Patient  <  ActiveRecord::Base    has_many  :appointments    has_many  :physicians,  :through  =>  :appointmentsend

参考: Active Record Associations: http://bit.ly/n3exu9

2011年7月13日水曜日

Page 38: 浜松Rails3道場 其の弐 Model編

データ検索

Query

Active Record Query Interface: http://bit.ly/qaeazJ

Finder methods

• where• select• group• order• limit• offset• joins• includes• lock• readonly• from• having

Retrieving a Single Object

Page.find(10)Page.where(:title => "Title 1").firstPage.last

Retrieving Multiple Objects

Page.find(1, 10)Page.all.each do ¦variable¦ page.save!end

Calculations

Page.limit(10).count #=> 11Page.limit(10).all.count #=> 10

遅延評価に注意

2011年7月13日水曜日

Page 39: 浜松Rails3道場 其の弐 Model編

ActiveRecord覚えること大杉

結論

2011年7月13日水曜日

Page 40: 浜松Rails3道場 其の弐 Model編

実演

2011年7月13日水曜日

Page 41: 浜松Rails3道場 其の弐 Model編

Q&A

2011年7月13日水曜日

Page 42: 浜松Rails3道場 其の弐 Model編

多分、ActionController

次回予告

2011年7月13日水曜日