[HN Gopher] Ruby Solved My Problem
       ___________________________________________________________________
        
       Ruby Solved My Problem
        
       Author : joemasilotti
       Score  : 127 points
       Date   : 2025-11-07 18:45 UTC (4 hours 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?
        
         | 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).
        
       | 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.
        
         | 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.
        
         | 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.
        
       | 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/
        
       | 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.
        
             | 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}'
        
         | 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.
        
         | __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
        
           | 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`.
        
         | 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.
        
         | 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);           }         }
        
       | 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 ... :>
        
       | 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.
        
       | 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) :)))
        
       ___________________________________________________________________
       (page generated 2025-11-07 23:00 UTC)