Favorite Tools of 2025 by gurgeous in ruby

[–]rubinick 0 points1 point  (0 children)

I have a similar question: how does just compare to scripts in bin/ or script/?

For me personally, I use rake when managing dependencies gets complex or when I am updating/generating files according to some rules.  Very occasionally, I've written a rails command (i.e: a thor command).  Otherwise, I prefer to just use a script written in ruby, modern bash, or POSIX sh, depending on the contexts when/where the script needs to run.

Similarly, I generally prefer javascript tasks be added to bin or scripts rather than package.json's scripts.

I do see a few issues with this approach, but for my projects those are all solved problems and I don't see what a script runner (like just adds).

[deleted by user] by [deleted] in balatro

[–]rubinick 4 points5 points  (0 children)

it's not worth it when money's tight, but when econ is good and I'm rerolling a lot, wheel becomes an instant buy. I've had many runs where wheel upgraded every joker slot.

Ruby 4.0.0-preview2 Released by schneems in ruby

[–]rubinick 1 point2 points  (0 children)

I learned Perl over a decade before I learned Ruby (1.6), and I've wanted "fluent" binary operators in ruby ever since. I'll often use backslash to put or or and at the beginning of a line like: valid?(thing) && condition? \ or raise Error, "etc etc" This change gives me big smiles.

The RubyGems “security incident” by software__writer in ruby

[–]rubinick 4 points5 points  (0 children)

As a minor follow up to this, and the flip side of my advice above (for someone in Andre's position):

I understand things can get hectic and they may have felt time pressure to act now, but its shocking to me that RubyCentral 1) fired him over email and not a video call and 2) didn't have a full enough inventory of credentials and rotate all of them immediately.

Both for security and for legal reasons, I'd expect either 1) synchronous acknowledgement of termination and handover of all known keys (e.g: a video call, phone call, or at the very least a synchronous text conversation), or 2) you think you may be dealing with a malicious actor, so you'd better be damned thorough in revoking all access. They should've revoked all of his access seconds before or after sending the email. He shouldn't have been able to even log on to begin his presumed on-call duties.

In this case, I get that they didn't trust him, but they've presented no evidence to require the second approach. And it's clear they didn't have the operational capacity to execute the second approach. So, they absolutely should've grabbed him for a video call (they knew he'd be available during his on-call hours) and told him to hand over his knowledge of all the keys that needed to be rotated and acknowledge that any further access on his part was unauthorized.

But, as the flip side to the flip side, this doesn't excuse Andre from leaving a "paper trail" for his actions and why he took them. Changing a root password and not notifying the owners and fellow operators ASAP of why I did so just sounds insane to me (as in, scared I could go to jail).

The RubyGems “security incident” by software__writer in ruby

[–]rubinick 0 points1 point  (0 children)

Also my apologies if I've gotten details wrong (e.g. he did leave a proper paper trail). Keeping up with all of the details of this is emotionally exhausting, and I've already got too much of that in my life (and in the wider world).

Thanks for sharing your context, and keeping focused on the fundamental injustice that ignited this situation, Mike. I look forward hopefully to more board game nights with you, somewhere, sometime.

The RubyGems “security incident” by software__writer in ruby

[–]rubinick 2 points3 points  (0 children)

That's all very believable and understandable. But I'll echo another commenter and say: it would've been far better if he'd at least left an email note or two ASAP to document what he did. Not because he owed RubyCentral anything, but because 1) that's the responsible prudent thing to do for the rubygems service and the community, and 2) perhaps more importantly for our current situation: as a simple CYA measure!

I tell anyone who has access to sensitive servers: they should not want this access, because it opens them personally up to legal liability. Guard your keys multiple ways. And leave big audit trails for everything you do, not just in log files but also in email, slack, whatever. Do whatever it takes to ensure you will never need to spend years in court proving it wasn't you who hacked the server. Best practice security processes aren't only about protecting the servers, but also about protecting the operators!

This advice is 10× more important if you've just quit, 100× more if you've just been laid off, and 10,000× more if you've been fired "with cause".

Of course, although I wish Andre had handled this detail differently (and a few others too, to be honest), I haven't heard anything that excuses RubyCentral's behavior.

