Using the Ruby block shortcut with arguments

Ruby has this handy block shortcut map(&:to_i) for map { |x| x.to_i }. However, it is limited to argument-less method invocations.

To call a method with an argument, you usually need to use the full block form. A common and annoying case is retrieving values from a list of hashes (imagine using a JSON API):

users = [ { name: 'Dominik', color: 'blue' }, { name: 'Stefan', color: 'red'} ]
names = users.collect do |user|
  user[:name]
end

If you're using Rails 5+, this example is covered by Enumerable#pluck (`users.pluck(:name)...

How to capture changes in after_commit

Your after_commit callbacks will not know about changes, as Rails discards them when committing.

The linked article shows a clever trick to work around that: It uses an after_save method that looks at changes and writes its decision to an instance variable. That instance variable can then be used in the after_commit method.

Note that while callbacks like after_save are not affected, there are valid reasons for using only after_commit, and not after_save. Enqueueing a Sidekiq job is just one of them.

Rails 5+

You can use ...

Checking database size by row count

As an application exists, data accumulates. While you'll be loosely monitoring the main models' record count, some supportive database tables may grow unnoticed.

To get a quick overview of database table sizes, you can view the row count like this:

PostgreSQL

SELECT schemaname,relname,n_live_tup 
FROM pg_stat_user_tables 
ORDER BY n_live_tup DESC
LIMIT 12;

 schemaname |                    relname                     | n_live_tup 
------------+------------------------------------------------+------------
 public     | images...

Upgrade from Ruby 1.8.7 to Ruby 1.9.2 on Ubuntu

Note that you cannot currently use Ruby 1.9.2 with Rails 2 applications that use RSpec, so don't upgrade if that is your setup. The rspec-rails gem has a fatal bug that was only fixed for rspec-rails-2.x, which only supports Rails 3. There is no fix for the rspec-rails-1.3.x series of the gem which supports Rails 2.

Anyway, here are upgrade instructions if you only work with Rails 3 or don't use RSpec. You will lose all your gems in the process, but you can get them back easily if you h...

Ruby: Removing leading whitespace from HEREDOCs

If you're on Ruby 2.3+ there's a <<~ operator to automatically unindent HEREDOCs:

str = <<~MESSAGE
  Hello Universe!
  This is me.
             Bye!
MESSAGE

If you have an older Ruby, you can use the String#strip_heredoc method from ActiveSupport. See Summarizing heredoc in ruby and rails for an example.

Technically...

A non-weird replacement for grouped_collection_select

Rails comes with grouped_collection_select that appears to be useful, but isn't.

As an alternative, consider the flat_grouped_collection_select found below. It takes a third argument that extracts the group from each element in the collection:

= form.flat_grouped_collection_select :user_id, users, :department, :id, :full_name

Here is the monkey-patch:

class ActionView::Helpers::FormBuilder

  def flat_grouped_collection_selec...

Always show all form errors during development

You've been there: A form cannot be submitted, but you don't see a validation error because the field at fault has no corresponding input field on the form. Because this is usually a bug, you insert debug information listing all errors into the form view. And once the bug is fixed, you forget to take out that debug information.

There is a better way. By copying one of the attached initializers into config/initializers, your forms will always render a small box listing all form errors in the bottom right corner of the screen. This box is n...

A saner alternative to SimpleForm's :grouped_select input type

SimpleForm is a great approach to simplifying your forms, and it comes with lots of well-defined input types. However, the :grouped_select type seems to be overly complicated for most use cases.

Example

Consider this example, from the documentation:

form.input :country_id, collection: @continents,
  as: :grouped_select, group_method: :countries

While that looks easy enough at a first glance, look closer. The example passes @continents for a country_id.\
SimpleForm actua...

How to migrate CoffeeScript files from Sprockets to Webpack(er)

If you migrate a Rails application from Sprockets to Webpack(er), you can either transpile your CoffeeScript files to JavaScript or integrate a CoffeeScript compiler to your new process. This checklist can be used to achieve the latter.

  1. If you need to continue exposing your CoffeeScript classes to the global namespace, define them on window directly:
-class @User
+class window.User
  1. Replace Sprocket's require statement with Webpacker's...

Threads and processes in a Capybara/Selenium session

TLDR: This card explains which threads and processes interact with each other when you run a Selenium test with Capybara. This will help you understand "impossible" behavior of your tests.


When you run a Rack::Test (non-Javascript) test with Capybara, there is a single process in play. It runs both your test script and the server responding to the user interactions scripted by your test.

A Selenium (Javascript) test has a lot more moving parts:

  1. One process runs your test script. This is the process you...

Capistrano: Speeding up asset compile during deploy

Remember How to skip Sprockets asset compile during Capistrano deployment and Automatically skipping asset compilation when assets have not changed? Turns out there is an even better way to speed up Capistrano deployments with asset compilation – and it's even simpler.

Adding the asset cache directory to symlinked directories

Popular asset managers for Rails are Sprockets and Webpacker. Both keep a cache of already compiled files that we're going to leverage for deployments now.

  • Sprockets cache...

Solving "cannot remove Object::ClassMethods"

Most likely you run rake and your code is causing an exception which is not the one shown in your terminal.

Rails tries to catch this exception and clean up constants but -- while it's still booting up -- fails on this which causes another exception:

rake aborted!
cannot remove Object::ClassMethods

