Friendly attributes pattern in Ruby

brunosutic | 84 points

This is...not for me. It follows a big pattern in Ruby/Rails culture where to understand the code, you first have to understand the magic. And, it's never all that obvious where to go to try and understand the magic, because the magic itself has been imported by magic.

I once was hackathoning with a colleague who was trying to get me excited about Rails, and he said, "look how great this is -- if you want the idea of '1 day', you can just write `1.day`!". I opened up a irb to try it out, and it didn't work. We were both confused for a bit until he figured out that it was a Rails thing, not a Ruby thing. That Rails globally punches date/time methods into integers, which he thought was cool, and I thought was abhorrent. I asked, "okay, if I came across this code, how would I be able to know that it came from Rails?" He said, there wasn't any way to really trace a method to its source definition, you just kinda have to know, and I decided this whole thing was too much of a conflict with my mental model for how humans and code and computers should work together.

montroser | 2 hours ago

    plans = { 
      1.month => {standard: 10, pro: 50, enterprise: 100},
      1.year => {standard: 100, pro: 500, enterprise: 1000}
    }
    
    plans.each do |interval, details|
      details.each do |name, amount|
        Billing::Plan::Factory.find_or_create_by!(name: , interval:, amount:)
      end
    end
drzel | 4 hours ago

This is a perfect example of something that looks good in a demo but fails in a real product. Business logic and 'packages' are never this clean or simple.

Putting this kind of type-based 'magic' in the code is a bad decision that will bite you very soon. It optimizes for being 'cute' rather than being clear and maintainable, and that's a trade-off that almost never pays off.

kburman | 7 hours ago

When I say Ruby is inefficient it’s not just the language, it’s stuff like this. I don’t fault the author but this kind of stuff is endemic.

This way of handling attributes is monumentally less efficient than just using keyword attributes, which are optimized by the runtime.

Unfortunately you’ll find this is every Ruby code base: tiny readability improvements that are performing allocations and wasting cycles for no real reason other than looking better.

I’ve certainly done that and it’s expected, efficient code looks “weird”. A regular “each” loop that looks complicated will be transformed into multiple array method chaining, allocating the same array many times. If you don’t do it someone else will.

dlisboa | 7 hours ago

Feels odd that two feature-equivalent plans are segregated with neighboring duplicates into monthly and yearly branches. I would consider monthly Enterprise & yearly Enterprise the same plan, with modified cost & billing frequency.

some1else | 5 hours ago

I don't really like the API design. Perhaps in the rails-world this makes sense, but it looks really strange to me.

    Billing::Plan::Factory.find_or_create_by!(
      name: :pro,
      interval: 1.month,
      amount: 50
    )
It is not only the verbosity or use of trailing '!' in a method for no real reason, IMO, but also things such as "1.month". I understand that rails thrives as a DSL, but to me having a method such as .month on an Integer, is simply wrong. Same with HashWithIndifferentAccess - I understand the point, to not have to care whether a key is a String or a Symbol, but it is simply the wrong way to think about this. People who use HashWithIndifferentAccess do not understand Symbols.
shevy-java | 8 hours ago

"This code should look less like it does what it's doing", that's the Ruby Way™.

PufPufPuf | 3 hours ago

I'll add another cautionary word in with everyone else who is panning this implementation.

This is just using operator overloading to determine keywords, but it locks you out of ever using the same type twice in your signature. Notice that :usd turns into a name. What?

This is cute, but has no place in a professional software interface.

nvader | 26 minutes ago

this is very unnecessary. Arrays and maps transformations are really easy and concise in core ruby already, one line of map, to_h or whatever.

codesnik | 8 hours ago

Off-topic, but unlike the example pricing plans, don’t make your SaaS’s “standard” plan $10/month. If you want a place to start, start with $50/month.

Or, as Patrick McKenzie used to tell us over and over, “charge more”.

(Yes, yes, I know some situations, customers, product, thinking, etc are different. But with broad brushstrokes, my advice is to not even entertain such a low price.)

stevoski | 7 hours ago

Yikes. This means that you’ll have 1000 micro-DSLs sprinkled all over your codebase, which will become unreadable and lead to confusion/accidents. Better to stick with good ol’ key-value labelling.

dudeinjapan | 9 hours ago

[dead]

sfgvvxsfccdd | 4 hours ago