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 ...
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...
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|
|-----------------------------|----------------|----------...
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/...
SASS: Reusing styles from other files
SASS has an @extend
keyword to inherit styles.
.alert
color: red
&.-framed
border: 1px solid red
padding: 5px
&.-homepage
@extend .-framed
border-width: 5px
When compiling, SASS will simply join the selectors. Note how .-homepage
is written where .-framed
was defined:
...
.alert.-framed, .alert.-homepage {
border: 1px solid red;
padding: 5px;
}
.alert.-homepage {
border-width: 5px;
}
Warning
Unfortunately, this does...
CSS: CSS Container Queries
Container queries enable you to apply styles to an element based on the size of the element's container. If, for example, a container has less space available in the surrounding context, you can hide certain elements or use smaller fonts. Container queries are an alternative to media queries, which apply styles to elements based on viewport size or other device characteristics.
This feature is now stable across browsers.
Warning
This feature landed in browsers in the beginning of 2023. According to our support policy this will bec...
Issue Checklist Template
This is a checklist I use to work on issues. For this purpose I extracted several cards related to the makandra process and ported them into a check list and refined that over time a little bit.
This task list is divided by the Gate keeping process in the following steps:
1. Starting a new feature
2. Working on the issue
3. Finishing a feature
4. After Review
Here are some ti...
Rails Partials
Rails partials have a lot of "hidden" features and this card describes some non-obvious usages of Rails Partials.
Rendering a basic partial
The most basic way to render a partial:
render partial: 'partial'
This will render a _partial.html.erb
file. Notice how all partials need to be prefixed with _.
It's possible to define local variables that are only defined in the partial template.
# _weather.html.erb
<h1>The weather is <%= condition %></h1>
# index.html.erb
render partial: 'weather', locals: { condition: ...
How to turn images into inline attachments in emails
Not all email clients support external images in all situations, e.g. an image within a link. In some cases, a viable workaround is to turn your images into inline attachments.
Note
Rails provides a simple mechanism to achieve this:
This documentation makes it look like you have to care about these attachments in two places. You have to create the attachment in t...
Fixing wall of net/protocol warnings
After upgrading to Rails 6.1.7.2 one of our apps printed a wall of warnings while booting:
/var/www/app/shared/bundle/ruby/2.6.0/gems/net-protocol-0.2.1/lib/net/protocol.rb:68: warning: already initialized constant Net::ProtocRetryError
/home/deploy-app/.rbenv/versions/2.6.10/lib/ruby/2.6.0/net/protocol.rb:66: warning: previous definition of ProtocRetryError was here
/var/www/app/shared/bundle/ruby/2.6.0/gems/net-protocol-0.2.1/lib/net/protocol.rb:214: warning: already initialized constant Net::BufferedIO::BUFSIZE
/home/deploy-app/.rben...
How to make your git aliases work with both master and main
The linked article found a simple way to rewrite legacy git aliases to make them work with differently named default branches
- Step 1: Decide which is the most common default branch name of your projects, e.g.
master
. Define it as the globalinit.defaultBranch
git configuration :
git config --global init.defaultBranch master
- Step 2: Overwrite the value in each project directory that uses different defaults
# cd /path/to/project, then run:
git config ...
Spreewald: patiently blocks must not change variables from the surrounding scope
I recently enjoyed debugging a Cucumber step that tried to be retryable using a patiently
block:
Then /^"([^"]*)" should( not)? be selected for "([^"]*)"$/ do |value, negate, field|
patiently do
field = find(:label, text: field)['for'].delete_suffix('-ts-control')
...
end
end
Unfortunately this block is not retryable:
- The first attempt changes the value of
field
. - All subsequent attempts will using the changed value of
field
, instead of the o...
Timecop: reset after each test
Timecop is a great gem to set the current time in tests. However, it is easy to introduce flakyness to your test suite when you forget to reset the time after the test.
This might be the case if:
- a test freezes time and a later test does not work for frozen time
- a later test needs the real current date to work correctly
Often you only notice these kinds of errors in rare cases when tests are executed in a particular order.
A way to avoid this is by using block notation (`Timecop.travel(...) ...
Jasmine: Mocking ESM imports
In a Jasmine spec you want to spy on a function that is imported by the code under test. This card explores various methods to achieve this.
Example
We are going to use the same example to demonstrate the different approaches of mocking an imported function.
We have a module 'lib'
that exports a function hello()
:
// lib.js
function hello() {
console.log("hi world")
}
export hello
We have a second module 'client'
that exports a function helloTwice()
. All this does is call hello()
...
Bookmarklet: cards Markup Link Bookmarklet
The cards editor has a feature "Cite other card" to create links to other cards in the same deck as mardown links.
If you want to reference a card from a different deck, this bookmarklet might be useful:
javascript:(function () {
const doAlert = () => { alert("Maybe not a makandra card?") };
let cardsPathPattern = /(\/[\w-]+\/\d+)-.+/;
if (window.location.pathname.match(cardsPathPattern)) {
let currentPath = window.location.pathname.match(cardsPathPattern)[1];
let title = document.querySelector('h1.note--title')?.textCon...
A modern approach to SVG icons
You have some SVG files you want to use as icons on your website. How would you embed them?
Common options are:
- Use them with an image:
<img src='path-to-icon.svg'>
- Embed them inline with
<svg>$ICON</svg>
- Embed them using CSS and
background-image: url(path-to-icon.svg)
or evenbackground-image: url(data:$ICON)
. - Build your own icon font.
All of these have drawbacks:
- Image and
background-image
do not allow to recolor the image using CSS. - Inline-
<svg>
are unnecessary work for the server and are...
Debug flaky tests with an Unpoly observeDelay
The problem
Unpoly's [up-observe]
, [up-autosubmit]
and [up-validate]
as well as their programmatic variants up.observe()
and up.autosubmit()
are a nightmare for integration tests.
Tests are usually much faster than the configured up.form.config.observeDelay
. Therefore, it may happen that you already entered something into the next field before unpoly updates that field with a server response, discarding your changes.
The steps I wait for active ajax requests to complete
(if configured) and capybara-lockstep can catch some ...
Git: Switch
tl;dr
git checkout
is the swiss army of git commands. If you prefer a semantically more meaningful command for branch related tasks, usegit switch
instead.You can use
git switch
since git 2.23.
Switch Branch
git branch
# * master
# feature-branch
git switch feature-branch
git branch
# master
# * feature-branch
git switch -
git branch
# * master
# feature-branch
Info
For this use case you can of course also use
git checkout
.
Switch on a Remote Branch
git branch -a
# * m...
Use capybara and not rspec matchers
One rule of thumb I try to follow in capybara tests is using capybara matchers and not plain rspec matchers.
One example:
visit(some_page)
text_field = find('.textfield')
expect(text_field['value']).to match /pattern/
This can work, but is too brittle and flaky. match
will not retry or synchronize the value of text_field
.
The equivalent code with a capybara matcher:
visit(some_page)
expect(page).to have_field('.textfield', with: /pattern/)
have_field
will retry for and synchronize the text_field.
Git: Restore
tl;dr
git checkout
is the swiss army of git commands. If you prefer a semantically more meaningful command for restoring tasks, usegit restore
instead.With this command you can ...
- ... do unstaging -
git restore --staged
- ... discard staged changes -
git restore --staged --worktree
- ... discard unstaged changes -
git restore
- ... restore deleted files -
git restore
- ... restore historic versions -
git restore --source
- ... recreate merge conflicts -
git restore --merge
- ... specifiy...
JSON APIs: Default design for common features
When you build a JSON API you need to come up with a style to represent attributes, pagination, errors or including associated objects. Instead of reinventing the wheel, you may reuse successful API designs.
JSON API
JSON:API specifies common features found in most JSON APIs, like pagination, ordering and nested resources. The spec looks very similar to how one would build an API with Rails and uses similar patterns. Even if you don't plan on supporting the whole spec, it can still make sense to know how th...
Updated: Git: removing feature branch on merge
- Resolve @{-1} to actual branch name. (Happens when merging "-".)
Creating a Rails application in a single file
Greg Molnar has written a neat article about creating a single-file Rails app.
This is not meant for production use but can be useful to try things out, e.g. when hunting down a bug or embedding a Rails app into the tests of a gem.
What you do is basically:
- Put everything (gems, application config, database migrations, models, controllers) into a single
.ru
file, likeapp.ru
. - Run it via
rackup app.ru
. (Hint: if your file is calledconfig.ru
, you can just run `rac...