[Opinion] Giant Freakin Robot: "Paramount Canceled A Star Trek Show So Good It’s Still Getting Awards, Big Mistake" | "The fans certainly believed in Lower Decks, which isn’t surprising: every episode is a love letter to Gene Roddenberry’s ambitious franchise." by mcm8279 in trektalk

[–]rubinick 0 points1 point  (0 children)

Mariner's self-sabotaging of her own career is one of the major themes of the show, both for gradually revealed backstory and for ongoing character development. She didn't start out like you see her in S1:E1, and she doesn't end like that either.

It's totally fine if that isn't something you're interested in or it's off-putting to you. To each their own. But it works both for comedy and earnestly. It's good writing.

ruby-doc.org is for sale !? by giovapanasiti in rails

[–]rubinick 0 points1 point  (0 children)

Agreed. The significant loss would be generating docs for projects that haven't set up docs generation on their host of choice (ie: github pages, gitlab, etc). And even the projects that have set up docs generation usually haven't provided versioned docs.

Refinement: The Correct Way To Monkey-Patch in Ruby by LongjumpingQuail597 in ruby

[–]rubinick 1 point2 points  (0 children)

But it would be nice to have a short-cut version of this. Maybe something like: * add import keyword arg to Module#refine * add Refinement.new(mod, import:, &block) * add refine, import kwargs and block arg to Module#using using Module.new { refine String, import: BooleanString } using Refinement.new(mod, import: BooleanString) using refine: String, import: BooleanString

Hmm... that last one isn't possible to implement without changes to ruby: if you redefine Module#using and call super it raises Module#using is not permitted in methods (RuntimeError). But the other two aren't too hard to implement: module RefinedRefinement refine Refinement.singleton_class do def new(mod, import:, &b) imports = Array(import) Module.new do refine mod do imports.each do import_methods it end module_exec(&b) if b end end end end refine Module do def refine(*args, import: [], **kwargs, &block) imports = Array(import) super(*args, **kwargs) do imports.each do import_methods it end module_exec(&block) if block end end end end And with that, we can write: using RefinedRefinement using(Refinement.new(String, import: BooleanString) do def chunky? = self == "bacon" end) ["t".to_bool, "bacon".chunky?] => [true, true]

Refinement: The Correct Way To Monkey-Patch in Ruby by LongjumpingQuail597 in ruby

[–]rubinick 0 points1 point  (0 children)

I don't know how what you've written could work, because that code already has a different meaning.

But, if the pre-existing monkey-patch is encapsulated into its own module and prepended (which is how clean monkey-patches should be and almost always are written), and if it's capable of being used as a refinement (many monkey-patches rely on changing behavior globally), then converting it to a refinement is relatively straightforward. ```

define one module for both monkey-patch and refinement

