PSA: Umlauts are not always what they seem to be
When you have a string containing umlauts which don't behave as expected (are not matched with a regexp, can't be found with an SQL query, do not print correctly on LaTeX documents, etc), you may be encountering umlauts which are not actually umlaut characters.
They look, depending on the font, like their "real" umlaut counterpart:
- ä ↔ ä
- ö ↔ ö
- ü ↔ ü
However, they are not the same:
'ä' == 'ä' # false
'ä'.size # 1
'ä'.size # 2
Looking at how those strings are constructed reveals what is going on:
'ä'.unpack('U*...
How to combine "change", "up", and "down" in a Rails migration
Rails migrations allow you to use a change
method whose calls are automatically inverted for the down path. However, if you need to some path-specific logic (like SQL UPDATE
statements) you can not define up
and down
methods at the same time.
If you were to define define all 3 of them, Rails would only run change
and ignore up
and down
. However, Rails 4+ features a helper method called reversible
:
class MyMigration < ActiveRecord::Migration
def cha...
RubyMine users: you should be using bookmarks
RubyMine allows bookmarking lines of code. This is super-helpful when working on a complex problem.
I've been using this feature for a few years now, and so should you! :)
Here are the default Linux/Windows keystrokes. See the documentation for other keybindings.
Add an anonymous bookmark
F11
A gray checkmark will be shown in the gutter on the left.
If you press F11
again on a bookmarked line, the bookmark will be removed.
Add a named bookmark ("mnemonic")
Ctrl
...
How the Date Header Affects Cookie Expiration and Caching
tl;dr
When a cookie includes an
Expires
attribute or an HTTP response includes caching headers likeExpires
orCache-Control
, their validity depends on the server'sDate
header if present. Otherwise, the browser uses its local time. This can lead to issues in tests with mocked time or inconsistent cache behavior.
Cookie Expires
depends on the Date
header or browser time
When a cookie includes an Expires
attribute, the browser evaluates the expiration date relative to a reference time:
- If the HTTP response ...
How to enable Rails' file_fixture helper in FactoryBot
In FactoryBot factories, Rails' file_fixture
is not available by default. To enable it, include a support module from rspec-rails:
FactoryBot::SyntaxRunner.include(RSpec::Rails::FileFixtureSupport)
That includes ActiveSupport::Testing::FileFixtures
, where file_fixture
is defined, but also configures the file_fixture_path
so that you can actually use file_fixture
.
Description list support (Updated: List of Helpful RubyMine Shortcuts)
Team C enabled support for description lists in makandracards.
Please make sure to add a newline before the definition starting with colon (:
).
Usage example:
Apple
: A fruit that grows on trees.
Orange
: A citrus fruit known for its vitamin C content.
More details in second paragraph.
will result in:
- Apple
-
A fruit that grows on trees.
- Orange
-
A citrus fruit known for its vitamin C content.
More details in second paragraph.
Updated: Breaking changes for boolean attributes in HAML 6
Updated most aspects of this card.
RSpec: Scoping custom matchers to example groups
When you find yourself in the situation that you would like to define a custom matcher in your specs, but you do not want to define a global matcher since you need it only for your specific test, there are two ways to do it:
Custom matcher for a single group
If you're only going to include
a matcher once, you can also use the matcher
macro within an example group:
describe "group" do
matcher :be_just_like do |expected|
match {|ac...
How to tackle complex refactorings in big projects
Sometimes huge refactorings or refactoring of core concepts of your application are necessary for being able to meet new requirements or to keep your application maintainable on the long run. Here are some thoughts about how to approach such challenges.
Break it down
Try to break your refactoring down in different parts. Try to make tests green for each part of your refactoring as soon as possible and only move to the next big part if your tests are fixed. It's not a good idea to work for weeks or months and wait for all puzzle pieces ...
Getting permanent links to files on Github or Gitlab
Please don't simply copy line number links from Github. The URL usually contains a branch name like master
which will change over time:
https://github.com/makandra/upjs/blob/master/lib/assets/javascripts/up/link.js.coffee#L76
If someone now posts an insertion or deletion to that file into master
your link points to the wrong line!
A better way is to press the Y
key after clicking on a line number. This will transform the URL to another URL that points to the particular commit:
https://github.com/makandra/upjs/blob/b3b14...
TestProf II: Factory therapy for your Ruby tests—Martian Chronicles, Evil Martians’ team blog
Some key highlights and points from the linked article TestProf II: Factory therapy for your Ruby tests.
The Problem with Factories in Ruby Tests
- Factories are used to easily generate test data.
- However, they can unintentionally slow down test suites by creating unnecessary or excessive associated data (factory cascades).
Understanding Factory-Induced Slowdowns
- Factories often create additional data (e.g., associated records) th...
Fragment Caching in Rails 7.1+ requires Haml 6
Rails slightly changed the fragment cache implementation from Rails 7.0 to Rails 7.1. Unfortunately, this is incompatible with how Haml 5 patches Action View buffers. I tried turning a String
buffer into an ActionView::OutputBuffer
, but this brought up...
Text fragments in the browser URI fragment
Text fragments allow linking directly to a specific portion of text in a web document, without requiring the author to annotate it with an ID, using particular syntax in the URL fragment. Supporting browsers are free to choose how to draw attention to the linked text, e.g. with a color highlight and/or scrolling to the content on the page. This is useful because it allows web content authors to deep-link to other content they don't control, without relying on the presence of IDs to make that possible. Building on top of that, it could be u...
How to enable pretty IRB inspection for your Ruby class
When Ruby objects are inspected in any modern IRB, some objects (like ActiveRecord instances) are rendered with neat colors and line breaks.
You will not get that for custom classes by default -- which can be annoying if your inspection contains lots of meaningful information.
Here is what you need to do if you want your objects to be inspected nicely.
Implement a pretty_print
method
As an example, consider the following class.
class MyClass
# ...
def inspect
"#<#{self.class} attr1: #{attr1.inspect}, attr2: #{attr2...
Generating an Entity Relationship Diagram for your Rails application
This card explains how to generate an entity relationship diagram for your Rails application.
We also show how to limit your ERD to a subset of models, e.g. models inside a namespace.
Generating a full ERD
Option A: RubyMine
- Right-click anywhere in your project tree
- In the context menu, find the "Diagrams" menu item at/near the bottom
- Inside, choose "Show diagram" → "Rails Model Dependency Diagram"
- A new tab will open with the diagram inside. You can modify it there, and export it as an image.
Option B: Use rails-e...
Custom error messages in RSpec or Cucumber steps
Sometimes you have a test expectation but actually want a better error message in case of a failure. Here is how to do that.
Background
Consider this test:
expect(User.last).to be_present
In case of an error, it will fail with a not-so-helpful error message:
expected present? to return true, got false (Spec::Expectations::ExpectationNotMetError)
Solution
That can be fixed easily. RSpec expectations allow you to pass an error message like this:
expect(User.last).to be_present, 'Could not find a user!'
...
Heads up: counting may be slow in PostgreSQL
The linked article points out that COUNT
queries might be unexpectedly slow in PostgreSQL.
If you just need to know "are there any records" use any?
. This uses SELECT 1 AS one FROM ... LIMIT 1
under the hood.
If you just need to know "are there no records" use empty?
or none?
. This uses SELECT 1 AS one FROM ... LIMIT 1
under the hood.
In short: Replace foo.count > 0
with foo.any?
or foo.count == 0
with foo.none?
If you require quick counts and can tolerate some level of imprecision, consider exploring the ...
Debugging Capistrano
Capistrano 3 has a doctor
task that will print information about
- Environment: Ruby, Rubygems and Bundler versions
- List of Capistrano gems and whether an update is available
- All config variables and their values
- Capistrano server config
$ bundle exec cap staging doctor
Rails 7 adds #caching? and #uncacheable!
Rails' fragment caching caches subtrees of an HTML document tree. While constructing that tree though, it can be really hard to keep track of whether some code is run in a caching context. Fortunately, Rails 7 brings two helpers that simplify this.
Note that these helpers are all about Rails' fragment caching and not about downstream caching (i.e. Cache-Control).
uncacheable!
Invoke this helper in a partial or another helper that should never be cached. Used outside of fragment caches, the helper does just nothing. But should it ...
Enabling YJIT
YJIT is Ruby's default just-in-time compiler. It is considered production-ready since Ruby 3.2 (source).
To activate YJIT you need two steps:
- Your
ruby
binary needs to be compiled with YJIT support. - You need to enable YJIT.
Getting a Ruby with YJIT support
We usually install Ruby with tools like rbenv
or asdf
. This compiles the ruby
binary from the source code. Support for YJIT
will be automatically added during this compilation...
Running Rubocop as a pre-push hook
Git has the concept of hooks: bash scripts that are invoked at certain points in the Git lifecycle. One handy use is a pre-push hook that runs Rubocop. It will prevent pushing code that Rubocop finds fault with.
Configuring the hook
Git hooks are normally stored locally with a repository. They are not committed.
- Store this snippet in .git/hooks/pre-push:
if [ -f ./.rubocop.yml ]; then
echo 'Running Rubocop ...'
bundle exe...
Updated: Rails: Manually decrypting a session cookie
- Added section for Rails 7