Rails 4 and ActiveRecord 4.0

Using open source software is like living in prehistoric ages: you’d better always stay with your mates, otherwise soon you will find yourself alone fighting against the harsh nature trying to survive until one day you will be eaten by a huge carnivore.

That’s what happens with those who are too lazy to keep their web application up to date with Rails. You can find many posts describing the process of migration to Rails 4.0. In this article I’d like to tell you about my favourite changes in ActiveRecord 4.0.

I assume that your Rails version is 3.2 or greater, otherwise you most likely were already eaten.

Rails 4.0 comes with a bunch of new features such as Russian Doll-caching and Turbolinks. But probably the most important change for us was improved support of PostgreSQL databases.

Since first version of Rails it assumed that you use MySQL to store your data. Steve Klabnik wrote an article about two default stacks used by Rails projects today. Whereas MySQL is well-supported by Rails and its default ORM ActiveRecord, most of PostgreSQL features (like hstores, arrays etc.) were undeservedly ignored. Until Rails 4 came in.

HStores

As you may noticed, here at adeven we are big fans of crunching numbers with Postgres. And we love to have them returned as hashes, that could be easily mapped on value objects. If you had ever tried using hstores with Rails, I bet you did it with activerecord-postgres-hstore gem that allows you to use ActiveRecord::Coders::Hstore serializer for handling hstores. Good news, since Rails 4.0 you can get rid of this dependency in your Gemfile. You don’t even need to declare your hstore fields as serializable, Rails is smart enough to detect and properly cast the type of your column.

Moreover, if you have a custom serializer built on top of hstore then you don’t have to manually parse a string with serialized hash anymore. Just assume the parameter for your load method to be a hash and it should be returned as a result of your customdump method.

Consider a serializer class for a field containing an hstore. With ActiveRecord::Coders::Hstore it could look like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
class CustomHstore
  def self.load(value)
    hash = value.from_hstore

    # Do something with hash

    hash
  end

  def self.dump(value)
    ActiveRecord::Coders::Hstore.dump(value)
  end
end

Since Rails 4.0 it turns into

1
2
3
4
5
6
7
8
9
10
11
class CustomHstore
  def self.load(hash)
    # Do something with hash

    hash
  end

  def self.dump(value)
    value
  end
end

Less work for us, right?

Everything is an ActiveRecord::Relation

Lazy loading of database objects was first introduced in ActiveRecord 3.2. That means that the database will be queried only when you really need the data. Until then you’re dealing with ActiveRecord::Relation. This approach plays well with a streaming feature deferring data extraction until view rendering phase. It was still possible to eager load the data by calling all on your scope which implicitly converted your ActiveRecord::Relation into Array. Starting from ActiveRecord 4.0 this ambiguity has been removed. From now all and scoped are deprecated and you should either explicitly call to_a on your scope to get an array of results or call load to eager-load your data.

where.not

That is the change we all were waiting for! Starting from Rails 4 you can make your code more database-agnostic by replacing pure string conditions with chained empty where and not methods.

1
2
3
4
5
6
7
names = %w(John James)

# Rails 3.2
User.where("name NOT IN (?)", names) # Database-dependent

# Rails 4.0
User.where.not(name: names) # We don't need to take care of type of 'names'

Scopes must be turned into lambdas

Consider a model that has a scope with datetime

1
2
3
class Post
  scope :yesterday, where(published_on: Date.yesterday)
end

Parameters that will be passed to where are evaluated at the time when Post is loaded, which means that on the next day Post.yesterday will keep the same records as for today. To avoid this since ActiveRecord 4.0 you have to use lambdas to define scope (as well as default scope) conditions:

1
2
3
class Post
  scope :yesterday, -> { where(published_on: Date.yesterday) }
end

Note that Date.yesterday will be evaluated only when the ActiveRecord will call lambda given as an argument to apply the scope.

Getting rid of “Unknown OID” warning

After I updated our app to Rails 4.0 I got a bunch of warnings saying Unknown OID: .... It happens when Rails tries to find out the OID for the column to properly cast the type. This pull-request gave me a hint that from now you should be more strict and explicitly define what column type should be expected when using SQL 'column' AS alias statement.

1
2
3
4
User.select("'name' AS full_name").first
# unknown OID: full_name(705) (SELECT  'name' AS full_name FROM "users" ORDER BY "users"."id" ASC LIMIT 1)

User.select("'name'::character(255) AS full_name") # The proper way

Conclusion

Almost all changes in ActiveRecord 4.0 that comes with Rails 4 related to advanced usage of Arel and PostgreSQL. If you don’t have too much logic in your database and if you were doing everything right then upgrading from 3.2 should be pretty smooth and surprise-less.

Comments