What's New in Rails4 ActiveModel?

Well, previouse article I had talked about What’s New in Rails4 ActiveRecord Finder?. Today please keep going to take a look “What’s New in Rails4 ActiveModel?”:

SCOPES

eager-evaluated scopes are deprecated
- Rails3:

Rails3
1
2
scope :sold, where(state: 'sold')
default_scope where(state: 'available')

Warning:
- Useing #scope without passing a callable object is deprecated.
- Calling #default_scope without a block is deprecated.

- Rails4:
Scopes should take a proc object:

Rails4
1
scope :sold, ->{ where(state: 'sold') }

Defaults scopes should take proc object or a block:

Rails4
1
2
default_scope { where(state: 'available') }
default_schop ->{ where(state: 'available') }

RELATION#NONE

- Rails3:

Rails3
1
2
3
4
5
6
7
8
9
10
11
12
class User < ActiveRecord::Base
  def visible_posts
    case role
    when 'Country Manager'
      Post.where(country: country)
    when 'Reviewer'
      Post.published
    when 'Bad User'
      [] #represents empty collection
    end
  end
end
1
2
  @posts = current_user.visible_posts
  @posts.recent

@posts.recent error when ‘Bad User’ because NoMethodError: undefined method ‘recent’ for []:Array.
One way we can fix this:

1
2
3
4
5
6
@posts = current_user.visible_posts
if @posts.any? # must check for presence
  @posts.recent
else
  [] # must return empty collection to caller
end

Rails4:

Rails4
1
2
3
4
5
6
7
8
9
10
11
12
class User < ActiveRecord::Base
  def visible_posts
    case role
    when 'Country Manager'
      Post.where(country: country)
    when 'Reviewer'
      Post.published
    when 'Bad User'
      Post.none # returns ActiveRecord:Relation and never hits the database
    end
  end
end
1
2
@posts = current_user.visible_posts
@posts.recent # no need to check for presence

Post.none returns ActiveRecord:Relation and never hits the database and @posts.recent no need to check for presence.

RELATION#NOT

Rails3:

Rails3
1
Post.where('author != ?', author)

When author is nil it’s going to generate incorrect SQL syntax: SELECT "posts".* FROM "posts" WHERE (author != NULL)
One way we can fix this:

Rails3
1
2
3
4
5
if author
  Post.where('author != ?', author)
else
  Post.where('author IS NOT NULL')
end

Rails4:

Rails4
1
Post.where.not(author: author)

When author is nill it’s goint to generate correct SQL syntax: SELECT "posts".* FROM "posts" WHERE (author IS NOT NULL)

RELATION#ORDER

case1

Rails3:

Rails3
1
2
3
4
5
class User < ActiveRecord:Base
  default_scope { order(:name) }
end

User.order("created_at DESC")

It’s going to generate SQL: SELECT * FROM users ORDER BY name asc, created_at desc, new calls to order are appended.

Rails4:

Rails4
1
2
3
4
5
class User < ActiveRecord:Base
  default_scope ->{ order(:name) }
end

User.order(created_at: :desc)

It’s going to generate SQL: SELECT * FROM users ORDER BY created_at desc, name asc New calls to order are prepend.

case2

Rails3:

Rails3
1
User.order(:name, 'created_at DESC')

Rails4:

Rails4
1
User.order(:name, created_at: :desc)

It’s going to generate SQL: SELECT * FROM users ORDER BY name asc, created_at desc

Rails3:

Rails3
1
User.order('created_at DESC')

Rails4:

Rails4
1
User.order(created_at: :desc)

It’s going to generate SQL: SELECT * FROM users ORDER BY created_at desc

So far so good, ActiveModel in Rails4 is better. :)