NHacker Next
  • new
  • past
  • show
  • ask
  • show
  • jobs
  • submit
Ruby Solved My Problem (newsletter.masilotti.com)
skrebbel 12 hours ago [-]
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.

dlisboa 10 hours ago [-]
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 10 hours ago [-]
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 10 hours ago [-]
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/ruby/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.

BurningFrog 7 hours ago [-]
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 2 hours ago [-]
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.
jfabre 9 hours ago [-]
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.
9rx 7 hours ago [-]
> 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 5 hours ago [-]
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 23 minutes ago [-]
> Doesn't matter if SQLite is now viable with rails

It does. The idea no longer holds water.

> 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.

dmix 3 hours ago [-]
> 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.

AdieuToLogic 4 hours ago [-]
>> 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 54 minutes ago [-]
"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.
dlisboa 9 hours ago [-]
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 4 hours ago [-]
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?

7 hours ago [-]
7 hours ago [-]
tombert 47 minutes ago [-]
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 :).

_old_dude_ 9 hours ago [-]
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("\\.")).mapToInt(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 8 hours ago [-]
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).
vault 11 hours ago [-]
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 11 hours ago [-]
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}"
pansa2 10 hours ago [-]
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}'
sczi 9 hours ago [-]
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 8 hours ago [-]
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.

alberth 11 hours ago [-]
Not knowing python, I find the data classes example extremely readable. More so than Ruby example.
disgruntledphd2 11 hours ago [-]
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 10 hours ago [-]
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 7 hours ago [-]
> 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).

jimbokun 10 hours ago [-]
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.

jimbokun 10 hours ago [-]
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 10 hours ago [-]
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.

11 hours ago [-]
AdieuToLogic 4 hours ago [-]
> 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... :-)

skrebbel 34 minutes ago [-]
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!
pxc 3 hours ago [-]
Scala also draws on many other sources, but syntactically it clearly takes inspiration from Ruby in a lot of nice ways!
chamomeal 3 hours ago [-]
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?

skrebbel 30 minutes ago [-]
> 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.

tombert 43 minutes ago [-]
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.
zumu 7 hours ago [-]
It allocates 2 collections for every compare call and obfuscates the comparison logic. Personally I find that extremely inelegant. Different strokes I suppose.
skrebbel 29 minutes ago [-]
Obfuscates how?
joemasilotti 10 hours ago [-]
> The author's AppVersion class is so nicely done, it's nuts how succinct eg the compare implementation is.

Why thank you! :D

shevy-java 10 hours ago [-]
> 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 7 hours ago [-]
Is that true. Feels like new Ruby versions and great updates are being churned out relatively fast these days.
__jonas 10 hours ago [-]
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 10 hours ago [-]
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.

raincole 10 hours ago [-]
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.

dlisboa 10 hours ago [-]
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 10 hours ago [-]
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 10 hours ago [-]
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.

weaksauce 10 hours ago [-]
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.
iamjs 10 hours ago [-]
it could be anything, but virtually everything implements `#to_s`.
zahlman 7 hours ago [-]
... But maybe not in a way that happens to be a good idea in the current context.
metalliqaz 10 hours ago [-]
it allows you to initialize an AppVersion with an other AppVersion object
kelvinjps10 8 hours ago [-]
I have always found python very succint, is ruby even more?
zahlman 7 hours ago [-]
> 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.

adverbly 13 hours ago [-]
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 13 hours ago [-]
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 7 hours ago [-]
Agreed. Also pretty to look at IMHO
jimbokun 10 hours ago [-]
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 39 minutes ago [-]
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.

fhars 12 hours ago [-]
Does Base https://github.com/garybernhardt/base still work with current versions?
chihuahua 10 hours ago [-]
"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.

shevy-java 10 hours ago [-]
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.

iagooar 13 hours ago [-]
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.

netghost 11 hours ago [-]
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 3 hours ago [-]
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

matltc 13 hours ago [-]
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.

graypegg 5 hours ago [-]
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

amerine 13 hours ago [-]
I love this idea!! Any luck finding an STL or at least a 3d model I can convert and copy your idea?
jonah 12 hours ago [-]
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?

lloeki 13 hours ago [-]
> 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 11 hours ago [-]
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 7 hours ago [-]
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 21 minutes ago [-]
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.

jaredcwhite 10 hours ago [-]
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.

Bergrebell 10 hours ago [-]
Going back to rails after all this JS years was the best decision we’ve ever made. Current state of rails is lit!
kazinator 9 hours ago [-]
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 8 hours ago [-]
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
websitescenes 10 hours ago [-]
Damn, I miss ruby and particularly Rails sooo much. I'm stuck rebuilding wheels in node currently :)
c-hendricks 13 hours ago [-]
title is actually "Ruby already solved my problem"
throwaway81523 13 hours ago [-]
Thanks, that helped. My unspoken question when I saw the title was "does that mean you now have two problems?".
aldousd666 11 hours ago [-]
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 10 hours ago [-]
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.
shevy-java 10 hours ago [-]
> 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 ... :>

saghm 10 hours ago [-]
> 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!?!

stefan_ 9 hours ago [-]
Aaand 10 years later you just learned to compare versions by equality instead of being impossibly clever.
10 hours ago [-]
frostyel 7 hours ago [-]
[dead]
123sereusername 10 hours ago [-]
[dead]
parentheses 11 hours ago [-]
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!

zahlman 7 hours ago [-]
"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

empiko 10 hours ago [-]
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.
Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact
Rendered at 08:07:01 GMT+0000 (Coordinated Universal Time) with Vercel.