2011年9月17日土曜日

Head First Rails

図書館で借りてきた本。RailsとRubyは初体験。
現バージョン(3.1.0)と本でコマンドが異なっていて少し困惑。
P.7
アプリケーション新規作成
>rails new アプリケーション名
P.8
アプリケーション起動
>cd アプリケーションのディレクトリ
>rails server
P.9
設定よりも規約・・・CoC(convention over configuration)。規約を一貫させて理解度を上げる。また、自動化もし易い。
Rubyはインタプリタ言語でコンパイル不要。
P.12
CRUD操作に必要なコードとページ作成。scaffold=土台。物理テーブル名はデータ名+s(複数形)になるので注意。
>cd アプリケーションのディレクトリ
>rails generate scaffold データ名 項目名1:string 項目名2:text 項目名3:decimal
P.14
DRY・・・Railsの基本原則。おなじことを繰り返さない。Don't Repeat Yourself.
データベースは、dbフォルダの中。
P.15
テーブル作成などDBの更新。db/migrateにあるmigrationはテーブル作成などのRubyスクリプト。
>cd アプリケーションのディレクトリ
>rake db:migrate
P.22
ビジネスロジック・・・モデルロジックorコントローラロジック
モデルロジック・・・データ管理に関連するルール
コントローラロジック・・・システムのワークフローを管理するルール
P.27
symbol・・・対象に名前を付けるために用いる。常に":"で開始される。メモリの使用効率が良くなる。
P.33
undo機能・・・直前の状態に戻す機能。
P.34
データの構成変更。
>cd アプリケーションのディレクトリ
>rails generate migration Add追加項目名Toデータテーブル名 追加項目:string
>rake db:migrate
P.37
<%=h ...%>・・・ヘルパーメソッドに一つ。特殊文字(<,&など)をエスケープするために使う。
P.50
Modelの作成。
>cd アプリケーションのディレクトリ
>rails generate model データ名 項目名1:string 項目名2:text 項目名3:decimal 項目名4:date 項目名5:integer
>rake db:migrate
P.51
rake db:migrateで作成されるデータファイル。
-->db/development.sqlite3
P.52
Controllerの作成。
>cd アプリケーションのディレクトリ
>rails generate controller データ名+"s"
P.53
SQLite3への接続。
>cd アプリケーションのディレクトリ
>rails dbconsole
P.54
ERB・・・Embedded Ruby。埋め込みRudy。テンプレートからWebページを生成する標準Rubyライブラリ。
P.57
config/routes.rb・・・RailsにWebページの場所を伝える。
※記述サンプルは、config/routes.rbを参照。
P.60
ビジネスオブジェクト、ドメインオブジェクト・・・モデルオブジェクトの別名。
P.67
モデルにデータ取得依頼。(コントローラが使用)
モデル.find(params[:id]) ※ユニークIDで検索
モデル.all ※すべてのデータを検索
P86
配列の繰り返し処理。
配列.each do |配列内のオブジェクト|
   # 処理
end
P94
レイアウト・・・あるモデルに属するすべてのテンプレートに対するHTMLのラッパーを定義するもの。
P112
入力画面の作成例 (モデルフォーム)
※form_forの前に"="が必要

<h1>New ad</h1>
<%= form_for(@ad,:url=>{:action=>'create'}) do |f| %>
    <p><b>Name</b><br /><%= f.text_field :name %></p>
    <p><b>Description</b><br /><%= f.text_area :description %></p>
    <p><b>Price</b><br /><%= f.text_field :price %></p>
    <p><b>Seller</b><br /><%= f.text_field :seller_id %></p>
    <p><b>Email</b><br /><%= f.text_field :email %></p>
    <p><b>Img url</b><br /><%= f.text_field :img_url %></p>
    <p><%= f.submit "Create" %></p>
