There is no real performance difference between "def" and "define_method"

You can define methods using def or define_method. In the real world, there is no performance difference.

define_method is most often used in metaprogramming, like so:

define_method :"#{attribute_name}_for_realsies?" do
  do_things
end

Methods defined via define_method are usually believed to have worse performance than those defined via def.
Hence, developers sometimes prefer using class_eval to define methods using def, like this:

class_eval "def #{attribute_name}_for_realsies?; do_things; end"

You can be...

has_one association may silently drop associated record when it is invalid

This is quite an edge case, and appears like a bug in Rails (4.2.6) to me.

Update: This is now documented on Edgeguides Ruby on Rails:

If you set the :validate option to true, then associated objects will be validated whenever you save this object. By default, this is false: associated objects will not be validated when this object is saved.

Setup

# post.rb
class Post < ActiveRecord::Base
  has_one :attachment
end
# attachm...

Ruby's default encodings can be unexpected

Note: This applies to plain Ruby scripts, Rails does not have this issue.

When you work with Ruby strings, those strings will get some default encoding, depending on how they are created. Most strings get the encoding Encoding.default_internal or UTF-8, if no encoding is set. This is the default and just fine.

However, some strings will instead get Encoding.default_external, notably

  • the string inside a StringIO.new
  • some strings created via CSV
  • files read from disk
  • strings read from an IRB

Encoding.default_external d...

Ruby 2.3 new features

Ruby 2.3.0 has been around since end of 2015. It brings some pretty nice new features! Make sure to read the linked post with its many examples!

Hash#fetch_values

Similar to Hash#fetch, but for multiple values. Raises KeyError when a key is missing.

attrs = User.last.attributes
attrs.fetch_values :name, :email

Hash#to_proc

Turns a Hash into a Proc that returns the corresponding value when called with a key. May be useful with enumerators like #map:

attrs.to_proc.call(:name)
attrs.keys.grep(/name/).map &attrs...

Download Ruby gems without installing

You can download .gem files using gem fetch:

gem fetch activesupport consul

This will produce files like active-support-5.0.0.gem and consul-0.12.1.gem in your working directory.

Dependencies will not be downloaded.

Ruby 2.3.0 has a safe navigation operator

As announced before, Ruby has introduced a safe navigation operator with version 2.3.0. receiver&.method prevents NoMethodErrors by intercepting method invocations on nil.

user = User.last
user&.name # => "Dominik"
# When there is no user, i.e. user is nil:
user&.name # => nil

