Updated: Documenting your Rails project's Node.js version in .nvmrc

capistrano-opscomplete now supports aliases like lts/gallium.

Consul 1.3.0 lets you override generated controller methods

When you use the :as option to map a power to a controller method you can now override the generated method. The original implementation can be accessed with super.

This is useful to chain additional conditions to a scope:

class NotesController < ApplicationController

  power :notes, as: :note_scope

  # ...
  
  private
  
  def note_scope
    super.where(trashed: false)
  end

end

Dynamic super-overridable methods in Ruby – The Pug Automatic

How a macro can dynamically define a method that can be overridden with super in the same class.

Fixing wall of net/protocol warnings

After upgrading to Rails 6.1.7.2 one of our apps printed a wall of warnings while booting:

/var/www/app/shared/bundle/ruby/2.6.0/gems/net-protocol-0.2.1/lib/net/protocol.rb:68: warning: already initialized constant Net::ProtocRetryError
/home/deploy-app/.rbenv/versions/2.6.10/lib/ruby/2.6.0/net/protocol.rb:66: warning: previous definition of ProtocRetryError was here
/var/www/app/shared/bundle/ruby/2.6.0/gems/net-protocol-0.2.1/lib/net/protocol.rb:214: warning: already initialized constant Net::BufferedIO::BUFSIZE
/home/deploy-app/.rben...

Defining class methods with Modularity traits

There are two ways to define a class method from a Modularity trait. Note that the usual caveats regarding class method visibility apply.

Using define_method

The recommended way is to define a method on your module's singleton class:

module SomeTrait
  as_trait do
    define_singleton_method :foo do
      # ...
    end
  end
end

Using def (has...

Do not rescue inline in Ruby

When you are calling a method that may raise an exception that you don't care about, you might think of doing something like this:

@user = User.power_find(something) rescue User.new

Do not do that! You will be rescuing away StandardError and all its subclasses, like NameError -- meaning that even a typo in your code won't raise an error.

Instead, rescue the exception type that you are expecting:

@user = begin
  User.power_find(something)...

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

Updated: Installing Node.js / npm under Ubuntu with nvm (with yarn)

Updated the way to install yarn to current recommendations.

Updated: Careful when using Time objects for generating ETags

Added a patch that I've been using for 4 months now without any issues. It allows not remembering all of this.

Updated: Testing ActiveRecord validations with RSpec

Added the section "Different ways of testing errors", just for a quick overview of common ways to look at ActiveRecord errors in RSpec.

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

["a", "B"].sort
# => ["B", "...

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

Updated: Raising JavaScript errors in Ruby E2E tests (RSpec, Cucumber)

I rewrote everything:

  • Helper is now packaged in a BrowserConsole class
  • Now also works for RSpec feature specs
  • Cucumber: Errors now also show up with the default progress runner
  • Improved test performance by only checking the browser console after a user interaction (instead of after every step). In an example project I measured 20% fewer checks.
  • Cleaner API to ignore errors
  • Cleaned up default ignore rules

Updated: How to evaluate CSS media queries in JavaScript

Added instructions on how to read the current breakpoint tier (e.g. "xs") in Javascript.

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

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