Invoices: How to properly round and calculate totals

While it might seem trivial to implement an invoice that sums up items and shows net, gross and vat totals, it actually involves a lot of rules and caveats. It is very easy to create invoices where numbers don't add up and a few cents are missing. A missing cent is a big deal for an accountant, so it is important for your invoices to list correct numbers.

Note that this is not legal advice. Also note that while this note has a number of code examples in Ruby and MySQL, the concepts apply to all programming languages and data stores.

When ...

How to negate scope conditions in Rails

Sometimes you want to find the inverse of an ActiveRecord scope. Depending on what you want to achieve, this is quite easy with Rails 7, and a bit more complicated with Rails 6 and below, or when the inverse scope may contain NULL values. [1]

As an example, consider the following model.

class User < ApplicationRecord
  scope :admins, -> { where(role: ['admin', 'superuser']) }
  # ...
end

Now what if we want a scope of users that are not admins? While you could declare a second scope like `scope :non_admins, -> { where(role: ['...

MySQL: Careful when using database locks in transactions

We tend to use database transactions as a magic bullet to get rid of all our concurrency problems. When things get really bad, we might even throw in some locking mechanism, but then are usually done with it.

Unfortunately, transactions semantics in databases are actually very complicated, and chances are, your making some incorrect assumptions.

The MySQL innodb engine actually has [four different modes](ht...

Rails: Different flavors of concatting HTML safe strings in helpers

This card describes different flavors for concatting HTML safe strings in a helper method in Rails. You might want to use the tag helper instead of the content_tag helper (the tag helper knows all self closing tags).

Example

We want to generate HTML like this:

<h1>Navigation</h1>
<ul>
  <li>Left</li>
  <li>Right</li>
</ul>

Below you ca...

Don't build randomness into your factories

Tests are about 100% control over UI interaction and your test scenario. Randomness makes writing tests hard. You will also push tests that are green for you today, but red for a colleague tomorrow.

That said, please don't do something like this:

Factory(:document) do |document|
  document.category { ['foo', 'bar', 'baz'].sample }
end

Instead do this:

Factory(:document) do |document|
  document.category 'foo'
end

The case against Faker

I even recommend to not use libraries like [Faker]...

Ruby: Making your regular expressions more readable with /x and alternative delimiters

The following two hints are taken from Github's Ruby style guide:

If your regular expression mentions a lot of forward slashes, you can use the alternative delimiters %r(...), %r[...] or %r{...} instead of /.../.

 %r(/blog/2011/(.*))
 %r{/blog/2011/(.*)}
 %r[/blog/2011/(.*)]

If your regular expression is growing complex, you can use the /x modifier to ignore whitespace and comments:

regexp = %r{
  start         # some text
  \s            # white space char
  (group)    ...

Text column sizes in MySQL

Postgres works differently

See PostgreSQL: Difference between text and varchar columns for PostgreSQL-specific info

MySQL has 4 different column sizes. They are actually different data types under the hood:

type size limit schema.rb option
TINYTEXT 256 bytes size: :tiny
TEXT 65,535 bytes (default)
MEDIUMTEXT 16,777,215 bytes size: :medium
LONGTEXT 4,294,967,...

Understanding database cleaning strategies in tests

TLDR: In tests you need to clean out the database before each example. Use :transaction where possible. Use :deletion for Selenium features or when you have a lot of MyISAM tables.

Understanding database cleaning

You want to clean out your test database after each test, so the next test can start from a blank database. To do so you have three options:

  • Wrap each test in a transaction which is rolled back when you're done (through DatabaseCleaner.strategy = :transaction or `config.use_transactional_fi...

Using regular expressions in JavaScript

Regular expressions in Javascript are represented by a RegExp object. There also is a regex literal as in many other languages: /regex/. However, they are used slightly differently.

Regex literal

  • Usage: /foo+/
  • Shorthand for creating a regular expression object

