Skip to content

Commit 511ffed

Browse files
authored
Merge pull request rails#47438 from benedikt/arel-documentation
Adds documentation for Arel::Nodes::Node
2 parents 3531ca6 + e0edad4 commit 511ffed

File tree

1 file changed

+97
-2
lines changed

1 file changed

+97
-2
lines changed

activerecord/lib/arel/nodes/node.rb

Lines changed: 97 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,103 @@
22

33
module Arel # :nodoc: all
44
module Nodes
5-
###
6-
# Abstract base class for all AST nodes
5+
# = Using +Arel::Nodes::Node+
6+
#
7+
# Active Record uses Arel to compose SQL statements. Instead of building SQL strings directly, it's building an
8+
# abstract syntax tree (AST) of the statement using various types of Arel::Nodes::Node. Each node represents a
9+
# fragment of a SQL statement.
10+
#
11+
# The intermediate representation allows Arel to compile the statement into the database's specific SQL dialect
12+
# only before sending it without having to care about the nuances of each database when building the statement.
13+
# It also allows easier composition of statements without having to resort to (brittle and unsafe) string manipulation.
14+
#
15+
# == Building constraints
16+
#
17+
# One of the most common use cases of Arel is generating constraints for +SELECT+ statements. To help with that,
18+
# most nodes include a couple of useful factory methods to create subtree structures for common constraints. For
19+
# a full list of those, please refer to Arel::Predications.
20+
#
21+
# The following example creates a equality constraint where the value of the name column on the users table
22+
# matches the value DHH.
23+
#
24+
# users = Arel::Table.new(:users)
25+
# constraint = users[:name].eq("DHH")
26+
#
27+
# # => Arel::Nodes::Equality.new(
28+
# # Arel::Attributes::Attribute.new(users, "name"),
29+
# # Arel::Nodes::Casted.new(
30+
# # "DHH",
31+
# # Arel::Attributes::Attribute.new(users, "name")
32+
# # )
33+
# # )
34+
#
35+
# The resulting SQL fragment will look like this:
36+
#
37+
# "users"."name" = 'DHH'
38+
#
39+
# The constraint fragments can be used with regular ActiveRecord::Relation objects instead of as Hash. The
40+
# following two example shows two ways of creating the same query.
41+
#
42+
# User.where(name: 'DHH')
43+
#
44+
# # SELECT "users".* FROM "users" WHERE "users"."name" = 'DHH'
45+
#
46+
# users = User.arel_table
47+
#
48+
# User.where(users[:name].eq('DHH'))
49+
#
50+
# # SELECT "users".* FROM "users" WHERE "users"."name" = 'DHH'
51+
#
52+
# == Functions
53+
#
54+
# Arel comes with built in support for SQL functions like +COUNT+, +SUM+, +MIN+, +MAX+, and +AVG+. The
55+
# Arel::Expressions module includes factory methods for the default functions.
56+
#
57+
# employees = Employee.arel_table
58+
#
59+
# Employee.select(employees[:department_id], employees[:salary].average).group(employees[:department_id])
60+
#
61+
# # SELECT "employees"."department_id", AVG("employees"."salary")
62+
# # FROM "employees" GROUP BY "employees"."department_id"
63+
#
64+
# It’s also possible to use custom functions by using the Arel::Nodes::NamedFunction node type. It accepts a
65+
# function name and an array of parameters.
66+
#
67+
# Arel::Nodes::NamedFunction.new('date_trunc', [Arel::Nodes.build_quoted('day'), User.arel_table[:created_at]])
68+
#
69+
# # date_trunc('day', "users"."created_at")
70+
#
71+
# == Quoting & bind params
72+
#
73+
# Values that you pass to Arel nodes need to be quoted or wrapped in bind params. This ensures they are properly
74+
# converted into the correct format without introducing a possible SQL injection vulnerability. Most factory
75+
# methods (like +eq+, +gt+, +lteq+, …) quote passed values automatically. When not using a factory method, it’s
76+
# possible to convert a value and wrap it in a Arel::Nodes::Quoted node (if necessary) by calling +Arel::Nodes.
77+
# build_quoted+.
78+
#
79+
# Arel::Nodes.build_quoted("foo") # 'foo'
80+
# Arel::Nodes.build_quoted(12.3) # 12.3
81+
#
82+
# Instead of quoting values and embedding them directly in the SQL statement, it’s also possible to create bind
83+
# params. This keeps the actual values outside of the statement and allows using the prepared statement feature
84+
# of some databases.
85+
#
86+
# attribute = ActiveRecord::Relation::QueryAttribute.new(:name, "DHH", ActiveRecord::Type::String.new)
87+
# Arel::Nodes::BindParam.new(attribute)
88+
#
89+
# When ActiveRecord runs the query, bind params are replaced by placeholders (like +$1+) and the values are passed
90+
# separately.
91+
#
92+
# == SQL Literals
93+
#
94+
# For cases where there is no way to represent a particular SQL fragment using Arel nodes, you can use an SQL
95+
# literal. SQL literals are strings that Arel will treat “as is”.
96+
#
97+
# Arel.sql('LOWER("users"."name")').eq('dhh')
98+
#
99+
# # LOWER("users"."name") = 'dhh'
100+
#
101+
# Please keep in mind that passing data as raw SQL literals might introduce a possible SQL injection.
7102
class Node
8103
include Arel::FactoryMethods
9104

0 commit comments

Comments
 (0)