Home > Enterprise >  composed_of updates ActiveRecord attributes
composed_of updates ActiveRecord attributes

Time:01-06

Example object:

class Foo < ApplicationRecord
  composed_of :bar, 
               mapping: [%w[user_id user_id], %w[color color]],
               converter: proc { |user_id, color| Bar.new(user_id, color) }
end

class Bar
  include ActiveModel::Model
  attr_accessor :user_id, :color

  def initialize(user_id, color)
    @user_id = user_id
    @color = color
  end
  
  def change_to_blue
    self.color = "blue"
  end
end 

Is there a way to make it so if I update the bar object's color it also updates the Foo color?

Example

foo = Foo.last
foo.color = "yellow"
foo.bar.change_to_blue
puts foo.color

Currently the above would result in "yellow" but was wondering if it was possible to create the linkage back to Foo so that result is "blue"? I want to be able to do Foo.save! and the color would be updated to "blue"

CodePudding user response:

The Docs explain this concept fairly well.

Active Record implements aggregation through a macro-like class method called composed_of for representing attributes as value objects.

Value objects are immutable and interchangeable objects that represent a given value...It's also important to treat the value objects as immutable. Don't allow the [Bar] object to have its [color] changed after creation. Create a new [Bar] object with the new value instead.

Additionally I believe your converter is incorrect as the converter is used in assignment and thus only passes a single value.

So I think the below is actually what you are looking for to change the color.

class Foo < ApplicationRecord
  composed_of :bar, 
               mapping: [%w[user_id user_id], %w[color color]],
               constructor: proc { |user_id, color| Bar.new(user_id, color) }
end

class Bar
  include ActiveModel::Model
  attr_reader :user_id, :color

  def initialize(user_id, color)
    @user_id = user_id
    @color = color
  end
  
  def change_to_blue
    self.class.new(user_id,'blue')
  end
end 

and then

foo = Foo.last
foo.color = "yellow"
foo.bar = foo.bar.change_to_blue
puts foo.color
  • Related