How to make Webpacker compile once for parallel tests, and only if necessary

Webpack is the future. We're using it in our latest Rails applications.

For tests, we want to compile assets like for production.
For parallel tests, we want to avoid 8 workers compiling the same files at the same time.
When assets did not change, we do not want to spend time compiling them.

Here is our solution for all that.

Its concept should work for all test suites.

Copy the following to config/initializers/webpacker_compile_once.rb. It will patch Webpacker, but only for the test environment:

# Avoid hardcoded asset host...

Cucumber: Test that an element is not overshadowed by another element

I needed to make sure that an element is visible and not overshadowed by an element that has a higher z-index (like a modal overlay).

Here is the step I wanted:

Then the very important notice should not be overshadowed by another element

This is the step definition:

Then(/^(.*?) should not be overshadowed by another element$/) do |locator|
  selector = selector_for(locator)
  expect(page).to have_css(selector)
  js = <<-JS
    var selector = #{selector.to_json};
    var elementFromSelector = document.querySelector(selector)...

Caution: Carrierwave has your file three times (temporarily)

When storing a file with Carrierwave, it is always cached prior to actually storing it (to support form roundtrips).

Carrierwave by default 1) copies the file to the cache and then 2) copies it again to its final destination (deleting the cached file immediately after storing). This means there are 3 (three) instances of a file on the disk at some point in time, and still 2 instances after Carrierwave is done if you do not remove...

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

How to: Rails cache for individual rspec tests

Rails default config uses the ActiveSupport::Cache::NullStore and disables controller caching for all environments except production:

config.action_controller.perform_caching = false
config.cache_store = :null_store

If you want to test caching you have at least two possibilities:

  1. Enable caching for every test (not covered by this card and straightforward)
  2. Enable caching for individual test

Enable caching for individual test (file cache)

1. Leave the defau...

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

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

RawGit

RawGit serves raw files directly from GitHub with proper Content-Type headers, for CDN-like purposes.

Note that they don't offer any uptime guarantee. You probably don't want to use it in production, but it may serve you well for some testing/prototyping/etc.

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

PostgreSQL: How to add/remove/modify array values (and how to replace 1 value with multiple values)

PostgreSQL's array data type is pretty useful, but manipulating values of arrays can be awkward because of its syntax.

Consider the following users table which each example below will start from:

name topics
Alice {cats,dogs}
Bob {llamas}

(PostgreSQL uses curly braces to represent arrays, true story.)

Adding values

Use the array_cat function, or the || operator.

These calls will add the values "cats" and "mice" to use...

Clicking issues with chromedriver

ChromeDriver clicking works by simulating a mouse click in the middle of the element's first client rect (or bounding client rect if it doesn't have a first client rect). (Clicking issues)

So if you are trying to click on an element, chromedriver tries to click at the position where it first finds that element.

This can lead to some problems and unexpected behavior like:

  • Element is not clickable at point ... erros
  • Another element which is now located at...

Rails 5's ApplicationRecord is the place to put generic model logic

Since Rails 5, domain models inherit from ApplicationRecord by default.

This is the place to put code that should be available in all your application's models.
There is no reason to monkey-patch ActiveRecord::Base when following that practice.

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

Ubuntu: keyring password won't change with user password

Ubuntu might create several keyrings for you. Note that keyring passwords are initially set to the user password but do not change with it. If you can't seem to find the correct password for a keyring, try entering the inital password.

If you still can't find it, you might have to delete that keyring:

  • open "Passwords and Keys" (seahorse application)
  • right-click on the keyring you want to change
  • press Delete

Testing webpages globally (as in "around the globe")

These tools help you in checking websites globally:

DNS Checker

This tool allows for global DNS propagation checking.

GeoScreenshot

This tool takes screenshots of a given URL from various locations across the world.

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

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

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.

Standard Gems

Ruby's standard library is in the process of being gemified. It will soon - Ruby 2.5 - consist of RubyGems, which can be updated independently from Ruby.

This might mean smoother Ruby upgrades in the future. If breaking API changes happen in standard gems, we can update these before upgrading Ruby.

HTTP/2 push is tougher than I thought - JakeArchibald.com

TLDR: Browser implementations of HTTP/2 push are horrible. You might end up with worse performance than without pushing. However, the article includes a great explanation of how HTTP/2 push are supposed to integrate with browser APIs.

RSpec: Stubbing a method that takes a block

If you stub a method or set expectations with should_receive these stubbed methods may also yield blocks. This is handy if the returning object is receiving a block call.

Consider this, where you cannot say and_return [] because of the block:

def crawl_messages
  Message.find_in_batches do |messages|
    messages.each(&:crawl)
  end
end

It works similar to and_return -- just use and_yield:

describe '#crawl_messages' do
  it 'should proc...

Middleman: Use pretty URLs without doubling requests

By default Middleman generates files with a .html extension. Because of this all your URLs end in /foo.html instead of /foo, which looks a bit old school.

To get prettier URLs, Middleman lets you activate :directory_indexes in config.rb. This makes a directory for each of your pages and puts a single file index.html into it, e.g. /foo/index.html. This lets you access pages with http://domain/foo.

Don't double your requests!

Unfortunately you are now forcing every br...

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