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.rbto 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
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...
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_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...
screenfull.js: Simple wrapper for cross-browser usage of the JavaScript Fullscreen API
Using the JS fullscreen API is painful because all browers use different methods and events and you need to use lots of boilerplate code to make your application work in all browsers.
The "screenfull" library wraps that for you, including events.
Examples
The linked GitHub repo contains some information. You basically use the library like this:
// Make an element go fullscreen
screenfull.request(element)
// Leave fullscreen
screenfull.exit()
...
jQuery: How to replace DOM nodes with their contents
You know that you can use jQuery's text() to get an element's contents without any tags.
If you want to remove only some tags, but keep others, use contents() and unwrap(). Here is how.
Consider the following example element.
$container = $('<div><strong>Hello</strong> <em>World</em></div>')
Let's say we want to discard any <em> tags, but keep their contents.
Simply find them, then dive into their child nodes via contents, and use unwrap replace their ...
ActionMailer: How to send a test mail directly from the console
If your rails application is unable to send mails, it might be useful to debug your settings using the rails console. Here is a snippet that shows the current settings and lets you send a test mail directly from the console:
mailer = ActionMailer::Base.new
# check settings:
mailer.delivery_method # -> :smtp
mailer.smtp_settings # -> { address: "localhost", port: 25, domain: "localhost.localdomain", user_name: nil, password: nil, authentication: nil, enable_starttls_auto: true }
# send mail:
mailer.mail(from: 'sender@example.com', ...
PostgreSQL: How to show table sizes
When you have a large PG database, you may want to find out which tables are consuming the most disk space.
You can easily check this using the following SQL statement from the PostgreSQL wiki.
SELECT nspname || '.' || relname AS "relation",
pg_size_pretty(pg_total_relation_size(C.oid)) AS "total_size"
FROM pg_class C
LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
WHERE nspname NOT IN ('pg_catalog', 'information_schema')
AND C.relkind <> 'i'
AND nspname !~ '^pg_toast'
ORDER BY pg_tot...
Webpacker: Configuring browser compatibility
Webpacker uses Babel and Webpack to transpile modern JavaScript down to EcmaScript 5. Depending on what browser a project needs to support, the final Webpack output needs to be different. E.g. when we need to support IE11 we can rely on fewer JavaScript features. Hence our output will be more verbose than when we only need support modern browsers.
Rails 5.1+ projects often use Webpacker to preconfigure the Webpack pipeline for us. The default configuration works something like this:
- Webpack checks w...
Carrierwave: Built-in RSpec matchers
CarrierWave comes with some RSpec matchers which will make testing more comfortable. Let's say you have an Uploader like this:
class MyUploader < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
# Create different versions of your uploaded files:
version :small do
process resize_to_fill: [100, 100]
end
version :medium do
process resize_to_fit: [200, nil]
end
version :large do
process resize_to_limit: [400, 400]
end
end
Imagine you have a class Movie with an attribute poster. In ...