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 dedupein 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...
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...
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.htmlis 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...
makandra tech survey - results
These are the results of the "personal tech stack survey". I've included only the most popular mentions, maybe it can help you find one or two useful tools for your own usage.
Desktop environment
pie title Desktop environment
"Gnome" : 16
"i3": 2
"sway": 2
"awesome": 1
"bspwm": 1
"mate": 1
"xfce": 1
Gnome dominates (unsuprising, it's the Ubuntu default), but quite a few people use tiling window managers, most popular i3 and the mostly i3-compatible [sway](https://swaywm....
Fix for mysql2 error "Incorrect MySQL client library version! This gem was compiled for x.x.x but the client library is y.y.y."
This should be fixed in the latest LTS-branches of our mysql2 fork, 0.2.x-lts and 0.3.x-lts.
Use
gem 'mysql2', git: 'https://github.com/makandra/mysql2', branch: '0.2.x-lts' # for Rails 2.x
gem 'mysql2', git: 'https://github.com/makandra/mysql2', branch: '0.3.x-lts' # for Rails 3.x
in your Gemfile, and do a
bundle update mysql2
Background
mysql2 used to check that the client library used at runtime actually matches the one it was compiled against. However, at least on Ubunt...
Reading the Rails session hash from a Rack middleware
To read the Rails session from a Rack middleware, use env['rack.session']. It's an ActionDispatch::Request::Session object.
class MyMiddlware
def initialize(app)
@app = app
end
def call(env)
status, headers, body = @app.call(env)
session = env['rack.session']
Rails.logger.info("Value of session['foo'] is: " + session['foo'].inspect)
[status, headers, body]
end
end
You may not be able to write to the session this way (I haven't tested this).
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...
Use a global .gitignore file to ignore stuff from your machine
Sometimes you want git to ignore certain files that appear on your machine. You can do this in 3 ways:
- Per project, in the project's
.gitignorefile - Per project, in a local exclude file
- Globally on your machine
Downsides of per-project .gitignore entries
While it might be tempting to set it per project (other devs might benefit from it), you
- need to do it each time for every project
- "pollute" a project's
.gitignorefile with stuff...
Running "bundle update" without arguments might break your application
Calling bundle update (without arguments) updates all your gems at once. Given that many gems don't care about stable APIs, this might break your application in a million ways.
To stay sane, update your gems using the applicable way below:
Projects in active development
Update the entire bundle regularily (e.g. once a week). This ensures that your libraries are up-to-date while it's easy to spot major version bumps which may break the app.
Projects that have not been updated in a while
- [Update a single gem conservatively](htt...
Calling a helper method with the same name as your current partial
Partials always define a local variable with the same name as themselves. E.g. when you are in _recent_users.html.erb, a local variable recent_users will be defined and overshadow any helper method that is also called recent_users().
If you would like to use a helper method recent_users() in a partial _recent_users.html.erb you can say this in the partial template:
<% recent_users = self.recent_users() %>
<% recent_users.each do |user| %>
...
<% end %>
Your database tables should always have timestamps
Whenever you create a table from a database migration, remember to add updated_at and created_at timestamps to that table. Without those timestamps, investigating future bug reports will be hell. Always have timestamps.
Adding timestamps to new tables
When you create a table using create_table, you can add timestamps by using the timestamps shortcut:
class CreateEpisode < ActiveRecord::Migration
def change
create_table :episodes do |t|
t.string :name
t.timestam...
CSS: The inset CSS shorthand
The inset CSS property is a shorthand that corresponds to the top, right, bottom, and/or left properties. It has the same multi-value syntax of the margin shorthand.
Example
<div class="outer">
<div class="inner">
Some text
</div>
</div>
.outer {
background-color: cyan;
position: relative;
width: 500px;
height: 500px;
}
Top, right, bottom and left
https://jsfiddle.net/jqx68wem/
.inner {
background-color: darkCyan;
position: absolute;
top: 10px;
right: 10px;
bottom: 10p...
Fix: esbuild assets are missing after capistrano deploy
Issue: You have an app using jsbundling-rails and esbuild. After deploy, the assets built by esbuild are missing in public/assets.
Solution: Add app/builds to your git repo (by adding a app/builds/.keep file).
Something in sprockets is caching paths and refuses to accept files in "unknown" locations.
Fix PNG colors in IE, old Safaris and new Firefoxes
Some browsers render PNG images with color profiles and other shenanigans, some don't.
The cleanest way to have consistent colors across browsers is to convert all your images to a standard color profile, strip the image's original profile and attach the standard profile.
If you can't be bothered to convert color profiles, a quicker (but less effective) method is to remove some PNG chunks from your files.
With Geordi
[Geordi](https://git...
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...