Git: Advisory for cherry-picks to production branches
We often have a separate production branch that lags a bit behind the more cutting edge main branch. Sometimes you want to move some, but not all commits from main to production. This can be done with a git cherry-pick.
However, this may lead to considerable pain later, since git does not understand the commits are actually "the same". Hazards are unnecessary and hard to resolve conflicts as well as incorrect auto-merges.
In order to avoid this, always merge the production branch back to the main after the cherry-pick. Even t...
Use find_in_batches or find_each to deal with many records efficiently
Occasionally you need to do something directly on the server -- like having all records recalculate something that cannot be done in a migration because it takes a long time.
Let's say you do something like this:
Project.all.each(&:recalculate_statistics!)
Even though you may have been successful with this on your development machine or the staging server, keep in mind that production machines often hold a lot more records. Using all may just work, even with lots of records, but when you iterate over such records and fetch a...
How DECIMAL columns deal with numbers exceeding their precision or scale
When storing floating-point numbers such as prices or totals in an SQL database, always use a DECIMAL column. Never use FLOAT or kittens will die.
DECIMAL columns are parametrized with a precision and a scale. These parameters describe which numbers can be stored in that column. E.g. a decimal with a precision of 5 and a scale of 2 can store numbers from -999.99 to 999.99, but not 1000 or 1.234.
This card explains what various databases do when you try to store a number in a DECIMAL field, and that number exceeds that colum...
Cancelling event propagation
Within an event handler, there are multiple methods to cancel event propagation, each with different semantics.
-
event.preventDefault()Only prevents the default browser behavior for the click, i.e. going to a different url or submitting a form.
When invoked on a
touchstartevent, this also prevents mouse events likeclickto be triggered. -
event.stopPropagation()Prevents the event from bubbling up the DOM.
-
`event.st...
ActiveRecord: Creating many records works faster in a transaction
When you need to insert many records into the same table, performance may become an issue.
What you can do to save time is to open a transaction and save multiple records within that transaction:
transaction do
500.times { Model.create! }
end
Although you will still trigger 500 INSERT statements, they will complete considerably faster.
When I tried it out with a simple model and 500 iterations, the loop completed in 1.5 seconds vs. 6 seconds without a transaction.
Alternative
Another fast way to insert many ...
Why two Ruby Time objects are not equal, although they appear to be
So you are comparing two Time objects in an RSpec example, and they are not equal, although they look equal:
expected: Tue May 01 21:59:59 UTC 2007,
got: Tue May 01 21:59:59 UTC 2007 (using ==)
The reason for this is that Time actually tracks fractions of a second, although #to_s doesn't say so and even though you probably only care about seconds. This means that two consecutive calls of Time.now probably return two inequal values.
Consider freezing time in your tests so it is not dependent on the speed of the executi...
Testing if two date ranges overlap in Ruby or Rails
A check if two date or time ranges A and B overlap needs to cover a lot of cases:
- A partially overlaps B
- A surrounds B
- B surrounds A
- A occurs entirely after B
- B occurs entirely after A
This means you actually have to check that:
- neither does A occur entirely after B (meaning
A.start > B.end) - nor does B occur entirely after A (meaning
B.start > A.end)
Flipping this, A and B overlap iff A.start <= B.end && B.start <= A.end
The code below shows how to implement this in Ruby on Rails. The example is a class `Interv...
How to define constants with traits
When defining a trait using the Modularity gem, you must take extra steps to define constants to avoid caveats (like when defining subclasses through traits).
tl;dr
In traits, always define constants with explicit
self.
If your trait defines a constant inside the as_trait block, it will be bound to the trait module, not the class including the trait.
While this may seem unproblematic at first glance, it becomes a problem when including trai...
Don't mix Array#join and String#html_safe
You cannot use Array#join on an array of strings where some strings are html_safe and others are not. The result will be an unsafe string and will thus be escaped when rendered in a view:
unsafe_string = '<span>foo</span>'
safe_string = '<span>bar</span>'.html_safe
[unsafe_string, safe_string].join(' ') # will incorrectly render as '<span>foo</span><span&t;bar</span>'
Bad
The solution is not to call html_safe on the joined array and if you thought it would be, you [don't understand how XSS prot...
Preloaded associations are filtered by conditions on the same table
When you eagerly load an association list using the .include option, and at the same time have a .where on an included table, two things happen:
- Rails tries to load all involved records in a huge single query spanning multiple database tables.
- The preloaded association list is filtered by the
wherecondition, even though you only wanted to use thewherecondition to filter the containing model.
The second case's behavior is mostly unexpected, because pre-loaded associations usually don't care about the circumstances under whi...
Copying validation errors from one attribute to another
When using virtual attributes, the attached trait can be useful to automatically copy errors from one attribute to another.
Here is a typical use case where Paperclip creates a virtual attribute :attachment, but there are validations on both :attachment and :attachment_file_name. If the form has a file picker on :attachment, you would like to highlight it with errors from any attribute:
class Note < ActiveRecord::Base
has_attached_file :attachment
validates_attachment_presence :a...
Know your Haml comments
There are two distinct ways of commenting Haml markup: HTML and Ruby.
HTML comments
This will create an HTML comment that will be sent to the client (aka browser):
/= link_to 'Example', 'www.example.com'
This produces the following HTML:
<!-- = link_to 'Example', 'www.example.com' -->
Only use this variant if you need the comment to appear in the HTML.
Ruby comments
This will comment code so it will not be sent to the client:
-# = link_to 'foo'
99% of the time you'll be adding notes f...
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...
Rails: Preloading associations in loaded records
Sometimes you want to fetch associations for an ActiveRecord that you already loaded, e.g. when it has deeply nested associations.
Edge Rider gives your models a static method preload_associations. The method can be used to preload associations for loaded objects like this:
class UsersController < ApplicationController
def show
@user = User.find(params[:id])
@user.preload_associations(threads: { posts: :author }, messages: :sender)
end
end
The attached initializers re...
Use the Git stash without shooting yourself in the foot
The Git stash does not work like a one-slot clipboard and you might shoot yourself in the foot if you pretend otherwise.
In particular git stash apply does not remove the stashed changes from the stash. That means you will probably apply the wrong stash when you do git stash apply after a future stashing.
To keep your stash clean, you can use
git stash pop
instead.
Another way to look at it:
git stash pop
is the same as
git stash apply && git stash drop
Notice: In case of a conflict git will not pop th...