Rails: Encrypting your database information using Active Record Encryption
Since Rails 7 you are able to encrypt database information with Active Record. Using Active Record Encryption will store an attribute as string in the database. And uses JSON for serializing the encrypted attribute.
Example:
-
p
: Payload -
h
: Headers -
iv
: Initialization Vector -
at
: Authentication Tag
{ "p": "n7J0/ol+a7DRMeaE", "h": { "iv": "DXZMDWUKfp3bg/Yu", "at": "X1/YjMHbHD4talgF9dt61A=="} }
Note this before encrypting attributes with Active Record:
...
git: find the version of a gem that releases a certain commit
Sometimes I ran across a GitHub merge request of a gem where it was not completely obvious in which version the change was released. This might be the case for a bugfix PR that you want to add to your project.
Git can help you to find the next git tag that was set in the branch. This usually has the name of the version in it (as the rake release
task automatically creates a git tag during release).
git name-rev --tags <commit ref>
Note
The more commonly used
git describe
command will return the last tag before a c...
Postgres: DISTINCT ON lets you select only one record per ordered attribute(s) for each group
-
To retrieve only unique combinations of the selected attributes: You can omit rows, where all selected columns are equal with the
DISTINCT
statement. -
To retrieve the group wise maximum of certain columns: You can keep only one record for each group with the
DISTINCT ON
statement, to omit equal rows within each specified group.
Use case
You have a query where you want only one record for a set of specifically ordered attributes.
How to use?
Let's say we look at the example how to query only the latest post for each user:
...
RSpec: How to compare ISO 8601 time strings with milliseconds
Rails includes milliseconds in Time
/ DateTime
objects when rendering them as JSON:
JSON.parse(User.last.to_json)['created_at']
#=> "2001-01-01T00:00:00.000+00:00"
In RSpec you might want to use .to_json
instead of .iso8601
to use the build-in eq
matcher:
it 'returns the created at attribute of a user' do
get '/users/1'
expect(JSON.parse(response.body)['created_at']).to eq(Time.parse('2001-01-01').to_json)
end
Otherwise the strings do not match:
DateTime.parse('2001-01-01').to_s (will defa...
How to kill a Rails development server by force
Sometimes, the rails dev server doesn't terminate properly. This can for example happen when the dev server runs in a RubyMine terminal.
When this happens, the old dev server blocks port 3000, so when you try to start a new server, you get the error:
Address already in use - bind(2) for "127.0.0.1" port 3000 (Errno::EADDRINUSE)
You can terminate such a dev server with this command:
lsof -t -i :3000 -s TCP:LISTEN | xargs kill -9
It might be worth it to add this to your bash aliases.
Heads up: expect(object).to receive(:method_name) does not execute the original implementation of the method
Let's assume that we have a model Movie
that registers a callback function when a new instance of Movie
is created (Note: For the purpose of this card it is not important what that callback does or which type of callback it is).
This is how we test whether the callback function (here it is named :my_method
) is called when a new movie is created:
expect_any_instance_of(Movie).to receive(:my_method)
create(:movie) # <-- this is where the method :my_method should be called
You might expect that when calling `create(:mo...
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...
Why Sidekiq Jobs should never be enqueued in an `after_create` or `after_save` callback
When an object is created / updated, various callbacks are executed in this order:
before_validation
after_validation
before_save
around_save
before_create
around_create
after_create
after_save
after_commit / after_rollback
Thus, each of these callbacks is executed at a specific time in the life cycle of the object. This is important because this point in time determ...
Ruby object equality
TLDR
if you define a equality method for a class you must also implement
def hash
.
Ruby has a lot of methods that have to do something with equality, like ==
, ===
, eql?
, equal?
. This card should help you differentiate between those and give you hints on how to implement your own equality methods in a safe manner.
Differences between the methods
for everyday use: ==
When you compare two objects in ruby, you most often see the use of foo == bar
. By default the ==
operator inherits from Object
and is impl...
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()
...
Heads up: network requests `Kernel#open` are not mocked with VCR
We usually rely on VCR and WebMock to prevent any real network connection when running our unit tests.
This is not entirely true: They are both limited to a set of HTTP libraries listed below (as of 2022). Direct calls to Kernel#open
or OpenURI#open_uri
are not mocked and will trigger real network requests even in tests. This might bite you e.g. in [older versions of CarrierWave](https://github.com/carrierwaveuploader/carrierwave/blob/0.11-stable/lib/carrierwave/upl...
Destructors for async Unpoly compilers
Usually, Unpoly compiler destructors are return
ed from the compiler function.
However, when using async
compiler functions, you can not register destructors via return
.
This will not work:
up.compiler('my-example', async (element) => {
await something
return function onDestroy() {
// ...
}
})
Instead, use up.destructor:
up.compiler('my-example', async (element) => {
await something
u...
Using path aliases in esbuild
In esbuild, you usually import other files using relative paths:
import './some-related-module'
import `../../utils/some-utility-module`
import `../../../css/some-css.sass`
This is totally fine if you import closely related files, but a bit clunky when you're trying to import some "global" module, like a utility module. When moving a file, your imports also need to change.
To get around this, esbuild support a mechanism first introduced in TypeScript called "path aliases". It works like this:
First, you create a file called `js...
Rails: Use STI in Migration
tl;dr
You should decouple migrations from models by embedding models into the migration. To use STI in this scenario you have to overwrite
find_sti_class
andsti_name
.
Tip
When possible, try to avoid STI in migrations by disabling it.
Example
Warning
This is more for the sake of I want to do it but I kno...
How to see how many inotify instances are used by each process
As a developer you may have many tools watching your project for changes: Your IDE, Webpack, Guard, etc. This is often done with an inotify watcher. If you have too many inotify instances you may run into limits of your operating system.
To find out which process is using them all up you can run:
sudo find /proc/*/fd/ -type l -lname "anon_inode:inotify" -printf "%hinfo/%f\n" | xargs grep -cE "^inotify" | column -t -s:
You will get a list like:
/proc/3753/fdinfo/7 1
/proc/3774/fdinfo/7 1
/proc/4034/fdinfo/12 14
/pr...
Jasmine: Creating DOM elements efficiently
Jasmine specs for the frontend often need some DOM elements to work with. Because creating them is such a common task, we should have an efficient way to do it.
Let's say I need this HTML structure:
<ul type="square">
<li>item 1</li>
<li>item 2</li>
</ul>
This card compares various approaches to fabricating DOM elements for testing.
Constructing individual elements
While you can use standard DOM functions to individually create and append elements, this is extremely verbose:
let list = document.createElement('...
Jasmine: Cleaning up the DOM after each test
Jasmine specs that work with DOM elements often leave elements in the DOM after they're done. This will leak test-local DOM state to subsequent tests.
For example, this test creates a <spoiler-text>
element, runs some expectations, and then forgets to remove it from the DOM:
describe('<spoiler-text>', function() {
it ('hides the secret until clicked', function() {
let element = document.createElement('spoiler-text')
element.secret = 'The butler did it'
document.body.appendChild(element)
...
Inspect and Debug CSS Flexbox and Grid Layouts by using the Layouts Tab in Dev Tools
tl;dr
In Chrome DevTools in the Layouts tab you have handy options to debug CSS Flexbox and Grid. Including:
- Display size and lines along with labels
- Changing their attributes
- Change how overlay is colored and fastly switch nested elements in the Elements panel
This guide will only cover some example gif recordings on how to use with Grid, since it's basically straight forward to apply this for Flexbox by yourself afterwards.
For this purpose a the link to documentation and a simple code pen have been added...
Do not use unitless zeros in CSS calc functions
While in CSS zero is usually referenced without specifying a unit (e.g. padding: 0
), you must not use a unitless zero in calc
functions.
You would probably not write something like calc(1rem + 0)
yourself, but it might be the result of a CSS preprocessor (like Sass) or when using custom properties.
The following is invalid:
.example {
--extra-padding: 0;
padding: calc(1rem + var(--extra-padding));
}
That is simply because it is unsupported, as per docum...
Organizing custom Date(Time) formats
Large Rails projects tend to define multiple custom ways to format Date
s or DateTime
s. This often leads to duplicated format strings, wrong formats and unnecessary introduction of new formats.
to_fs
also supports to refer to those formats by name e.g. to_formatted_s(:iso_8601)
or to_formatted_s(:short)
.
to_fs
is an alias for to_formatted_s
.
Those names are defined in Time::DATE_FORMATS
and it's possible to add your own formats. There is a how to in the official [docs](https://api.rubyonrails.org/classes/Date.html#method-i-t...
Sass: How to get rid of deprecation warnings in dependencies
sass >= 1.35.0
has the option quietDeps
and silenceDeprecations
to silence deprecation warnings from dependencies.
-
quietDeps
: No deprecation warnings for dependencies e.g. Bootstrap -
silenceDeprecations
: Allows to set a list of deprecation types you want to silence for your own code
Below there are a few examples for different build tools how to set the Sass options.
Webpacker
const sassLoaderConfig = environment.loaders.get('sass')
const...
Chrome DevTools: Event Listener Breakpoints
tl;dr
In Chrome DevTools in the Sources tab you can activate Event Listener Breakpoints for debugging events.
Example
The Event Listener Breakpoints in the Chrome DevTools can be quiet useful for debugging why and where code is handling specific events.
Here you can see a very simple example that shows what lines of code handle a click:
You can use this Code Pen if you want to try it yourself.
Limitation
...
Chrome DevTools: DOM Breakpoints - Breakpoints on HTML Elements
tl;dr
In Chrome DevTools in the Elements tab or in Firefox in the Inspector tab you can right click on an element and choose Break on to debug changes related to this element.
Example
DOM Breakpoints can be quite useful to quickly find the JavaScript that is responsible for some (unexpected) behavior. You can use DOM Breakpoints for debugging subtree modifications, attribute modifications or node removal.
Here you can see a very simple example that shows what JavaScript lines are responsible for ...