tips  >> 02.プログラミングなど  >> Ruby  >> Railsめも.txt
[フレームで表示]

Railsめも



インストール(Windows)
    (1) Rubyのインストーラを入手
        http://rubyinstaller.rubyforge.org
        実行 → 「すべてのコンポーネント」を選択
    (2) Railsのインストール
        gem install rails --include-dependencies

※ MySQLもインストールしておく
    http://dev.mysql.com

アップデート
    gem update rails



インストール(Linux)
    (1) yum install ruby rubygems
    (2) gem install rails --include-dependencies
        Q. 次のようなエラーが出たときは・・・
            Bulk updating Gem source index for: http://gems.rubyforge.org
            ERROR:  While executing gem ... (Gem::GemNotFoundException)
                Could not find rails (> 0) in any repository 
            
            ERROR:  While executing gem ... (Gem::RemoteFetcher::FetchError)
                SocketError reading http://gems.rubyforge.org/gems/activerecord-1.15.3.gem
        
        A. 同じコマンドで何回か試すとうまくいくかも
    
            ※ なので rails → Rails と直したらOKになったのは たまたま?
    
    ※ MySQL
        yum install mysql mysql-server
    


用語

    ルータ:リクエストを適切なアクションに結びつけるコンポーネント
    アクション:コントローラ内の1つのメソッド
    コントローラ:なにをすべきかをモデルに伝える
    モデル:どのように実行すべきかを知っている



ORM
    ORMについて
        ・クラス:テーブル
        ・オブジェクト:行
        ・属性(プロパティ):列
        ・クラスメソッド:テーブル単位の操作
        ・インスタンスメソッド:行単位の操作
        という風にオブジェクトとRDBをマッピングする手法。
    
    RailsでのORM
        ActiveRecord クラス
    
    一般的なORMの問題点
        マッピングを大量のXMLで設定しないといけないこと
        → ActiveRecordはそうでもないらしい
    
    作成
        scaffoldユーティリティで作成可能
        ※例:products テーブルの adminコントローラを作るとき
              ※ Admin って名前は他の名前でもよい
            ruby script/generate scaffold Product Admin
    
    設定ファイル
        config/database.yml

    参照(結果が1行の場合)
        order = Order.find(1);
        puts "Order #{order.customer_id}, amount=#{order.amount}"
    
    参照(結果が複数行の場合)
        Order.find(:all, :conditions => " name='dave'") do |order|
            puts order.amount
        end
        
        ※ TODO: 整理 → Order.find(params[:id]) という書き方も
        ※ :all の代わりに :first というのもある
    
    更新
        Order.find(:all, :conditions => " name='dave'") do |order|
            order.discount = 0.5
            order.save
        end
    
    外部キー(FK)の設定
        → 「1対多」の「多」に当たる方を belongs_to メソッドを使用して定義
        ※ 「1対多」の「1」 に当たる方は has_many メソッドを使用
            class LineItem < ActiveRecord::Base
                # 外部キーの設定
                belongs_to :product
                
                # ↓ これは必須ではない ※「作っとくと良いよ」っていうオススメのメソッドらしい。
                def self.for_product(product)
                    item = self.new
                    item.quantitiy = 1
                    item.product = product   # belongs_to で指定した分はこう使える
                    item.unit_price = product.price
                    item
                end
            end
        ※ DDL書いても generate コマンドで生成できたりはしないので注意
    
    
    
    where 句を動的に云々
    
        Order.find(:all, [:conditions => " name = :name and pay_type = :pay_type",
                          {:name => 'xxx', :pay_type => 'xxx'}])
        ※ 'xxx' の部分を変数に出来る
        ※ {} ごとハッシュにして渡してもいい
    
    
    find_by_sql
        例
            customer = Customer.find_by_sql('select * from xxxx')
        なにが入ってるか見たい
            p customer[0].attributes
            p customer[0].attribute_names
    
    
    count, count_by_sql
        → 戻り値はオブジェクトじゃなくて数字
    
    
    find_by_xxx
    
        find_by_col1('hello')
        find_by_col1_and_col2('hello''world')
            → よくやるなあ
    


Model

    Q. モデル層にDBと関係ないクラスを作成したいのだけども、
        テーブルが無いと困ったことになったりする?
    A. Activerecord::Base を継承しなければOK!
        → 普通にクラスを作ればいい
            class Cart
                ....
            end


ActionPack
    ビューとコントローラがActionPackというコンポーネントで ひとまとめにしてある。
    
    動的コンテンツはテンプレートによって生成
        方法1: ERb (Embedded Ruby)で、HTML内にRubyコードの断片を埋め込む。拡張子は.rhtml。 ← JSPみたいな
        方法2: ビルダスタイルビュー。Rubyコードを使用してXMLを生成 ← TODO:イメージ沸かないので後で読み返す
        
        ※ ERb は現在の呼び方は eRuby。
    



