Capistrano task to tail remote application logs of multiple servers

When your application is running on a multi-server setup, application logs are stored per server (unless you choose a centralized logging solution).
Here is a Capistrano task that connects to all servers and prints logs to your terminal like this:

$ cap production app:logs
00:00 app:logs
      01 tail -n0 -F /var/www/your-application/shared/log/production.log | while read line; do echo "$(hostname): $line"; done
      01 app01-prod: Started GET "/sign_in" for 1.2.3.4 at 2018-04-26 11:28:19 +0200
      01 app01-prod: Proc...

Unpoly: Loading large libraries on-demand

When your JavaScript bundle is so massive that you cannot load it all up front, I would recommend to load large libraries from the compilers that need it.

Compilers are also a good place to track whether the library has been loaded before. Note that including same <script> tag more than once will cause the browser to fetch and execute the script more than once. This can lead to memory leaks or cause duplicate event handlers being registered.

In our work we mostly load all JavaScript up front, since our bundles are small enough. We recent...

jQuery: How to replace DOM nodes with their contents

You know that you can use jQuery's text() to get an element's contents without any tags.
If you want to remove only some tags, but keep others, use contents() and unwrap(). Here is how.

Consider the following example element.

$container = $('<div><strong>Hello</strong> <em>World</em></div>')

Let's say we want to discard any <em> tags, but keep their contents.
Simply find them, then dive into their child nodes via contents, and use unwrap replace their ...

When you want to format only line breaks, you probably do not want `simple_format`

For outputting a given String in HTML, you mostly want to replace line breaks with <br> or <p> tags.
You can use simple_format, but it has side effects like keeping some HTML.

If you only care about line breaks, you might be better off using a small, specialized helper method:

def format_linebreaks(text)
  safe_text = h(text)
  paragraphs = split_paragraphs(safe_text).map(&:html_safe)

  html = ''.html_safe
  paragraphs.each do |paragraph|
    html << content_tag(:p, paragraph)
  end
  html
end

Full di...

Rails: How to provide a public link in a mail

Lets say we have a user with a contract whereas contract is a mounted carrierwave file.

Now we want to send the link to the contract in a mail. For this use case join the root_url with the public contract path in the mailer view:

Plain text email

URI.join(root_url, @user.contract.url)

HTML email

link_to('Show contract', URI.join(root_url, @user.contract.url).to_s)

