PostgreSQL's OVERLAPS operator is not fully inclusive

PostgreSQL supports the SQL OVERLAPS operator. You can use it to test if two date ranges overlap:

=> SELECT ('2001-02-16'::date, '2001-12-21'::date) OVERLAPS
          ('2001-12-20'::date, '2002-10-30'::date);

overlaps
--------
true

An important caveat is that the date ranges are defined as start <= time < end. As such the later date is not included in the range:

=> SELECT ('2001-02-16'::date, '2001-12-21'::date) OVERLAPS
          ('2001-12-21'::date, '2002-10-30'::date);

overlaps
--------
false

Also compar...

chromedriver-helper gem in Gemfile might break you selenium tests (of other projects)

Summary: Don't add chromedriver-helper to the Gemfile

  • the executables might break your tests in projects where chromedriver-helper is not in the Gemfile
  • developers with different chrome versions will have problems using the same chromedriver-helper version

Background

If you install the chromedriver-helper gem, but don't have it in you Gemfile, your selenium tests might fail with:

Selenium::WebDriver::Error::WebDriverError: unable to connect to chromedriver 127.0.0.1:9515

The reason is that chromedriver-helper ov...

Fix for: "can't convert nil to String" when running "gem update --system"

When attempting to update RubyGems, depending on updates your previously performed, you might run into an error

ERROR:  While executing gem ... (TypeError)
can't convert nil into String

Fix this by running

gem uninstall rubygems-update -a -x

Don't require files in random order

A common pattern in Ruby is to to require all files in a specific diretory, using something like

Dir.glob(Rails.root.join('lib/ext/**/*.rb')).each do |filename|
  require filename
end

However, this causes files to be required in an order determined by the file system. Since load order can be important, this may lead to different behavior on different machines which are hard to debug.

Simply add a .sort:

Dir.glob(Rails.root.join('lib/ext/**/*.rb')).sort.each do |filename|
  require filename
end

Ruby 3

...

JavaScript: Testing the type of a value

Checking if a JavaScript value is of a given type can be very confusing:

  • There are two operators typeof and instanceof which work very differently.
  • JavaScript has some primitive types, like string literals, that are not objects (as opposed to Ruby, where every value is an object).
  • Some values are sometimes a primitive value (e.g. "foo") and sometimes an object (new String("foo")) and each form requires different checks
  • There are three different types for null (null, undefined and NaN) and each has different rules for...

Capybara: Accessing the parent of an element

If you already selected an element and want to get its parent, you can call find(:xpath, '..') on it.

To get the grand-parent element, call find(:xpath, '../..').

Example

Find a link which contains a twitter icon and check that it links to the correct page:

<a href="http://twitter.com/">
  <i class="icon is-twitter"></i>
</a>
link = page.find("a .icon.is-twitter").find(:xpath, '..')
expect(link[:href]).to eq("http://twitter.com/")

About XPath

