nvm: Setting a default Node.js version
To set a default Node version for new shells, use nvm alias default <VERSION>
:
nvm alias default 1.2.3
I like to use the most recent LTS version as my default:
nvm alias default lts/erbium
Jasmine: Test that an object is an instance of a given class
To test that an object was constructed by a given constructor function, use jasmine.any(Klass)
:
describe('plus()', function() {
it ('returns a number', function() {
let result = plus(1, 2)
expect(result).toEqual(jasmine.any(Number))
})
})
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...
Defining "partials" in pure HTML without additional rendering helpers
A while ago I tweeted a thread about how a small JavaScript snippet, one that can fit in a single tweet in fact, can be used to allow defining custom elements purely in HTML. This post will expand on the idea, show how the snippet works, and argue for why you might want to actually use this.
A nice trick that lets you define "partials" in HTML without any additional rendering technology on the server or client.
Bookmarklet to generate a Pivotal Tracker story from Zammad Ticket
This is a bookmarklet you can add to Chrome or Firefox which will allow you to create a story in Pivotal Tracker from a Zammad ticket. This might come in handy when creating stories for SWAT Teams.
But first you will have to set two variables in the script below:
-
pt_project_id
: the ID of the Pivotal Tracker Project you want to add stories to. This can be found as part of the URL of the project (https://www.pivotaltracker.com/n/projects/<pt_project_id>
) -
pt_token
: the Pivotal Tracker token used for authentication. Can be found in y...
Guideline for moving from jQuery to vanilla JavaScript
jQuery is still a useful and pragmatic library, but chances are increasingly that you’re not dependent on using it in your projects to accomplish basic tasks like selecting elements, styling them, animating them, and fetching data—things that jQuery was great at. With broad browser support of ES6 (over 96% at the time of writing), now is probably a good time to move away from jQuery.
[Practical and clear reference with the most common jQuery patterns and their equivalent translations in vanilla JS](https://tobiasahlin.com/blog/move-from-j...
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...
WProofreader: How to manually enable for a WYSIWYG editor instance (with CKeditor 4 example)
WProofreader is a spelling and grammar checking tool that integrates with textareas and numerous WYSIWYG editors.
While it usually activates automatically, depending on your application, it may fail to boot.
In my case, an application was using CKEditor 4 and programmatically activated CKEditor. WProofreader's autoSearch
logic failed to hook into that for some reason.
You can choose not to use its autoSearch
feature, but explicitly enable WProofreader.
Here is a guide for CKEditor 4. It should apply to other WYSIWYG editors as well.
1...
Bash script to list commits by Pivotal Tracker ID
The main benefit of our convention to prefix commits by their corresponding Pivotal Tracker ID is that we can easily detect commits that belong to the same story. You can either do that manually or use the bash script below by copying it somewhere to your .bashrc
.
# Usage: ptcommits 123456
function ptcommits {
if test "$1"
then
local PTID=$(echo "$1" | grep "[0-9]*" -o) # Allow URLs
git log --onel...
Using CSS transitions
CSS transitions are a simple animation framework that is built right into browsers. No need for Javascript here. They're supported by all browsers.
Basic usage
Transitions are used to animate the path between to property values. For example, to let the text color fade from red to green on hover, the following SASS is used (shorthand syntax):
.element
color: red
transition: color .1s
&:hover
color: green
This tells the browser "whenever the color
of an .element
changes...
DOM API for jQuery users
General hints on the DOM
- the root of the DOM is
document
- custom elements inherit from
HTMLElement
. They need a-
(dash) in their name, e.g.<notification-box>
. - event listeners don't have event delegation à la
.on('click', cssSelector, handler)
Comparison
Action | jQuery | DOM API equivalent |
---|---|---|
Find descendant(s) by CSS selector | .find(selector) |
one: `.querySelector(selecto... |
Unpoly: Showing the better_errors page when Rails raises an error
When an AJAX request raises an exception on the server, Rails will show a minimal error page with only basic information. Because all Unpoly updates work using AJAX requests, you won't get the more detailled better_errors page with the interactive REPL.
Below is an event listener that automatically repeats the request as a full-page load if your development error shows an error page. This means you get...
Webpack(er): A primer
webpack is a very powerful asset bundler written in node.js to bundle (ES6) JavaScript modules, stylesheets, images, and other assets for consumption in browsers.
Webpacker is a wrapper around webpack that handles integration with Rails.
This is a short introduction.
Installation
If you haven't already, you need to install node.js and Yarn.
Then, put
gem 'webpacker', '~> 4.x' # check if 4.x is still cu...
RubyMine: Efficiently filtering results in the "Finder" overlay
RubyMine comes with a nice way to grep through your project's files: The finder (ctrl + shift + f
). Don't be discouraged about the notice 100+ matches in n+ files
if your searched keyword is too general or widely used in your project.
RubyMine comes with a few ways to narrow down the resulting list, don't hesitate to apply those filters to speed up your search. Your keybinding might vary based on your personal settings.
File mask (alt + k
)
If you already know the file extension of your ...
Adding Jasmine JavaScript specs to a Webpack(er) project
The goal is to get Jasmine specs running in a Rails project using Webpacker, with the browser based test runner. Should be easily adaptable to a pure Webpack setup.
Step 1: Install Jasmine
yarn add jasmine-core
Step 2: Add two separate packs
Since we do not want to mix Jasmine into our regular Javascript, we will create two additional packs. The first only contains Jasmine and the test runner. The second will contain our normal application code and the specs themselves.
We cannot...
Unpoly: Testing values for presence or blankness
In Ruby on Rails, all objects have a useful blank?
method. It returns true for nil
but also for empty strings or empty arrays. There is also a universal method present?
which returns true
for all values that are not blank?
.
In JavaScript you need to roll your own implementation of blank?
and present?
.
If your application uses [Unpoly](...
Documenting your Rails project's Node.js version in .nvmrc
Not all versions of Node.js are compatible with each other. Also npm packages may require a minimum or maximum version of Node.js. We use nvm on our development PCs so we can operate multiple versions of Node.js in parallel.
To make sure that all developers use a compatible version of Node.js, your Rails project should declare the required Node.js in a file called .nvmrc
.
When a .nvmrc
exists, developers can cd
in your project directory and activate...
Events triggered by jQuery cannot be observed by native event listeners
jQuery has a function $.fn.trigger()
. You can use it to dispatch an event on a jQuery object:
let $element = $('.foo')
$element.trigger('change')
A caveat is that such an event will be received by jQuery event listeners, but not by native event listeners:
let $element = $('.foo')
$element.on('change', event => console.log('I will be called'))
$element[0].addEventListener('change', event => console.log("I WON'T be called"))
$element.trigger('change')
This is not an issue when your entire app is ...
Migrating from CoffeeScript to ES6
It is quite easy to migrate from CoffeeScript to ES6. You can use decaffeinate to convert your CoffeeScript source to modern JavaScript.
Install decaffeinate globally:
npm install -g decaffeinate
Call decaffeinate
on each .coffee
file, relaxing some options to get the most modern (and concise) JS:
decaffeinate file.coffee --use-cs2 --loose --optional-chaining --logical-assignment
Tip
If you use Babel and see errors while decaffeinati...
Webpack: Automatically generating an icon font from .svg files
Over the years we have tried several solution to have vector icons in our applications. There are many ways to achieve this, from SVGs inlined into the HTML, SVGs inlined in CSS, JavaScript-based solutions, to icon fonts.
Out of all these options, the tried and true icon font seems to have the most advantages, since
- icon fonts are supported everywhere
- they perform well and require no JavaScript at all
- their icons align nicely with text
- their icons automatically inherit color and size of the surrounding text
The big issue used to b...
Webpack: How to split your bundles
To keep JavaScript sources small, it can sometimes make sense to split your webpack bundles. For example, if your website uses some large JavaScript library – say TinyMCE – which is only required on some selected pages, it makes sense to only load that library when necessary.
In modern webpack this is easily doable by using the asynchronous import
function.
Say we have an unpoly compiler that sets up TinyMCE like this (code is somewhat simplified):
// TinyMCE as part of the main bundle!
import tinymce from 'tinymce/tinymce'
// U...
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...
Webpack(er): Analyze the size of your JavaScript components
We're always striving towards keeping our website's JavaScript as small as possible.
If you're using webpack(er), you can use the webpack-bundle-analyzer plugin to get a good overview, which of your JavaScript modules take up how much space, and where you can optimize.
To use it, add it via npm or yarn
yarn add webpack-bundle-analyzer
Then add this to your environment.js
:
// Uncomment this code to show statistics of bundle sizes. Generated file will automatically...
SameSite cookies
TL;DR Most web applications do not require action on this. SameSite=None
(old browser default) will continue to work, and SameSite=Lax
(new Chrome default, gradually rolled out) is an even better default for cookies. Set SameSite=Strict
only for extra security in special cases (see below). If your application is rendered in an iframe (e.g. a video player or some news stream), you need to configure its relevant cookies as SameSite=None
.
The SameSite
cookie attribute targets **c...