Do not forget mailer previews
When changing code in mailers, updating the corresponding mailer preview can be forgotten very easily.
Mailer previews can be tested like other code as well and I sometimes add the following tests to test suites:
# Make sure to require the previews
Dir[Rails.root.join('spec/mailers/previews/*.rb')].each { |file| require(file) }
ActionMailer::Preview.all.index_with(&:emails).each do |preview, mails|
mails.each do |mail|
describe preview do
specify "##{mail} works" do
expect { preview.call(mail...
Show a JS fiddle in fullscreen
If you have a JS fiddle, you can open it in fullscreen by appending /show
to the URL.
Example: https://jsfiddle.net/b275g910/3
=> https://jsfiddle.net/b275g910/3/show
How to debug file system access in a Rails application
It might sometimes be useful to check whether your Rails application accesses the file system unnecessarily, for example if your file system access is slow because it goes over the network.
The culprit might be a library like carrierwave that checks file existence or modification times, whereas your application could determine all this from your database.
Introducing strace
One option it to use strace for this, which logs all system calls performed by a process.
To do this, start your rails server using something like
DISA...
Capybara: Most okayest helper to download and inspect files
Testing file download links in an end-to-end test can be painful, especially with Selenium.
The attached download_helpers.rb
provides a download_link
method for your Capybara tests. It returns a hash describing the download's response:
details = download_link('Download report')
details[:disposition] # => 'attachment' or 'inline'
details[:filename] # => 'report.txt'
details[:text] # => file content as string
details[:content_type] # => 'text/plain'
Features
Compared to [other approaches](...
RSpec: How to turn off partial double verification temporarily
While verifying doubles in RSpec is a good default, it is limited in the amount of methods it actually is able to verify.
The background is that RSpec can't verify dynamically defined methods, which is a known issue for the usage of helper_method and also the reason why [RSpec >= 3.6](http://rspec.info/blog/2017/05/rspec-3-6-has-been-rel...
Project maintenance: four levels of code quality
Code quality can be measured in four levels:
- (Working code)
- Reliable code (minimum)
- Readable code (ok for short-lived code)
- Changeable code (standard level)
The code quality of a project directly impacts its maintainability.
Generally you should aim for level 3. If the code will stay for less than a few months, it may stay at level 2. Never go below level 1.
0. Working code
You have implemented that feature and it works. Congrats! You have reached level zero, which means three levels of code quality lie ahead.
First, m...
Use DatabaseCleaner with multiple test databases
There is a way to use multiple databases in Rails.
You may have asked yourself how you're able to keep your test databases clean, if you're running multiple databases with full read and write access at the same time. This is especially useful when migrating old/existing databases into a new(er) one.
Your database.yml
may look like this:
default: &default
adapter: postgresql
encoding: unicode
username: <%= ENV['DATABASE_USER'] %>
host: <%= ENV['DATABASE...
How to bind an event listener only once with Unpoly
You can use Unpoly's up.on with a named listener function and immediately unbind this event listener with { once: true }
:
up.on('up:fragment:inserted', { once: true }, function () { ... })
In Unpoly 1 you can immediately unregister the listener with up.off:
up.on('up:fragment:inserted', function fragmentInsertedCallback() {
up.off('up:fragment:inserted', fragmentInsertedCallback)
// ... code for the callback function, which should run only once
})
Exam...
Chrome DevTools: List Registered Event Listeners
In Chrome DevTools you can use getEventListeners(object)
to get a list of registered event listeners on the specified object.
You can also do this without the console, by selecting an element in the DOM inspector. In the element details, select the tab Event Listeners".
Example
const registry = getEventListeners(document)
registry['up-click']
// 0: { useCapture: false, passive: false, once: false, type: 'up:click', listener: ƒ }
// 1: { useCapture: false, passive: false, once: false, type: 'up:click', listener: ƒ }
//...
How to get information about a gem (via CLI or at runtime from Ruby)
When you need information about a gem (like version(s) or install path(s)), you can use the gem
binary from the command line, or the Gem
API inside a ruby process at runtime.
gem
binary (in a terminal)
You can get some information about a gem by running gem info <gem name>
in your terminal.
Example:
$ gem info irb
*** LOCAL GEMS ***
irb (1.4.1, 1.3.5)
Author: Keiju ISHITSUKA
Homepage: https://github.com/ruby/irb
Licenses: Ruby, BSD-2-Clause
Installed at (1.4.1): /home/arne/.rbenv/versions/3.0.3/lib/ruby/g...
JavaScript: Working with Query Parameters
tl;dr: Use the URLSearchParams
API to make your live easier if you want to get or manipulate query parameters (URL parameters).
URLSearchParams
API
The URLSearchParams
API is supported in all major browsers except IE 11.
It offers you a bunch of useful methods:
-
URLSearchParams.append()
- appends a query parameter -
URLSearchParams.delete()
- deletes the specified query parameter -
URLSearchParams.get()
- returns the value of the specified query parameter - `URLSearchP...
Jasmine: Fixing common errors during initialization
Due to the way we setup Jasmine tests in our projects, you may run into various errors when Jasmine boots.
Setting jasmineRequire
on undefined
Jasmine 4 may fail with an error like this:
Uncaught TypeError: Cannot set properties of undefined (setting 'jasmineRequire')
This is due to issues in Jasmine's [environment detection](https://github.com/jasmine/jasmine/blob/502cb24bb89212917a3c943b593fd918ffc481cb/lib/jasmine-core/...
Generating an Entity Relationship Diagram for your Rails application
This card explains how to generate an entity relationship diagram for your Rails application.
We also show how to limit your ERD to a subset of models, e.g. models inside a namespace.
Generating a full ERD
Option A: RubyMine
- Right-click anywhere in your project tree
- In the context menu, find the "Diagrams" menu item at/near the bottom
- Inside, choose "Show diagram" → "Rails Model Dependency Diagram"
- A new tab will open with the diagram inside. You can modify it there, and export it as an image.
Option B: Use rails-e...
Version 5 of the Ruby Redis gem removes Redis.current
Redis.current
will be removed without replacement in redis-rb
5.0.
Version 4.6.0 adds deprecation warnings for Redis.current
and Redis.current=
:
`Redis.current=` is deprecated and will be removed in 5.0.
If your application still uses Redis.current
, you can only fix it by no longer using it. Here is how.
Redis.new when you need it
You can easily instantiate a Redis
client when you need it.
There is probably already a constant like REDIS_URL
that you use to configure Sidekiq or similar. So just use that one.
``...
How to access Chrome Devtools when running JavaScript tests via CLI
While we are used to run our JavaScript tests on a test page within our Browser, it's also possible to run them on the command line with NodeJS. I think that's actually the most common way to run JS tests.
Given a Vue project that uses Jest (via vue-cli-service
) with the following package.json
:
{
"scripts": {
"test": "vue-cli-service test:unit --testMatch='**/tests/**/*.test.js' --watch"
},
}
This allows us to run J...
Jest: How to clear the cache
I recently was in a weird situation where my (Jest/CLI) tests were referencing a function that was no longer part of my code - I had just refactored it.
Apparently Jest has some kind of cache that caused the issue, running npx jest --clearCache
solved it for me.
RSpec: You can super into parent "let" definitions
RSpec's let
allows you to super
into "outside" definitions, in parent contexts.
Example:
describe '#save' do
subject { described_class.new(attributes) }
let(:attributes) { title: 'Example', user: create(:user) }
it 'saves' do
expect(subject.save).to eq(true)
end
context 'when trying to set a disallowed title' do
let(:attributes) { super().merge(title: 'Hello') } # <==
it 'will not save' do
expect(subject.save).to eq(false)
end
end
end
I suggest you don't make a habit of using this regula...
Integrating ESLint
Introduction
To ensure a consistent code style for JavaScript code, we use ESLint. The workflow is similar to integrating rubocop for Ruby code.
1. Adding the gem to an existing code base
You can add the following lines to your package.json
under devDependencies
:
"devDependencies": {
"@eslint/js": "x",
"@stylistic/eslint-plugin": "x",
"eslint": "x",
"eslint-plugin-import": "x",
"globals": "x",
}
...
How to add esbuild to the rails asset pipeline
This are the steps I needed to do to add esbuild to an application that used the vanilla rails asset pipeline with sprockets before.
Preparations
- update Sprockets to version 4
- add a
.nvmrc
with your preferred node version (and install it) - add gems
jsbundling-rails
andforeman
to yourGemfile
:gem 'jsbundling-rails' group :development, :test do gem 'foreman' # ... end
bundle install
- run
bin/rails javascript:install:esbuild
in a console to prepare esbuild. - run `yarn instal...
PostgreSQL: "WHERE NOT <column> = '<value>'" statements do not include NULL values
Sometimes we write plain SQL queries in migrations so we don't have to mock ActiveRecord classes. These two migrations do the same:
class Migration1 < ActiveRecord::Migration[5.2]
class User < ActiveRecord::Base; end
def up
add_column :users, :trashed, :boolean
User.update_all(trashed: false)
end
end
class Migration2 < ActiveRecord::Migration[5.2]
def up
add_column :users, :trashed, :boolean
update("UPDATE users SET trashed = #{quoted_false}")
end
end
The plain SQL migration is less code, but h...
esbuild: Make your Rails application show build errors
Building application assets with esbuild is the new way to do it, and it's great, especially in combination with Sprockets (or Propshaft on Rails 7).
You might be missing some convenience features, though.
Here we cover one specific issue:
Once you have started your development Rails server and esbuild with the --watch
option (if you used jsbundling-rails to set up, you probably use bin/dev
), esbuild will recompile your assets upon change, but build errors will only be printed to the terminal. Your application won't complain about them ...
Carrierwave: How to attach files in tests
Attaching files to a field that is handled by Carrierwave uploaders (or maybe any other attachment solution for Rails) in tests allows different approaches. Here is a short summary of the most common methods.
You might also be interested in this card if you see the following error in your test environment:
CarrierWave::FormNotMultipart:
You tried to assign a String or a Pathname to an uploader, for security reasons, this is not allowed.
If this is a file upload, please check that your upload form is multipart encoded.
Factor...
Deployment: Merge consecutive commits without cherry-picking
You want to deploy new features but the latest commits are not ready for production? Then use git merge master~n
to skip the n-last commits.
Tip
A big advantage of merging vs. cherry-picking is that cherry-picking will create copies of all picked commits. When you eventually do merge the branch after cherry-picking, you will have duplicate commit messages in your history.
Example
It's time for a production deployment!
git log --pretty=format:"%h - %s" --reverse origin/production..origin/master
0e6ab39f - Feature A
6396...
Capybara: Working with invisible elements
When Capybara locates elements in the DOM, by default it allows only accessing visible elements -- when you are using a driver that supports it (e.g. Selenium, not the default Rack::Test
driver).
Consider the following HTML:
<div class="test1">One<div>
<div class="test2">Two</div>
With some CSS:
.test1 { display: block }
.test2 { display: none }
We will be using Capybara's find
below, but this applies to any Capybara finder methods.
Default: visible: :visible
As described above, by default Capybara finds ...