From 42be84ba40d95561554a4ccd62f482b028454025 Mon Sep 17 00:00:00 2001 From: Dylan Thacker-Smith Date: Sun, 6 Jul 2014 01:49:19 -0400 Subject: active_record: Type cast booleans and durations for string columns. --- activerecord/CHANGELOG.md | 11 ++++++++ activerecord/lib/active_record/type/binary.rb | 2 +- activerecord/lib/active_record/type/string.rb | 4 ++- .../test/cases/adapters/mysql2/boolean_test.rb | 4 +-- activerecord/test/cases/relation/where_test.rb | 33 +++++++++++++++++++++- 5 files changed, 49 insertions(+), 5 deletions(-) diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 21075d0636..42f2b72a08 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,14 @@ +* Avoid type casting boolean and ActiveSupport::Duration values to numeric + values for string columns. Otherwise, in some database, the string column + values will be coerced to a numeric allowing false or 0.seconds match any + string starting with a non-digit. + + Example: + + App.where(apikey: false) # => SELECT * FROM users WHERE apikey = '0' + + *Dylan Thacker-Smith* + * Add a `:required` option to singular associations, providing a nicer API for presence validations on associations. diff --git a/activerecord/lib/active_record/type/binary.rb b/activerecord/lib/active_record/type/binary.rb index 7416c554c7..d29ff4e494 100644 --- a/activerecord/lib/active_record/type/binary.rb +++ b/activerecord/lib/active_record/type/binary.rb @@ -24,7 +24,7 @@ module ActiveRecord class Data # :nodoc: def initialize(value) - @value = value + @value = value.to_s end def to_s diff --git a/activerecord/lib/active_record/type/string.rb b/activerecord/lib/active_record/type/string.rb index 8cc533bc41..e1791169d8 100644 --- a/activerecord/lib/active_record/type/string.rb +++ b/activerecord/lib/active_record/type/string.rb @@ -17,8 +17,10 @@ module ActiveRecord def type_cast_for_database(value) case value - when ::Numeric then value.to_s + when ::Numeric, ActiveSupport::Duration then value.to_s when ::String then ::String.new(value) + when true then "1" + when false then "0" else super end end diff --git a/activerecord/test/cases/adapters/mysql2/boolean_test.rb b/activerecord/test/cases/adapters/mysql2/boolean_test.rb index 267aa232d9..f3c711a64b 100644 --- a/activerecord/test/cases/adapters/mysql2/boolean_test.rb +++ b/activerecord/test/cases/adapters/mysql2/boolean_test.rb @@ -47,7 +47,7 @@ class Mysql2BooleanTest < ActiveRecord::TestCase assert_equal "1", attributes["published"] assert_equal 1, @connection.type_cast(true, boolean_column) - assert_equal 1, @connection.type_cast(true, string_column) + assert_equal "1", @connection.type_cast(true, string_column) end test "test type casting without emulated booleans" do @@ -60,7 +60,7 @@ class Mysql2BooleanTest < ActiveRecord::TestCase assert_equal "1", attributes["published"] assert_equal 1, @connection.type_cast(true, boolean_column) - assert_equal 1, @connection.type_cast(true, string_column) + assert_equal "1", @connection.type_cast(true, string_column) end test "with booleans stored as 1 and 0" do diff --git a/activerecord/test/cases/relation/where_test.rb b/activerecord/test/cases/relation/where_test.rb index 937f226b1d..a6a36a6fd9 100644 --- a/activerecord/test/cases/relation/where_test.rb +++ b/activerecord/test/cases/relation/where_test.rb @@ -6,10 +6,11 @@ require 'models/post' require 'models/comment' require 'models/edge' require 'models/topic' +require 'models/binary' module ActiveRecord class WhereTest < ActiveRecord::TestCase - fixtures :posts, :edges, :authors + fixtures :posts, :edges, :authors, :binaries def test_where_copies_bind_params author = authors(:david) @@ -179,5 +180,35 @@ module ActiveRecord assert_equal 4, Edge.where(blank).order("sink_id").to_a.size end end + + def test_where_with_integer_for_string_column + count = Post.where(:title => 0).count + assert_equal 0, count + end + + def test_where_with_float_for_string_column + count = Post.where(:title => 0.0).count + assert_equal 0, count + end + + def test_where_with_boolean_for_string_column + count = Post.where(:title => false).count + assert_equal 0, count + end + + def test_where_with_decimal_for_string_column + count = Post.where(:title => BigDecimal.new(0)).count + assert_equal 0, count + end + + def test_where_with_duration_for_string_column + count = Post.where(:title => 0.seconds).count + assert_equal 0, count + end + + def test_where_with_integer_for_binary_column + count = Binary.where(:data => 0).count + assert_equal 0, count + end end end -- cgit v1.2.3