Minifying object properties in JavaScript files

An introduction to mangling

When you minify ("compress", "optimize") your JavaScript for production, the names of your functions and variables will be renamed for brevity. This process is often called mangling.

E.g. if this is your source code:

function function1() {
  function2()
}

After mangling it would look like this:

function a() {
  b()
}

Object properties are not mangled by default

Minfiers never mangle properties by default, as this can be an unsafe transformation. This leads to larger file sizes if...

Debugging SPF records

While debugging a SPF record I found spf-record.de to be very helpful.

  • it lists all IPs that are covered by the SPF record
  • shows syntax errors
  • helps you debugging errors like DNS lookup limit reached
  • it also lets you test a new SPF strings before applying it. This can save you time as you don't have to loop with operations

Also the advanced check at vamsoft.com has a very good interface to test new SPF policies.

HTTP 302 redirects for PATCH or DELETE will not redirect with GET

A HTTP 302 Found redirect to PATCH and DELETE requests will be followed with PATCH or DELETE. Redirect responses to GET and POST will be followed with a GET. The Rails form_for helper will use a workaround to send POST requests with a _method param to avoid this issue for PATCH/DELETE.

If you make requests yourself, watch out for the following behavior.

When you make an AJAX request PATCH /foo and the /foo action redirects to /bar, browsers will request PATCH /bar. You probably expected the second requ...

What's so hard about PDF text extraction? ‚Äč

There is a common view that extracting text from a PDF document should not be too difficult. After all, the text is right there in front of our eyes and humans consume PDF content all the time with great success. Why would it be difficult to automatically extract the text data?

Turns out, much how working with human names is difficult due to numerous edge cases and incorrect assumptions, working with PDFs is difficult due to the extreme flexibility given by the PDF format.

Node: How to run a globally installed package with npx

You can tell npm to install a package globally with npm -g install @puppeteer/browsers. However, it seems that its not possible that npx can run commands from global packages without referencing the global package path.

Example

Installing @puppeteer/browsers globally:

$ npm -g install @puppeteer/browsers

The globally installed package @puppeteer/browsers can not be access via npx:

$ npx --no-install @puppeteer/browsers

npm ERR! canceled # Error message when package is not installed

But it is installed g...

Updated: Verifying doubles in RSpec 3

Added information that verifying doubles have some limited usages with dynamically defined methods along with some advice on how to handle this.
This includes a link to the documentation that shows several ways on how this can be addressed and a link on how to disable verifying doubles if a simpler approach is necessary.

RSpec: How to write isolated specs with cookies

Background

Rails offers several methods to manage three types of different cookies along with a session storage for cookies. These are normal, signed and encrypted cookies.

