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...
Canceling 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...
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...