RSpec: Efficiently rerunning failed examples during development

Note

Don't use reruns as a mean to work around flaky tests. You should always try to fix those instead of rerunning them regularly.

Setup

Configure RSpec to persist the result of your test runs to a file. This is necessary to be able to rerun examples.
Add this to your spec/spec_helper.rb :

  config.example_status_persistence_file_path = 'spec/examples.txt'

Rerun all failed examples using --only-failures

bundle exec rspec --only-failures (or `...

Using Ruby's Method objects for inspecting methods

Do you remember finding where a method is defined?

I recently learned from a senior colleague that Method objects are quite useful within a debugging feast to find out the currently defined internals of methods, because they are either called within the current context or because you want to learn something about the API of the current objects.

Why is this useful?

This is especially useful since Ru...

Use rbenv-each to run a command for every installed Ruby version

The linked rbenv plugin rbenv-each is very helpful to keep QoL gems up to date that are not part of the Gemfile.

For example, you can bump the geordi version for all your rubies with the following command:

rbenv each gem update geordi

Another useful example would be to bulk-update bundler or rubygems.

Note that rbenv-each hasn't been updated since 2018, but it is fully functiona...

What collapsing margins are, how they work and when margins do not collapse

Imagine you have 2 HTML boxes. The first one has a margin-bottom of let's say 30px and the second one a margin-top of 20px. After rules of collapsing margins have been applied we have a margin of 30px (not 50px) between these two boxes . This is because no addition of both margins takes place but the maximum of both is applied. This behavior is called collapsing margins.

Oftentimes it is a good behavior but collapsing margins can be annoying, too. For example child el...

Be careful to use correct HTTP status codes for maintenance pages

When your public-facing application has a longer downtime for server maintenance or long migrations, it's nice to setup a maintenance page to inform your users.

When delivering the maintenance page, be very careful to send the correct HTTP status code. Sending the wrong status code might get you kicked out of Google, or undo years of SEO work.

Popular footguns

Here are some ways to shoot yourself in the foot during maintenance:

  • If all your routes send a "200 OK" with a HTML body "We're b...

Ag: Very fast grep replacement

Ag (aka "the silver searcher") is a very fast replacement for grep.

It will parse your .gitignore for additional speedup. To ignore even more files (node_modules, *.min.js etc), add an .ignore with syntax identical to .gitignore.

See Faster Grepping in Vim for hints about vim integration.

Devise: How to send asynchronous emails

By default, Devise sends all emails synchronously with deliver_now.

To change that, Devise's readme suggests overwriting the send_devise_notification method like this:

class User

  def send_devise_notification(notification, *args)
    devise_mailer.send(notification, self, *args).deliver_later
  end
  
end

However, there is one problem: When deliver_later enqueues the mail with ActiveJob, the job arguments are logged. In case of a password reset, this includes the password reset token, which should not be logged.

A...

Tasks, microtasks, queues and schedules - JakeArchibald.com

The way that Javascript schedules timeouts and promise callbacks is more complicated than you think. This can be the reason why callbacks are not executed in the order that they are queued.

Please read this article!


This is an extract of the example in the article which demonstrates the execution order of tasks and microtasks.

console.log('script start');

setTimeout(function() {
  console.log('setTimeout');
}, 0);

Promise.resolve().then(function() {
  console.log('promise1');
}).then(function() {
  console.log('promise2');
})...

Restarting all God tasks

To restart all tasks monitored by God, don't use god restart. This command is only meant to soft-restart a given process or group.

Instead you should:

god stop
god terminate
god start -c yourgodconfig.god

Rails: Talking to the database without instantiating ActiveRecord objects

Instantiating ActiveRecord objects comes expensive. To speed up things, you can choose a more direct way to talk to your database: the ActiveRecord::ConnectionAdapters::DatabaseStatements module.

Using the module and its methods is not suggested in the usual in-app workflow, as validations, callbacks, custom getters/setters etc. are ignored. However, for database-centered stuff like migrations, these fill the gap between writing pure SQL and full...

How to work around selenium chrome missing clicks to elements which are just barely visible

Chromedriver (or selenium-webdriver?) will not reliably scroll elements into view before clicking them, and actually not click the element because of that.

We've seen this happen for elements which are just barely in the viewport (e.g. the upper 2px of a 40px button). Our assumption is that the element is considered visible (i.e. Capybara::Selenium::ChromeNode#visible? returns true for such elements) but the Selenium driver wants to actually click the center of the element which is outside of the viewport.

We don't know who exactly i...

HTML: Auto fill-in OTP received in text message (SMS)

Browsers can auto fill-in one time codes if advised. Use it like this:

<input autocomplete="one-time-code">

Demo: https://twitter.com/sulco/status/1320700982943223808

Browser support is pretty good since mid-2022 (Chrome 93+, no Firefox).

Upgrade Rails: Awareness list

Disclaimer

This card is a collection of guides and things to have in mind when upgrading to a specific version. It is not meant to be complete, so please feel free to contribute!

General workflows

Upgrade to Rails 7

Don't use log level :debug in your production environments

Catch phrase

You don't want sensitive user data in your logs.

Background

Rails per default filters sensitive data like passwords and tokens and writes [FILTERED] to the logs. The code which is responsible for enabling that usually lives in filter_parameter_logging.rb (Rails.application.config.filter_parameters). Here is an example of a filtered log entry:

Unfiltered:
`User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."token" = $1 LIMIT $2 [["token", "secret-token"], ["LIMIT", 1]]`

After the filter is appl...

Advanced plotting in Ruby with Gnuplot

Besides Plotting graphs in Ruby with Gruff, which comes handy for many uses cases, you sometimes might need configuration for more advanced plots, e.g. for academic concerns. Then using Gnuplot, the first academic open source plotting software, might be a good option.

There are several wrappers for Ruby available and I mainly looked at one of the two most frequently used ones, which are [ruby_gnuplot](https://github.com/rdp/ruby_gnuplot...

Split your parallel tests by execution time and keep execution logs up to date

Both knapsack and parallel_tests have the option to split groups by historic execution time. The required logs for this might be outdated since you manually have to update and push them into your repository.

The following card includes an option how you can keep them consistently up to date with no extra effort locally and/or remotely.

How to always split by execution logs

Parallel Tests

The parallel_tests gem has the option flag `--group...

Lightning Talk: Coverage based Test Case Prioritization in Ruby on Rails

For my computer science bachelor's thesis I programmed and evaluated a CLI Test Case Prioritization (TCP) tool for makandra. It has been written as a Ruby Gem and was tested and evaluated against one Ruby on Rails project. This card will summarize and present the research results, the evaluation and the programmed CLI tool.

The code has been published for educational purposes on GitHub. The german bachelor's thesis has also been included for download at the end.


...

What Ruby’s ||= (Double Pipe / Or Equals) Really Does

It is a common misunderstanding that all [op]=-operators work the same way, but actually they don't.

||= and &&=

Those are special cases, because the assignment will only happen if the first variable passes the check (false or nil for || and true for &&).

a ||= b   # => a || (a = b)
a &&= b   # => a && (a = b)

But still, if reading a has any side effects, they will take place regardless of to what a resolves.

Other [op]=

Assignment will always take place, no matter the value of a.

Geordi: How to rerun failed features

Geordi's cucumber command has a --rerun option that reruns failing tests the given number of times. Usage:

geordi cucumber path/to/features --rerun=2
geordi cucumber path/to/features -r2

Background and how to rerun manually

Cucumber will save a file tmp/parallel_cucumber_failures.log containing the filenames and line number of the failed scenarios after a full test run. Normally you can say cucumber -p rerun (rerun is a profile defined by default in config/cucumber.yml) to rerun all failed scenarios.

Here are a few al...

Git restore vs. reset for reverting previous revisions

The git doc states on the difference of these two commands:

  • git-restore[1] is about restoring files in the working tree from either the index or another commit. This command does not update your branch. The command can also be used to restore files in the index from another commit.
  • git-reset[1] is about updating your branch, moving the tip in order to add or remove commits from the branch. This operation changes the commit history.

git reset can also be used to restore th...

redirect_to and redirect

There are multiple ways to redirect URLs to a different URL in Rails, and they differ in small but important nuances.

Imagine you want to redirect the following url https://www.example.com/old_location?foo=bar to https://www.example.com/new_location?foo=bar.

Variant A

You can use ActionController::Redirecting#redirect_to in a controller action

class SomeController < ActionController::Base
  def old_location
    redirect_to(new_location_url(params.permit(:foo))) 
  end
end

This will:

  • It will redirect with a 302 st...

Do not pass params directly into url_for or URL helpers

Rails' url_for is useful for generating routes from a Hash, but can lead to an open redirect vulnerability.

Your application's generated route methods with a _url suffix are also affected because [they use url_for unter the hood](https://github.com/rails/rails...

Do not use "permit!" for params

Rails' Strong Parameters enable you to allow only specific values from request params to e.g. avoid mass assignment.

Usually, you say something like params.permit(:email, :password) and any extra parameters would be ignored, e.g. when calling to_h.
This is excellent and you should definitely use it.

What is permit! and why is it dangerous?

However, there is also params.permit! whic...

Running external commands with Open3

There are various ways to run external commands from within Ruby, but the most powerful ones are Open3.capture3 and Open3.popen3. Since those can do almost everything you would possibly need in a clean way, I prefer to simply always use them.

Behind the scenes, Open3 actually just uses Ruby's spawn command, but gives you a much better API.

Open3.capture3

Basic usage is

require 'open3'

stdout_str, error_str, status = Open3.capture3('/some/binary', 'with', 'some', 'args')
if status.success?...