The document discusses using Arel to construct ActiveRecord queries in a more object-oriented way compared to using literal SQL strings. It provides examples of using Arel to select columns, join tables, and add where conditions in a chained method syntax. Some benefits highlighted are that Arel avoids needing to know SQL syntax, provides Ruby syntax checking, and results in more readable queries.
1 of 160
Downloaded 161 times
More Related Content
Advanced Arel: When ActiveRecord Just Isn't Enough
16. PROBLEMS
.joins(
"JOIN authors ON authors.id = comments.author_id"
)
HAVE TO WRITE ¡°´³°¿±õ±·¡± AND ¡°ON¡±
HAVE TO KNOW MYSQL SYNTAX
17. PROBLEMS
.joins(
"JOIN authors ON authors.id = comments.author_id"
)
HAVE TO WRITE ¡°´³°¿±õ±·¡± AND ¡°ON¡±
HAVE TO KNOW MYSQL SYNTAX
NO SYNTAX CHECKING
21. PROBLEMS
.where(
"authors.name = ? AND posts.active = ?",
"Barack Obama", true
)
HAVE TO KNOW MYSQL SYNTAX
CONFUSING TO MATCH ARGUMENTS WITH
QUESTION MARKS
22. PROBLEMS
.where(
"authors.name = ? AND posts.active = ?",
"Barack Obama", true
)
HAVE TO KNOW MYSQL SYNTAX
CONFUSING TO MATCH ARGUMENTS WITH
QUESTION MARKS
NOT OBJECT-ORIENTED
23. PROBLEMS
.where(
"authors.name = ? AND posts.active = ?",
"Barack Obama", true
)
HAVE TO KNOW MYSQL SYNTAX
CONFUSING TO MATCH ARGUMENTS WITH
QUESTION MARKS
NOT OBJECT-ORIENTED
NO SYNTAX CHECKING
39. WHAT WE¡¯LL COVER
ACTIVERECORD VS AREL
TABLES, COLUMNS
TERMINAL METHODS
SELECT, WHERE, JOIN, JOIN ASSOCIATION, ORDER
AND, OR , GREATER/LESS THAN, NOT EQUALS, ETC
MATCH, IN
40. ACTIVERECORD
DATABASE ABSTRACTION
NO NEED TO SPEAK A DIALECT OF SQL
PERSISTENCE
DATABASE ROWS AS RUBY OBJECTS
DOMAIN LOGIC
MODELS CONTAIN APPLICATION LOGIC, VALIDATIONS, ETC
MODELS DEFINE ASSOCIATIONS
57. TABLES AND COLUMNS
class Post < ActiveRecord::Base
has_many :comments
end
Post.arel_table[:id]
Post.arel_table[:text]
58. TABLES AND COLUMNS
class Post < ActiveRecord::Base
has_many :comments
end
Post.arel_table[:id]
Post.arel_table[:text]
=> #<struct Arel::Attributes::Attribute ... >
59. TABLES AND COLUMNS
class Post < ActiveRecord::Base
include ArelHelpers::ArelTable
has_many :comments
end
Post.arel_table[:id]
Post.arel_table[:text]
=> #<struct Arel::Attributes::Attribute ... >
60. TABLES AND COLUMNS
class Post < ActiveRecord::Base
include ArelHelpers::ArelTable
has_many :comments
end
Post[:id]
Post[:text]
=> #<struct Arel::Attributes::Attribute ... >
62. RELATIONS
POP QUIZ! WHAT DOES THIS STATEMENT RETURN?
Post.select(:title)
A. [¡°Rails is Cool¡± ... ]
63. RELATIONS
POP QUIZ! WHAT DOES THIS STATEMENT RETURN?
Post.select(:title)
A. [¡°Rails is Cool¡± ... ]
B. [#<Post title=¡±Rails is Cool¡±>, ... ]
64. RELATIONS
POP QUIZ! WHAT DOES THIS STATEMENT RETURN?
Post.select(:title)
A. [¡°Rails is Cool¡± ... ]
C. <ActiveRecord::Relation ... >
B. [#<Post title=¡±Rails is Cool¡±>, ... ]
65. RELATIONS
POP QUIZ! WHAT DOES THIS STATEMENT RETURN?
Post.select(:title)
A. [¡°Rails is Cool¡± ... ]
C. <ActiveRecord::Relation ... >
B. [#<Post title=¡±Rails is Cool¡±>, ... ]
75. SELECT
Post.select([:id, :text]).to_sql
=> SELECT id, text FROM `posts`
Post.select(:id).count.to_sql
=> NoMethodError: undefined method `to_sql' for 26:Fixnum
WHAT HAPPENED??
.count IS A TERMINAL METHOD
80. TERMINAL METHODS
Post.where(title: "Arel is Cool").each do |post|
puts post.text
end
Post.where(title: "Arel is Cool").each_slice(3)
BOTH EXECUTE THE QUERY IMMEDIATELY
92. WHERE
Post.where(title: "Arel is Cool").to_sql
=> SELECT `users`.* FROM `users`
WHERE `users`.`title` = 'Arel is Cool'
WITH ACTIVERECORD SUGAR
93. WHERE
Post.where(title: "Arel is Cool").to_sql
=> SELECT `users`.* FROM `users`
WHERE `users`.`title` = 'Arel is Cool'
WITH ACTIVERECORD SUGAR
Post.where(Post[:title].eq("Arel is Cool")).to_sql
=> SELECT `users`.* FROM `users`
WHERE `users`.`title` = 'Arel is Cool'
WITH AREL
103. JOIN
class Post < ActiveRecord::Base
has_many :comments
end
class Comment < ActiveRecord::Base
belongs_to :post
has_one :author
end
class Author < ActiveRecord::Base
belongs_to :comment
end
112. JOIN
=> SELECT `authors`.* FROM `authors`
LEFT OUTER JOIN `comments`
ON `comments`.`id` = `authors`.`comment_id`
LEFT OUTER JOIN `posts`
ON `posts`.`id` = `comments`.`post_id`
WHERE `posts`.`id` = 42
120. JOIN
Author
.joins(
join_association(Author, :comment) do |assoc_name, join_conds|
join_conds.and(Comment[:created_at].lteq(Date.yesterday))
end
)
.joins(join_association(Comment, :post, Arel::OuterJoin))
.where(Post[:id].eq(42))
.to_sql
include ArelHelpers::JoinAssociation
121. JOIN
=> SELECT `authors`.* FROM `authors`
INNER JOIN `comments`
ON `comments`.`id` = `authors`.`comment_id`
AND `comments`.`created_at` <= '2014-04-15'
LEFT OUTER JOIN `posts`
ON `posts`.`id` = `comments`.`post_id`
WHERE `posts`.`id` = 42
122. JOIN TABLES
class Course < ActiveRecord::Base
has_and_belongs_to_many :teachers
end
class Teacher < ActiveRecord::Base
has_and_belongs_to_many :courses
end
123. JOIN TABLES
class Course < ActiveRecord::Base
has_and_belongs_to_many :teachers
end
class Teacher < ActiveRecord::Base
has_and_belongs_to_many :courses
end
courses
teachers
courses_teachers
137. ORDER
Post.order(:visitors).to_sql
=> SELECT `posts`.* FROM `posts` ORDER BY visitors
Post.order(:views).reverse_order.to_sql
=> SELECT `posts`.* FROM `posts` ORDER BY visitors DESC
Post.order(Post[:views].desc).to_sql
=> SELECT `posts`.* FROM `posts` ORDER BY visitors DESC
143. class QueryBuilder
extend Forwardable
attr_reader :query
def_delegators :@query, :to_a, :to_sql, :each
def initialize(query)
@query = query
end
protected
def reflect(query)
self.class.new(query)
end
end
144. class PostQueryBuilder < QueryBuilder
def initialize(query = nil)
super(query || post.unscoped)
end
def with_title_matching(title)
reflect(
query.where(post[:title].matches("%#%"))
)
end
def with_comments_by(usernames)
reflect(
query
.joins(:comments => :author)
.where(Author[:username].in(usernames))
)
end
def since_yesterday
reflect(
query.where(post[:created_at].gteq(Date.yesterday))
)
end
private
def author
Author
end
def post
Post
end
end
145. class PostQueryBuilder < QueryBuilder
def initialize(query = nil)
super(query || post.unscoped)
end
def with_title_matching(title)
reflect(
query.where(post[:title].matches("%#%"))
)
end
def with_comments_by(usernames)
reflect(
query
.joins(:comments => :author)
.where(Author[:username].in(usernames))
)
end
def since_yesterday
reflect(
query.where(post[:created_at].gteq(Date.yesterday))
)
end
private
def author
Author
end
def post
Post
end
end
147. class PostQueryBuilder < QueryBuilder
def initialize(query = nil)
super(query || post.unscoped)
end
def with_title_matching(title)
reflect(
query.where(post[:title].matches("%#%"))
)
end
def with_comments_by(usernames)
reflect(
query
.joins(:comments => :author)
.where(Author[:username].in(usernames))
)
end
def since_yesterday
reflect(
query.where(post[:created_at].gteq(Date.yesterday))
)
end
private
def author
Author
end
def post
Post
end
end
148. class PostQueryBuilder < QueryBuilder
def initialize(query = nil)
super(query || post.unscoped)
end
def with_title_matching(title)
reflect(
query.where(post[:title].matches("%#%"))
)
end
def with_comments_by(usernames)
reflect(
query
.joins(:comments => :author)
.where(Author[:username].in(usernames))
)
end
def since_yesterday
reflect(
query.where(post[:created_at].gteq(Date.yesterday))
)
end
private
def author
Author
end
def post
Post
end
end
150. class PostQueryBuilder < QueryBuilder
def initialize(query = nil)
super(query || post.unscoped)
end
def with_title_matching(title)
reflect(
query.where(post[:title].matches("%#%"))
)
end
def with_comments_by(usernames)
reflect(
query
.joins(:comments => :author)
.where(Author[:username].in(usernames))
)
end
def since_yesterday
reflect(
query.where(post[:created_at].gteq(Date.yesterday))
)
end
private
def author
Author
end
def post
Post
end
end
151. class PostQueryBuilder < QueryBuilder
def initialize(query = nil)
super(query || post.unscoped)
end
def with_title_matching(title)
reflect(
query.where(post[:title].matches("%#%"))
)
end
def with_comments_by(usernames)
reflect(
query
.joins(:comments => :author)
.where(Author[:username].in(usernames))
)
end
def since_yesterday
reflect(
query.where(post[:created_at].gteq(Date.yesterday))
)
end
private
def author
Author
end
def post
Post
end
end