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:

Image

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

Chrome DevTools: Quick Bite - Store Element in Global Variable

tl;dr

In the Elements tab in the Chrome DevTools you can right click on an element and select Store as global variable.

Example

Image

Temporary solution for connection errors with rubygems

The problem

If you're experiencing that your bundle install command fails with an error message like this, rubygems.org might have issues with their ipv6 connectivity:

$ bundle install
Fetching source index from https://rubygems.org/

Retrying fetcher due to error (2/4): Bundler::HTTPError Could not fetch specs from https://rubygems.org/ due to underlying error <timed out (https://rubygems.org/specs.4.8.gz)>

The (a little bit dirty) possible solution

If that's actually the case, then you can try to deprioritize the ipv...

Debug flaky tests with an Unpoly observeDelay

The problem

Unpoly's [up-observe], [up-autosubmit] and [up-validate] as well as their programmatic variants up.observe() and up.autosubmit() are a nightmare for integration tests.

Tests are usually much faster than the configured up.form.config.observeDelay. Therefore, it may happen that you already entered something into the next field before unpoly updates that field with a server response, discarding your changes.

The steps I wait for active ajax requests to complete (if configured) and capybara-lockstep can catch some ...

Rails: Validations of Dates, Numerics and Strings with ComparisonValidator

tl;dr

Since Rails 7+ you can use ComparisonValidator for validations like greater_than, less_than, etc. on dates, numerics or strings.

Example

We have a model for booking a trip. This model has mandatory attributes to enforce dates for the start and the end.

# == Schema Information
#
# start_date :date
# end_date   :date
# ...

class TripBooking < ApplicationRecord
  validates :start_date, presence: true
  validates :end_date, presence: true
end

These validations are enough. We also want to ensure, th...

Careful when using Time objects for generating ETags

You can use ETags to allow clients to use cached responses, if your application would send the same contents as before.

Besides what "actually" defines your response's contents, your application probably also considers "global" conditions, like which user is signed in:

class ApplicationController < ActionController::Base

  etag { current_user&.id }
  etag { current_user&.updated_at }

end

Under the hood, Rails generates an ETag header value like W/"f14ce3710a2a3187802cadc7e0c8ea99". In doing so, all objects from that etagge...

Use capybara and not rspec matchers

One rule of thumb I try to follow in capybara tests is using capybara matchers and not plain rspec matchers.

One example:

visit(some_page)
text_field = find('.textfield')
expect(text_field['value']).to match /pattern/

This can work, but is too brittle and flaky. match will not retry or synchronize the value of text_field.

The equivalent code with a capybara matcher:

visit(some_page)
expect(page).to have_field('.textfield', with: /pattern/)

have_field will retry for and synchronize the text_field.

RubyMine: Find and Replace with Regex (Capture Groups and Backreferences)

tl;dr

In RubyMine you can use find and replace with capture groups (.*?) and backreferences $1 (if you have several groups: $[Capture-Group ID]).
Named captures (?<text>.*) are also supported.

Examples

Replace double quotes with single quotes

If you want to replace double quotes with single quotes, replacing every " with a ' is prone to errors. Regular expressions can help you out here.

  1. Open find and replace
  2. Activate the regex mode (click on the .* icon next to the "find" field).
  3. Fill in f...

Prefer using Dir.mktmpdir when dealing with temporary directories in Ruby

Ruby's standard library includes a class for creating temporary directories. Similar to Tempfile it creates a unique directory name.

