Rails 5

What's new?

MaurĂ­cio Linhares / @mauriciojr / Technical Lead at Neat.com

It's still in beta

                
                    gem install rails --pre
                
            

Requires Ruby 2.2.2

Yes, you will have to upgrade, so upgrade right into 2.3

Why care about Ruby 2.3?

Safe navigation with &.

                
2.3.0 :001 > post = nil
=> nil
2.3.0 :002 > post&.author&.email
=> nil
2.3.0 :003 > post.author.email
NoMethodError: undefined method `author' for nil:NilClass
                
            

Frozen strings!

Immutable strings are now part of the standard library, calling "some string".freeze now allocates much less memory and you can force source files to only produce frozen strings.

Ok, back to Rails

Upgrading?

Make sure your test suite is trustworthy and covers what your app is doing. Otherwise, well, good luck with that!

Run the upgrade script

                
rake rails:update
                
            

Callback chains for ActiveRecord or ActiveModel do not stop on a false value

                
throw(:abort)
                
            

Now you have to run your test suite and fix issues and deprecations one by one

And include gems, as a lot of funcionality has been moved out, like respond_with.

Using rspec?

You have to run on master, there is no released version for Rails 5

Out with rake, in with rails

Now commands like rake db:migrate can be called with rails db:migrate

ActiveRecord now has an or method

                
range = (1.hour.ago..Time.now)
Post.where(created_at: range).or(Post.where(featured: true))
                
            

ApplicationRecord now instead of ActiveRecord::Base

                
class ApplicationRecord < ActiveRecord::Base
    self.abstract_class = true
end

class Post < ApplicationRecord
    validates_presence_of :title, :contents
end
                
            

New attributes API for custom attributes

Simplifies handling custom input from forms and operations inside the app.

First, cast_value

                
class TagsType < ActiveModel::Type::Value

  def cast_value(value)
    case value
      when String
        value.split(',').map(&:strip)
      when Array
        value
      else
        nil
    end
  end

end
                
            

Then serialize

                
class TagsType < ActiveModel::Type::Value

  def serialize(value)
    case value
      when Array
        value.join(',')
      else
        nil
    end
  end

end
                
            

config/initializers/types.rb

                
                    ActiveRecord::Type.register(:tag_list, TagsType)
                
            

And use it!

                
class Post < ApplicationRecord
  attribute :tags, :tag_list
end
                
            

Here's how it looks like

                
post = Post.create!(
  title: 'sample post',
  contents: 'this is not a joke',
  tags: 'this, is, some, tag')

expect(post.tags).to eq(['this', 'is', 'some', 'tag'])
                
            

rails-api is now part of rails

                
                    rails new your_project_name --api
                
            

Why?

  • No template rendering engine
  • No session management
  • Perfect as a backend for a single page app

Turbolinks!

Turbolinks 3 is out, 5 is in

Support for mobile browsers, permanent chunks of content and more. You'll need both Rails master and Turbolinks v5 branch to give this a try. Very few docs or directions so far.

ActionCable

Biggest new addition, websockets as part of Rails!

Websockets!

Stateful and persistent connections to a web server.

Realtime communication

  • Chat applications
  • Movement and location based apps
  • Status notifications for background operations

Just every HTTP response

                
< HTTP/1.1 200 OK
< Date: Mon, 15 Feb 2016 02:31:17 GMT
< Expires: -1
< Cache-Control: private, max-age=0
< Content-Type: text/html; charset=ISO-8859-1
< P3P: CP="This is not a P3P policy! See https://www.google.com/support/accounts/answer/151657?hl=en for more info."
< Server: gws
< X-XSS-Protection: 1; mode=block
< X-Frame-Options: SAMEORIGIN
< Set-Cookie: NID=76=jA4wUTDrPnFpUZaLyn5uaHdZcsg0fOCiSR2QA89cKM_a-kETlZ-fov32w-N6mkIQhme5v2FCZ-0ht54HmVIYUmie10HsKWmANBl41cj6NScFwMuv3Nc3lWI9pnZOsqXO-ELTNLxWBPQfnQ; expires=Tue, 16-Aug-2016 02:31:17 GMT; path=/; domain=.google.com; HttpOnly
< Accept-Ranges: none
< Vary: Accept-Encoding
< Transfer-Encoding: chunked
                
            

No overhead?

HTTP headers are now gone. But there is no protocol, client and server must figure out a way to communicate correctly and understand each other. ActionCable defines and abstracts this away from your.

When you create your app

  • app/channels/application_channel/connection.rb - where auth lives
  • app/channels/application_channel/channel.rb - superclass for all channels

Channels

The link between client and server, clients and servers can send messages to a channel at any moment.

Demo time!

Let's start with the model

                
class Post < ApplicationRecord

  after_save :send_create_notification
  after_destroy :send_destroy_notification

  def send_create_notification
    ActionCable.server.broadcast "posts",
      post: self, action: "created"
  end

  def send_destroy_notification
    ActionCable.server.broadcast "posts",
      post: self, action: "destroyed"
  end

end
                
            

Now the channel

                
class PostsChannel < ApplicationCable::Channel

  def follow
    stream_from "posts"
  end

  def unfollow
    stop_all_streams
  end

end
                
            

And the client side code

                
App.posts = App.cable.subscriptions.create 'PostsChannel',

  connected: ->
    @perform 'follow'

  received: (data) ->
    switch data.action
      when "created" then @postCreated(data.post)
      when "destroyed" then @postDestroyed(data.post)
      else console.log("no idea what to do with #{data.action}")

            
                
App.posts = App.cable.subscriptions.create 'PostsChannel',

  postCreated: (post) ->
    content = """
      

#{post.title}

#{post.contents}

""" jQuery("#posts").append(content); postDestroyed: (post) -> jQuery("#post_#{post.id}").remove()

Magic!

Almost

Based on EventMachine and Redis. You don't want to debug EventMachine code. Trust me.

Still, first class support is awesome

If you need realtime communication and websockets fit your bill, ActionCable looks like a nice contender if you're already running on Rails.

New! Shiny!

It's still the very first version, it will change, we'll have to learn how well it scales and there will be a lot to learn on how to really make use of it.

Questions?