Skip to content

Instantly share code, notes, and snippets.

@stewart

stewart/draft.md Secret

Last active December 18, 2015 02:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save stewart/7b3d143010a6309929d9 to your computer and use it in GitHub Desktop.
Save stewart/7b3d143010a6309929d9 to your computer and use it in GitHub Desktop.

What's New and Awesome in Ruby 2?

The transition from Ruby 1.9 to 2 doesn't bring as many changes as 1.8 to 1.9, but there's still a number of performance improvements, new features, and refinements added. Here's some of the cooler stuff:

Faster

Ruby 2 has a few patches that dramatically improve performance. The biggest of these is a substantial optimization to Kernel#require, which speeds up Rails startup dramatically. Ruby 2 also has improved GC, VM optimizations, and improvements to floating point operations.

UTF-8 By Default

All Ruby scripts now default to UTF-8 encoding. This makes the #encoding: utf-8 magic comment no longer necessary.

Keyword Arguments

Previous versions of Ruby could approximate keyword arguments by using a hash as a method argument:

def test options = {}
  options = {bar: 'bar'}.merge options
  [options[:foo], options[:bar]]
end

test foo: 'foo'
#=> ["foo", "bar"]

This works, but it's messier than it could be, it's hard to see with a glance at the method signature what arguments this method accepts, and it's difficult to set default values for arguments.

Ruby 2's new keyword arguments feature is still backed by a hash, but it's much easier to use now. Here's the same method rewritten to make use of it:

def test foo: nil, bar: 'bar'
  [foo, bar]
end

test foo: 'foo'
#=> ["foo", "bar"]

Note that though these act like keyword arguments, they're still just a hash underneath, so they're missing some functionality and flexibility that Python's keyword arguments have. For example:

def test a, b
  [a, b]
end

test a: 'foo', b: 'bar'
#=> ArgumentError: wrong number of arguments (1 for 2)

Lazy Enumerators

Ruby 2 introduces the concept of a lazy Enumerable object. A lazy Enumerable will not consume excess memory, and will process individual elements as needed instead of the entire array/range passed to it.

As an example, this code in Ruby 1.9 would never complete:

(1..Float::INFINITY).collect { |x| x * x }.first(10)

While with Ruby 2's lazy evaluation:

(1..Float::INFINITY).lazy.collect { |x| x * x }.first(10)
#=> [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

Lazy enumeration has drawbacks, though. Ruby 2's lazy enumerators are much slower than normal ones, up to 4 times slower. This bug report goes into more detail about the issue.

Kernel#__dir__

Ruby 2 introduces the __dir__ method. It returns the same result as File.dirname(File.realpath(__FILE__)). It's useful when loading configuration files or modifying load paths.

Literal Symbol Array Syntax

You can now use %i and %I to make an array of symbols:

%i( alpha bravo )
#=> [:alpha, :bravo]

Debugging

Ruby 2 brings support for DTrace probes, as well as a new Ruby class named TracePoint that allows for inspecting an app's inner workings.

What's new and awesome in Rails 4?

Rails 4 isn't quite out yet, still at release candidate 1 as of this writing, but you can still preview the cool new features with gem install rails --pre. Here's a sampling of some of the new stuff you'll find coming to Rails 4:

Faster

Mainly because of optimizations in Ruby 2, Rails 4 is much faster than Rails 3, particularly with startup time.

Postgres Hstore

Rails 4 brings support for PostgreSQL's Hstore. Hstore is a schema-less key-value store inside of Postgres that allows for storing hash-like datasets directly inside of a column.

If you have a version of Postgres that supports Hstore, you can enable it by running this in a Postgres shell:

CREATE EXTENSION hstore;

Alternatively, you can put this in a Rails migration:

class EnableHstore < ActiveRecord::Migration
  def up
    execute 'CREATE EXTENSION hstore'
  end

  def down
    execute 'DROP EXTENSION hstore'
  end
end

Once Hstore is enabled, you'll have to create a column with a type of 'hstore'.

class AddDataToUsers < ActiveRecord::Migration
  def change
    add_column :users, :data, :hstore
  end
end

Now you can store any type of data you want in the data column:

User.create(name: "TestUser", data: {age: 26})
User.last.data['age'] #=> 26
User.where("data @> (:k => :v)", k: 'age', v: '26').name #=> "TestUser"