rails コマンド
    
    # プロジェクトを作成 (demoという名前として)
    rails demo
    
    # テスト用サーバ起動
    cd demo
    ruby script/server
        → http://localhost:3000/
        ※ 止めるときは Ctrl+C でOK
    
    # コントローラを生成 (Sayという名前として)
    ruby script/generate controller Say
        → app/controllers/say_controller.rb  ほかが生成される
    
    # ※ アクションも設定する場合
    ruby script/generate controller Say index
    ruby script/generate controller Say action1
        → index はデフォルトのアクション
    
    # アクションの定義
    app/controllers/say_controller.rb にメソッドを追加
    
    # ビューの作成
    app/views/say/hello.rhtml
    
    # テーブル作成後・・・ (TODO: 適切なコメントを・・・)
    ruby script/generate scaffold Product Admin
                                   ↑      ↑
                                   |      コントローラの名前
                                   モデル名(テーブル名(products)の単数形を指定すること)
        
        → データのCRUD操作が出来るページが生成される
        ※ ここでgenerateコマンドは 開発用DB(xxx_development) のテーブル定義を見に行ってる
        ※ scaffold は、建築現場などの「足場」を意味するそうだ。
    
    # Validation(フォームの入力チェック)のための記述は、モデルのクラスに書き加える
    こんな感じ↓
        class Product < ActiveRecord::Base
            # Railsが用意しているメソッドで そのまま使える条件
            validates_presence_of(:title, :description, :image_url)  # 空でないことチェック
            validates_numericality_of(:price)                        # 数値として妥当かチェック
            validates_uniqueness_off(:title)                         # ユニーク制約
            validates_format_of(                                     # 正規表現で指定
                :image_url,
                :with => %r{^http:.+\.(gif|jpg|png)$}i,
                :message => "はGIF、JPG、PNG画像のURLでなければなりません"
            )
            # 自由に条件を書き足す場合
            protected
            def validate
                # 正の数であることチェック
                errors.add(:price, "は 0 より大きくなければなりません") unless price.nil? || price > 0.0
            end
        end
    
    


ディレクトリ構成について

D:.
└─demo
    │  Rakefile
    │  README
    │
    ├─app
    │  ├─controllers                  # コントローラはここに配置
    │  │      application.rb           #  - アプリケーション共通のフィルタ処理とか
    |  |      xxxx.rb                  #  - コントローラ毎に1ファイル。アクションはファイル内の1メソッド
    │  │
    │  ├─helpers                      # ヘルパー:コントローラで共通で使うメソッドを定義
    │  │      application_helper.rb    #  - アプリケーション全体で使うものはここに
    |  |      xxxx_helper.rb           #  - コントローラ毎にも定義できる
    │  │
    │  ├─models                       # モデル(含ORMのオブジェクト)はここに配置
    |  |                               #   Validateの処理もここに書く
    │  └─views                        # ビュー(rhtml)はここに配置
    |      |                           #   1コントローラに1ディレクトリ、
    |      |                           #   1アクション1ファイルに対応
    │      └─layout                   # レイアウトファイル(各画面共通のファイル)
    ├─config                           # 設定情報
    │  │  database.yml                 #  - データベース接続設定
    │  │  routes.rb
    │  │  boot.rb
    │  │  environment.rb               #  - 文字コード設定とか
    │  │
    │  └─environments
    │          production.rb
    │          development.rb
    │          test.rb
    │
    ├─components
    ├─db
    ├─doc
    │      README_FOR_APP
    │
    ├─lib
    │  └─tasks
    ├─log
    │      server.log
    │      production.log
    │      development.log
    │      test.log
    │
    ├─public               # 公開ディレクトリ
    │  │  .htaccess
    │  │  dispatch.rb      # ディスパッチャ
    │  │  dispatch.cgi     
    │  │  dispatch.fcgi    
    │  │  404.html
    │  │  500.html
    │  │  index.html
    │  │  favicon.ico
    │  │  robots.txt
    │  │
    │  ├─images
    │  │      rails.png
    │  │
    │  ├─javascripts
    │  │      prototype.js
    │  │      effects.js
    │  │      dragdrop.js
    │  │      controls.js
    │  │      application.js
    │  │
    │  └─stylesheets       # スタイルシート
    ├─script                   # 開発過程で使うユーティリティ
    │  │  about
    │  │  breakpointer
    │  │  console
    │  │  destroy
    │  │  generate
    │  │  runner
    │  │  server               #  - WEBrickサーバ(テスト用のWebサーバ)
    │  │  plugin
    │  │
    │  ├─performance
    │  │      benchmarker
    │  │      profiler
    │  │
    │  └─process
    │          reaper
    │          spawner
    │          inspector
    │
    ├─test
    │  │  test_helper.rb
    │  │
    │  ├─fixtures
    │  ├─functional
    │  ├─integration
    │  ├─mocks
    │  │  ├─development
    │  │  └─test
    │  └─unit
    ├─vendor
    │  └─plugins
    └─tmp
        ├─sessions
        ├─sockets
        ├─cache
        └─pids



