Rails: How to find records with empty associations

Imagine these models and associations:

class Deck < ApplicationRecord
  has_many :cards
end

class Card < ApplicationRecord
  belongs_to :deck, optional: true
end

Now you want to find all Decks without any Card or all Cards without a Deck.

Rails 6.1+

Rails 6.1 introduced a handy method ActiveRecord#missing to find records without given associations.

Deck.where.missing(:cards)
SELECT "decks".*
FROM "dec...

Do not forget mailer previews

When changing code in mailers, updating the corresponding mailer preview can be forgotten very easily.

Mailer previews can be tested like other code as well and I sometimes add the following tests to test suites:

# Make sure to require the previews
Dir[Rails.root.join('spec/mailers/previews/*.rb')].each { |file| require(file) }


ActionMailer::Preview.all.index_with(&:emails).each do |preview, mails|
mails.each do |mail|
    describe preview do
      specify "##{mail} works" do
        expect { preview.call(mail...

Rails: Default HTTP status codes when redirecting

When redirecting you should take care to use the right HTTP status code.

From controllers

When redirecting from a controller, the default status code is 302 Found (aka Moved Temporarily):

red...

Show a JS fiddle in fullscreen

If you have a JS fiddle, you can open it in fullscreen by appending /show to the URL.

Example: https://jsfiddle.net/b275g910/3 => https://jsfiddle.net/b275g910/3/show

Project management best practices: Budget control

When starting a project we always make a good estimate of all known requirements, and plan budgets and available developers accordingly.

Requirements change. Budgets usually don't.

To make sure a project stays on track, we update our estimates once a month and compare them to the remaining budget. If this doesn't match any more, we have to act.

To update an estimate, do the following:

  • Start with the most recent estimate for the project.
  • Which stories have been completed? Set their estimate to zero.
  • Have any requirements cha...

IRB's multi-line autocomplete and how to disable it

Recent IRB versions include a multi-line autocomplete which may be helpful to novice users but can be distracting.

Cycling through options works by pressing the Tab key (as usual), and for some methods you also get some kind of documentation, though the quality of results is usually not on par with your IDE of choice.

I have found that it also slows down my IRB in some cases, or that pressing the Backspace key does not always reliably remove characters, which I find more annoying than useful.

You may disable multi-line autocomplete by

  • ...

makandra_sidekiq 0.2.0 respects the configured Sidekiq timeout

There was an issue with makandra_sidekiq < 0.2 concerning the stopping of Sidekiq.

Sidekiq < 6 has two finishing timeouts: one for finishing things itself (A), and one for sidekiqctl before killing a running Sidekiq instance (B). While USAGE banner of sidekiqctl advises to have B always greater than A, makandra_sidekiq < 0.2 runs sidekiqctl without passing a timeout. If the Sidekiq instance is configured with a timeout higher than the default 10s timeout of sidekiqctl, `sideki...

Capybara: Most okayest helper to download and inspect files

Testing file download links in an end-to-end test can be painful, especially with Selenium.

The attached download_helpers.rb provides a download_link method for your Capybara tests. It returns a hash describing the download's response:

details = download_link('Download report')
details[:disposition]  # => 'attachment' or 'inline'
details[:filename]     # => 'report.txt'
details[:text]         # => file content as string
details[:content_type] # => 'text/plain'

Features

Compared to [other approaches](...

Ruby: __FILE__, __dir__ and symlinks

Ruby's __FILE__ keyword returns the path to the current file. On popular for this are Ruby binaries:

#!/usr/bin/env ruby
$LOAD_PATH << File.expand_path('../../lib', __FILE__)
require 'my_cli'
MyCli.run!

However, if you create a symlink to this file, this will no longer work. __FILE__ will resolve to the path of the symlink, not to its target.

One solution is to use File.realpath(__FILE__).

In Ruby 2+ you can also use this:

$LOAD_PATH << File.expand_path('../lib', __dir__)

__dir__ is simply a shortcut for `...

Stop animations and network polling when the document tab isn't visible

Is your application doing something expensive every few seconds? Maybe an animated slider that rotates images? Maybe you are updating data over the network every five minutes?

It's a good idea to pause this if the your document tab is not even visible to the user. This saves your user's battery and data plan.

You can ask document.visibilityState whether this tab is visible:

function pulse() {
  if (!document.visibilityState || document.visibilityState...

How to quickly inspect an Angular scope in your webkit browser

Current webkit browsers like Chrome and Safari have a special variable in their consoles that refers to the selected DOM node in the elements panel. This lets us easily inspect Angular scopes.

  1. Right click in the page and click "Inspect" to open the Dev Tools

  2. Select the element you're interested in from the elements panel

  3. Focus the console (in Chrome, hit ESC)

  4. Get the scope object and store it

    s=$($0).scope()
    
    // That is:
    element = $0 // Store element
    $element = $(element) // Wrap with j...
    

net-ssh and openssl-3.0.0

You'll need openssl-3 or newer for servers running 22.04

Ruby version 3.1 uses by default the gem openssl-3.0.0. This can cause issues with the gem net-ssh (6.1.0). This is a known bug.

Typically this can cause an error while deploying an application with capistrano:

could not verify server signature (SSHKit::Runner::ExecuteError)

or

Ed25519::VerifyError: signature verification failed!

As temporary workaround add the following line to your Gemfile:

gem 'openssl', ...

RSpec: How to turn off partial double verification temporarily

While verifying doubles in RSpec is a good default, it is limited in the amount of methods it actually is able to verify.

The background is that RSpec can't verify dynamically defined methods, which is a known issue for the usage of helper_method and also the reason why [RSpec >= 3.6](http://rspec.info/blog/2017/05/rspec-3-6-has-been-rel...

Ruby tempfiles

Tempfiles get deleted automatically

With the the ruby Tempfile class you can create temporary files. Those files only stick around as long as you have a reference to those. If no more variable points to them, the GC may finalize the object at some point and the file will be removed from the filesystem. If you would try to access your tempfile then using its path (which you stored previously), you would get an error because the file no longer exists.

Unlink your tempfiles when you're done with them
-...

How to show jQuery event handler on element

Chrome gives you the currently selected element in the inspector with $0. If you select a button in the DOM you can set and inspect the event handler with the following two code lines:

$($0).on('click', function() { console.log('Hello') })
jQuery._data($0, "events").click[0].handler
// => "function () { console.log('Hello') }"

This is useful for debugging.

When upgrading/downgrading RubyGems and Bundler on a server, you must clear bundled gems

On application servers, gems are usually bundled into the project directory, at a location shared across deployments.

This is usually shared/bundle inside your project's root directory, e.g. /var/www/your-project/shared/bundle/.
If you can't find that, take a look at current/.bundle/config and look for BUNDLE_PATH.

When you are changing the version of RubyGems or Bundler on a system where gems are installed this way, you must wipe that bundle directory in addition to the user and system gems or gems that are already ins...

Rails asset pipeline: Why relative paths can work in development, but break in production

The problem

When using the asset pipeline your assets (images, javascripts, stylesheets, fonts) live in folders inside app:

app/assets/fonts
app/assets/images
app/assets/javascripts
app/assets/stylesheets

With the asset pipeline, you can use the full power of Ruby to generate assets. E.g. you can have ERB tags in your Javascript. Or you can have an ERB template which generates Haml which generates HTML. You can chain as many preprocessors as you want.

When you deploy, Rails runs assets:precompile...

How to make RubyMine aware of Cucumber steps defined in gems

If your Ruby project includes a gem like Spreewald that comes with some external step definition, RubyMine does not know about them by default and will highlight the step as an undefined reference:

Image

To link these external step definitions to RubyMine, add the corresponding gems to your RubyMine-Settings:

  • Go to Settings (ctrl + alt + s)
  • Go to Languages and Frameworks
  • Go to Cucumber
  • There, add your gem (e.g "spreewald") via the little "+" from the b...

Flexbox: How to prevent <pre> elements from overflowing

I recently had the problem that embedded code boxes crashed my layout.

It turned out that pres break out of their containers when using them inside a deeper nested flex layout.
For me it was a flex inside a flex item (fleXzibit).

Image

<div class="flex">
  <div class="flex-item">
    ...
    <div class="nested-flex">
      <div class="nested-flex-item">
        <pre>A code example</pre>
      </div>
    </div>
    ...
  </div>
</div>

The reason is that flexbox items default to `mi...

JavaScript: New Features in ES2021

tl;dr

With ES2021 you now can use str.replaceAll(), Promise.any(), logical assignment operators, numeric separators and WeakRef on all major browsers except IE11.

replaceAll

JavaScript's replace(searchValue, replaceValueOrFn) by default replaces only the first match of a given String or RegExp.
When supplying a RegExp as the searchValue argument, you can specify the g ("global") modifier, but you have to remember doing that, hence using replace when you expect global replacement is prone to errors.
When supplying st...

Use DatabaseCleaner with multiple test databases

There is a way to use multiple databases in Rails.
You may have asked yourself how you're able to keep your test databases clean, if you're running multiple databases with full read and write access at the same time. This is especially useful when migrating old/existing databases into a new(er) one.

Your database.yml may look like this:

default: &default
  adapter: postgresql
  encoding: unicode
  username: <%= ENV['DATABASE_USER'] %>
  host: <%= ENV['DATABASE...

ActiveRecord: Query Attributes

tl;dr
You can use attribute? as shorthanded version of attribute.present?, except for numeric attributes and associations.

Technical Details

attribute? is generated for all attributes and not only for boolean attributes.

These methods are using #query_attribute under the hood. For more details you can see ActiveRecord::AttributeMethods::Query.

In most circumstances query_attribute is working like attribute.present?. If your attribute is responding to :zero? then you have to be aware that `query_attri...

How to bind an event listener only once with Unpoly

You can use Unpoly's up.on with a named listener function and immediately unbind this event listener with { once: true }:

up.on('up:fragment:inserted', { once: true }, function () { ... })

In Unpoly 1 you can immediately unregister the listener with up.off:

up.on('up:fragment:inserted', function fragmentInsertedCallback() {
  up.off('up:fragment:inserted', fragmentInsertedCallback)
  // ... code for the callback function, which should run only once
})

Exam...

Gem development: recommended gem metadata

The gemspec for gems allows to add metadata to your gem, some of which have a special meaning and are helpful for users.
You can provide links to your Github bugtracker or changelog file that are then used on the rubygems page of your gem (in the sidebar, e.g. see gem page of consul).

Here are some keys that should be filled:

Gem::Specification.new do |s|
  s.name = 'my-gem'
  s.homepage = 'https://github.com/makandra/my-gem'

  s.metadata = {
    'source_code_uri' => s.homepage,
    'bug_tracker...