Heads up: expect(object).to receive(:method_name) does not execute the original implementation of the method

Let's assume that we have a model Movie that registers a callback function when a new instance of Movie is created (Note: For the purpose of this card it is not important what that callback does or which type of callback it is).

This is how we test whether the callback function (here it is named :my_method) is called when a new movie is created:

expect_any_instance_of(Movie).to receive(:my_method)
create(:movie)  # <-- this is where the method :my_method should be called

You might expect that when calling `create(:mo...

How to make your git aliases work with both master and main

The linked article found a simple way to rewrite legacy git aliases to make them work with differently named default branches

  • Step 1: Decide which is the most common default branch name of your projects, e.g. master. Define it as the global init.defaultBranch git configuration :
git config --global init.defaultBranch master
  • Step 2: Overwrite the value in each project directory that uses different defaults
# cd /path/to/project, then run:
git config ...

Spreewald: patiently blocks must not change variables from the surrounding scope

I recently enjoyed debugging a Cucumber step that tried to be retryable using a patiently block:

Then /^"([^"]*)" should( not)? be selected for "([^"]*)"$/ do |value, negate, field|
  patiently do
    field = find(:label, text: field)['for'].delete_suffix('-ts-control')
    ...
  end
end

Unfortunately this block is not retryable:

  • The first attempt changes the value of field.
  • All subsequent attempts will using the changed value of field, instead of the o...

Signed URLs with Ruby on Rails

Using ActiveRecord's #signed_id and .find_signed methods you can create URLs that expire after some time. No conditionals or additional database columns required.

Finding a method name on a Ruby object

Wondering how a specific method on an object is exactly named? You can use Enumerable#grep to detect it in the array of methods.

@user.methods.grep /name/ # => [:name, :first_name, :last_name]

You can also call #private_methods or #public_methods. To find only relevant methods, it is suggested to subtract generic methods like this:

User.methods - Object.methods
User.methods - ActiveRecord::Base.methods
@user.methods - Object.instance_methods
@user.methods - ActiveRecord::Base.instance_methods

RubyMine's clipboard can hold more than one string

By pressing Ctrl + Shift + V you can select a recently copied string for pasting.

Ruby: Natural sort strings with Umlauts and other funny characters

Why string sorting sucks in vanilla Ruby

Ruby's sort method doesn't work as expected with special characters (like German umlauts):

["Schwertner", "Schöler"].sort
# => ["Schwertner", "Schöler"] # you probably expected ["Schöler", "Schwertner"]

Also numbers in strings will be sorted character by character which you probably don't want:

["1", "2", "11"].sort
# => ["1", "11", "2"] # you probably expected ["1", "2", "11"]

Also the sorting is case sensitive:

...

Bundler 2.3 honors the version specified in `BUNDLED_WITH`

Bundler so far ignored the version specified under BUNDLED_WITH in the Gemfile.lock. This had two annoying consequences:

  • If the bundler version on your system was lower than in the Gemfile.lock, you got an error message and had to manually install the correct version.
  • If the bundler version on your system was higher than in the Gemfile.lock, bundler silently updated the version in the Gemfile.lock to your system's bundler version. To avoid this, you had to always specify, which version you want to use for each bundler c...

Ruby object equality

TLDR

if you define a equality method for a class you must also implement def hash.

Ruby has a lot of methods that have to do something with equality, like ==, ===, eql?, equal?. This card should help you differentiate between those and give you hints on how to implement your own equality methods in a safe manner.

Differences between the methods

for everyday use: ==

When you compare two objects in ruby, you most often see the use of foo == bar. By default the == operator inherits from Object and is impl...

Fixing Yarn 1 error "unexpected end of file"

Our CI setup frequently sees this error while running yarn install:

yarn install v1.22.19
[1/4] Resolving packages...
[2/4] Fetching packages...
error An unexpected error occurred: "https://registry.yarnpkg.com/typescript/-/typescript-3.9.7.tgz: unexpected end of file".
info If you think this is a bug, please open a bug report with the information provided in "/builds/projects/foo-app/yarn-error.log".
info Visit https://yarnpkg.com/en/docs/cli/install for documentation about this command.

This error is caused by [Yarn not retryin...

How to read the current breakpoint tier(s) in JavaScript

To read the current breakpoint tier in JavaScript, employ this CSS:

:root {
  --current-breakpoint-tier: xs;
  
  @media (min-width: $screen-sm-min) {
    --current-breakpoint-tier: sm;
  }
  @media (min-width: $screen-md-min) {
    --current-breakpoint-tier: md;
  }
  @media (min-width: $screen-lg-min) {
    --current-breakpoint-tier: lg;
  }
  @media (min-width: $screen-xl-min) {
    --current-breakpoint-tier: xl;
  }
  @media (min-width: $screen-xxl-min) {
    --current-breakpoint-tier: xxl;
  }
}

Then use this JavaScript:

Timecop: reset after each test

Timecop is a great gem to set the current time in tests. However, it is easy to introduce flakyness to your test suite when you forget to reset the time after the test.
This might be the case if:

  • a test freezes time and a later test does not work for frozen time
  • a later test needs the real current date to work correctly

Often you only notice these kinds of errors in rare cases when tests are executed in a particular order.

A way to avoid this is by using block notation (`Timecop.travel(...) ...

How to develop designs for an enterprise customer

Usually, design development starts with drafts, sketches and prototypes. These are reviewed, refined and iterated until the final design is ready – shiny, accepted and ready for implementation. I believe this works well when you get to work with the final decider in person.