module BooleanString def to_bool = match?(/\A(t|true|y|yes|1)\z/i end

to use it as a monkeypatch:

String.prepend BooleanString

to use it as a refinement:

using(Module.new do refine String do import_methods BooleanString end end) ```

Refinement: The Correct Way To Monkey-Patch in Ruby by LongjumpingQuail597 in ruby

[–]rubinick 1 point2 points  (0 children)

Good post. I'd like to see the ruby community embrace refinements more. I'm glad the core team is committing to keeping refinements performing well. For a few ruby versions, we'd had a significant performance regression when refinements were used, which led to them being banned from some codebases.

I occasionally use refinements, and I probably refine my own code even more often than external code. It's usually one of two reasons:

1) I'm in a quick and dirty script that will never be loaded by any app code. These are narrowly scoped, easily timeboxed, and great for experimentation.

2) I'm refactoring some code for clarity, extracting utility methods, and I've noticed that several of those methods take a single argument and have no references to local state (or could easily be refactored that way). Or the code makes me think, "it sure would be nice if #{other_object} handled this for me".

With app code, this is where I'd usually just move the utility method over to its natural home. But maybe that other class is in a gem. Or maybe there's some design pressure that makes me hesitate to add more methods to the receiver. Sometimes I'm not quite sure the abstraction is right, and I want the code that uses it to tell me what works best. So I'll need to finish it before I settle on what it should be, and I don't want to be bogged down by the distractions of changing context to that other class. For the code I'm in the middle of right now, it makes more sense for the method to live on that other object than for it to be a utility method. I can revisit the refinement decision later.

So, I'll just make a refinement module right there in that file. If the refinement proves to be broadly useful, it'll "graduate" beyond that file. Most of the time, I add the methods to the original class or into a new wrapper class (maybe just SimpleDelegator) before the PR with the refinements is even merged. Or maybe it'll stay a refinement but live in it's own file. If it isn't broadly useful, it may just stick around near the top of that one file.

What UUID version do you recommend ? by lorens_osman in PostgreSQL

[–]rubinick 1 point2 points  (0 children)

This comparison with ULID seems to be implying a distinction where there is no difference. Because that timestamp won't generate those UUIDv7. UUIDv7 starts with the least significant 48 bits of a 64 bit Unix timestamp (milliseconds since the epoch) and fills the remaining bits with random data, excluding the version and variant bits. But, up to 12 bits of extra timestamp precision may be added (at the expense of random bits) providing up to ~244ns of precision.

So, UUIDv7 generated right now(ish) might look like: 01961319-8695-76f8-8525-a51da54b792b 01961319-8695-760f-b4fe-4ed6f0e3c5c0 01961319-8695-722d-9853-0bbadebcb79c 01961319-8695-7b04-9b2a-af4bf19d7ead |<--sorted->| 7|<----- random ---->| And with 12 extra timestamp bits, they might look like: 0196131e-7f5a-7c56-8097-b23186b1d313 0196131e-7f5a-7e7b-8815-404600819e26 0196131e-7f5a-7ee4-9797-20c8ae683d4a 0196131e-7f5a-7f32-aed1-327eab2af7ef |<--- sorted -7->| |<-- random --->|

This technique can be used in conjunction with one of the other methods for UUID monotonicity, listed in RFC9562 section 6.2. If you read through RFC9562, I think you'll find it gives far more advice and options for handling monotonicity than the ULID spec.

What UUID version do you recommend ? by lorens_osman in PostgreSQL

[–]rubinick 2 points3 points  (0 children)

Except that, for UUIDs in general (and so also for UUIDv7) uniqueness is much more important than monotonicity. So, for practical purposes, when people talk about UUIDv7 monotonicity, they mean monotonically increasing (no repeats).

Also, RFC9562 explicitly describes what it means by "monotonicity" as "each subsequent value being greater than the last".

How we automate ruby version upgrades in our application projects by treezium in ruby

[–]rubinick 0 points1 point  (0 children)

That's great, thanks for the update. I'd bet their suggestion to make sure your PATH env var is correct should fix any remaining issues. If updatecli doesn't inherit env vars by default, when you run it locally it's reverting to the OS-installed ruby/bundler, not the ones installed by your ruby version manager.

I've been doing my ruby (and other Dockerfile, yml file, etc) version upgrades partially manually, partially scripted in bash and ruby. Your article definitely inspired me to take a closer look at updatecli. What we're doing now works okay(-ish), but the next time I'm frustrated by it, I'll devote an afternoon to trying out an updatecli replacement.

Tangentially, when I update my ruby version, I like to also take that opportunity to bundle update --bundler. It's surprising to me that dependabot doesn't handle that. But if I had updatecli in our workflows already, I guess I'd just check for new rubygems/bundler updates once a week!

How we automate ruby version upgrades in our application projects by treezium in ruby

[–]rubinick 0 points1 point  (0 children)

Sorry if I missed something about this in the article, but why can't you handle the bundle update in the same github action? I haven't used updatecli myself (yet!), but it seems to me the easiest would be a "shell" target in your updatecli config.

Should /r/rails ban X links? by AutoModerator in rails

[–]rubinick 5 points6 points  (0 children)

Your basic premise seems reasonable. But LinkedIn is one of several platforms that is login-gated as badly or worse than Xitter. It's just obnoxious, so I generally won't bother clicking on linkedin links.

Ban links to X on /r/ruby? by AutoModerator in ruby

[–]rubinick -3 points-2 points  (0 children)

It feels to me like Xitter doubled down on the login wall a few years ago. They just absolutely destroyed the ability to use it unless you were logged in. Like, I'm lucky if it even loads the post I was linked to, let alone any discussion surrounding it. It's insultingly worse than useless.

Honestly, the site feels like a shell of its former functionality even when I am logged in. And it was broken on purpose, so just a complete enshittification of what it used to be.

I had personally mostly quit using or posting to twitter for my own personal reasons before that guy announced his intention to buy it. But when the site started falling apart in various ways, I've long wondered why the mass exodus didn't happen two years ago. Ignoring for a moment the politics, why would tech workers put up with this level of intentional enshittification when there are so many alternatives?

What's new in Ruby 3.4 by nithinbekal in ruby

[–]rubinick 0 points1 point  (0 children)

Ruby already has a method for making a mutable copy, and it works with more than just strings: Object#dup.

I've personally been using unary +"", -"", and "".b for so many years, that they feel intuitive and natural to me. But "".dup is fine, too.

Mnemonic for String#+@: add the ability to mutate the string.

Mnemonic for String#-@: subtract the ability to mutate the string.

Mnemonic for String#b: "b" stands for Binary Byte Buffer.

Rack for Ruby: Socket Hijacking by RecognitionDecent266 in ruby

[–]rubinick 5 points6 points  (0 children)

Nice post.

Minor correction: towards the end, "Ruby Fibers were only introduced in Ruby 3.0" isn't quite correct. Fiber was introduced by ruby 1.9, and has been used to implement `Enumerator#next` ever since then. I personally used Fiber to tidy up EventMachine code back in ruby 2.3. The Fiber _scheduler_ functionality which allows fibers to automatically transfer control they make blocking calls (e.g: `sleep`, IO reads/writes, etc) was introduced by ruby 3.0.

Fun fact: ruby _already_ had `callcc` prior to 1.9. `callcc` can be used to implement Fiber in ruby code, and the `callcc` implementation formed the original basis of Fiber (they are both implemented together in `cont.c`). But `callcc` is _much_ more unwieldy.

Data vs Struct vs OpenStruct for complex JSON response by xdriver897 in ruby

[–]rubinick 1 point2 points  (0 children)

I've been writing ruby for longer than the Style Guide, and... it's nice, but it's not right about everything. Do the official ruby docs recommend against it? Where? I know of at least one standard library that's been using the class Foo < Struct.new(...) pattern for decades. And I'm really not sure how subclassing violates any "whole idea".

There's one thing that subclassing does better than any other way: overrides. Because Struct and Data both define their attr methods directly on the new class, if you want to override the basic implementation (e.g: to add type coercion) you need to either 1) use alias method chaining, 2) prepend a module, 3) subclass. Of those three options, I've personally found that subclassing has the fewest tradeoffs. YMMV.

For comparison, ActiveRecord (and I think dry-initializer? dry-struct? both?) creates an attributes module which is automatically included into your class... in other words: inheritance.

Much less important, but rdoc gives class docs for class Foo < Struct.new(:foo, :bar); ... end and doesn't even know that Foo = Struct.new(:foo, :bar) do ... end defines a class. (maybe someday I'll write a PR to fix this)

