Greg Navis Profile picture
Jan 5 18 tweets 5 min read
🆕 Ruby 3.2 is out, so let's start 2023 by exploring what's new.

Many of the changes warrant separate deep-dives, but we can describe them briefly to raise awareness.

Let's roll!
1/16 New hook method! 🎉

Whenever a new constant is added to a module, .const_added is called with that constant's name as a symbol.

Example use case: automatic registration of classes inside a module with some third-party API
2/16 New core class: Data

Data is like Struct for immutable value objects. Changing its attributes is only possible by creating a new instance with the updated values.

I think immutability is a promising direction to explore in Ruby.
3/16 Rest and keyword rest arguments can be forwarded anonymously.

In other words: if a method only references rest arguments to forward them to another method then these rest arguments can be unnamed.

Result: positional, keyword, and block arguments can be anonymous!
4/16 Ruby pattern matching is constantly evolving, and 3.2 is another step in its evolution.

The find pattern is no longer considered experimental.

Find pattern = one or more adjacent array items, with some items before or after them.
5/16 Regexp performance improvements / DoS protection

Many regexps can now be matched in linear time, providing DoS protection.

A match timeout can be set, providing a fallback protection.
6/16 Error reporting improvements

Syntax errors are more informative, and try to point to causes.

Type and argument errors are mapped to more specific locations in program text.

Such quality of life improvements DO accumulate, and help produce great developer experience.
7/16 WASI-based WebAssembly

The Ruby interpreter can now be run in WebAssembly environments (e.g. the browser), and interact with the operating system via WASI.

WASI = set of APIs WebAssembly code can use to manage files, open sockets, etc. outside of browsers.
8/16 YJIT is production-ready 🎉

YJIT is a just-in-time compiler developed by Shopify and GitHub. It's 40% faster in synthetic benchmarks than not using JIT.

Additionally, it supports arm64 now, e.g. Apple M1.

Enable via `ruby --yjit`
9/16 Procs taking one positional parameter + keyword parameters behave in a less surprising way

It's a bit of an edge case, but I'm sure you can totally imagine wasting time debugging it.

Fortunately, Ruby 3.2 is much less surprising here!
10/16 Left-to-right constant assignment on explicit objects

This was another potentially surprising behavior, which was made predictable in 3.2.
11/16 Bundler (but not RubyGems!) has switched its dependency resolution algorithm from Molinillo to PubGrub.

PubGrub is more efficient and offers more helpful error messages in case of conflicting package dependencies.

Another DX improvement!
12/16 Refinements and meta-programming

Ruby 3.2 offers more methods for reflection related to refinements. It's not something you'd use often, but it's there in case you're doing meta-programming.
13/16 MatchData can return a byte range, in addition to character range, for a capturing group.

Very helpful when working with regular expressions at a byte level, as it saves you from determining offsets manually.
14/16 String got three byte-level methods:

• byteindex - byte-level offset of a substring
• byterindex - same as above, but for the last occurence
• bytesplice - replace a range of bytes within the string
15/16 Other improvements:

• MJIT was reimplemented from C to Ruby
• Set is built-in
• Hash: shift returns nil even if there's a default
• Proc: dup and parameters improvements
• Some new parser options
• Struct can be initialized with keyword arguments by default
16/16 Last but not least, some removals:

• Fixnum, Bignum, and a few other consts
• taint- and trust- related methods
• No libyyaml and libffi
That's 99% of changes brought by Ruby 3.2.

I'll explore some of them in deep-dive threads and articles, so follow me @gregnavis not to miss them.

You can help other Rubyists catch up on the latest changed by liking and retweeting the thread.

• • •

Missing some Tweet in this thread? You can try to force a refresh
 

Keep Current with Greg Navis

Greg Navis Profile picture

Stay in touch and get notified when new unrolls are available from this author!

Read all threads

This Thread may be Removed Anytime!

PDF

Twitter may remove this content at anytime! Save it as PDF for later use!

Try unrolling a thread yourself!

how to unroll video
  1. Follow @ThreadReaderApp to mention us!

  2. From a Twitter thread mention us with a keyword "unroll"
@threadreaderapp unroll

Practice here first or read more on our help page!

More from @gregnavis

Nov 29, 2022
💡 Rails deep-dive: let's have a look at the ActionDispatch::Static middleware
1️⃣ First step: familiarize yourself with the comments already there.

The comment provides quite a lot of context for what we're going to see.

