[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)