What Exactly Is Arel in Rails 3.0

What exactly is Arel in Rails 3.0?


What exactly is Arel in Rails 3.0?

It's an object model for an algebra of relational query operators.

I understand that it is a replacement for ActiveRecord

No, it isn't. It's a replacement for hand-crafting SQL queries in strings. It is a common query layer that underlies ActiveRecord, but it can also be used as an underpinning for DataMapper, for example.

If it is a replacement for anything, it's a replacement for Ambition. Or, you can think of it as a Ruby version of the LINQ standard query operators or Python's SQLAlchemy. (In fact, the author explicitly cites both LINQ and SQLAlchemy as inspirations.)

Or, you can see it as a replacement for named_scopes. In fact, ARel is pretty much the realization of the idea that "every query is a named_scope". And, whaddayaknow: both were written by the same guy.

and that it uses objects instead of queries.

No, it uses objects as queries.

why is this better?

Ruby is an object-oriented language, not a string-oriented language. For that reason alone, it makes sense to represent queries as objects instead of strings. Building a proper object model for queries instead of using strings for everything gives you pretty much the same benefits that building a proper object model for an accounting system instead of using strings for everything gives you.

Another big advantage is that ARel implements an actual algebra of query operators. In other words, ARel knows about the mathematical rules for constructing and composing queries. If you concatenate two strings, each of which contains a valid SQL query, the result is probably not going to be a valid SQL query. Or, even worse, it is a valid SQL query, but one that doesn't make sense, or that does something totally different from what you think it does. This can never happen with ARel. (This is what the article I link to below means with "closed under composition".)

will objects/queries be "easier" to create?

Yes. For example, as I mentioned above, it is much easier to construct more complex queries from simpler parts.

will it lead to more efficient SQL queries?

Yes. The fact that ARel has a proper object model for the queries means that it can perform optimizations on those queries long before it ever generates an actual SQL query.

will it be compatible with all major DBs? - I assume it will.

Yes. In fact, I always talked about SQL above, but actually a relational query algebra can generate queries for pretty much everything. Again, see LINQ or Ambition as examples: both can query SQL, LDAP, ActiveResource, CouchDB, Amazon, Google, … all with the same syntax.

Perhaps the best discussion as to what ARel is and why Nick Kallen wrote is the aptly named article Why Arel? by Nick Kallen himself. Note: the article contains some mild mathematical and computer science jargon, but that is exactly the point: ARel has some strong foundations in mathematics and computer science, those foundations are what give it its powerful properties.

Rails 3 or arel chain/merge SQL conditions conditionally

Easy:

# oh, the joy of lazy evaluation...
result = Record.where(true) # I'd like to do Record.all, but that fetches the records eagerly!
result = result.where("...") if params[:x].present?
result = result.where("...") if user.role?(:admin)
result = result.where("...") if params[:y].present?

By the way, don't try this in irb: the "print" part of "read-eval-print" will force evaluation of the record set.

EDIT: I just figured out that instead of Record.where(true), you could use Record.scoped. This won't work in Rails 4, though.

Rails3 Arel visits to custom classes

I think it's not possible now. Let's try to figure out why.

When you try to pass your custom object to the where method User.where(:name => custom_item) Arel try to call visit_YOUR_CLASS_NAME method (https://github.com/rails/arel/blob/master/lib/arel/visitors/visitor.rb#L15) but it fails. Then Arel try to call visit_+object.class.ancestors.find {|klass| respond_to?(DISPATCH[klass], true)} but it fails again because:

irb(main):001:0> class Foo
irb(main):002:1> end
=> nil
irb(main):003:0> foo = Foo.new
=> #<Foo:0x261edc0>
irb(main):004:0> foo.class.ancestors.map {|klass| p klass}
Foo
Object
Kernel
BasicObject
=> [Foo, Object, Kernel, BasicObject]

and Arel doesn't have any of this names in this list https://github.com/rails/arel/blob/master/lib/arel/visitors/to_sql.rb#L394:

alias :visit_ActiveSupport_Multibyte_Chars :quoted
alias :visit_ActiveSupport_StringInquirer :quoted
alias :visit_BigDecimal :quoted
alias :visit_Class :quoted
alias :visit_Date :quoted
alias :visit_DateTime :quoted
alias :visit_FalseClass :quoted
alias :visit_Float :quoted
alias :visit_Hash :quoted
alias :visit_NilClass :quoted
alias :visit_String :quoted
alias :visit_Symbol :quoted
alias :visit_Time :quoted
alias :visit_TrueClass :quoted

Actually, I don't think it's a very useful feature but I've asked Aaron Patterson about this and if he'll like it I'll try to implement it.

Errors with Arel query

You need to tell Arel what you want to select:

users = ::MyModule::User.arel_table
query = users.project(users[Arel.star]).where(users[:name].eq('amy'))

Although it would be a lot easier to just use the ActiveRecord query interface which will parameterize it:

query = ::MyModule::User.where(name: 'amy').arel
# SELECT "users".* FROM "users" WHERE "users"."name" = ?

ActiveRecord Arel OR condition

I'm a little late to the party, but here's the best suggestion I could come up with:

admins = User.where(:kind => :admin)
authors = User.where(:kind => :author)

admins = admins.where_values.reduce(:and)
authors = authors.where_values.reduce(:and)

User.where(admins.or(authors)).to_sql
# => "SELECT \"users\".* FROM \"users\" WHERE ((\"users\".\"kind\" = 'admin' OR \"users\".\"kind\" = 'author'))"

Where can I find good AREL documentation?

I think that this will be useful :

https://github.com/brynary/arel

And an interesting asciicast :

http://asciicasts.com/episodes/215-advanced-queries-in-rails-3



Related Topics



Leave a reply



Submit