Running rake with the --trace parameter will give you no love; the backtrace is useless in most cases.

Try these approaches:

First: Check if there is a helpful error message

  • Ha...

Make "rake notes" learn about Haml, Sass, CoffeeScript, and other file types

Rails comes with a Rake task notes that shows code comments that start with "TODO", "FIXME", or "OPTIMIZE".

While it's generally not good practice to leave them in your code (your work is not done until it's done), in larger projects you will occasionally have to use them as other parts of the application that you depend upon are not yet available.
To keep track of them, run rake notes. Its output looks something like this:

$ rake notes
app/controllers/fron...

Geordi 1.0 released

Geordi 1.0 features a command line application geordi, that holds most of Geordi's previous commands.

New features

  • command help and usage examples right within geordi (geordi help and geordi help <command>)

  • quick command access: type just the first few letters of a command, e.g. geordi rs or geordi dev[server]

  • command dependencies, e.g. geordi rspec invokes geordi bundle-install (which bundles only if needed)

  • no cluttered /usr/bin, but all commands in one handy tool

  • template for easily adding new...

Carrierwave: Using a nested directory structure for file system performance

When storing files for lots of records in the server's file system, Carrierwave's default store_dir approach may cause issues, because some directories will hold too many entries.

The default storage directory from the Carrierwave templates looks like so:

class ExampleUploader < CarrierWave::Uploader::Base
  def store_dir
    "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
  end
end

If you store files for 500k records, that store_dir's parent directory will have 500k sub-directories which will cause some...

Freeze (vendor, unpack) a single Ruby gem with and without Bundler

When you need to patch an existing gem, one way is to "vendor" the gem by copying it into the vendor/gems directory of your Rails project. You can then make any changes you require and Rails will use the vendored version of the gem after a server restart. Unfortunately you need to perform some additional steps to marry Rails and the copied gem. This notes describes what to do.

With Bundler

This is super-painful. If you just copy the gem to vendor/gems, Rails will complain:

Unpacked gem foolib in vendor/gems has no s...

Using RSpec stubs and mocks in Cucumber

By default, Cucumber uses mocha. This note shows to use RSpec stubs and mocks instead.

Rspec 1 / Rails 2

Put the following into your env.rb:

require 'spec/stubs/cucumber'

Rspec 2 / Rails 3

Put the following into your env.rb:

require 'cucumber/rspec/doubles'

Note: Since Cucumber 4 it is important to require these lines in the env.rb and not any other file in support/* to register the hooks after any other After hook in support/*. Otherwise your doubles are removed, while other After steps requi...

Always store your Paperclip attachments in a separate folder per environment

tl;dr: Always have your attachment path start with :rails_root/storage/#{Rails.env}#{ENV['RAILS_TEST_NUMBER']}/.


The directory where you save your Paperclip attachments should not look like this:

storage/photos/1/...
storage/photos/2/...
storage/photos/3/...
storage/attachments/1/...
storage/attachments/2/...

The problem with this is that multiple environments (at least development and test) will share the same directory structure. This will cause you pain eventually. Files will get overwritten and...

PostgreSQL: How to show table sizes

When you have a large PG database, you may want to find out which tables are consuming the most disk space.
You can easily check this using the following SQL statement from the PostgreSQL wiki.

SELECT nspname || '.' || relname AS "relation",
    pg_size_pretty(pg_total_relation_size(C.oid)) AS "total_size"
  FROM pg_class C
  LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
  WHERE nspname NOT IN ('pg_catalog', 'information_schema')
    AND C.relkind <> 'i'
    AND nspname !~ '^pg_toast'
  ORDER BY pg_tot...

How to set up SMTP email delivery with a Gmail account

If you want to make your Rails application be capable of sending SMTP emails, check out the action mailer configuration section in the Ruby on Rails guide.

TL;DR you will end up having an smtp_settings hash that looks something like this:

smtp_settings = {
  address: ...,
  domain: ...,
  port: ...,
  user_name: ...,
  password: ...,
  authentication: ...,
  tls: ...,
  enable_starttls_auto: ...,
}

This hash can be set as the `delivery_me...

makandra/gemika: Helpers for testing Ruby gems

We have released a new library Gemika to help test a gem against multiple versions of Ruby, gem dependencies and database types.

Here's what Gemika can give your test's development setup (all features are opt-in):

  • Test one codebase against multiple sets of gem dependency sets (e.g. Rails 4.2, Rails 5.0).
  • Test one codebase against multiple Ruby versions (e.g. Ruby 2.1.8, Ruby 2.3.1).
  • Test one codebase against multiple database types (currently MySQL or PostgreSQL).
  • Compute a matrix of all possib...

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

Sprites with Compass

Using CSS sprites for background images is a technique for optimizing page load time by combining smaller images into a larger image sprite.

There are ongoing arguments on how useful this still is, as modern browsers become more comfortable to load images in parallel. However, many major websites still use them, for example amazon, [facebook](...

Fun with Ruby: Returning in blocks "overwrites" outside return values

In a nutshell: return statements inside blocks cause a method's return value to change. This is by design (and probably not even new to you, see below) -- but can be a problem, for example for the capture method of Rails.


Consider these methods:

def stuff
  puts 'yielding...'
  yield
  puts 'yielded.'
  true
end

We can call our stuff method with a block to yield. It works like t...