Rails developers: Have better context in Git diffs
Git diffs show the surrounding contexts for diff hunks. It does so by applying regular expressions to find the beginning of a context. When it comes to Ruby, however, it will not find method heads and travel up to the class definition:
@@ -24,7 +24,7 @@ class TicketPdf # <=== Actually expected here: the method definition
ApplicationController.render(
"tickets/index.html.haml",
layout: "tickets",
- assigns: { tickets: tickets }
+ assigns: { tickets: tickets, event_name: event_name }
)
end
end
```...
Project management best practices: Project team responsibilities
In a project team for a bigger project people have several roles:
- Developer: at makandra
- Project lead: at makandra
- Project manager (PM): at makandra, external, or with the customer. In a smaller project this person is also the project lead.
- Product owner (PO): with the customer
Developer
- Development
- Take responsibility for their stories. This includes always gathering all necessary information from the project lead or the PM, communicate blockers, make sure stories are merged, deployed etc.
- Tell the project lead, if y...
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...
How to enable template coverage support for simplecov
Since Ruby 3.2.0 you can measure coverage support for eval statements and support has been added for the simplecov gem as well.
This allows to track coverage across ruby templates such as haml, erb, ...
Simply set this within simplecov
SimpleCov.start do
enable_coverage_for_eval
end
SVGO: SVG Optimizer
SVG files are often much larger than necessary, containing comments, metadata, hidden elements etc.
Optimize them with this tool.
- Web UI: https://jakearchibald.github.io/svgomg/
- Binary: https://github.com/svg/svgo
Note that for a properly scaling SVG, you need to keep the viewBox attribute. There's an option --disable=removeViewBox for this.
FactoryBot: Passing attributes to associated records using transient attributes
FactoryBot.define do
factory :parent do
transient do
child_name nil
child_allowed_to_drive false
end
child do
association(:child, name: child_name, allowed_to_drive: child_allowed_to_drive)
end
end
factory :child do
name 'Child'
allowed_to_drive false
end
end
# Usage
daughter = FactoryBot.create(:parent, child_name: 'Lisa').child
daughter.name # => 'Lisa'
daughter.allowed_to_drive? # => false
son = FactoryBot.create(:parent, child_name: 'Benedikt', child_allowed_to_drive: tr...
Project management best practices: User stories & Issues
We organize our daily work with issues in our Linear workspace.
Issue format
A good issue needs to be precise. It should be very clear what is part of an issue, and what is not. If there are different expectations between the person who writes and who implements an issue, there will be rejects.
To this end, we use a consistent format for issues that looks like this:
Issue: Autocomplete
As a journalist, I want to have an autocomplete in the search bar, to have a more efficient way to find articles.
Acceptance criteri...
Deterministic ordering of records by created_at timestamp
Creating records in specs can be so fast that two records created instantly after one another might have the same created_at timestamp (especially since those timestamps don't have an indefinitely high resolution). When ordering lists by timestamps, you should therefore always include a final order condition using the primary key of the table.
class Photo < ActiveRecord::Base
scope :by_date, -> { order('created_at DESC, id DESC') }
end
Photo.by_date
Remember to include the id field in the database index.
Rails migration: Changing a column type without losing the content
The change_column method for rails migrations support casting with a custom SQL statement. This allows us to change a column type and keep the former content as the new type. This way, we can for example prepare an address number column to hold German address numbers, which can contain letters:
Example (in most cases not a good idea!)
class ChangeAnIntegerColumnToString < ActiveRecord::Migration[6.1]
def up
change_column :users, :address_number, 'varchar USING CAST(rating AS varchar)'
end
def down
change_column ...
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...
Creating a sample video with ffmpeg
If you need a sample video with certain properties for a test you can create one using ffmpeg.
You might want a very low bitrate file to speed up processing in your test. (e.g. you only care about the length, then you can create a video with a very low resolution and framerate)
Create a 21s video with 1fps and 10x10 resolution:
ffmpeg -t 21 -s 10x10 -r 1 -f rawvideo -pix_fmt rgb24 -i /dev/zero sample_21_seconds.mp4
| Option | Explanation |
|---|---|
-t 21 |
set the length to 21s |
-s 10x10 |
set the resolution the 10 by 10 p... |
Shorthand function properties in ES6
Here is an ES5 object literal with two string properties and a function property:
let user = {
firstName: 'Max',
lastName: 'Muster',
fullName: function() { return this.firstName + ' ' + this.lastName }
}
user.fullName() // => 'Max Muster'
In ES6 we can define a function property using the following shorthand syntax:
let user = {
firstName: 'Max',
lastName: 'Muster',
fullName() { return this.firstName + ' ' + this.lastName }
}
user.fullName() // => 'Max Muster'
We can also define a gette...
nvm: Setting a default Node.js version
To set a default Node version for new shells, use nvm alias default <VERSION>:
nvm alias default 1.2.3
I like to use the most recent LTS version as my default:
nvm alias default lts/erbium
Heads up: Deployment with newly generated SSH key (using ED25519) might fail
If you use a newer SSH key generated with the ED25519 algorithm instead of RSA (see Create a new SSH key pair), the deployment with Capistrano may fail with the following message:
The deploy has failed with an error: unsupported key type `ssh-ed25519'
net-ssh requires the following gems for ed25519 support:
* ed25519 (>= 1.2, < 2.0)
* bcrypt_pbkdf (>= 1.0, < 2.0)
See https://github.com/net-ssh/net-ssh/issues/565 for more information
Gem::LoadError : "ed25519 i...
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...
Manipulate color with Sass functions
Sass comes with many built-in functions to manipulate color. Some of the more interesting functions include:
adjust-hue($color, $degrees)
:Changes the hue of a color.
lighten($color, $amount)
:Makes a color lighter.
darken($color, $amount)
:Makes a color darker.
saturate($color, $amount)
:Makes a color more saturated.
desaturate($color, $amount)
:Makes a color less saturated.
grayscale($color)
:Converts a color to grayscale.
complement($color)
:Return...
Jasmine: Testing AJAX calls that manipulate the DOM
Here is a Javascript function reloadUsers() that fetches a HTML snippet from the server using AJAX and replaces the current .users container in the DOM:
window.reloadUsers = ->
$.get('/users').then (html) ->
$('.users').html(html)
Testing this simple function poses a number of challenges:
- It only works if there is a
<div class="users">...</div>container in the current DOM. Obviously the Jasmine spec runner has no such container. - The code requests
/usersand we want to prevent network interaction in our uni...
Rails: Using database default values for boolean attributes
In the past we validate and set default values for boolean attributes in Rails and not the database itself.
Reasons for this:
- Older Rails didn't support database defaults when creating new records
- Application logic is "hidden" in the database
An alternative approach, which currently reflects more the general opinion of the Rails upstream on constraints in the database, is adding default values in the schema of the database itself. We also ...
Configuring RubyGems to not install documentation by default
When installing gems, a lot of time is spent building locally installed documentation that you probably never use.
We recommend you disable documentation generation for gem install by default.
Note that Bundler won't install documentation, so this advice applies only when installing gems manually.
If you don't already have it, create a ~/.gemrc file. The gemrc is a Yaml file, so add the following line to add default switches to the gem command.
gem: --no-document
(If you do n...
ActiveRecord: count vs size vs length on associations
TL;DR: You should generally use #size to count associated records.
size
- Counts already loaded elements
- If the association is not loaded, falls back to a
COUNTquery
count
- If a counter cache is set up, returns the cached value
- Issues a
COUNTquery else
Caveats
- If you trigger a
COUNTquery for an association of an an unsaved record, Rails will try to load all children where the foreign keyIS NULL. This is not what you want. To prevent this behavior, you can useunsaved_record.association.to_a.size. - `c...
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...
Carrierwave: Custom file validations inside custom Uploaders
Carrierwave's BaseUploader can have some validations that you can use by overriding a certain method, which's expected name is hard coded. A popular example is extension_allowlist, which returns an array of strings and let's you only upload files that have a filename with an extension that matches an entry in that array. Another useful validation can be size_range, which gives you a little bit of control over how your storage gets polluted.
This is often good enough, but some times you need to validate special cases.
Validations t...
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:
...
How to view a file from another branch
Just run git show branch:file. Examples:
git show HEAD~:bin/command
git show origin/master:../lib/version.rb