From 2f8c596d1beb46acd8a74f0e6d5c1c48a29dcee9 Mon Sep 17 00:00:00 2001 From: Sean Griffin Date: Tue, 10 Feb 2015 08:20:54 -0700 Subject: [PATCH] Maintain a consistent order in `ActiveRecord::Base#attributes` Fixes #18871 --- .../lib/active_record/attribute_set/builder.rb | 12 +++++++++++- activerecord/test/cases/attribute_set_test.rb | 10 ++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/attribute_set/builder.rb b/activerecord/lib/active_record/attribute_set/builder.rb index 0f3c285a80..e85777c335 100644 --- a/activerecord/lib/active_record/attribute_set/builder.rb +++ b/activerecord/lib/active_record/attribute_set/builder.rb @@ -20,7 +20,7 @@ module ActiveRecord end class LazyAttributeHash # :nodoc: - delegate :select, :transform_values, :each_key, to: :materialize + delegate :transform_values, :each_key, to: :materialize def initialize(types, values, additional_types) @types = types @@ -50,6 +50,16 @@ module ActiveRecord super end + def select + keys = types.keys | values.keys | delegate_hash.keys + keys.each_with_object({}) do |key, hash| + attribute = self[key] + if yield(key, attribute) + hash[key] = attribute + end + end + end + protected attr_reader :types, :values, :additional_types, :delegate_hash diff --git a/activerecord/test/cases/attribute_set_test.rb b/activerecord/test/cases/attribute_set_test.rb index 8025c7c4d2..112cd2fb14 100644 --- a/activerecord/test/cases/attribute_set_test.rb +++ b/activerecord/test/cases/attribute_set_test.rb @@ -65,6 +65,16 @@ module ActiveRecord assert_equal({ foo: 1, bar: 2.2 }, attributes.to_h) end + test "to_hash maintains order" do + builder = AttributeSet::Builder.new(foo: Type::Integer.new, bar: Type::Float.new) + attributes = builder.build_from_database(foo: '2.2', bar: '3.3') + + attributes[:bar] + hash = attributes.to_h + + assert_equal [[:foo, 2], [:bar, 3.3]], hash.to_a + end + test "values_before_type_cast" do builder = AttributeSet::Builder.new(foo: Type::Integer.new, bar: Type::Integer.new) attributes = builder.build_from_database(foo: '1.1', bar: '2.2')