Shoulda Matchers: how to test conditional validations

Shoulda Matchers don't provide canditional validations (validations with if: option). Here is how to write tests for the condition:

Class:

class Employee < ActiveRecored::Base
  validates :office, presence: true, if: manager?
  
  def manager?
    ...
  end
end

Test:

describe Employee do

  describe '#office' do
    
    context 'is a manager' do
      before { allow(subject).to receive(:manager?).and_return(true) }
      it { is_expected.to validate_presence_o...

MySQL 5.7.5 enables `ONLY_FULL_GROUP_BY` mode per default

When using GROUP BY, MySQL now complains if the SELECT includes columns which are not part of the GROUP BY.

Reason:

There could be multiple values for those columns per group but only one value can be picked for the results.

The default behaviour of MySQL prior to version 5.7 will not complain and arbitrarily choose a value. But this leads to non-deterministic results. So MySQL now has enabled the only_full_group_by setting by default to prevent this.

In Rails this could lead to some trouble, because scopes do not have sp...

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

Capistrano 2: How to deploy a single server

When you have a multi-server setup, you'll be adding a new server from time to time. Before doing a full deploy, you might want to test that server in an isolated deploy. There is a single way to do this: the HOSTFILTER env variable.

Commenting out "server" lines in the Capistrano deploy config will raise a Capistrano::NoMatchingServersError with <task> is only run for servers matching {:roles=> <role>}, but no servers matched. Instead, specify the server-under-test like this:

HOSTFILTER=separate-sidekiq.makandra.de cap productio...

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

How to define height of a div as percentage of its variable width

This is useful if, for example, you want to use a background-image that has to scale with the width and the div should only have the height of the picture.

html:

<div class="outer">
  <div class="inner">
  </div>
</div>

css:

.outer {
  width: 100%;
  background-image: image-url('background.png');
  background-size: cover;
}
  
.inner {
  padding-top: 60%;
}

How does it work?

There are several CSS attributes that can handle values as percentage. But they use different other attributes as "reference value...

Git: Rebasing dependent feature branches

This card will show you how to use git rebase --onto without confusion.

Use case:

You've got two feature branches (one and two), where two depends on one. Now commits of branch one have changed after you branched two from it (i.e. after code review the commits of branch one are squashed). Thus the commit history of branch one has changed. Branch two's history however didn't change.

Solution:

To make the branches share the same commit history again you will have to rebase and replay (attach) the a...

Webmock's hash_including doesn't parse query values to string

Webmocks hash_including is similar to RSpec::Mocks::ArgumentMatchers#hash_including. Be aware that hash_including (webmock v3.0.1) doesn't parse integer values to String.

Without hash including you would say:

uri = URI('http://example.com/?foo=1&bar=2')
stub_request(:get, 'example.com').with(query: {foo: 1, bar: 2})
Net::HTTP.get(uri) # ===> Success

If you only want to check if foo is present you can use hash_including:

uri = URI('http://example.com/?foo=1&bar=2')
stub_request(:get, 'example.com').with(query: hash_i...

Webmock normalizes arrays in urls

Typhoeus has a different way of representing array params in a get request than RestClient.

Typhoeus: http://example.com/?foo[0]=1&foo[1]=2&foo[2]=3
RestClient: http://example.com/?foo[]=1&foo[]=2&foo[]=3

Webmock normalizes this url when matching to your stubs, so it is always http://example.com/?foo[]=1&foo[]=2&foo[]=3. This might lead to green tests, but in fact crashes in real world. Rack::Utils.build_nested_query might help to build a get re...

Middleman configuration for Rails Developers

Middleman is a static page generator that brings many of the goodies that Rails developers are used to.

Out of the box, Middleman brings Haml, Sass, helpers etc. However, it can be configured to do even better. This card is a list of improvement hints for a Rails developer.

Gemfile

Remove tzinfo-data and wdm unless you're on Windows. Add these gems:

gem 'middleman-livereload'
gem 'middleman-sprockets' # Asset pipeline!

gem 'bootstrap-sass' # If you want to use Bootstrap

gem 'byebug'

gem 'capistrano'
gem 'capistrano-mid...

Capistrano 3: How to deploy when a firewall blocks your git repo

Sometimes, through some firewall or proxy misconfiguration, you might have to deploy to a server that cannot access the git repository.

Solution 1: HTTP Proxy (this is the preferred fix)

SSH can be tunneled over an HTTP Proxy. For example, when the repo is on github, use this:

  1. Install socat

  2. Add a ~/.ssh/config on the target server(s) with permission 0600 and this content:

    Host github.com ssh.github.com
      User git
      Hostname ssh.github.com
      Port 443
      ProxyCommand socat - PROXY:<your proxyhost>:%h:%p,...
    

Quickly printing data in columns on your Ruby console

Dump this method into your Ruby console to quickly print data in columns. This is helpful for e.g. comparing attributes of a set of Rails records.

def tp(objects, *method_names)
  terminal_width = `tput cols`.to_i
  cols = objects.count + 1 # Label column
  col_width = (terminal_width / cols) - 1 # Column spacing

  Array(method_names).map do |method_name|
    cells = objects.map{ |o| o.send(method_name).inspect }
    cells.unshift(method_name)

    puts cells.map{ |cell| cell.to_s.ljust(col_width) }.join ' '
  end

  nil
end

Usag...

Style Guide for Git commit messages

  1. Separate subject from body with a blank line
  2. Limit the subject line to 50 characters (max. 72), include reference (unique story ID) to requirements tracker (Linear in our case)
  3. Capitalize the subject line
  4. Do not end the subject line with a period
  5. Use the imperative mood in the subject line
  6. Wrap the body at 72 characters
  7. Use the body to explain what and why vs. how.

    Tip

    As an alternative, use a commit message that refers to a GitHub issue (fixes #321) or [story ID](https://makandracards.com/makandra/620718-b...

Using ActiveRecord with threads might use more database connections than you think

Database connections are not thread-safe. That's why ActiveRecord uses a separate database connection for each thread.

For instance, the following code uses 3 database connections:

3.times do
  Thread.new do
    User.first # first database access makes a new connection
  end
end

These three connections will remain connected to the database server after the threads terminate. This only affects threads that use ActiveRecord.

You can rely on Rails' various clean-up mechanisms to release connections, as outlined below. This may...

RSpec: Debug flickering test suites with rspec --bisect

In modern default RSpec configurations, your tests are usually run in random order. This helps to detect "flickering" tests that only fail when run in a certain order.

The reason for this are tests that have side effects causing other tests to fail later. The hard part is to find the offending test.

Enter rspec --bisect:

  1. Say you have a flickering test that passes on its own, but you just saw it fail in a full test run. At the top of the RSpec output, you will see a message like Randomized with seed 12345. Take a note of the number....

Howto: Require a gem that is not in Gemfile

In case you want to require a gem, that is not in the Gemfile of you bundle and therefore not in your loadpath, you need to add the path manually and require the gem afterwards.

Expected error

Requiring a gem, that is not in the Gemfile or .gemspec, will cause an LoadError exception:

require 'example_gem' => LoadError: cannot load such file -- example_gem

Adding a gem to the loadpath temporary

  1. You need to install the gem

gem install 'example_gem'

  1. Then you need to require the path where the gem was install...

Storing trees in databases

This card compares patterns to store trees in a relation database like MySQL or PostgreSQL. Implementation examples are for the ActiveRecord ORM used with Ruby on Rails, but the techniques can be implemented in any language or framework.

We will be using this example tree (from the acts_as_nested_set docs):

root
|
+-- Child 1
|   |
|   +-- Child 1.1
|   |
|   +-- Child 1.2
|
+-- ...

How to use Parallel to speed up building the same html partial multiple times (for different data)

The parallel-gem is quite easy to use and can speed up rendering time if you want to render the same partial multiple times (e.g. for rendering long lists of things).

Parallel supports execution using forked processes (the default), threads (mind the GVL) and Ractors (some limitations for data sharing).

If your parallelized code talks to the database, you should [ensure not to leak database connections](https://makandracards.com/makandra/45360-using-activerecord-with-threads-will-leak-database-connect...

How to disable Chrome's save password bubble for Selenium tests

When filling out forms in Selenium tests, Chrome shows the (usual) bubble, asking to store those credentials.

While the bubble does not interfere with tests, it is annoying when debugging tests. Here are two ways to disable it:

Option 1: prefs

You can set profile preferences to disable the password manager like so:

prefs = {
  'credentials_enable_service' => false,
  'profile.password_manager_enabled' => false
}

Capybara::Selenium::Driver.new(app, browser: :chrome, prefs: prefs)

Sadly, there are no command line s...

CSS: Don't target multiple vendor-prefixed pseudo-elements in a single rule

Some pseudo-elements need to be addressed with vendor prefixes. E.g. ::selection is not supported by Firefox, you need to use ::-moz-selection instead.

What you cannot do is to define a single CSS rule to address both the standard and vendor-prefixed form:

::selection, ::-moz-selection {
  background-color: red;
}

This rule will be ignored by all browsers. The reason is that if a browser doe...

How to make http/https requests yourself with nc/telnet/openssl

Sometimes you want/have to send specific http(s) requests. You can do that easy with curl or just write the request yourself.

make a http request with nc

nc example.com 80
GET / HTTP/1.1
Host: example.com
# press enter

make a http request with telnet

telnet example.com 80
GET / HTTP/1.1
Host: example.com
# press enter

make https request with openssl

openssl s_client -connect example.com:443
GET / HTTP/1.1
Host: example.com
# press enter

You can specify more headers if you want:

nc example.c...

Sending errors to sentry from development

For the initial setup or changes in the sentry reporting it might be useful to enabled reporting of sentry in development. Don't commit these changes and prefer to report to the staging environment. As other developers might be confused of these errors try to given them a proper message and delete them afterwards.


  1. Add config.raven_dsn = 'your-dns' in config/environments/development.rb.
  2. Add development to existing environments in the Raven.configure block: config.environments = ['development', 'staging', 'production'].
  3. ...

How to view Image Metadata on the Linux Command Line with ImageMagick

ImageMagick has a command line tool called identify which can read image metadata:

>identify -verbose DSC00136.JPG
Image: DSC00136.JPG
  Format: JPEG (Joint Photographic Experts Group JFIF format)
  Class: DirectClass
  Geometry: 5472x3648+0+0
  Resolution: 350x350
  Print size: 15.6343x10.4229
  Units: PixelsPerInch
  Type: TrueColor
  Endianess: Undefined
  Colorspace: sRGB
  Depth: 8-bit
  Channel depth:
    red: 8-bit
    green: 8-bit
    blue: 8-bit
  Channel statistics:
    Red:
      min: 0 (0)
      max: 255 (1)
      mean: 11...

Beware: Nested Spreewald patiently blocks are not patient

Note: The behaviour of Spreewald's within step is as described below for version < 1.9.0; For Spreewald >= 1.9.0 it is as described in Solution 1.


When doing integration testing with cucumber and selenium you will often encounter problems with timing - For example if your test runs faster than your application, html elements may not yet be visible when the test looks for them. That's why Spreewald (a collection of cucumber steps) has a concept of doing things patiently, which means a given b...