Self-expiring URLs with Apache
When delivering non-public uploaded files (images, documents etc), one has to decide whether and how to do authorization. The usual approaches are:
- Using
send_filewith a regular controller. This is secure, but potentially slow, especially for large collections of images. - Using unguessable URLs. This is fast (because Apache can deliver assets without going through Rails), but less secure.
When going with the "unguessable URL" approach, it is possible to somewhat increase security by using expiring URLs. The idea is to encode the expi...
Carrierwave: always limit images to a reasonable size
Today's cameras create huge images, some beyond 50MB. Unless you need to offer this large files, you should always shrink uploaded files to a reasonable resolution.
class ImageUploader < CarrierWave::Uploader::Base
process resize_to_limit: [3000, 3000]
# ...
end
Don't mix Array#join and String#html_safe
You cannot use Array#join on an array of strings where some strings are html_safe and others are not. The result will be an unsafe string and will thus be escaped when rendered in a view:
unsafe_string = '<span>foo</span>'
safe_string = '<span>bar</span>'.html_safe
[unsafe_string, safe_string].join(' ') # will incorrectly render as '<span>foo</span><span&t;bar</span>'
Bad
The solution is not to call html_safe on the joined array and if you thought it would be, you [don't understand how XSS prot...
Jasmine: Mocking ESM imports
In a Jasmine spec you want to spy on a function that is imported by the code under test. This card explores various methods to achieve this.
Example
We are going to use the same example to demonstrate the different approaches of mocking an imported function.
We have a module 'lib' that exports a function hello():
// lib.js
function hello() {
console.log("hi world")
}
export hello
We have a second module 'client' that exports a function helloTwice(). All this does is call hello() ...
Heads up: RSpec-Mocks' #stub_const will define intermediate modules that have not been loaded yet
The issue: You are using stub_const to change a constant value for your test.
stub_const "SomeClass::CONST", 'test'
All of a sudden, tests fail with undefined method 'some_method' for #<SomeClass:0x00000000101433a8>.
The reason
When using stub_const before the Class containing the constant has been loaded, a module is automatically created with the name.
Since RSpec does no autoloading, it will create a SomeClass module by itself. This is arguably a good idea.
As a workaround, use stub_const in your Rails specs li...
BigDecimal arithmetic in Ruby
Ruby comes with a class BigDecimal which you can use for arbitrary precision arithmetic. You should use BigDecimal instead of Float whenever you care about rounding errors, e.g. whenever you are dealing with money.
You should remember these two rules when working with BigDecimal values:
- When you add or multiply a
BigDecimalwith anotherBigDecimal, the ...
sessionStorage: Per-window browser storage
All major browsers (IE8+, FF3.5+, Safari 4+, any Chrome) support sessionStorage, a JavaScript storage object that
- survives page reloads and browser restores,
- but is different per new tab/window (in contrast to
localStoragewhich is shared across all tabs).
MDN says:
The
sessionStorageobject is most useful for hanging on to temporary data that should be saved and restored if the browser is accidentally refreshed
Demo
Example usage:
Set default_url_options for entire Rails application
Instead of cobbling together default settings in several different places as the issues arise, you can define them application-wide.
subscript, superscript and line-heights
By default subscript (<sub></sub>) and superscript (<sup></sup>) tags are styled with vertical-align: sub, respectively vertical-align: super by most browsers.
However, without adaptations, this will probably break your line-heights.
A common suggestion is to style those two tags accordingly:
sup, sub {
vertical-align: baseline;
position: relative;
top: -0.4em; /* can be adapted according to preferences */
}
sub {
...
Linux: Running a single unique instance of command + arguments
run-one is a wrapper script that won't run the given command while another instance of it is running. Is brings several utility commands that offer similar behavior.
NAME
run-one - run just one instance at a time of some command and unique set of arguments (useful for cronjobs, eg)
SYNOPSIS
run-one COMMAND [ARGS]
run-this-one COMMAND [ARGS]
run-one-constantly COMMAND [ARGS]
keep-one-running COMMAND [ARGS]
run-one-until-success COMMAND [ARGS]
run-one-until-failure COMMAND [ARGS]
...
Destructors for async Unpoly compilers
Usually, Unpoly compiler destructors are returned from the compiler function.
However, when using async compiler functions, you can not register destructors via return.
This will not work:
up.compiler('my-example', async (element) => {
await something
return function onDestroy() {
// ...
}
})
Instead, use up.destructor:
up.compiler('my-example', async (element) => {
await something
u...
Bookmarklet: cards Markup Link Bookmarklet
The cards editor has a feature "Cite other card" to create links to other cards in the same deck as mardown links.
If you want to reference a card from a different deck, this bookmarklet might be useful:
javascript:(function () {
const doAlert = () => { alert("Maybe not a makandra card?") };
let cardsPathPattern = /(\/[\w-]+\/\d+)-.+/;
if (window.location.pathname.match(cardsPathPattern)) {
let currentPath = window.location.pathname.match(cardsPathPattern)[1];
let title = document.querySelector('h1.note--title')?.textCon...
Using path aliases in esbuild
In esbuild, you usually import other files using relative paths:
import './some-related-module'
import `../../utils/some-utility-module`
import `../../../css/some-css.sass`
This is totally fine if you import closely related files, but a bit clunky when you're trying to import some "global" module, like a utility module. When moving a file, your imports also need to change.
To get around this, esbuild support a mechanism first introduced in TypeScript called "path aliases". It works like this:
First, you create a file called `js...
Rails: Use STI in Migration
tl;dr
You should decouple migrations from models by embedding models into the migration. To use STI in this scenario you have to overwrite
find_sti_classandsti_name.
Tip
When possible, try to avoid STI in migrations by disabling it.
Example
Warning
This is more for the sake of I want to do it but I kno...
How to see how many inotify instances are used by each process
As a developer you may have many tools watching your project for changes: Your IDE, Webpack, Guard, etc. This is often done with an inotify watcher. If you have too many inotify instances you may run into limits of your operating system.
To find out which process is using them all up you can run:
sudo find /proc/*/fd/ -type l -lname "anon_inode:inotify" -printf "%hinfo/%f\n" | xargs grep -cE "^inotify" | column -t -s:
You will get a list like:
/proc/3753/fdinfo/7 1
/proc/3774/fdinfo/7 1
/proc/4034/fdinfo/12 14
/pr...
Capybara: Testing file downloads
Download buttons can be difficult to test, especially with Selenium. Depending on browser, user settings and response headers, one of three things can happen:
- The browser shows a "Save as..." dialog. Since it is a modal dialog, we can no longer communicate with the browser through Selenium.
- The browser automatically downloads the file without prompting the user. For the test it looks like nothing has happened.
- The browser shows a binary document in its own window, like a PDF. Capybara/Selenium freaks out because there is no HTML docum...
Diátaxis: A systematic framework for technical documentation authoring
The Diátaxis framework aims to solve the problem of structure in technical documentation. It adopts a systematic approach to understanding the needs of documentation users in their cycle of interaction with a product.
Diátaxis identifies four modes of documentation - tutorials, how-to guides, technical reference and explanation. It derives its structure from the relationship between them.
In Diátaxis, each of these modes (or types) answers to a different user need, fulfils a different purpose and requires a different appr...
Jasmine: Creating DOM elements efficiently
Jasmine specs for the frontend often need some DOM elements to work with. Because creating them is such a common task, we should have an efficient way to do it.
Let's say I need this HTML structure:
<ul type="square">
<li>item 1</li>
<li>item 2</li>
</ul>
This card compares various approaches to fabricating DOM elements for testing.
Constructing individual elements
While you can use standard DOM functions to individually create and append elements, this is extremely verbose:
let list = document.createElement('...
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...
Capybara: Execute asynchronous JavaScript
Capybara provides execute_script and evaluate_script to execute JavaScript code in a Selenium-controlled browser. This however is not a good solution for asynchronous JavaScript.
Enter evaluate_async_script, which allows you to execute some asynchronous code and wait until it finishes. There is a timeout of a couple of seconds, so it will not wait forever.
Use it like this:
page.evaluate_async_script(<<~JS)
let [done] = arguments
doSomethingAsynchronous().then(() => {
done() // call this to indicate we're done
})
J...
Fast Navigation Tips In Dev Tools
Hide/Show Drawer
You can press Escape to show/hide the drawer.
Fast Navigation with Command Menu
Use Ctrl + Shift + p to open Command Menu, where you can switch to different panels, toggle settings etc.
Reorder DevTools panels and Drawer tabs and combine them by moving them up and down to and from the Drawer.
Right click on any tab and then choose Move to top / Move to bottom
Change DevTools layout from auto to always horizontal or vertical.
Click on Settings > Preferences > Panel Layout, Choose between Horizontal / Ve...
Inspect and Debug CSS Flexbox and Grid Layouts by using the Layouts Tab in Dev Tools
tl;dr
In Chrome DevTools in the Layouts tab you have handy options to debug CSS Flexbox and Grid. Including:
- Display size and lines along with labels
- Changing their attributes
- Change how overlay is colored and fastly switch nested elements in the Elements panel
This guide will only cover some example gif recordings on how to use with Grid, since it's basically straight forward to apply this for Flexbox by yourself afterwards.
For this purpose a the link to documentation and a simple code pen have been added...
Do not use unitless zeros in CSS calc functions
While in CSS zero is usually referenced without specifying a unit (e.g. padding: 0), you must not use a unitless zero in calc functions.
You would probably not write something like calc(1rem + 0) yourself, but it might be the result of a CSS preprocessor (like Sass) or when using custom properties.
The following is invalid:
.example {
--extra-padding: 0;
padding: calc(1rem + var(--extra-padding));
}
That is simply because it is unsupported, as per docum...
How to choose keyboard shortcuts for web applications
One-line takeaways
- Don’t override native browser (or OS) shortcuts.
- Support standard shortcuts that don’t contradict the previous rule, and use one or two letter shortcuts for other actions.
- Always have a consistent system.
- Pay maximum attention to discoverability.