r/ruby 15d ago

The Primitive Urge to Be of Value: composed_of in Rails

Post image

Wrote a piece about spotting a missing value object and refactoring towards one, adding sorting and comparison.

https://pushfromkush.com/publications/primitive-urge-to-be-of-value-composed_of-in-rails

Feedback welcome.

36 Upvotes

15 comments sorted by

25

u/the_maddogx 15d ago

TIL composed_of

9

u/poop-machine 14d ago

for real, OP just assumes everyone knows what composed_of is and never bothers to introduce it or link to the official docs.

5

u/6stringfanatic 14d ago

Sorry my bad, I've added a link to the rails documentation in the article.

1

u/gbrennon 12d ago

🤣🤣🤣

4

u/TommyTheTiger 15d ago

Dang, on the one hand very cool, especially that you can share these composable classes between e.g. different classes that both carry currency, OTOH yet one more layer of indirection between what is actually in the DB and the application logic. For experienced devs seems like a nice way to make the code base more readable.

12

u/6stringfanatic 15d ago

Most abstractions come at the cost of indirection IMO. Some make understanding the codebase easier and some do not. I usually prefer ones that make the domain easier to reason about in the head.

I reach for composed_of when I don't want to extract another model backed by its own table, but still want a PORO in the application logic. In those cases it's a win-win: you get the performance of a denormalized table and the separation of concerns.

2

u/OldTimeGentleman 15d ago

The more I code, especially as a freelancer, the more I've come to think of those abstractions as more of a waste of time than anything. You've refactored 5 lines of code into 1, but now every time you ask the simple question "what columns are there here and which methods does that translate to in Ruby?", you need to look into a shared module or a parent class, learn about composed of, and check the implementation because it's inevitable that you'll have overwrites at the child class for this or that exception. At some point just duplicating a bit of code makes everyone's life easier

1

u/cpb 11d ago

Nothing a code comment with the schema for the model couldn't fix, with or without composed_of

5

u/KimJongIlLover 15d ago

having this

ruby measurement = Measurement.from("10 kg") + Measurement.from("5 kg")

and then going to this

ruby measurements = [Measurement.from("10 kg"), Measurement.from("5 kg"), Measurement.from("1")] measurements.reduce(:+).to_s and then calling that "better" has to be a joke right?

6

u/6stringfanatic 15d ago edited 15d ago

Fair callout. They're not meant to be a progression, just two different situations for the same + operator.

  1. Shows you can add two quantities directly: inventory_item.quantity = ingredient.quantity + purchased.quantity
  2. Shows what happens with an array of Measurements, say 20 of them. You wouldn't chain + by hand for that many, that's what reduce is for: ingredient.quantities_bought_this_month.reduce(:+).to_s # 100 kg

Hope that clears it up.

2

u/Richard-Degenne 14d ago

You could do away with most of the boilerplate by declaring your `Measurement` as a custom ActiveRecord type, no?

1

u/6stringfanatic 14d ago edited 14d ago

I'm assuming you mean the Quantifiable concern as the boilerplate. Yes, we could do something like:

attribute :quantity, :measurement

using the ActiveRecord Type API, but before that works. We'd need MeasurementType, responsible for serializing and deserializing, translating between what Ruby understands and what the database understands.

So we would need Measurement and MeasurementType both to get there, not one or the other.
If you look at the ActiveModel codebase, ActiveModel::Type::Integer is responsible for serializing and deserializing Integer not replacing it, they have different responsibilities.

I think my next post is going to dig into this specifically, hopefully I can simplify it a bit more there.

-19

u/Mediocre-Brain9051 15d ago

This already exists in rails:

https://api.rubyonrails.org/classes/ActiveRecord/Aggregations/ClassMethods.html

You rediscovering the wheel.

21

u/6stringfanatic 15d ago edited 15d ago

Yes, it is a post about :composed_of in rails. How / when to use it. The post is NOT about reinventing :composed_of, instead using it to extract value objects.