Using ActiveRecord with threads might use more database connections than you think
Database connections are not thread-safe. That's why ActiveRecord uses a separate database connection for each thread.
For instance, the following code uses 3 database connections:
3.times do
Thread.new do
User.first # first database access makes a new connection
end
end
These three connections will remain connected to the database server after the threads terminate. This only affects threads that use ActiveRecord.
You can rely on Rails' various clean-up mechanisms to release connections, as outlined below. This may...
How to use html_safe correctly
By default, Rails views escape HTML in any strings you insert. If you want to insert HTML verbatim, you need to call #html_safe. However, #html_safe does not "unescape" a string. It merely marks a string as safe for unescaped insertion.
How html_safe works
Calling html_safe on a String returns a new object that looks and acts like a String, but actually is a ActiveSupport::SafeBuffer:
"foo".length
# => 3
"foo".class
# => String
"foo".html_safe.length
# => 3
"foo".html_safe.class
# => ActiveSupport::S...
Implementing authentication and authorization for ActiveStorage blobs/files
ActiveStorage does not provide any built-in way of implementing authentication for the available DirectUpload endpoint in Rails. When using DirectUpload as JS wrapper in the frontend, be aware that its Rails endpoint is public by default, effectively allowing anyone to upload an unlimited amount of files to your storage.
The DirectUploadController from @rails/activestorage bypasses your form controller because it uploads the file using an AJAX request that runs directly, before any form roundtrip happens. This is a comfortable solutio...
Capistrano + Rails: Automatically skipping asset compilation when assets have not changed
In medium-sized to large Rails applications, asset compilation can take several minutes. In order to speed up deployment, asset precompilation can be skipped. This card automates the process.
Capistrano 3
namespace :deploy do
desc 'Automatically skip asset compile if possible'
task :auto_skip_assets do
asset_locations = %r(^(Gemfile\.lock|app/assets|lib/assets|vendor/asset))
revisions = []
on roles :app do
within current_path do
revisions << capture(:cat, 'REVISION').strip
...
Dealing with I18n::InvalidPluralizationData errors
When localizing model attributes via I18n you may run into errors like this:
I18n::InvalidPluralizationData: translation data { ... } can not be used with :count => 1. key 'one' is missing.
They seem to appear out of the blue and the error message is more confusing than helpful.
TL;DR A model (e.g. Post) is lacking an attribute (e.g. thread) translation.
Fix it by adding a translation for that model's attribute (attributes.post.thread). The error message reveals the (wrongly) located I18n data (from `attributes.thread...
Careful: `fresh_when last_modified: ...` without an object does not generate an E-Tag
To allow HTTP 304 responses, Rails offers the fresh_when method for controllers.
The most common way is to pass an ActiveRecord instance or scope, and fresh_when will set fitting E-Tag and Last-Modified headers for you. For scopes, an extra query is sent to the database.
fresh_when @users
If you do not want that magic to happen, e.g. because your scope is expens...
Using feature flags to stabilize flaky E2E tests
A flaky test is a test that is often green, but sometimes red. It may only fail on some PCs, or only when the entire test suite is run.
There are many causes for flaky tests. This card focuses on a specific class of feature with heavy side effects, mostly on on the UI. Features like the following can amplify your flakiness issues by unexpectedly changing elements, causing excessive requests or other timing issues:
- Lazy loading images
- Autocomplete in search f...
Do not pass params directly into url_for or URL helpers
Rails' url_for is useful for generating routes from a Hash, but can lead to an open redirect vulnerability.
Your application's generated route methods with a _url suffix are also affected because [they use url_for unter the hood](https://github.com/rails/rails...
Different ways to set attributes in ActiveRecord
Rails 5 / 6 / 7
| Method | Uses Default Accessor | Saves to Database | Runs Validations | Runs Callbacks | Updates updated_at/updated_on | Respects Readonly |
|---|---|---|---|---|---|---|
attribute= |
Yes | No | n/a | n/a | n/a | n/a |
attributes= |
Yes | No ... |
Cancelling the ActiveRecord callback chain
| Goal | Within before_*
|
Within after_*
|
|---|---|---|
| Cancel later callbacks | throw :abort |
throw :abort |
| Rollback the transaction | throw :abort |
raise ActiveRecord::Rollback |
When a callback raises an error
Exceptions raised in callbacks always rollback the transaction, but only exceptions that are not ActiveRecord::Rollback will bubble up to the caller.
Further readi...
better rails app restart with the passenger restart-app tool
With this command you can initiate an application restart without touching restart.txt. Unlike touching restart.txt, this tool initiates the restart immediately instead of on the next request. http://blog.phusion.nl/2014/01/02/phusion-passenger-4-0-33-released/
If you want to use this with capistrano 2.x just replace the touch command:
- run "touch #{current_path}/tmp/restart.txt"
+ run "passenger-config restart-app --ignore-app-not-running #{deploy_to}...
Nested ActiveRecord transaction pitfalls
When working with custom transactions and use ActiveRecord::Rollback you may encounter unexpected behaviour if you try to roll back your changes.
tl;dr
When using nested transactions, ActiveRecord::Rollback might not do what you expect, since it will only roll back the inner, but not the outer transaction.
You can fix this behavior by using transaction(joinable: false) but this leads to a bunch of different problems.
When you don't need an explicit ActiveRecord::Rollback, don't worry about any of this and just use a plan `tran...
Configuring ActionMailer host and protocol for URL generation
When you generate a URL in a mailer view, ActionMailer will raise an error unless you previously configured it which hostname to use.
There are two options to set the default_url_options of ActionMailer:
- Hardcoded solution (preferred solution when using Rails with ActiveJob/Sidekiq or Cronjobs)
- Dynamic solution
1. Hardcoded solution
When you are sending mails from outside the request cycle, e.g. ActiveJob/Sidekiq or Cronjobs, y...
Delivering Carrierwave attachments to authorized users only
Preparation
To attach files to your records, you will need a new database column representing the filename of the file. To do this, add a new migration (rails g migration <name>) with the following content:
class AddAttachmentToNotes < ActiveRecord::Migration[6.0]
def change
add_column :notes, :attachment, :string
end
end
Don't forget to rename the class and change the column details to fit your purpose. Run it.
1) Deliver attachments through Rails
The first way is to store your Carrierwave attachments not ...
SameSite cookies
TL;DR Most web applications do not require action on this. SameSite=None (old browser default) will continue to work, and SameSite=Lax (new Chrome default, gradually rolled out) is an even better default for cookies. Set SameSite=Strict only for extra security in special cases (see below). If your application is rendered in an iframe (e.g. a video player or some news stream), you need to configure its relevant cookies as SameSite=None.
The SameSite cookie attribute targets **c...
Rails: Configure ActionController to raise when strong params are invalid
Put the line below in the respective env.rb file to make your action controllers raise an ActionController::UnpermittedParameters error when strong params are not valid. This might come in handy when you are implementing an API and you want to let the user know that the request structure was invalid. You can then rescue those errors by implementing a rescue_from to have a generic handling of those cases. Note that you might need to whitelist common params such as :format to not raise on valid requests.
config.action_controller.a...
Differences between transactions and locking
Web applications can be used by multiple users at the same time. A typical application server like Passenger has multiple worker processes for a single app. In a distributed deployment setup like we use at makandra you will even have multiple application servers, each with their own worker pool.
This means that your code needs to deal with concurrent data access. The two main tools we use to cope with concurrency are database transactions and distributed locks. These two are not interchangeable. You ca...
Taking screenshots in Capybara
Capybara-screenshot can automatically save screenshots and the HTML for failed Capybara tests in Cucumber, RSpec or Minitest.
Requires Capybara-Webkit, Selenium or poltergeist for making screenshots. Screenshots are saved into $APPLICATION_ROOT/tmp/capybara.
Manually saving a page
Additionally you can trigger the same behavior manually from the test using Capybara::Session#save_and_open_page and [...
Action Mailer Previews
Rails includes a way to see what an e-mail will look like.
Integration to RSpec
All you need to do is implement a preview-class in spec/mailers/previews/notifier_preview.rb:
class NotifierPreview < ActionMailer::Preview
def welcome
Notifier.welcome(User.first)
end
end
And adapt the preview load path in your application.rb:
config.action_mailer.preview_path = "#{Rails.root}/spec/mailers/previews" # For Rails < 7.1
config.action_mailer.preview_paths << "#{Rails.root}/spec/mailers/previews" # For Rails >=...
Logic of `where.not` with multiple attributes
When using where.not with a Hash of multiple attributes, Rails applies logical NAND (NOT (A AND B)).
This contrasts with logical NOR (NOT A AND NOT B), which is achieved by chaining multiple where.not calls.
The difference in logic alters the scope of excluded records:
- NAND: Excludes records only if they match all attributes simultaneously.
- NOR: Excludes records if they match any of the attributes.
| NAND | NOR |
|---|---|
| (also calledvarcharor juststring): Contents are limited to n characters, smaller contents are allowed. -
character(n): All contents are padded with spaces to allocate exactly n characters. -
text: There is no upper or lower character limit (except for the absolute...
HTTP 302 redirects for PATCH or DELETE will not redirect with GET
A HTTP 302 Found redirect to PATCH and DELETE requests will be followed with PATCH or DELETE. Redirect responses to GET and POST will be followed with a GET. The Rails form_for helper will use a workaround to send POST requests with a _method param to avoid this issue for PATCH/DELETE.
If you make requests yourself, watch out for the following behavior.
When you make an AJAX request PATCH /foo and the /foo action redirects to /bar, browsers will request PATCH /bar. You probably expected the second requ...
Load order of the environment
Rails 3, 4, 5, 6
config/application.rb-
config/environment.rbbefore theinitialize!call (we don't usually edit this file) - The current environment, e.g.
environments/production.rb - Gems
- Vendored plugins
- All initializers in
config/initializers/*.rb -
config/environment.rbafter theinitialize!call (we don't usually edit this file) - Your own code from
app
Rails 2
- Code in
config/preinitializer.rb(if it exists) -
environment.rb, code above theRails::Initializer.runblo...