[HN Gopher] Friendly attributes pattern in Ruby
       ___________________________________________________________________
        
       Friendly attributes pattern in Ruby
        
       Author : brunosutic
       Score  : 92 points
       Date   : 2025-11-02 19:02 UTC (6 days ago)
        
 (HTM) web link (brunosutic.com)
 (TXT) w3m dump (brunosutic.com)
        
       | dudeinjapan wrote:
       | 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.
        
         | rubyn00bie wrote:
         | Yeah, this is honestly the sort of thing I grew to hate in
         | Ruby. It looks cute, but all it does is create more cruft. Good
         | ol' boring keys are just fine, expressive enough, and are very
         | unlikely to cause problems. This feels like it's attempting to
         | solve a problem that does not exist.
        
           | onli wrote:
           | The advantage is the amount of code minimized and not using a
           | generic factory pattern. But that probably can be achieved
           | with a bit less magic...
        
           | shevy-java wrote:
           | > this is honestly the sort of thing I grew to hate in Ruby
           | 
           | But nobody forces you to use a DSL such as rails, so I am not
           | sure why ruby should be hated for this when it is a rails dev
           | who does that.
           | 
           | The blog has much more to do with rails than ruby; such API
           | design is really strange.
           | 
           | I don't think this design causes problems as such, but it is
           | too verbose and way too ugly. To me it seems that they are
           | just shuffling data structures around; that could even be
           | solved via yaml files.
        
             | whstl wrote:
             | Working in a team means you are kind of forced to use what
             | the team wants.
             | 
             | Of course you can try to convince them otherwise, or just
             | be an asshole and mass-refactor to remove the DSLs.
             | 
             | But this kind of code is part of Ruby's culture now.
             | 
             | The simple answer for anyone that doesn't like this style
             | is to leave Rails and Ruby for people who enjoy it.
             | 
             | It's fine to hate it and want to distance yourself from it.
        
         | molf wrote:
         | This is a philosophy. One which many people that write Ruby
         | subscribe to. The fundamental idea is: create a DSL that makes
         | it very easy to implement your application. It is what made
         | Rails different when it was created: it is a DSL that makes
         | expressing web applications easy.
         | 
         | I don't know its history well enough, but it seems to originate
         | from Lisp. PG wrote about it before [1].
         | 
         | It can result in code that is extremely easy to read and reason
         | about. It can also be incredibly messy. I have seen lots of
         | examples of both over the years.
         | 
         | It is the polar opposite of Go's philosophy (be explicit &
         | favour predictability across all codebases over
         | expressiveness).
         | 
         | [1]: https://paulgraham.com/progbot.html
        
           | dudeinjapan wrote:
           | If there is one DSL which is a central abstraction of one's
           | entire app, used in 100s of places--this is fine.
           | 
           | If there is a DSL such as Rails' URL routing, which will be
           | the same in every app--this is also fine.
           | 
           | When one makes 100s of micro-DSLs for object creation, that
           | are only ever used in one or two places--this is pure
           | madness.
        
       | shevy-java wrote:
       | 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.
        
         | byroot wrote:
         | > I understand that rails thrives as a DSL, but to me having a
         | method such as .month on an Integer, is simply wrong
         | 
         | It's not that different from `1.times` or `90.chr` which are
         | vanilla Ruby.
         | 
         | > HashWithIndifferentAccess
         | 
         | HashWithIndifferentAccess was an unfortunate necessity to avoid
         | DOS attacks when Symbols used to be immortal. There's no longer
         | a reason to use it today, except for backward compatibility.
        
         | Lukas_Skywalker wrote:
         | The exclamation mark has a reason: if the newly created records
         | fails validations, an exception is raised. Without the
         | exclamation mark, the error is silenced (and the method returns
         | a falsey value). This is a convention across Rails.
         | 
         | Ruby itself mostly uses it for mutating methods (e.g.
         | #gsub("a", "b") replaces the character a with b in a string and
         | returns a new string, but #gsub!("a", "b") mutates the
         | original.
        
         | dagi3d wrote:
         | when *everything* is an object this kind of syntax makes
         | absolutely sense and is quite convenient
        
           | apsurd wrote:
           | my first thought too, as a rubyist.
           | 
           | Yes, "everything is an object" is an essential insight to
           | understanding ruby.
        
         | culi wrote:
         | This is a typical API design in Ruby, but the post is about a
         | somewhat novel API design than what you're pointing out. To
         | address some of your points:
         | 
         | The exclamation mark is a convention. It is used whenever a
         | method could possibly result in an exception being raised.
         | Sometimes it's instead used for non-idempotent methods.
         | 
         | "3.days", etc are Rails things. A lot of non-Rubyists don't
         | like it but once you use it for long enough you tend to really
         | grow to it.
         | 
         | As for HashWithIndifferentAccess, yes this is generally
         | acknowledged as a mistake in Ruby's design and is rarely used
         | in my experience. Originally, all Ruby hashes were HWIA. When
         | they finally realized this was a design mistake they had to
         | create HWIA for some level of backwards compatibility
        
         | sodapopcan wrote:
         | `1 + 1` in Ruby is syntactic sugar for `1.+(1)`. Nothing wrong
         | about it at all, it's just different from what you're
         | apparently used to. This type of thing isn't even unique to
         | Ruby or even OOP.
        
       | codesnik wrote:
       | 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.
        
       | kburman wrote:
       | 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.
        
         | nkrisc wrote:
         | They do say they use this in their real production code.
        
           | kburman wrote:
           | Just because it's in their production code doesn't mean it's
           | not a ticking time bomb.
        
             | nurettin wrote:
             | You are still talking about the three line find_or_create
             | in this article, right?
        
         | brunosutic wrote:
         | Hi, I'm the author of the article and the software library. I
         | confirm I actually do use the examples from the article in my
         | code.
         | 
         | Here's the example that runs in hundreds of integration tests:
         | expect(billing_pricing_plans).to eq billing_plans(
         | 1.month => [:free, :premium, :pro, :enterprise],         1.year
         | => [:free, :premium, :pro, :enterprise]       )
         | 
         | It asserts what plans the customers see on the pricing page.
        
           | kburman wrote:
           | There's a massive gap between that pattern and the real-world
           | complexity of billing. It's too much to cover in a comment,
           | but this link explains the actual nightmare -
           | https://www.getlago.com/blog/why-billing-systems-are-a-
           | night...
        
             | Lukas_Skywalker wrote:
             | We can't really tell that without knowing where the code is
             | used, no? It's not hard to imagine a test that checks the
             | following:                  bill = FactoryBot.create(:bill,
             | products: [])        expect(bill.currency).to eq("USD")
             | 
             | It doesn't cover all possibilities of all currencies, but
             | it doesn't need to. It covers the one case it needs to
             | test.
        
             | brunosutic wrote:
             | That link describes billing problems of a neobank... I
             | mean, yes, there's a big gap between my test helpers and
             | financial institution's problems - to the point it's not
             | related at all.
             | 
             | But, in principle I agree billing, even the simple SaaS
             | stuff, is much harder than most people expect it to be in
             | 2025. My product (linked in the original article) is based
             | completely on Stripe Billing - and it is still very hard to
             | avoid all the footguns.
             | 
             | For people wondering, I even have an example how wrong it
             | can go: I "audited" a successful SaaS I know uses custom
             | Stripe billing. I paid $30 for a starter plan, but was able
             | to "upgrade" to $2k plain for free. Here's the full video:
             | https://www.youtube.com/watch?v=YuXp7V4nanU
        
       | stevoski wrote:
       | 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.)
        
         | anamexis wrote:
         | This is such a broad generalization as to be useless. I use
         | several pieces of software that are around $10/month which
         | there's no way in hell I would pay $50 for.
        
         | PufPufPuf wrote:
         | As an end user, there's no way I'd pay $50/month for any SaaS.
        
           | stevoski wrote:
           | Lots of people feel the same.
           | 
           | Which leads me to another piece of advice: don't do B2C. Sell
           | to businesses who will be far more willing to pay higher
           | prices, will churn at a lower rate, and will - in general -
           | require less support.
        
       | dlisboa wrote:
       | 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.
        
         | brunosutic wrote:
         | The usual response to this complaint in the Ruby/Rails
         | community is that optimizing for nanoseconds, or even
         | milliseconds doesn't matter when the same operation also
         | involves multiple database queries or API calls.
         | 
         | Let's take this example from the article:
         | Billing::Plan.find_or_create_all_by_attrs!(         1.month =>
         | {standard: 10, pro: 50, enterprise: 100},         1.year =>
         | {standard: 100, pro: 500, enterprise: 1000}       )
         | 
         | This ensures six billing plans are created. That means 6 DB
         | queries and 6 Stripe API queries, at a minimum.
        
           | dlisboa wrote:
           | > The usual response to this complaint in the Ruby/Rails
           | community is that optimizing for nanoseconds, or even
           | milliseconds doesn't matter when the same operation also
           | involves multiple database queries or API calls
           | 
           | The problem with that logic is that it's pervasive: people
           | have that same attitude everywhere even if no IO is being
           | done. That's how we get multi gigabyte processes.
           | 
           | The whole language (and Rails) also pushes you towards a less
           | efficient path. For instance you're probably iterating over
           | those six plans and inserting them individually in the DB.
           | Another approach would've been to accumulate all of them in
           | memory then build and perform a single query. That's not
           | something people really consider because it's "micro"
           | optimization and makes the code look worse. But if you miss
           | out on hundreds of these micro optimizations then you get a
           | worse system.
           | 
           | In a general sense optimizing Ruby is indeed futile: any
           | optimization is dwarfed by just choosing a different
           | language.
           | 
           | I say all this as someone who has worked with it for two
           | decades, I like the language, it's just laughably
           | inefficient.
        
             | radanskoric wrote:
             | Have you used it over the last few years? It has it been
             | rapidly improving, mainly because Shopify put a team full
             | time on it. It doesn't take a lot of people to optimize a
             | VM/interpreter it just has to be the right people.
             | 
             | And the question is always "fast enough for what?"
             | Different languages are more suitable for different types
             | of projects. I wouldn't code a rendering engine in Ruby but
             | for web apps it's amazing.
        
               | dlisboa wrote:
               | Yes, every web app I've worked on the past ~18 years has
               | been with Rails. I've seen it all except an efficient
               | app. Sure, Ruby and Rails never bankrupted these
               | companies but they'd all have been better off with
               | something else. Certain cloud bills would've been much
               | smaller for sure.
               | 
               | Those optimizations to the VM are just very workload
               | specific and become less relevant today when you're using
               | containers and fractional CPU/mem. It also doesn't take
               | much for a dev to write the wrong code and make them
               | irrelevant again. Even if you get everything right you're
               | leaving so much performance on the table it feels like
               | crumbs.
               | 
               | For small web apps Rails is fine though. I just never
               | worked on one. The issue is perhaps no one threw the code
               | away when it got big.
        
               | radanskoric wrote:
               | Can you explain why you say they would be better off?
               | What else would be a better choice and why?
        
               | whstl wrote:
               | Not GP but I can answer:
               | 
               | Rails apps can get very expensive server wise because the
               | "IO is slow anyways" attitude means more servers will be
               | needed to serve the same amount of requests. For a
               | specific bad case I worked at, the cloud bill was the
               | same cost of 15 senior developers. And it was an app
               | without external users (I was actually responsible for
               | the external parts of it, it was isolated and not in
               | Rails).
               | 
               | Excessive abstraction at the ORM can also make it
               | extremely difficult to optimize db queries, so each user
               | request can trigger way more DB queries than necessary,
               | and this will require more db power. I have seen this
               | happening over and over due to abstraction layers such as
               | Trailblazer, but anything that is too layered clean-code
               | style will cause issues and requires constant
               | observation. And refactoring is made difficult due to
               | "magic". Even LLMs might find it too much.
               | 
               | Another problem with the slowness is that it slows down
               | local development too. The biggest test suite I ever saw
               | took 2 hours to run in a 60-machine cluster, so 120 hours
               | of CI. Impossible to run locally, so major refactoring
               | was borderline impossible without a huge feedback cycle.
               | 
               | The solution for the slow development ends up being
               | hiring more developers, of course, with each one
               | responsible for a smaller part of the app. In other
               | companies these kind of features I saw would be written
               | by people over days, not by team over months.
               | 
               | The terseness of both Ruby and Rails is also IMO
               | countered by the culture of turning 10-line methods into
               | bigger classes and using methods and instance variables
               | instead of local variables. So it also hurts both
               | readability (because now you have 5x more lines than
               | needed) but also hurts optimization and stresses the
               | garbage collection. If you know this, you know. I have
               | seen this in code from North+Latin American, European and
               | Japanese companies, so it's not isolated cases. If you
               | don't know I can provide examples.
               | 
               | I have seen this happening with other tech too, of
               | course, but with Rails it happens much much faster IME.
               | 
               | It is also 100% preventable, of course, however a lot of
               | advice on how to prevent these problems will clash with
               | Ruby/Rails traditions and culture.
               | 
               | These are just examples out of personal experience, but
               | definitely not isolated cases IMO.
        
             | boredtofears wrote:
             | > For instance you're probably iterating over those six
             | plans and inserting them individually in the DB. Another
             | approach would've been to accumulate all of them in memory
             | then build and perform a single query. That's not something
             | people really consider because it's "micro" optimization
             | and makes the code look worse.
             | 
             | This same pitfall exists in every language. This has
             | nothing to do with Ruby.
        
               | AlphaSite wrote:
               | eh. Ruby makes it easy to do the wrong thing. imo.
        
               | boredtofears wrote:
               | Maybe, but this isn't one of the ways it does.
        
           | em-bee wrote:
           | i find this structure a bit odd. i would have gone for the
           | following pattern:
           | Billing::Plan.find_or_create_all_by_attrs!(
           | standard => {1.month: 10, 1.year: 100},           pro =>
           | {1.month: 50, 1.year: 500},           enterprise => {1.month:
           | 100, 1.year: 1000}         )
        
             | brunosutic wrote:
             | This is supported and would work with no implementation
             | changes. The "Friendly Attributes" idea is very flexible.
             | 
             | Just a small Ruby syntax correction for your example:
             | Billing::Plan.find_or_create_all_by_attrs!(
             | standard: {1.month => 10, 1.year => 100},         ...
             | )
        
           | whstl wrote:
           | This attitude towards wastefulness is how you have web apps
           | that could run in a single machine but struggle to run in a
           | server cluster.
           | 
           | And after a couple years even Postgres is struggling because
           | the amount of queries is too massive because of abstractions
           | that don't lend themselves to optimization.
           | 
           | Also it's how you have codebases that could be maintained by
           | two or three suddenly needing dozens because the testing
           | suite needs hours to run and people even celebrate when
           | there's no tests in sight.
           | 
           | Just anecdotal personal experience. But I saw this happening
           | inside at least 4 successful companies that started with
           | Rails but didn't care about those problems, and ended up
           | wanting/having to move to something else.
        
             | fny wrote:
             | The reality is most companies and products never blow bast
             | the point of needed to ditch Rails. The argument made at
             | the time was scaling horizontally is cheaper than hiring
             | new devs, and you probably will never need to scale that
             | much horizontally.
             | 
             | Test suite bloat is a different problem that stems from the
             | lack of incremental typing which I think is what ultimately
             | killed Ruby and Rails.
             | 
             | Any big Rails codebase can be a nightmare to grok unless
             | people have been diligent about documenting what different
             | methods return.
        
               | obiefernandez wrote:
               | Nothing has "killed Ruby on Rails".
               | 
               | Ridiculous comment.
        
               | fny wrote:
               | I'm honored to have you of all people make that comment.
               | 
               | But with all due respect the excitement and job market
               | for Ruby isn't anything close to what it used to be:
               | 
               | [0]: https://trends.google.com/trends/explore?date=all&q=
               | %2Fm%2F0...
        
             | cortesoft wrote:
             | Again, though, these bottlenecks are because of how the
             | system queries the database, not how methods are
             | dispatched.
             | 
             | I agree on the ORM abstractions causing huge performance
             | issues, but it has nothing to do with Ruby's dynamic method
             | declarations.
        
               | whstl wrote:
               | I'm not talking about method dispatch, I'm talking about
               | the _" usual response to this complaint in the Ruby/Rails
               | community"_.
        
       | some1else wrote:
       | 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.
        
         | brunosutic wrote:
         | > I would consider monthly Enterprise & yearly Enterprise the
         | same plan, with modified cost & billing frequency.
         | 
         | How would you then call the objects that store costs and
         | billing frequency? :)
         | 
         | Here's what Stripe uses:
         | 
         | - Product: "describes the goods or services". This is where you
         | define a (plan) name and features.
         | 
         | - Price: defines the amount, currency, and (optional) billing
         | interval. Since interval is optional, Prices can be used to
         | define both recurring, and one-off purchases.
         | 
         | Technically, using Prices for recurring, and one-off payments
         | is a brilliant idea. The problem is, no one refers to recurring
         | payments as "prices". Everyone calls a "$50 per year" option a
         | "plan".
        
       | sfgvvxsfccdd wrote:
       | Haters gonna hate. My take: DSLs are a useful way to make code
       | easier to read, and more importantly easier to write _correctly_.
       | Exploring this space and sharing your learnings is useful and
       | valuable.
        
         | culi wrote:
         | I wouldn't call this a DSL
        
         | brunosutic wrote:
         | Article author here - thank you for putting it this way. This
         | is exactly the attitude I wanted to convey: it's something I
         | tried and really liked for this specific use case. I shared
         | because I hope it might inspire others.
         | 
         | "Friendly Attributes" is _not_ the  "new way", _not_ to be used
         | "everywhere now", does _not_ "apply to all scenarios".
         | 
         | If you like it, maybe you'll use it once in the next five years
         | when the opportunity arises.
        
         | AlphaSite wrote:
         | Ruby is a language that optimizes for the local maxima at the
         | cost of the global maxima.
         | 
         | Now every library, company or code base has its own pattern and
         | you have to learn its pit falls. Better to learn once, cry once
         | and just deal with it imo.
         | 
         | As they say, good enough is the enemy of perfection.
        
       | drzel wrote:
       | 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
        
         | bradly wrote:
         | I think this is the pattern I would reach for as well,
         | separating the data from the execution. Being declarative about
         | the plans (either with a config file, db backend, or simply a
         | PORO) allows the plans themselves to be agnostic to how they
         | are used and leaves you room to write a clean API for their
         | creation without mixing in their definition.
         | 
         | Also ActiveSupport has Object#with_options which has a similar
         | intent, but I rarely ever see it used in codebases.
        
         | Footkerchief wrote:
         | Exactly. Use a fancy expressive structure if you want, but
         | don't try to abstract away the mapping between that and the
         | general-purpose code that it relies on. "Each domain has its
         | own rules"? How would I even know where to look for those?
        
       | PufPufPuf wrote:
       | "This code should look less like it does what it's doing", that's
       | the Ruby Way(tm).
        
       | montroser wrote:
       | 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.
        
         | jaredcwhite wrote:
         | 1.method(:day).source_location <= your friend was wrong _shrug_
         | 
         | Look, I'm not a big fan of all of Rails' monkeypatching. That's
         | why I don't use Rails anymore, I use other Ruby frameworks like
         | Bridgetown, Roda, and Hanami. But there's definitely a way to
         | dive into the "magic" and find out what's going on.
        
           | montroser wrote:
           | Ah, that's good to know! Yeah, he was wrong about a lot of
           | stuff, so that adds up. He ended up in jail for a stint.
        
             | jaredcwhite wrote:
             | whoa
        
               | bradgessler wrote:
               | Quite the plot twist there
        
             | radanskoric wrote:
             | Woah, this thread escalated quickly.
        
         | isr wrote:
         | That's one thing which ruby unfortunately did not adopt from
         | Smalltalk. In Smalltalk (at least, in the dialects I'm familiar
         | with), the "method categories" metadata is used to signal that
         | we're adding new methods (or overwriting existing ones) to
         | classes that are outside the scope of this package (ie: classes
         | you didn't create as part of your app).
         | 
         | That way, it's easy to trace, forwards (from package to all the
         | methods it introduces) & backwards (from method to package),
         | who introduced a method, where, and why.
         | 
         | Other than that, I think a lot of this aversion to "ruby magic"
         | is a bit overblown. The ability to cleanly remold any part of
         | the system with minimal friction, to suit the app you're
         | building right now - that's a KEY part of what makes it
         | special.
         | 
         | Its like all these polemics warning wannabe lispers away from
         | using macros. Lisp, Smalltalk, and ruby, all give you very
         | powerful shotguns to express your creative ideas. If you can't
         | stop blowing your own foot off, then pick a different language
         | with a different paradigm.
        
       | nvader wrote:
       | 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.
        
         | culi wrote:
         | > but it locks you out of ever using the same type twice in
         | your signature.
         | 
         | I don't see how you drew that conclusion. It seems to me the
         | author provided several examples of this not being the case.
         | Care to elucidate?
        
       | culi wrote:
       | So the underlying assumption is that there is always at least one
       | attribute that serves as a "discriminator" between the billing
       | plans, right? Is it possible to represent something like this
       | then?
       | 
       | ```rb                 [:red, 1.month, 10]       [:red, 1.year,
       | 120]       [:blue, 1.month, 120]       [:blue, 1.year,  300]
       | 
       | ```
       | 
       | Every possible attribute (name, interval, amount) has at least
       | two objects that share a value
        
         | brunosutic wrote:
         | Your input would work exactly as you wrote it if passed to
         | `Billing::Plan.find_or_create_all_by_attrs!`, just add commas
         | at the end of lines.
         | 
         | If you want to make it even shorter, you have a few options -
         | it really just comes down to preference:                 #
         | Option 1. my personal favorite, follows structure of       #
         | intervals and plans on a pricing page.       1.month => {red:
         | 10, blue: 120},       1.year => {red: 120, blue: 300}
         | # Option 2. this is fine too       red: {1.month => 10, 1.year
         | => 120},       blue: {1.month => 120, 1.year => 300}
         | # Option 3. possible and works, but hurts my brain, NOT
         | recommended       10 => {red: 1.month},       120 => {red:
         | 1.year, blue: 1.month},       300 => {blue: 1.year}
         | 
         | > there is always at least one attribute that serves as a
         | "discriminator" between the billing plans, right
         | 
         | Just a note: if you try to create two plans with the same
         | attributes, that would error because of ActiveRecord uniqueness
         | validations (and DB constraints). No point in having multiple
         | identical plans.
        
       ___________________________________________________________________
       (page generated 2025-11-08 23:01 UTC)