From 502ba903857051b6336caaa1806cfb1c0e679694 Mon Sep 17 00:00:00 2001 From: Niall Mullally Date: Fri, 3 Oct 2025 18:05:36 +0100 Subject: [PATCH] Add `attr_evaluated` to StaticAssociation objects `attr_evaluated` can be used to define an attribute who's return value is evaluated/computed at runtime. It defines a normal writer method using `attr_writer` which can take either a static value or a proc/lambda. If the latter, the reader method will evaluate the proc/lambda when called and return its value. This allows for defining dynamic runtime behaviour for specific record attributes. A static value can still be assigned like other non-evaluated attributes, and that value will always be returned. E.g. ```ruby class MyClass include StaticAssociation attr_accessor :toggle attr_evaluated :status record(id: 1) do self.toggle = true self.status = -> { toggle ? "on" : "off" } end record(id: 2) do self.toggle = true self.status = "static value" end end record1 = MyClass.find(1) record1.status # => "on" record2 = MyClass.find(2) record2.status # => "static value" ``` As with `attr_accessor` you can pass multiple attribute names to a single `attr_evaluated` call. ```ruby attr_evaluated :my_attr_1, :my_attr_2 ``` --- lib/static_association.rb | 16 ++++++++++++ spec/static_association_spec.rb | 46 +++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) diff --git a/lib/static_association.rb b/lib/static_association.rb index 012ede9..5fe0ae5 100644 --- a/lib/static_association.rb +++ b/lib/static_association.rb @@ -82,6 +82,22 @@ def record(settings, &block) index[id] = record end + def attr_evaluated(*names) + attr_writer(*names) + + names.each do |name| + define_method(name) do + attr_value = instance_variable_get("@#{name}") + + if attr_value.respond_to?(:call) + instance_exec(&attr_value) + else + attr_value + end + end + end + end + private def matches_attributes?(record:, attributes:) diff --git a/spec/static_association_spec.rb b/spec/static_association_spec.rb index 2a76e73..8a54628 100644 --- a/spec/static_association_spec.rb +++ b/spec/static_association_spec.rb @@ -3,7 +3,9 @@ class DummyClass include StaticAssociation + attr_accessor :name + attr_evaluated :gender end class AssociationClass @@ -11,6 +13,7 @@ class AssociationClass attr_accessor :dodo_class_id extend StaticAssociation::AssociationHelpers + belongs_to_static :dummy_class belongs_to_static :dodo_class, class_name: "DummyClass" end @@ -75,6 +78,49 @@ class AssociationClass expect { DummyClass.record(id: 1) }.to change(DummyClass, :count).by(1) end end + + context "when attr_evaluated defined attribute is assigned a lambda" do + it "evaluates the lambda when the attribute is read" do + record = DummyClass.record(id: 1) do + self.gender = -> do + if name == "Jane" + :female + else + :male + end + end + end + + expect(record.gender).to eq(:male) + end + end + + context "when attr_evaluated defined attribute is assigned a proc" do + it "evaluates the proc when the attribute is read" do + record = DummyClass.record(id: 1) do + self.name = "Jane" + self.gender = proc do + if @name == "Jane" + :female + else + :male + end + end + end + + expect(record.gender).to eq(:female) + end + end + + context "when attr_evaluated defined attribute is assigned a static value" do + it "returns the assigned value when the attribute is read" do + record = DummyClass.record(id: 1) do + self.gender = "Not disclosed" + end + + expect(record.gender).to eq("Not disclosed") + end + end end describe ".all" do