Rails4 チュートリアル写経

WEB+DB Press の特集記事を見つつ。さっきナニした通り Ruby は 2.0.0-p0 です。

$ ruby -v
ruby 2.0.0p0 (2013-02-24 revision 39474) [x86_64-linux]

とりあえず bundler 入れなさい、とあるな。

$ gem i bundler --no-ri --no-rdoc
Fetching: bundler-1.3.2.gem (100%)
Successfully installed bundler-1.3.2
Done installing documentation for bundler (0 sec).
1 gem installed

で、rails を普通に導入すれば 4 なナニが入るのかな。

$ gem i rails

確認。

$ rails -v
Rails 3.2.12

む。--pre を付けろ、とか注釈がありますね。リトライできるのかな。

$ gem i rails --pre --no-ri --no-rdoc

なんか導入でハマりそうな予感。案の定以下な出力。

ERROR:  While executing gem ... (Gem::DependencyError)

gem list --local してみると action* なナニが導入されてるのでこれがいかんのかな。手動で全部 uninstall してみます。で、リトライ。
駄目か。-v 指定で云々、なのかな。と思ったら --pre 指定なのか。
む、thread_safe 入れれ、ときた。

$ gem i thread_safe -v 0.1 --no-ri --no-rdoc

で、

$ gem i activesupport --pre --no-ri --no-rdoc

actionpack を入れようとしたら builder (~> 3.1.0), rack (~>1.5.2) と出た。

gem i builder -v 3.1.0 --no-ri --no-rdoc
gem i rack -v 1.5.2 --no-ri --no-rdoc

これはなかなかにキツいな。で、再開。

$ gem i actionpack --pre --no-ri --no-rdoc

activerecord が activemodel (=4.0.0) および arel (~>4.0.0) activerecord-deprecated_finders (~>0.0.3) を要求したので導入。

$ gem i activemodel --pre --no-ri --no-rdoc
$ gem i arel --pre --no-ri --no-rdoc
$ gem i activerecord-deprecated_finders -v 0.0.3 --no-ri --no-rdoc

で、再開。

$ gem i activerecord --pre --no-ri --no-rdoc

次は actionmailer が mail (~>2.5.3) を要求。

$ gem i mail -v 2.5.3 --no-ri --no-rdoc

再開。

$ gem i actionmailer --pre --no-ri --no-rdoc
$ gem i railties --pre --no-ri --no-rdoc

これはなかなかにキツいな。で、sprockets-rails が sprockets (~>2.8) を要求。

$ gem i sprockets -v 2.8 --no-ri --no-rdoc

再開。

$ gem i sprockets-rails --pre 2.0.0 --no-ri --no-rdoc

そして最後に

$ gem i rails --pre --no-ri --no-rdoc

を、終わった。

$ rails -v
Rails 4.0.0.beta1

やれやれ。

アプリケーションの作成

写経着手。まずプロジェクトの作成。

$ rails new tetsuzine

で cd して ls したら以下なカンジ。

$ ls
app  
bin  
config  
config.ru  
db  
Gemfile  
Gemfile.lock  
lib  
log  
public  
Rakefile  
README.rdoc  
test  
tmp  
vendor

spring なるソレを入れとけ、とあるので導入。

$ gem i spring --no-ri --no-rdoc

書店なアプリらしく、とりあえず scaffold で本を表現するリソースを、とのこと。

$ spring generate scaffold book title price:integer

あら、Could not find a JavaScript runtime と言われたぞ。Gemfile に以下を追加して

gem 'execjs'
gem 'therubyracer'

bundle install してリトライ。て駄目じゃん。この症状で苦しんだ方が沢山いらっしゃるようですが、nodejs 入れれ、とかあるな。あと Gemfile を以下に修正。

gem 'therubyracer', :platforms => :ruby

で、bundle install してどうか。つうかなかなかにアレですね。beta だから仕方が無いのでしょうが、これは色々な意味で地雷の域を超えてますね。あ、正常終了。

$ spring generate scaffold book title price:integer
      invoke  active_record
      create    db/migrate/20130310063816_create_books.rb
      create    app/models/book.rb
      invoke    test_unit
      create      test/models/book_test.rb
      create      test/fixtures/books.yml
      invoke  resource_route
       route    resources :books
      invoke  jbuilder_scaffold_controller
      create    app/controllers/books_controller.rb
      invoke    erb
      create      app/views/books
      create      app/views/books/index.html.erb
      create      app/views/books/edit.html.erb
      create      app/views/books/show.html.erb
      create      app/views/books/new.html.erb
      create      app/views/books/_form.html.erb
      invoke    test_unit
      create      test/controllers/books_controller_test.rb
      invoke    helper
      create      app/helpers/books_helper.rb
      invoke      test_unit
      create        test/helpers/books_helper_test.rb
      invoke    jbuilder
       exist      app/views/books
      create      app/views/books/index.json.jbuilder
      create      app/views/books/show.json.jbuilder
      invoke  assets
      invoke    coffee
      create      app/assets/javascripts/books.js.coffee
      invoke    scss
      create      app/assets/stylesheets/books.css.scss
      invoke  scss
      create    app/assets/stylesheets/scaffolds.css.scss

あんま時間無いのでどんどん進めます。中身確認は別途で。
まず DB 作成。

$ spring rake db:migrate
==  CreateBooks: migrating ====================================================
-- create_table(:books)
   -> 0.0026s
==  CreateBooks: migrated (0.0028s) ===========================================