URLについて

    http://xxx.xxx.xxx.com/say/hello
                           ↑    ↑
                           |    アクション
                           コントローラ


ERb (rhtml) について ※ view、ビュー

    埋め込むもの
        <% スクリプト %>   ← 画面表示したくないとき(ifとかwhileのブロックの開始行とか)
        <%= スクリプト %>  ← こうすると戻り値が表示される
        
        <% スクリプト -%>  ← こうすると行末の改行が出力されない(HTMLを見たときに変なところで改行が入らないようになってうれしい)
    
    HTMLの特殊文字のエスケープ
        <%= h("xxxx") %>
            ※ h("(-_-) --> (._.)") とか書いたときに > が &gt; に変換される
    
    変数
        コントローラのインスタンス変数(@xxx)にアクセスできる。
        ※ PHPでいうところの smarty->assign($xxx); が不要である
        ↑ ステキ機能だと思う
    
    リンク
        <%= link_to("表示名",
                    :action  => "アクション名",
                    :confirm => "確認メッセージ"# 3つ目以降の引数はoptional
                    :id      => "モデル名",       # モデル名.id がフォームパラメータとして渡される
                    :class   => "CSSクラス名") %>
        
        ※ 他に指定できるもの
            :id => 'モデル名?'
            :controller => 'コントローラ名' というのもある?
    
    一覧表示について
        次のようにして全部の行、全部のカラムについて処理している
        (scaffoldで吐き出されるコード)
            <% for product in @products %>
                <% for column in Product.content_columns %>
    
    ページネーション
        @product_pages.current.previous : 前のページ
        @product_pages.current.next : 次のページ
    
    レイアウトファイル
        app/view/layout ディレクトリにファイルを配置することで、共通のHTMLを呼び出せる。
        ・コントローラと同じ名前で作成すると、そのコントローラ内の全てのアクションで共通
        ・
        
        <html>
            <head>
                <title> @page_title || "デフォルトのページ名" </title>
                ....
            </head>
            <body>
                   :
                (メニュー(ホーム, news, お問い合わせ)とか)
                   :
                <%= @content_for_layout %>
            </body>
        </html>
        
        ※ レイアウトファイル内で参照できる変数
            @content_for_layout : 各ページ固有の内容
            @page_title         : ページのタイトル
                ※ @page_titleは どこで設定してる?
                    → コントローラでもいいけど @content_for_layout に当たるファイル内で記述できる
                    <% @page_title = "このページのタイトル" -%>
        
    共通部分を切り出す
        <%= render(:partial => 'form') %>
        のように書くと、
            ・同ディレクトリにある _form.rhtml (頭に _ が要るよ) が include される
    
        <%= render(:partial => 'order_line', :collection => @pending_orders) %>
        のように書くとさらに、
            ・@pending_orders の要素数分だけ繰り返し(TODO:合ってる?)
            ・@pending_orders の1要素には、 _order_line.rhtml 内から
               order_line というローカル変数としてアクセス可能
    
    他のアクションを呼び出して表示する
        <%= render_component(:action => "display_cart", :params => { :context => :checkout }) %>
        
        ※ レイアウトファイルを通さずに直接表示したい場合(ヘッダとかフッタが不要な場合)は
            呼び先のアクション(display_cart)で、次の行を書く
            render(:layout => false)
        
    
    フォーム関連のヘルパー
        <%= error_messages_for("table_name") %>                                 # 入力エラーメッセージ表示用
        <%= start_form_tag(:action => 'create') %>                              # <form>
            <%= text_field("table_name""column_nmame", :size => 40) %>        #   <input type="text" ...>
            <%= textarea("table_name""column_name", :size => 40) %>           #   <textarea> ..
            <%= datetime_select('table_name''column_name') %>                 #   <select .. 日付と時刻
            <%= select("table_name""column_name", [["1","あ"],["2","い"]]) %> #   <select .. 普通の
            <%= check_box("to_be_shipped", order_line.id, {}, "yes""no") %>   #   <input type="checkbox" ...>
                                                                                #     3つ目の引数( {} ) はどういうときに使われるのだろう
            <%= submit_tag("Create") %>                                         #   <input type="submit" ...>
        <%= end_form_tag %>                                                     # </form>
        
        ※コントローラで受け取り時のデータは、例えばこんな感じ
            @params = { "to_be_shipped" => { "1" => "yes" } }
            
            なので、全部のチェックボックスに関して処理をするなら
                to_ship = params[:to_be_shipped]
                if to_ship
                    to_ship.each do |order_id, do_it|
                        if do_it == "yes"
                            # 何か処理
                        end
                    end
                end
        


