1. Vacuum, I/O performance, and query performance gains. 2. JSON_TABLE for converting JSON documents into tables. 3. More features added to MERGE. 4. Simpler upgrades when using logical replication + failover!
💡 Rails tip: Rails 7 shipped with error handling infrastructure
It's a small module, but I find it extremely useful, especially when integrating with third-party APIs, as you usually want to gracefully inform the user the API is down, but still report that internally.
Ready?
At a high level:
- there are multiple subscribers registered with the error reporter
- each error is passed to each subscriber
- the reporter offers methods for detecting, reporting, and swallowing exceptions thrown by the app
Let's take a close look at those methods.
Apr 5, 2023 • 7 tweets • 3 min read
💡 Ruby meta-programming tip: it's trivial to add support for object literals to Ruby
Object literals are frequently found in JavaScript: these are objects defined on the spot, both state and methods, without a prior class definition.
Let's try to add them to Ruby! ⬇️
Let's start with a JavaScript example: an app object comprised of a database connection and a router; there's a method to start the whole thing up.
The app object was instantiated without a class definition.
How would a Ruby counterpart look like?
Mar 25, 2023 • 9 tweets • 3 min read
💡 Ruby meta-programming: lazy accessors are simple to implement and more robust than lazy computations via ||=
Meta-programming can be fun, productive, and helpful. Let's have a look at another example, including a bigger engineering lesson.
⬇️ Let's go!
What's a lazy accessor?
It's an accessor with a block of code to determine a value. That block of code is called only on first use; subsequent calls reuse the value the block returned.
In short: lazy accessor = laziness + caching
Mar 24, 2023 • 12 tweets • 4 min read
💡 Ruby meta-programming idea: final classes can be easily implemented in Ruby
I've actually published a gem whose first feature is exactly that. More on that later though. Let's understand the concept of final classes first.
Ready? Set? Go!
What's a final class?
A final class is a class that cannot be inherited from. Any attempt to subclass it would result in an error (compile-time or runtime, depending on the language).
It's a foreign concept in Ruby land, so let's have a look at other languages.
Mar 8, 2023 • 9 tweets • 3 min read
💡 Rails tip: as_json can be used to convert models into ... no, not JSON ... into hashes
It can be useful when building and working with APIs, so it's always a good idea to understand how it works. It's implemented by Active Model and is quite generic.
Let's dive in! 🤿 😅
Called without arguments it returns a hash including all columns in the model.
Is this a good idea? NO!
⚠️ Do that in a public API and it's guaranteed you'll leak sensitive data sooner or later. Therefore, you should use this rarely, if ever.
What's the alternative?
Feb 27, 2023 • 7 tweets • 2 min read
💡 Ruby tip: hierarchical logger can be built in under 20 lines
Motivated by helping active_doctor_users identify specific parts of their code base that break the tool.
Logs benefit from structure, and it's trivial to add _some_ structure even if they're text-based.
Here's how
Text logs shown as a sequence of lines fail to capture the call stack structure.
If line A is followed by line B then it's difficult to tell whether B is from the same function, a caller function, a callee function, or another place entirely.
Fortunately, it's easy to fix.
Feb 21, 2023 • 7 tweets • 3 min read
💡 Ruby tip: when defining methods, it's easy to teach Ruby to tell the difference between
- no argument passed
- argument equal to the default value passed
⬇️ Code speaks louder than words, so let's jump right in! First, an example motivating the whole thread ...
Let's start with an example of the problem.
Have a look at two method calls below: in the naive approach the "nothing passed" and "nil passed" approaches are indistinguishable.
Our goal: make them distinguishable!
What's great is it's actually quite simple ...
Feb 15, 2023 • 6 tweets • 2 min read
💡 Ruby tip: object allocation and initialization are two separate steps
Usually we just call .new to instantiate a class. Did you know it's possible to allocate an object WITHOUT initializing it?
⚠️ Useless but interesting content ahead!
⬇️ Read on if you're intrigued!
There's a method called .allocate. It returns an instance of a call that IS NOT INITIALIZED 😱
The $1M question is: can such object be initialized?
Attempting to call initialize will result in an error: the constructor is PRIVATE!
Fortunately, this is easy to fix!
Feb 15, 2023 • 6 tweets • 2 min read
💡 Ruby tip: multiple arrays can be easily combined without duplicates
There's no need to convert to and from Set or any other tricks like that. It's all built into Array.
⬇️ Let's have a look at `|` and `union`: two relatively unknown `Array` methods.
Example: we need to send reminders for two types of invoices: unpaid and overdue.
Problem: if an invoice is BOTH unpaid and overdue we may end up sending the reminder twice if we aren't careful
It's more likely when working with two separate set of model IDs.
What can we do?
Feb 8, 2023 • 10 tweets • 3 min read
💡 Rails tip: Active Support offers an instrumentation API
I'm using it in a client project right now and need to say it's quite simple, yet powerful. Unfortunately, it's relatively obscure, too.
Let's put it in the spotlight today 🔦
There are two sides to instrumentation:
• Emitting events, including time measurements
• Subscribing to events
Active Support offers a simple API that handles both. We'll start with emitting events (which is simpler) and then proceed to subscribing and handling.
Feb 6, 2023 • 9 tweets • 3 min read
💡 Ruby tip: use memoization to avoid running expensive operations twice or resolve dependencies automatically
I guess you may be intrigued
A quick way to implement memoization is via ||=
Let's have a look at an example ChurnRiskDetector class. It uses single- and multi-line memoization. More statements can be memoized by wrapping them in begin/end.
There's a problem with this approach, though ...
Feb 3, 2023 • 6 tweets • 2 min read
💡 Ruby tip: assertions usually accept an error message to show when they fail.
Let's have a look at three SIMPLE techniques that can make your test suite much more informative and easier to work with.
Let's start with a relatively unknown parameter to assert ...
The last argument to assert is the error message. It's optional, so if you don't pass it the assertion will work, producing a potentially unclear message.
A bit of discipline (and a linter?) can greatly help make the test output more understandable.
That's not everything ...
Feb 1, 2023 • 6 tweets • 2 min read
💡 Rails tip: Active Record supports the hstore column type in PostgreSQL
hstore = a map from strings to strings (or a null!)
No nesting, no arrays, no other types. And sometimes it's EXACTLY what you need!
Let's have a look at how to use it.
First, you must enabled the relevant PostgreSQL extension. After the extension is enabled, you can use `hstore` as the column type.
The example below adds `feature_flags` to `organizations`.
Jan 28, 2023 • 8 tweets • 3 min read
💡 Ruby tip: don't rely on libraries calling refinement-provided methods
I guess that may sound indecipherable, so let's unpack this step by step. This thread is based on my work on Feature Envy: a bunch of features from other languages implemented in Ruby.
Let's dive in!
Refinements are a Ruby feature for safe monkey patching.
A refinement is a change to an already existing class. Refinements can be grouped in a module which then can be activated (via `using`) in a given scope.
The effect is classes are monkey patched WITHIN THAT SCOPE ONLY.
Jan 18, 2023 • 4 tweets • 2 min read
💡 Ruby tip: hash keys and values can be easily transformed via
• transform_keys
• transform_values
Such small conveniences can make code much more readable.
Let's have a look ...
transform_keys/transform_values are map-like methods that map each key or value respectively, and are helpful when only one part of a hash must be changed.
Example: converting keys to strings.
Code speaks louder than words, so have a look at the snippet below.
Jan 12, 2023 • 7 tweets • 2 min read
💡 Ruby gem: YARD is a great tool for documenting Ruby code
I'm writing documentation for my soon-to-be-released gem, which is a great reason to talk about documenting Ruby.
Documentation, often neglected, is a huge factor in good developer experience.
Let's look at YARD, now.
Code speaks louder than words, so we'll start with a piece of code.
Here's a snippet from the gem I'm working on. It makes use of several YARD tags, but it's the tip of the iceberg!
The resulting HTML document should look familiar to most who look up documentation online ...
Jan 9, 2023 • 13 tweets • 4 min read
💡 Ruby deep-dive: Benchmark makes it easy to measure and compare performance of one or more pieces of code
Useful for:
1. Profiling 2. Picking the highest performer 3. Automating measurements as part of the test suite
Let's have a closer look at what it's got to offer.
First, we need to require "benchmark". We'll have access to Benchmark and several methods implemented on it.
We'll explore each of these methods in turn, from simple to complex.
Jan 5, 2023 • 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
Dec 22, 2022 • 7 tweets • 3 min read
💡 Ruby tip: be careful when storing test data in constants
Constants may look innocent, but can easily lead to flaky tests and implicit test ordering dependencies.
Let's understand what can go wrong!
First, we need to clarify what "constant" means. In Ruby, it's an identifier that cannot be reassigned. The object it points though, can be modified.
⚠️ This is NOT what most people think of when they hear "constant"
Dec 21, 2022 • 5 tweets • 2 min read
💡 Rails tip: Active Support adds convenience methods for working with time intervals and data sizes
• Make code more expressive and intention revealing
• Monkey-patching 🙈
⬇️ Let's have a look at what's available.
⚠️ Inconsistencies ahead!
First, you can write 2.kilobytes instead of 2 * 1024 or 2048. The same is true for units up to exabytes.
Processing zettabytes, yottabytes, or hellabytes? Active Support won't help, but you must be coming from a distant future either way. 😉