Mailcatcher: An alternative to inaction_mailer

Looks simpler than inaction_mailer:

gem install mailcatcher
mailcatcher

Setup Rails to send mails to 127.0.0.1:1025. Usually you want the following config in config/environments/development.rb and maybe in test.rb or cucumber.rb.

config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
  :address => 'localhost',
  :port => 1025
}

Now you can see sent mails in your browser when opening http://127.0.0.1:1080

Note: In order to s...

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

Strict Loading Associations can prevent n+1 queries

Rails 6.1 has a "strict loading" mode that forces the developer to preload any association they plan to use. Associations no longer load lazily. An error is raised when reading an association that was not preloaded.

Enabling strict loading is a tool to prevent n+1 queries.

Strict loading can be enabled for individual records, for a single association,...

Geordi 10.0.0 released

10.0.0 2024-03-07

Compatible changes

  • console command: You can now globally disable the IRB multiline feature by setting irb_flags: --nomultiline in ~/.config/geordi/global.yml. All configured irb_flags are automatically passed on to the console IRB.
  • console command: Ctrl + C now properly exits a local Rails console
  • rspec and cucumber commands: Run specs even if the automatic chromedriver update fails
  • Improve detection of IRB version
  • Add new hints to 'Did you know'

Breaking changes

  • dump command: Drop...

Webpacker: Configuring browser compatibility

Webpacker uses Babel and Webpack to transpile modern JavaScript down to EcmaScript 5. Depending on what browser a project needs to support, the final Webpack output needs to be different. E.g. when we need to support IE11 we can rely on fewer JavaScript features. Hence our output will be more verbose than when we only need support modern browsers.

Rails 5.1+ projects often use Webpacker to preconfigure the Webpack pipeline for us. The default configuration works something like this:

  1. Webpack checks w...

Integrating or upgrading makandra-rubocop

Introduction

Most of the time it is a tedious task to apply a code style guide to an existing code base as there are likely to be a lot of conflicts. At makandra we are using makandra-rubocop to have code style checks. Here is some advice on how to add makandra-rubocop efficiently.

Note

RubyMine by default has a Rubocop inspection with rules that we don't always agree with. We recommend replacing this with makandra-rubocop or disabling the inspection.
...

Five years of "Today I Learned" from Josh Branchaud

The linked GitHub repository is a bit like our "dev" cards deck, but groomed from a single person (Josh Branchaud). It includes an extensive list of over 900 TILs on many topics that might be interesting for most of us. (e.g. Ruby, Rails, Git, Unix..)

Ruby

Here is an excerpt of all the Ruby TILs that were new to me. I encourage you to take your time to skim over the original list as well!

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

Mock the browser time or time zone in Selenium features

In Selenium features the server and client are running in separate processes. Therefore, when mocking time with a tool like Timecop, the browser controlled by Selenium will still see the unmocked system time.

timemachine.js allows you to mock the client's time by monkey-patching into Javascript core classes. We use timemachine.js in combination with the Timecop gem to synchronize the local browser time to the ...

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

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

Chromedriver: Connect local chromedriver with docker

Debugging your integration tests, that run a headless Chrome inside a docker image, is tricky.

In many cases you can connect your Chrome to a remote docker container like docker-selenium, which should be the preferred way when you try to inspect a page within your integration test.

Otherwise you might be able to start your docker container with --net=host and access your local chromedriver in the host address space host.docker.internal.

If both options above don't work for you here is a...

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

Install RubyMine under Ubuntu

This card explains how to install RubyMine for the first time. If you want to upgrade an existing RubyMine installation (after legacy install) to a newer version, see How to upgrade RubyMine.


Option A (new way)

Ubuntu 16.04 comes with snap, a way to package software with all its dependencies. RubyMine is also packaged as a snap.

A snap will always track a channel (like stable, beta) and automatically update to the newest version available in this channel. By default the snap daemon will check for ...

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

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

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

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

Speed up JSON generation with oj

Using this gem I could get JSON generation from a large, nested Ruby hash down from 200ms to 2ms.

Its behavior differs from the default JSON.dump or to_json behavior in that it serializes Ruby symbols as ":symbol", and that it doesn't like an ActiveSupport::HasWithIndifferentAccess.

There are also some issues if you are on Rails < 4.1 and want it to replace #to_json (but you can always just call Oj.dump explicitely).

Security warning: Oj does not escape HTML entities in JSON
---------...

Dynamic conditions for belongs_to, has_many and has_one associations

Note: Consider not doing this. Use form models or vanilla methods instead.


The :conditions option for Rails associations cannot take a lambda. This makes it hard to define conditions that must be evaluated at runtime, e.g. if the condition refers to the current date or other attributes.

A hack to fix this is to use faux string interpolation in a single-quoted :conditions string:

class User < ActiveRecord::Base
  has_many :contracts
  has_one :current_contract, :class_name => 'Contract', :conditions => '...

How to recognize CVE-2019-5418

If you get requests with values for formats like this:

{:locale=>[:de], :formats=>["../../../../../../../../../../etc/services{{"], :variants=>[], :handlers=>[:erb, :builder, :raw, :ruby, :coffee, :haml]}

or fails like this:

Invalid query parameters: invalid %-encoding (../../../../../../../../../etc/passwd%%0000.html)

Someone tries to exploit CVE-2019-5418.
If you use the latest Rails (or latest Rails LTS) you're...