However, this approach is not successful when the customer has complex internal structures ("Enterprise"). While the drafts and iterations might be all approved by the department you're working directly with ("Fachbereich"), deciders further up the hierarchy (the CEO, po...

Rails: Passing array values to tag helpers like link_to

From at least Rails 4, the ActionView tag helper turns Array values of HTML options into a single space-separated string.
This means you can pass an array to :class options:

extra_classes = %w[one two]

= link_to 'Dashboard', root_path, class: ['btn', 'btn-primary', *extra_classes]
=> <a href="/" class="btn btn-primary one two">Dashboad</a>

= content_tag 'div', 'Hello World', class: %w[alert alert-info]
=> <div class="alert alert-info">Hello World</div>...

Self-expiring URLs with Apache

When delivering non-public uploaded files (images, documents etc), one has to decide whether and how to do authorization. The usual approaches are:

  • Using send_file with a regular controller. This is secure, but potentially slow, especially for large collections of images.
  • Using unguessable URLs. This is fast (because Apache can deliver assets without going through Rails), but less secure.

When going with the "unguessable URL" approach, it is possible to somewhat increase security by using expiring URLs. The idea is to encode the expi...

Carrierwave: always limit images to a reasonable size

Today's cameras create huge images, some beyond 50MB. Unless you need to offer this large files, you should always shrink uploaded files to a reasonable resolution.

class ImageUploader < CarrierWave::Uploader::Base
  
  process resize_to_limit: [3000, 3000]
  
  # ...

end

Heads up: JavaScript does not like big numbers

In a JavaScript console, type this:

> 9112347935156469760
9112347935156470000

Ooops. And that's not a float!

This occurs because JavaScript uses double precision floats to store numbers.

So according to IEEE floating point definition only numbers between -(2^53 - 1) (-9007199254740991) and 2^53 - 1 (9007199254740991) can safely be represented in JavaScript.

Note that ECMAScript 6 will probably also offer [Number.MAX_SAFE_INTEGER](https://developer.mozilla.org/en-US/docs/W...

How to evaluate CSS media queries in JavaScript

To make CSS rules dependent on the screen size, we use media queries:

@media (max-width: 500px) {
  // rules for screen widths of 500px or smaller
}

Browsers will automatically enable and disable the conditional rules as the screen width changes.

To detect responsive breakpoints from JavaScript, you may use the global matchMedia() function. It is supported in all brow...

Don't mix Array#join and String#html_safe

You cannot use Array#join on an array of strings where some strings are html_safe and others are not. The result will be an unsafe string and will thus be escaped when rendered in a view:

unsafe_string = '<span>foo</span>'
safe_string = '<span>bar</span>'.html_safe
[unsafe_string, safe_string].join(' ') # will incorrectly render as '&lt;span&gt;foo&lt;/span&gt;&lt;span&t;bar&lt;/span&gt;'

Bad

The solution is not to call html_safe on the joined array and if you thought it would be, you [don't understand how XSS prot...

RSpec: Expecting non-primitive objects as method invocation arguments

Expecting a primitive value as an argument to a method invocation is easy:

expect(object).to receive(:foo).with('arg1', 'arg2')

This expectation would be met with this call:

object.foo('arg1', 'arg2')

But what if the argument is expected to be an object with a given set of methods? E.g. this class with #first_name and #last_name methods:

class Person

  def initialize(first_name, last_name)
    @first_name = first_name
    @last_name = last_name
  end
  
  attr_reader :first_name, :last_name
  
end
``...

Rails: Including HTML in your i18n locales

TL;DR

Append your locale keys with _html to have them marked as html_safe and translate them with = t('.text_html').

When you're localizing a Rails application, sometimes there is this urge to include a little HTML. Be it some localized link, or a set of <em> tags, you'd like to have it included in the locale file. Example:

# Locale file
en:
  page:
    text: 'Please visit our <a href="https://www.corporate.com/en">corporate website</a> to learn more about <em>the corporation</em>.'
    
# HAML
= t('.text')
    
# D...

How to create giant memory leaks in AngularJS (and other client-side JavaScript)

This guide shows how to create an AngularJS application that consumes more and more memory until, eventually, the browser process crashes on your users.

Although this guide has been written for Angular 1 originally, most of the advice is relevant for all client-side JavaScript code.

How to observe memory consumption

To inspect the amount of memory consumed by your Javascripts in Chrome:

  • Open an incognito window
  • Open the page you want to inspect
  • Press Shift + ESC to see a list of Chrome processes...

Jasmine: Mocking ESM imports

In a Jasmine spec you want to spy on a function that is imported by the code under test. This card explores various methods to achieve this.

Example

We are going to use the same example to demonstrate the different approaches of mocking an imported function.

We have a module 'lib' that exports a function hello():

// lib.js

function hello() {
  console.log("hi world")
}

export hello

We have a second module 'client' that exports a function helloTwice(). All this does is call hello() ...

Heads up: RSpec-Mocks' #stub_const will define intermediate modules that have not been loaded yet

The issue: You are using stub_const to change a constant value for your test.

stub_const "SomeClass::CONST", 'test'

All of a sudden, tests fail with undefined method 'some_method' for #<SomeClass:0x00000000101433a8>.

The reason

When using stub_const before the Class containing the constant has been loaded, a module is automatically created with the name.

Since RSpec does no autoloading, it will create a SomeClass module by itself. This is arguably a good idea.

As a workaround, use stub_const in your Rails specs li...