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:
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.rb
to your project (see How to organize monkey patches in Ruby on Rails projects) - Add the extension, which ov...
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
require
all files yourself - no
active_support
niceties
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...
ActiveRecord::Store: migrate data in store
When you need to store structured data (like Ruby hashes) in a single database column with ActiveRecord, a simple way is to use PostgreSQL's jsonb
columns. ActiveRecord will automatically serialize and deserialize your Hash to and from JSON, and you can index JSON paths for fast reads.
As an alternative, ActiveRecord::Store
offers a way to store hashes in a single database column. This card will show you how to migrate those hashes in an ActiveRecord::Migration
by example:
...
RSpec: Expect one of multiple matchers to match
RSpec let's you chain a matcher with .or
. The expectation will then pass if at least one matcher matches:
expect(color).to eq("red").or eq("green")
Real-world example
A real-world use case would be to test if the current page has a button with the label "Foo". There are many ways to render a button with CSS:
<input type="button" value="Foo">
<input type="submit" value="Foo">
<button>Foo</button>
We cannot express it with a single have_css()
matcher, since we need the { text: 'Foo' }
optio...
CSS: Using the current text color for other color properties
There is a kinda secret, yet well supported CSS feature called currentColor
. It's like a special CSS variable that has been supported in almost all browsers for almost all time (see linked Caniuse).
Usage
The currentColor
value can be used in CSS to indicate the current value of color
should be used. A common use case is setting a border color:
a.ghost
color: white
border: 1px solid currentColor
&:hover
color: red // Border color will change as well
Note that in many cases, you can simply omit the color to ac...
Jasmine: Adding custom matchers
Definition
A matcher is a function that returns an object with a compare
key. Usually it is registered with beforeEach
:
beforeEach(() => {
jasmine.addMatchers({
// Example matcher
toBeAnything() {
return {
compare(actualValue, ...matcherArguments) {
// Do some computations here ...
// Return whether the actualValue matches the expectation
return {pass: true}
}
}
}
})
})
Usage
expect(actualValue).toBeAnything(...matcherArg...
Rails: Including HTML in your i18n locales
TL;DR
Append your locale keys with _html to have them marked as
html_safe
and 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...