aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/arel/attributes/attribute.rb8
-rw-r--r--lib/arel/nodes.rb11
-rw-r--r--lib/arel/nodes/table_alias.rb8
-rw-r--r--lib/arel/table.rb21
-rw-r--r--lib/arel/visitors/to_sql.rb23
-rw-r--r--test/attributes/test_attribute.rb34
6 files changed, 89 insertions, 16 deletions
diff --git a/lib/arel/attributes/attribute.rb b/lib/arel/attributes/attribute.rb
index 0906fa4f1d..cda5a5a3db 100644
--- a/lib/arel/attributes/attribute.rb
+++ b/lib/arel/attributes/attribute.rb
@@ -12,6 +12,14 @@ module Arel
def lower
relation.lower self
end
+
+ def type_cast_for_database(value)
+ relation.type_cast_for_database(name, value)
+ end
+
+ def able_to_type_cast?
+ relation.able_to_type_cast?
+ end
end
class String < Attribute; end
diff --git a/lib/arel/nodes.rb b/lib/arel/nodes.rb
index 8432106719..7d900fe710 100644
--- a/lib/arel/nodes.rb
+++ b/lib/arel/nodes.rb
@@ -87,17 +87,6 @@ module Arel
else
case attribute
when Arel::Attributes::Attribute
- unless $arel_silence_type_casting_deprecation
- warn <<-eowarn
-Arel performing automatic type casting is deprecated, and will be removed in Arel 8.0. If you are seeing this, it is because you are manually passing a value to an Arel predicate.
-
-If you're certain the value is already of the right type, change `attribute.eq(value)` to `attribute.eq(Arel::Nodes::Quoted.new(value))` (you will be able to remove that in Arel 8.0, it is only required to silence this deprecation warning).
-
-You can also silence this warning globally by setting `$arel_silence_type_casting_deprecation` to `true`. (Do NOT do this if you are a library author)
-
-If you are passing user input to a predicate, you are responsible for type casting appropriately before passing the value to Arel.
- eowarn
- end
Casted.new other, attribute
else
Quoted.new other
diff --git a/lib/arel/nodes/table_alias.rb b/lib/arel/nodes/table_alias.rb
index b32f057117..a5adc0766a 100644
--- a/lib/arel/nodes/table_alias.rb
+++ b/lib/arel/nodes/table_alias.rb
@@ -12,6 +12,14 @@ module Arel
def table_name
relation.respond_to?(:name) ? relation.name : name
end
+
+ def type_cast_for_database(*args)
+ relation.type_cast_for_database(*args)
+ end
+
+ def able_to_type_cast?
+ relation.respond_to?(:able_to_type_cast?) && relation.able_to_type_cast?
+ end
end
end
end
diff --git a/lib/arel/table.rb b/lib/arel/table.rb
index 2c7a2b7f93..b4b4a861b8 100644
--- a/lib/arel/table.rb
+++ b/lib/arel/table.rb
@@ -11,18 +11,19 @@ module Arel
# TableAlias and Table both have a #table_name which is the name of the underlying table
alias :table_name :name
- def initialize name, options = {}
+ def initialize(name, as: nil, type_caster: nil)
@name = name.to_s
@columns = nil
@aliases = []
+ @type_caster = type_caster
# Sometime AR sends an :as parameter to table, to let the table know
# that it is an Alias. We may want to override new, and return a
# TableAlias node?
- @table_alias = options[:as]
- if @table_alias.to_s == @name
- @table_alias = nil
+ if as.to_s == @name
+ as = nil
end
+ @table_alias = as
end
def alias name = "#{self.name}_2"
@@ -98,6 +99,18 @@ module Arel
end
alias :== :eql?
+ def type_cast_for_database(attribute_name, value)
+ type_caster.type_cast_for_database(attribute_name, value)
+ end
+
+ def able_to_type_cast?
+ !type_caster.nil?
+ end
+
+ protected
+
+ attr_reader :type_caster
+
private
def attributes_for columns
diff --git a/lib/arel/visitors/to_sql.rb b/lib/arel/visitors/to_sql.rb
index 30c8634119..acf0a74d37 100644
--- a/lib/arel/visitors/to_sql.rb
+++ b/lib/arel/visitors/to_sql.rb
@@ -721,7 +721,11 @@ module Arel
alias :visit_Fixnum :literal
def quoted o, a
- quote(o, column_for(a))
+ if a && a.able_to_type_cast?
+ quote(a.type_cast_for_database(o))
+ else
+ quote(o, column_for(a))
+ end
end
def unsupported o, collector
@@ -761,6 +765,9 @@ module Arel
def quote value, column = nil
return value if Arel::Nodes::SqlLiteral === value
+ if column
+ print_type_cast_deprecation
+ end
@connection.quote value, column
end
@@ -810,6 +817,20 @@ module Arel
collector
end
end
+
+ def print_type_cast_deprecation
+ unless defined?($arel_silence_type_casting_deprecation) && $arel_silence_type_casting_deprecation
+ warn <<-eowarn
+Arel performing automatic type casting is deprecated, and will be removed in Arel 8.0. If you are seeing this, it is because you are manually passing a value to an Arel predicate, and the `Arel::Table` object was constructed manually. The easiest way to remove this warning is to use an `Arel::Table` object returned from calling `arel_table` on an ActiveRecord::Base subclass.
+
+If you're certain the value is already of the right type, change `attribute.eq(value)` to `attribute.eq(Arel::Nodes::Quoted.new(value))` (you will be able to remove that in Arel 8.0, it is only required to silence this deprecation warning).
+
+You can also silence this warning globally by setting `$arel_silence_type_casting_deprecation` to `true`. (Do NOT do this if you are a library author)
+
+If you are passing user input to a predicate, you must either give an appropriate type caster object to the `Arel::Table`, or manually cast the value before passing it to Arel.
+ eowarn
+ end
+ end
end
end
end
diff --git a/test/attributes/test_attribute.rb b/test/attributes/test_attribute.rb
index e4ddb27e72..0f6d6e447b 100644
--- a/test/attributes/test_attribute.rb
+++ b/test/attributes/test_attribute.rb
@@ -950,5 +950,39 @@ module Arel
end
end
end
+
+ describe 'type casting' do
+ it 'does not type cast by default' do
+ table = Table.new(:foo)
+ condition = table["id"].eq("1")
+
+ refute table.able_to_type_cast?
+ condition.to_sql.must_equal %("foo"."id" = '1')
+ end
+
+ it 'type casts when given an explicit caster' do
+ fake_caster = Object.new
+ def fake_caster.type_cast_for_database(attr_name, value)
+ if attr_name == "id"
+ value.to_i
+ else
+ value
+ end
+ end
+ table = Table.new(:foo, type_caster: fake_caster)
+ condition = table["id"].eq("1").and(table["other_id"].eq("2"))
+
+ assert table.able_to_type_cast?
+ condition.to_sql.must_equal %("foo"."id" = 1 AND "foo"."other_id" = '2')
+ end
+
+ it 'falls back to using the connection adapter for type casting' do
+ table = Table.new(:users)
+ condition = table["id"].eq("1")
+
+ refute table.able_to_type_cast?
+ condition.to_sql.must_equal %("users"."id" = 1)
+ end
+ end
end
end