<% end %>
P117
モデルの新オブジェクト作成。(コントローラが使用)
モデル.new
P120
モデルの新オブジェクトをハッシュテーブルのパラメータを初期値にして作成。フォームからサブミットされた値を設定するのに使用する。
モデル.new(ハッシュテーブル(ex.params[:ad]))
P122
モデルをDBに登録
モデルのインスタンス.save
P132
別のページにリダイレクト。(コントローラが使用)
ex.
redirect_to "/ads/#{@ad.id}"
P139
モデルの更新。(コントローラが使用)
モデルのインスタンス.update_attributes(ハッシュテーブル)
ex.
def update
  @ad = Ad.find(params[:id])
  @ad.update_attributes(params[:ad])
  redirect_to "/ads/#{@ad.id}"
end
P147
モデルの削除。(コントローラが使用)
モデルのインスタンス.destroy
P162
検索画面の作成例 (非モデルフォーム)
<%= form_tag "/client_workouts/find" do %>
<% text_field_tag :search_string %>
<% submit_tag "Search" %>
<% end %>
P168
Webサーバコンソールに標準出力。
puts "出力文字列"
P170
アプリケーションで使用可能なルートの表示。
>rake routes
P174
モデルにデータ取得依頼その2。(コントローラが使用)
モデル.find_all_by_テーブル項目名(パラメータ) ※検索対象のテーブル項目を指定して検索
P181
モデルにデータ取得依頼その3。(コントローラが使用)
モデル.all(:conditions=>[検索条件,パラメータ,パラメータ,・・・(複数パラメータを渡すことが可能)]※細かい条件を指定して検索
ex.
@client_workouts = ClientWorkout.all(:conditions=>["client_name = ? OR trainer = ?", params[:search_string],params[:search_string]])
P194
バリデータ・・・モデルで属性の検証を行う機能。
ex.
class ClientWorkout < ActiveRecord::Base
  validates_numericality_of :paid_amount
end
数値フィールドの検証。
validates_numericality_of :検証する属性, "エラーメッセージ(省略可)"
P197
必須フィールドの検証
validates_presence_of :検証する属性, "エラーメッセージ(省略可)"
P199
フィールドの長さ検証
validates_length_of :検証する属性, :maximum=>最大長
フィールドのユニーク検証
validates_uniqueness_of :検証する属性
フィールドのフォーマット検証
validates_format_of :検証する属性, :with=>/検証するフォーマット/
フィールドのスペル検証(属性が指定された値と同じか検証する)
validates_inclusion_of :検証する属性, in=>[検証する値1,検証する値2,・・・,検証する値n]
P210
エラー処理の実装例。
(コントローラに実装)
if @ad.save
  redirect_to "/ads/#{@ad.id}"
else
  render :template => "ad/new" ←※URLではないので注意
end
(Viewに実装)

<% if @ad.errors.any? %>
  <div id="error_explanation">
    <h2><%= pluralize(@ad.errors.count, "error") %> prohibited this client_workout from being saved:</h2>
    <ul>
    <% @ad.errors.full_messages.each do |msg| %>
      <li><%= msg %></li>
    <% end %>
    </ul>
  </div>
<% end %>
P226
各ERBファイルの説明。
レイアウト・・・複数のWebページに一貫した外観を与えるファイル。
テンプレート・・・Webページのメインコンテンツファイル。アクションと関連付けられている。
パーシャル・・・ページの部分的な断片を出力するサブルーチン。
P230
パーシャルの作成。
テンプレートファイルをコピーして、ファイル名の先頭に"_"を付ける。Railsは、先頭にアンダースコアがついているかどうかでパーシャルとテンプレートを区別している。
※バージョン3.1.0の場合、_form.html.erbもコピーする必要があるので注意。
P231
パーシャルをテンプレートに含める。
ex._new_seat.html.erbを含める場合
<%= render :partial=>"new_seat" %>
先頭のアンダースコアと末尾の.html.erbは省略。
※_form.html.erbのソースを_new_seat.html.erbのフォーム部分にコピーしておく必要あり。
P235
パーシャルにローカル変数を渡す。(コントローラの変数を参照しないほうがコントローラとパーシャルの結びつきが強くならないので管理が楽。)
ex._new_seat.html.erbを含める場合
<%= render :partial=>"new_seat", :locals=>{:seat=>Seat.new(:flight_id=>@flight.id)} %>
※_new_seat.html.erbの参照も@seatからローカル変数seatに変更すること。
P247
テーブルの関連をモデルに定義する。
ex.Seatsテーブルのflight_idとFlightsテーブルのidを関連付けする。FlightsテーブルがSeatsテーブルを複数持っている。
class Flight < ActiveRecord::Base
  has_many :seats
end
※カラムの名前付けが重要。Flight.idと関連付けするには、Seatsテーブルのカラム名は、flight_idとする必要がある。
関連を使ってデータを取得する。
ex.
<%= render :partial=>"seat_list", :locals=>{:seats=>@flight.seats} %>
P.253
テーブルの関連をモデルに定義する。
ex.SeatsテーブルがFligthsテーブルに属している。
Class Seats < ActiveRecord::Base
  belongs_to :flight
end
バリデーションチェックをカスタマイズする。
ex.
Class Seats < ActiveRecord::Base
  belogns_to :flight
  validate :baggage_allowance_check,:name_check
  def baggage_allowance_check
    if baggage > flight.baggage_allowance
      errors.add(:baggage,"You have too much baggage") ※フォーカスを当てる項目がない場合、:base
    end
  end
  def name_check
    if name == flight_id.to_s #to_sで数値を文字列に変換
      errors.add(:name,"Your name is the same as your flight number")
    end
  end
end
P.272
Rails3.1.0でprototype.jsを使用する場合、アプリケーション作成時のコマンドで以下のオプションを使用する。
>rails new アプリケーション名 -j prototype
(参考資料)
http://ja.asciicasts.com/episodes/265-rails-3-1-overview
ex.
<div id="seats">
<%= render :partial=>"seat_list", :locals=>{:seats=>@flight.seats} %>
</div>
<%= link_to "Refresh seats",
            :url=>"/flights/#{@flight.id}/seats",
            :method=>"get",
            :update=>"seats",
            :remote=> true
%>
(補足)
後からアプリケーションにprototype.jsを導入する場合、以下のコマンドを実行。
rails plugin install git://github.com/rails/prototype_legacy_helper.git
(参考資料)
https://github.com/rails/prototype_legacy_helper
P282
タイマーイベントを受け取る。
本来は
<div id="seats">
<%= render :partial=>"seat_list", :locals=>{:seats=>@flight.seats} %>
</div>
<%= periodically_call_remote :url=>"/flights/#{@flight.id}/seats",
                                        :method=>"get",
                                        :update=>"seats",
                                        :frequency=> 20
%>
と本の通り書きたかったが、Railsのバージョンにより、仕方なく
<%= javascript_tag do %>
$(document).ready(
  function(){
    setInterval(function(){
      $.ajax({
        url: "<%= "/flights/#{@flight.id}/seats" %>",
        type: "get",
        dataType: "html",
        success: function(html) {
           if (document.getElementById("seats") != null)
           {
             $("#list").remove();
           }
           $("<div>", { id:"list"}).appendTo("#seats");
           $("#list").append(html);
        }
    });
  }, 20000 );
});
<% end %>
<div id="seats">
<div id="list">
<%= render :partial=>"seat_list", :locals=>{:seats=>@flight.seats} %>
</div>
</div>
<%= link_to "Refresh seats",
            :url=>"/flights/#{@flight.id}/seats",
            :method=>"get",
            :update=>"list"
%>
と記述。(他によい方法があるのかは不明)
P287-290
AjaxリクエストでフォームをPOST送信する。
(テンプレートを以下のように修正)
<%= form_for(seat) do |f| %>
⇒<%= form_for(seat, :update=>'seats', :remote=>true) do |f| %>
(コントローラを修正)
  def create
    @seat = Seat.new(params[:seat])
    @seat.save
    render :partial=>"flights/seat_list", :locals=>{:seats=>@seat.flight.seats}
  end
P296-304
Ajaxでページの複数個所を更新する。
⇒コントローラでHTMLの代わりにJavaScriptを返却する。
※ただし、JavaScriptにprototype.jsを選択していない(jQueryを選択した)場合以下の方法は使用できない。
(コントローラを修正)
  def create
    @seat = Seat.new(params[:seat])
    render :update do |page|
      if @seat.save
        page.replace_html 'notice', 'Seat was successfully booked'
      else
        page.replace_html 'notice', 'Sorry - the seat could not be booked'
      end
    end
  end

(レイアウトに追加)
<p style="color: green" id="notice">
  <%= flash[:notice] %>
</p>
(テンプレートを修正)
<%= form_for(seat, :update=>'seats', :remote=>true) do |f| %>
⇒<%= form_for(seat, :remote=>true) do |f| %>
P321
モデルオブジェクトからXMLを生成。
def show_with_map
  @incident = Incident.find(params[:id])
  render :text=>@incident.to_xml
end
※renderメソッドは、ブラウザに返すレスポンスを生成。
P324
モデルオブジェクトから生成したXMLのレイアウトを修正する。
  def show_with_map
    @incident = Incident.find(params[:id])
    render :text=>@incident.to_xml(
      :only=>[:latitude, :longitude, :title, :description],
      :root=>"data")
  end
P330
どのフォーマット(例の場合、xml or html)でレスポンスを返すかを判断する処理を実装。
(ルートの実装)
match 'incidents/map/:id.:format' => 'incidents#show_with_map'
(コントローラの実装)
  def show_with_map
    @incident = Incident.find(params[:id])
    respond_to do |format|
      format.html
      format.xml {
        render :text=>@incident.to_xml(
          :only=>[:latitude, :longitude, :title, :description],
          :root=>"data")
      }
    end
  end
※to_xmlは、配列にも使用可能。P340
(テンプレートの実装)
<%=render(:partial=>'map',:locals=>{:data=>"#{@incident.id}.xml"}) %>
P335
コントローラにJavaScriptのAjaxリクエストとブラウザからのリクエストを区別させる方法。
式request.xhr?がtrueならAjaxリクエスト。(ただし、Prototypeライブラリのみ)

デフォルトのテンプレート(アクション名とマッチしているもの)で処理できる場合は、renderメソッドの呼び出しは省略可。

P346
更新日付が24時間以内のデータ取得サンプル。
  def news
    @incidents = Incident.all(:conditions=>['updated_at > ?',Time.now.yesterday])
    render :xml=>@incidents
  end
P352
XMLビルダーテンプレート。RSSフィードに使用。
(XMLビルダーテンプレートの実装)
xml.rss(:version=>"2.0"){
  xml.channel{
    xml.title("Head First Climbers News")
    xml.link("http://localhost:3000/incidents/")
    @incidents.each do |incident|
      xml.item{
        xml.title(incident.title)
        xml.description(incident.description)
        xml.link("http://localhost:3000/incidents/#{incident.id}")
      }
    end
  }
}
※ファイル名=news.xml.builderで保存
P353
ページにRSSフィードを追加。
⇒ブラウザは、<link../>タグの参照先を探して、ニュースフィードを認識する。
(レイアウトのヘッダー部に追加)※application.html.erb
<%= auto_discovery_link_tag(:rss, {:action=>'news'}) %>
P385
メソッド名の末尾に?を付ける理由のは、Rubyの慣習。trueかfalseを返却するメソッドに付ける。
ex. new_record?
P388
RESTfulルートについて。
ルートに"resources :incidents"と記述すると自動で標準的なルートが生成される。
これは、以下のコマンドで確認可能。
>rake routes
上記画像のedit_incidentにアクセスするには、
edit_incident_path(@incident) ※相対パスを返却(ex. /incidents/3/edit)
または、
edit_incident_url(@incident) ※絶対パスを返却(ex. http://localhost:3000/incidents/3/edit)

0 件のコメント:

コメントを投稿