There is a good overview on XPath syntax on [w3schools](htt...

Fixing flaky E2E tests

An end-to-end test (E2E test) is a script that remote-controls a web browser with tools like Selenium WebDriver. This card shows basic techniques for fixing a flaky E2E test suite that sometimes passes and sometimes fails.

Although many examples in this card use Ruby, Cucumber and Selenium, the techniques are applicable to all languages and testing tools.

Why tests are flaky

Your tests probably look like this:

When I click on A
And I click on B
And I click on C
Then I should see effects of C

A test like this works fine...

Async control flow in JavaScript: Promises, Microtasks, async/await

Slides for Henning's talk on Sep 21st 2017.


Understanding sync vs. async control flow

Talking to synchronous (or "blocking") API

print('script start')
html = get('/foo')
print(html)
print('script end')

Script outputs 'script start', (long delay), '<html>...</html>', 'script end'.

Talking to asynchronous (or "evented") API

print('script start')
get('foo', done: function(html) {
  print(html)
})
print('script end')

Script outputs 'script start', 'script end', (long ...

How to add a custom dictionary to Ruby Mine

  1. Download the dictionary from http://www.winedt.org/dict.html, e.g. http://www.winedt.org/dict/de_neu.zip
  2. unzip de_neu.zip
  3. mkdir ~/Documents/dic
  4. iconv -f UTF-16 -t UTF-8 de_neu.dic -o ~/Documents/dic/de_neu_utf8.dic
  5. In RubyMine: Go to Settings (CTRL + ALT + S) > Editor > Spelling > Dictionaries and add the folder ~/Documents/dic

Bundler 2 will rename Gemfile and Gemfile.lock

TL;DR: Bundler 2.0 will rename Gemfile to gems.rb and Gemfile.lock to gems.locked (sic).

The old filenames will be supported until the release of Bundler 3.0.

Some more breaking changes here.

RSpec: How to define classes for specs

RSpec allows defining methods inside describe/context blocks which will only exist inside them.
However, classes (or any constants, for that matter) will not be affected by this. If you define them in your specs, they will exist globally. This is because of how RSpec works (short story: instance_eval).

Negative example:

describe Notifier do
  class TestRecord < ApplicationRecord # DO NOT do this!
    # ...
  end
  
  let(:record) { TestRecord.new }
  
  it { ... }
end

# TestRecord will exist here, outside of the spec!

D...

Import Excel files without running into memory limitations

There are several gems that make it easy to read and process xlsx files. Parsing the entire file at once however is error-prone since each cell is transformed to a ruby object - sometimes including thousands of formatted but empty cells.

As of today, I found two promising alternatives that provide a stream-based access to spradsheet rows:

  • Roo supports multiple spreadsheet types like ODS or CSV and has a quite large contributor base
  • [Creek](https://github.com/pythonicrubyis...

Ruby: A small summary of what return, break and next means for blocks

Summary

  • Use return to return from a method. return accepts a value that will be the return value of the method call.
  • Use break to quit from a block and from the method that yielded to the block. break accepts a value that supplies the result of the expression it is “breaking” out of.
  • Use next to skip the rest of the current iteration. next accepts an argument that will be the result of that block iteration.

The following method will serve as an example in the details below:

def example
  puts yield
  puts ...

Custom Ruby method Enumerable#count_by (use for quick statistics)

I frequently find myself needing a combination of group_by, count and sort for quick statistics. Here's a method on Enumerable that combines the three:

module Enumerable
  def count_by(&block)
    group_by(&block)
      .transform_values(&:count)
      .sort_by(&:last)
      .to_h
  end
end

Just paste that snippet into a Rails console and use #count_by now!

Usage examples

  • Number of email addresses by domain:
> User.all.count_by { |user| user.email.sub /^.*@/, '' }
=> { "sina.cn"=>2, ..., "hotmail.com"=>128...

Understanding Scope in Ruby

Scope is all about where something is visible. It’s all about what (variables, constants, methods) is available to you at a given moment. If you understand scope well enough, you should be able to point at any line of your Ruby program and tell which variables are available in that context, and more importantly, which ones are not.

The article gives detailed explanation on the variable scope in ruby with examples that are easy to understand. Every ruby developer should at least know the first part of the article by heart. The second half ...

How to use a local gem in your Gemfile

You can use local copies of gems in your Gemfile like this:

gem 'spreewald', path: '~/gems/spreewald'

As soon as you have bundled your project with the local copy of the gem, all code changes in the copy will be available on your project. So you can for example set a debugger or add console output in the gem and use it from your project.
If you checked out the gem with your versioning tool, you can easily reset your changes afterwards or make a pull request for the gem if you improved it.

Don't commit a Gemfile with local path...

IRB: last return value

In the ruby shell (IRB) and rails console the return value of the previous command is saved in _ (underscore). This might come in handy if you forgot to save the value to a variable and further want to use it.

Example:

irb(main):001:0> 1 + 2
=> 3
irb(main):002:0> _
=> 3
irb(main):003:0> a = _
=> 3

Caution when using the || operator to set defaults

I often see the use of || to set a default value for a variable that might be nil, null or undefined.

x = x || 'default-value'

This pattern should be avoided in all languages.

While using || works as intended when x is null or an actual object, it also sets the default value for other falsy values, such as false. false is a non-blank value that you never want to override with a default.

To make it worse, languages like JavaScript or Perl have [many more fal...

ActiveRecord::RecordNotFound errors allow you to query the :name and :id of the model that could not be found

ActiveRecord::RecordNotFound errors provide quite meaningful error messages that can provide some insight on application details. Consider the following:

ActiveRecord::RecordNotFound: Couldn't find Organisation::Membership with 'id'=12 [WHERE "organisation_memberships"."user_id" = 1]

You should probably not simply render those error messages to the user directly. Instead you you might want to re-raise your own errors. ActiveRecord::RecordNotFound provides you with methods :model and :id where you can get information about w...

Ruby: define a class with Struct.new

This card will show you a cool way to define a class using Struct.new.
A common usecase for Structs are temporary data structures which just hold state and don't provide behaviour. In many cases you could use a simple hash as a data structure instead. However, a Struct provides you with a nice constructor, attribute accessors and complains if you try to access undefined attributes. Structs are easy to compare (by attributes). A struct gives meaning to the data.

Disclaimer

Structs are great...

Speed up better_errors

If you use the Better Errors gem, you will sometimes notice that it can be very slow. This is because it sometimes renders a huge amount of data that will actually be hard to render for your browser.

You can significantly improve performance by adding this to config/initializers/better_errors:

if defined?(BetterErrors) && Rails.env.development?
  module BetterErrorsHugeInspectWarning
    def inspect_value(obj)
      inspected = obj.inspect
      if inspected.size > 20_000
        inspec...

Rails: How to write custom email interceptors

Nowadays it is fairly easy to intercept and modify mails globally before they are sent. All you have to do is register an interceptor class which responds to .delivering_email(message). This card will show you two common use cases.

Subject prefix:

Usually you want to prefix the subject line of emails with the current environment (except production) so you can differentiate between production mails and mails from other environments. Of course a...

Working with or without time zones in Rails applications

Rails supports time zones, but there are several pitfalls. Most importantly because Time.now and Time.current are completely different things and code from gems might use one or the other.

Especially configuring an application that cares only about one time zone is a bit tricky.

The following was tested on Rails 5.1 but should apply to Rails 4.2 as well.

Using only local time

Your life will be easier if your application does not need to support time zones. Disable them like this:

config.time_zone = 'Berlin' # Your local ...

Listing all gems on a private gem server

You can use gem list to list all gems available from a remote gem server:

gem list -r --clear-sources -s 'https://user:password@gemserver.tld/'

This is useful to debug cases where Bundler complains of a gem existing in more than one gem source.