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
- [Rails 7.1: Take care of...
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?...