Rails 5.0.0.beta1の新機能紹介の公式ブログ記事を読んでみた


Rails公式ブログのRails 5 beta1の新機能についてのブログ記事『Riding Rails: Rails 5.0.0.beta1: Action Cable, API mode, Rails command』を英語の勉強がてら読んでみました!


🐡 Action Cable

- Action Cableは、Websocketをハンドリングするためのフレームワーク
- チャットや、Notificationを簡単に実装することができる

チャットアプリケーションのサンプルソース

# アプリの作成
$ rails _5.0.0.beta1.1_ new action_cable_sample --skip-spring --skip-bundle
$ cd action_cable_sample

# bundle install の実行
bundle install --jobs=4 --path=vendor/bundle

# Message の scaffold を実行
$ rails g scaffold messages content:text

# マイグレーションを実行
$ rails db:migrate

# Chat チャネル with Speakアクション を作成
$ rails g channel chat speak

# 非同期でブロードキャストするための Jobを作成
$ rails g job message_broadcast

ルーティングを修正。

# config/routes.rb
Rails.application.routes.draw do
# ↓ 以下を追加
root to: 'messages#index'

# Serve websocket cable requests in-process
mount ActionCable.server => '/cable'
end

JS側で、Action Cableを有効にします。

# app/assets/javascripts/cable.coffee
@App ||= {}
App.cable = ActionCable.createConsumer()

クライアントサイド(CoffeeScript)のSpeakアクションを修正。

# app/assets/javascripts/channels/chat.coffee
received: (data) ->
# メッセージを受け取ったら #messages に付け足す
$('#messages').append data['message']

speak: (message) ->
# formでsubmitしたメッセージをサーバに送信
@perform 'speak', message: message

# リターンキーを押したら、メッセージが送信される
$(document).on 'keypress', '[data-behavior~=chat_speaker]', (event) ->
if event.keyCode is 13
App.chat.speak event.target.value
event.target.value = ''
event.preventDefault()

サーバサイドのAction Cableのクラス CatChannel を修正。

# ActionCableのクラス
class ChatChannel < ApplicationCable::Channel
# 配信する際の名前。 chat.coffee内の「ChatChannel」と対応(?)
def subscribed
# ↓ 以下をコメントアウトする
stream_from 'chat_channel'
end

# クライアントから送られた message を テーブルに保存
def speak(data)
Message.create! content: data['message']
end
end

Messageをテーブルに保存したら、非同期でJobを起動。

# Messageを管理するクラス
class Message < ApplicationRecord
# 作成後のコミットが完了したら ブロードキャストする
after_create_commit { MessageBroadcastJob.perform_later self }
end

保存されたメッセージを、MessageBroadcastJobで非同期でブロードキャスト。

# 非同期でクライアントにメッセージを送るためのJob
class MessageBroadcastJob < ApplicationJob
queue_as :default

# 部分テンプレートをrenderする
def perform(message)
ActionCable.server.broadcast 'chat_channel', message: _render_message(message)
end

private

# 部分テンプレートをrenderする
def _render_message(message)
ApplicationController.renderer.render(partial: 'messages/message', locals: { message: message })
end
end

チャット風にメッセージを表示。


<h1>Chath1>

<div id="messages">
<%= render @messages %>
div>

<form>
<label>Send Message:label><br>
<input type="text" data-behavior="chat_speaker">
form>

あと部分テンプレート追加。キャッシュも普通に使うことができます!


<% cache message do %>
<div class="message">
<p><%= message.content %>p>
div>
<% end %>

あとはいつ戻おり rails s で起動するとpumaが立ち上がります。

感想

以下、使ってみて感じたことです。

  • サーバ/クライアントサイド両方の処理を意識する必要がある
  • Viewのキャッシュなど、Railsの今までの知識を有効活用できる
  • 一時的に接続が失敗したり、エラー時のリカバリ処理等、実際に作る場合はいろいろ考慮が必要そう
  • Basecamp 3では本番で使われているけど、ネットの知見がたまるまで少し様子見が吉かも

関連リンク

🐯 API Mode

BackendとしてAPIを作るのに特化したrailsプロジェクトを rails new backend --api 等で簡単に作成できる。

このAPI Modeとは直接の関係はないですが、『jsonapi-resources』というプロジェクトがあるようです。
こちらもサンプルソースを見た限りはなかなかおもしろそう。うまく使いこなせれば、楽ができそうです!

も少し詳しく調べてみました => Rails 5.0.0.beta2 APIモードについて調べてみた

🐰 Active Record Attributes

DBに保存している値の型と、Active Recordで取り出した時の型を分けることができる。

# 小数点付きの型でDBに保存
# db/schema.rb
create_table :store_listings, force: true do |t|
t.decimal :price_in_cents
end

#### Before ####
# app/models/store_listing.rb
class StoreListing < ActiveRecord::Base
end

# 小数点付きで値を返す
store_listing = StoreListing.new(price_in_cents: '10.1')
store_listing.price_in_cents # => BigDecimal.new(10.1)