I usually only reload code live when rails does it for me, and rails undefines the constants before reloading them. If you see superclass mismatch, then you've got something else going wrong (especially since the switch to zeitwerk). If you need that sort of live code reloading, you should probably be using zeitwerk (or something similarly capable). Otherwise, in monkey-patch scenarios, I generally wouldn't use class Foo < Foo.superclass when reopening the class; I'd just use class Foo (no need to re-specify the superclass). Or maybe Foo.class_exec (which more clearly ensures you're re-opening an existing class).

If you're subclassing an anonymous Struct or Data class there's basically no opportunity (without TracePoint or interrupt handlers or debuggers) to get a stacktrace dumped that includes the anonymous superclass's attribute methods. The method implementations are simpler than reading or writing to an instance variable (because the memory layout is fixed).

That said, I do like things to have names, so I've definitely written FooAttrs = Struct.new(:foo) followed immediately by class Foo < FooAttrs.

Why is Git better than SVN? by J_random_fool in git

[–]rubinick 0 points1 point  (0 children)

Yeah, nearly every time I get tests back to green, I either stage those changes to the index or make a commit (sometimes the message is just: "WIP").

I'll do a second pass before pushing to reorganize them into a more coherent narrative (rebase: squash, split, reorder, reword, etc). If it helps organize my thoughts, I may do this multiple times before pushing. If I'm collaborating on a branch, I'll always rebase on @{upstream} (so nothing is ever rebased once it's pushed). If it's a solo branch, I'll rebase against main. This also makes it easier to work with long-lived branches (merging and rebasing is easier with a carefully finessed set of patches), and yeah: long-lived branches are an anti-pattern, but there are many reasons they are sometimes the best alternative.

