From 0d43aa1561384bc1f98182708fefd02ae0e69b65 Mon Sep 17 00:00:00 2001 From: "Aleksandar N. Kostadinov" Date: Thu, 3 Feb 2022 19:22:49 +0200 Subject: [PATCH] raw type should be able to deserialize ActiveRecord expects types to properly deserialize any values that have been serialized. Right now (Rails 7.1) by every object save the attributes are reset by serialize/deserialize cycle. see ActiveModel::ActiveModel#forget_attribute_assignments --- lib/active_record/type/oracle_enhanced/raw.rb | 21 +++++++++- .../oracle_enhanced/type/raw_spec.rb | 38 ++++++++++++++++++- 2 files changed, 55 insertions(+), 4 deletions(-) diff --git a/lib/active_record/type/oracle_enhanced/raw.rb b/lib/active_record/type/oracle_enhanced/raw.rb index 947947ae1..0493830fc 100644 --- a/lib/active_record/type/oracle_enhanced/raw.rb +++ b/lib/active_record/type/oracle_enhanced/raw.rb @@ -10,14 +10,31 @@ def type :raw end + def deserialize(value) + value.is_a?(HEXData) ? value.raw_binary_string : super + end + def serialize(value) # Encode a string or byte array as string of hex codes if value.nil? super else - value = value.unpack("C*") - value.map { |x| "%02X" % x }.join + HEXData.from_binary_string(value) + end + end + + class HEXData < ::String + def self.from_binary_string(str) + new(str.unpack1("H*")) + end + + def raw_binary_string + (0..length - 2).step(2).reduce(::String.new(capacity: length / 2, encoding: Encoding::BINARY)) do |data, i| + data << self[i, 2].hex + end end + + OCI8::BindType::Mapping[self] = OCI8::BindType::String end end end diff --git a/spec/active_record/oracle_enhanced/type/raw_spec.rb b/spec/active_record/oracle_enhanced/type/raw_spec.rb index 4cd4d6812..a391e5d5f 100644 --- a/spec/active_record/oracle_enhanced/type/raw_spec.rb +++ b/spec/active_record/oracle_enhanced/type/raw_spec.rb @@ -38,6 +38,7 @@ class ::TestEmployee < ActiveRecord::Base last_name: "Last", binary_data: @binary_data ) + expect(@employee.binary_data).to eq(@binary_data) @employee.reload expect(@employee.binary_data).to eq(@binary_data) end @@ -51,6 +52,7 @@ class ::TestEmployee < ActiveRecord::Base expect(@employee.binary_data).to be_nil @employee.binary_data = @binary_data @employee.save! + expect(@employee.binary_data).to eq(@binary_data) @employee.reload expect(@employee.binary_data).to eq(@binary_data) end @@ -77,6 +79,7 @@ class ::TestEmployee < ActiveRecord::Base @employee.reload @employee.binary_data = @binary_data2 @employee.save! + expect(@employee.binary_data).to eq(@binary_data2) @employee.reload expect(@employee.binary_data).to eq(@binary_data2) end @@ -116,13 +119,14 @@ class ::TestEmployee < ActiveRecord::Base @employee.reload @employee.binary_data = @binary_data @employee.save! + expect(@employee.binary_data).to eq(@binary_data) @employee.reload expect(@employee.binary_data).to eq(@binary_data) end it "should allow equality on select" do TestEmployee.delete_all - TestEmployee.create!( + employee = TestEmployee.create!( first_name: "First", last_name: "Last", binary_data: @binary_data, @@ -132,6 +136,36 @@ class ::TestEmployee < ActiveRecord::Base last_name: "Last1", binary_data: @binary_data2, ) - expect(TestEmployee.where(binary_data: @binary_data)).to have_attributes(count: 1) + expect(TestEmployee.where(binary_data: @binary_data).to_a).to eq([employee]) + end + + it "should allow equality on select with NULL value" do + TestEmployee.delete_all + employee = TestEmployee.create!( + first_name: "First", + last_name: "Last", + ) + TestEmployee.create!( + first_name: "First1", + last_name: "Last1", + binary_data: @binary_data2, + ) + expect(TestEmployee.where(binary_data: nil).to_a).to eq([employee]) + end + + it "should report changed when changed in place" do + employee = TestEmployee.create!( + first_name: "First", + last_name: "Last", + binary_data: @binary_data, + ) + expect(employee.changed?).to be_falsey + + employee.binary_data << "a" + expect(employee.changed?).to be(true) + expect(employee.changes).to eq({ "binary_data" => [@binary_data, @binary_data + "a"] }) + + employee.reload.binary_data << "b" + expect(employee.changes).to eq({ "binary_data" => [@binary_data, @binary_data + "b"] }) end end