By following the happy path of testing a web application, that is only the main use-case is tested as a integration test and the rest as isolated (more unit ...

Rails: Kill spring with fire

To ensure Spring is not running:

bin/spring stop
pkill -f spring

To prevent Spring from starting again:

export DISABLE_SPRING=1

Devise: Invalidating all sessions for a user

Background information about session storage in Rails

Rails has a default mechanism to store the session in the CookieStore. This is a cookie which holds the entire user session hash in the browser. This cookie is serialized, encoded with base64, and signed.

How Devise handles authentication

Devise uses this CookieStore. To track a users session, a salt is stored in the session ...

NVM: How to automatically switch version when changing directories

The Node Version Manager allows installing multiple NodeJS versions and switching between them.
By default, it does not automatically switch versions when entering a directory that holds a .nvmrc file.

The project's readme document offers a bash function which calls nvm use after each cd. In fact, it replaces cd in your bash.

I did not want to do that, but instead use the $PROMPT_COMMAND feature. So here is my take on it.
Note that it is much shorter, it probably does a f...

Scrum: Online Planning Poker

If you are using scrum in a project you might be familiar with planning poker, a playful way to agree on story estimates.

PoinZ is an online planning poker app for just that. It's easy to use and does not require registration.

How to transition the height of elements with unknown/auto height

If you want to collapse/expand elements with dynamic content (and thus unknown height), you can not transition between height: 0 and height: auto.

Doing it properly, with modern CSS features

In the past, you might have resorted to bulky JavaScript solutions or CSS hacks like transitioning between max-height: 0 and max-height: 9999px. All of them were awkward and/or have several edge cases.

With modern CSS, there is actually a way to do it properly:
Just use a display: grid container which transitions its grid row height betwe...

How to prevent a 1fr grid column overflow

TL;DR:

Grid elements have min-width: auto in a 1fr column, which may lead to overflows. With minmax(0, $width) you can reset the min-width.


Say you have a simple grid layout:

.container
  .first-item
  .second-item
  .third-item
.container
  display: grid
  grid-template-columns: 100px 1fr 100px

Your expectation is now that

  • the first item will be located on the left hand side with a fixed width of 100px
  • the third item will be located on the right hand side, also with a width of 100px
  • ...

Use -webkit-line-clamp to natively truncate long (multi-line) texts with an ellipsis

Note: You won't need this for single lines of text. In this case it is better to just use the text-overflow property: Use CSS "text-overflow" to truncate long texts

You can use -webkit-line-clamp in your CSS/SASS to natively render an ellipsis (...) after a specific amount of lines for a multi-line text in your HTML.
Earlier, it was necessary to implement JavaScript solutions like Superclamp.js to enable this because the browser support has been rather limited...

E-mail deliverability

When your application is open for public sign up and sends out transactional e-mails to a large number of users, e-mail deliverability becomes an issue.

E-mail providers work hard to eliminate spam and have put in place relatively tight checks what kinds of emails they will accept, and from whom. To that end we use tools like mail-tester.com to make our mails as acceptable as possible. Unfortunately, there will always be providers that reject our e-mails for some reason or other, sometimes outside of our control.

For exa...

How Rails chooses error pages (404, 500, ...) for exceptions

When your controller action raises an unhandled exception, Rails will look at the exception's class and choose an appropriate HTTP status code and error page for the response.

For instance, an ActiveRecord::RecordNotFound will cause Rails to render a red "The page you were looking for doesn't exist" with a status code of "404" (not found).

The mapping from exception classes to error types is a hash in Rails.configuration.action_dispatch.rescue_responses. The...

Sentry: Different ways of deferring notifications for an issue

We use Sentry to be informed about different kinds of issues. One of the key features is that you are not getting spammed if many errors of the same kind occur in a small timespan.
If an issue pops up the usual workflow is to fix the code and mark the issue as "resolved" in Sentry. Only new or resolved issues trigger another email notification when they are proxied through Sentry.

This workflow does not fit well for issues we cannot fix, e.g. when consuming an external API that is sometimes down. In such cases you ...

Useful filtering options of git log

Git log offers useful options for filtering. This card provides a short overview.

By message

Only commits that include a specific string in their commit message

git log --grep="tracker id"

By file

Only commits that introduced changes to a specific file

git log -- foo.rb bar.rb

By content

If you want to know when a specific line of code was added in the project

git log -S"def function_name"

By range

If you want to know which commits have been added between two commits.

git log base_branch..compare_branch will s...

A non-weird replacement for grouped_collection_select

Rails comes with grouped_collection_select that appears to be useful, but isn't.

As an alternative, consider the flat_grouped_collection_select found below. It takes a third argument that extracts the group from each element in the collection:

= form.flat_grouped_collection_select :user_id, users, :department, :id, :full_name

Here is the monkey-patch:

class ActionView::Helpers::FormBuilder

  def flat_grouped_collection_selec...

Project management best practices: The story tracker

In general, the tracker should always be the definitive source of truth of what needs to be done as part of a project. If you identify a task that needs to be done, you should create a story. If you learn something that contradicts an existing story, change it.

The tracker can contain two types of stories: Developer stories, and non-developer stories.

Non-developer stories

Non-developer stories should be clearly marked. They usually belong to the PM (or maybe people from the operations team). Those story can take all forms, could just...

Reading an element's attributes with Capybara

capybara_element['attribute_name'] allows accessing an element's attributes in Capybara.

A few examples:

find('#my_element')['class']
# => "first-class second-class"

find('#my_input')['placeholder']
# => "My placeholder value"

find('a#example-link')['href']
# => "http://example.com"

find('#my_element')['missing_attribute']
# => nil

Using git patchfiles to speed up similar implementation tasks

Sometimes you'll find yourself with a set of tasks that require similar code for different models. For example, if you start working at a new application that allows CRUDing pears and apples, each commit might look similar to this:

commit 41e3adef10950b324ae09e308f632bef0dee3f87 (HEAD -> ml/add-apples-12345)
Author: Michael Leimstaedtner <michael.leimstaedtner@acme.com>
Date:   Fri Aug 11 09:42:34 2023 +0200

    Add Apples as a new fruit

diff --git a/app/models/apple.rb b/app/models/apple.rb
new file mode 100644
index 0000000..a51...

JavaScript has a native event emitter

Suppose you want to implement a publish/subscribe pattern in your Frontend application to react to data changes and events. First, you might be looking for an event emitter library.

Today I learned that vanilla JavaScript comes with a native event emitter, which I've been indirectly using forever. There's no need for extra code!

const emitter = new EventTarget()

emitter.addEventListener('YOLO', () => {
  console.log('YOLO');
})

// invoke attached event listeners
emitter.dispatchEvent(new Event('YOLO'));

Git stash: Working with old entries

First find the reference for the entry you want through looking at the stash:

$ git stash list
stash@{0}: WIP on feature/foo
stash@{1}: WIP on feature/bar
stash@{2}: WIP on fix/baz

Now you can simply use that reference, but curly braces must be escaped:

git stash pop stash@\{1\}

or quoted:

git stash apply "stash@{1}"

Quick reminder to [not shoot yourself in the foot](https://makandracards.com/makandra/634-use-the-git-stash-withou...