Traverse an ActiveRecord relation along an association

The Edge Rider gem gives your relations a method #traverse_association which
returns a new relation by "pivoting" around a named association.

Say we have a Post model and each Post belongs to an author:

class Post < ActiveRecord::Base
  belongs_to :author
end

To turn a relation of posts into a relation of its authors:

posts = Post.where(:archived => false)
authors = posts.traverse_association(:author)

You can traverse multiple associations in a single call.
E....

8 steps for fixing other people's code

Guide how to make fixes in other people's GitHub repositories. It's basically "Open Source Development 101".

Way back in mid-2007, when Rails 1.2 was the new hotness and GitHub was still a year away from crawling out of the primordial internet soup, prolific open source contributor Dr Nic wrote an article titled “8 steps for fixing other people’s code”. (...)

Here in the fantastical future world of 2012, while we still don’t have hoverboards or household nuclear fusion, we do have some great tools that make fixing other people’s code...

JavaScript Sentry: How to check if errors will be reported

One really simple way to check whether JavaScript Sentry integration was successful (raven-js or @sentry/browser), is to create an erroneous event handler like this:

<h1 onClick="throw new Error('JavaScript Sentry was successfully integrated')">
  My Website
</h1>

… and clicking on the element afterwards.

If your site has a strict CSP, see Using inline event handlers with a strict Content Security Policy (CSP).

Trigger a link's click action with Javascript

Use the click method on the DOM element:

let link = document.querySelector('a')
link.click()

Rails 6.1: where.not changes behaviour from NOR to NAND

Since Rails 6.1, if we use where.not with multiple attributes, it applies logical NAND (NOT(A) OR NOT(B)) instead of NOR (NOT(A) AND NOT(B)). If you do not take care, this change will increase the matched set.

Examples

"Don't send newsletters neither to admins nor to trashed users!" becomes "Don't send newsletters to trashed admins".

User.where.not(role: 'admin', trashed: true)
# Before Rails 6.1, with NOR
=> "SELECT "users".* FROM "users" WHERE "users"."role" != 'admin' AND "users"."trashed" != TRUE"
# Equivale...

RSpec: Define negated matcher

You can use RSpec::Matchers.define_negated_matcher to define a negated version of an existing matcher.

This is particularly useful in composed matcher expressions or to create more expressive and meaningful matchers.

Examples

RSpec::Matchers.define_negated_matcher :not_change, :change

describe 'A negated matcher' do
  it 'can be used to chain negated changes' do
    expect { subject.maybe_change(object) }
      .to not_change(object, :attr_1)
      .and not_change(contract, :attr_2)
  end
end
RSpec::...

Browsers will not send a referrer when linking from HTTPS to HTTP

  • When your site is on HTTPS and you are linking or redirecting to a HTTP site, the browser will not send a referrer.
  • This means the target site will see your traffic as "direct traffic", i.e. they cannot distinguish such hits from a user who directly typed in the URL.

Reasons for this behavior

It's probably because of this RFC:

Clients SHOULD NOT include a Referer header field in a (non-secure) HTTP request if the referring page was transferr...

Ruby: Using named groups in Regex

An alternative of using a multiple assignment for a Regex are named groups. Especially when your Regex becomes more complicates it is easier to understand and to process.

Note:

  • In case a string does not match the pattern, .match will return nil.
  • With Ruby 2.4 the result of .match can be transformed to a Hash with named_captures. This allows you to use methods like slice or fetch on the result.

Example with a mult...

CSS Grid Display allows defining number of grid columns based on child width

An element with display: grid can define its grid-template-columns based on (preferred) child width using the repeat function with auto-fill or auto-fit, like so:

grid-template-columns: repeat(auto-fit, 100px)

auto-fill and auto-fit behave differently if you use rules with dynamic sizing, like minmax(100px, 1fr). Simply put, auto-fill will create as many columns as possible, including empty ones, while auto-fit hides empty columns.

See the linked page for more details.

Defensive CSS

Table Of Contents

  • Flexbox wrapping
  • Spacing
  • Long content
  • Prevent an image from being stretched or compressed
  • Lock scroll chaining
  • CSS variable fallback
  • Using fixed width or height
    • The fixed height
    • The fixed width
  • Forgetting background-repeat
  • Vertical media queries
  • Using justify-content: space-between
  • Text over images
  • Be careful with fixed values in a CSS grid
  • Show a scrollbar only when it's needed
  • Scrollbar gutter
  • Minimum content size in CSS flexbox
  • Minimum content size in CSS grid
  • Auto fit vs auto...

Selector for finding the currently selected option in a <select> tag

Use option:checked to find the currently selected option:

select.querySelector('option:checked')

Yes, :checked, not :selected.

This is the same as finding an option with the { selected: true } property in JavaScript:

select.querySelectorAll('option').find((option) => option.selected)

What about the selected attribute?

Note that option[selected] would only find an <option selected> tag. This may be the selected option right after loading the page, but not once the user switched to a different value. ...

RSpec: How to aggregate failures

RSpec >= 3.3 added aggregate_failures, which allows multiple failures in an example and list them all, rather than aborting on the first failure.

This can be used:

  • In the global configuration
  • With the tag :aggregate_failures (our preferred option in case every expectations should be aggregated)
  • With the method aggregate_failures

