A simpler default controller implementation
Rails has always included a scaffold
script that generates a default controller implementation for you. Unfortunately that generated controller is unnecessarily verbose.
When we take over Rails projects from other teams, we often find that controllers are the unloved child, where annoying glue code has been paved over and over again, negotiating between request and model using implicit and convoluted protocols.
We prefer a different approach. We believe that among all the classes in a Rails project, controllers are some of the hardest to...
Controlling how your website appears on social media feeds
When a user shares your content, a snippet with title, image, link and description appears in her timeline. By default social networks will use the window title, the first image, the current URL and some random text snippet for this purpose. This is often not what you want.
Luckily Facebook, Twitter, etc. lets you control how your content appears in the activity streams. They even have agreed on a common format to do this: OpenGraph <meta>
tags that go into your HTML's <head>
:
<meta property="og:url" content="http://start.m...
Defining new elements for your HTML document
Browsers come with a set of built-in elements like <p>
or <input>
. When we need a new component not covered by that, we often build it from <div>
and <span>
tags. An alternative is to introduce a new element, like <my-element>
.
When a browser encounters an unknown element like <my-element>
, the browser will proceed to render <my-element>
's children. The visual rendering of your page will not be affected.
If you care about their HTML being valid, your new element should contain a dash character (-
) to mark it as a *custom el...
Updated: Git: removing feature branch on merge
- Resolve @{-1} to actual branch name. (Happens when merging "-".)
Creating a Rails application in a single file
Greg Molnar has written a neat article about creating a single-file Rails app.
This is not meant for production use but can be useful to try things out, e.g. when hunting down a bug or embedding a Rails app into the tests of a gem.
What you do is basically:
- Put everything (gems, application config, database migrations, models, controllers) into a single
.ru
file, likeapp.ru
. - Run it via
rackup app.ru
. (Hint: if your file is calledconfig.ru
, you can just run `rac...
Web development: Accepting a self-signed certificate in Google Chrome
Working with a self-signed certificate is much easier, when the browser accepts it.
Google Chrome
Warnings from chrome might not be accurate
Even though the certificate is working locally, chrome might still complain that the certificate is not valid and the connection is not secure while blotting out the "https" part of the url.
Accept a specific certificate
- Go to chrome://settings/certificates
- Under "Authorities", click "Import"
- Select the certificate file (.crt)
- Check "Trust this certificate for identi...
Debugging cucumber feature with javascript + firefox vnc
TL;DR Debugging problems with javascript errors in cucumber tests is sometimes easier in the browser. Run the test, stop at the problematic point (with Then pause
from Spreewald 1.7+) and open VNC for Firefox.
Features:
- It does not freeze your server like when you're using a debugger. (Compared to the
Then console
step) - It enables interacting with the server. (Compared to taking screenshots in Capybara)
- It is a faster alternat...
Cancelling event propagation
Within an event handler, there are multiple methods to cancel event propagation, each with different semantics.
-
event.preventDefault()
Only prevents the default browser behavior for the click, i.e. going to a different url or submitting a form.
When invoked on a
touchstart
event, this also prevents mouse events likeclick
to be triggered. -
event.stopPropagation()
Prevents the event from bubbling up the DOM.
-
`event.st...
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 ...
Local development with SSL and Puma
Sometimes the need arises for SSL in local development. We have guides for different webservers, this one is for puma.
-
make sure mkcert is installed
-
create an SSL certificate for localhost with mkcert:
$ mkcert-v1.4.4-linux-amd64 localhost
Created a new local CA 💥
...
- use the certificate in the Puma config
config/puma.rb
:
localhost_key = "#{File.join('localhos...
Yarn: Use yarn-deduplicate to cleanup your yarn.lock
Note
Use
yarn dedupe
in Yarn v2+: https://yarnpkg.com/cli/dedupe
This package only works with Yarn v1. Yarn v2 supports package deduplication natively!
A duplicate package is when two dependencies are resolved to a different version, even when a single version matches the range specified in the dependencies. See the Deduplication strategies section for a few examples.
Yarn is stupid, so it can happen that there are several version of the same package in your bundle, although one would fulf...
Rails: Do not load frameworks you don't need
Rails is split into a large number of (sub-) frameworks.
The most important and central of those are
- activesupport (extends the Ruby standard library)
- activerecord / activemodel (ORM for Rails)
- actionview / actionpack (controller / views)
- actionmailer (sends mails)
However, there are also some more situational frameworks included, such as
- actioncable (real time communications using websockets)
- actionmailbox (receives mails)
- actiontext (support for WYSIWYG text editor)
- activejob (background jobs)
- activestorage (file uplo...
CSS: :is() pseudo selector
tl;dr
The
:is()
pseudo selector - specificity of its most specific argument - matches against a comma-separated list of selectors.
Example
Compound selectors like ...
.datepicker .prev, .datepicker .next, .datepicker .switch
padding-bottom: 1rem
ul li, ol li
list-style-type: none
can be simplified by using the :is()
pseudo selector ...
.datepicker :is(.prev, .next, .switch)
padding-bottom: 1rem
:is(ul, ol) li
list-style-type: none
Hint
The specificity of
:is()
is equals t...
CSS: :where() pseudo selector
tl;dr
The
:where()
pseudo selector - zero specificity - matches against a comma-separated list of selectors.
Example
Compound selectors like ...
.datepicker .prev, .datepicker .next, .datepicker .switch
padding-bottom: 1rem
ul li, ol li
list-style-type: none
can be simplified by using the :where()
pseudo selector ...
.datepicker :where(.prev, .next, .switch)
padding-bottom: 1rem
:where(ul, ol) li
list-style-type: none
Hint
The specificity of
:where()
is always zero!I...
Don't sum up columns with + in a SQL query if NULL-values can be present.
Don't sum up columns with +
in a sql-query if NULL
-Values can be present.
MySQL and PostgreSQL cannot sum up NULL
values with the +
value. The sum value will be NULL
.
MySQL:
mysql> select 1 + 2 + 3;
+-----------+
| 1 + 2 + 3 |
+-----------+
| 6 |
+-----------+
1 row in set (0,00 sec)
mysql> select 1 + NULL + 3;
+--------------+
| 1 + NULL + 3 |
+--------------+
| NULL |
+--------------+
1 row in set (0,00 sec)
Postgres:
test_database=# select 1 + 2 + 3;
?column?
----------
6
(1 row)
t...
How to use Ubuntu in English, but still show German formats
If you want to have an English Ubuntu UI, but still see dates, money amounts, paper formats, etc. in German formats, you can fine-tune your /etc/default/locale
like this:
LANG="en_US.UTF-8"
LC_CTYPE="de_DE.UTF-8"
LC_NUMERIC="de_DE.UTF-8"
LC_TIME="de_DE.UTF-8"
LC_COLLATE="de_DE.UTF-8"
LC_MONETARY="de_DE.UTF-8"
LC_PAPER="de_DE.UTF-8"
LC_NAME="de_DE.UTF-8"
LC_ADDRESS="de_DE.UTF-8"
LC_TELEPHONE="de_DE.UTF-8"
LC_MEASUREMENT="de_DE.UTF-8"
LC_IDENTIFICATION="de_DE.UTF-8"
Make sure you have both en...
Rails: How to check if a certain validation failed
If validations failed for a record, and you want to find out if a specific validation failed, you can leverage ActiveModel's error objects.
You rarely need this in application code (you usually just want to print error messages), but it can be useful when writing tests.
As an example, consider the following model which uses two validations on the email
attribute.
class User < ApplicationRecord
validates :email, presence: true, uniqueness: true
end
Accessing errors
Let's assume we have a blank user:
user = Us...
Generating and streaming ZIP archives on the fly
When your Rails application offers downloading a bunch of files as ZIP archive, you basically have two options:
- Write a ZIP file to disk and send it as a download to the user.
- Generate a ZIP archive on the fly while streaming it in chunks to the user.
This card is about option 2, and it is actually fairly easy to set up.
We are using this to generate ZIP archives with lots of files (500k+) on the fly, and it works like a charm.
Why stream downloads?
Offering downloads of large archives can be cumbersome:
- It takes time to b...
Rails: Fixing the memory leak / performance issues in prepend_view_path
Recently we detected a memory leak in one of our applications. Hunting it down, we found that the memory leak was located in Rails' #prepend_view_path
. It occurs when the instance method prepend_view_path
is called in each request, which is a common thing in a multi-tenant application.
On top of leaking memory, it also causes a performance hit, since templates rendered using the prepended view path will not be cached and compiled anew on each request.
This is not a new memory leak. It was [first reported in in 2014](https://github.com/...
Showing a custom maintenance page while deploying
Note
The maintenance mode is enabled on all application server as soon as the file
/public/system/maintenance.html
is present.
Installation
Add this line to your application's Gemfile:
gem 'capistrano', '~> 3.0'
gem 'capistrano-maintenance', '~> 1.0'
Add this line to you application's Capfile:
require 'capistrano/maintenance'
Enable task
Present a maintenance page to visitors. Disables your application's web interface by writing a #{maintenance_basename}.html
file to each web server. The servers m...
Cucumber pitfall: "Around" does not apply to your "Background" steps
Around
will not happen until after a feature's Background
has been processed. Use Before
and After
to avoid that.
Details
Consider this Cucumber feature file:
Feature: Something that needs to be tested
Background:
Given a user
And I sign in
Scenario: Sign out
When I sign out
Then I should see "Signed out"
Scenario: Something else
# ...
Now, assume you have these step definitions:
Around do
puts "** Around: before yield"
...
Valuable Chrome DevTools Shortcuts
In the DevTools settings, there's a "Shortcuts" section. Found these keyboard shortcuts there:
General
ESC
Toggle drawer
CTRL + ~ or CTRL + `
Show console in drawer
Styles
SHIFT + up/down
Change number by 10
CTRL + up/down
Change number by 100
Elements
H
Toggle "visibility:hidden!important" (useful when debugging page repaint times)
CTRL + hover above element in the DOM list
Don't show the yellow dimensions tooltip (useful when the tooltip covers just the area you need to see).
Drag...
A short overview of common design patterns implemented within Rails
The linked content includes a few design patterns implemented with Ruby on Rails.
What is the card indented to achieve?
- You can use the pattern names for code reviews, so all parties know with only a few words which change is requested. Example: "Please use a form object here"
- You can learn about new code patterns
- You should read the sections "Advantages of using design patterns" and "Disadvantages of using design patterns in a wrong way", since design patterns do not replace good code
Included Design Patterns: Service, Value objec...