Managing Rails locale files with i18n-tasks
When internationalizing your Rails app, you'll be replacing strings like 'Please enter your name'
with t('.name_prompt')
. You will be adding keys to your config/locales/*.yml
files over and over again. Not to miss any key and place each at the right place is a challenging task.
The gem i18n-tasks
has you covered. See its README for a list of things it will do for you.
Note
The
i18n-tasks
gem does not understand aliases and will duplicate all referenced data when it writes locales. If yo...
Fuzzy scoping in Rails with PostgreSQL
When you want to filter records in a model where a string column roughly matches a given term, you can use PostgreSQL’s trigram similarity search.
Writing a fuzzy query in Rails
User.where("similarity(name, ?) > 0.3", "John")
This finds all users where the name is similar to "John" with a similarity score above 0.3.
You can tune the threshold:
- Closer to 1.0 = stricter match
- Closer to 0.0 = looser match
Ordering by best match
User
.where("similarity(name, ?) > 0.3", "John")
.order(Arel.sql("similarity(n...
tel_to Rails helper for linking phone numbers
When putting phone numbers into web pages, you should use tel:
links so smartphone users can click those numbers to call someone.
Here is a small helper method that you can use to simplify this:
def tel_to(text)
groups = text.to_s.scan(/(?:^\+)?\d+/)
link_to text, "tel:#{groups.join '-'}"
end
This will allow you to use human-readable numbers and get clean links:
>> tel_to '(01234) 555 6789'
=> <a href="tel:01234-555-6789">(01234) 555 6789</a>
>> tel_to '+1 555 123-456'
=> <a href="tel:+1-555-123-456...
Your Rails sandbox console
Just found out about a great feature in Rails that seems to be around since Rails 2. Start a console with the --sandbox
(or -s
) parameter:
rails console --sandbox
All changes you make to the database will be rolled back on exit.
Warning
Changes beyond the database (deleting files, sending emails, etc) cannot be rolled back!
How to query PostgreSQL's json fields from Rails
PostgreSQL offers a really handy field type: json
. You can store any JSON there, in any structure.
While its flexibility is great, there is no syntactic sugar in Rails yet. Thus, you need to manually query the database.
Demo
# Given a Task model with 'details' json column
Task.where("details->>'key' = ?", "value") # matches where 'details' contains "key":"value"
Task.where("details->>'{a,b}' = ?", "value") # matches where 'details' contains "a":{"b":"value"}
Task.where("details->'a'->>'b' = ?", "value") # same as above, but vi...
Rails: Using database default values for boolean attributes
In the past we validate and set default values for boolean attributes in Rails and not the database itself.
Reasons for this:
- Older Rails didn't support database defaults when creating new records
- Application logic is "hidden" in the database
An alternative approach, which currently reflects more the general opinion of the Rails upstream on constraints in the database, is adding default values in the schema of the database itself. We also ...
Querying model errors in Rails 4
ActiveModel
supplies an errors object that behaves similar to a Hash. It can be used to add errors to a record, as well as to query the record for registered errors. This object is returned when calling <object>.errors
:
errors = @user.errors # => #<ActiveModel::Errors ...>
Here are some helpful messages of its API:
[<attribute name>]
-
Returns an array of error messages on that attribute. Example:
errors[:name] => ['is missing']
-
add_on_blank(<attribute list>)
(similarlyadd_on_empty
) -
Registers an error ...
bower-rails can rewrite your relative asset paths
The asset pipeline changes the paths of CSS files during precompilation. This opens a world of pain when CSS files reference images (like jQuery UI) or fonts (like webfont kits from Font Squirrel), since all those url(images/icon.png)
will now point to a broken path.
In the past we have been using the vendor/asset-libs
folder ...
Rails 4.1+: New locale files require a Spring restart
So you want to organize your I18n using multiple .yml
files but your Rails 4.1 application simply won't use any extra files in development? Spring is to blame.
Halt spring by running:
spring stop
The next time you spawn a bin/rails console
or similar, your new translations will work.
You will need to repeat the above every time you create a new .yml
file or rename existing ones.
:(
Rails: Migration helper for inserting records without using models
You should avoid using application models in your migrations. But how else could you create records in a migration?
The most basic way is to write plain SQL, but since INSERT statements are no pleasant write for Rubyists, here is a simple wrapper:
Record creation helper for migrations
The helper method below takes a table name and a hash of attributes, which it inserts into the specified table. Copy it over to your migration and profit!
private
def insert_record(table, **attributes)
attributes.merge!...
Rails: Integrating shoelace components
shoelace is a library of web components. Here is a proof of concept how a integration (slightly different as the official docs for Rails) might look like in Rails + webpack + Unpoly. Also see the HN discussion for pro and cons.
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...
Solving "TypeError (nil can't be coerced into Integer)" in the Rails console / IRB
On the Rails console, assigning an object to a variable can lead to this strange error (without stacktrace):
irb > recipient = Recipient.find(123)
Traceback (most recent call last):
TypeError (nil can't be coerced into Integer)
irb > recipient
#<Recipient ...
The error is only in the output – the assignment is working. It only occurs when using the --nomultiline
option, and thus [only with IRB 1.2.0+ and before Ruby 3](https://github.com/makandra/geordi/blob...
Reverse lookup a fixture name by its id and table name
To reverse lookup a fixture by its table name and id, use the following approach on ActiveRecord::FixtureSet
:
table = 'users' # Specify the fixture table name
id = 123122 # Specify the ID to look for
# Find the fixture that matches the given ID
ActiveRecord::FixtureSet.all_loaded_fixtures[table].fixtures.find { |key, value| value['id'] == id }
Result Example:
[
"one", # Fixture name
#<ActiveRecord::Fixture:0x00007e79990234c8>, # ActiveRecord::Fixture object
@fixture= { ... }, # The raw fixtu...
Rails routing: Using constraints to avoid "Missing template" errors
You can use constraints in your routes.rb
to avoid getting ActionView::MissingTemplate
errors when wrong routes are called. Instead, the user will see a 404.
If you want multiple routes to use the same constraint you can use the block syntax:
constraints(format: 'html') do
resources :pages
resources :images
end
If you want constraints only on certain routes, you can do:
get '/users/account' => 'users#account', constraints: { format: 'html' }
Tip
You can also avoid this error type through [format con...
Rails: Removing the cucumber-rails warning when setting cache_classes to false without Spring enabled
We are using Spring in our tests for sequential test execution but not for parallel test execution. And Rails requires you to set the config.cache_classes = false
if you are using Spring in tests.
With our setup, this would raise the following error in cucumber-rails for parallel test executions due to some legacy database cleaner issue.
WARNING: You have set Rails' config.cache_classes to false
(Spring needs cache_classes set to false). This is known to cause probl...
Rails: render a template that accepts a block by using the layout option of render
Let's say you have a form that you render a few times but you would like to customize your submit section each time. You can achieve this by rendering your form partial as layout and passing in a block. Your template or partial then serves as the surrounding layout of the block that you pass in. You can then yield back the form to the block and access the form in your block.
-# record/_form.haml
= form_for record do |form|
-# ...
.form-actions
yield(form)
In order to make your template record/_form.haml
accept a...
whenever: Installing cron jobs only for a given Rails environment or Capistrano stage
We use the whenever gem to automatically update the crontab of the servers we deploy to. By default, whenever will update all servers with a matching role (we use the :cron
role ).
This card describes how to install some tasks only for a given Rails environment or for a given Capistrano stage ("deployment target").
Installing jobs only for a given Rails environment
-----------------------------------...
Security issues with hash conditions in Rails 2 and Rails 3
Find conditions for scopes can be given either as an array (:conditions => ['state = ?', 'draft']
) or a hash (:conditions => { 'state' => 'draft' }
). The later is nicer to read, but has horrible security implications in some versions of Ruby on Rails.
Affected versions
Version | Affected? | Remedy |
---|---|---|
2.3.18 | yes | Use chain_safely workaround |
3.0.20 | no | ... |
Capistrano + Rails: Tagging production deploys
Just like Ruby Gems tag their version releases to the corresponding Git commit, it can be helpful to track production deploys within the commit history. This task does the tagging for you.
Capistrano 3
# lib/capistrano/tasks/deploy.rb
namespace :deploy do
...
desc 'Tag the deployed revision'
task :tag_revision do
date = Date.today.to_s
puts `git tag deploy-#{date} #{fetch :current_revision}`
puts `git push --tags origin`
end
end
# config/deploy/production.rb
after 'deploy:finished', 'deploy:tag_...
Why Sidekiq Jobs should never be enqueued in an `after_create` or `after_save` callback
When an object is created / updated, various callbacks are executed in this order:
before_validation
after_validation
before_save
around_save
before_create
around_create
after_create
after_save
after_commit / after_rollback
Thus, each of these callbacks is executed at a specific time in the life cycle of the object. This is important because this point in time determ...
Rails migration: Changing a column type without losing the content
The change_column
method for rails migrations support casting with a custom SQL statement. This allows us to change a column type and keep the former content as the new type. This way, we can for example prepare an address number column to hold German address numbers, which can contain letters:
Example (in most cases not a good idea!)
class ChangeAnIntegerColumnToString < ActiveRecord::Migration[6.1]
def up
change_column :users, :address_number, 'varchar USING CAST(rating AS varchar)'
end
def down
change_column ...
Debug MiniMagick calls in your Rails app
Most of our applications use CarrierWave for file uploads. CarrierWave has an integrated processing mechanism for different file versions with support for ImageMagick through CarrierWave::MiniMagick
(which requires the mini_magick
gem). In case your processing runs into an error, CarrierWave will just swallow it and rethrow an error with a very generic message like Processing failed. Maybe it is not an image?
which does not help you finding out what the actual problem is. CarrierWave probably does this for security purposes, but does n...
Supporting multiple SAML IdPs within a single Rails application
The linked article shows how to configure omniauth-multi-provider
to support multiple SAML identity providers for a single Rails app:
To solve this, the omniauth-multi-provider gem acts as a dynamic wrapper around OmniAuth. It enables your application to load the correct IdP configuration at runtime—based on the tenant—allowing for flexible and secure SSO authentication across multiple organisations.