From ae96f229f6501d8635811d6b22d75d43cdb880a4 Mon Sep 17 00:00:00 2001
From: Sean Griffin <sean@thoughtbot.com>
Date: Mon, 23 Jun 2014 09:48:47 -0600
Subject: Add a deprecation cycle for `NullColumn` from `column_for_attribute`

This is public API, and `simple_form` depends on the `nil` return value.
We need to go through a deprecation cycle to return a null object. If
people want hash access, they can access the hash.
---
 activerecord/CHANGELOG.md                          |  3 ++-
 .../associations/preloader/association.rb          |  4 ++--
 .../lib/active_record/attribute_methods.rb         | 16 ++++++++++------
 .../active_record/connection_adapters/column.rb    |  6 ------
 activerecord/test/cases/reflection_test.rb         | 22 ++++------------------
 5 files changed, 18 insertions(+), 33 deletions(-)

diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index c87565452d..33dec99ec7 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -117,7 +117,8 @@
 
     *Lauro Caetano*, *Carlos Antonio da Silva*
 
-*   Return a null column from `column_for_attribute` when no column exists.
+*   Deprecate returning `nil` from `column_for_attribute` when no column exists.
+    It will return a null object in Rails 5.0
 
     *Sean Griffin*
 
diff --git a/activerecord/lib/active_record/associations/preloader/association.rb b/activerecord/lib/active_record/associations/preloader/association.rb
index 33c8619359..441768f302 100644
--- a/activerecord/lib/active_record/associations/preloader/association.rb
+++ b/activerecord/lib/active_record/associations/preloader/association.rb
@@ -104,11 +104,11 @@ module ActiveRecord
         end
 
         def association_key_type
-          @klass.column_for_attribute(association_key_name).type
+          @klass.type_for_attribute(association_key_name.to_s).type
         end
 
         def owner_key_type
-          @model.column_for_attribute(owner_key_name).type
+          @model.type_for_attribute(owner_key_name.to_s).type
         end
 
         def load_slices(slices)
diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb
index 267377cec6..51f6a009db 100644
--- a/activerecord/lib/active_record/attribute_methods.rb
+++ b/activerecord/lib/active_record/attribute_methods.rb
@@ -186,8 +186,7 @@ module ActiveRecord
       end
 
       # Returns the column object for the named attribute.
-      # Returns a +ActiveRecord::ConnectionAdapters::NullColumn+ if the
-      # named attribute does not exist.
+      # Returns nil if the named attribute does not exist.
       #
       #   class Person < ActiveRecord::Base
       #   end
@@ -197,12 +196,17 @@ module ActiveRecord
       #   # => #<ActiveRecord::ConnectionAdapters::SQLite3Column:0x007ff4ab083980 @name="name", @sql_type="varchar(255)", @null=true, ...>
       #
       #   person.column_for_attribute(:nothing)
-      #   # => #<ActiveRecord::ConnectionAdapters::NullColumn:0xXXX @name=nil, @sql_type=nil, @cast_type=#<Type::Value>, ...>
+      #   # => nil
       def column_for_attribute(name)
-        name = name.to_s
-        columns_hash.fetch(name) do
-          ConnectionAdapters::NullColumn.new(name)
+        column = columns_hash[name.to_s]
+        if column.nil?
+          ActiveSupport::Deprecation.warn(<<-MESSAGE.strip_heredoc)
+            `column_for_attribute` will return a null object for non-existent columns
+            in Rails 5.0. If you would like to continue to receive `nil`, you should
+            instead call `model.class.columns_hash[name]`
+          MESSAGE
         end
+        column
       end
     end
 
diff --git a/activerecord/lib/active_record/connection_adapters/column.rb b/activerecord/lib/active_record/connection_adapters/column.rb
index 8be4678ace..1f1e2c46f4 100644
--- a/activerecord/lib/active_record/connection_adapters/column.rb
+++ b/activerecord/lib/active_record/connection_adapters/column.rb
@@ -57,12 +57,6 @@ module ActiveRecord
         end
       end
     end
-
-    class NullColumn < Column
-      def initialize(name)
-        super name, nil, Type::Value.new
-      end
-    end
   end
   # :startdoc:
 end
diff --git a/activerecord/test/cases/reflection_test.rb b/activerecord/test/cases/reflection_test.rb
index c6e73f78cc..acbd065649 100644
--- a/activerecord/test/cases/reflection_test.rb
+++ b/activerecord/test/cases/reflection_test.rb
@@ -80,24 +80,10 @@ class ReflectionTest < ActiveRecord::TestCase
     assert_equal :integer, @first.column_for_attribute("id").type
   end
 
-  def test_non_existent_columns_return_null_object
-    column = @first.column_for_attribute("attribute_that_doesnt_exist")
-    assert_instance_of ActiveRecord::ConnectionAdapters::NullColumn, column
-    assert_equal "attribute_that_doesnt_exist", column.name
-    assert_equal nil, column.sql_type
-    assert_equal nil, column.type
-    assert_not column.number?
-    assert_not column.text?
-    assert_not column.binary?
-  end
-
-  def test_non_existent_columns_are_identity_types
-    column = @first.column_for_attribute("attribute_that_doesnt_exist")
-    object = Object.new
-
-    assert_equal object, column.type_cast_from_database(object)
-    assert_equal object, column.type_cast_from_user(object)
-    assert_equal object, column.type_cast_for_database(object)
+  def test_non_existent_columns_return_nil
+    assert_deprecated do
+      assert_nil @first.column_for_attribute("attribute_that_doesnt_exist")
+    end
   end
 
   def test_reflection_klass_for_nested_class_name
-- 
cgit v1.2.3