Debugging SPF records

While debugging a SPF record I found spf-record.de to be very helpful.

  • it lists all IPs that are covered by the SPF record
  • shows syntax errors
  • helps you debugging errors like DNS lookup limit reached
  • it also lets you test a new SPF strings before applying it. This can save you time as you don't have to loop with operations

Also the advanced check at vamsoft.com has a very good interface to test new SPF policies.

List of Helpful RubyMine Shortcuts

Navigation

CTRL + SHIFT + ALT + N

Search for any symbol in your application, like CSS classes, Ruby classes, methods, helpers etc.

CTRL + SHIFT + N

Search for filename in your application (also dependencies)

CTRL + E

Open a list of recently opened files

ALT + POS1

Open a the navigation bar as a context menu. Allows you to quickly navigate between files.

CTRL + G

Go to line

Actions

CTRL + SHIFT + A

:...

Gatekeeping: Guide for gatekeeper

If you're responsible for gatekeeping in a projects, here is a guide, what to do.
In order to reduce the number of rejects we get from clients, we want to review all code written before it goes to the staging server.

Note: This process is tailored to our specific needs and tools at makandra. While it will certainly not apply to all (especially larger teams), we think it is a helpful starting point.


First, read the [Gatekeeping for developers](https://makandracards.com/makandra/6579-gatekeeping-guide-for...

How to use Active Job to decouple your background processing from a gem

In a web application you sometimes have tasks that can not be processed during a request but need to go to the background.
There are several gems that help to you do that, like Sidekiq or Resque.

With newer Rails you can also use ActiveJob as interface for a background processing library. See here for a list of supported queueing adapters.
For ...

SameSite cookies

TL;DR Most web applications do not require action on this. SameSite=None (old browser default) will continue to work, and SameSite=Lax (new Chrome default, gradually rolled out) is an even better default for cookies. Set SameSite=Strict only for extra security in special cases (see below). If your application is rendered in an iframe (e.g. a video player or some news stream), you need to configure its relevant cookies as SameSite=None.


The SameSite cookie attribute targets **c...

Building web applications: Beyond the happy path

When building a web application, one is tempted to claim it "done" too early. Make sure you check this list.

Different screen sizes and browsers

Desktops, tablets and mobile devices have all different screen resolutions. Does your design work on each of them?

  • Choose which browsers to support. Make sure the page looks OK, is usable and working in these browsers.
  • Use @media queries to build a responsive design
    • If you do not suppo...

Gatekeeping: Guide for developer

If your project manager wants to do gatekeeping on a project, as a developer you need to follow the following guidelines (e.g. by using something like this issue checklist template).

In order to reduce the number of rejects we get from clients, we want to review all code written before it goes to the staging server.

Note

This process is tailored to our specific needs and tools at makandra. While it will certainly not apply to all (especially larger teams), we think it...

How to use html_safe correctly

By default, Rails views escape HTML in any strings you insert. If you want to insert HTML verbatim, you need to call #html_safe. However, #html_safe does not "unescape" a string. It merely marks a string as safe for unescaped insertion.

How html_safe works

Calling html_safe on a String returns a new object that looks and acts like a String, but actually is a ActiveSupport::SafeBuffer:

"foo".length
# => 3
"foo".class
# => String

"foo".html_safe.length
# => 3
"foo".html_safe.class
# => ActiveSupport::S...

Integrating or upgrading makandra-rubocop

Introduction

Most of the time it is a tedious task to apply a code style guide to an existing code base as there are likely to be a lot of conflicts. At makandra we are using makandra-rubocop to have code style checks. Here is some advice on how to add makandra-rubocop efficiently.

Note

RubyMine by default has a Rubocop inspection with rules that we don't always agree with. We recommend replacing this with makandra-rubocop or disabling the inspection.
...

How to write complex migrations in Rails

Rails gives you migrations to change your database schema with simple commands like add_column or update.
Unfortunately these commands are simply not expressive enough to handle complex cases.

This card outlines three different techniques you can use to describe nontrivial migrations in Rails / ActiveRecord.

Note that the techniques below should serve you well for tables with many thousand rows. Once your database tables grows to millions of rows, migration performance becomes an iss...

Heads up: network requests `Kernel#open` are not mocked with VCR

We usually rely on VCR and WebMock to prevent any real network connection when running our unit tests.

This is not entirely true: They are both limited to a set of HTTP libraries listed below (as of 2022). Direct calls to Kernel#open or OpenURI#open_uri are not mocked and will trigger real network requests even in tests. This might bite you e.g. in [older versions of CarrierWave](https://github.com/carrierwaveuploader/carrierwave/blob/0.11-stable/lib/carrierwave/upl...

VCR: Alternative way of mocking remote APIs

If you need to test interaction with a remote API, check out the VCR gem as an alternative to Webmock or stubbing hell.

The idea behind VCR is that is performs real HTTP requests and logs the interaction in a .yml file. When you run the test again, requests and responses are stubbed from the log and the test can run offline.

It's a great way to mock network requests to an external service without going through the pain of log...

How to set up database_cleaner for Rails with Cucumber and RSpec

Add gem 'database_cleaner' to your Gemfile. Then:

Cucumber & Rails 3+

# features/support/database_cleaner.rb

DatabaseCleaner.clean_with(:deletion) # clean once, now
DatabaseCleaner.strategy = :transaction
Cucumber::Rails::Database.javascript_strategy = :deletion

Cucumber & Rails 2

The latest available cucumber-rails for Rails 2 automatically uses database_cleaner when cucumber/rails/active_record is required -- but only if transactional fixtures are off. To have database_cleaner work correctly:

  1. Add the ...

Your browser might silently change setTimeout(f, 0) to setTimeout(f, 4)

When you're nesting setTimeout(f, 0) calls, your browser will silently increase the delay to 5 milliseconds after the fourth level of nesting.

This is called "timeout clamping" and defined in the HTML spec:

If nesting level is greater than 5, and timeout is less than 4, then set timeout to 4.

Timeouts are clamped harder in background tabs

On a similar note, all major browsers have implemented throttling rules for setInterval and setTimeout calls from tabs...

RSpec: Composing a custom matcher from existing matchers

When you find similar groups of expect calls in your tests, you can improve readability by extracting the group into its own matcher. RSpec makes this easy by allowing matchers to call other matchers.

Example

The following test checks that two variables foo and bar (1) have no lowercase characters and (2) end with an exclamation mark:

expect(foo).to_not match(/[a-z]/)
expect(foo).to end_with('!')

expect(bar).to_not match(/[a-z]/)
expect(bar).to end_with('!')

We can extract the repeated matcher chains into a custom m...

How to: Use git bisect to find bugs and regressions

Git allows you to do a binary search across commits to hunt down the commit that introduced a bug.

Given you are currently on your branch's HEAD that is not working as expected, an example workflow could be:

git bisect start # Start bisecting
git bisect bad # Tag the revision you are currently on (HEAD) as bad. You could also pass a commit's SHA1 like below:
git bisect good abcdef12345678 # Give the SHA1 of any commit that was working as it should
# shorthand:
git bisect start <bad ref> <good ref>

Git will fetch a comm...

How to: Benchmark an Active Record query with a Ruby script

Recently I needed to benchmark an Active Record query for performance measurements. I wrote a small script that runs each query to benchmark 100 times and calculates the 95th percentile.

Note: The script requires sudo permissions to drop RAM cache of PostgreSQL. Due to the number of iterations it was impractical to enter my user password that often. And I temporary edited my /etc/sudoers to not ask for the sudo password with johndoe ALL=(ALL) NOPASSWD: ALL.

# Run this script with e.g. `rails ru...

PostgreSQL: Be careful when creating records with specific ids

In tests, it is sometimes useful to create records with specific ids. On PostgreSQL this can cause problems:

Usually, PostgreSQL uses an "autoincrement" sequences to provide sequential ids for new database rows. However, these sequences will not increment if you insert a record "by hand". This will cause an error:

record = Record.create!
record.id                             # => 100, next automatic id will be 101
Record.create!(id: record.id + 1)     # okay, but next automatic id will still be 101
Record.create!                       ...

Spreewald development steps

Our gem spreewald supports a few helpers for development. In case you notice errors in your Cucumber tests, you might want to use one of them to better understand the underlying background of the failure. The following content is also part of the spreewald's README, but is duplicated to this card to allow repeating.

Then console

Pauses test execution and opens an IRB shell with current cont...

Using the ActiveSupport::BroadcastLogger

The ActiveSupport::BroadcastLogger allows you to log to multiple sinks. You know this behavior from from the rails server command, that both logs to standard out and the log/development.log file.

Here is an example from the ActiveSupport::BroadcastLogger API:

stdout_logger = ActiveSupport::Logger.new(STDOUT)
file_logger = ActiveSupport::Logger.new("development.log")
broadcast = ActiveSupport::BroadcastLogger.new(stdout_logger, file_logger)

broadcast.i...

RSpec: Running examples by name (or running a single shared example)

When an Rspec example fails, I usually investigate by running that example again using rspec <file:line>. However, this does not work with shared examples, since Rspec doesn't know in which context the shared example should be run.

But there is a different way: You can run the shared example using the -e, --example option. It takes a string value and runs all scenarios containing that substring in their full description.

This allows you to run a single uniquely named example, all examples with
similar names, all the examples in a u...

A simple example with a GIN index in Rails for optimizing a ILIKE query

You can improve your LIKE / ILIKE search queries in PostgreSQL by adding a GIN index with an operate class ("opclass") to split the words into trigrams to the required columns.

Example

class AddSearchTextIndexToUsers < ActiveRecord::Migration[7.1]
  def change
    enable_extension 'pg_trgm'

    add_index :users, :search_tex...

Custom error pages in Rails

Static error pages

To add a few basic styles to the default error pages in Rails, just edit the default templates in public, e.g. public/404.html.

A limitation to these default templates is that they're just static files. You cannot use Haml, Rails helpers or your application layout here. If you need Rails to render your error pages, you need the approach below.

Dynamic error pages

  1. Register your own app as the applicatio...

Always convert and strip user-provided images to sRGB

Debugging image color profiles is hard. You can't trust your eyes in this matter, as the image rendering depends on multiple factors. At least the operation system, browser or image viewer software and monitor influence the resulting image colors on your screen.

When we offer our users the possibility to upload images, they will most likely contain tons of EXIF metadata and sometimes exotic color profiles like eciRGB. We want to get rid of the metadata, as it might contain sensitiv...