• Serves files from a disk directory
• Only GET and HEAD
• Non-existent paths are handed off to the (Rails) app
2️⃣ The middleware itself is surprisingly small. What you see below is literally its entire source code.

Unfortunately, the presence of FileHandler and the call to attempt on in indicate the bulk of complexity lies somewhere else.

Let's dig deeper 🕳️
Read 17 tweets
Nov 28, 2022
💡 Ruby thread: implementing Elixir-style operation pipelines

1️⃣ Step-by-step reasoning behind the implementation
2️⃣ Ruby refinements in action (have you ever used them?)
3️⃣ Tiny bit of syntactic sugar; no magic 🪄
4️⃣ Less than 20 lines of code

⬇️ Let's roll!
Last week we explored the << and >> methods on Proc and Method. That approach had drawbacks:

⚠️ Explicit calls to curry
⚠️ Explicit calls to method
⚠️ Low consistency from one step to another (depending on implementation details).

Let's take an object-oriented approach.
Pipelines are composed of operations, i.e. objects responding to call (much like Procs or Methods). Each can be parametrized via the constructor.

FindUser and ConfirmAccount take no parameters. SendConfirmationNotification takes one.

Problem: it's quite long ...

We can fix it!
Read 13 tweets
Nov 25, 2022
💡 Ruby tip: Ruby procs support currying which can help in composing pipes of procs

We'll explore:

1️⃣ What currying is
2️⃣ How currying is used in Ruby
3️⃣ How currying can help parametrize proc pipelines

⬇️ Let's start with some definitions
Also, be sure you've seen the previous thread on Elixir-style pipelines in Ruby. This thread will make much more sense if you know the context.

Back to currying ...

1️⃣ Currying = converting a regular callable into a higher-order callable taking one argument at a time.

Arguments "accumulate" and the original proc is run when all arguments are ready.

See the example below as it's much easier to understand than a dry definition. Image
Read 8 tweets
Nov 23, 2022
💡 Ruby tip: the method `then` can be used for sequencing operations without polluting scope with variables for intermediate values.

then can be compared to |> in Elixir, although I find the latter superior syntactically.

⬇️ Let's have a look at some before and after code ...
Before: read_json has to

1️⃣ Build a path to a file
2️⃣ Read the file
3️⃣ Parse JSON
4️⃣ Convert keys to symbols

It's _not_ bad, but in a larger method that sequence can add scope pollution (= variables used only in that block but defined until the method returns). Image
After: read_json can be converted to then, resulting in no local variables defined in read_json.

It's a small change, but can help make a larger method more readable.

⚠️ Keep in mind more complex operations (like performing an HTTP request) may need error handling! Image
Read 5 tweets
Nov 16, 2022
💡 Ruby tip: method_missing has 10+ brothers and cousins.

There are more than 10 hook methods that get called in response to certain events.

⬇️ Let's have a quick look at each one along with code examples.
1️⃣ method_added

method_added gets called on the model or class that had a new instance method defined Image
2️⃣ method_removed

method_removed gets called on the model or class that had an existing instance method removed

This looks awfully similar to the next one which is ... Image
Read 17 tweets
Nov 15, 2022
💡 Ruby tip: GEM_PATH and GEM_HOME can be used to customize gem repository location

These environment variables determine where Ruby installed and looks for gems.

⬇️ Let's have a closer look at each.
GEM_HOME determines where gems are installed when running gem install and bundler.

It's trivial to install gems locally to a user without rbenv, rvm, etc. This is how I work on client projects.

Just set GEM_HOME=$HOME/.gems
There's a problem, though.

If you run Ruby and try to require one of those gems then it won't be found (unless it's also present in the default location, but you'd technically require a different installation).

This is where GEM_PATH comes in!
Read 7 tweets

Did Thread Reader help you today?

Support us! We are indie developers!


This site is made by just two indie developers on a laptop doing marketing, support and development! Read more about the story.

Become a Premium Member ($3/month or $30/year) and get exclusive features!

Become Premium

Don't want to be a Premium member but still want to support us?

Make a small donation by buying us coffee ($5) or help with server cost ($10)

Donate via Paypal

Or Donate anonymously using crypto!

Ethereum

0xfe58350B80634f60Fa6Dc149a72b4DFbc17D341E copy

Bitcoin

3ATGMxNzCUFzxpMCHL5sWSt4DVtS8UqXpi copy

Thank you for your support!

Follow Us on Twitter!

:(