From a015afbd8e1be9de2c173d7ceda788c61034e51f Mon Sep 17 00:00:00 2001 From: HolyWalley Date: Mon, 6 Jul 2026 19:55:06 +0200 Subject: [PATCH 1/2] [BAC-1267] Compile the JSON attribute assigner per exact class --- lib/active_model/entity/parsers/json.rb | 32 +++++++++++-------- spec/active_model/entity/parsers/json_spec.rb | 21 ++++++++++++ 2 files changed, 39 insertions(+), 14 deletions(-) diff --git a/lib/active_model/entity/parsers/json.rb b/lib/active_model/entity/parsers/json.rb index d4b9596..b1e7902 100644 --- a/lib/active_model/entity/parsers/json.rb +++ b/lib/active_model/entity/parsers/json.rb @@ -11,6 +11,10 @@ def set_attribute_from_json(name, value) @attributes[name] = @attributes[name].with_value_from_json(value) end + def assign_attributes_from_json(json) + instance_exec(json, &self.class.json_attributes_assigner) + end + # Class-level methods. module ClassMethods def from_json(json) @@ -18,16 +22,23 @@ def from_json(json) instance.assign_attributes_from_json(json) end end - end - def method_missing(method_name, *, &) - if method_name == :assign_attributes_from_json - setters = attributes.keys.map do |name| + # Compiled once per exact class (class-level ivars are not + # inherited), so an assigner compiled for a superclass can never + # shadow attributes that exist only on a subclass. + def json_attributes_assigner + @json_attributes_assigner ||= compile_json_attributes_assigner + end + + private + + def compile_json_attributes_assigner + setters = attribute_types.keys.map do |name| <<~RUBY #{name}_value = json[#{name.camelize(:lower).inspect}] #{name}_value = json[#{name.camelize(:lower).to_sym.inspect}] if #{name}_value.nil? - self.set_attribute_from_json( + set_attribute_from_json( #{name.inspect}, #{name}_value ) @@ -35,21 +46,14 @@ def method_missing(method_name, *, &) end code = <<~RUBY - def assign_attributes_from_json(json) + ->(json) do #{setters.join("\n")} end RUBY - self.class.class_eval(code) - send(method_name, *, &) - else - super + class_eval(code, __FILE__, __LINE__) end end - - def respond_to_missing?(method_name, include_private) - method_name == :assign_attributes_from_json || super - end end end end diff --git a/spec/active_model/entity/parsers/json_spec.rb b/spec/active_model/entity/parsers/json_spec.rb index 0fd43b0..1d6d696 100644 --- a/spec/active_model/entity/parsers/json_spec.rb +++ b/spec/active_model/entity/parsers/json_spec.rb @@ -17,6 +17,16 @@ class Role attribute :field_positions, :array, of: "ParsersTest::Position" end + class Employee + include ActiveModel::Entity + + attribute :field_name, :string + end + + class Manager < Employee + attribute :field_department, :string + end + class Person include ActiveModel::Entity @@ -121,6 +131,17 @@ class Person end end + context "parsing into a subclass after the superclass has compiled its assigner" do + it "keeps subclass-only attributes" do + employee = ParsersTest::Employee.from_json({ "fieldName" => "emp" }) + manager = ParsersTest::Manager.from_json({ "fieldName" => "mgr", "fieldDepartment" => "sales" }) + + expect(employee.field_name).to eq("emp") + expect(manager.field_name).to eq("mgr") + expect(manager.field_department).to eq("sales") + end + end + context "parsing symbolized json" do let(:source) do { From 238f6ff2c1e0cfc701a7b6114b46ee8bf065a523 Mon Sep 17 00:00:00 2001 From: HolyWalley Date: Mon, 6 Jul 2026 19:55:06 +0200 Subject: [PATCH 2/2] [BAC-1267] Bump nokogiri in the dev lockfile (1.16.6 no longer builds on current macOS) --- Gemfile.lock | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index e0a0a29..c155081 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - activemodel-entity (0.8.0) + activemodel-entity (0.13.0) actionpack (>= 7) activemodel (>= 7) activesupport (>= 7) @@ -69,19 +69,23 @@ GEM loofah (2.22.0) crass (~> 1.0.2) nokogiri (>= 1.12.0) - minitest (5.24.1) + mini_portile2 (2.8.9) + minitest (6.0.6) + drb (~> 2.0) + prism (~> 1.5) mutex_m (0.2.0) - nokogiri (1.16.6-aarch64-linux) + nokogiri (1.19.4) + mini_portile2 (~> 2.8.2) racc (~> 1.4) - nokogiri (1.16.6-arm-linux) + nokogiri (1.19.4-aarch64-linux-gnu) racc (~> 1.4) - nokogiri (1.16.6-arm64-darwin) + nokogiri (1.19.4-arm-linux-gnu) racc (~> 1.4) - nokogiri (1.16.6-x86-linux) + nokogiri (1.19.4-arm64-darwin) racc (~> 1.4) - nokogiri (1.16.6-x86_64-darwin) + nokogiri (1.19.4-x86_64-darwin) racc (~> 1.4) - nokogiri (1.16.6-x86_64-linux) + nokogiri (1.19.4-x86_64-linux-gnu) racc (~> 1.4) parallel (1.25.1) parser (3.3.4.0) @@ -90,6 +94,7 @@ GEM pp (0.6.2) prettyprint prettyprint (0.2.0) + prism (1.9.0) psych (5.2.6) date stringio