using dry-struct for mutable entities

I’ve been thinking about using Dry::Struct for entities, but they must be mutable.

To set the attributes, post-initialization, from within the object I can use @attributes[:key] = 'whatever' but I’m guessing this isn’t part of the public API, so subject to breaking in future releases.

This is an example of what I want to do:

class Issue < Dry::Struct
  include Types

  attribute :title, String
  attribute? :resolved_at, HappenedAt
  attribute? :resolved_by, UserId

  def resolve(by:, at:)
    @attributes[:resolved_at] = at
    @attributes[:resolved_by] = by.id
    self
  end

  def resolved?
    !resolved_at.nil?
  end
end

issue = Issue.new(title: 'Shipment Delayed')
issue.resolve(by: current_user, at: Time.now  )

Maybe this is outside the use case for dry-struct?

Dry::Struct is designed to be immutable. This means that changes to the data use a CoW strategy instead of mutation.

class Issue < Dry::Struct
  include Types

  attribute :title, String
  attribute? :resolved_at, HappenedAt
  attribute? :resolved_by, UserId

  def resolve(by:, at:)
    return self if resolved_at && resolved_by
    new(resolved_at: at, resolved_by: by.id)
  end

  def resolved?
    !resolved_at.nil?
  end
end

issue = Issue.new(title: 'Shipment Delayed')
issue = issue.resolve(by: current_user, at: Time.now)
2 Likes

Thanks for the clarification, it feels like Clojure’s persistent data structures in that respect.

    def resolve(by:, at:)
      new(attributes.merge(
        resolved_at: at,
        resolved_by: by.id
      ))
    end

There is no need to explicitly merge attributes, Struct#new will do that for you.

2 Likes