[HN Gopher] Ruby already solved my problem
       ___________________________________________________________________
        
       Ruby already solved my problem
        
       Author : joemasilotti
       Score  : 252 points
       Date   : 2025-11-07 18:45 UTC (1 days ago)
        
 (HTM) web link (newsletter.masilotti.com)
 (TXT) w3m dump (newsletter.masilotti.com)
        
       | iagooar wrote:
       | Ruby has a lot of these hidden gems (pun intended).
       | 
       | I wouldn't be as much in love with programming, if it wasn't for
       | Ruby. And although I use many other programming languages these
       | days, Ruby will forever have a special place in my heart.
        
         | matltc wrote:
         | Agreed. Was looking around for STL files so I could print a
         | ruby and put it on my desk.
         | 
         | Glad to see it's getting love on here recently.
        
           | amerine wrote:
           | I love this idea!! Any luck finding an STL or at least a 3d
           | model I can convert and copy your idea?
        
             | jonah wrote:
             | A quick search returned this:
             | https://sketchfab.com/3d-models/gemstone-
             | pack-68c4ec3dd23247...
             | 
             | Not _exactly_ the same cut, but might be good enough for
             | you?
        
           | graypegg wrote:
           | I gave it a try! I tried to get the odd-ball perfectly
           | circular cut and square dimensions, but I'm mostly just
           | eyeballing it. Haven't tried printing it yet, but I have some
           | nice red filament that I think is going to look good!
           | 
           | Here's a screenshot from inside FreeCAD:
           | 
           | https://f.toi.sh/rubygem-screenshot.png
           | 
           | A nice manifold solid:
           | 
           | https://f.toi.sh/rubygem.3mf / https://f.toi.sh/rubygem.stl
           | 
           | A terrifying non-manifold FreeCAD mess: (requires surface WB)
           | 
           | https://f.toi.sh/rubygem.FCStd
        
         | netghost wrote:
         | A long while back I wrote a bunch of articles covering some of
         | the standard library:
         | https://snakeshands.com/series/ruby_standard_library/
         | 
         | Ruby, and Ruby on Rails is a treasure trove of little handy
         | bits you can use if you just know where to look. I really miss
         | some aspects of ruby (I just don't have a chance to use it
         | these days).
        
           | dmix wrote:
           | I'm worried LLMs will make people ignore what's already there
           | and auto generate useless functions instead of using what's
           | there in Ruby/Rails. I've been using Rails for almost 20yrs
           | (on and off) and I can't count the number of times I did
           | something only to find out it was either natively supported
           | in a recent version... or at least a new best practice in
           | modern Rails.
           | 
           | You find the same thing with JS to an even higher degree, but
           | there's always 10 options in NPM and they all need to be
           | updated every year otherwise the other 20+ packages you
           | depend on can't be upgraded. There's a stark contrast in
           | maintenance overhead and DX between frontend and server side.
           | 
           | Even the rails JS libraries are very stable. Hotwire's
           | Stimulus hasn't had a release since 2023 and it always works
           | just fine. https://github.com/hotwired/stimulus/releases
        
             | pxc wrote:
             | > I'm worried LLMs will make people ignore what's already
             | there and auto generate useless functions instead of using
             | what's there in Ruby/Rails.
             | 
             | I think you're probably right. But fwiw, as a non-Rubyist
             | who values good style and is recently working in a one-off
             | Ruby codebase, I've found it easy to use LLMs to write Ruby
             | code that is idiomatic and leverages built-ins well. I use
             | ChatGPT 5 Thinking for this, and I don't let it generate
             | any code that I use directly. I ask it about ways to do
             | things, including stuff built-in to the stdlib, and
             | sometimes have it generate 3 or 4 implementations. Then I
             | compare them, consult the language docs or stdlib API docs
             | or the pickaxe book, and choose what seems the most stylish
             | or composable. I then write it out by hand, bearing in mind
             | what I just learned, and see what Rubocop has to say about
             | its style.
             | 
             | I wouldn't say LLMs have been essential in this, but
             | they've been pretty convenient. Because Ruby has relatively
             | a lot of special syntax, it's also nice to be able to
             | inquire directly about the meaning of some syntax in a bit
             | of generated code-- especially when it involves characters
             | that are (for some reason) ungoogleable, like an ampersand.
             | 
             | I think people who use LLMs to generate code and people
             | that embrace agentic coding AIs and "vibe coding" will
             | absolutely fall into the pattern you describe. But RTFMers
             | and developers who care about craftsmanship will probably
             | use LLMs as another discovery mechanisms for the stdlib,
             | popular Gems, and popular style conventions.
        
       | c-hendricks wrote:
       | title is actually "Ruby already solved my problem"
        
         | throwaway81523 wrote:
         | Thanks, that helped. My unspoken question when I saw the title
         | was "does that mean you now have two problems?".
        
       | adverbly wrote:
       | If you ignore performance and mathematical elegance and safety
       | and just look at how much a language lets you get away with from
       | a productivity standpoint, I think Ruby is a pretty standout
       | winner and nobody else even comes close really...
       | 
       | Very clear APIs and syntax(with the possible exception of blocks
       | which can be weird because they aren't quite functions), and tons
       | of raw metaprogramming powers.
       | 
       | You can argue it sacrifices too much of the other things to
       | deliver on these things, but it's hard to argue against it doing
       | well at what it optimizes for!
        
         | jnovek wrote:
         | I love writing Ruby. It's one of the most pleasant and elegant
         | languages I've used... but the footguns it comes equipped with
         | rival those of Perl.
        
           | ajoski9 wrote:
           | Agreed. Also pretty to look at IMHO
        
             | IshKebab wrote:
             | But awful to navigate - the terse syntax combined with lack
             | of static types and regular use of generated identifiers
             | turns large codebases into Where's Wally. Good luck finding
             | where the `process` function is called from. You can't even
             | search for `process(` like you can in most languages.
        
               | 15155 wrote:
               | You can search for "def process" - it's inheritance and
               | metaprogramming that make finding the underlying
               | implementation difficult.
        
               | Someone wrote:
               | >> Good luck finding where the `process` function is
               | called from. You can't even search for `process(` like
               | you can in most languages.
               | 
               | > You can search for "def process"
               | 
               | That tells you where it is defined, not where it is
               | called from.
        
               | 15155 wrote:
               | True. `caller` tells you where it is called from, during
               | runtime.
        
               | speleding wrote:
               | Well, just add "puts caller" in the function to find out.
               | You can do this in your own code, but also you can also
               | just briefly patch the library you're working with, if
               | that's where the process is.
               | 
               | By the way, the generated identifiers are more a rails
               | thing than a ruby thing.
        
               | IshKebab wrote:
               | Doesn't that just tell you the functions that happen to
               | call it when you run a program? That's not remotely as
               | good as just getting a complete list at the click of a
               | button.
        
               | lloeki wrote:
               | > lack of static types
               | 
               | RBS is a thing; we use it extensively.
               | 
               | > Good luck finding where the `process` function is
               | called from
               | 
               | I don't use it like that but I seem to recall RBS itself
               | has a query mechanism and can answer these kind of
               | questions.
        
               | IshKebab wrote:
               | > we use it extensively
               | 
               | Who is "we"? The Ruby projects I use are Asciidoctor and
               | Gitlab and neither of them use it.
               | 
               | Also putting types in a totally separate file is an
               | abysmal UX.
        
           | tenacious_tuna wrote:
           | > I love writing Ruby
           | 
           | I work at an enterprise running Rails (you've heard of us, if
           | you're in North America). Discussions about rails abound, and
           | my opinion has distilled into "I love writing Ruby, I loathe
           | reading it"
        
             | asa400 wrote:
             | Same! I had a job at a shop with a monolithic Rails app
             | where I had so much trouble understanding the codebase I
             | almost quit the industry entirely.
             | 
             | Maybe I am just stupid when it comes to reading Ruby/Rails
             | or maybe that codebase was uniquely awful, but it was
             | ~impossible to figure out where things were defined or how
             | data moved through the system. A huge ball of mutable state
             | that was defined at runtime.
             | 
             | When people say "I love writing Ruby" what I hear is "I
             | love writing greenfield Ruby". Everybody loves writing
             | greenfield code! The difference between greenfield and
             | brownfield Ruby is stark, in my experience.
             | 
             | And to be clear I do not hate Ruby. It got me into the
             | industry, it taught me a lot, it just optimizes for a set
             | of values that I don't happen to share anymore, which is
             | fine.
        
         | fhars wrote:
         | Does Base https://github.com/garybernhardt/base still work with
         | current versions?
        
         | jimbokun wrote:
         | Various Lisps can give it a run for its money, depending on the
         | problem.
         | 
         | Metaprogramming is Lisp's canonical super power. Ruby is going
         | to win out on tasks where it has built in syntax, like matching
         | regular expressions.
         | 
         | But once you get to metaprogramming Lisp macros are going to
         | give Ruby a run for its money.
         | 
         | I will say one of the under appreciated aspects of Ruby is the
         | consistency of its semantics, where everything is message
         | passing very much like Smalltalk.
        
           | tombert wrote:
           | I am extremely partial to Scheme's `define-syntax` construct.
           | I remember the first I saw it, I thought it was one of the
           | elegant and amazing things I had ever seen in a programming
           | language, and I kind of got annoyed that it wasn't something
           | easily available in every language.
           | 
           | I love me some Clojure, and its macros aren't bad or
           | anything, but I feel Scheme (and Racket) has the most elegant
           | metaprogramming.
        
         | shevy-java wrote:
         | Right. But ruby also has awful crap. The documentation - look
         | at opal, webassembly and many other projects in ruby. The
         | documentation is just total garbage.
         | 
         | rubygems.org also has decided to, rather than fix on existing
         | problems, eliminate all former maintainers and instead put in
         | Hiroshi Shibata as the solo lead - the same guy who keeps on
         | writing on different github issue trackers how he does not have
         | time to handle any issue requests for low-used projects.
         | Wowsers.
        
         | chihuahua wrote:
         | "If you ignore performance and safety..."
         | 
         | Other than that, how was the play, Mrs. Lincoln?
         | 
         | Also, add readability and maintainability to that list, and
         | scaling to a large codebase. And good debugger support.
        
         | ramon156 wrote:
         | What about PHP/Symfony? I have more experience in that and,
         | after trying rails out this week, so much was overlapping.
        
         | tartakynov wrote:
         | Let's be honest, when you're starting a startup, Ruby's
         | performance won't be a bottleneck until much later, when you're
         | successful and get tons of usage - at that point you can afford
         | to hire someone to fix it. Your productivity will be a
         | bottleneck from the very beginning.
         | 
         | Pieter Levels writes his startups in PHP and hasn't had a
         | performance bottleneck so far. For most applications, the
         | performance of the language won't be an issue. I personally
         | wouldn't pick PHP for any of my own projects, but Ruby I'd pick
         | any day.
        
           | tliltocatl wrote:
           | > at that point you can afford to hire someone to fix it
           | 
           | That's the reason why we can't have nice things.
        
           | bjclark wrote:
           | Ruby's performance has not been a bottleneck for multiple
           | companies that have IPO'd and are worth billions of dollars.
        
         | wutwutwat wrote:
         | > if you ignore performance
         | 
         | man, people are still parroting decade old, incorrect talking
         | points I see.
         | 
         | Is ruby as performant as C, probably not, although, actually,
         | in some cases, it outperforms C ->
         | https://railsatscale.com/2023-08-29-ruby-outperforms-c/
         | 
         | One of the largest ecommerce apps in the world runs ruby,
         | shopify. Ruby now has a JIT, there has been insane effort put
         | into making ruby faster.
        
       | lloeki wrote:
       | > it's built into Ruby!
       | 
       | Nitpick: technically `Gem::Version` is part of `rubygems`, and
       | while `rubygems` is (typically) packaged with Ruby, it's actually
       | _entirely optional_ , so much so that `rubygems` actually
       | monkeypatches+ Ruby core's `Kernel` (notably `require`) to inject
       | gem functionality.
       | 
       | MRuby has none of it, and CRuby has a `--disable-rubygems`
       | configure flag.
       | 
       | Back in 1.8 days, you even had to manually require `rubygems`!
       | 
       | + https://github.com/ruby/rubygems/tree/4e4d2b32353c8ded870c14...
        
         | dragonwriter wrote:
         | Nitpicking your nitpick, but Ruby's standard library has three
         | components:
         | 
         | * default libraries (these are maintained by the Ruby core
         | team, delivered with Ruby, and upgraded only as part of Ruby
         | version upgrades.)
         | 
         | * default gems (these are maintained by the Ruby core team,
         | delivered with Ruby, not removable, can be required directly
         | just like default libraries, but can be updated separately from
         | Ruby version upgrades.)
         | 
         | * bundled gems (these are gems that are delivered and installed
         | with Ruby, but which can be upgraded separately or removed.)
         | 
         | Rubygems is a default gem. [0] It used to not be part of the
         | standard library, but it has been since Ruby 1.9, released in
         | 2007.
         | 
         | [0] see, https://stdgems.org/
        
           | zahlman wrote:
           | Frankly I wish Python's standard library were more like this.
           | Maybe then we wouldn't be seeing tens of millions of daily
           | downloads of pip (most likely orchestrated by other copies of
           | pip!), requests (already vendored by pip!), setuptools, six
           | (compatibility wrappers for ancient 2.x code, declared as a
           | dependency by python-dateutil so that it can keep supporting
           | 2.x, even though it's overwhelmingly downloaded for up-to-
           | date Python installations)....
           | 
           | ...There are presumably many other ways to solve that
           | problem, but still.
        
             | dragonwriter wrote:
             | When Ruby gemified the standard library, it wasn't about
             | moving popular third-party libs _into_ the standard library
             | as gems, but making large parts of the standard library
             | separately updateable (and, in some cases, removeable); all
             | of the standard library used to be the equivalent of
             | "default libraries", the change was moving them from that
             | status to default or bundled gems.
             | 
             | So, doing the same thing with Python would be less like
             | bundling requests with python and more like moving http
             | into a wheel that was installed with python but could be
             | upgraded separately.
        
           | lloeki wrote:
           | > Nitpicking your nitpick
           | 
           | That's not a nitpick, that's paraphrasing ;)
           | 
           | > It used to not be part of the standard library, but it has
           | been since Ruby 1.9, released in 2007.
           | 
           | That's the mention of 1.8 I made, but it's a bit more
           | complex: it _still_ can be excluded, in two ways:
           | 
           | - at build time via `configure` (and then it's not even
           | there)
           | 
           | - at run time via `--disable-gems`
           | 
           | The interaction between rubygems and ruby core is
           | surprisingly small.
           | 
           | > Rubygems is a default gem
           | 
           | It is not! See `lib/ruby/gems/3.3.0/specifications/default`
           | in any Ruby install, or in source the absence of _any_
           | gemspec for `rubygems` while there is for bundler[0].
           | 
           | Instead it's, as you mentioned, a _default library_.
           | 
           | The very principle of gems is that you can have multiple
           | versions of a gem separately installed (to the point of
           | allowing only one to be visible at any one time, activated by
           | `rubygems`-provided `Kernel.gem`). The implementation of that
           | is done by `rubygems` itself so if it were a gem, one would
           | not be able to activate `rubygems` without `rubygems`
           | itself...
           | 
           | This is also why it can be special-case upgraded only via the
           | very special `gem update --system`, which downloads a "gem"
           | named `rubygems-update` (not `rubygems`); scare quotes
           | because it's using the gem format and infrastructure mostly
           | as a delivery mechanism, not by being an _actual_ gem[1] in
           | the traditional sense (well it is a gem, but a gem of an
           | _updater_ , not of `rubygems` itself).
           | 
           | When updated, the new copy of rubygems is installed in
           | `site_ruby`, because the load path is the only mechanism
           | available to define location priority (`ruby --disable-gems
           | -e 'p $LOAD_PATH'`).
           | 
           | Fun fact: the only thing that "prevents" a file present it
           | `${PREFIX}/lib/ruby/3.4.0/rubygems` to not be `load`ed or
           | `require`d is merely that new code in
           | `${PREFIX}/lib/ruby/site_ruby/3.4.0` shall not make reference
           | to it, but it's all perfectly visible otherwise.
           | docker run --rm -it ruby:3.4 /bin/bash         gem update
           | --system         ls -ld /usr/local/lib/ruby/3.4.0/rubygems
           | /usr/local/lib/ruby/site_ruby/3.4.0/rubygems         echo 'p
           | __dir__' > /usr/local/lib/ruby/3.4.0/rubygems/foo.rb
           | ruby -e 'p $LOAD_PATH; require "rubygems/foo"'
           | 
           | [0]: https://github.com/ruby/ruby/blob/v3_4_7/lib/bundler/bun
           | dler...
           | 
           | [1]: https://github.com/ruby/rubygems/blob/v3.7.2/hide_lib_fo
           | r_up...
        
         | jes5199 wrote:
         | I just remembered, in those days, there was an alias called
         | `ubygems` so you could pass `-rubygems` (ie, `-r` with
         | `ubygems` as the argument) on the command line as if it was a
         | first-class feature
         | 
         | it's so typical of ruby culture "haha, what if I do this silly
         | thing" and then that gets shipped to production
        
       | skrebbel wrote:
       | Unrelated side note, but I haven't written any Ruby in maybe 15
       | years or so and dammn I forgot how elegant the language is at its
       | core. The author's AppVersion class is so nicely done, it's nuts
       | how succinct eg the compare implementation is.
       | 
       | Having done mostly TypeScript and Elixir lately, I had forgotten
       | things could be so succinct yet so clear. The combo of modern (to
       | me) Ruby's lambda syntax (in the .map call), parentheses-less
       | function calls, the fact that arrays implement <=> by comparing
       | each item in order, that there's an overloadable compare operator
       | at all, having multiple value assignments in one go... It all
       | really adds up!
       | 
       | In any other language I can think of real quick (TS, Elixir, C#,
       | Python, PHP, Go) a fair number of these parts would be
       | substantially more wordy or syntaxy at little immediately obvious
       | benefit. Like, this class is super concise but it doesn't trade
       | away any readability at all.
       | 
       | Having learned Ruby before Rails became commonplace, with its
       | love for things that automagically work (until they don't), I had
       | kinda grown to dislike it. But had forgotten how core Ruby is
       | just an _excellent_ programming language, regardless of what I
       | think of the Rails ecosystem.
        
         | vault wrote:
         | Like you, I remember 15 years ago when I decided to solve
         | Project Euler in Ruby, a completely new language to me. I still
         | remember the joy I was feeling when I started coding with this
         | new language. So elegant! So natural! Like it was made to fit
         | my brain. It's a pity I ended up working professionally with
         | entirely different stuff.
        
         | js2 wrote:
         | Challenge accepted:                   from dataclasses import
         | dataclass                  @dataclass(frozen=True, order=True)
         | class AppVersion:             major: int = 0             minor:
         | int = 0             patch: int = 0
         | @classmethod             def from_string(cls, version_string:
         | str):                 return cls(*[int(x) for x in
         | version_string.split(".")])                      def
         | __str__(self):                 return
         | f"{self.major}.{self.minor}.{self.patch}"
         | 
         | Before dataclasses you could've used namedtuples, at a loss of
         | attribute typing and default initializer:
         | from collections import namedtuple                  class
         | AppVersion(namedtuple("AppVersion", "major minor patch")):
         | @classmethod             def from_string(cls, version_string:
         | str):                 parts = [int(x) for x in
         | version_string.split(".")] + [0, 0]                 return
         | cls(*parts[:3])                      def __str__(self):
         | return f"{self.major}.{self.minor}.{self.patch}"
        
           | alberth wrote:
           | Not knowing python, I find the data classes example extremely
           | readable. More so than Ruby example.
        
             | disgruntledphd2 wrote:
             | I write mostly Python these days, but agree with op. The
             | comparables implementation in Ruby seems much nicer to me
             | (maybe because I'm less familiar with it).
        
               | js2 wrote:
               | It's virtually the same in Python if you wrote it
               | explicitly:                   def <=>(other)
               | [major, minor, patch] <=> [other.major, other.minor,
               | other.patch]         end
               | 
               | vs:                   def __lt__(self, other):
               | return (self.major, self.minor, self.patch) <
               | (other.major, other.minor, other.patch)
               | 
               | Then use the `total_ordering` decorator to provide the
               | remaining rich comparison methods.
               | 
               | That said, it's a little annoying Python didn't keep
               | __cmp__ around since there's no direct replacement that's
               | just as succinct and what I did above is a slight fib:
               | you still may need to add __eq__() as well.
        
               | zahlman wrote:
               | > Then use the `total_ordering` decorator to provide the
               | remaining rich comparison methods.
               | 
               | While we're here, worth highlighting `cmp_to_key` as well
               | for `sorted` etc. calls.
               | 
               | > it's a little annoying Python didn't keep __cmp__
               | around since there's no direct replacement that's just as
               | succinct
               | 
               | The rationale offered at the time
               | (https://docs.python.org/3/whatsnew/3.0.html) was
               | admittedly weak, but at least this way there isn't
               | confusion over what happens if you try to use _both_ ways
               | (because one of them just isn 't a way any more).
        
               | disgruntledphd2 wrote:
               | I know, but the ability to use symbols to define the
               | comparator is super, super cool, as opposed to the
               | horrendously ugly lt dunder method.
        
             | jimbokun wrote:
             | It's a little magicky. I guess the "Order=True" is what
             | ensures the order of the parameters in the auto-generated
             | constructor matches the order in which the instance
             | variables are defined?
        
               | js2 wrote:
               | order: If true (the default is False), __lt__(),
               | __le__(), __gt__(), and __ge__() methods will be
               | generated. These compare the class as if it were a tuple
               | of its fields, in order.
               | 
               | eq: If true (the default), an __eq__() method will be
               | generated. This method compares the class as if it were a
               | tuple of its fields, in order. Both instances in the
               | comparison must be of the identical type.
        
             | jimbokun wrote:
             | However, I think comparing the Ruby example implementation
             | with the "data classes example" is a category error.
             | 
             | The Ruby example should be compared to the _implementation_
             | of data classes. The Ruby code shows how cleanly the code
             | for parsing, comparing and printing a version string can
             | be. We would need to see the code underlying the data
             | classes implementation to make a meaningful comparison.
        
           | pansa2 wrote:
           | You could also use a normal class, a direct translation of
           | the Ruby example:                   @functools.total_ordering
           | class AppVersion:           def __init__(self,
           | version_string):             parts = [int(x) for x in
           | str(version_string).split('.')]             self.major,
           | self.minor, self.patch = parts[0] or 0, parts[1] or 0,
           | parts[2] or 0                def __lt__(self, other):
           | return [self.major, self.minor, self.patch] < [other.major,
           | other.minor, other.patch]                def __eq__(self,
           | other):             return [self.major, self.minor,
           | self.patch] == [other.major, other.minor, other.patch]
           | def __str__(self):             return
           | f'{self.major}.{self.minor}.{self.patch}'
        
             | js2 wrote:
             | FWIW, these lines are not equivalent:
             | @major, @minor, @patch = parts[0] || 0, parts[1] || 0,
             | parts[2] || 0              self.major, self.minor,
             | self.patch = parts[0] or 0, parts[1] or 0, parts[2] or 0
             | 
             | The Ruby case is intended to handle strings like "1",
             | "1.2", and "1.2.3".
             | 
             | The Python code will throw an IndexError as written. Which
             | is why I did this in the namedtuple example:
             | parts = [int(x) for x in version_string.split(".")] + [0,
             | 0]
             | 
             | That ensures you'll have at least three parts so you can
             | then:                   self.major, self.minor, self.patch
             | = parts[:3]
        
           | sczi wrote:
           | Nice solution with dataclass! And for a complete comparison
           | with the blog you can also use a library to do it for you.
           | It's not quite in the official python distribution but it's
           | maintained by pypa as a dependency of pip so you probably
           | have it installed already.                   >>> from
           | packaging.version import Version         >>> Version("1.2.3")
           | > Version("1.2.2")         True         >>> Version("2.0") >
           | Version("1.2.2")         True
        
             | js2 wrote:
             | packaging.version has a somewhat weird (or at least Python-
             | specific) set of rules that don't match the semantics of
             | Ruby's Gem:Version, which will accept basically anything as
             | input.
             | 
             | I'd use `semver` from PyPI and whatever the equivalent Gem
             | is on the Ruby side in most cases.
        
         | dlisboa wrote:
         | Ruby trades away quite a few things for readability. It's
         | beautiful but a lot is being hidden.
         | 
         | Some of those languages would have you deal with the problem of
         | allocating multiple arrays in the heap just to compare three
         | numbers. Or give you tools to outlaw passing invalid strings to
         | AppVersion.new (quick: what is the comparison between
         | AppVersions "foo" and "bar"?).
         | 
         | Plus you have very few tools to ensure code remains beautiful.
         | I've worked with Ruby for close to two decades, almost nothing
         | in the real world looks that clean. Take a look at the
         | Gem::Version#<=> implementation that the article talks about:
         | https://github.com/ruby/ruby/blob/master/lib/rubygems/versio...
        
           | jimbokun wrote:
           | Wow I wonder why it's so verbose. Performance optimizations?
           | Seems like this wouldn't be called often enough to show up in
           | any performance profiling exercise.
        
             | dlisboa wrote:
             | Ruby is very slow so you gotta squeeze everything you can
             | everywhere. Even a seemingly simple method will have to be
             | scrutinized so that overall performance isn't impacted.
             | It's death by a thousand cuts.
             | 
             | See the commit that made it complex: https://github.com/rub
             | y/ruby/commit/9b49ba5a68486e42afd83db4...
             | 
             | It claims 20-50% speedups in some cases.
             | 
             | There's churn that comes with that. Ruby will have code
             | that is ever changing to gain 5%, 10% performance every now
             | and then. You gotta put that on balance: in a language like
             | Go this method would've been ugly from the start but no one
             | would've needed to touch it in 100 years.
        
               | jfabre wrote:
               | There are plenty of businesses that have under 10k users
               | and can live perfectly well with http requests around
               | 500-1000 ms. When there are performance issues, 95% of
               | the times they come from the database, not the language.
        
               | dlisboa wrote:
               | I somewhat agree. In general most apps are small where
               | the language choice doesn't really matter.
               | 
               | Caching is also vastly underutilized, most apps are read-
               | heavy and could serve a significant portion of their
               | requests from some form of caching.
               | 
               | > When there are performance issues, 95% of the times
               | they come from the database, not the language.
               | 
               | Eh, statements like these are always too hand wavy.
               | Resource usage has to do with performance, the DB has no
               | fault in it but the runtime does.
               | 
               | Having worked with Rails a ton there's a very large
               | overhead. Most apps would see a significant speed up if
               | rewritten in a faster language and framework, with no
               | changes to the DB whatsoever. The amount of memory and
               | CPU expended to the app servers is always significant,
               | often outweighing the DB.
        
               | jfabre wrote:
               | But what do you mean, give me a real example. You loaded
               | too many active_records in memory and it's using a ton of
               | ram? Did you try pluck, batches or even skipping
               | active_record and using a raw query?
               | 
               | Unless you really need to scale for a ton of users, you
               | don't have to go crazy to get decent performances out of
               | rails/ruby. How many requests/sec are we even talking
               | about here?
        
               | 9rx wrote:
               | _> When there are performance issues, 95% of the times
               | they come from the database, not the language._
               | 
               | DHH used to say that it didn't matter if Rails was slow
               | because the database was I/O bound anyway. But that was a
               | long time ago. Times have changed considerably. Most
               | especially because DHH now promotes using SQLite, which
               | completely takes the worst I/O offender right out of the
               | picture. Nowadays the language (when it is as slow as
               | Ruby) is most likely to be the problem.
        
               | jfabre wrote:
               | Doesn't matter if SQLite is now viable with rails, no
               | legacy rails app is using it and it's not like you're
               | going to wake up one morning and migrate your production
               | db from postgres, mysql, sql server or oracle just
               | because you felt like it.
               | 
               | In theory the language is slow, in practice it doesn't
               | really matter because the db is much slower unless you're
               | github or twitter and you really need to scale.
               | 
               | When you choose ruby, you trade faster dev time for
               | slower runtime. I am OK with this trade-off 99% of the
               | time. My dev time costs way more than a couple ms lost on
               | my production server.
        
               | 9rx wrote:
               | _> When you choose ruby, you trade faster dev time for
               | slower runtime._
               | 
               | Ruby is a beautiful language, but that does not translate
               | to efficient use of dev time. Ruby is not a language that
               | you can quickly write usable code in. Some other
               | languages are clearly more productive. You can create
               | works of art, though, which is still pretty appealing.
               | Ruby does have a place in this world.
               | 
               | It was, again, DHH/Rails that used to make the claim
               | about developer time -- premised on Rails eliminating all
               | the so-called "situps" that other frameworks imposed on
               | developers. It is very true that when you eliminate a
               | bunch of steps you can move a lot faster. But:
               | 
               | 1. Everyone and their bother have created Rails clones in
               | other languages that also eliminate the same "situps",
               | negating any uniqueness Rails once offered on that front.
               | 
               | 2. It turns out those "situps", while a dog in early
               | development, actually speed development up down the road.
               | If you are churning out a prototype to demonstrate an
               | idea that will be thrown away after, its a pretty good
               | tradeoff to ignore what you can, but things become far
               | less clear cut when a program finds longevity.
        
               | AdieuToLogic wrote:
               | >> When there are performance issues, 95% of the times
               | they come from the database, not the language.
               | 
               | > DHH used to say that it didn't matter if Rails was slow
               | because the database was I/O bound anyway. But that was a
               | long time ago. ... Nowadays the language (when it is as
               | slow as Ruby) is most likely to be the problem.
               | 
               | Nowadays CPU speeds, available RAM, and network speeds
               | dwarf what was top-of-the-line "a long time ago," making
               | the trope of "Ruby is too slow" much less believable
               | "nowadays."
        
               | 9rx wrote:
               | "Too slow" is a mischaracterization. Ruby was never too
               | slow, only comparatively slow. What DHH was talking about
               | is that when the system was I/O bound, even if you could
               | eliminate all the time spent in Ruby, you'd only see
               | small percentage increases in performance at best. But
               | the calculus has changed. I/O isn't the bottleneck like
               | it was when those statements were made. Now, if you could
               | eliminate the time spent in Ruby, you'd see significant
               | percentage increases in performance.
        
               | vidarh wrote:
               | Ruby itself has also gotten far faster, even if you stick
               | with MRI, and even faster if you don't.
        
               | dmix wrote:
               | > Nowadays the language (when it is as slow as Ruby) is
               | most likely to be the problem.
               | 
               | These days the main issue why web apps are slow or
               | fragile is because they are huge React apps that do way
               | too much in the browser than they need too. The response
               | time from the server is rarely the issue unless you're
               | doing heavy analytics. High end React shops do some crazy
               | optimization to try to compensate for this fact. Linear
               | is the only one I've seen do it well with JS.
        
               | BurningFrog wrote:
               | You never gotta squeeze everything you can everywhere!
               | 
               | Regardless of how slow the language is, the 90/10 rule
               | applies: 90% of the time is spent in 10% of the code.
               | Optimize that 10%! Making the rest of the code faster
               | isn't worth the code quality cost.
        
               | ben-schaaf wrote:
               | That's a rule that might hold for applications and
               | services. It does not hold for languages and libraries,
               | where any and every aspect is going to be the bottleneck
               | in someone else's code. It's a different 10% for each
               | user.
        
               | nikanj wrote:
               | You can't build a Ford Pinto and then swap the piston
               | heads and rear differential to get a Formula 1 car.
        
               | jbverschoor wrote:
               | lol.. yet we have no problems running webviews on chromes
               | on electron on VMs on later.
               | 
               | You can use Crystal which is faster that Go
        
         | __jonas wrote:
         | I don't really know Ruby, what is the to_s doing in
         | parts = version_string.to_s.split(".").map(&:to_i)
         | 
         | Is it to_string? Isn't version_string already a string?
        
           | jimbokun wrote:
           | Yes, to_s returns the string representation of an object.
           | 
           | I think it's a safety measure in case the argument passed in
           | is not a string, but can be turned into a string. Safe to
           | assume that calling "to_s" on a string just returns the
           | string.
        
           | dlisboa wrote:
           | Ruby is a dynamic language, `version_string` can be anything.
           | The author uses `to_s` to coerce it into a string. There are
           | problems with that: if I pass in an array it'll coerce into
           | `"[1,2,3]".split(".").map(&:to_i)`, which makes no sense.
        
             | shevy-java wrote:
             | One could do a conversion e. g.                   if
             | x.is_a? Array           x = x.first
             | 
             | Or something like that. Could be one line too:
             | x = x.first if x.is_a? Array
        
               | dlisboa wrote:
               | Most times it's better to just accept the dynamic nature
               | of the language rather than do this kind of runtime type
               | checking. You'd have to do this `.is_a?` dance for every
               | type to have it be reliable.
               | 
               | Even if you implement an "interface" (duck typing) with
               | `respond_to?(:to_app_version)` you still can't be sure
               | that the return type of this `:to_app_version` is
               | actually a string you can call `split()` on.
        
           | metalliqaz wrote:
           | it allows you to initialize an AppVersion with an other
           | AppVersion object
        
             | hashmal wrote:
             | This is the most relevant use of `to_s` in this class
             | indeed. One could imagine additional methods like:
             | def bump_minor           self.class.new(major, minor + 1,
             | patch)         end
             | 
             | (although I'm not sure why it would be useful in that
             | particular case, it's just an example of how you can build
             | new objects out of existing ones without having to mutate
             | them)
        
           | weaksauce wrote:
           | it is a string usually but could be called with a single
           | number or some other object that has that method overwritten
           | and it would still do the right thing.
        
           | raincole wrote:
           | It's trying to make it more 'type tolerant' so it accepts
           | both string and int and perhaps other types that implement
           | `to_s`.
           | 
           | It's also a quite bad practice to my eye.
        
           | iamjs wrote:
           | it could be anything, but virtually everything implements
           | `#to_s`.
        
             | zahlman wrote:
             | ... But maybe not in a way that happens to be a good idea
             | in the current context.
        
         | shevy-java wrote:
         | > But had forgotten how core Ruby is just an excellent
         | programming language, regardless of what I think of the Rails
         | ecosystem.
         | 
         | A problem is that ruby lost many developres; rails too but it
         | is by far the biggest driver in ruby. And this creates
         | problems, because it overshadows the remaining ruby developers.
        
           | ajoski9 wrote:
           | Is that true. Feels like new Ruby versions and great updates
           | are being churned out relatively fast these days.
        
         | joemasilotti wrote:
         | > The author's AppVersion class is so nicely done, it's nuts
         | how succinct eg the compare implementation is.
         | 
         | Why thank you! :D
        
         | _old_dude_ wrote:
         | Yes, the version in Java is clearly less elegant. Java has
         | map+lambda and compareTo (<=>) but no tuple assignemnt and no
         | splat.                   record AppVersion(int major, int
         | minor, int patch) implements Comparable<AppVersion> {
         | public static AppVersion of(String version) {             var
         | array = Arrays.copyOf(Arrays.stream(version.split("\\.")).mapTo
         | Int(Integer::parseInt).toArray(), 3);             return new
         | AppVersion(array[0], array[1], array[2]);           }
         | public int compareTo(AppVersion other) {             return
         | Comparator.comparingInt(AppVersion::major)
         | .thenComparingInt(AppVersion::minor)
         | .thenComparingInt(AppVersion::patch)
         | .compare(this, other);           }                public String
         | toString() {             return "%d.%d.%d".formatted(major,
         | minor, patch);           }         }
        
           | spullara wrote:
           | Also the copyOf isn't really the same as being able to ||
           | things since it just happens both copyOf default is 0 and in
           | this case it is also 0 (i.e. what if it was -1 to indicate
           | there was no version).
        
         | kelvinjps10 wrote:
         | I have always found python very succint, is ruby even more?
        
         | zahlman wrote:
         | > modern (to me) Ruby's lambda syntax (in the .map call)
         | 
         | It's syntactic sugar for what Ruby does with a lambda, but
         | fundamentally the purpose is to extract a method from the
         | input. Python has that in the standard library, as
         | `operator.attrgetter`. But also in Python, you generally
         | convert by passing to the type constructor rather than calling
         | a method; so you can just use that directly.
         | 
         | > parentheses-less function calls
         | 
         | Only methods are called here, not plain functions. You can get
         | this effect in many other languages by defining properties
         | instead of zero-argument methods.
         | 
         | > the fact that arrays implement <=> by comparing each item in
         | order
         | 
         | This is also done in Python, and probably many other languages.
         | 
         | > that there's an overloadable compare operator at all, having
         | multiple value assignments in one go
         | 
         | Idem.
         | 
         | > In any other language I can think of real quick (TS, Elixir,
         | C#, Python, PHP, Go) a fair number of these parts would be
         | substantially more wordy or syntaxy at little immediately
         | obvious benefit.
         | 
         | A relatively direct translation looks like:
         | import functools             @functools.total_ordering
         | class AppVersion:         def __init__(self, version_string):
         | self.major, self.minor, self.patch, *_ = map(int,
         | str(version_string).split('.'))         def __lt__(self,
         | other):           return (self.major, self.minor, self.patch) <
         | (other.major, other.minor, other.patch)         def
         | __eq__(self, other):           return (self.major, self.minor,
         | self.patch) == (other.major, other.minor, other.patch)
         | def __str__(self):           return
         | f'{self.major}.{self.minor}.{self.patch}'
         | 
         | You don't need any `end`s, but you don't (in 3.x) have the
         | convenience of a direct `<=>` analog (it used to be `__cmp__`).
         | The actual integer conversion function could be done
         | differently, of course, to handle invalid values (I don't know
         | why the Ruby code is doing the `|| 0` business; `to_i` already
         | takes care of that AFAICT).
         | 
         | Although the rough ecosystem equivalent of Gem::Version (https:
         | //github.com/pypa/packaging/blob/main/src/packaging/ve...) does
         | much more sophisticated parsing. And you could also get the
         | comparison logic by leveraging `collections.namedtuple`,
         | `typing.NamedTuple` (but changing the initialization logic
         | isn't so neat for these immutable types),
         | `dataclasses.dataclass` etc. as in js2's reply.
        
         | zumu wrote:
         | It allocates 2 collections for every compare call and
         | obfuscates the comparison logic. Personally I find that
         | extremely inelegant. Different strokes I suppose.
        
           | skrebbel wrote:
           | Obfuscates how?
        
         | AdieuToLogic wrote:
         | > In any other language I can think of real quick (TS, Elixir,
         | C#, Python, PHP, Go) a fair number of these parts would be
         | substantially more wordy or syntaxy at little immediately
         | obvious benefit. Like, this class is super concise but it
         | doesn't trade away any readability at all.
         | 
         | Scala would like to have a terse say about this... :-)
        
           | pxc wrote:
           | Scala also draws on many other sources, but syntactically it
           | clearly takes inspiration from Ruby in a lot of nice ways!
        
           | skrebbel wrote:
           | Fair! I was underwhelmed by a Python port in a sibling
           | comment but I can imagine Scala could come close indeed! I
           | mean it'd take a day to compile but it'd look pretty!
        
         | chamomeal wrote:
         | I don't know any ruby but I dabbled in elixir and I gotta ask:
         | why do you prefer parenthesis-less function calls?
         | 
         | I like when parens/brackets are reliable wrappers for chunks of
         | code. Like being able to 'vi{' in vim to select a function
         | body. Or '%' to jump to the matching paren.
         | 
         | Do you find the language more readable without it? Less visual
         | noise?
        
           | tombert wrote:
           | Yeah, I don't really understand the hatred for parentheses.
           | Lisp famously has parentheses everywhere, and it looks a bit
           | noisy at first but the precedence and scope is never
           | ambiguous and with Vim you can always do % to find the match.
        
           | skrebbel wrote:
           | > why do you prefer parenthesis-less function calls?
           | 
           | I don't feel strongly about it, but you gotta admit that this
           | is remarkably easy on the eyes yet also easy to follow:
           | parts = version_string.to_s.split(".").map(&:to_i)
           | 
           | The Elixir equivalent, likely a series of pipes, would be
           | just as easy to follow but substantially more to read, more
           | symbols to parse etc. I don't feel like this here line of
           | Ruby makes any sacrifices in understandability compared to
           | this Elixir port we're both imagining.
           | 
           | Good point about grepping though.
        
             | asa400 wrote:
             | > The Elixir equivalent, likely a series of pipes, would be
             | just as easy to follow but substantially more to read, more
             | symbols to parse etc.
             | 
             | Huh?                 [major, minor, patch] =
             | version_string         |> String.split(".")         |>
             | Enum.map(&String.to_integer/1)
             | 
             | I don't know that I'd call this "substantially more" of
             | anything.
        
         | tombert wrote:
         | There's no accounting for taste but I have never really seen
         | why people consider Ruby so elegant.
         | 
         | Admittedly, I have never done a lot with Ruby, but I have done
         | some Rails and I tried for a few months to use it for simple
         | "shell" scripts, and the language never felt beautiful or
         | elegant to me.
         | 
         | Admittedly, I come from a strong functional programming
         | background, so it is entirely possible that my brain sees "it's
         | not really a 'functional' and therefore I don't like it", but I
         | do like Rust (even when I write it very imperatively) and I
         | even kind of like modern Java.
         | 
         | Dunno, I will admit that I am weird, my favorite language is
         | Clojure and I know that that one is an acquired taste :).
        
         | BiteCode_dev wrote:
         | Modern Python is quite close to the ruby version:
         | from dataclasses import dataclass
         | @dataclass(order=True)         class AppVersion:
         | major: int = 0             minor: int = 0             patch:
         | int = 0                          @classmethod             def
         | from_string(cls, version_string):                 parts =
         | map(int, str(version_string).split('.'))                 return
         | cls(*parts)                          def __str__(self):
         | return f"{self.major}.{self.minor}.{self.patch}"
         | 
         | All languages steal from each other, so on a long enough time
         | scale, it makes sense they kinda converge.
        
       | aldousd666 wrote:
       | I discovered this a few years ago when someone who didn't
       | understand what semver is was trying to do a rails version
       | upgrade for us. They were practically throwing stuff when I got
       | there and explained that lexicographical comparison of the
       | strings would not work. I was about to write my own class for it,
       | but then I thought that since Bundler knew how to resolve deps we
       | should see what it uses. The rest is history!
        
         | psadauskas wrote:
         | I use it quite a bit when I have to monkeypatch a gem to
         | backport a fix while I wait for a release:
         | raise "check if monkeypatch in #{__FILE__} is still needed" if
         | Gem::Version.new(Rails.version) >= Gem::Version.new("8.0.0")
         | 
         | This will blow up immediately when the gem gets upgraded, so we
         | can see if we still need it, instead of it laying around in
         | wait to cause a subtle bug in the future.
        
         | saghm wrote:
         | > They were practically throwing stuff when I got there and
         | explained that lexicographical comparison of the strings would
         | not work.
         | 
         | Versions numbers can go to 10!?!
        
         | shevy-java wrote:
         | > I discovered this a few years ago
         | 
         | Right. I think I found it on stackoverflow.
         | 
         | The question is: why does the official documentation not
         | mention this, along with guides?
         | 
         | Answer: because documentation is something the ruby core team
         | does not want to think about. It is using scary language after
         | all: English. The bane of most japanese developers. Plus, it is
         | well-documented in Japanese already ... :>
        
         | stefan_ wrote:
         | Aaand 10 years later you just learned to compare versions by
         | equality instead of being impossibly clever.
        
       | parentheses wrote:
       | I think this article is funny. Python's STL is way more useful
       | and contains myriad useful things that Ruby lacks out of the box.
       | 
       | difflib is probably my favorite one to cite.
       | 
       | Go see for yourself: https://docs.python.org/3/library/index.html
       | 
       | The benefit there is that their quality, security, completeness
       | and documentation are all great!
        
         | empiko wrote:
         | If I would get a cent every time I solved a difficult problem
         | in a project by pulling out difflib and shocking the team with
         | it, I would have two cents. It's not a lot, but it's amusing
         | that it happened two times already.
        
         | zahlman wrote:
         | "STL" is not an abbreviation for "standard library". It doesn't
         | even correctly refer to the _C++_ standard library; that 's a
         | misnomer. There was only one STL, and it was third-party;
         | saying "Python's STL" makes barely any more sense than saying
         | "Python's Boost".
         | 
         | https://en.wikipedia.org/wiki/Standard_Template_Library
        
       | websitescenes wrote:
       | Damn, I miss ruby and particularly Rails sooo much. I'm stuck
       | rebuilding wheels in node currently :)
        
       | Bergrebell wrote:
       | Going back to rails after all this JS years was the best decision
       | we've ever made. Current state of rails is lit!
        
       | jaredcwhite wrote:
       | Ruby is an awesome language. The first few 3.x releases brought
       | incredible modern advancements, including pattern matching which
       | I totally adore.
       | 
       | I'd love to see a lot more writing and advocacy around _Ruby_ ,
       | and not Ruby/Rails. I don't use Ruby/Rails! I use Ruby. And I
       | suspect a lot of folks who have left Ruby behind over the years
       | might not realize some (many?) of their gripes are not with Ruby
       | in general, but Rails in particular.
        
       | kazinator wrote:
       | TXR Lisp:                 $ txr -i version.tl       1> (equal
       | (new (app-ver "1.2.003")) (new (app-ver "1.2.3")))       t
       | 2> (equal (new (app-ver "1.2.003")) (new (app-ver "1.2.4")))
       | nil       3> (less (new (app-ver "1.2")) (new (app-ver "1.2.3")))
       | t       4> (greater (new (app-ver "1.2")) (new (app-ver
       | "1.2.3")))       nil       5> (tostringp (new (app-ver
       | "1.2.3.4")))       "1.2.3.4"       6> (tostring (new (app-ver
       | "1.2.3.4")))       "#S(app-ver str \"1.2.3.4\" vec (1 2 3 4))"
       | 
       | Code:                 (defstruct (app-ver str) ()         str
       | vec         (:postinit (me)           (set me.vec (flow me.str
       | (spl ".") (mapcar int-str))))         (:method equal (me) me.vec)
       | (:method print (me stream pretty-p)           (if pretty-p (put-
       | string `@{me.vec "."}` stream) :)))
        
         | kazinator wrote:
         | These objects can be inserted into a hash table, and are keyed
         | by their semantic value, not the string:                 1>
         | (hash)       #H(())       2> *1       #H(())       3> (set [*1
         | (new (app-ver "1.2.3.0004"))] 'mapped)       mapped       4> *1
         | #H(() (#S(app-ver str "1.2.3.0004" vec (1 2 3 4)) mapped))
         | 5> [*1 (new (app-ver "1.2.3.4"))]       mapped       6> [*1
         | (new (app-ver "1.2.03.4"))]       mapped       7> [*1 (new
         | (app-ver "1.2.02.4"))]       nil
        
       | PufPufPuf wrote:
       | Today we're praising Ruby for... having a version parsing utility
       | in the standard library? Really? Many languages like Python, C#,
       | even PHP have it too.
        
         | jbverschoor wrote:
         | Just not the same.
         | 
         | And yes.. without ecosystem/libraries, everybody's just
         | creating their own thing over and over again and there's not a
         | coherent way of thinking
         | 
         | The Joy of Ruby
        
       | another_twist wrote:
       | I wish I was in the Ruby camp. Unfortunately the learning curve
       | is quite high, you kinda have to know what you are doing from a
       | language standpoint. I am more of a Java nerd myself and for me
       | the cost of switching to any other language is simply not
       | justified. But every now and then, I do get fomo.
        
       ___________________________________________________________________
       (page generated 2025-11-08 23:01 UTC)