Setup Sidekiq and Redis
If you want Sidekiq to be able to talk to Redis on staging and production servers, you need to add the following to your configuration:
# config/initializers/sidekiq.rb
require 'sidekiq'
Sidekiq.configure_client do |config|
config.redis = { url: REDIS_URL }
end
Sidekiq.configure_server do |config|
config.redis = { url: REDIS_URL }
end
The following step may be skipped for new Sidekiq 6+, since it isn't recommended anymore to use a global redis client.
# config/initializers/redis.rb
require 'redis'
require_relativ...
Collect all values for a given column in an ActiveRecord scope
In modern Rails versions you can also use ActiveRecord's pluck method.
User.active.pluck(:id)
=> [1, 5, 23, 42]
If you are plucking from the id column in particular you can also say:
User.active.ids
=> [1, 5, 23, 42]
For a DISTINCT selection, use distinct on your scope (not the resulting array).
Article.distinct.pluck(:state)
...
Advantages of using appname.daho.im:3000 over localhost:3000
Running rails server will start a local server that you can access via http://localhost:3000.
When you are working on multiple web apps, they will likely set cookies with generic names on localhost. This is annoying, since you will sign out your current user whenever you switch to another app.
A better way is to use our own daho.im service. All daho.im subdomains resolve to your local IP (127.0.0.1). That means you can use a different hostname for different apps, and you will stay logged in in each app:
http://foo-ap...
Defining custom RSpec matchers
There are three ways to define your own RSpec matchers, with increasing complexibility and options:
1) Use RSpec::Matchers.define
RSpec::Matchers.define :be_a_multiple_of do |expected|
match do |actual|
actual % expected == 0
end
# optional
failure_message do |actual|
"expected that #{actual} would be a multiple of #{expected}"
end
# optional
failure_message_when_negated do |actual|
"expected that #{actual} would not be a multiple of #{expected}"
end
end
- This is automatically available i...
ActionMailer: Previewing mails directly in your email client
In Rails, we usually have a mailer setup like this:
class MyMailer < ActionMailer::Base
def newsletter
mail to: 'receiver@host.tld',
from: 'sender@host.tld',
subject: 'My mail'
end
end
If you want to preview your mail in the browser, you can use the Action Mailer Preview. To inspect the mail directly in your email client, just create an .eml file and open it with your client:
mail = MyMailer.newsletter
Fil...
Make Nokogiri use system libxml2
The nokogiri gem provides different packages for several platforms. Each platform-specific variant ships pre-built binaries of libxml2, e.g. x86_64-linux includes binaries for 64bit Linux on Intel/AMD. This significantly speeds up installation of the gem, as Nokogiri no longer needs to compile libxml2.
However, this also means that for each security issue with libxml2, Nokogiri maintainers have to update their pre-built binaries and release a new version of the gem. Then, you need to update and ...
Git: Restore
tl;dr
git checkoutis the swiss army of git commands. If you prefer a semantically more meaningful command for restoring tasks, usegit restoreinstead.With this command you can ...
- ... do unstaging -
git restore --staged- ... discard staged changes -
git restore --staged --worktree- ... discard unstaged changes -
git restore- ... restore deleted files -
git restore- ... restore historic versions -
git restore --source- ... recreate merge conflicts -
git restore --merge- ... specifiy...
Building web applications: Beyond the happy path
When building a web application, one is tempted to claim it "done" too early. Make sure you check this list.
Different screen sizes and browsers
Desktops, tablets and mobile devices have all different screen resolutions. Does your design work on each of them?
- Choose which browsers to support. Make sure the page looks OK, is usable and working in these browsers.
- Use @media queries to build a responsive design
- If you do not suppo...
Ruby: A small summary of what return, break and next means for blocks
Summary
- Use
returnto return from a method.returnaccepts a value that will be the return value of the method call. - Use
breakto quit from a block and from the method that yielded to the block.breakaccepts a value that supplies the result of the expression it is “breaking” out of. - Use
nextto skip the rest of the current iteration.nextaccepts an argument that will be the result of that block iteration.
The following method will serve as an example in the details below:
def example
puts yield
puts ...
Custom Ruby method Enumerable#count_by (use for quick statistics)
I frequently find myself needing a combination of group_by, count and sort for quick statistics. Here's a method on Enumerable that combines the three:
module Enumerable
def count_by(&block)
group_by(&block)
.transform_values(&:count)
.sort_by(&:last)
.to_h
end
end
Just paste that snippet into a Rails console and use #count_by now!
Usage examples
- Number of email addresses by domain:
> User.all.count_by { |user| user.email.sub /^.*@/, '' }
=> { "sina.cn"=>2, ..., "hotmail.co...
Sending newsletters via rapidmail with SMTP and one-click unsubscribe
If you need to implement newsletter sending, rapidmail is a solid option.
Support is very fast, friendly and helpful, and the initial setup is simple. Since rapidmail works via SMTP, you can simply ask the Ops team to configure SMTP credentials for your application.
You also do not need to use rapidmail’s built-in newsletter feature. Instead, you can send emails as transactional mails, which allows you to keep the entire newsletter logic inside your application.
One thing to keep an ey...
How to query GraphQL APIs with Ruby
While most Rails Apps are tied to at least one external REST API, machine-to-machine communication via GraphQL is less commonly seen. In this card, I'd like to give a quick intro on how to query a given GraphQL API - without adding any additional library to your existing app.
Core aspects of GraphQL
Interacting with GraphQL feels a bit like querying a local database. You are submitting queries to fetch data in a given structure (like SELECT in SQL) or mutations to alter the database (similar to POST/PUT/DELETE in REST). You can ...
Faking and testing the network with WebMock
An alternative to this technique is using VCR. VCR allows you to record and replay real HTTP responses, saving you the effort to stub out request/response cycles in close details. If your tests do require close inspection of requests and responses, Webmock is still the way.
WebMock is an alternative to FakeWeb when testing code that uses the network. You sh...
How to use pessimistic row locks with ActiveRecord
When requests arrive at the application servers simultaneously, weird things can happen. Sometimes, this can also happen if a user double-clicks on a button, for example.
This often leads to problems, as two object instances are modified in parallel maybe by different code and one of the requests writes the results to the database.
In case you want to make sure that only one of the requests "wins", i.e. one of the requests is fully executed and completed while the other one at least has to wait for the first request to be completed, you ha...
Minifying object properties in JavaScript files
An introduction to mangling
When you minify ("compress", "optimize") your JavaScript for production, the names of your functions and variables will be renamed for brevity. This process is often called mangling.
E.g. if this is your source code:
function function1() {
function2()
}
After mangling it would look like this:
function a() {
b()
}
Object properties are not mangled by default
Minfiers never mangle properties by default, as this can be an unsafe transformation. This leads to larger file sizes if...
Take care of indentation and blank lines when using .erb for plain text emails
Building plain text emails with an .erb template doesn't allow you to indent code like you normally do in HTML mails.
❌ DON'T
<%= 'foo' if bar %>
"\n" if bar is false
"foo\n" if bar is true
<%= nil %>
"\n"
<% if true %>
<%= 'foo' %>
<% end %>
" foo"
<%= 'foo' %>
<%= 'bar' %>
"foo\n\nbar\n"
✔️ DO
Write unindented code to get the expected result.
<% if bar %>
<%= 'bar' %>
<% end %>
<%= 'foo' %>
<%= 'bar' %>
- Use [Form Models](https://github.com/makandr...
Controlling how your website appears on social media feeds
When a user shares your content, a snippet with title, image, link and description appears in her timeline. By default social networks will use the window title, the first image, the current URL and some random text snippet for this purpose. This is often not what you want.
Luckily Facebook, Twitter, etc. lets you control how your content appears in the activity streams. They even have agreed on a common format to do this: OpenGraph <meta> tags that go into your HTML's <head>:
<meta property="og:url" content="http://start.m...
Ruby: How to collect a Hash from an Array
There are many different methods that allow mapping an Array to a Hash in Ruby.
Array#to_h with a block (Ruby 2.6+)
You can call an array with a block that is called with each element. The block must return a [key, value] tuple.
This is useful if both the hash key and value can be derived from each array element:
users = User.all
user_names_by_id = users.to_h { |user| [user.id, user.name] }
{
1 => "Alice",
2 => "Bob"
}
Array#to_h on an array of key/value tuples (Ruby 2.1+)
Converts an Array ...
PSA: Be super careful with complex `eager_load` or `includes` queries
TLDR
Using
.includesor.eager_loadwith 1-n associations is dangerous. Always use.preloadinstead.
Consider the following ActiveRecord query:
BlogPost.eager_load(
:comments
:attachments,
).to_a
(Let's assume we only have a couple of blog posts; if you use pagination the queries will be more complicated, but the point still stands.
Looks harmless enough? It is not.
The problem
ActiveRecord will rewrite this into a query using LEFT JOINs which looks something like this:
SELECT "blog_posts...
Regular tasks for long-running projects
When projects run for many years, they require special regular maintenance to stay fresh. This kind of maintenance is usually not necessary for small or quick projects, but required to keep long-running projects from getting stale.
You should be able to fit this into a regular development block.
Quarterly
Check which libraries need updating
As time goes by, libraries outdate. Check your software components and decide if any of it needs an update. Your main components (e.g. Ruby, Rails, Unpoly) should always be reasonably up to da...
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: Benchmark an Active Record query with a Ruby script
Recently I needed to benchmark an Active Record query for performance measurements. I wrote a small script that runs each query to benchmark 100 times and calculates the 95th percentile.
Note: The script requires sudo permissions to drop RAM cache of PostgreSQL. Due to the number of iterations it was impractical to enter my user password that often. And I temporary edited my /etc/sudoers to not ask for the sudo password with johndoe ALL=(ALL) NOPASSWD: ALL.
# Run this script with e.g. `rails ru...
RSpec: automatic creation of VCR cassettes
You can configure VCR to automatically record/replay cassettes for any RSpec example tagged as :vcr or vcr: true.
-
If a spec is not tagged with
:vcr, VCR will complain about any attempted HTTP request. This is the default behaviour. If you want to turn this off temporarily, e.g. to communicate with an actual API while writing a new spec, simply add the linec.allow_http_connections_when_no_cassette = trueto theVCR.configure-block. -
If a spec is tagged with
:vcr, a cassette with an automatically determined name will be gener...
Fixing authentication in legacy applications
Authentication is hard: there are many edge cases, and most users (including yourself) usually only go the "happy path" once and never see the edge cases. If you have rolled your own authentication, or been using older authentication solutions, or resorted to HTTP Basic Authentication, this card will tell you what to do to make your application safe.
Any application that stores sensitive data in the browser
That is: cookies, e.g. by offering a login.
- Ask the admins to [turn on SSL](https://makandracards.com/makandra/1416-integrate-s...