Sending newsletters via rapidmail with SMTP and one-click unsubscribe
If you need to implement newsletter sending, rapidmail is a solid option.
Support is very fast, friendly and helpful, and the initial setup is simple. Since rapidmail works via SMTP, you can simply ask the Ops team to configure SMTP credentials for your application.
You also do not need to use rapidmail’s built-in newsletter feature. Instead, you can send emails as transactional mails, which allows you to keep the entire newsletter logic inside your application.
One thing to keep an ey...
Locally testing a website on its real domain
In rare circumstances, you want to use a websites full domain (say https://mywebsite.com) while testing in dev mode. This can be useful if you need to test third party integrations like embeds, cookie banners, Google Tag Manger or other integrations that allowlist your actual domain.
To achieve this, we will have to
- make our system resolve
mywebsite.comtolocalhost, - run a reverse proxy on
localhost:443with a (locally signed and accepted) SSL certificate formywebsite.com, - forward the traffic to our actual dev server.
On l...
TypeScript: Get in Shape with Structural Typing
TypeScript basically uses structural typing, which is conceptually quite similar to duck typing, but with static compile-time type checking. We'll explore what this means in practice.
TypeScript is a superset of JavaScript, meaning TypeScript compiles down to native JavaScript syntax and checks type consistency only at compile time.
Idea of Structural Typing
TypeScript only wants to know whether the shapes of two objects are identical:
interface Point2D {
x: number;
y: number;
}
function printPoint(p: Point2D) {
c...
Rubocop fails with undefined StringIO
If your rubocop run fails with a stack like
rubocop-1.61.0/lib/rubocop/server/socket_reader.rb:36:in `ensure in read!': undefined method `string' for nil:NilClass (NoMethodError)
Cache.stderr_path.write(stderr.string)
^^^^^^^
...
rubocop-1.61.0/lib/rubocop/server/socket_reader.rb:27:in `read!': uninitialized constant RuboCop::Server::SocketReader::StringIO (NameError)
stderr = StringIO.new
^^^^^^^^
this card might help you.
In old versions of rubocop stringio w...
How to accept more than 4k query parameters in Rails
I have a form with a dynamic number of fields. Submitting it worked fine until I tried out a very large version of it. The development log was not very helpful:
Invalid or incomplete POST params
As it turned out, the following exception did not reach the log output
Rack::QueryParser::QueryLimitError
Error Message: total number of query parameters (6313) exceeds limit (4096)
If you ever happen to be in the same position, this is how to increase the limit of allowed query parameters:
# config/initializers/rack_query_parser.rb
...
How to iterate over all ActiveRecord models
Sometimes you have a maintenance script where you want to iterate over all ActiveRecord models. Rails provides this out of the box:
# script/maintenance_task.rb
# Load all models eagerly, otherwise you might only get a subset
Rails.application.eager_load!
ApplicationRecord.descendants.select(&:table_exists?).each do |model|
# ...
end
Caution
If you iterate over individual records, please provide a progress indicator: See [https://makandracards.com/makandra/625369-upload-run-scripts-production](https://makandracards.com/...
Migrating from rbenv / nvm to mise
Install mise
Follow the installation guidelines at https://mise.jdx.dev/getting-started.html.
Remove rbenv configuration
Search for rbenv config in .bashrc and .profile and remove it:
eval "$(rbenv init - bash)"
Search for rbenv config in .profile and remove it:
source /home/$user/.rbenvrc
Remove nvm configuration
Search for nvm config in .bashrc and remove it:
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_...
How to inspect Rails view cache keys (when using Redis)
When your Rails application is using Redis as its cache store, this is how you can list existing keys:
- Check:
Rails.cacheshould return anActiveSupport::Cache::RedisCacheStore. -
Rails.cache.redis.with(&:keys)lists existing keys. Cached views start with "views/".
Caution! The list of keys may be huge in production.
Making minimal updates to DOM trees using morphdom / idiomorph
When you replace parts of the DOM with new HTML, using .innerHTML = newHtml is usually the simplest and fastest option. It comes at the price of your DOM elements losing state, like input values, scroll position, progress in a video player, or even more complex state for custom elements.
One option to avoid this are libraries like morphdom (as used by Phoenix Liveviews) or idiomorph (as used by Rails' Turbo).
It lets you write
morphdo...
Custom Angular Test Bootstrap
Compatibility: Angular 20+ with Jasmine 5.x and Karma 6.x
As a default Angular CLI auto-generates test bootstrap via angular:test-bed-init via injecting it as a dynamic virtual module.
Custom Bootstrap
Override the main test option in angular.json → projects.{app}.architect.test.options.main: <test entry file> and [then initialize th...
Faux-disabled fields in HTML forms
You want to prevent input to a form field, but all the solutions have side effects:
- The
[readonly]attribute is only available for text fields, but not for checkboxes, selects, buttons, etc. - The
[disabled]attribute is available for all kinds of fields, but will no longer include the field value in the submitted form data. -
pointer-events: nonestill allows keyboard input, and does not indicate disabledness visually, or to screen readers.
Ye...
File System Access API: Recursive Directory Traversal
The File System Access API is a new capability of modern browsers that allows us to iterate over selected folders and files on a user's machine. Browser support is not great yet, but if the feature is only relevant for e.g. a single admin user it could still be worth using it prior to wider adaption instead of building yet another ZIP upload form.
Below is a simple compiler that i used to evaluate this feature.
!...
Sentry Local Logging in Ruby
Enable local logging for Sentry when:
- Debugging Sentry event capture locally
- Testing error handling without polluting production metrics
- Developing background jobs and want to see what Sentry captures
How to enable
To capture and log Sentry events locally during development without sending to the server, add this to config/initializers/sentry.rb inside the Sentry.init block:
if Rails.env.development?
# Use dummy transport to prevent actual transmission to Sentry
config.transport.transport_class = Sentry::DummyTran...
Unpoly: Passing Data to Compilers
Quick reference for passing data from Rails to JavaScript via Unpoly compilers.
Haml Attribute Syntax
# Ising hash rockets and string symbols (method calls)
= form.text_field :name, 'date-picker': true
# Curly braces or brackets (elements)
%div.container{ id: 'main', 'data-value': '123' }
Simple Values: data-* Attributes
Use for: Scalar values (IDs, strings, booleans)
%span.user{ 'data-age': '18', 'data-first-name': 'Bob' }
up.compiler('.user', (element, data) => {
console.log(...
Unpoly: Compiler Selector Patterns
Quick guide for frequently used compiler selector patterns of Unpoly.
1. BEM Component Pattern
When: Reusable UI components with multiple child elements
Examples: toggleable.js, collapsible.js, searchable_select.js
up.compiler('.toggleable', (toggleable) => {
const checkbox = toggleable.querySelector('.toggleable--checkbox')
const content = toggleable.querySelector('.toggleable--content')
// ...
})
%td.toggleable.-inverted.-ignore-when-not-empty
.toggleable--content
= form.text_fie...
Format your JavaScript with prettier
prettier calls itself an opinionated code formatter. I recommend using it for your JavaScript and TypeScript code.
prettier only concerns itself with the formatting of your JavaScript (and also some other file types like json or yaml). When you use it, it has an opinion on every single whitespace and linebreak, as well as a few other things. You renamed a variable and now your line is a bit longer than looks good? prettier will reformat your code.
This might not work for you if you have strong opinions yourself....
Stabilize integrations tests with flakiness introduced by Turbo / Stimulus / Hotwire
If you run a Rails app that is using Turbo, you might observe that your integration tests are unstable depending on the load of your machine. We have a card "Fixing flaky E2E tests" that explains various reasons for that in detail.
Turbo currently ships with three modules:
- Turbo Drive accelerates links and form submissions by negating the need for full page reloads.
- Turbo Frames decompose pages into independent contexts, which scope navigation and can be lazily loaded.
- T...
Processing GitLab Merge Requests within RubyMine
GitLab has a RubyMine plugin that enables you to review and process merge requests within RubyMine!
Setup
- Open RubyMine settings (Ctrl + Alt + S) > Plugins > Search for "GitLab" > Install
- (You might need to re-open settings afterwards.)
- In the RubyMine settings > Version Control > GitLab > Connect your GitLab account with "+"
Working with merge requests
- From the Actions menu (Ctrl + Shift + A), choose "View merge...
Improve accessibility with [aria-required] in SimpleForm
SimpleForm comes with an option browser_validations which could be used to give fields that have a presence validation the HTML required attribute. We usually turn it off due to difficulties controlling its behavior and appearance. Instead we only mark required fields with an asterisk (*) next to its label. Blind users probably only discover the validation issue after submitting the form due to the now displayed error messages.
A compromise with better acce...
RSpec: Marking sections in long examples
RSpec examples can get quite long, especially in feature specs. This makes them hard to read & understand. Also, when executing them, it may take seconds to see any progress.
To improve this, I have successfully been using a little "step" helper in my tests. It marks semantic sections, structuring an example while improving documentation. When the test runs, each step prints its message (or a dot, depending on your formatter).
# spec/support/step_helper.rb
module StepHelper
# Use this helper to label groups of related actions in l...
Testing for Performance: How to Ensure Your Web Vitals Stay Green
Frontend performance and user experience are orthogonal to feature development. If care is not taken, adding features usually degrades frontend performance over time.
Many years, frontend user experience has been hard to quantify. However, Google has developed some metrics to capture user experience on the web: the Web Vitals. The Core Web Vitals are about "perceived loading speed" (Largest Contentful Paint), reactivity (Interaction to Next Paint) and visual stability (Content Layout Shift).
I have recent...
Adding comments to ambiguous database columns
The DB schema is the most important source of truth for your application and should be very self-explanatory. If determining the true meaning of a DB column requires historical research in your issue tracker or reverse engineering of your source code you might consider adding a comment.
Both PostgreSQL and MySQL support comments in the DB schema:
- For new columns: https://guides.rubyonrails.org/active_record_migrations.html#comments
- Changing the comment for existin...
How to: Self-hosted fonts via NPM packages
We usually ship applications that self-host webfonts to comply with GDPR.
Many popular web fonts are available as NPM packages provided by Fontsource.
We recommend using those instead of downloading and bundling font files yourself. (See below for a list of benefits.)
Usage
- Go to fontsource.org and search for the font you want to add (or a font that suits your application).
- Click the font card to vie...
Better performance insights with gem `rails_performance`
Even if you don't make any beginner mistakes like N+1 queries or missing DB indices, some requests can have bad performance. Without good performance metrics, you probably won't notice this until it's too late.
We investigated multiple gems and found that rails_performance (https://github.com/igorkasyanchuk/rails_performance) provides a lot of valuable information with very little setup cost. It only needs Redis which we use in the majority of our applications anyw...