[Here](https://web.archive.org/web/20210110131654/https://relishapp.com/rspec...

Jasmine: Spy on value properties

Jasmine has spyOnProperty(), but it only works if the property is implemented using getter and setter functions. This is a known limitation of Jasmine.

If the mocked property is a simple value, it will not work:

const x = { foo: 1 }
console.log(x.foo) // 1
spyOnProperty(x, 'foo').and.returnValue(2)
// Throws: Error: <spyOnProperty> : Property foo does not have access type get

Below you can find a function `spyOnValuePr...

Nokogiri: How to parse large XML files with a SAX parser

In my case [...] the catalog is an XML that contains all kinds of possible products, categories and vendors and it is updated once a month. When you read this file with the Nokogiri default (DOM) parser, it creates a tree structure with all branches and leaves. It allows you to easily navigate through it via css/xpath selectors.

The only problem is that if you read the whole file into memory, it takes a significant amount of RAM. It is really ineffective to pay for a server if you need this RAM once a month. Since I don't need to n...

New gem: Rack::SteadyETag

Rack::SteadyETag is a Rack middleware that generates the same default ETag for responses that only differ in CSRF tokens or CSP nonces.

By default Rails uses Rack::ETag to generate ETag headers by hashing the response body. In theory this would enable caching for multiple requests to the same resourc...

Spreewald 4.3.3 released

Field error steps

Spreewald's The ... field should have an error and The ... field should have the error ... steps now have built-in support for Rails and Bootstrap (v3-v5) error classes. When using Bootstrap, it is no longer necessary to overwrite the steps in your project.

At the same time, support for formtastic has been removed as there were no real use cases. Due to that, no breaking change was introduced, as the amount of users affected by this should be zero (it was neither in the documentation nor tested).

Users may now add...

Semantic HTML

Besides their default styling properties, HTML elements have a semantic meaning. For example, an h1 tag is usually styled with a larger font and bold, while it denotes "the single most important headline in its context".

While CSS enables us to style almost any HTML element like anything that is needed, choosing HTML elements corresponding to the meaning of their content has a few advantages:

  • HTML becomes a little clearer
  • Edge cases have already been considered and implemented:
    • Keyboard support (tabbing, arrow keys)
    • State...

Elasticsearch: recover from readonly mode

Elasticsearch defaults to go into readonly mode when you run low on disk space (< 95%). You might then see an error like this when you try to write to elastic:

Elasticsearch::Transport::Transport::Errors::Forbidden:
       [403] {"error":{"root_cause":[{"type":"cluster_block_exception","reason":"blocked by: [FORBIDDEN/12/index read-only / allow delete (api)];"}],"type":"cluster_block_exception","reason":"blocked by: [FORBIDDEN/12/index read-only / allow delete (api)];"},"status":403}

Even after freeing up space and restarting elas...

Events triggered by jQuery cannot be observed by native event listeners

jQuery has a function $.fn.trigger(). You can use it to dispatch an event on a jQuery object:

let $element = $('.foo')
$element.trigger('change')

A caveat is that such an event will be received by jQuery event listeners, but not by native event listeners:

let $element = $('.foo')

$element.on('change', event => console.log('I will be called'))
$element[0].addEventListener('change', event => console.log("I WON'T be called"))

$element.trigger('change')

This is not an issue when your entire app is ...

How to reload a belongs_to association

To reload a single-item association in Rails 5+, call #reload_<association>:

post.reload_author

In older Railses you can say

post.author(true)

Ruby: How to use prepend for cleaner monkey patches

Let's say you have a gem which has the following module:

module SuperClient

  def self.foo
    'Foo'
  end
  
  def bar
    'Bar'
  end

end

For reasons you need to override foo and bar.

Keep in mind: Your code quality is getting worse with with each prepend (other developers are not happy to find many library extensions). Try to avoid it if possible.

  1. Add a lib/ext/super_client.rb to your project (see How to organize monkey patches in Ruby on Rails projects)
  2. Add the extension, which ov...

ImageMagick: Converting SVG to raster image formats like PNG or JPEG

Conversion

ImageMagick can convert SVGs to raster image formats.

Example for PNG:

convert input.svg output.png

If the SVG has a size of 24x24 (viewBox="0 0 24 24"), the resulting PNG will also have a size of 24x24.

Resizing

An SVG's viewBox specifies the intended size, but vector image formats can be scaled freely.

Resize flag (poor results)

If you want your raster image to be larger, the naive approach would be to use the resize flag.

convert -resize 96x96 input.svg output.png

However, this resu...

The TCF 2.0 (Tranparency and Consent Framework) standard, and what you should know about it

The Interactive Advertising Bureau (IAB) is a European marketing association which has introduced a standard how advertising can be served to users in line with the General Data Protection Regulation (GDPR). This standard is called the TCF 2.0 (Transparency and Consent Framework). If you want to integrate any kind of advertising into a website, chances are the advertising network will require your website to implement that standard. This is a very brief overview of what this means:

The basic idea in the TCF 2.0 ...

Ad blockers: How to debug blocked elements

Some users might use Adblock Plus or similar browser plugins to reduce the number of ads displayed. If you run into an issue that your application or part of an application is blocked, this card will give you some guidance on how to debug it.


In general ad blocking is not an issue for most of our web apps. But if your application uses iframes or is embedded in another site it's more prone to it.

Blocked elements most of the time appear to the user as empty frames in the page. The indicator icon of the ad blocker also gives ...