Ruby object equality
TLDR
if you define a equality method for a class you must also implement
def hash.
Ruby has a lot of methods that have to do something with equality, like ==, ===, eql?, equal?. This card should help you differentiate between those and give you hints on how to implement your own equality methods in a safe manner.
Differences between the methods
for everyday use: ==
When you compare two objects in ruby, you most often see the use of foo == bar. By default the == operator inherits from Object and is impl...
Fixing Yarn 1 error "unexpected end of file"
Our CI setup frequently sees this error while running yarn install:
yarn install v1.22.19
[1/4] Resolving packages...
[2/4] Fetching packages...
error An unexpected error occurred: "https://registry.yarnpkg.com/typescript/-/typescript-3.9.7.tgz: unexpected end of file".
info If you think this is a bug, please open a bug report with the information provided in "/builds/projects/foo-app/yarn-error.log".
info Visit https://yarnpkg.com/en/docs/cli/install for documentation about this command.
This error is caused by [Yarn not retryin...
How to read the current breakpoint tier(s) in JavaScript
To read the current breakpoint tier in JavaScript, employ this CSS:
:root {
--current-breakpoint-tier: xs;
@media (min-width: $screen-sm-min) {
--current-breakpoint-tier: sm;
}
@media (min-width: $screen-md-min) {
--current-breakpoint-tier: md;
}
@media (min-width: $screen-lg-min) {
--current-breakpoint-tier: lg;
}
@media (min-width: $screen-xl-min) {
--current-breakpoint-tier: xl;
}
@media (min-width: $screen-xxl-min) {
--current-breakpoint-tier: xxl;
}
}
Then use this JavaScript:
How to develop designs for an enterprise customer
Usually, design development starts with drafts, sketches and prototypes. These are reviewed, refined and iterated until the final design is ready – shiny, accepted and ready for implementation. I believe this works well when you get to work with the final decider in person.
However, this approach is not successful when the customer has complex internal structures ("Enterprise"). While the drafts and iterations might be all approved by the department you're working directly with ("Fachbereich"), deciders further up the hierarchy (the CEO, po...
Rails: Passing array values to tag helpers like link_to
From at least Rails 4, the ActionView tag helper turns Array values of HTML options into a single space-separated string.
This means you can pass an array to :class options:
extra_classes = %w[one two]
= link_to 'Dashboard', root_path, class: ['btn', 'btn-primary', *extra_classes]
=> <a href="/" class="btn btn-primary one two">Dashboad</a>
= content_tag 'div', 'Hello World', class: %w[alert alert-info]
=> <div class="alert alert-info">Hello World</div>...
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
Heads up: JavaScript does not like big numbers
In a JavaScript console, type this:
> 9112347935156469760
9112347935156470000
Ooops. And that's not a float!
This occurs because JavaScript uses double precision floats to store numbers.
So according to IEEE floating point definition only numbers between -(2^53 - 1) (-9007199254740991) and 2^53 - 1 (9007199254740991) can safely be represented in JavaScript.
Note that ECMAScript 6 will probably also offer [Number.MAX_SAFE_INTEGER](https://developer.mozilla.org/en-US/docs/W...
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...
Rails: Including HTML in your i18n locales
TL;DR
Append your locale keys with _html to have them marked as
html_safeand translate them with= t('.text_html').
When you're localizing a Rails application, sometimes there is this urge to include a little HTML. Be it some localized link, or a set of <em> tags, you'd like to have it included in the locale file. Example:
# Locale file
en:
page:
text: 'Please visit our <a href="https://www.corporate.com/en">corporate website</a> to learn more about <em>the corporation</em>.'
# HAML
= t('.text')
# D...
How to create giant memory leaks in AngularJS (and other client-side JavaScript)
This guide shows how to create an AngularJS application that consumes more and more memory until, eventually, the browser process crashes on your users.
Although this guide has been written for Angular 1 originally, most of the advice is relevant for all client-side JavaScript code.
How to observe memory consumption
To inspect the amount of memory consumed by your Javascripts in Chrome:
- Open an incognito window
- Open the page you want to inspect
- Press
Shift + ESCto see a list of Chrome processes...
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.
Beware: Many browsers define window.event
Some browsers define window.event, which will return a copy of the "current" event. However, this is not defined by the W3C. Most importantly, Firefox does not support it, neither do recent versions of IE.
You should completely avoid accessing the global event and instead pass the event object around that the event handler receives. The easiest protection against accidentally accessing window.event is just never naming the event event, but e or evnt or so.
function brokenInFirefox() {
event.preventDefault(); // Break...
Heads up: network requests `Kernel#open` are not mocked with VCR
We usually rely on VCR and WebMock to prevent any real network connection when running our unit tests.
This is not entirely true: They are both limited to a set of HTTP libraries listed below (as of 2022). Direct calls to Kernel#open or OpenURI#open_uri are not mocked and will trigger real network requests even in tests. This might bite you e.g. in [older versions of CarrierWave](https://github.com/carrierwaveuploader/carrierwave/blob/0.11-stable/lib/carrierwave/upl...
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]
...
How to update a single gem conservatively
The problem
Calling bundle update GEMNAME will update a lot more gems than you think. E.g. when you do this:
bundle update cucumber-rails
... you might think this will only update cucumber-rails. But it actually updates cucumber-rails and all of its dependencies. This will explode in your face when one of these dependencies release a new version with breaking API changes. Which is all the time.
In the example above updating cucumber-rails will give you Capybara 2.0 (because capybara is a dependency of `cucumber-rail...
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...