Rails 4 also includes the convenience method store_accessor, which makes it even easier to access and update data inside the Hstore:

class Users < ActiveRecord::Base
  store_accessor :data, :age
end

User.create(name: "TestUser", age: 26)
User.last.age #=> 26

The main caveat to using Hstore is difficulty in migrating to a new database system in the future; but realistically, how often do you switch your backing database system?

HTTP PATCH

PATCH is the new primary HTTP method for updating resources in Rails 4. This more correctly adheres to the principles of REST and Hypermedia APIs. For now, PUT requests still route to update actions, but this may change in a future release.

Streaming

A new module introduced in Rails 4, ActionController::Live allows for streaming data to clients. All you need to do is include the module in a controller, and your app will now be enabled to stream arbitrary data.

You will, however, need to use a threaded webserver, like Puma or Rainbows!, in order to stream data. Actions from streaming controllers run in a seperate thread.

Here's an example streaming controller from the Rails 4 documentation:

class MyController < ActionController::Base
  include ActionController::Live

  def index
    100.times {
      response.stream.write "hello world\n"
    }
    response.stream.close
  end
end

This also allows for cool technologies like Server-sent events to be used.

Engine Yard doesn’t currently support live streaming out of the box. The default server options on Engine Yard are Unicorn and Passenger, and neither of these support live streaming yet.

No More Dynamic Finders

Previous versions of Rails included dynamic finders for models, so we could use methods like User.find_by_email and User.find_or_create_by_email_and_password. These relied on method_missing and had messy implementations.

Rails 4 introduces new find_by, find_or_create_by, and find_or_initialize_by methods that all take hashes for arguments. For example:

# Rails 3
User.find_by_name(user_name)
User.find_or_create_by_email_and_password(email, password)

# Rails 4
User.find_by(name: user_name)
User.find_or_create_by(email: email, password: password)

These will be a big pain point while upgrading to Rails 4, but you can continue to use them with the activerecord-deprecated_finders gem. This is a dependency of Rails 4 to ease the transition, but will be deprecated in Rails 4.1, and supported until Rails 5.

Strong Parameters

attr_accessible is the system for mass-assignment protection in Rails 3. It worked, but it's confusing and not very flexible. Some discussion happened around how to improve Rails' handling of mass assignment, and from this discussion the strong_parameters gem was born.

As an example, let's take this controller example:

class UsersController < ApplicationController
  def create
    @user = User.create(params[:user])
    # validation, redirection, etc
  end
end

In Rails 3, you would protect against unexpected input with attr_accessible declarations in the User model:

class User < ActiveRecord::Base
  attr_accessible :email, :password, :password_confirmation
end

With Rails 4 and Strong Parameters, this validation moves from the model to the controller layer:

class UsersController < ApplicationController
  def create
    @user = User.create(user_params)
    # validation, redirection, etc
  end

  private
  def user_params
    params.require(:user).permit(:email, :password, :password_confirmation)
  end
end

If you don’t use the correct params, an ActiveModel::ForbiddenAttributes exception is raised.

There's a few outstanding issues with strong parameters, notably with nested attributes. These will likely be resolved sometime soon, but you may want to watch for edge cases with complex models when updating.

Put Them Together

So what happens when you combine Ruby 2 and Rails 4?

Your app will be:

  • Faster
  • More secure
  • Easier to debug

Hopefully this article's gotten you excited about some of the awesome things you can expect with the new Ruby and Rails. The upgrade process is definitely not as punishing as going from Rails 2 to Rails 3. And as DHH says, the water's fine.

Outline

  • What's new and awesome in Ruby 2?

    • Faster
      • GC patches
    • Keyword Arguments
    • Literal array-of-symbols syntax
    • utf8 default
    • lazy enumerators
    • __dir__
    • DTrace
      • TracePoint
  • What's new and awesome in Rails 4?

    • Faster (mainly because of Ruby 2)
    • Postgres HStore
    • HTTP Patch
    • Threadsafe by default
      • STREAMING !!!
    • No more find_by_* method_missing nonsense
    • Better error pages
    • Security
      • attr_accessible kind of sucked
      • strong parameters is much better
  • What happens when you put them together?

    • Faster Rails boot
    • Faster tests
    • Easier debugging
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment