Updated: Unpoly + Nested attributes in Rails: A short overview of different approaches

With the new unpoly client side templates (available since 3.10) there's another way to substitute the ids of inserted nested attribute forms which I added to this card.

Changes to positional and keyword args in Ruby 3.0

Ruby 3.0 introduced a breaking change in how it treats keyword arguments.

There is an excellent blog post on the official Ruby blog going into the details. You should probably read that, but here is a slightly abbreviated version:

What changed

When the last argument of a method call is a Hash, Ruby < 3 automatically converted it to to Keyword Arguments. If you call a method in Ruby >= 3 that accepts keyword arguments, eithe...

CarrierWave: How to remove GIF animation

When accepting GIF images, you will also accept animated GIFs. Resizing them can be a time-consuming task and will block a Rails worker until the image is processed.

Save yourself that trouble, and simply tell ImageMagick to drop any frames but the first one.

Add the following to your uploader class:

process :remove_animation

private

def remove_animation
  if content_type == 'image/gif'
    manipulate! { |image| image.collapse! }
  end
end

You may also define that process for specific versions only (e.g. only for thum...

Javascript: Comparing two arrays for equality

If your project uses Lodash or Underscore.js, you can use _.isEqual():

_.isEqual([1, 2], [2, 3]) // => false
_.isEqual([1, 2], [1, 2]) // => true

If your project already uses Unpoly you may also use up.util.isEqual() in the same way:

up.util.isEqual([1, 2], [2, 3]) // => false
up.util.isEqual([1, 2], [1, 2]) // => true

To compare two arrays for equality in a Jasmine spec assertion, see [Jasmine: Testing complex types for equality](/makand...

How to apply transparency to any color with pure CSS

To apply transparency to an element, you can use opacity in CSS. However, sometimes you don't want to make the entire element transparent, only a color.
Using not-so-modern CSS, you can simply generate non-opaque versions of a color. This card describes how to do that.

Note that we're not talking about defining colors with transparency. This is about the case where you have a color but need a more transparent variant of it (e.g. for a border, background, etc.), and where your component does that without defining the transparent variant ...

How to invert currentColor with pure CSS

The currentColor CSS keyword references the current text color and can be used to apply an element's text color to other properties.
Using modern CSS, you can also use it to calculate colors from it. This card describes how to invert currentColor, but you can use this technique for other calculations as well.

How to invert currentColor

They key is CSS' hsl() function and from keyword for it:

--inverted-color: hsl(from ...

Selecting the nth element matching a selector

The :nth-child pseudo class is commonly used for targeting elements based on their position within a parent container, for example the third element in a list or every even-numbered item. However, :nth-child can do more.

In modern CSS specifications (Level 4), there’s an additional feature that lets you use :nth-child in combination with a list of css selectors. This way, you can ta...

The developer console can do more than you think!

You can do so much more than console.log(...)! See the attached link for a great breakdown of what the developer console can give you.

Some of my favorites:

console.log takes many arguments

E.g. console.log("Current string:", string, "Current number:", 12)

Your output can have hyperlinks to Javascript objects

E.g. console.log("Check out the current %o, it's great", location)

[Di...

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

Capybara: Preventing server errors from failing your test

When your Rails application server raises error, Capybara will fail your test when it clears the session after the last step. The effect is a test that passes all steps, but fails anyway.

Capybara's behavior will help you to detect and fix errors in your application code. However, sometimes your application will explode with an error outside your control. Two examples:

  • A JavaScript library references a source map in its build, but forgets to package the source map
  • CarrierWave cleans up an upload or cache file after the record was delet...

Fuzzy scoping in Rails with PostgreSQL

When you want to filter records in a model where a string column roughly matches a given term, you can use PostgreSQL’s trigram similarity search.

Writing a fuzzy query in Rails

User.where("similarity(name, ?) > 0.3", "John")

This finds all users where the name is similar to "John" with a similarity score above 0.3.

You can tune the threshold:

  • Closer to 1.0 = stricter match
  • Closer to 0.0 = looser match

Ordering by best match

User
  .where("similarity(name, ?) > 0.3", "John")
  .order(Arel.sql("similarity(n...

Git Rebase: How to squash/fixup/edit/... commits without actually rebasing (keeping the base)

Purpose:
Interactively rebase your current branch onto main, keeping the original base commit (i.e. not rebasing onto main directly).

Use Case:
Useful when you've branched off a long-lived feature branch and want to clean up your commits without changing the original base. Afterwards you will be able to rebase one clean commit interactively onto main without going through each commit individually using git rebase -i main.

What it does:

  • Opens an interactive rebase UI to choose squash/edit/fixup for each co...

Prevent unnecessary automated back-and-forth when sending noreply-emails

When you send automated emails from a noreply@-address, and the recipient has an out of office enabled, the autoreply bounces and they get an additional email with an error message.

To prevent the recipient's auto-response and the following error message you can set the email header Auto-Submitted: auto-generated in your mailer, for example like this:

class NoReplyMailer < ApplicationMailer
  default from: 'noreply@yourapp.com', 'Auto-Submitted' => 'auto-generated'
end  

Sidekiq: How to check the maximum client Redis database size

You can check the maximum client Redis database size in Sidekiq with this command.

Sidekiq.redis { |redis| puts redis.info.fetch('maxmemory_human') }
#=> 512.00M

If you just want the maximum database size for a known Redis database URL you can use the Redis Ruby client or the Redis CLI:

Redis database size via Ruby client

irb(main):002> Redis.new(url: 'redis://localhost:16380/1').info.fetch('maxmemory_human')
=> "512.00M"

Redis database size via CLI

$ redis-c...

How to fill in multiple lines in a textarea with cucumber

If you want to fill in textareas with multiple lines of text (containing line breaks / new lines) you can use Cucumber's docstrings:

And I fill in "Comment" with:
  """
  This is a long comment.
  With multiple lines.

  And paragraphs.
  """

The step definition is part of the spreewald gem

How to use a local gem in your Gemfile

You can use local copies of gems in your Gemfile like this:

gem 'spreewald', path: '~/gems/spreewald'

As soon as you have bundled your project with the local copy of the gem, all code changes in the copy will be available on your project. So you can for example set a debugger or add console output in the gem and use it from your project.
If you checked out the gem with your versioning tool, you can easily reset your changes afterwards or make a pull request for the gem if you improved it.

Don't commit a Gemfile with local path...

Escape a string for transportation in a URL

To safely transport an arbitrary string within a URL, you need to percent-encode characters that have a particular meaning in URLs, like & or =.

If you are using Rails URL helpers like movies_path(:query => ARBITRARY_STRING_HERE), Rails will take care of the encoding for you. If you are building URLs manually, you need to follow this guide.

Ruby

In Ruby, use CGI.escape:

# ✅ good
CGI.escape('foo=foo&bar=bar')
=> "foo%3Dfoo%26bar%3Dbar"

Do not ever use `URI.en...

Testing Accessibility using Orca

Orca is a Linux screen reader. Since it is part of the GNOME project it should come preinstalled with Ubuntu installations.
To turn on the screen reader you can either go to Settings > Accessibility and then activate Screen Reader in the "Seeing" section or you can simply type orca in your terminal.

Note

It may feel quite strange in the beginning to use a screen reader. It is constantly commenting on everything you do and every application you visit will be read aloud.

Once you started your screen reader you can simply navigate to w...

Rails: Configuring the default sorting behaviour

In Rails, the implicit_order_column (added in Rails 6) is a configuration option that helps you define the default sorting behavior of ActiveRecord queries when no explicit ORDER BY clause is provided. This option allows you to specify a column that Rails will use to automatically sort records in a particular order when no specific ordering is given.

Since the id is typically the primary key and automatically indexed, Rails will default t...

Efficiently add an event listener to many elements

When you need to add a event listener to hundreds of elements, this might slow down the browser.

An alternative is to register an event listener at the root of the DOM tree (document). Then wait for events to bubble up and check whether the triggering element (event.target) matches the selector before you run your callback.

This technique is called event delegation.

Performance considerations

Because you only register a single listener, registering is ...

How to grep through the DOM using the Capybara API

When your Cucumber feature needs to browse the page HTML, and you are not sure how to express your query as a clever CSS or XPath expression, there is another way: You can use all and find to grep through the DOM and then perform your search in plain Ruby.

Here is an example for this technique:

Then /^I should see an image with the file...

Capybara: Accessing the parent of an element

If you already selected an element and want to get its parent, you can call find(:xpath, '..') on it.

To get the grand-parent element, call find(:xpath, '../..').

Example

Find a link which contains a twitter icon and check that it links to the correct page:

<a href="http://twitter.com/">
  <i class="icon is-twitter"></i>
</a>
link = page.find("a .icon.is-twitter").find(:xpath, '..')
expect(link[:href]).to eq("http://twitter.com/")

About XPath

There is a good overview on XPath syntax on [w3schools](htt...

How to set Chrome's time zone in Selenium tests

For Selenium tests, your browser starts in your local timezone, or whatever your system's environment specifies.
This is usually good enough. To test any timezone-dependent behavior in Chrome, you can change the time zone using the Chrome DevTools Protocol.

Example

Chrome accepts any IANA time zone name, like "Europe/Berlin" or "Asia/Tokyo".
Here is the relevant command for Capybara:

page.driver.browser.execute_cdp('Emulation.setTimezoneOverride', timezoneId: 'Asia/Tokyo')
``...