Colcade is a lightweight Masonry alternative
Masonry is a famous library to dynamically arrange a grid of items that have different aspect ratio, like horizontal and vertical images.
Colcade is an alternative masonry-layouting library, developed by the same developer, but with a more modern approach.
It is said to have better performance while being smaller and having no dependencies. It automagically detects jQuery and defines a jQuery initializer, if present.
However, it offers [a few less features](https:...
Heads up: Sidekiq per default silently fails when retries are exhausted!
For Sidekiq to be able to retry your jobs it has to be able to catch errors that occur while a job is executed.
Per default, Sidekiq will not raise / notify you if the retry count is exhausted. It will only copy the job to the dead queue (see wiki).
If you want to get notified, you have to implement it in your worker explicitly with a sidekiq_retries_exhausted-block, e.g. like this:
class DownloadWorker
include Sidekiq::Worker
# Import jobs are retried a few time...
A quick introduction to CORS
Background
Cross-Site Request Forgery (CSRF) is an attack pattern for websites. A CSRF attack is usually relevant in a browser context, where state is kept for multiple domains (as opposed to independent requests made e.g. with curl). The most common example is authentication via cookies. If a script on https://example.com made requests to https://docs.google.com, the browser would send all cookies for docs.google.com along, effectively given the script access to anythin...
Raising JavaScript errors in Ruby E2E tests (RSpec, Cucumber)
A JavaScript error in an E2E test with Selenium will not cause your test to fail. This may cause you to miss errors in your frontend code.
Using the BrowserConsole helper below you can check your browser's error console from your E2E tests.
The following will raise BrowserConsole::ErrorsPresent if there is an error on the browser console:
BrowserConsole.assert_no_errors!
Ignoring errors
You can ignore errors by their exact message:
BrowserConsole.ignore('Browser is burning')
You can ignore errors with me...
Pretty commit messages via geordi
Geordi provides a pretty neat way to generate beautiful commit messages according to your stories in Linear:
geordi commit
Geordi reads from a .geordi.yml file inside your repo and connects to Linear to list started and finished stories with their title. Choosing one of them generates a commit message including id and title from Linear app and a link to the original issue. For example:
[VW-1337] CRUD Users
Issue: https://linear.app/makandra/issue/VW-1337/crud-user...
Geordi: How to rerun failed features
Geordi's cucumber command has a --rerun option that reruns failing tests the given number of times. Usage:
geordi cucumber path/to/features --rerun=2
geordi cucumber path/to/features -r2
Background and how to rerun manually
Cucumber will save a file tmp/parallel_cucumber_failures.log containing the filenames and line number of the failed scenarios after a full test run. Normally you can say cucumber -p rerun (rerun is a profile defined by default in config/cucumber.yml) to rerun all failed scenarios.
Here are a few al...
HTML file inputs support picking directories
HTML's <input type="file"> accepts a single file. You can allow multiple files via <input type="file" multiple>.
But sometimes, selecting multiple files is not enough and can be cumbersome for the user. Enter webkitdirectory:
<input type="file" webkitdirectory multiple>
Using webkitdirectory switches the browser's file picker to select a directory. All files inside that directory, and inside any nested subdirectories, will be selected for the file input.
This can be useful when users want to upload all files from a nested dire...
Rails < 5: How to get after_commit callbacks fired in tests
If you use transactional_fixtures or the database_cleaner gem with strategy :transaction, after_commit callbacks will not be fired in your tests.
Rails 5+
Rails 5 has a fix for this issue and no further action is needed.
Rails 3, Rails 4
Add the gem test_after_commit to your test group in the Gemfile and you are done. You don't need to change the database strategy to deletion (wh...
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...
Do not use "flex: 1" or "flex-basis: 0" inside "flex-direction: column" when you need to support IE11
Flexbox is awesome. Most of it even works in IE11, but flex: 1 won't work reliably in Internet Explorer.
This it because implicitly sets flex-basis: 0 which IE fails to support properly.
Example
Consider the following HTML and CSS.
<div class="container">
<div class="child">
foo
</div>
<div class="bar">
bar
</div>
</div>
.container {
display: flex;
flex-direction: column;
}
.child {
flex: 1;
}
See it in action at Plunker.
Background
...
The Definitive Guide to Cookie Domains
Restricting access to cookies is essential for security in many web apps. For example, the session ID, the secret token used to identify a particular session, is typically stored in a cookie. Cookies have several important settings. Previously, I discussed the secure flag. This time, let’s dive into the cookie domain.
The cookie domain is an important security feature, probably even more important than the secure flag. It tells the browser that this cookie must only be sent to matching domains. Matching however, can happen in several w...
Ruby: How to use prepend for cleaner monkey patches
Let's say you have a gem which has the following module:
# within the imaginary super gem
module SuperClient
def self.foo
'Foo'
end
def bar
'Bar'
end
end
For reasons you need to override foo and bar.
Keep in mind: Your code quality is getting worse with with each prepend (other developers are not happy to find many library extensions). Try to avoid it if possible.
- Add a
lib/ext/super_client.rbto your project (see How to organize monkey patches in Ruby on Rails projects)
2...
How to write a good changelog
We want to keep a changelog for all gems we maintain. There are some good practices for writing a changelog that adds value, please stick to these.
- Add a notice to the README's contribute section about the changelog
- For every release update the changelog
- Note the date format yyyy-mm-tt
What is a changelog?
A changelog is a file which contains a curated, chronologically ordered list of notable changes for each version of a project.
Why keep a changelog?
To make it easier for users and...
HTML forms with multiple submit buttons
Most forms have a single submit button that will save the record when pressed.
Sometimes a form needs additional submit buttons like "accept" or "reject". Such buttons usually attempt a state transition while updating the record.
To process a form with multiple buttons, your server-side code will need to know which button was pressed. To do so you can give each submit button a different [formaction] attribute. This will override the ...
Best practices: Large data migrations from legacy systems
Migrating data from a legacy into a new system can be a surprisingly large undertaking. We have done this a few times. While there are significant differences from project to project, we do have a list of general suggestions.
Before you start, talk to someone who has done it before, and read the following hints:
Understand the old system
Before any technical considerations, you need to understand the old system as best as possible. If feasible, do not only look at its API, or database, or frontend, but let a user of the old system sho...
How to make changes to a Ruby gem (as a Rails developer)
At makandra, we've built a few gems over the years. Some of these are quite popular: spreewald (> 1M downloads), active_type (> 1M downloads), and geordi (> 200k downloads)
Developing a Ruby gem is different from developing Rails applications, with the biggest difference: there is no Rails. This means:
- no defined structure (neither for code nor directories)
- no autoloading of classes, i.e. you need to
requireall files yourself - no
active_supportniceties
Also, their scope...
Haml: Generating a unique selector for an element
Having a unique selector for an element is useful to later select it from JavaScript or to update a fragment with an Unpoly.
Haml lets you use square brackets ([]) to generate a unique class name and ID from a given Ruby object. Haml will infer a class attribute from the given object's Ruby class. It will also infer an id attribute from the given object's Ruby class and #id method.
This is especially useful with ActiveRecord instances, which have a persisted #id and will hence **generate the same selector o...
Haml: Prefixing a group of attributes
Haml lets you prefix a group of attributes by wrapping them in a hash. This is only possible with the {} attribute syntax, not with the () attribute syntax.
Example: HTML5 data attributes
HTML5 allows you to use arbitrary attributes like data-method and data-confirm. You can prefix a group of data- attributes like this:
%a{href: '/path', data: { method: 'delete', confirm: 'Really delete?' }} Label
This compiles to:
<a data-confirm='Really delete?' data-method='delete' href='/path'>Label</a>
Exa...
Introduction to Google Tag Manager (for web developers who know Google Analytics)
As a web developer, you know Google Analytics (GA). Probably you've dropped the GA snippet into more than one website, maybe you've even used its Javascript API to implement tracking at the event level.
Google Tag Manager (GTM) is a related tool, but on a higher level and thus with much more power. GTM is not a replacement for GA. Rather, it can make GA configurable without changing anything in the application's code base (and much more beyond, see below).
Only prefer GTM if the customer requests it, or if he is updating his tracking r...
Rails: Flagging all cookies as secure-only to pass a security audit
Why secure-only cookies used to be necessary
Cookies have an optional secure flag. It tells the browser to not send the cookie for a non-https request.
It used to be important to activate the secure flag even on sites that automatically redirect users from http:// to https://. The reason was that most users will only enter a scheme-less domain like makandra.de into their location bar, which will default to `http://m...
Minimal JavaScript function to detect version of Internet Explorer or Edge
If possible your code should detect features, not browsers. But sometimes you just need to sniff the browser. And when you do, you're probably fighting a Microsoft product.
The following function returns a Number like 10, 11, 12, 13 for Internet Explorer or Edge (anything above 11 is Edge). It returns undefined for any other browser.
function ieVersion(uaString) {
uaString = uaString || navigator.userAgent;
var match = /\...
Heads up: Rails offers two similar means for text truncation
Rails defines a #truncate helper as well as a method String#truncate.
= truncate("my string", length: 5)
= "my string".truncate(5)
Both are really similar; in fact, the helper invokes the method and improves it with two niceties: support for passing a block (which could e.g. render a "read on" link), and html_safe knowledge.
Prefer the truncate() helper
Warning: truncate() calls html_safe if you're not escaping. FWIW, an HTML string may easily become invalid when truncated, e.g. when a closing tag gets chopped off.
...
Using the Ruby block shortcut with arguments
Ruby has this handy block shortcut map(&:to_i) for map { |x| x.to_i }. However, it is limited to argument-less method invocations.
To call a method with an argument, you usually need to use the full block form. A common and annoying case is retrieving values from a list of hashes (imagine using a JSON API):
users = [ { name: 'Dominik', color: 'blue' }, { name: 'Stefan', color: 'red'} ]
names = users.collect do |user|
user[:name]
end
If you're using Rails 5+, this example is covered by Enumerable#pluck (`users.pluck(:name)...
Beware: Don't name a controller action "cookies"
The method cookies is defined in the ActionController and should never be overwritten.
Bad example
class StaticPagesController < ApplicationController
def imprint
end
def cookies
redirect_to '/'
end
end
If you create an action called cookies, any call to the cookie storage will be broken and call the method. What's more, in this example calls to static_pages_controller#imprint might as well end up redirecting to the homepage.
Solution
Just define the action as cookies_action or similar and adjust...