データベース関連

    テーブル作成時
        データベースを3つ作る
            ・dbname_development : 開発用
            ・dbname_text        : テスト用
            ・dbname_production  : 本番用
        TODO: 切り替え方の話について追記
    
    接続情報の設定
        config/database.yml
            ※ YAML形式のファイル
            ※ 上記の3種類分のための設定項目が、あらかじめ書かれている
    
    テーブル名の規約
        テーブル名
            アンダーバー(_)区切りで、複数形の名前をつける
                → generate コマンドで生成するクラスは先頭大文字、単数形のクラス名にする
            例: (テーブル名)   (モデルのクラス名)
                products       Product
                line_items     LineItem
        主キー
            id
        外部キー(これは規約?)
            constraint fk_items_product foreign key (product_id) references products(id),
        



セッション

設定
    session[:キー名] = xxxx; 


(1) コントローラにメソッドを追加
  private
  def find_cart
    session[:cart] ||= Cart.new # 無かったらnewする
  end


※ 保存されるファイル
    /tmp/ruby_sess*****



エラー処理

コントローラ側
    アクションのメソッド内に記述
        def action1
            xxx = xxx
            ...
            redirect_to(:action => 'xxxx')
        rescue                              # begin は省略可能ということ?
            logger.error("ログに書くメッセージ")
            flash[:notice] = 'エラー画面に出すメッセージ'
            redirect_to(:action => 'index')
        end
    
テンプレート側
    <% if @flash[:notice] -%>
        <div><%= @flash[:notice] %></div>
    <% end -%>
    
    → レイアウトファイルに記述すると各画面共通にできて良い


※さらに拾いきれてないエラーは、ApplicationControllerで処理するといい
    class ApplicationController < ActionController::Base
        ...
        def rescue_action_in_public(exception) 
            ... # という書き方で良いのかしら ※ TODO: 
        end
        ...
    end



ログ機能

    書き方
        logger.info("これは通常")
        logger.warn("これは警告")
        logger.error("これはエラー")
        logger.fatal("これは致命的")

    書き出される場所
        log/development.log
    
    どのレベルまで出すか設定
        TODO:



デバッグ用機能

    ruby script/console
        irb が開き、コマンドプロンプトのようにRubyスクリプトが書ける
    
    ruby script/breakpointer
        あらかじめソースに埋め込んでおいた breakpoint() メソッドの位置で(ブラウザ操作で)進むと、、
        irb が開き、breakpoint() 部分の状態で、オブジェクトが参照/操作できる
        irb を終了すると(quitでいいのかな)、ブラウザ操作に戻る
        
        ※ 他のサーバと通信する場合は breakpointer -s 192.168.x.xxx
        
    


generate コマンドいろいろ

    # コントローラのみ生成
    ruby script/generate controller Say
    
    # (コントローラと)アクションを生成
    ruby script/generate controller Store index
                                     ↑     ↑
                                     |     アクションメソッド名(省略可)
                                     コントローラ名
    
    # モデルを生成
    ruby script/generate model LineItem
        ※ TODO:確認 → 無いテーブルを指定すると db/migrate というところにテーブル作成用のスクリプトらしきものが出来る
    
    # CRUDの画面を生成
    ruby script/generate scaffold Product Admin
                                   ↑      ↑
                                   |      コントローラの名前
                                   モデル名(テーブル名(products)の単数形を指定すること)
    
    オプション引数
        -f : 強制的に上書き
        -s : 存在するファイルはスキップする
        -q : 表示を抑える
    
    ジェネレータ
        script/generate xxxxxx の、xxxxx部分
            scaffold           : 足場作成
            model              : モデルのみ
            controller         : コントローラのみ
            
            integration_test
            mailer
            migration
            observer
            plugin
            resource
            scaffold_resource
            session_migration
            web_service



日本語設定

    (1) my.iniの設定
        
        [mysql]
        default-character-set=utf8

        [mysqld]
        default-character-set=utf8
        init-connect=SET NAMES utf8
        skip-character-set-client-handshake

    (2) config/environment.rb の先頭に KCODE の設定をする
        $KCODE = "UTF8"
    
    (3) config/database.yml に encoding パラメータを設定する
            development:
              adapter: mysql
              database: xxxx
                  :
              encoding: utf8          # ←ここ
        
    
    (4) app/controllers/application.rb (ApplicationControllerクラス) に
        文字コードセット設定の記述を追加。
        ※ ここに書くと全てのコントローラの一番最初に呼ばれる。
            (こういうのを「フィルタによる処理」という)
        
        class ApplicationController < ActionController::Base
            before_filter :set_charset
            
            private
            
            def set_charset
                headers["Content-Type"] = "text/html; charset=UTF-8"
            end
        end
    
    (5) データベース作成時にデフォルトのキャラクタセットにUTF-8を設定
        create database xxx_development default character set utf8;
    
    

