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