Note: You need to follow [http://guides.rubyonrails.org/action_mailer_basics.html#g...

Writing strings as Carrierwave uploads

When you have string contents (e.g. a generated binary stream, or data from a remote source) that you want to store as a file using Carrierwave, here is a simple solution.

While you could write your string to a file and pass that file to Carrierwave, why even bother? You already have your string (or stream).
However, a plain StringIO object will not work for Carrierwave's ActiveRecord integration:

>> Attachment.create!(file: StringIO.new(contents))
TypeError: no implicit conversion of nil into String

This is because Carrierwav...

HTML5: disabled vs. readonly form fields

Form fields can be rendered as noneditable by setting the disabled or the readonly attribute. Be aware of the differences:

disabled fields

  • don’t post to the server
  • don’t get focus
  • are skipped while tab navigation
  • available for button, fieldset, input, select, textarea, command, keygen, optgroup, option

Browser specific behavior:

  • IE 11: text inputs that are descendants of a disabled fieldset appear disabled but the user can still interact with them
  • Firefox: selecting text in a disabled text field is no...

Chrome: Making high-resolution website screenshots without add-ons

If you want to make a screenshot of a website that works well in print or on a high-DPI screen (like Apple Retina displays), here is how you can capture a high-resolution screenshot.

You can do this without an addon:

  • Open the website
  • If you have multiple monitoros:
    • Resize the Chrome window so it covers multiple monitors (in Linux you can hold ALT and resize by dragging with the right mouse button)
    • Zoom into the page using CTRL + and CTRL - so it covers most of the window area. Leave a little padding on the left and right so...

XHR is not JSON

When a Rails controller action should handle both HTML and JSON responses, do not use request.xhr? to decide that. Use respond_to.

I've too often seen code like this:

def show
  # ...
  if request.xhr?
    render json: @user.as_json
  else
     # renders default HTML view
  end
end

This is just plain wrong. Web browsers often fetch JSON via XHR, but they (should) also send the correct Accept HTTP header to tell the server the data they expect to receive.

If you say request.xhr? as a means for "wants JSON" you are ...

Carrierwave: Deleting files outside of forms

TL;DR Use user.update!(remove_avatar: true) to delete attachments outside of forms. This will have the same behavior as if you were in a form.


As you know, Carrierwave file attachments work by mounting an Uploader class to an attribute of the model. Though the database field holds the file name as string, calling the attribute will always return the uploader, no matter if a file is attached or not. (Side note: use #present? on the uploader to check if the file exists.)

class User < ApplicationRecord
  mount :avatar, ...

Form letters with LibreOffice Writer

This is painful. Consider using Microsoft Office or switching careers. If you need to write < 20 letters consider doing it manually.

So you didn't listen and here it comes:

  1. Ignore the Mail Merge Wizard. It will crash or destroy your document.
  2. Export your addresses, recipient names, etc. as a .ods spreadsheet (.xls, .xlsx, .ods). Use any columns that work for you, but be consistent. I like to use one column for the address, one column for the salutation line.
  3. Import the spreadsheet as an address book source: *Tools => Add...

Rails: Migration helper for inserting records without using models

You should avoid using application models in your migrations. But how else could you create records in a migration?

The most basic way is to write plain SQL, but since INSERT statements are no pleasant write for Rubyists, here is a simple wrapper:

Record creation helper for migrations

The helper method below takes a table name and a hash of attributes, which it inserts into the specified table. Copy it over to your migration and profit!

  private

  def insert_record(table, **attributes)
    attributes.merge!...

How to fix HTML elements being cut off when printing

When you print (or print preview) and elements are cut off (e.g. after 1st page, or "randomly") you should check your CSS rules for these:

  • Is there an element with "display: inline-block" that surrounds your content? Make sure it has "display: block" for printing.
    This primarily affects Firefox and Internet Explorer. Chrome seems to be able to handle inline-block elements in most cases.

  • Does the element itself, or a container, define "overflow: hidden"? Use "overflow: auto" (or maybe "overflow: visible") instead.

  • Is th...

HTML: Making browsers wrap long words

By default, browsers will not wrap text at syllable boundaries. Text is wrapped at word boundaries only.

This card explains some options to make browsers wrap inside a long word like "Donaudampfschifffahrt".

Option 1: hyphens CSS property

Modern browsers are able to hyphenate natively with the CSS property hyphens:

hyphens: auto

There is also hyphens: none (disable hyphenations even at &shy; entities) and hyphens: manual (hyphenation at &shy; only).
This feature was integrated [just ...

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

How to install packages from newer Ubuntu releases

We're usually running Ubuntu LTS versions. Sometimes newer hardware requires packages from more recent Ubuntu releases that only come with 6 months of support. If there is really no other way, it's possible to install packages from later Ubuntu releases

Caution: Pay really close attention to what you're doing. Depending on the package, this process may require upgrading a lot of dependencies, possibly breaking the system! You really should not do this unless you've carefully calculated the impact on your system

Preparation

First,...

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

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

Be careful to use correct HTTP status codes for maintenance pages

When your public-facing application has a longer downtime for server maintenance or long migrations, it's nice to setup a maintenance page to inform your users.

When delivering the maintenance page, be very careful to send the correct HTTP status code. Sending the wrong status code might get you kicked out of Google, or undo years of SEO work.

Popular footguns

Here are some ways to shoot yourself in the foot during maintenance:

  • If all your routes send a "200 OK" with a HTML body "We're b...