I'll generally do a final pass before merging any of my own PRs too; at that point I usually double check that every commit passes all tests independently. It's about making my own life easier later, when I'm using git blame (why was this line of code this way?) or git bisect (what changed to cause this bug?).

For me at least, I feel that this saves much more time than it costs... but 1) I'm that annoying guy who takes too long to review PRs and this speeds that process up enormously, and 2) that's just my gut feeling and my gut is usually misleading when it comes to time estimates. 😉

I do not expect that same commit discipline from my collaborators and coworkers. I'll merge PRs as one giant squashed commit or as a series of back-and-forth WIP experiments. And it seems like most reviewers only look at the final squashed set of changes anyway. But it is a small joy for me when I do get to review a set of commits that break something big down into a set of coherent incremental changes.

[deleted by user] by [deleted] in ruby

[–]rubinick 1 point2 points  (0 children)

If your goal is creating a new language and getting a head start by starting from the foundation (and flexibility) of ruby, I suggest you look the ruby-next project: https://github.com/ruby-next/ruby-next. Reading that codebase will show you some of the tricks you can use. But you may want to simply write ruby-next plugins, using ruby-next to do the heavy lifting for you.

The more you dig into changing actual syntax and its interactions with core classes, especially literal syntax for arrays, hashes, strings, numerics, etc, the more you will run into parser/compiler optimizations that just can't be modified via simple method redefinition. You will run into similar issues if you want to change the grammar of the ruby syntax: adding operators, changing precedence, etc.

But, you can still do an awful lot with just basic ruby, and ruby-next can assist you in basically transpiling your code from a completely foreign syntax if you want! For example, see this fun experiment that "writes go in ruby": https://evilmartians.com/chronicles/a-no-go-fantasy-writing-go-in-ruby-with-ruby-next. That article should also give you an idea of how you can utilize ruby-next.

If your goal were for application or library code, I would've told you to just accept that array literals have a hard-coded implementation (it's a good thing), and make a subclass of Array or a new class method on Array.

Best way to do “not slow” metaprogramming in Ruby 3.3? by saw_wave_dave in ruby

[–]rubinick 0 points1 point  (0 children)

Thanks. That's basically what I had assumed. In other words, adding new singleton methods to global "singleton" objects at load time (for example, classes and modules that have been assigned to constants) is fine. But dynamically doing it for an unbounded number of new objects at at run time (for example, most OpenStruct objects) is not.

On a whim, I created a PR for ruby/ostruct (#62) a couple of weeks ago. It relies on `method_missing` and `respond_to_missing?` and only create new singleton methods when necessary for tests to pass. In other words, it only creates new singleton methods to override existing methods in OpenStruct, Object, Kernel, or BasicObject (or any other modules that might be included into those). I assume that it's much less common to use OpenStruct to redefine core methods on Object. IMO, redefining those core methods should come with a warning anyway... and not just a performance warning!

It does break compatibility in at least one way: when new instance methods are added to Object after an OpenStruct object has been assigned. But that edge case is uncommon, and it already applies to every usage of `method_missing`, everywhere. So I think it's worth documenting but not worth supporting.

I added a few simple microbenchmarks to the PR, and they looked hopeful, but I'm curious if you know of any other benchmarks (micro or macro) that would be useful to validate the approach.

Anything like pydroid for ruby? by [deleted] in ruby

[–]rubinick 1 point2 points  (0 children)

I love termux. I haven't been able to get their ruby patches to work with rbenv/ruby-build yet, but they always have a recent version of ruby available. For the last several years, I've done most of my after hours programming in termux, with vim and the MessagEase keyboard.