How to organize monkey patches in Ruby on Rails projects
As your Rails project grows, you will accumulate a number of small patches. These will usually fix a bug in a gem, or add a method to core classes.
Instead of putting many files into config/initializers, I recommend to group them by gem in lib/ext:
lib/
ext/
factory_girl/
mixin.rb
carrierwave/
change_storage.rb
fix_cache_ids.rb
sanitize_filename_characters.rb
ruby/
range/
covers_range.rb
array/
dump_to_excel.rb
xss_aware_join.rb
enumerable/
...
ActiveType::Object: Be careful when overriding the initialize method
Background:
ActiveType::Object inherits from ActiveRecod::Base and is designed to behave like an ActiveRecord Object, just without the database persistence.
Don't remove any of the default behavior of the initialize method!
If you have a class which inherits from ActiveType::Object and you need to override the #initialize method, then you should be really careful:
- Always pass exactly one attribute.
ActiveRecod::Baseobjects really want to get their arguments processable as keyword arguments. Don't change the syntax, or y...
Temporary solution for connection errors with rubygems
The problem
If you're experiencing that your bundle install command fails with an error message like this, rubygems.org might have issues with their ipv6 connectivity:
$ bundle install
Fetching source index from https://rubygems.org/
Retrying fetcher due to error (2/4): Bundler::HTTPError Could not fetch specs from https://rubygems.org/ due to underlying error <timed out (https://rubygems.org/specs.4.8.gz)>
The (a little bit dirty) possible solution
If that's actually the case, then you can try to deprioritize the ipv...
Debug flaky tests with an Unpoly observeDelay
The problem
Unpoly's [up-observe], [up-autosubmit] and [up-validate] as well as their programmatic variants up.observe() and up.autosubmit() are a nightmare for integration tests.
Tests are usually much faster than the configured up.form.config.observeDelay. Therefore, it may happen that you already entered something into the next field before unpoly updates that field with a server response, discarding your changes.
The steps I wait for active ajax requests to complete (if configured) and capybara-lockstep can catch some ...
Rails: Validations of Dates, Numerics and Strings with ComparisonValidator
tl;dr
Since Rails
7+you can useComparisonValidatorfor validations likegreater_than,less_than, etc. on dates, numerics or strings.
Example
We have a model for booking a trip. This model has mandatory attributes to enforce dates for the start and the end.
# == Schema Information
#
# start_date :date
# end_date :date
# ...
class TripBooking < ApplicationRecord
validates :start_date, presence: true
validates :end_date, presence: true
end
These validations are enough. We also want to ensure, th...
How to emulate simple classes with plain JavaScript
If you want a class-like construct in JavaScript, you can use the module pattern below. The module pattern gives you basic class concepts like a constructor, private state, public methods.
Since the module pattern only uses basic JavaScript, your code will run in any browser. You don't need CoffeeScript or an ES6 transpiler like Babel.
A cosmetic benefit is that the module pattern works without the use of this or prototypes.
Example
Here is an example for a Ruby class that we want to translate into Javascript using the module patter...
Whitelist Carrierwave attributes correctly
Say you have a User with a Carrierwave attribute #avatar:
class User < ActiveRecord::Base
mount_uploader :avatar, AvatarUploader
end
When whitelisting the avatar field in the controller, you might do this:
params[:user].permit(:avatar)
But you probably want this:
params[:user].permit(:avatar, :avatar_cache, :remove_avatar)
In this example:
-
:avatar_cacheallows a newly upload image to persist through form roundtrips in the case of validation errors (something that isn't possibl...
Git: Switch
tl;dr
git checkoutis the swiss army of git commands. If you prefer a semantically more meaningful command for branch related tasks, usegit switchinstead.You can use
git switchsince git 2.23.
Switch Branch
git branch
# * master
# feature-branch
git switch feature-branch
git branch
# master
# * feature-branch
git switch -
git branch
# * master
# feature-branch
Info
For this use case you can of course also use
git checkout.
Switch on a Remote Branch
git branch -a
# * m...
Exploring the disk usage with ncdu
Ncdu is a disk usage analyzer with an ncurses interface. It is designed to find space hogs on a remote server where you don’t have an entire graphical setup available, but it is a useful tool even on regular desktop systems. Ncdu aims to be fast, simple and easy to use, and should be able to run in any minimal POSIX-like environment with ncurses installed.
Alternatives
du -hs * | sort -h- Disk Usages Analyser
Careful when using Time objects for generating ETags
You can use ETags to allow clients to use cached responses, if your application would send the same contents as before.
Besides what "actually" defines your response's contents, your application probably also considers "global" conditions, like which user is signed in:
class ApplicationController < ActionController::Base
etag { current_user&.id }
etag { current_user&.updated_at }
end
Under the hood, Rails generates an ETag header value like W/"f14ce3710a2a3187802cadc7e0c8ea99". In doing so, all objects from that etagge...
Rules of thumb against flaky specs
Here are a few common patterns that will probably lead to flaky specs. If you notice them in your specs, please make sure that you have not introduced a flaky spec.
Using RSpec matchers
One rule of thumb I try to follow in capybara tests is using capybara matchers and not plain rspec matchers.
One example:
visit(some_page)
text_field = find('.textfield')
expect(text_field['value']).to match /pattern/
This can work, but is too brittle and flaky. match will not retry or synchronize the value of text_field.
The equivale...
You can implement basic object-fit behavior with background images
So you want to use object-fit, but you also need to support Internet Explorer.
One option is to use lazysizes as a kinda-polyfill. Another option is to implement the requirement with background-size: contain, and background-size: cover, which is supported in IE9+.
E.g. to make an image cover a 100x100 px² area, cropping the image when nece...
Detect the current Rails environment from JavaScript or CSS
Detecting if a Javascript is running under Selenium WebDriver is super-painful. It's much easier to detect the current Rails environment instead.
You might be better of checking against the name of the current Rails environment. To do this, store the environment name in a data-environment of your <html>. E.g., in your application layout:
<html data-environment=<%= Rails.env %>>
Now you can say in a pi...
In MySQL, a zero number equals any string
In MySQL comparing zero to a string 0 = "any string" is always true!
So when you want to compare a string with a value of an integer column, you have to cast your integer value into a string like follows:
SELECT * from posts WHERE CAST(posts.comments_count AS CHAR) = '200'
Of course this is usually not what you want to use for selecting your data as this might cause some expensive database operations. No indexes can be used and a full table scan will always be triggered.
If possible, cast the compared value in your application to...
A community-curated list of flexbox issues and cross-browser workarounds for them
This repository is a community-curated list of flexbox issues and cross-browser workarounds for them. The goal is that if you're building a website using flexbox and something isn't working as you'd expect, you can find the solution here.
As the spec continues to evolve and vendors nail down their implementations, this repo will be updated with newly discovered issues and remove old issues as they're fixed or become obsolete. If you discover a bug that's not listed here, please report it so everyone else can benefit.
How to fix "Exit with code 1 due to network error: ProtocolUnknownError" with wkhtmltopdf
New versions of wkhtmltopdf dissallow file:// URLs by default. You can allow them by passing --enable-local-file-access.
If you are using PDFKit, set the option
PDFKit.configure do |config|
config.default_options = {
enable_local_file_access: true,
}
end
This will be necessary in many setups to allow wkhtmltopdf to fetch assets (such as stylesheets) from the filesystem.
Note on security
Allowing this poses some risk when you render user input, since it might be feasible to include data from the local filesyste...
SEO: The subtle differences of robots.txt disallow vs meta robots no-index
The robots.txt file and <meta name="robots"> HTML tag can be used to control the behavior of search engine crawlers. Both have different effects.
robots.txt
Marking a URL path as "disallowed" in robots.txt tells crawlers to not access that path.
robots.txt is not a guarantee for exclusion from search engine results.
A "disallowed" URL might be known from an external link, and can still be displayed for a matching search.
Example: even if/adminis disallowed in robots.txt, `/admin/som...
Do not use "flex: 1" or "flex-basis: 0" inside "flex-direction: column" when you need to support IE11
Flexbox is awesome. Most of it even works in IE11, but flex: 1 won't work reliably in Internet Explorer.
This it because implicitly sets flex-basis: 0 which IE fails to support properly.
Example
Consider the following HTML and CSS.
<div class="container">
<div class="child">
foo
</div>
<div class="bar">
bar
</div>
</div>
.container {
display: flex;
flex-direction: column;
}
.child {
flex: 1;
}
See it in action at Plunker.
Background
...
Project management best practices: Standup
If the project team consists of at least 2 members, do a daily standup. It should not take much longer than 15 minutes.
Format
Tell everyone else
- what you did yesterday
- what you intend to do today
- where you might need help or other input
- if there are new developments everyone needs to know about
A "still working on X, will probably be done today" is totally fine. No need to tell a long story.
If you are out of work, find a new story with the others.
If there are new stories in the backlog, look at them and
- make sure ev...
Prefer using Dir.mktmpdir when dealing with temporary directories in Ruby
Ruby's standard library includes a class for creating temporary directories. Similar to Tempfile it creates a unique directory name.
Note:
- You need to use a block or take care of the cleanup manually
- You can create a prefix and suffix e.g.
Dir.mktmpdir(['foo', 'bar']) => /tmp/foo20220912-14561-3g93n1bar - You can choose a different base directory than
Dir.tmpdire.g. `Dir.mktmpdir('foo', Rails.root.join('tmp')) => /home/user/rails_example/tmp/foo20220912-14...
How to get the git history of a file that does not exist anymore
If you want to see the git history of a project file, that doesn't exist anymore, the normal git log <path_to_file> won't work. You have to add certain flags to make it work:
git log --all --full-history -- <path_to_file>
Rails: Comparison of Dates - before? and after?
tl;dr
Since Rails
6+you can usebefore?andafter?to check if a date/time is before or after another date/time.
Example
christmas = Date.parse('24.12.2022')
date_of_buying_a_gift = Date.parse('12.12.2022')
date_of_buying_a_gift.before?(christmas)
# => true
# Now you are well prepared for Christmas!! ;)
date_of_buying_a_gift = Date.parse('26.12.2022')
date_of_buying_a_gift.after?(christmas)
# => true
# Now you are too late for christmas! :(
Hint
If you want to check if a date/time is between to ot...
Ruby and Rails: Debugging a Memory Leak
A memory leak is an unintentional, uncontrolled, and unending increase in memory usage. No matter how small, eventually, a leak will cause your process to run out of memory and crash.
If you have learned about a memory leak, looking at the number of Ruby objects by type can help you track it down:
> pp ObjectSpace.count_objects
{:TOTAL=>77855,
:FREE=>4526,
:T_OBJECT=>373,
:T_CLASS=>708,
:T_MODULE=>44,
:T_FLOAT=>4,
:T_STRING=>65685,
:T_REGEXP=>137,
:T_ARRAY=>984,
:T_HASH=>87,
:T_STRUCT=>12,
:T_BIGNUM=>2,
:T_FILE=>3,
:T_D...
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 ...