From 8f4e93d1c43843c915d0d5565aebe9b8d20b87f2 Mon Sep 17 00:00:00 2001 From: Alexander Sviridov Date: Thu, 15 Aug 2024 13:46:52 +0300 Subject: [PATCH] Treat all input which responds to `to_h` as Hash --- lib/store_model/types/one.rb | 17 ++++++---- lib/store_model/types/one_polymorphic.rb | 12 ++++--- spec/store_model/model_spec.rb | 33 +++++++++++++++++++ .../store_model/types/one_polymorphic_spec.rb | 4 +-- spec/store_model/types/one_spec.rb | 4 +-- 5 files changed, 55 insertions(+), 15 deletions(-) diff --git a/lib/store_model/types/one.rb b/lib/store_model/types/one.rb index a94f838..b9da78d 100644 --- a/lib/store_model/types/one.rb +++ b/lib/store_model/types/one.rb @@ -27,12 +27,17 @@ def type # @param value [Object] a value to cast # # @return StoreModel::Model - def cast_value(value) - case value - when String then decode_and_initialize(value) - when Hash then model_instance(value) - when @model_klass, nil then value - else raise_cast_error(value) + def cast_value(value) # rubocop:disable Metrics/MethodLength + return nil if value.nil? + + if value.is_a?(String) + decode_and_initialize(value) + elsif value.is_a?(@model_klass) + value + elsif value.respond_to?(:to_h) # Hash itself included + model_instance(value.to_h) + else + raise_cast_error(value) end rescue ActiveModel::UnknownAttributeError => e handle_unknown_attribute(value, e) diff --git a/lib/store_model/types/one_polymorphic.rb b/lib/store_model/types/one_polymorphic.rb index f5437d0..9608aa2 100644 --- a/lib/store_model/types/one_polymorphic.rb +++ b/lib/store_model/types/one_polymorphic.rb @@ -29,11 +29,13 @@ def type # @param value [Object] a value to cast # # @return StoreModel::Model - def cast_value(value) - case value - when String then decode_and_initialize(value) - when Hash then extract_model_klass(value).new(value) - when nil then value + def cast_value(value) # rubocop:disable Metrics/MethodLength + return nil if value.nil? + + if value.is_a?(String) + decode_and_initialize(value) + elsif value.respond_to?(:to_h) # Hash itself included + extract_model_klass(value).new(value.to_h) else raise_cast_error(value) unless value.class.ancestors.include?(StoreModel::Model) diff --git a/spec/store_model/model_spec.rb b/spec/store_model/model_spec.rb index 8121cf9..19a726d 100644 --- a/spec/store_model/model_spec.rb +++ b/spec/store_model/model_spec.rb @@ -42,6 +42,39 @@ it("assigns attributes") { is_expected.to have_attributes(attributes) } end + + context "when hash-like other class passed" do + subject do + class Product < ActiveRecord::Base + attribute :configuration, Configuration.to_type + end + + Product.new(configuration: attributes) + end + + let(:attributes) do + class NotHash + def initialize(attrs) + @attrs = attrs + end + + def to_h + @attrs + end + end + NotHash.new( + color: "red", + model: nil, + active: false, + disabled_at: Time.new(2019, 2, 10, 12).utc, + encrypted_serial: "111-222" + ) + end + + it("assigns attributes") do + expect(subject.configuration).to have_attributes(attributes.to_h) + end + end end describe "#fetch" do diff --git a/spec/store_model/types/one_polymorphic_spec.rb b/spec/store_model/types/one_polymorphic_spec.rb index d1d75b4..99348ad 100644 --- a/spec/store_model/types/one_polymorphic_spec.rb +++ b/spec/store_model/types/one_polymorphic_spec.rb @@ -58,12 +58,12 @@ end context "when instance of illegal class is passed" do - let(:value) { [] } + let(:value) { 1 } it "raises exception" do expect { type.cast_value(value) }.to raise_error( StoreModel::Types::CastError, - "failed casting [], only String, Hash or instances which " \ + "failed casting 1, only String, Hash or instances which " \ "implement StoreModel::Model are allowed" ) end diff --git a/spec/store_model/types/one_spec.rb b/spec/store_model/types/one_spec.rb index 796dab1..494df9a 100644 --- a/spec/store_model/types/one_spec.rb +++ b/spec/store_model/types/one_spec.rb @@ -58,12 +58,12 @@ end context "when instance of illegal class is passed" do - let(:value) { [] } + let(:value) { 1 } it "raises exception" do expect { type.cast_value(value) }.to raise_error( StoreModel::Types::CastError, - "failed casting [], only String, Hash or Configuration instances are allowed" + "failed casting 1, only String, Hash or Configuration instances are allowed" ) end end