ActiveRecord

    フック:
        例えば・・
            before_createフック:
                ActiveRecord::Base を継承しているクラスで
                before_create()メソッド をオーバーライドすると
                before_create()メソッド内に書いた処理が create文を実行する前に実行される
    
    class User < ActiveRecord::Base
          :
        def before_create
            self.name = self.name << "様"
        end
    end

    TODO:他のフックは・・
    
    
    スキーマについての規約
        ・テーブル名とクラス名
            - テーブル名は複数形
            - 単語の区切りは _ (アンダーバー)
            - 対応するクラス名は単語の先頭を大文字にして _ を取り除いたもの
                ※ 例:テーブル favorite_songs に対して、クラス FavoriteSong
        
        ・キーのカラム名
            - 主キーのカラム名は「id」、int auto_increment
            - 外部キーのカラム名は「テーブル名の単数_id」 
                ※例: vip_customers テーブルに対して vip_customer_id
        
        ・日付関連のカラム名
            - DATE型のカラムには名前を 「受動態_on」にする
            - TIMESTAMP型のカラムには名前を「受動態_at」にする
                ※例: sent_at, fired_on
            - 更新日時、作成日時は「updated_at」、「created_at」(日までなら「〜_on」)
        
        ・関連テーブル(「多対多」のときに間にくるテーブル)は
            - 関連させたいテーブル名をくっつけた名前にする
            - カラム「id」を作らずに、関連させる2つのキーのセットを主キーにする
            ※例:
                create table products_categories (
                    product_id int,
                    category_id int,
                    primary key(product_id, category_id)
                );
        
        ・lock_version というint default 0 のカラムがあると、
            参照してから更新するまでの間に その行が他の人によって更新されていると、
            exceptionが吐かれる (こういうのを「楽観的ロック」というらしい)
        
    
    
    Q. 規約に沿ってないテーブル名も使いたい
    
    A.
        (1) 別のテーブル名が使いたい場合はset_table_name ディレクティブを使う
            class Customer < ActiveRecord::Base
                set_table_name "T-00859_03"
            end
        
        (2) 単数複数で分けたくない場合は environment.rb に設定する
            config.active_record.pluralize_table_names = false
        
        (3) 主キーを id という名前以外にしたい場合
            set_primary_key "isbn"
        
        (4) belongs_to
            belongs_to :xxxxx,
                       :class_name => "Order"
                       :foreign_key => "order_id"
                       :conditions => "paid_on is not null"
            
            has_and_belongs_to_many :recommend_products,
                                    :join_table => 'recommend_lists'
        
    
    
    Q. 型とかデフォルト値知りたい
    
    A. column_hash を使う
        p Customer.column_hash['birthday'].type
        p Customer.column_hash['birthday'].default
    
    
    Q. 型キャストされる前のデータが取り出したい
    
    A. 属性名 xxxx のとき xxxx_before_type_cast で取り出せる
    
    
    boolean のデータを評価するときは カラム名の後ろに ? をつけたアクセサを使うこと
    
        user.superuser  : nil, false 以外 (0 とか f とか) が true と見なされてしまう
        user.superuser? : 0, f, false '', nil, false が false と見なされる
    
    
    値を更新した後に DBに反映させるには save() メソッドを使用する
    
        INSERTの仕方の例
            (1) new メソッドの場合
                order = Order.new(:name => 'xxx', :price => 'xxx')
                order.save
            (2) create メソッドの場合
                Order.create(:name => 'xxx', :price => 'xxx')
                ※ create だと saveしなくても反映される
                ※ 配列渡すとまとめて insert出来たりする
        
        UPDATEの仕方の例
            (1) 1件だけ
                order.name = 'xxx'
                order.save()
            (2) 1件だけ(2)
                order.update_attribute(:name, "xxx")
            (3) まとめて
        
        DELETEの仕方の例
            (1) delete
                User.delete(id)
                delete_all は まとめて消す
            (2) destroy
                destroy
                destroy_all
                →「コールバック」の処理を通したい場合は destroy じゃないとダメらしい TODO:?
    
    
    
    失敗した場合に exception 吐かせたい場合は save ではなく save! を使う
    
    



フィルタ
    
    # コントローラ内の全部のアクションで、実行前に呼ぶ
    class AdminController < ApplicationController
        before_filter :authorize
          :
    end
    
    # 特定のアクション以外全部、実行前に呼ぶ
    class LoginController < ApplicationController
        before_filter :authorize, :except => :login
          :
    end



Active Support

    全てのRailsコンポーネントによって共有されてるライブラリのセット。
        ※ なので、Rubyでは使えないが Railsでは使える
    
    数値を拡張して追加されたメソッド
        even?         ※ if (num.even?)  print "偶数"  みたいにかけるのだろうか
        odd?
        1.hours          # => 72000
        1.hour.from_now # => 1時間前の時刻
    時刻
        now = Time.now
        now.last_month   # => 先月
    文字列
        "cat".pluralize    # => cats (複数に)
        "cats".singularize # => cat  (単数に)




