Statistics and Reports on Web Performance Optimization
Case studies and experiments demonstrating the impact of web performance optimization (WPO) on user experience and business metrics.
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...
Rails: Custom validator for "only one of these" (XOR) presence validation
For Rails models where only one of multiple attributes may be filled out at the same time, there is no built-in validation.
I've seen different solutions in the wild, each with different downsides:
- Private method referenced via
validate
: works, but is barely portable and clutters the model. - Multiple presence validations with "if other is blank" each: looks pretty, but is incorrect as it allows both values to be filled in; also the error messages for a blank record are misleading.
Here is a third option: Write a custom validator to ...
Creating a Rails application in a single file
Greg Molnar has written a neat article about creating a single-file Rails app.
This is not meant for production use but can be useful to try things out, e.g. when hunting down a bug or embedding a Rails app into the tests of a gem.
What you do is basically:
- Put everything (gems, application config, database migrations, models, controllers) into a single
.ru
file, likeapp.ru
. - Run it via
rackup app.ru
. (Hint: if your file is calledconfig.ru
, you can just run `rac...
Git: removing feature branches on merge
When working with feature branches, stale branches pile up over time. It's best to remove them right after merge, locally and on the remote, but it is a little tedious: you need to remember it, and perform the few steps manually each time.
Enter Git hooks. The folks at Liquid Light have built a little post-merge hook that will delete a feature branch on confirmation....
Rails: Fixing the memory leak / performance issues in prepend_view_path
Recently we detected a memory leak in one of our applications. Hunting it down, we found that the memory leak was located in Rails' #prepend_view_path
. It occurs when the instance method prepend_view_path
is called in each request, which is a common thing in a multi-tenant application.
On top of leaking memory, it also causes a performance hit, since templates rendered using the prepended view path will not be cached and compiled anew on each request.
This is not a new memory leak. It was [first reported in in 2014](https://github.com/...
A short overview of common design patterns implemented within Rails
The linked content includes a few design patterns implemented with Ruby on Rails.
What is the card indented to achieve?
- You can use the pattern names for code reviews, so all parties know with only a few words which change is requested. Example: "Please use a form object here"
- You can learn about new code patterns
- You should read the sections "Advantages of using design patterns" and "Disadvantages of using design patterns in a wrong way", since design patterns do not replace good code
Included Design Patterns: Service, Value objec...
PostgreSQL: How to show database size
SELECT pg_size_pretty(pg_database_size('some-database'));
Example
SELECT pg_size_pretty(pg_database_size('cards_p'));
----------------
13 GB
(1 row)
SELECT pg_database_size('cards_p');
pg_database_size
------------------
13524832927
(1 row)
Related
Postgresql: Paginate and count in one query using window functions
When paginating records, we usually need to know the number of total records in order to render pagination links. Popular pagination libraries like will_paginate or Kaminari do this for us by simply issuing an extra query, like this:
SELECT post.* FROM posts LIMIT 20 OFFSET 100;
SELECT COUNT(*) FROM posts;
This is fine most of the time. But rarely, you might have very complicated WHERE
conditions or a subquery that takes time to run. In thes...
Josh McArthur: Fancy Postgres indexes with ActiveRecord
I recently wanted to add a model for address information but also wanted to add a unique index to those fields that is case-insensitive.
The model looked like this:
create_table :shop_locations do |t|
t.string :street
t.string :house_number
t.string :zip_code
t.string :city
t.belongs_to :shop
end
But how to solve the uniqueness problem?
Another day, another undocumented Rails feature!
This time, it’s that ActiveRecord::Base.connection.add_index supports an undocumented option to pass a string argument as the v...
Debug file system access in a Rails application
It might sometimes be useful to check whether your Rails application accesses the file system unnecessarily, for example if your file system access is slow because it goes over the network.
The culprit might be a library like carrierwave that checks file existence or modification times, whereas your application could determine all this from your database.
Introducing strace
One option it to use strace for this, which logs all system calls performed by a process.
To do this, start your rails server using something like
DISA...
Capybara: Most okayest helper to download and inspect files
Testing file download links in an end-to-end test can be painful, especially with Selenium.
The attached download_helpers.rb
provides a download_link
method for your Capybara tests. It returns a hash describing the download's response:
details = download_link('Download report')
details[:disposition] # => 'attachment' or 'inline'
details[:filename] # => 'report.txt'
details[:text] # => file content as string
details[:content_type] # => 'text/plain'
Features
Compared to [other approaches](...
Use DatabaseCleaner with multiple test databases
There is a way to use multiple databases in Rails.
You may have asked yourself how you're able to keep your test databases clean, if you're running multiple databases with full read and write access at the same time. This is especially useful when migrating old/existing databases into a new(er) one.
Your database.yml
may look like this:
default: &default
adapter: postgresql
encoding: unicode
username: <%= ENV['DATABASE_USER'] %>
host: <%= ENV['DATABASE...
ActiveRecord: Query Attributes
tl;dr
You can useattribute?
as shorthanded version ofattribute.present?
, except for numeric attributes and associations.
Technical Details
attribute?
is generated for all attributes and not only for boolean attributes.
These methods are using #query_attribute
under the hood. For more details you can see ActiveRecord::AttributeMethods::Query
.
In most circumstances query_attribute
is working like attribute.present?
. If your attribute is responding to :zero?
then you have to be aware that `query_attri...
How to bind an event listener only once with Unpoly
You can use Unpoly's up.on with a named listener function and immediately unbind this event listener with { once: true }
:
up.on('up:fragment:inserted', { once: true }, function () { ... })
In Unpoly 1 you can immediately unregister the listener with up.off:
up.on('up:fragment:inserted', function fragmentInsertedCallback() {
up.off('up:fragment:inserted', fragmentInsertedCallback)
// ... code for the callback function, which should run only once
})
Exam...
JavaScript: Working with Query Parameters
tl;dr: Use the URLSearchParams
API to make your live easier if you want to get or manipulate query parameters (URL parameters).
URLSearchParams
API
The URLSearchParams
API is supported in all major browsers except IE 11.
It offers you a bunch of useful methods:
-
URLSearchParams.append()
- appends a query parameter -
URLSearchParams.delete()
- deletes the specified query parameter -
URLSearchParams.get()
- returns the value of the specified query parameter - `URLSearchP...
Version 5 of the Ruby Redis gem removes Redis.current
Redis.current
will be removed without replacement in redis-rb
5.0.
Version 4.6.0 adds deprecation warnings for Redis.current
and Redis.current=
:
`Redis.current=` is deprecated and will be removed in 5.0.
If your application still uses Redis.current
, you can only fix it by no longer using it. Here is how.
Redis.new when you need it
You can easily instantiate a Redis
client when you need it.
There is probably already a constant like REDIS_URL
that you use to configure Sidekiq or similar. So just use that one.
``...
RSpec: You can super into parent "let" definitions
RSpec's let
allows you to super
into "outside" definitions, in parent contexts.
Example:
describe '#save' do
subject { described_class.new(attributes) }
let(:attributes) { title: 'Example', user: create(:user) }
it 'saves' do
expect(subject.save).to eq(true)
end
context 'when trying to set a disallowed title' do
let(:attributes) { super().merge(title: 'Hello') } # <==
it 'will not save' do
expect(subject.save).to eq(false)
end
end
end
I suggest you don't make a habit of using this regula...
PostgreSQL: "WHERE NOT <column> = '<value>'" statements do not include NULL values
Sometimes we write plain SQL queries in migrations so we don't have to mock ActiveRecord classes. These two migrations do the same:
class Migration1 < ActiveRecord::Migration[5.2]
class User < ActiveRecord::Base; end
def up
add_column :users, :trashed, :boolean
User.update_all(trashed: false)
end
end
class Migration2 < ActiveRecord::Migration[5.2]
def up
add_column :users, :trashed, :boolean
update("UPDATE users SET trashed = #{quoted_false}")
end
end
The plain SQL migration is less code, but h...
Rails 6.1: where.not changes behaviour from NOR to NAND
Since Rails 6.1, if we use where.not
with multiple attributes, it applies logical NAND (NOT(A) OR NOT(B)) instead of NOR (NOT(A) AND NOT(B)). If you do not take care, this change will increase the matched set.
Examples
"Don't send newsletters neither to admins nor to trashed users!" becomes "Don't send newsletters to trashed admins".
User.where.not(role: 'admin', trashed: true)
# Before Rails 6.1, with NOR
=> "SELECT "users".* FROM "users" WHERE "users"."role" != 'admin' AND "users"."trashed" != TRUE"
# Equivale...
Nokogiri: How to parse large XML files with a SAX parser
In my case [...] the catalog is an XML that contains all kinds of possible products, categories and vendors and it is updated once a month. When you read this file with the Nokogiri default (DOM) parser, it creates a tree structure with all branches and leaves. It allows you to easily navigate through it via css/xpath selectors.
The only problem is that if you read the whole file into memory, it takes a significant amount of RAM. It is really ineffective to pay for a server if you need this RAM once a month. Since I don't need to n...
Caching in Rails < 6.1 may down parts of your application when using public cache control
Proxy caching is a good feature to serve your publicly visible application content faster and reduce load on your servers. It is e.g. available in nginx, but also affects proxies delivered by ISPs.
Unfortunately, there is a little problem in Rails < 6.1 when delivering responses for different MIME-types. Say you have an arbitrary route in your Rails application that is able to respond with regular HTML and JSON. By sending the specific MIME type in the Accept
header, you tell the application to either return HTML (text/html
) or JSON (`t...
Spreewald 4.3.3 released
Field error steps
Spreewald's The ... field should have an error
and The ... field should have the error ...
steps now have built-in support for Rails and Bootstrap (v3-v5) error classes. When using Bootstrap, it is no longer necessary to overwrite the steps in your project.
At the same time, support for formtastic has been removed as there were no real use cases. Due to that, no breaking change was introduced, as the amount of users affected by this should be zero (it was neither in the documentation nor tested).
Users may now add...
The TCF 2.0 (Tranparency and Consent Framework) standard, and what you should know about it
The Interactive Advertising Bureau (IAB) is a European marketing association which has introduced a standard how advertising can be served to users in line with the General Data Protection Regulation (GDPR). This standard is called the TCF 2.0 (Transparency and Consent Framework). If you want to integrate any kind of advertising into a website, chances are the advertising network will require your website to implement that standard. This is a very brief overview of what this means:
The basic idea in the TCF 2.0 ...