From 1823d4ab7a154fcc7b6bdc5491508b0a4cd4ab2b Mon Sep 17 00:00:00 2001 From: Stephen Drew Date: Mon, 4 Apr 2022 19:25:52 +0000 Subject: [PATCH] Add support for ActiveRecord::Point type casts using hash values This allows ActiveRecord::Point to be cast or serialized from a hash with :x and :y keys of numeric values, mirroring the functionality of existing casts for string and array values. Both string and symbol keys are supported. --- activerecord/CHANGELOG.md | 24 +++++++++++++++++++ .../postgresql/oid/point.rb | 10 ++++++++ .../adapters/postgresql/geometric_test.rb | 7 ++++++ 3 files changed, 41 insertions(+) diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 70e7bf9f009..a780bec51f6 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,27 @@ +* Add support for `ActiveRecord::Point` type casts using `Hash` values + + This allows `ActiveRecord::Point` to be cast or serialized from a hash + with `:x` and `:y` keys of numeric values, mirroring the functionality of + existing casts for string and array values. Both string and symbol keys are + supported. + + ```ruby + class PostgresqlPoint < ActiveRecord::Base + attribute :x, :point + attribute :y, :point + attribute :z, :point + end + + val = PostgresqlPoint.new({ + x: '(12.34, -43.21)', + y: [12.34, '-43.21'], + z: {x: '12.34', y: -43.21} + }) + ActiveRecord::Point.new(12.32, -43.21) == val.x == val.y == val.z + ``` + + *Stephen Drew* + * Replace `SQLite3::Database#busy_timeout` with `#busy_handler_timeout=`. Provides a non-GVL-blocking, fair retry interval busy handler implementation. diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/point.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/point.rb index f53b3fa6027..dc8e67fe73f 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/point.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/point.rb @@ -25,6 +25,10 @@ module ActiveRecord build_point(x, y) when ::Array build_point(*value) + when ::Hash + return if value.blank? + + build_point(*values_array_from_hash(value)) else value end @@ -36,6 +40,8 @@ module ActiveRecord "(#{number_for_point(value.x)},#{number_for_point(value.y)})" when ::Array serialize(build_point(*value)) + when ::Hash + serialize(build_point(*values_array_from_hash(value))) else super end @@ -57,6 +63,10 @@ module ActiveRecord def build_point(x, y) ActiveRecord::Point.new(Float(x), Float(y)) end + + def values_array_from_hash(value) + [value.values_at(:x, "x").compact.first, value.values_at(:y, "y").compact.first] + end end end end diff --git a/activerecord/test/cases/adapters/postgresql/geometric_test.rb b/activerecord/test/cases/adapters/postgresql/geometric_test.rb index d82e1267e07..a1a109e64bd 100644 --- a/activerecord/test/cases/adapters/postgresql/geometric_test.rb +++ b/activerecord/test/cases/adapters/postgresql/geometric_test.rb @@ -88,6 +88,13 @@ class PostgresqlPointTest < ActiveRecord::PostgreSQLTestCase assert_equal ActiveRecord::Point.new(1, 2), p.x end + def test_hash_assignment + p = PostgresqlPoint.new(x: { x: 1, y: 2 }, y: { "x" => 3, "y" => 4 }) + + assert_equal ActiveRecord::Point.new(1, 2), p.x + assert_equal ActiveRecord::Point.new(3, 4), p.y + end + def test_string_assignment p = PostgresqlPoint.new(x: "(1, 2)")