RegExp() object

  • Usage: RegExp("foo+") or new RegExp("foo+")
  • No surrounding slashes required (they're the literal markers)
  • Since the argument is a string, backslashes need to be escaped as well: RegExp("\\d+")

Gotchas

  • Regex objects [never eq...

How to handle when an HTML <video> element cannot autoplay

HTML <video> elements can automatically start playing when the autoplay attribute is set on them. Except for when they can not, e.g. on pageload, or when the element enters the DOM without user interaction, or when the browser for some other reason decided to not start playing the video.

While there is no native "autoplay failed" event to listen to, you can wait for video data to be loaded and then check if the video actually started playing.

Example

<video autoplay>
  <source src="example.mp4" type="video/mp4" />
</video>
...

Using rack-mini-profiler (with Unpoly)

Debugging performance issues in your Rails app can be a tough challenge.

To get more detailed insights consider using the rack-mini-profiler gem.

Setup with Unpoly

Add the following gems:

group :development do
  gem 'memory_profiler'
  gem 'rack-mini-profiler'
  gem 'stackprof'
end

Unpoly will interfere with the rack-mini-profiler widget, but configuring the following works okayish:

// rack-mini-profiler + unpoly
if (process...

SAML Single Logout (SLO)

There are two ways a logout in SAML can happen: Service Provider (SP) initiated and Identity Provider (IDP) initiated logout. I'll explain how to implement both flows with devise_saml_authenticatable.

Note

SAML also supports a SOAP and an Artifact binding to do this. This guide only refers to POST and Redirect bindings. devise_saml_authenticatable does not support SOAP and Artifact bindings.

SP initiated logout (using the Redirect Binding)

When the user clicks on Logout within the app, the app can trigger...

Rails 7.1: Take care of the new production log default to standard out

Starting with Rails 7.1 the production logger is set to standard out. For applications running with opscomplete ensure to keep logging to a file as before (e.g. when running bin/rails app:update).

It should be enough to change these lines in the config/environments/production.rb back to the implementation in Rails <7.1:

-  # Log to STDOUT by default
-  config.logger = ActiveSupport::Logger.new(STDOUT)
-    .tap  { |lo...

Caution: rem in @media query definitions ignore your font-size

Note

Using rem only ever makes sense when the root font size is dynamic, i.e. you leave control to the user. Either by a) respecting their user agent defaults, or by b) offering multiple root font sizes in your application.

By defining @media queries in rem, they will accommodate to the root font size of your page. At a larger root font, breakpoints will be at larger widths, scaling with the font. However, there is a catch in case b) mentioned in the note above.

Relative length units in media queries are based on the initial value,...

Problems with git submodules in Gitlab CI

If you are using git submodules in Gitlab CI, you might run into a "The project you were looking for could not be found or you don't have permission to view it."

Gitlab added a feature that new projects are no longer allowed to be cloned inside CI runs of other repositories by default. To fix this

  • Go into the project used as a submodule
  • Go to "Settings" -> "CI/CD" (if you don't see this section, enable it in "Settings" -> "General" -> "Visibility, project features, permissions")
  • Go to "Token Access"
  • Either disable "Limit access to ...

Cucumber's table diffing does not play nice with Spreewald's `patiently do`

Turns out, Cucumber::MultilineArgument::DataTable#diff! caches some stuff. Code of the following form will not work as intended:

Then('some table should look like') do |expected_table|
  patiently do
    actual_table = calculate_actual_table
    expected_table.diff!(actual_table) # not actually patient, will keep failing if it failed the first time
  end
end

Instead, simply use

expected_table.dup.diff!(actual_table)

Debug SAML in development using a local keycloak server

Developing or debugging SAML functionality can be a hassle, especially when you need to go back and forth with someone external who is managing the identity provider (IDP).
But you can setup a local keycloak server to act as your IDP to play around with. This might seam intimidating, but is actually quite simple when using docker and turning off some verification steps.

1. Start a keycloak instance using docker

`mkdir -p keycloak_data && docker run --network=host -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN...

Ruby: How to connect to a host with expired SSL certificate

If you need to make an HTTPS connection to a host which uses an expired certificate, do not disable certificate verifications entirely. Doing that enables e.g. man in the middle attacks.
If you accept only a single expired and known certificate, you are much less in trouble.

Setup

All the solutions described below use a verify_callback for the request's OpenSSL::X509::Store where you can specify a lambda to adjust its verification response.
Your callback must return either true or false and OpenSSL's verification result is...

Your Cronjobs should not rely on a perfect schedule

Due to network or hardware failures, it can happen that one of your cronjobs will not run at the time you specify in the schedule. Your code should be built in a way that it can be re-run at a later time (when the failure is resolved).

For example, if you are synchronizing data with another service once every day, your cronjob should not only synchronize changes from the last 24 hours. If you do this and a network failure will delay the execution of your job by 5 hours, you will only synchronize changes from hour 6-29, but forget change...

Stretching an HTML page to full height

This card existed before, but was outdated due to browser implementation changes. The information below is validated for the current list of browsers we support.


By default your html and body elements are only as high as the actual page content inside their container. If you only have two lines of text in your page, your html and body elements will only be around 40 pixels high, regardless of the size of your browser window.

You might be surprised by this, since setting a background on either html and `body...