Generating test images on the fly via JavaScript or Ruby

When you need test images, instead of using services like lorempixel or placehold.it you may generate test images yourself.

Here we build a simple SVG image and wrap it into a data: URI. All browsers support SVG, and you can easily adjust it yourself.
Simply set it as an image's src attribute.

JavaScript

Simple solution in modern JavaScript, e.g. for use in the client's browser:

function svgUri(text) {
  let svg = `
    <svg wid...

RubyMine: You can disable inspections you don't care about

When you find yourself constantly ignoring a RubyMine warning, you can simple disable that warning and de-clutter your editor. E.g. in my Cucumber scenarios RubyMine underlines 90% of all lines because it does not know about spreewald, making the file really hard to read.

You can disable any unwanted inspection by opening File / Settings / Editor / Inspections and searching for the warning text.

What you disable or keep is up to your personal preference. I personally disable at least the following...

Running Rails 2 apps with modern MariaDB SQL server

You might have some trouble running a Rails LTS 2 app with MySQL 5.7.

If you don't want to hack Mysql 5.6 into your modern Ubuntu or use the MySQL sandbox, you might want to try MariaDB 10.x.

MariaDB 10.x should work with both old and new Rails applications.

[Switch to MariaDB](https://makandracards.com/makandra/468343-how-...

Making httpclient use the operating system's SSL cert store

The httpclient gem comes with a custom SSL cert store.

While an customizable, application-level cert store is great when you need to deal with broken or self-signed certificates, you usually want to use the cert store from the underlying Linux. The Linux cert store is updated periodically while httpclient's cert store goes out of date and will eventually not be able to verify certs.

To use the cert store from the underlying operating system:

client = HTTPClient.new
client.ssl_config.cert_store...

Upgrading a Rails app to Cucumber 3

Upgrade gems

You need to update a lof gems. Make sure you don't have any version constraints in your Gemfile or your bundle update won't do anything!

Upgrade cucumber_priority:

bundle update cucumber_priority

Upgrade spreewald:

bundle update spreewald

Upgrade cucumber_factory:

bundle update cucumber_factory

Upgrade parallel_tests:

bundle update parallel_tests

Even on the latest version, parallel_tests will print some deprecation warnings due to using an older formatter A...

PostgreSQL: Upgrading your user to a superuser

Your default postgres user is named like your linux user. That default user has limited access privileges, which can cause issues such as:

  • DatabaseCleaner needs to disable foreign key constraints before it can wipe the database.
  • Importing a remote dump with geordi
  • Asking Postgres to show the storage path of a database

Doing these things without a superuser will show a Postgres error or (in Ruby) raise PG::InsufficientPrivilege.

To do so, the application's PostgreSQL user must be a superuser. ...

How to: Fix incorrect MySQL client library version

Bundler::GemRequireError: There was an error while trying to load the gem 'mysql2'.
Gem Load Error is: Incorrect MySQL client library version! This gem was compiled for 5.5.46 but the client library is 5.6.30.

Same as in Fix "libmysqlclient.so.20: cannot open shared object file: No such file or directory":

gem pristine mysql2

gem pristine re-installs a gem (without re-downloading), re-compiling all native extensions in the process.

Mysql::Error: BLOB/TEXT column can't have a default value

mysql> SELECT @@global.version;
+------------------+
| @@global.version |
+------------------+
| 5.6.30           |
+------------------+
1 row in set (0,00 sec)

MySQL 5.6 Reference Manual says "BLOB and TEXT columns cannot have DEFAULT values".

If you want to run migrations in development here are two variants which might help. If you are not sure about the side effects (e.g. your application is broken when it doesn't set additional default values on application side, too...

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

Configuring RubyGems to not install documentation by default

When installing gems, a lot of time is spent building locally installed documentation that you probably never use.

We recommend you disable documentation generation for gem install by default.
Note that Bundler won't install documentation, so this advice applies only when installing gems manually.

If you don't already have it, create a ~/.gemrc file. The gemrc is a Yaml file, so add the following line to add default switches to the gem command.

gem: --no-document

(If you do n...

Katapult 0.3.0 released

Katapult 0.3.0 brings Rails 5 and Ruby 2.5 support with a new design, plus a ton of smaller features, fixes and improvements.

Features

  • Generating a Rails 5.1.4 app on Ruby 2.5.0
  • Dropped asset pipeline in favor of Webpacker
  • The generated application now has a sleek, simple design based on Bootstrap
  • Employing Unpoly
  • New application model DSL shortcut crud for "create a model and a web UI with crud actions"
  • The generated application model is now a transformable e...

Ruby: Using all? with empty collection

Enumerable#all? returns true for an empty collection. This totally makes sense but you have to think about it when making assumptions on relations which might be empty.

[].all?(&:will_never_be_called_here) => true

Example with empty collection

class Researcher < ActiveRecord::Base
  has_many :papers
end

class Paper
  validates :topic, inclusion: { in: ['computer_science', 'mathematics', 'economy'] }
end

Easy goal: Delete all researches who write onl...

Ruby: control flow with throw and catch

This article will show you how to use throw and catch. It's a nice tool to break out of multiple loops when a result is obtained.

Also see our card Ruby: A small summary of what return, break and next means for blocks.

Error during Rails 5 upgrade: Environment data not found in the schema

This error is raised because your old database does not have a configured environment yet, which Rails 5 enforces.

If this error occurs while migrating your parallel test databases, make sure to update the parallel_tests gem first: current versions fix this. If you're still using Cucumber < v3, the latest version of parallel_tests will be 2.18.0.

Writing a README for a project

Rails applications and ruby gems should have a README that gives the reader a quick overview of the project. Its size will vary as projects differ in complexity, but there should always be some introductory prose for a developer to read when starting on it.

Purpose

That's already the main purpose of a project README: Give a new developer a quick overview of the project. In sketching this outline, the README should notify the reader of any peculiarity he needs to know of.

Remember that in a few months, you'll be a kind of "new ...

xlsxtream: Streaming & Fast XLSX Spreadsheet Writer for Ruby

When writing XLSX files, there are gems like rubyXL or axlsx. While they do offer features like formatting or graphs, to represent spreadsheet cells, they have to keep several Ruby objects in memory. When writing huge files, both will become slow and consume lots of memory.

Enter Xlsxtream, a Ruby XLSX library with less features (e.g. no individual cell styles) but which does away with the memory issue by streaming ...

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