ActiveRecord::Store: migrate data in store

When you need to store structured data (like Ruby hashes) in a single database column with ActiveRecord, a simple way is to use PostgreSQL's jsonb columns. ActiveRecord will automatically serialize and deserialize your Hash to and from JSON, and you can index JSON paths for fast reads.

As an alternative, ActiveRecord::Store offers a way to store hashes in a single database column. This card will show you how to migrate those hashes in an ActiveRecord::Migration by example:
...

Checklist: Rails Authentication

Authentication is a special part of web applications. On the one hand, it usually is a crucial security mechanism restrict access to certain people and roles. On the other hand, most users authenticate only once, so it is very unlikely to spot issues by accident.

So, here comes a quick checklist to help you verifying your authentication solution is all set.

  • This should be default: use HTTPS with HSTS. The HSTS part is important.
  • Use a reliable authentication solution, e.g. Clearance or [Devise...

screenfull.js: Simple wrapper for cross-browser usage of the JavaScript Fullscreen API

Using the JS fullscreen API is painful because all browers use different methods and events and you need to use lots of boilerplate code to make your application work in all browsers.

The "screenfull" library wraps that for you, including events.

Examples

The linked GitHub repo contains some information. You basically use the library like this:

// Make an element go fullscreen
screenfull.request(element)

// Leave fullscreen
screenfull.exit()

...

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

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

Fixing: Gem::Package::PathError: installing into parent path is not allowed

This might be a known issue with Rubygems 2.5.1. This will help:

gem update --system

How to resize your boot partition when there is an encrypted partition after it

Boot partitions from installations prior to the 16.04 era are terribly small. When you install updates and encounter errors due to a full /boot partition, consider risizing it.

If you can't do the steps described below, ask someone experienced to help you out.
This has worked 100% so far. 1 out of 1 tries. ;)

Scenario A: There is unassigned space on your physical drive

When there is some unpartitioned space on your drive, increasing the size of /boot is actually very easy (even though the list below is rather long). It only takes a...

Rails: Overriding view templates under certain conditions only

Rails offers a way to prepend (or append) view paths for the current request. This way, you can make the application use different view templates for just that request.

Example

A use case of this is a different set of view templates that should be used under certain circumstances:

class UsersController < ApplicationController

  before_action :prepare_views
  
  def index
    # ...
  end    
  
  private
  
  def prepare_views
    if <condition>
      prepend_view_path Rails.root.join('app', 'views', 'special')
    end
  end
 ...

Advantages of using appname.daho.im:3000 over localhost:3000

Running rails server will start a local server that you can access via http://localhost:3000.

When you are working on multiple web apps, they will likely set cookies with generic names on localhost. This is annoying, since you will sign out your current user whenever you switch to another app.

A better way is to use our own daho.im service. All daho.im subdomains resolve to your local IP (127.0.0.1). That means you can use a different hostname for different apps, and you will stay logged in in each app:

http://foo-ap...

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 control Chromedriver using curl

Here is how to use Chromedriver without libraries like selenium-webdriver. This can be useful for debugging.

The following example visits a web page and reads the a headline's text contents.

  1. Create a session. You will get a JSON response containing lots of information about your Chrome session, including a sessionId. Use that to send any future commands to your chromedriver session.

    $ curl -XPOST http://localhost:9515/session -d '{"desiredCapabilities":{"browserName":"chrome"}}'
    {"sessionId":"your-session-id-here","sta...
    

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

Cucumber: How to avoid VCR errors for unused requests in pending tests

When you have a pending Cucumber step (or feature) that also uses an existing VCR cassette, your pending test may fail at the very end with an error like this:

There are unused HTTP interactions left in the cassette:
  - [get ...] => [200 ...]
  - [get ...] => [200 ...] (VCR::Errors::UnusedHTTPInteractionError)

The error happens because your VCR is configured to complain about cassettes that contain extra requests which your test did not use. This is often a good configuration.
If you do not want to change your whole test suite...

Rails and Postgres: How to test if your index is used as expected

This is a small example on how you can check if your Postgres index can be used by a specific query in you Rails application. For more complex execution plans it might still be a good idea to use the same path of proof.

1. Identify the query your application produces

query = User.order(:last_name, :created_at).to_sql
puts query
# => SELECT "users".* FROM "users" ORDER BY "users"."last_name" ASC, "users"."created_at" ASC

2. Add an index in your migration and migrate

add_index :users, [:last_name, :created_at]...

How to use cookies with curl

When making requests using curl, no cookies are sent or stored by default.
However, you can tell curl to re-use cookies received earlier (or forge your own cookies).

There are 2 command line switches you need to use:

  • -c will write cookies to a given file
  • -b will read cookies from a given file

Example

The remote server sets a "foo" cookie to value "bar". We tell curl to store them to a file at /tmp/cookies using the -c switch.

$ curl -c /tmp/cookies http://httpbin.org/cookies/set?foo=bar

You may look at the file, ...

Jasmine: Mocking API requests in an Angular service spec

Situation: You want to write a spec for a function inside an Angular service. This function at some point makes an API request and acts upon response. Notably, your Angular app employs uiRouter, although it is not used nor actually required for this test.

Working test setup

# Capitalized expressions are intended to be replaced with YOUR values

describe 'SERVICE', ->

  beforeEach ->
    module 'MODULE'

    module ($urlRouterProvider) ->
      # Prevent uiRouter's initialization, i.e. do not sync the current URL
      # with its r...

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 (preferred)

Modern browsers can hyphenate natively. Use the hyphens CSS property:

hyphens: auto

There is also hyphens: none (disable hyphenations even at &shy; entities) and hyphens: manual (hy...

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

Inspecting Angular 1.x UI Router

If your Angular app has some decent complexity, it will not be easy to use UI Router straight away. Here are some hints on how to get around.

Accessing the UI Router $state service from the browser console

$state = angular.element(document.body).injector().get('$state'): Retrieves the Angular injector and asks it for the $state service.

Inspecting the current state

$state.current

Inspecting params of the current state

$state.params

Turning off VCR when stubbing with Webmock

Sometimes when working with VCR you still want to use webmock. Since VCR hooks into webmock and fails when an unknown request is happening, you have to turn it off in order to use webmock like you are used to. Here is how to do this in rspec.

RSpec.configure do |config|
  config.around do | example |
    if example.metadata[:turn_off_vcr]
      VCR.turn_off!
      example.run
      VCR.turn_on!
    else
      example.run
    end
  end
end

Webservice to test and inspect requests

Requestb.in is a webservice that gives you a temporary URL you can use to test request. The page will automatically record and display the latest web request made to it.

Spreewald: Content-Disposition not set when testing a download's filename

Precondition

  • You are not using javascript tests
  • The file is served from a public folder (not via controller)

Problem description

If you deliver files from a public folder it might be that the Content-Disposition header is not set. That's why the following spreewald step might raise an error:

Then I should get a download with filename "..."
expected: /filename="some.pdf"$/
     got: nil (using =~) (RSpec::Expectations::ExpectationNotMetError)

Solution

One solution...

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