Shortcuts for getting ids for an ActiveRecord scope
You can use .ids
on an ActiveRecord scope to pluck all the ids of the relation:
# Modern Rails
User.where("users.name LIKE 'Foo Bar'").ids
# Rails 3.2+ equivalent
User.where("users.name LIKE 'Foo Bar'").pluck(:id)
# Edge rider equivalent for Rails 2+
User.where("users.name LIKE 'Foo Bar'").collect_ids
Upgrade guide for moving a Rails app from Webpack 3 to Webpack 4
Webpacker is Rails' way of integrating Webpack, and version 4 has been released just a few days ago, allowing us to use Webpack 4.
I successfully upgraded an existing real-world Webpack 3 application. Below are notes on everything that I encountered.
Note that we prefer not using the Rails asset pipeline at all and serving all assets through Webpack for the sake of consistency.
Preparations
- Remove version locks in
Gemfile
forwebpacker
- Remove version locks in
package.json
forwebpack
andwebpack-dev-server
- Install by ca...
Rails: How to get URL params without routing parameters (or vice versa)
Rails' params
hash contains any request parameters (URL parameters or request payload) as well as routing parameters like :controller
, :action
, or :id
.
To access only URL parameters, use request.query_parameters
. Routing params are available through request.path_parameters
.
# On /users?query=Bob&page=2
>> request.params
=> {"page"=>"2", "query"=>"Bob", "controller"=>"users", "action"=>"index"}
>> request.query_parameters
=> {"page"=>"2", "query"=>"Bob"}
>> request.path_parameters
=> {:controller=>"users", :action=>"i...
Carrierwave processing facts
- Class-level
process
definitions are only applied to the original file - Versions are generated based on the processed original file
- Callbacks (
before
/after
) are applied to original file and each version by itself - Under the hood, a version is an instance of the uploader class that has no versions
- Version uploader and original uploader can be distinguished by checking
#version_name
: version uploaders return the version name, whereas the original uploader instance returnsnil
- Version instances do not have a re...
Trigram indexing as an alternative to PostgreSQL fulltext search
For searching in large database tables we usually use PostgreSQL's fulltext search capabilities.
While this works reasonably well for content primarily consisting of prose, it is not necessarily a good solution for all use cases. The main issue is that it is only possible to search for prefixes of text tokens, which can potentially be unexpected for users.
One example are dates:
If you index the text 2019-01-23 15:16
, PostgreSQL will create the following tokens: 2019, -01, -23, 15 16
. A user searching for 01-23
wi...
JavaScript without jQuery
This is a presentation from 2019-01-21.
Summary
- We want to move away from jQuery in future projects
- Motivations are performance, bundle size and general trends for the web platform.
- The native DOM API is much nicer than it used to be, and we can polyfill the missing pieces
- Unpoly 0.60.0 works with or without jQuery
Is jQuery slow?
From: Sven
To: unpoly@googlegroups.com
Subject: performance on smartphones and tablets
Hello
I just used your framework in one project and must say,
I am really pleased with it -- but o...
Heads up: pg_restore --clean keeps existing tables
When restoring a PostgreSQL dump using pg_restore
, you usually add the --clean
flag to remove any existing data from tables.
Note that this only removes data from tables that are part of the dump and will not remove any extra tables. You need to do that yourself.
When is this relevant?
As an example: You want to load a staging dump into your development machine. On your development machine, you have run migrations that introduced more tables which do not yet exist on staging. pg_restore
with --clean
will loa...
How to drop all tables in PostgreSQL
To remove all tables from a database (but keep the database itself), you have two options.
Option 1: Drop the entire schema
You will need to re-create the schema and its permissions. This is usually good enough for development machines only.
DROP SCHEMA public CASCADE;
CREATE SCHEMA public;
GRANT ALL ON SCHEMA public TO postgres;
GRANT ALL ON SCHEMA public TO public;
Applications usually use the "public" schema. You may encounter other schema names when working with a (legacy) application's database.
Note that f...
How to upgrade Rails: Workflow advice
When upgrading Rails versions -- especially major versions -- you will run into a lot of unique issues, depending on the exact version, and depending on your app.
However, it is still possible to give some generic advice on how you want to tackle the update in principle.
If you are not really confident about upgrading Rails, have a look at Rails LTS.
How many update steps?
Besides the Rails upgrade itself, you might also want to upgrade your other gems and upgrade your Ruby version.
First decide in how many st...
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 d...
Rubymine FileType mismatch
If your Rubymine does not recognize a file type correctly although you have entered the unmistakeable file extension like material_orders_controller.rb
, this may help you:
Causing the Problem
Sometimes you create a new file and forget to enter the ending like material_orders_controller
Rubymine handles such files per default as simple txt files.
You delete this file and create a new one with correct ending: material_orders_controller.rb
. But still Rubymine treats this file as text file, no highlighting is available.
What happene...
ActiveJob Inline can break the autoloading in development
We figured out, that ActiveJob Inline might lead to autoloading problems in development. The result was an exception when running an import script, which delivers async mails.
A copy of XXX has been removed from the module tree but is still active! (ArgumentError)
Our fix was to use .deliver_now
and not .deliver_later
(this is not a general fix, but it was okey for us). Below there are some debug hints which helped us to locate the problem:
- We placed a pry debugger in [ActiveSupport#clear](https://github.com/rails/rails/blob...
Devise: Don't forget to lock users with soft delete
There are two ways to lock a user in devise.
- Using the lockable module
- Customizing the user account status validation when logging in.
It depends on your requirements which methods works best.
Locking a user on soft delete
We recommend to use option 2 when you want to couple the lock to the m...
RSpec 3 allows chaining multiple expectations
When you are using lambdas in RSpec to assert certain changes of a call, you know this syntax:
expect { playlist.destroy }.to change { Playlist.count }.by(-1)
While you can define multiple assertions through multiple specs, you may not want to do so, e.g. for performance or for the sake of mental overhead.
Multiple expectations on the same subject
RSpec allows chaining expectations simply by using and
.
expect { playlist.destroy }
.to change { Playlist.count }.by(-1)
.and not_change { Video.count }
...
cucumber_factory 1.14 lets you set array fields, has_many associations, numbers without quotes
Setting array columns
When using PostgreSQL array columns, you can set an array attribute to a value with square brackets:
Given there is a movie with the tags ["comedy", "drama" and "action"]
Setting has_many associations
You can set has_many
associations by referring to multiple named records in square brackets:
Given there is a movie with the title "Sunshine"
And there is a movie with the title "...
Minify Font Awesome fonts with webpack
Font Awesome 5 is a comprehensive solution for vector icons on your website.
Originally, Font Awesome came as an icon font (plus stylesheets), but recently it can also be used as a pure JavaScript solution (which will render icons as inline <svg>
tags), or even as SVG sprites.
All solutions have their pros and cons:
Icon font:
- little CPU load (no JavaScript)
- fonts are relatively large
- 1 extra HTTP request
Javascript + inline SVG:
- higher CPU load (needs to watch the DOM via mutation observers to ad...
How to let passenger restart after deployment with capistrano
Phusion Passenger changed the way how it gets restarted several times. Through the project's history, these all were valid:
touch tmp/restart.txt
sudo passenger-config restart-app /path/to/app
passenger-config restart-app /path/to/app
You should not need to know which one to use. Instead, the capistrano-passenger gem will choose the appropriate restart mechanism automatically based on your installed the passenger version.
Installation
-
Add to your
Gemfile
:gem 'capistr...
An incomplete guide to migrate a Rails application from paperclip to carrierwave
In this example we assume that not only the storage gem changes but also the file structure on disc.
A general approach
Part A: Create a commit which includes a script that allows you to copy the existing file to the new file structure.
Part B: Create a commit which removes all paperclip logic and replace it with the same code you used in the first commit
Part A
Here are some implementation details you might want to reuse:
- Use the existing models to read the files from
- Use your own carrierwave models to write t...
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:...
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...
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...
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 ...