MySQL: Disable query cache for database profiling
If you want to see how long your database queries actually take, you need to disable MySQL's query cache. This can be done globally by logging into a database console, run
SET GLOBAL query_cache_type=OFF;
and restart your rails server.
You can also disable the cache on a per query basis by saying
SELECT SQL_NO_CACHE * FROM ...
You also probably want to disable Rails internal (per-request) cache. For this, wrap your code with a call to ActiveRecord::Base.uncached
. For example, as an around_filter
:
d...
How to write a good changelog
We want to keep a changelog for all gems we maintain. There are some good practices for writing a changelog that adds value, please stick to these.
- Add a notice to the README's contribute section about the changelog
- For every release update the changelog
- Note the date format yyyy-mm-tt
What is a changelog?
A changelog is a file which contains a curated, chronologically ordered list of notable changes for each version of a project.
Why keep a changelog?
To make it easier for users and...
Carrierwave processing facts
- Class-level
process
definitions are only applied to the original file - Versions are generated based on the processed original file
- Callbacks (
before
/after
) are applied to original file and each version by itself - Under the hood, a version is an instance of the uploader class that has no versions
- Version uploader and original uploader can be distinguished by checking
#version_name
: version uploaders return the version name, whereas the original uploader instance returnsnil
- Version instances do not have a re...
Ubuntu: Disable webcam microphone
If you want to system-wide disable the microphone of your external webcam in PulseAudio use the following one-liners:
# Connected cards
$ pactl list short cards
1 alsa_card.pci-0000_00_1f.3 module-alsa-card.c
5 alsa_card.usb-Lenovo_ThinkPad_Thunderbolt_4_Dock_USB_Audio_000000000000-00 module-alsa-card.c
6 alsa_card.usb-HD_Webcam_C270_HD_Webcam_C270-02 module-alsa-card.c
# Disable HD_Webcam_C27
$ pactl list short cards | grep HD_Webcam_C27 | cut -f2 | xargs -rt -I % pactl set-card-profile % off
pactl s...
Geordi 10.0.0 released
10.0.0 2024-03-07
Compatible changes
-
console
command: You can now globally disable the IRB multiline feature by settingirb_flags: --nomultiline
in~/.config/geordi/global.yml
. All configured irb_flags are automatically passed on to the console IRB. -
console
command:Ctrl + C
now properly exits a local Rails console -
rspec
andcucumber
commands: Run specs even if the automatic chromedriver update fails - Improve detection of IRB version
- Add new hints to 'Did you know'
Breaking changes
-
dump
command: Drop...
Grid by Example: a website about CSS Grid
Rachel Andrew has built a website about CSS Grid.
- Video tutorials
- More than 30 layout examples for feature demonstration
- Layout patterns for copy-paste use
- All grouped by topic: "Placing items onto the grid", "Sizing of tracks and items" etc. with video, linked articles, examples each
Chaining Capybara matchers in RSpec
You can chain multiple Capybara matchers on the page
or any element:
expect(page)
.to have_content('Example Course')
.and have_css('.course.active')
.and have_button('Start')
When you chain multiple matchers using and
, [Capybara will retry the entire chain](https://github.com/teamcapybara/capybara/blob/c0cbf4024c1abd48b0c22c2930e7b05af58ab284/lib/capybara/rspec/matc...
UX details for web developers
A list of implementation details that make for a better / expected user experience. Have these in mind when implementing a web design.
This document outlines a non-exhaustive list of details that make a good (web) interface. It is a living document, periodically updated based on learnings. Some of these may be subjective, but most apply to all websites. The WAI-ARIA spec is deliberately not duplicated in this document. However, some accessibility guidelines may be pointed out.
CSS: Opacity is not inherited in Internet Explorer
Non-static elements will not inherit their parent's opacity in IE for no good reason. This can lead to unexpected behaviour when you want to fade/hide an element and all its children.
To fix it, give the parent element defining the opacity a non-static positioning. For example:
.parent {
opacity: 0.2;
position: relative; /* for IE */
}
While the linked article describes this problem for IE9 and below, I have encountered the same issue in IE10 and IE11.
Just go away, Internet Explorer!
Understanding z-index: it's about stacking contexts
The CSS property z-index
is not as global as you might think. Actually, it is scoped to a so-called "stacking context". z-indexes only have meaning within their stacking context, while stacking contexts are treated as a single unit in their parent stacking context. This means indices like 99999
should never actually be needed.
Creating a new stacking context
In order to create a stacking context with the least possible side effects, use the isolation
property on an...
Imagemagick: Batch resize images
Trick: Do not use convert
but mogrify
:
mogrify -resize 50% *
This overwrites the original image file.
In contrast, convert
writes to a different image file. Here is an example if you need this:
cd /path/to/image/directory
for i in `ls -1 *jpg`; do convert -resize 50% $i "thumb_$i"; done
Accessibility: Making non-standard elements interactive
A common cause of non-accessible web pages are elements that were made interactive via JavaScript but cannot be focused or activated with anything but the mouse.
❌ Bad example
Let's take a look at a common example:
<form filter>
<input filter--query name="query" type="text">
<span filter--reset>Clear Search</span>
</form>
The HTML above is being activated with an Unpoly compiler like this:
up.compiler('[filter]', function(filterForm) {
const resetButton = filterForm.querySelec...
Top Accessibility Errors in 2023
These are the top ten accessibility errors as researched by TPGi, a company focusing on accessibility. See the linked article for details on each item, as well as instructions on how to do it correctly.
- No link text
- Non-active element in tab order
- Missing link alt attribute
- No alt text
- List not nested correctly
- Duplicate labels used
- Positive tabindex value
- Invalid aria-describedby
- No label for button element
- Invalid aria-labelledby
Generally, I am surprised by these items. I would have expected more complex i...
Virtual scrolling: A solution for scrolling wide content on desktops
I recently built a screen with a very high and wide table in the center. This posed some challenges:
- Giving the table a horizontal scroll bar is very unergonomic, since the scrollbar might be far off screen.
- Making the whole page scrollable looks bad, since I don't want the rest of the UI to scroll.
- Giving the table its own vertical scrollbar and a limited height would have solved it, but felt weird, since the table was 90% of the page.
What I ended up doing is reusing the horizontal page scrollbar (which is naturally fixed at t...
Getter and setter functions for JavaScript properties
JavaScript objects can have getter and setter functions that are called when a property is read from or written to.
For example, if you'd like an object that has a virtual person.fullName
attribute that dynamically composes person.firstName
and person.lastName
:
var person = {
firstName: 'Guybrush',
lastName: 'Threepwood',
get fullName() {
return this.firstName + " " + this.lastName;
},
set fullName(name) {
var parts = name.split(" ");
this.firstName = parts[0];
this.lastName = parts[1];
}
};
`...
RSpec: Where to put custom matchers and other support code
Custom matchers are a useful RSpec feature which you can use to DRY up repetitive expectations in your specs. Unfortunately the default directory structure generated by rspec-rails
has no obvious place to put custom matchers or other support code.
I recommend storing them like this:
spec/support/database_cleaner.rb
spec/support/devise.rb
spec/support/factory_bot.rb
spec/support/vcr.rb
spec/support/matchers/be_allowed_access.rb
s...
Developer Soft Skills
Knowing when to refactor
Just feeling like refactoring is not a good reason to do it. Make an educated decision: Is the code change worth the effort? Stay straight on track, don't go astray.
Note: Usually, you'd refactor after finishing your story. At times, it might be necessary to refactor up front. But try to keep refactoring out of your current task.
Knowing where to optimize
Things should only be optimized a) when they have settled, i.e. haven't changed for a longer time, and b) when there is a measurable gain from th...
How to not repeat yourself in Cucumber scenarios
It is good programming practice to Don't Repeat Yourself (or DRY). In Ruby on Rails we keep our code DRY by sharing behavior by using inheritance, modules, traits or partials.
When you reuse behavior you want to reuse tests as well. You are probably already reusing examples in unit tests. Unfortunately it is much harder to reuse code when writing integration tests with Cucumber, where you need to...
RSpec 3 allows chaining multiple expectations
When you are using lambdas in RSpec to assert certain changes of a call, you know this syntax:
expect { playlist.destroy }.to change { Playlist.count }.by(-1)
While you can define multiple assertions through multiple specs, you may not want to do so, e.g. for performance or for the sake of mental overhead.
Multiple expectations on the same subject
RSpec allows chaining expectations simply by using and
.
expect { playlist.destroy }
.to change { Playlist.count }.by(-1)
.and not_change { Video.count }
...
ActiveRecord: validate_uniqueness_of is case sensitive by default
By default, Rails' validates_uniqueness_of
does not consider "username" and "USERNAME" to be a collision. If you use MySQL this will lead to issues, since string comparisons are case-insensitive in MySQL.
(If you use PostgreSQL, read this instead.)
Say you have a user model
class User < ActiveRecord::Base
validates_uniqueness_of :name
end
with a unique index in the database.
If you try to create the users "user" and "USER", this will not trigger a validation error, but may fail with an SQL error due ...
Migrating from Elasticsearch to Opensearch: searchkick instructions (without downtime!)
General
A general overview about why and how we migrate can be found under Migrating from Elasticsearch to Opensearch
This card deals with specifics concerning the use of searchkick.
Step 1: Make Opensearch available for Searchkick
In your Gemfile
# Search
gem 'searchkick' # needs to be > 5, to use Opensearch 2
gem 'elasticsearch'
gem 'opensearch-ruby'
in config/initializers/searchkick.rb
(or wherever you have configured your Searchkick settings) add:
SEARCHKICK_CLIENT_T...
You should be using the Web Animations API
The Web Animations API has great browser support, and you should be using it to animate DOM elements from JavaScript, or to control or wait for CSS animations.
Here is a quick overview of a few useful features:
Animating elements from JavaScript
Use the Element#animate() function to perform animations on an element.
Its API probably a bit different from how your...
Be careful when checking scopes for blankness
Today I stumbled across a pretty harmless-looking query in our application which turned out to be pretty harmful and caused huge memory usage as well as downing our passenger workers by letting requests take up to 60 seconds. We had a method that received a scope and then checked, if the scope parameter was blank?
and aborted the method execution in this case.
def foo(scope)
return if scope.blank?
# Use scope, e.g.
scope.find(...)
end
We then called this method with an all
scope: foo(Media::Document::Base.all)
. *...