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 t...

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 ...

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?
  
  # Do something with scope
end

We then called this method with an all scope: foo(Media::Document::Base.all). **Be carefu...

Rails route namespacing (in different flavors)

TL;DR There are three dimensions you can control when scoping routes:

scope module: 'module', path: 'path', as: 'as' do
  resources :examples, only: :index
end

=> Path Helpers: as_examples_path and as_examples_url
=> URLs: /path/examples
=> Controller module: Module::ExamplesController and views location: app/views/module/examples/

These options work with resources as well, e.g. resources :examples, path: 'demonstration'

Changing URLs only

When you want to namespace some paths but leave the corresponding ...

ES6 imports are hoisted to the top

From Exploring ES6:

Module imports are hoisted (internally moved to the beginning of the current scope). Therefore, it doesn’t matter where you mention them in a module and the following code works without any problems:

foo();
import { foo } from 'my_module';

Footgun example

When you're not aware of import hoisting you may be surprised that your code runs in a different order than you see in the source file.

The example below is taken from the [...

How to examine an unknown Ruby object

When debugging your application, you will come across objects created by some gem or framework. You don't have the source code at hand, still need to inspect this object. Here are some tools to do so:

Relevant methods

@object.methods - Object.instance_methods returns a list of methods excluding methods inherited from Object. This makes the methods list drastically more relevant. You can also try subtracting other base classes like ActiveRecord::Base.methods etc.
To further narrow it down you can also just look at public methods...

How to display an unsaved changes alert

All browsers implement an event named beforeunload. It is fired when the active window is closed and can be used to display an alert to warn the user about unsaved changes.

To trigger the alert, you have to call preventDefault() on the event.

Note

The beforeunload event is only dispatched when the user navigation makes a full page load, or if it closes the tab entirely. It will not be dispatched when navigating via JavaScript. In this case you need to ...

Working on the Linux command line: How to bash `cd` with autocorrect

There is an option you can set so that when using the cd command, small typos are automatically corrected. Add the following to your ~/.bashrc:

# cd: autocorrect small typos and use best guess
shopt -s cdspell 

Example:

cd Porjects
# Projects

pwd
# /home/judith/Projects

Also, I recommend adding aliases for your most common typos of commands you regularly use to your ~/bashrc. Which ones that are is highly personal, for me it's e.g. tig:

alias tog='tig'
alias tug='tig'

How to discard ActiveRecord's association cache

You know that ActiveRecord caches associations so they are not loaded twice for the same object. You also know that you can reload an association to make Rails load its data from the database again.

user.posts.reload
# discards cache and reloads and returns user.posts right away
# => [...]

If you want to discard the cache but not query the database (only the next time the association is accessed), you can use reset:

user.posts.reset
# discards cache, but does not load anything yet
user.posts
# SQL query happens to ...

In Chrome 121+ the now supported spec-compliant scrollbar properties override the non-standard `-webkit-scrollbar-*` styles

Up until Chrome 120, scrollbars could only be styled using the various -webkit-scrollbar-* pseudo elements, e.g. to make the scrollbars have no arrows, be rounded, or with additional margin towards their container.

Starting with version 121, Chrome now also supports the spec-compliant properties scrollbar-width and scrollbar-color.
These allow less styling. You may only specify the track and thumb colors, and a non-specific width like auto, thin, or none.

Heads up: You should always use "current_window.resize_to" to resize the browser window in tests

I recently noticed a new kind of flaky tests on the slow free tier GitHub Action runners: Integration tests were running on smaller screen sizes than specified in the device metrics. The root cause was the use of Selenium's page.driver.resize_window_to methods, which by design does not block until the resizing process has settled:

We discussed this issue again recent...

Use <input type="number"> for numeric form fields

Any form fields where users enter numbers should be an <input type="number">.

Numeric inputs have several benefits over <input type="text">:

  • On mobile or tablet devices, number fields show a special virtual keyboard that shows mostly digit buttons.
  • Decimal values will be formatted using the user's language settings.
    For example, German users will see 1,23 for <input type="number" value="1.23">.
  • Values in the JavaScript API or when submitting forms to the server will always use a point as decimal separator (i.e. "1.23" eve...