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 install
...
Understanding Ruby's def keyword
This StackOverflow question about nested function definitions in Ruby imparts a good understanding of Ruby's def
.
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...
Regular Expressions: Excessive backtracking can get yourself in trouble
Two weeks ago, Cloudflare was struck by a global outage that lasted ~30 minutes. The incident was rooted on a CPU exhaustion caused by a single regular expression containing some catastrophic backtracking:
.*(?:.*=.*)
This is a small reminder do keep using the lazy operator ?
whenever possible and furthermore be aware that regular expressions should not only be unit-tested but also evaluated in terms of performance. See <https://makandracards.com/makandra/494822-regul...
How to combine "change", "up", and "down" in a Rails migration
Rails migrations allow you to use a change
method whose calls are automatically inverted for the down path. However, if you need to some path-specific logic (like SQL UPDATE
statements) you can not define up
and down
methods at the same time.
If you were to define define all 3 of them, Rails would only run change
and ignore up
and down
. However, Rails 4+ features a helper method called reversible
:
class MyMigration < ActiveRecord::Migration
def cha...
Howto: Write a proper git commit message
Seven Rules
- Separate subject from body with a blank line
- Limit the subject line to 50 characters (max. 72), include reference (unique story ID) to requirements tracker (Linear in our case)
- Capitalize the subject line
- Do not end the subject line with a period
- Use the imperative mood in the subject line
- Wrap the body at 72 characters
- Use the body to explain what and why vs. how
5. Use the imperative mood in the subject line (partially extracted)
If applied, this commit will your subject line here
Good:
*...
Terminator setup for Procfile-based applications for more comfortable debugging
We use foreman
to start all necessary processes for an application, which are declared in a Procfile
. This is very convenient, but the outputs of all processes get merged together. Especially while debugging you might not want other processes to flood your screen with their log messages.
The following setup allows you to start Terminator in a split view with the Rails server running in the left pane and all remaining processes running via foreman in the right pane. It was heavily inspired by [this card](https://makandracards.com/makandr...
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 ...
RubyMine users: you should be using bookmarks
RubyMine allows bookmarking lines of code. This is super-helpful when working on a complex problem.
I've been using this feature for a few years now, and so should you! :)
Here are the default Linux/Windows keystrokes. See the documentation for other keybindings.
Add an anonymous bookmark
F11
A gray checkmark will be shown in the gutter on the left.
If you press F11
again on a bookmarked line, the bookmark will be removed.
Add a named bookmark ("mnemonic")
Ctrl
...
Documenting your Rails project's Node.js version in .nvmrc
Not all versions of Node.js are compatible with each other. Also npm packages may require a minimum or maximum version of Node.js. We use nvm on our development PCs so we can operate multiple versions of Node.js in parallel.
To make sure that all developers use a compatible version of Node.js, your Rails project should declare the required Node.js in a file called .nvmrc
.
When a .nvmrc
exists, developers can cd
in your project directory and activate...
skorks/nesty
Nested exceptions for Ruby:
When you rescue an error and then re-raise your own, you don't have to lose track of what actually occured, you can keep/nest the old error in your own and the stacktrace will reflect the cause of the original error.
This is awesome when you classes convert exception classes. I now always subclass Nesty::NestedStandardError
instead of StandardError
for my own error classes.
About Exception#cause
Ruby 2.1 has a built-in mechanism with Exception#cause
, which serves a similiar purpos...
Capybara: Finding invisible elements and how to test that an element is not visible
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
or visible: true
As described above, by defa...
Rails: When to use :inverse_of in has_many, has_one or belongs_to associations
When you have two models in a has_many
, has_one
or belongs_to
association, the :inverse_of
option in Rails tells ActiveRecord that they're two sides of the same association.
Example with a has_many
/ belongs_to
association:
class Forum < ActiveRecord::Base
has_many :posts, inverse_of: :forum
end
class Post < ActiveRecord::Base
belongs_to :forum, inverse_of: :posts
end
Knowing the other side of the same association Rails can optimize object loading so forum
and forum.posts[0].forum
will reference the same o...
Traverse an ActiveRecord relation along an association
The Edge Rider gem gives your relations a method #traverse_association
which
returns a new relation by "pivoting" around a named association.
Say we have a Post
model and each Post
belongs to an author:
class Post < ActiveRecord::Base
belongs_to :author
end
To turn a relation of posts into a relation of its authors:
posts = Post.where(:archived => false)
authors = posts.traverse_association(:author)
You can traverse multiple associations in a single call.
E....
8 steps for fixing other people's code
Guide how to make fixes in other people's GitHub repositories. It's basically "Open Source Development 101".
Way back in mid-2007, when Rails 1.2 was the new hotness and GitHub was still a year away from crawling out of the primordial internet soup, prolific open source contributor Dr Nic wrote an article titled “8 steps for fixing other people’s code”. (...)
Here in the fantastical future world of 2012, while we still don’t have hoverboards or household nuclear fusion, we do have some great tools that make fixing other people’s code...
JavaScript Sentry: How to check if errors will be reported
One really simple way to check whether JavaScript Sentry integration was successful (raven-js or @sentry/browser), is to create an erroneous event handler like this:
<h1 onClick="throw new Error('JavaScript Sentry was successfully integrated')">
My Website
</h1>
… and clicking on the element afterwards.
If your site has a strict CSP, see Using inline event handlers with a strict Content Security Policy (CSP).
Does <html> or <body> scroll the page?
Scrolling overflowing elements with JavaScript
HTML elements with overflow-y: auto
or overflow-y: scroll
will get a scrollbar when their content is higher than their own height.
When you scroll an element , the element's scrollTop
property is updated with the scrollbar's new position. You can also set element.scrollTop = 30
to scroll the element to a vertical pixel position counted from the top.
Scrolling the main viewport with JavaScript
The browser's main document viewport is also scrollable by default. The element that ...
Trigger a link's click action with Javascript
Use the click
method on the DOM element:
let link = document.querySelector('a')
link.click()
RSpec: Inferring spec type from file location
RSpec Rails can automatically mix in different behaviors to your tests based on their type
tag, for example enabling you to call get
and
post
in specs with the tag type: :request
.
Alternatively you can skip these tags by setting the config config.infer_spec_type_from_file_location!
in the spec_helper.rb
. This will automatically choose the right type context based on the file location of the test.
For instan...
Rails 6.1: where.not changes behaviour from NOR to NAND
Since Rails 6.1, if we use where.not
with multiple attributes, it applies logical NAND (NOT(A) OR NOT(B)) instead of NOR (NOT(A) AND NOT(B)). If you do not take care, this change will increase the matched set.
Examples
"Don't send newsletters neither to admins nor to trashed users!" becomes "Don't send newsletters to trashed admins".
User.where.not(role: 'admin', trashed: true)
# Before Rails 6.1, with NOR
=> "SELECT "users".* FROM "users" WHERE "users"."role" != 'admin' AND "users"."trashed" != TRUE"
# Equivale...
RSpec: Define negated matcher
You can use RSpec::Matchers.define_negated_matcher
to define a negated version of an existing matcher.
This is particularly useful in composed matcher expressions or to create more expressive and meaningful matchers.
Examples
RSpec::Matchers.define_negated_matcher :not_change, :change
describe 'A negated matcher' do
it 'can be used to chain negated changes' do
expect { subject.maybe_change(object) }
.to not_change(object, :attr_1)
.and not_change(contract, :attr_2)
end
end
RSpec::...
Browsers will not send a referrer when linking from HTTPS to HTTP
- When your site is on HTTPS and you are linking or redirecting to a HTTP site, the browser will not send a referrer.
- This means the target site will see your traffic as "direct traffic", i.e. they cannot distinguish such hits from a user who directly typed in the URL.
Reasons for this behavior
It's probably because of this RFC:
Clients SHOULD NOT include a Referer header field in a (non-secure) HTTP request if the referring page was transferr...
Ruby: Using named groups in Regex
An alternative of using a multiple assignment for a Regex are named groups. Especially when your Regex becomes more complicates it is easier to understand and to process.
Note:
- In case a string does not match the pattern,
.match
will returnnil
. - With Ruby 2.4 the result of
.match
can be transformed to aHash
withnamed_captures
. This allows you to use methods likeslice
orfetch
on the result.
Example with a mult...
CSS Grid Display allows defining number of grid columns based on child width
An element with display: grid
can define its grid-template-columns
based on (preferred) child width using the repeat
function with auto-fill
or auto-fit
, like so:
grid-template-columns: repeat(auto-fit, 100px)
auto-fill
and auto-fit
behave differently if you use rules with dynamic sizing, like minmax(100px, 1fr)
. Simply put, auto-fill
will create as many columns as possible, including empty ones, while auto-fit
hides empty columns.
See the linked page for more details.