で、試験実行。rake のデフォなソレが試験になってるのか。

$ spring rake
Run options: --seed 54073

# Running tests:



Finished tests in 0.005872s, 0.0000 tests/s, 0.0000 assertions/s.

0 tests, 0 assertions, 0 failures, 0 errors, 0 skips
Run options: --seed 63666

# Running tests:

.......

Finished tests in 2.177550s, 3.2146 tests/s, 5.9700 assertions/s.

7 tests, 13 assertions, 0 failures, 0 errors, 0 skips

で、動作確認。

$ rails s
=> Booting WEBrick
=> Rails 4.0.0.beta1 application starting in development on http://0.0.0.0:3000
=> Call with -d to detach
=> Ctrl-C to shutdown server
[2013-03-10 15:43:04] INFO  WEBrick 1.3.1
[2013-03-10 15:43:04] INFO  ruby 2.0.0 (2013-02-24) [x86_64-linux]
[2013-03-10 15:43:04] INFO  WEBrick::HTTPServer#start: pid=22708 port=3000

む、config/routes.rb を云々とありますね。以下を追加。

  root 'books#index'

すげー。アプリケーションの再起動も不要なのか。しかも public/index.html の削除も不要とか書いてありますね。

カラム追加

以下、とのこと。

$ spring generate migration add_published_on_to_books published_on:date
      invoke  active_record
      create    db/migrate/20130310064951_add_published_on_to_books.rb

内容は以下。

class AddPublishedOnToBooks < ActiveRecord::Migration
  def change
    add_column :books, :published_on, :date
  end
end

むむ。実行して各種パーツを修正とのこと。

$ spring rake db:migrate
==  AddPublishedOnToBooks: migrating ==========================================
-- add_column(:books, :published_on, :date)
   -> 0.0006s
==  AddPublishedOnToBooks: migrated (0.0007s) =================================

なんとなく実行も早い気がする。以下を修正なのか

  • app/views/books
    • index.html.erb
    • show.html.erb
    • _form.html.erb

StrongParameters の働きを確認

このままではフォームからの入力はできるけど、登録はされない、とのこと。確かに反映されないですね。app/controllers/books_controller.rb のいっちゃん下に以下な記述があります。

    # Never trust parameters from the scary internet, only allow the white list through.
    def book_params
      params.require(:book).permit(:title, :price)
    end

ここに published_on なフィールドを追加すれば、とのこと。ここで明示的に許可した属性のみがデータベースの引き渡されるとのこと。
動作もそのとおりになってますね。

検索機能の実装

むむ、なんかよく分からんが手順としては

  • 検索モデルを追加
  • app/views/books/index.html.erb に検索フォームを追加
  • コントローラの index アクションに処理を追加
  • books モデルに検索処理を追加

Rails4 では model の all メソド呼び出し時には SQL は発行されない、とか書いてありますね。では盛り込んでみます。
まず model 追加。app/models/search_form.rb として以下を新規作成。

class SearchForm
  include ActiveModel::Model

  attr_accessor :q
end

で、app/views/books/index.html.erb に以下を盛り込み。先頭って書いてますね。

<%= form_for @search_form, url: books_path,
    html: {method: :get} do |f| %>
  <%= f.search_field :q %>
  <%= f.submit 'search' %>
<% end %>

<h1>Listing books</h1>

次に app/controllers/books_controllers.rb の index なアクションを云々。

  # GET /books
  # GET /books.json
  def index
    @search_form = SearchForm.new params[:search_form]
    @books = Book.all
    if @search_form.q.present?
      @books = @books.titled @search_form.q
    end
  end

で、最後に app/models/book.rb に検索処理を記述。

class Book < ActiveRecord::Base
  scope :titled, ->(q) {where 'title like ?', "%#{q}%" }
end

これでサーバ起動して動作確認。落ちてるな。何が悪いのか。てコントローラ上書きしてなかったorz
で動作確認してみたのですが検索できるようになってますね。

検索処理の AJAX

手順としては

  • 各行で book オブジェクトの id を覚えておくように修正 (view)
  • JSON テンプレートの修正
  • AJAX 処理の実装
  • 問題があるらしいので修正

とのこと。
まず、app/views/books/index.html.erb を以下に、とのこと。

  <tbody>
    <% @books.each do |book| %>
    <%= content_tag_for tr:, @books do |book| %>
      <td><%= book.title %></td>

次に app/views/books/index.json.jbuilder を以下に。

json.array!(@books) do |book|
  json.extract! book, :id
end

で、app/views/books/index.html.erb を以下に。

<%= form_for @search_form, url: books_path(format: :json),
    remote: true, html: {method: :get} do |f| %>
  <%= f.search_field :q %>
  <%= f.submit 'search' %>
<% end %>

で、app/assets/javascripts/books.js.coffee を以下に。

$ ->
  $('#new_search_form').on 'ajax:success',  (e, books) ->
  $('tr.book').hide()
  ids = (books.map (b) -> "#book#{b.id}").join(',')
  $(ids).show()

このへんさっぱりワケワカですな。サーバ再起動して確認。あ、表示されなくなっちゃいましたねorz
あ、typo 発見。books.js.coffee が以下でした (一部のみ)。

  ids = (books.map (b) -> "#book_#{b.id}").join(',')

これで表示された。でもまだ何かがおかしい。さっきは S で両方表示されてたけど今は駄目だし。

とりあえず

写経終了。問題解決できれば追記するかも。それにしても JS が出てくるとさっぱりなのは何とかしないとマズい。