読者です 読者をやめる 読者になる 読者になる

techium

このブログは何かに追われないと頑張れない人たちが週一更新をノルマに技術情報を発信するブログです。もし何か調査して欲しい内容がありましたら、@kobashinG or @muchiki0226 までいただけますと気が向いたら調査するかもしれません。

Rails Tutorial 4 Rails-flavored Ruby

Rails Tutorial 4 Rails-flavored Ruby

ここまで Rails を諸々触ってきたが、この章では Rails 習得に必須となる Ruby の様々な要素に触れていく。

高度なセットアップ

前章で見落としていた項目があったので追加。

minitestレポーター

minitest の実行結果を RED/GREEN で表示できるようにする。
minitest-reporters gem を使用する。

ENV['RAILS_ENV'] ||= 'test'
require File.expand_path('../../config/environment', __FILE__)
require 'rails/test_help'
require "minitest/reporters"
Minitest::Reporters.use!

class ActiveSupport::TestCase
  # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
  fixtures :all

  # Add more helper methods to be used by all tests here...
end

これで成功時は GREEN, 失敗時は RED で出力されるようになる。

Guardによるテストの自動化

統合テストとビューが更新されたら自動的に適切なテストが実行できるようにする。

$ bundle exec guard init

上記コマンドを実行すると /home/ubuntu/workspace/sample_app/Guardfile が生成される。

下記のように書き換えることで、ファイル書き換えの度に適切なテストのみが実行されるようになる。

# Defines the matching rules for Guard.
guard :minitest, spring: true, all_on_start: false do
  watch(%r{^test/(.*)/?(.*)_test\.rb$})
  watch('test/test_helper.rb') { 'test' }
  watch('config/routes.rb')    { integration_tests }
  watch(%r{^app/models/(.*?)\.rb$}) do |matches|
    "test/models/#{matches[1]}_test.rb"
  end
  watch(%r{^app/controllers/(.*?)_controller\.rb$}) do |matches|
    resource_tests(matches[1])
  end
  watch(%r{^app/views/([^/]*?)/.*\.html\.erb$}) do |matches|
    ["test/controllers/#{matches[1]}_controller_test.rb"] +
    integration_tests(matches[1])
  end
  watch(%r{^app/helpers/(.*?)_helper\.rb$}) do |matches|
    integration_tests(matches[1])
  end
  watch('app/views/layouts/application.html.erb') do
    'test/integration/site_layout_test.rb'
  end
  watch('app/helpers/sessions_helper.rb') do
    integration_tests << 'test/helpers/sessions_helper_test.rb'
  end
  watch('app/controllers/sessions_controller.rb') do
    ['test/controllers/sessions_controller_test.rb',
     'test/integration/users_login_test.rb']
  end
  watch('app/controllers/account_activations_controller.rb') do
    'test/integration/users_signup_test.rb'
  end
  watch(%r{app/views/users/*}) do
    resource_tests('users') +
    ['test/integration/microposts_interface_test.rb']
  end
end

# Returns the integration tests corresponding to the given resource.
def integration_tests(resource = :all)
  if resource == :all
    Dir["test/integration/*"]
  else
    Dir["test/integration/#{resource}_*.rb"]
  end
end

# Returns the controller tests corresponding to the given resource.
def controller_test(resource)
  "test/controllers/#{resource}_controller_test.rb"
end

# Returns all tests for the given resource.
def resource_tests(resource)
  integration_tests(resource) << controller_test(resource)
end

と思いきや、

Note: As of this writing, there is an interaction bug between Guard and Spring that causes all the tests to run every time. I’m keeping an eye on the issue and will update this section when it’s resolved.

とのことで全部のテストが実行されてしまう。

.gitignore に以下を追記する。

# Ignore Spring files.
/spring/*.pid

新しいターミナルで以下を実行しておく。

$ bundle exec guard

これでファイル更新時にテストが実行される。
また、guard> プロンプトが出ている状態でエンターキーを押せば全テストが実行されることが確認できる。

git にコミットしておく。

$ git add -A
$ git commit -m "Complete advanced setup"

Rails風味のRuby

カスタムヘルパーの作成

各ページのタイトルについて、

  • ページタイトルが provide で与えられていたらそれを使用して表示
  • なければデフォルトのタイトルを表示

という挙動を実現する。

app/helpers/application_helper.rb

module ApplicationHelper

  # Returns the full title on a per-page basis.
  def full_title(page_title = '')
    base_title = "Ruby on Rails Tutorial Sample App"
    if page_title.empty?
      base_title
    else
      page_title + " | " + base_title
    end
  end
end

上記を使用するように app/views/layouts/application.html.erb を書き換える。

<!DOCTYPE html>
<html>
  <head>
    <title><%= full_title(yield(:title)) %></title>
    <%= csrf_meta_tags %>
    <%= stylesheet_link_tag    'application', media: 'all',
                                              'data-turbolinks-track': 'reload' %>
    <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
  </head>
  <body>
    <%= yield %>
  </body>
</html>

これで、Homeページにこれまで表示されていた余分な「Home」という単語を表示せず、基本タイトルのみを正しく表示できるようになる。
その修正を加える前にテストを変更する。

test/controllers/static_pages_controller_test.rb

require 'test_helper'

class StaticPagesControllerTest < ActionDispatch::IntegrationTest
  test "should get home" do
    get static_pages_home_url
    assert_response :success
    assert_select "title", "Ruby on Rails Tutorial Sample App" # ここを変更
  end

  test "should get help" do
    get static_pages_help_url
    assert_response :success
    assert_select "title", "Help | Ruby on Rails Tutorial Sample App"
  end

  test "should get about" do
    get static_pages_about_url
    assert_response :success
    assert_select "title", "About | Ruby on Rails Tutorial Sample App"
  end
end

自動テストが走って失敗する。

テストが通るように修正する。
と言ってもapp/views/static_pages/home.html.erb から provide を消すだけ。

これでテストが通って修正完了。

Ruby の学習

ここからはRailsコンソールを使用してRubyを学んでいく。

以下が筆者の好きな設定だそうな。 ~/.irbrc

IRB.conf[:PROMPT_MODE] = :SIMPLE
IRB.conf[:AUTO_INDENT_MODE] = false

ここから特にハマるところはなくチュートリアル通りに進めていくのみ。
この章で学んだこと。

  • Rubyは文字列を扱うためのメソッドを多数持っている
  • Rubyの世界では、すべてがオブジェクトである
  • Rubyではdefというキーワードを使ってメソッドを定義する
  • Rubyではclassというキーワードを使ってクラスを定義する
  • Railsのビューでは静的なHTMLと埋め込みRuby(ERb)が使える
  • Rubyの組み込みクラスには配列、範囲、ハッシュなどがある
  • Rubyのブロックは (他の似た機能と比べ) 柔軟な機能で、添え字を使ったデータ構造よりも自然にイテレーションができる
  • シンボルとはラベルである。追加的な構造を持たない (代入などができない) 文字列みたいなもの。
  • Rubyではオブジェクトを継承できる
  • Rubyでは組み込みクラスですら内部を見たり修正したりできる
  • 「“deified”」という単語は回文である

最後の1行はこちらから。

(Rubyで組み込みクラスにメソッドを追加できるということは実にクールですが、"deified" (=神格化された) という単語が回文になっていることも、それに劣らずクールではないでしょうか。)