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

Migrating to RSpec 2 from RSpec 1

You will need to upgrade to RSpec >= 2 and rspec-rails >= 2 for Rails 3. Here are some hints to get started:

  • In RSpec 2 the executable is rspec, not spec.
  • RSpec and rspec-rails have been completely refactored internally. All RSpec classes have been renamed from Spec::Something to RSpec::Something. This also means that every require 'spec/something' must now be require 'rspec/something'.
  • In spec_helper.rb, Spec::Runner.configure becomes RSpec.configure
  • It has become really hard to extend specific example groups ...

Disabling Spring when debugging

Spring is a Rails application preloader. When debugging e.g. the rails gem, you'll be wondering why your raise, puts or debugger debugging statements have no effect. That's because Spring preloads and caches your application once and all consecutive calls to it will not see any changes in your debugged gem.

Howto

Disable spring with export DISABLE_SPRING=1 in your terminal. That will keep Spring at bay in that terminal session.

In Ruby, [you can only write environment variables that subproc...

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

How to: Solve gem loaded specs mutex

Use bundler > 1.15 to fix Gem::LOADED_SPECS_MUTEX (NameError).


Given the following project:

ruby -v
ruby 1.8.7

bundler -v
Bundler version 1.13.7

gem -v
1.8.30

rails -v
Rails 3.2.22.1

Running specs or features resulted in:

uninitialized constant Gem::LOADED_SPECS_MUTEX (NameError)

The previous settings described in Maximum version of Rubygems and Bundler for Ruby 1.8.7 and Rails 2.3 (even the rails version was rails 3.2 and not 2.3) seems not to work here, so I used (also described in the ca...

When sessions, cookies and Clearance tokens expire and how to change it

Expiration of Rails sessions

By default Rails sessions expire when the user closes her browser window.

To change this edit your config/initializers/session_store.rb like this:

ActionController::Base.session = {
  :key          => '...',
  :secret       => '...'
  :expire_after => 10.years
}

In older Railses the initializer is not available. Set the option in the environment.rb instead:

config.action_controller.session = {
  :key          => '...',
  :secret       => '...'

...

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

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

Spec "content_for" calls in helpers

This only applies to RSpec below version 1.3.2. The issue has been fixed in RSpec 1.3.2, and most likely RSpec 2 and later versions.


When you have a helper that calls content_for and want to check its behavior you should probably write a feature instead. If you still want to do it, mind the following.

Consider this helper:

module LayoutHelper
  def title(string)
    content_for :title, string
    string
  end
end

Somewhere in the layout we'd then say something like this: `<%= yield :title %...</p>

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

Force absolute URLs for parts of a view or controller

You know that you can force absolute URLs throughout a response. Now you want to modify URLs similarly, but only in parts of a view (or controller) logic. Here is how.


Note: this has only been tested on a Rails 2 application. It should work similarly for Rails 3.


Put this into your ApplicationController:

def rewrite_options(*args)
  options = super
  options.merge!(:only_path => false) if @with_full_urls
  options
end...

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

How to disable cookies in cucumber tests

Unfortunately, Capybara does not offer a switch to disable cookies in your test browser. However, you can work around that by using a tiny Rack middleware -- it works for both Selenium and non-Selenium tests.


Wouldn't it be nice to say something like this?

Given cookies are disabled
When I try to sign in
Then I should see "Can't sign you in. Please enable cookies."

You can! Put the code below into some place like lib/rack/cookie_stripper.rb.

module Rack
  class CookieStripper
    
    ENABLED = false

...

Fix warning: No secret option provided to Rack::Session::Cookie

You will get this when you are using the latest version of Rails with a recent version of Rack:

SECURITY WARNING: No secret option provided to Rack::Session::Cookie.
This poses a security threat. It is strongly recommended that you
provide a secret to prevent exploits that may be possible from crafted
cookies. This will not be supported in future versions of Rack, and
future versions will even invalidate your existing user cookies.

The warning is caused by Rails calling Rack incorrectly. [It is unclear](https://github.c...

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

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

Safely chain scopes with hash conditions

There is a nasty bug in all version of Rails 2 and some versions of Rails 3.x where two chained scopes with hash conditions on the same attribute would overwrite each other.

This is a horrible security issue if you are using scopes to limit what a user may see or change.

Workaround

If you are using an affected Rails version and cannot switch to a fixed version, you can use this manual workaround....

Gem development: When your specs don't see dependencies from your Gemfile

When you develop a gem and you have a Gemfile in your project directory, you might be surprised that your gem dependencies aren't already required in your specs. Here is some info that should help you out:

  • Bundler actually doesn't automatically require anything. You need to call Bundler.require(:default, :your_custom_group1, ...) for that. The reason why you never had to write this line is that Rails does this for you when it boots the environment.
  • That also means that if you have an embedded Rails app in your spec folder (like [h...

How to remove properties of ActiveRecord scopes

When dealing with AR scopes, you can remove conditions, order, etc by using the unscope method.

It is available on Rails 4+.


Examples

Consider an exemplary User class as follows. For the examples below, we will use a scope that applies all its constraints.

class User < ActiveRecord::Base
  scope :active, -> { where(locked: false) }
  scope :admins, -> { where(role: 'admin') }
  scope :ordered, -> { order(:name) }
end

users = User.active.admins.ordered

^
SELECT "users".* FROM "users" WHERE "use...

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

Upgrading Ruby from 1.8.7 to 2.3.5

Suggested Workflow

Set the ruby version in .ruby-version to 2.3.5, then perform these steps one by one, fixing errors as they occur:

  1. Update gems as listed below, and bundle
  2. Boot a Rails console - see below for a list of changes you will probably need
  3. Run Specs with --backtrace option
  4. Run Cucumber features (with Geordi's --debug option)
  5. When all tests are green, look through your Gemfile and remove as many version constraints as possible.
  6. Boot the application in different environements to spot further issues, e...

Make Less interpret the escape codes in a logfile

The unix command line tool less is a good choice for browsing logfiles. In the standard configuration, though, it does not interpret the escape sequences used in the rails logfiles. To enable this type:

less -R my_logfile.log

You can also have an alias to save yourself the typing

alias less='less -R'