#### After ####
class StoreListing < ActiveRecord::Base
attribute :price_in_cents, :integer # 整数に設定した場合
end

# 整数で値を返す
store_listing = StoreListing.new(price_in_cents: '10.1')
store_listing.price_in_cents # => 10

配列や、レンジの状態で値を受け取ることもできます。

# 配列やレンジを設定する事もできる
class MyModel < ActiveRecord::Base
attribute :my_string, :string
attribute :my_int_array, :integer, array: true
attribute :my_float_range, :float, range: true
end

model = MyModel.new(
my_string: "string",
my_int_array: ["1", "2", "3"],
my_float_range: "[1,3.5]",
)
model.attributes
# =>
# {
# my_string: "string",
# my_int_array: [1, 2, 3],
# my_float_range: 1.0..3.5
# }

さらに、独自の型を定義することもできます。これはいいかも!

class MoneyType < ActiveRecord::Type::Integer
def type_cast(value)
if value.include?('$')
price_in_dollars = value.gsub(/\$/, '').to_f
price_in_dollars * 100
else
value.to_i
end
end
end

# クラスに設定
class StoreListing < ActiveRecord::Base
attribute :price_in_cents, MoneyType.new
end

# 変換した結果を返す
store_listing = StoreListing.new(price_in_cents: '$10.00')
store_listing.price_in_cents # => 1000

実用性も高いし、これは地味にかなりありがたい機能。

ちなみに、こんな使い方もできるそうで…夢が広がりんぐ。

class Money < Struct.new(:amount, :currency)
end

class MoneyType < Type::Value
def initialize(currency_converter)
@currency_converter = currency_converter
end

def type_cast_for_database(value)
value_in_bitcoins = currency_converter.convert_to_bitcoins(value)
value_in_bitcoins.amount
end
end

class Product < ActiveRecord::Base
currency_converter = ConversionRatesFromTheInternet.new
attribute :price_in_bitcoins, MoneyType.new(currency_converter)
end

Product.where(price_in_bitcoins: Money.new(5, "USD"))
# => SELECT * FROM products WHERE price_in_bitcoins = 0.02230

Product.where(price_in_bitcoins: Money.new(5, "GBP"))
# => SELECT * FROM products WHERE price_in_bitcoins = 0.03412

ActiveRecord::Attributes::ClassMethods

🐞 belongs_toが必須に

belongs_to でassociationを定義した際には、対応するデータがあることが必須になりました。
対応するデータがない場合はエラーとなるため、エラーにしないためには次のように optional: true を追加する必要があります。

class User
belongs_to :organization, optional: true
end

😼 renderをどこからでも呼び出せる

Rakeタスクや、バックグラウンドのジョブキューなどでも #render を行うことができるようになりました。

ApplicationController.render _render_options_

assings オプションなどを使うことで、インスタンス変数をテンプレートに渡すこともできます。

🚕 Ruby 2.2.2以上をサポート

本番環境で動いている場合は、Railsのバージョンアップよりも、Rubyのバージョンアップを先やる必要があるかも。

😀 パフォーマンス改善について

Ruby 2.2.2以上や、Rails 5を使うことで、GCの改善や、メモリ消費量の削減といったパフォーマンスの改善の恩恵を受けることができる。
次のコミットはパフォマンスの改善に関するコミットである。

👽 Turbolinks 3

Turbolinksを新しく書きなおしたバージョン。最大の特徴は、テンプレートの部分的な書き換えに対応していること。

- data-turbolinks-permanent => ページ間で保持されることでより高速に動作。サイドバーとかに使えそう
- data-turbolinks-temporary => ページ間で切り替わっていく要素につける

😸 そのほか変更点

- rake db:migrate => rails db:migrate に変更
- テストの failures 情報が見やすくなる
- 抽象クラスの ApplicationRecord をベースにするようになりました
- ActiveRecord::Relation#in_batches を扱いやすくしてメモリの使いすぎを防ぐ

🗽 CHANGELOG

🎃 (補足) Turbolinks 5

ネイティブのiOSとAndroidのラッパ実装として、Turbolinks5を新しく作っているらしいです。まだどんな実装なのかはわかりませんが、新しい試みでおもしろそう。

turbolinks/turbolinks: Turbolinks makes navigating your web application faster

🐠 (補足) Ruby 2.2.2以上を使用

Rails 5からはRuby 2.2.2以上を利用します

🐮 参考リンク

🍣 変更来歴

  • (2016-01-31 22:00) 新規作成
  • (2016-02-08 21:50) APIモードの記述を修正
  • (2016-02-09 08:25) Change Logをbeta2にバージョンアップ
  • (2016-02-12 20:30) belongs_toやrender anywhereについて追記

🖥 VULTRおすすめ

VULTR」はVPSサーバのサービスです。日本にリージョンがあり、最安は512MBで2.5ドル/月($0.004/時間)で借りることができます。4GBメモリでも月20ドルです。 最近はVULTRのヘビーユーザーになので、「ここ」から会員登録してもらえるとサービス開発が捗ります!

📚 おすすめの書籍