This might remind you of andand, and indeed it behaves very similar. The only difference is in handling of `fa...

Ruby 2.3 brings Array#dig and Hash#dig

#dig lets you easily traverse nested hashes, arrays, or even a mix of them. It returns nil if any intermediate value is missing.

x = {
  foo: {
    bar: [ 'a', { baz: 'x' } ]
  }
}

x.dig(:foo, :bar) # => [ 'a', { baz: 'x' } ]
x.dig(:foo, :bar, 1, :baz) # => "x"
x.dig(:foo, :wronk, 1, :baz) # => nil

There is a tiny gem that backports this.

Testing terminal output with RSpec

When testing Ruby code that prints something to the terminal, you can test that output.
Since RSpec 3.0 there is a very convenient way to do that.

Anything that writes to stdout (like puts or print) can be captured like this:

expect { something }.to output("hello\n").to_stdout

Testing stderr works in a similar fashion:

expect { something }.to output("something went wrogn\n").to_stderr

Hint: Use heredoc to test multi-line output.

expect { something }.to output(<<-MESSAGE.strip_heredoc).to_stdout...

PostgreSQL and its way of sorting strings

PostgreSQL uses the C library's locale facilities for sorting strings:

  • First, all the letters are compared, ignoring spaces and punctuation.
  • It sorts upper and lower case letters together. So the order will be something like a A b B c C
  • Then, spaces and punctuation are compared to break ties.

Example:

Ruby PostgreSQL
IMAGE3.jpg image2.jpg
image.jpg image3.jpg
image2.jpg IMAGE3.jpg
image3.jpg image.jpg

Further reading

  • [PostgreSQL-FAQ: Why do my strings sort incorrectly?](h...

Ruby: Converting UTF-8 codepoints to characters

Converting string characters to or from their integer value (7-bit ASCII value or UTF-8 codepoint) can be done in different ways in Ruby:

  • String#ord or String#unpack to get character values
  • Integer#chr or Array#pack to convert character values into Strings

Character values to Strings

Integer#chr

To get the character for a 7-bit ASCII value or UTF-8 codepoint (0-127) you can use Integer#chr:

116.chr
# => "t"

To get a character for values larger than 127, you need to pass the encoding. E.g. to get codepoint 25...

How to organize monkey patches in Ruby on Rails projects

As your Rails project grows, you will accumulate a number of small patches. These will usually fix a bug in a gem, or add a method to core classes.

Instead of putting many files into config/initializers, I recommend to group them by gem in lib/ext:

lib/
  ext/
    factory_girl/
      mixin.rb
    carrierwave/
      change_storage.rb
      fix_cache_ids.rb
      sanitize_filename_characters.rb
    ruby/
      range/
        covers_range.rb
      array/
        dump_to_excel.rb
        xss_aware_join.rb
      enumerable/
    ...

Introducing Helix: Rust + Ruby, Without The Glue

Helix allows you to implement performance-critical code of your Ruby app in Rust, without requiring glue code to bridge between both languages.

See attached article for a longer write-up about the why and how.

How to Work With Time Zones in Rails

When dealing with time zones in Rails, there is one key fact to keep in mind:

Rails has configurable time zones, while
Ruby is always in the server's time zone

Thus, using Ruby's time API will give you wrong results for different time zones.

"Without" time zones

You can not actually disable time zones, because their existence is a fact. You can, however, tell Rails the only single time zone you'll need is the server's.

config.time_zone = "Berlin" # Local time zone
config.active_record.default_timezone = :loca...

rroblak/seed_dump

This gem gives you a rake task db:seed:dump do create a db/seeds.rb from your current database state.

The generated db/seeds.rb will look this:

Product.create!([
  { category_id: 1, description: "Long Sleeve Shirt", name: "Long Sleeve Shirt" },
  { category_id: 3, description: "Plain White Tee Shirt", name: "Plain T-Shirt" }
])
User.create!([
  { password: "123456", username: "test_1" },
  { password: "234567", username: "test_2" }
])

Install MySQL 5.6 in Ubuntu 16.04

Instead of using this hack you might want to use MariaDB 10.x which can work with both old and new apps.


An alternative could be to use the MySQL Docker image which is still updated for 5.6.


Ubuntu 16.04 only provides packages for MySQL 5.7 which has a range of backwards compatibility issues with code written against older MySQL versions.

Oracle maintains a list of official APT repositories for MySQL 5.6, but those repositories do...

Using Bumbler to Reduce Runtime Dependencies - The Lean Software Boutique

Tool to show you which gems are slow to load:

➜  git:(master) ✗ bundle exec bumbler
[#################################################                             ]
(49/65) travis-lint...
Slow requires:
    110.21  render_anywhere
    147.33  nokogiri
    173.83  haml
    179.62  sass-rails
    205.04  delayed_job_active_record
    286.76  rails
    289.36  mail
    291.98  capistrano
    326.05  delayed_job
    414.27  pry
    852.13  salesforce_bulk_api

Stackprof - sampling call-stack profiler for ruby

Stackprof is a sampling call-stack profile for Ruby 2.1+.

Instead of tracking all method calls, it will simply collect the current stack trace of a running program at fixed intervals. Methods that appear on top of the stack trace most often, are the methods your program spends most of its time in.

The big advantage of this is that it is very fast. You can even enable it in production and collect real performance data. See the README on how to add it as a middleware. It will dump its data to the tmp directory.

Sampling is by default base...

djberg96/sys-filesystem: A Ruby library for getting filesystem information

Uses FFI and works all relevant operating systems.

If you'd try to do it yourself, you'd have to use FFI which is a bit awkward (see the gem's code), or call commands like df -B1 and search the strings for your relevant data.
That gem takes the pain away and works nicely.

Testing ActiveRecord validations with RSpec

Validations should be covered by a model's spec.

This card shows how to test an individual validation. This is preferrable to save an entire record and see whether it is invalid.

Recipe for testing any validation

In general any validation test for an attribute :attribute_being_tested looks like this:

  1. Make a model instance (named record below)
  2. Run validations by saying record.validate
  3. Check if record.errors[:attribute_being_tested] contains the expected validation error
  4. Put the attribute into a valid state
  5. Run...

Bundler: Install gems behind a proxy

To install gems Bundler needs to be able to talk to https://api.rubygems.org.

If you are behind a proxy you can use the https_proxy environment variable:

https_proxy=http://myproxy:123 bundle install

Note that if there is no https_proxy env variable, Bundler will also look for a http_proxy env variable.

With Capistrano

Ideally the server you're deploying on exports an https_proxy variable for all shells.

If you don't have control over the server setup, you can also add this to your Capistrano config:
...

NoMethodError: undefined method `cache' for Gem:Module

I got this error when running Rails 2.3 tests for Rails LTS. More stacktrace:

NoMethodError: undefined method `cache' for Gem:Module
    /vagrant/rails-2-3-lts-repository/railties/lib/rails_generator/lookup.rb:212:in `each'
    /vagrant/rails-2-3-lts-repository/railties/lib/rails_generator/lookup.rb:146:in `to_a'
    /vagrant/rails-2-3-lts-repository/railties/lib/rails_generator/lookup.rb:146:in `cache'
    /opt/vagrant_ruby/lib/ruby/1.8/fileutils.rb:243:in `inject'
    /vagrant/rails-2-3-lts-repository/railties/l...

Showing a custom maintenance page while deploying

Note

The maintenance mode is enabled on all application server as soon as the file /public/system/maintenance.html is present.

Installation

Add this line to your application's Gemfile:

 gem 'capistrano', '~> 3.0'
 gem 'capistrano-maintenance', '~> 1.0'

Add this line to you application's Capfile:

require 'capistrano/maintenance'

Enable task

Present a maintenance page to visitors. Disables your application's web interface by writing a #{maintenance_basename}.html file to each web server. The servers m...

Linux Performance Analysis in 60,000 Milliseconds

You login to a Linux server with a performance issue: what do you check in the first minute?

uptime
dmesg | tail
vmstat 1
mpstat -P ALL 1
pidstat 1
iostat -xz 1
free -m
sar -n DEV 1
sar -n TCP,ETCP 1
top

Also see:

rack-mini-profiler - the Secret Weapon of Ruby and Rails Speed

rack-mini-profiler is a powerful Swiss army knife for Rack app performance. Measure SQL queries, memory allocation and CPU time.

This should probably not be loaded in production (the article recommends otherwise), but this looks like a useful tool.