未整理メモ

    組み込みのヘルパーメソッド
        link_to
        redirect_to
        number_to_currency
        stylesheet_link_tag "scaffold""depot", :media => "all"
            
        
    
    「:action => "goodbye"」という記述について
    → 「{:action => "goodbye"}」の省略形、つまりハッシュ
    → :(コロン)が要るのはなんで?
    → Rubyの「シンボル」という文法らしい
    
    
    Q.
        app/models/product.rb:6: syntax error, unexpected kEND, expecting $end
        みたいなエラーが出るとき
    
    A.
        ファイルをSJISで書いてしまってないか、UTF-8(BOMつき)で保存していないか確認。
        ※ UTF-8(BOMなし)でOK
        
        ※ 単に文法間違いの可能性も

    truncate(str, 80)  → 80文字で折り返し
    
    
    コントローラからアクションを指定して画面遷移
        redirect_to(:action => 'display_cart')


    flash
        @flash : 次のリクエストの処理が終わるまで保持される
    
    カラム名の規約
        xxxx_at : datetimeフィールド  ※ sent_at とか
        xxxx_on : dateフィールド      ※ last_edited_on とか
        
        ※ 
            updated_at, updated_on
            created_at, created_on
    
    テストサーバ起動したときに、test, production の環境でも実行したい
        ruby script/server -e test
        ruby script/server -e production
    
    コントローラにサブディレクトリを掘った場合、ディレクトリ名は「モジュール名」として定義
        つまり、controllers/admin/register_controller.rb というファイルの場合
        
            class Admin::RegisterController < ApplicationController
                ...
            end
        
        となる
    
    
    Q. 標準の日付形式を変える。
        (scaffoldで作ったページで日付があると横に長くて見づらい・・・)
    
    A. config/environment.rb に次の記述を追加
        ActiveSupport::CoreExtensions::Time::Conversions::DATE_FORMATS.merge!({
          :default => "%Y/%m/%d %H:%M",
        })
        
        ※ scaffold 実行時はエラーになるのでコメントアウトしとこう
    
    
    Q. 一覧に罫線がつけたい(ついでにマージンとかフォントとか色の設定も)
    
    A. public/stylesheets/scaffold.css (の一番うしろ) に次を追加
        body, p, ol, ul, td {
          font-family: 'MS ゴシック';
        }
        table, th, td {
            border-color: silver;
            border-width: 1px;
            border-style: solid;
            padding: 5px;
            margin: 0px;
            vertical-align: top;
            border-collapse: collapse;
        }
        tr:hover {
            background-color: ghostwhite;
        }
        th {
            background-color: lavender;
        }
        p {
            padding: 5px;
            margin: 0px;
        }
        a {
            padding: 3px;
        }
    
    
    Q. ページネーションで1ページに表示する件数を変更
    
    A. コントローラ(app/controllers/xxx_controller.rb)に記述する
        → :per_page に指定する数字を変更すればいい
        
        def list
          @user_pages, @users = paginate :users, :per_page => 10
        end
    
    
    
    ActiveRecordいろいろ
                  親                     子
        1対1  : has_one              belongs_to
        1対多 : has_many             belongs_to

        class Category < ActiveRecord::Base
          has_many :products
          :
        end

         :

        category.products.each do |product|
          p product.name
        end



        has_and_belongs_to_many のことを略して habtm と呼んだりするらしい

        has_and_belongs_to_many :services, :join_table => 'price_list'
        <% action_card.character_classes.each do |character_class| %>
            <%=h character_class.name %>
        <% end %>


        同じテーブル同士で多対多のとき
            継承するといい
    
    
    
    ActiveRecordを単体で使う("ActionRecord"は覚え間違い
    
        #!/usr/bin/ruby -w

        require "rubygems"
        require "active_record"

        ActiveRecord::Base.establish_connection(
            :adapter  => "mysql",
            :host     => "localhost",
            :username => "xxxxxxxx",
            :password => "xxxxxxxx",
            :database => "xxxxxxxx",
            :socket   => "/var/lib/mysql/mysql.sock",
            :encoding => "utf8"
        )

        class User < ActiveRecord::Base
        end

        User.find(:all).each { |user|
            p user.name
        }   
    
    
    
    Q. ActiveRecordを単体で使おうとして次のように書いた
            require "rubygems"
            require_gem "activerecord"
        実行時に次のwarningが出た
            「./test.rb:4:Warning: require_gem is obsolete.  Use gem instead.」
    
    A.
        require_gem "activerecord" の代わりに
        require "active_record" と書けばいい (active と record の間の _ が必要)
    
    
    
    


    



Q. scaffoldでカンタンなCRUDのページは出来るのは分かったけど、リレーション組んであるページにも使いたい!


A. 例を書いてみた

       +---------------+
       | products      |                        +---------------+
       +---------------+                        | categories    |
       | id            |                        +---------------+
       | category_id   | (∞ --------------- 1) | id            |
       | name          |                        | name          |
       +---------------+                        +---------------+

    のようになっていたとして

    1. DDL
        create table products (id int not null auto_increment, category_id int not null, name varchar(100) not null, primary key(id))engine=InnoDB;
        create table categories (id int not null auto_increment, name varchar(100) not null, primary key(id))engine=InnoDB;

    2. 足場作成
        ruby script/generate scaffold Product AdminProduct
        ruby script/generate scaffold Category AdminCategory

    3. URL
        http://localhost:3000/admin_product
        http://localhost:3000/admin_category

    4. 関連を定義
        モデル側
            (1) app/models/product.rb に 依存関係を記述する
                belongs_to :category
                
                → View側の(2)のように書くために必要

        View側
            (1) app/views/admin_product/_form.rhtml を編集 : 入力画面と編集画面で使用される
                <p>
                <%=
                  # これは selectの要素に
                  @categories = Category.find(:all).map{|u| [u.name, u.id] }
                  
                  # <select> ... </select> を生成
                  #  select 'テーブル名(の単数)''カラム名', @一覧の配列
                  select 'product''category_id', @categories
                %>
                </p>
            
            (2) app/views/admin_product/list.rhtml を編集 : 一覧表示
                <td><%=h product.category.name %></td>
            
            (3) app/views/admin_product/show.html を編集 : 詳細表示
                ※ 一覧表示で情報足りてるし、なくてもいいや

        コントローラ側
            特に何もしない

    5. 完了! 4行足すだけ!
        ※ でもこれも単調作業だから、「モデル」か設定ファイルに依存関係を定義するだけで、残りはgenerate コマンドで一緒に生成されてくれたらうれしいなあと思ったり、知らないだけで、そういう方法もあるのかもしれないと思ったり。



Q. 上の例で、関連してるテーブルを参照する方法は分かった。
   こんどは「多対多」の関係のテーブルを扱うときに、相手のテーブルが参照したい。


A. 例を書いてみた

    +------------+             +--------------+
    | customers  |             | orders       |             +-------------+
    +------------+             +--------------+             | products    |
    | id         | (1 ---- ∞) | customer_id  |             +-------------+
    | name       |             | product_id   | (∞ ---- 1) | id          |
    +------------+             +--------------+             | name        |
                                                            | category_id |
                                                            +-------------+

    注文(orders)テーブルに、誰が(customers)、何の商品を(products) 買うかを記録するという設定。
    
    先の例だと、ordersテーブルのデータを登録するのにプルダウンが使えるようになるが、
    customers の一覧で、その人が どの商品を注文しているのか表示したい場合の話。
    
    1. DDL
        create table products (id int not null auto_increment, category_id int null, name varchar(100) not null, primary key(id)) engine=InnoDB;
        create table customers (id int not null auto_increment, name varchar(100) not null, primary key(id)) engine=InnoDB;
        create table orders (customer_id int not null, product_id int not null, primary key(customer_id, product_id)) engine=InnoDB;

    2. 足場作成
        ruby script/generate scaffold Product AdminProduct
        ruby script/generate scaffold Customer AdminCustomer
        ruby script/generate scaffold Order AdminOrder

    3. URL
        http://localhost:3000/admin_product
        http://localhost:3000/admin_customer
        http://localhost:3000/admin_order
        
    4. 取りあえず orders テーブルの入力画面を作る
        モデル側
            app/models/order.rb に 依存関係を記述する
                belongs_to :customer
                belongs_to :product
        View側
            (1) app/views/admin_order/_form.rhtml を編集
                <p><%=
                  @customers = Customer.find(:all).map{|u| [u.name, u.id] }
                  select 'order''customer_id', @customers
                %></p>
                <p><%=
                  @products = Product.find(:all).map{|u| [u.name, u.id] }
                  select 'order''product_id', @products
                %></p>
            
            (2) app/views/admin_order/list.rhtml を編集 : 一覧表示
                <td><%=h order.customer.name %></td>
                <td><%=h order.product.name %></td>
        
        と、ここまでは先の例と同じ。
    
    5. customer の一覧から products が参照できるようにする (★ ポイントはここだけ)
        モデル側
            app/models/customer.rb に依存関係を記述
                has_and_belongs_to_many :products, :join_table => 'orders'
                
                → 実は orders テーブルの代わりに
                   customers_products っていう名前にしとくと 第2引数は不要
        
        ビュー側
            app/views/admin_customer/list.rhtml を編集
                <th>買ったもの</th>
                ...
                <td>
                  <% customer.products.each do |product| %>
                    <%=h product.name %><br />
                  <% end %>
                </td>
                
                → customers から(ordersテーブルを挟んで向こう側の)
                   productsテーブルが配列として取れる!
    
    6. ついでに罫線とかつけたいから css をいじる
        public/stylesheets/scaffold.css
            table, th, td {
                border-color: silver;
                border-width: 1px;
                border-style: solid;
                padding: 5px;
                margin: 0px;
                vertical-align: top;
                border-collapse: collapse;
            }
            th {
                background-color: lavender;
            }
    
    7. 完了! これをSQLいっこも書かずにいけるってすごいですよね。



Q. でも「多対多」の結合って、同じテーブルを指してるときあるよね?


A. 例を書いてみた

    +--------------------+             +----------------------+
    | products           |             | recommend_list       |
    +--------------------+             +----------------------+
    | id                 | (1 ―┬ ∞) | product_id           |
    | name               |      └ ∞) | recommend_product_id |
    | category_id        |             +----------------------+
    +--------------------+
    
    お勧めリスト(recommend_list)テーブルに、商品(products)に対する、お勧め商品(products)を登録しておくという設定。
    
    先の例で has_and_belongs_to_many, :products としたいけど、どっちのフィールドも products に関連づいてるからどうしようという話。
    
    → Railsではこういう場合、「継承」が便利に使える。
       モデルの product を継承した recommend_product を作成すると、
       次のように、あたかも recommend_products というテーブルがあるかのように扱える。
    
    +--------------------+             +----------------------+
    | products           |             | recommend_lists      |
    +--------------------+             +----------------------+
    | id                 | (1 ―― ∞) | product_id           |
    | name               |      ┌ ∞) | recommend_product_id |
    | category_id        |      |     +----------------------+
    +--------------------+      |
             △                 |
             | (継承)          |
    +--------------------+      |
    | recommend_products |      |
    +--------------------+      |
    | (id)               | (1 ―┘
    | (name)             |
    | (category_id)      |
    +--------------------+
    
    
    1. DDL
        create table products (id int not null auto_increment, category_id int null, name varchar(100) not null, primary key(id)) engine=InnoDB;
        create table recommend_lists (product_id int not null, recommend_product_id int not null, primary key(product_id, recommend_product_id)) engine=InnoDB;

    2. 足場作成
        ruby script/generate scaffold Product AdminProduct
        ruby script/generate scaffold Customer AdminRecommendList

    3. URL
        http://localhost:3000/admin_product
        http://localhost:3000/admin_recommend_list
    
    4. 子クラスを定義する(★ポイントはここだけ)
        モデル側
            app/models/product.rb に 子クラスを定義する
                class RecommendProduct < Product
                end
    
    5. 後はいままで同様 recommend_lists テーブルの入力画面を作る
        モデル側
            app/models/recommend_list.rb に依存関係を定義
                belongs_to :product
                belongs_to :recommend_product
            
        View側
            (1) app/views/admin_recommend_list/_form.rhtml を編集
                <p><%=
                  @products = Product.find(:all).map{|u| [u.name, u.id] }
                  select 'recommend_list''product_id', @products
                %></p>
                <p><%=
                  @recommend_products = RecommendProduct.find(:all).map{|u| [u.name, u.id] }
                  select 'recommend_list''recommend_product_id', @recommend_products
                %></p>
            
            (2) app/views/admin_recommend_list/list.rhtml を編集 : 一覧表示
                <td><%=h recommend_list.product.name %></td>
                <td><%=h recommend_list.recommend_product.name %></td>
        
    6. product の一覧から recommend_products が参照できるようにする
        モデル側
            app/models/product.rb に依存関係を記述
                has_and_belongs_to_many :recommend_products, :join_table => 'recommend_lists'
        
        ビュー側
            app/views/admin_product/list.rhtml を編集
                <th>買ったもの</th>
                ...
                <td>
                  <% customer.products.each do |product| %>
                    <%=h product.name %><br />
                  <% end %>
                </td>
                
                → customers から(ordersテーブルを挟んで向こう側の)
                   productsテーブルが配列として取れる!
    
    7. 完了! なんだかスマートだ。







未整理

TODO: PHP+Smartyで開発している人を対象としたRoR勉強会してみる
    ※他にこんなのがあるよって話
        Python: Django
        PHP: Symphony, Maple(国産!+DIコンテナ使用が特徴。でも開発が止まってる), 
    ※ Ruby使わなくてもPHPでも参考に出来るパターンがたくさん詰まってるよって話

    ※ いいと思うところ
        「よくある悩み」のキレイな解決法が示されている
            ・テンプレートの共通部分(layout)置き場と書き方。
            ・テンプレートでコントローラのインスタンス変数が参照できること
            ・O/Rマッピングされたクラスがgenerateコマンドで生成できること(ActiveRecord!ActiveRecord!)
            ・フィルタ
            ・フォーム関連のヘルパー

TODO: Symphonyも使ってみる→機能の対応表とか作る?→そんなモチベーションはなくなってきた