Note:

  • You need to use a block or take care of the cleanup manually
  • You can create a prefix and suffix e.g. Dir.mktmpdir(['foo', 'bar']) => /tmp/foo20220912-14561-3g93n1bar
  • You can choose a different base directory than Dir.tmpdir e.g. `Dir.mktmpdir('foo', Rails.root.join('tmp')) => /home/user/rails_example/tmp/foo20220912-14...

Rails: Comparison of Dates - before? and after?

tl;dr

Since Rails 6+ you can use before? and after? to check if a date/time is before or after another date/time.

Example

christmas = Date.parse('24.12.2022')
date_of_buying_a_gift = Date.parse('12.12.2022')

date_of_buying_a_gift.before?(christmas)
# => true
# Now you are well prepared for Christmas!! ;)

date_of_buying_a_gift = Date.parse('26.12.2022')

date_of_buying_a_gift.after?(christmas)
# => true
# Now you are too late for christmas! :(

Hint

If you want to check if a date/time is between to ot...

Careful: `fresh_when last_modified: ...` without an object does not generate an E-Tag

To allow HTTP 304 responses, Rails offers the fresh_when method for controllers.

The most common way is to pass an ActiveRecord instance or scope, and fresh_when will set fitting E-Tag and Last-Modified headers for you. For scopes, an extra query is sent to the database.

fresh_when @users

If you do not want that magic to happen, e.g. because your scope is expens...

JSON APIs: Default design for common features

When you build a JSON API you need to come up with a style to represent attributes, pagination, errors or including associated objects. Instead of reinventing the wheel, you may reuse successful API designs.

JSON API

JSON:API specifies common features found in most JSON APIs, like pagination, ordering and nested resources. The spec looks very similar to how one would build an API with Rails and uses similar patterns. Even if you don't plan on supporting the whole spec, it can still make sense to know how th...

CSS: :is() pseudo selector

tl;dr

The :is() pseudo selector - specificity of its most specific argument - matches against a comma-separated list of selectors.

Example

Compound selectors like ...

.datepicker .prev, .datepicker .next, .datepicker .switch
  padding-bottom: 1rem

ul li, ol li
  list-style-type: none

can be simplified by using the :is() pseudo selector ...

.datepicker :is(.prev, .next, .switch)
  padding-bottom: 1rem

:is(ul, ol) li
  list-style-type: none

Hint

The specificity of :is() is equals t...

CSS: :where() pseudo selector

tl;dr

The :where() pseudo selector - zero specificity - matches against a comma-separated list of selectors.

Example

Compound selectors like ...

.datepicker .prev, .datepicker .next, .datepicker .switch
  padding-bottom: 1rem

ul li, ol li
  list-style-type: none

can be simplified by using the :where() pseudo selector ...

.datepicker :where(.prev, .next, .switch)
  padding-bottom: 1rem

:where(ul, ol) li
  list-style-type: none

Hint

The specificity of :where() is always zero!

I...

Generating and streaming ZIP archives on the fly

When your Rails application offers downloading a bunch of files as ZIP archive, you basically have two options:

  1. Write a ZIP file to disk and send it as a download to the user.
  2. Generate a ZIP archive on the fly while streaming it in chunks to the user.

This card is about option 2, and it is actually fairly easy to set up.

We are using this to generate ZIP archives with lots of files (500k+) on the fly, and it works like a charm.

Why stream downloads?

Offering downloads of large archives can be cumbersome:

  • It takes time to b...

makandra tech survey - results

These are the results of the "personal tech stack survey". I've included only the most popular mentions, maybe it can help you find one or two useful tools for your own usage.

Desktop environment

pie title Desktop environment
    "Gnome" : 16
    "i3": 2
    "sway": 2
    "awesome": 1
    "bspwm": 1
    "mate": 1
    "xfce": 1

Gnome dominates (unsuprising, it's the Ubuntu default), but quite a few people use tiling window managers, most popular i3 and the mostly i3-compatible [sway](https://swaywm....

PostgreSQL: How to show database size

SELECT pg_size_pretty(pg_database_size('some-database'));

Example

SELECT pg_size_pretty(pg_database_size('cards_p'));
----------------
 13 GB
(1 row)
SELECT pg_database_size('cards_p');
 pg_database_size 
------------------
      13524832927
(1 row)

Related

Double loading issue with Ruby default gems

Ruby includes many standard gems that are bundled into the Ruby installation. Here is an example for the gem strscan that will be displayed as default:

gem list strscan     

*** LOCAL GEMS ***

strscan (default: 3.0.1)

It is still possible to have newer version of a gem installed beside the default version:

gem install strscan  
Fetching strscan-3.0.3.gem
Building native extensions. This could take a while...
Successfully installed strscan-3.0.3
1 gem installed
gem list strscan   

*** LOC...

Do not forget mailer previews

When changing code in mailers, updating the corresponding mailer preview is easily forgotten.

Mailer previews can be tested like other code as well and I sometimes add the following test to test suites:

# Make sure to require the previews
Dir[Rails.root.join('test/mailers/previews/*.rb')].sort.each { |file| require(file) }


ActionMailer::Preview.all.index_with(&:emails).each do |preview, mails|
  mails.each do |mail|
    it "#{preview}##{mail} works" do
      expect { preview.call(mail) }.not_to raise_error
   ...