Automatically validating dependency licenses with License Finder
"Open-source software (OSS) is great. Anyone can use virtually any open-source code in their projects."
Well, it depends. Licenses can make things difficult, especially when you are developing closed-source software. Since some OSS licenses even require the employing application to be open-sourced as well (looking at you, GPL), you cannot use such software in a closed-source project.
To be sure on this, we have developed a project-level integration of Pivotal's excellent [license_finder](https:/...
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...
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...
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...
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 ...
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
...
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...
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...
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}...
Best practice: How to manage versions in a Gemfile
It most cases it's not necessary to add a version constraint next to your gems in the Gemfile
. Since all versions are saved in the Gemfile.lock
, everyone running bundle install
will get exactly the same versions.
There are some exceptions, where you can consider adding a version constrain to the Gemfile
:
- You are not checking in the
Gemfile.lock
into the version control (not recommended) - A specific gem has a bug in a more recent version (adding a comment for the reason is highly recommended)
- You want to ensure no one upgrade...
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...
Load order of the environment
Rails 3, 4, 5, 6
config/application.rb
-
config/environment.rb
before 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.rb
after 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.run
blo...
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...
Timeouts for long-running SQL queries
While the main goal always is to prevent long-running queries in the first place, automatic timeouts can serve as a safety net to terminate problematic queries automatically if a set time limit is exceeded. This prevents single queries from taking up all of your database’s resources and reduces the need for manual intervention that might destabilize or even crash the application.
As Rails does not set a timeout on database statements by default, the following query will run for an entire day:
ActiveRecord::Base.connection.execute("S...
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 >=...
Webpack(er): A primer
webpack is a very powerful asset bundler written in node.js to bundle (ES6) JavaScript modules, stylesheets, images, and other assets for consumption in browsers.
Webpacker is a wrapper around webpack that handles integration with Rails.
This is a short introduction.
Installation
If you haven't already, you need to install node.js and Yarn.
Then, put
gem 'webpacker', '~> 4.x' # check if 4.x is still cu...
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 [...
Using rack-mini-profiler (with Unpoly)
Debugging performance issues in your Rails app can be a tough challenge.
To get more detailed insights consider using the rack-mini-profiler gem.
Setup with Unpoly
Add the following gems:
group :development do
gem 'memory_profiler'
gem 'rack-mini-profiler'
gem 'stackprof'
end
Unpoly will interfere with the rack-mini-profiler widget, but configuring the following works okayish:
// rack-mini-profiler + unpoly
if (process...
ActiveRecord: Passing an empty array into NOT IN will return no records
Caution when using .where
to exclude records from a scope like this:
# Fragile - avoid
User.where("id NOT IN (?)", excluded_ids)
When the exclusion list is empty, you would expect this to return all records. However, this is not what happens:
# Broken example
User.where("id NOT IN (?)", []).to_sql
=> SELECT `users`.* FROM `users` WHERE (id NOT IN (NULL))
Passing an empty exclusion list returns no records at all! See below for better implementations.
Rails 4+
Use the .not
method to let Rails do the logic
`...
PostgreSQL: Difference between text and varchar columns
TL;DR PostgreSQL handles Rails 4+ text
and string
columns the same. Some libraries may still reflect on the column type and e.g. render differently sized text fields.
PostgreSQL offers three character types for your columns:
-
character varying(n)
(also calledvarchar
or 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...