# What's the right way to set an attribute for a Hanami Struct (ROM::Struct)?

**URL:** <https://discourse.hanakai.org/t/whats-the-right-way-to-set-an-attribute-for-a-hanami-struct-rom-struct/1035>\
**Category:** questions\
**Created:** [September 24, 2024, 8:57am UTC](https://discourse.hanakai.org/t/whats-the-right-way-to-set-an-attribute-for-a-hanami-struct-rom-struct/1035 "2024-09-24T08:57:54Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![wuarmin](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.hanakai.org/wuarmin/32/548_2.png) [@wuarmin](https://discourse.hanakai.org/u/wuarmin)\
**Post date:** [September 24, 2024, 8:57am UTC](https://discourse.hanakai.org/t/whats-the-right-way-to-set-an-attribute-for-a-hanami-struct-rom-struct/1035/1 "2024-09-24T08:57:54Z")

</div>

Hey 👋 ,  
I ask this because in certain situations it may be necessary to set i.e. a relation property for an object without reloading it from the DB.

```ruby
# relation
module MySlice
  module Relations
    class Orders < MySlice::DB::Relation
      schema :orders, infer: true do
        associations do
          has_many :add_ons
        end
      end
    end
  end
end

# struct
module MySlice
  module Structs
    class Order < MySlice::DB::Struct
    end
  end
end

```

```ruby
# pseudo-code
transaction do 
   order = step create_order
   order = step create_addons(order)
end

def create_add_ons(order)
  # after add_on creation, I do not want to reload
  # the order with the included add_ons

  # Approach 1
  # The following should actually be possible with dry struct, but does not
  # cause the order-object was not loaded with combine(:add_ons)
  order.new(add_ons: created_add_ons)

  # Approach 2
  # feels cumbersome
  # Furthermore the Order struct has to be changed to make this work
  order.add_ons = created_add_ons
  order
end

```

Do you know another way?  
Thanks

---

<div class="post-metadata">

**Author:** ![alassek](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.hanakai.org/alassek/32/532_2.png) [@alassek](https://discourse.hanakai.org/u/alassek)\
**Post date:** [September 24, 2024, 5:25pm UTC](https://discourse.hanakai.org/t/whats-the-right-way-to-set-an-attribute-for-a-hanami-struct-rom-struct/1035/2 "2024-09-24T17:25:45Z")

</div>

ROM structs are immutable structures, which means making a change to their attributes is _copy-on-write_.

This will not work, because the struct does not allow mutations.

```ruby
order.add_ons = created_add_ons

```

This is close, but you discard the new `order`.

```ruby
order.new(add_ons: created_add_ons)

```

Instead, do this:

```ruby
order = order.new(add_ons: created_add_ons)

```

---

<div class="post-metadata">

**Author:** ![wuarmin](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.hanakai.org/wuarmin/32/548_2.png) [@wuarmin](https://discourse.hanakai.org/u/wuarmin)\
**Post date:** [September 24, 2024, 8:26pm UTC](https://discourse.hanakai.org/t/whats-the-right-way-to-set-an-attribute-for-a-hanami-struct-rom-struct/1035/3 "2024-09-24T20:26:02Z")

</div>

Thanks for your response.  
Discarding the `order` variable is only a problem in pseudo code. It does not work even if it is not discarded. `add_ons` is a relation that can be loaded with `combine`. The `order` class here has no `add_ons` attribute, so the new `order` also contains no `add_ons`. Do you think this could be changed at `ROM::Struct`?

---

<div class="post-metadata">

**Author:** ![alassek](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.hanakai.org/alassek/32/532_2.png) [@alassek](https://discourse.hanakai.org/u/alassek)\
**Post date:** [September 24, 2024, 8:56pm UTC](https://discourse.hanakai.org/t/whats-the-right-way-to-set-an-attribute-for-a-hanami-struct-rom-struct/1035/4 "2024-09-24T20:56:23Z")

</div>

Okay I believe I understand your question now. You want to take one struct schema, an “order”, and produce an entirely new schema that contains “add\_ons” which are a relational association. In ROM’s design, these are considered entirely different structs.

This should absolutely be possible, since this is literally what Repository does. I will have to think about it and look into the code though. I have not actually done this myself.

In the interests of treating these structs as generic DTOs in the system, I believe this is a pretty solid use-case that will likely come up again.

---

<div class="post-metadata">

**Author:** ![alassek](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.hanakai.org/alassek/32/532_2.png) [@alassek](https://discourse.hanakai.org/u/alassek)\
**Post date:** [September 25, 2024, 5:50pm UTC](https://discourse.hanakai.org/t/whats-the-right-way-to-set-an-attribute-for-a-hanami-struct-rom-struct/1035/5 "2024-09-25T17:50:47Z")

</div>

The process of turning a Relation dataset into a struct is very, very complicated. I don’t think it’s fruitful to attempt using that plumbing directly.

But in the end, all ROM structs are just [dry-struct](https://dry-rb.org/gems/dry-struct/1.6/) objects. So here’s how we might approach it from that direction:

```ruby
order_with_addons = Class.new(order.class) { attribute :add_ons, Types::Array.of(ROM::Struct::AddOn) }
order = order_with_addons.new({ **order, add_ons: created_add_ons })

```

---

<div class="post-metadata">

**Author:** ![wuarmin](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.hanakai.org/wuarmin/32/548_2.png) [@wuarmin](https://discourse.hanakai.org/u/wuarmin)\
**Post date:** [September 27, 2024, 7:28am UTC](https://discourse.hanakai.org/t/whats-the-right-way-to-set-an-attribute-for-a-hanami-struct-rom-struct/1035/6 "2024-09-27T07:28:42Z")

</div>

Thanks for taking a look at this.

Do you see a good way to integrate these features into hanami-struct, or should this be left in the user area?

I also believe that some people will come across this use case. An elegant solution would be great.

For now, to work around this, I load the created order combined with all relations, I want to set in the flow. This works quite well.

---

<div class="post-metadata">

**Author:** ![alassek](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.hanakai.org/alassek/32/532_2.png) [@alassek](https://discourse.hanakai.org/u/alassek)\
**Post date:** [September 27, 2024, 8:07pm UTC](https://discourse.hanakai.org/t/whats-the-right-way-to-set-an-attribute-for-a-hanami-struct-rom-struct/1035/7 "2024-09-27T20:07:24Z")

</div>

Perhaps @solnic can weigh in on how ROM was intended to work in this case, before we go so far as to build an abstraction for it.

---

<div class="post-metadata">

**Author:** ![solnic](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.hanakai.org/solnic/32/757_2.png) [@solnic](https://discourse.hanakai.org/u/solnic)\
**Post date:** [September 29, 2024, 3:46pm UTC](https://discourse.hanakai.org/t/whats-the-right-way-to-set-an-attribute-for-a-hanami-struct-rom-struct/1035/8 "2024-09-29T15:46:36Z")

</div>

I recommend thinking about changing data separately from reading data. If you want to write something to the database, you want to use a separate data representation. Using plain hashes with changesets should suffice.

---

<div class="post-metadata">

**Author:** ![wuarmin](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.hanakai.org/wuarmin/32/548_2.png) [@wuarmin](https://discourse.hanakai.org/u/wuarmin)\
**Post date:** [September 30, 2024, 6:17am UTC](https://discourse.hanakai.org/t/whats-the-right-way-to-set-an-attribute-for-a-hanami-struct-rom-struct/1035/9 "2024-09-30T06:17:20Z")

</div>

Thanks @solnic for your input.

This is not about writing the data back to the DB. It is about the possibility of using a struct in such a way that data is added to it that is relevant in the further flow.

Using a hash may be a way out here, but the struct offers many advantages.

```ruby
# pseudo-code
transaction do 
   order = step create_order
   order = step create_addons(order)
   # use enriched order in further flow
end

def create_addons(order)
  # create addons and return order with addons
  order.new(addons: created_addons)
end

```

---

<div class="post-metadata">

**Author:** ![alassek](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.hanakai.org/alassek/32/532_2.png) [@alassek](https://discourse.hanakai.org/u/alassek)\
**Post date:** [September 30, 2024, 6:20pm UTC](https://discourse.hanakai.org/t/whats-the-right-way-to-set-an-attribute-for-a-hanami-struct-rom-struct/1035/10 "2024-09-30T18:20:53Z")

</div>

If we were querying an order from the db, it would be simple:

```ruby
order = orders_repo.orders.by_pk(id).combine(:addons).one

```

The question, as I understand it, is: what if we are creating these two records separately, but want to combine them into one struct after-the-fact?

It seems less than ideal that we should have to make another round-trip to the database when we already have the data, we just need to combine it outside the context of a dataset.

There is a further need for this because changesets don’t support combines presently.
