Jasmine: Adding custom matchers
Definition
A matcher is a function that returns an object with a compare
key. Usually it is registered with beforeEach
:
beforeEach(() => {
jasmine.addMatchers({
// Example matcher
toBeAnything() {
return {
compare(actualValue, ...matcherArguments) {
// Do some computations here ...
// Return whether the actualValue matches the expectation
return {pass: true}
}
}
}
})
})
Usage
expect(actualValue).toBeAnything(...matcherArg...
Use find_in_batches or find_each to deal with many records efficiently
Occasionally you need to do something directly on the server -- like having all records recalculate something that cannot be done in a migration because it takes a long time.
Let's say you do something like this:
Project.all.each(&:recalculate_statistics!)
Even though you may have been successful with this on your development machine or the staging server, keep in mind that production machines often hold a lot more records. Using all
may just work, even with lots of records, but when you iterate over such records and fetch a...
CSS: Combining different length units with calc()
calc()
lets you mix CSS units. Ever wanted to give an element "the container's width minus 20px on each side"? Here you go:
.foo {
width: calc(100% - (20px * 2));
}
When using Sass, you need to interpolate Sass expressions:
$margin: 20px * 2
.foo
width: calc(100% - #{$margin})
Supported by all modern browsers and IE9+.
How to fix: irb / rails console randomly crashing
If your irb or rails console keeps randomly crashing and you can't figure out why then you can try to disable multi-line autocomplete.
Sidekiq 7: Rate limiting with capsules
Sidekiq 7 adds a new feature called capsules.
Use cases:
- a
chrome
queue limited to1
for e.g. PDF processing to not overload the application server - an
api
queue, that limits a queue to2
to protect the API server from too many requests in parallel
Example:
Sidekiq.configure_server do |config|
# Edits the default capsule
config.queues = %w[critical default low]
config.concurrency = 5
# Define a new capsule which ...
ActiveRecord::Relation#merge overwrites existing conditions on the same column
In Ruby on Rails ActiveRecord::Relation#merge
overwrites existing conditions on the same column. This may cause the relation to select more records than expected:
authorized_users = User.where(id: [1, 2])
filtered_users = User.where(id: [2, 3])
authorized_users.merge(filtered_users).to_sql
# => SELECT * FROM users WHERE id IN (2, 3)
The merged relation select the users (2, 3)
, although we are only allowed to see (1, 2)
. The merged result should be (2)
.
This card explores various workarounds to combine two scopes so t...
Using the Oklch color space to generate an accessible color palette
The linked content describes:
- The different color space of Oklch and RGB/HSL (HDR colors)
- The advantage of Oklch when you change a base color and your derived colors will keep the same assertions on contrast level
Warning
This feature landed in browsers at the end of 2022. According to our support policy this will become generally usable starting Dec 2024.
The oklch() functional notation expresses a given color in the Oklch color space. It has the same L axis as oklab(), but uses polar coordinates C (Chroma) and H (Hue).
Sour...
Understanding database Indexes in PostgreSQL
I found the linked article very helpful to refresh my understanding of database indexes. As a small bonus, it includes a few helpful SQL oneliners like these two:
Warning
Do not run random code snippets unless you understand them in detail - especially in production.
How to create a multiline map in SASS/SCSS
If you want to to create maps within SASS/SCSS-files, it normally works like this:
$some-map: (key1: value1, key2: value2)
However, some maps can get big really fast, if they are being used to contain all of the project's icon names and their sizes for example.
Therefore splitting a map into multiple lines, like we do it in Ruby with big hashes, would become really handy.
Unfortunately SASS doesn't support multiline maps. There has been an open issue since 2011 and it hasn't been...
Rubymine: Configure CTRL + ALT + SHIFT + c to work with "Test Source Roots"
To navigate between test and test subject Rubymine requires you to set the test root sources as Test Sources Root.
In case you are using the keyboard shortcut "CTRL + ALT + SHIFT + c" to copy the reference path + you have set the "Test Sources Root" for your test folders, you might consider setting this keyboard to "Copy From Repository Root". This will return the path `spec/foo_spec....
Webpacker: Configuring browser compatibility
Webpacker uses Babel and Webpack to transpile modern JavaScript down to EcmaScript 5. Depending on what browser a project needs to support, the final Webpack output needs to be different. E.g. when we need to support IE11 we can rely on fewer JavaScript features. Hence our output will be more verbose than when we only need support modern browsers.
Rails 5.1+ projects often use Webpacker to preconfigure the Webpack pipeline for us. The default configuration works something like this:
- Webpack checks w...
Do not use transparent PNGs for iOS favicons
Safari on iOS accepts an apple-touch-icon
favicon that is used for stuff like desktop bookmarks. Always define a solid background color for them.
If you use PNGs with a transparent background, Safari will use just set a black background on your pretty icon. This is almost never what you want.
You can fix that by applying a white background via ImageMagick like this:
convert a...
Verifying doubles in RSpec 3
RSpec 3 has verifying doubles. This breed of mock objects check that any methods being stubbed are present on an instance of a given class. They also check methods aren't called with the wrong number of arguments.
This dual approach allows you to move very quickly and test components in isolation, while
giving you confidence that your doubles are not a complete fiction.
You should always prefer using a verifying double to using an old-school mock
...
How Haml 6 changes attribute rendering, and what to do about it
Haml 6 was a major rewrite with performance in mind. To achieve a performance improvement of 1.7x, some design trade-offs had to be made. The most notable change might be the simplified attribute rendering.
In Haml 5, attribute rendering knew two special cases: an attribute with value true
would be rendered without a value, an attribute with a falsy value would not be rendered at all. All other values would just be rendered as attribute values.
According to the Haml maintai...
Rails: Assigning associations via HTML forms
Let's say we have posts with an attribute title
that is mandatory.
Our example feature request is to tag these posts with a limited number of tags. The following chapters explain different approaches in Rails, how you can assign such an association via HTML forms. In most cases you want to use Option 4 with assignable values.
The basic setup for all options looks like this:
config/routes.rb
Rails.application.routes.draw do
root "posts#index"
resources :posts, except: [:show, :destroy]
end
**db/migrate/...
Rails cache connection settings
If you're using a Redis cache in Rails (e.g. :redis_cache_store
), it's possible to configure additional parameters for your Redis connection.
Example config for Rails 7.2
config.cache_store = :redis_cache_store, {
pool: { timeout: 0.5 },
read_timeout: 0.2, # default 1 second
write_timeout: 0.2, # default 1 second
# Attempt two reconnects with some wait time in between
reconnect_attempts: [1, 5], # default `1` attempt in Redis 5+
url: REDIS_URL,
error_handler: ->(method:, returning:, exception:) {
Sentry.captur...
Simple Form: Rendering errors without an appropriate attribute
Usually you add errors to :base in ActiveRecord, in case no appropriate attribute could be used to add the error.
Simple Form doesn't render errors on :base
by default, but here a few options how you can render these on demand. For all the options below we use the following example with a Simple Form Bootstrap configuration:
- @user = Backend::User.new
- @user.errors.add(:base, 'First error')
- @user.errors.add...
Using Capybara finder methods with arbitrary matching conditions
Capybara has a variety of finder methods like find_button
to help you look up DOM elements. There are also matchers like have_field
to make expectations during tests.
These methods also have a number of options to influence the lookup. E.g. the :disabled
option lets you control whether Capybara will match disabled fields.
If you have a matching condition that cannot be expressed by the existing Capybara opt...
Project management best practices: Technical debt summary
Maintaining larger projects makes it more difficult to balance refactoring and upgrade tasks according to its actual value. Consider to create and periodically maintain a summary, which helps you and your team in the decision which refactoring task should be taken next.
Template
Here is an template on how you might categorize your tasks:
| Technical debt | Estimated Efforts | Visible customer value| Customer value explained| Developer value|Developer value explained|
|-----------------------------|----------------|----------...
PostgreSQL: WITH Queries (Common Table Expressions)
PostgreSQL's Common Table Expressions (CTEs) can be used to extract sub-queries from bulky SQL statements into a temporary table to be referenced instead.
This is most useful to avoid ugly joins or sub-selects. CTEs can be used for SELECT
, INSERT
, UPDATE
or DELETE
.
Example (from the PostgreSQL docs):
WITH regional_sales AS (
SELECT region, SUM(amount) AS total_sales
FROM orders
GROUP BY region
), top_regions AS (
SELECT region
FROM regional_sales
WHERE total_sales > (SE...
Controlling issue grouping in Sentry
When you use Sentry to monitor exceptions, an important feature is Sentry's error grouping mechanism. It will aggregate similar error "events" into one issue, so you can track and monitor it more easily. Grouping is especially important when you try to silence certain errors.
It is worth understanding how Sentry's grouping mechanism works.
The default grouping mechanism
The exact algorithm has changed over time, and Sentry will keep using the algorithm t...
How not to turn your application into a spam relay
Spammers have started abusing other application to send their spam. This works like this:
- The application has some form that allows to send e-mails to arbitrary users. This can be something like a newsletter sign-up with a double-opt in, a registration confirmation e-mail (or even password reset e-mail), or something similar.
- The e-mail also includes some reflected text. For example, a user may be able to give their name, and the name is used within the e-mail. The spammer will then abuse that text to include his advertisment.
Potentia...
JavaScript: Detecting the end of native smooth scrolling
When you use native smooth scrolling there is no built-in method to detect the end of the scrolling animation. Methods like scrollTo()
don't return a promise. We may eventually get a scrollend
event, but that is still some time away.
Until then I'm using the ...
Rails: Rescuing exceptions for specific exception types
By default most exceptions in Rails will render a 500 error page and will create a new issue in your error monitoring. There are some built-in rules in Rails that
- render a different error than 500
- will rescue the exception and not create an issue in your error monitoring
A good example is ActiveRecord::NotFound
: You don't want an exception in your error monitoring when users navigate to e.g. a blog post t...