Ruby: alias_method conflicting with prepend
alias_method
makes a copy of a method. This works fine until the same method is overridden using prepend
. If the prepend
is executed after alias_method
, you'll either see an infinite loop, or loose the prepended functionality.
Solution
Your options are:
- Ensure
prepend
happens beforealias_method
- If you can control the
alias_method
invocation: rewrite to the modern & betterprepend
- If you can only control the
prepend
invocation: rewrite to the legacyalias_method
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...
How to pretty print all values in a Redis database
With this Ruby script you can print all values in a Redis database to your console (derived from this bash script).
Note: Do not run this command in production. This is for debugging purposes only.
def pretty_print_redis(redis)
redis.keys.each_with_object({}) do |key, hash|
type = redis.type(key)
hash[key] = case type
when 'string'
redis.get(key)
when 'hash'
redis.hgetall(key)
when 'list'
redis.lrange(key, 0, -1)
when 'set'
redis.smembers(...
ASDF: A Version Manager To Rule Them All
tl;dr
asdf
allows you to manage multiple runtime versions with a single CLI tool and is backwards compatible by supporting existing config files, like e.g..nvmrc
or.ruby-version
.
Getting Started
- Disable rbenv
1.1 Delete or comment outsource /home/$user/.rbenvrc
in~/.profile
1.2 Delete or comment oureval "$(rbenv init -)"
in~/.bashrc
or~/.zshrc
1.3 To take effect you may have to restart your shell or log out and log in again from your current linux session - Install asdf by following the official ...
ActiveRecord::Relation#merge overwrites existing conditions on the same column
In Ruby on Rails ActiveRecord::Relation#merge
overwrites existing conditions on the same column. This may cause the relation to select more records than expected:
authorized_users = User.where(id: [1, 2])
filtered_users = User.where(id: [2, 3])
authorized_users.merge(filtered_users).to_sql
# => SELECT * FROM users WHERE id IN (2, 3)
The merged relation select the users (2, 3)
, although we are only allowed to see (1, 2)
. The merged result should be (2)
.
This card explores various workarounds to combine two scopes so t...
How to create a multiline map in SASS/SCSS
If you want to to create maps within SASS/SCSS-files, it normally works like this:
$some-map: (key1: value1, key2: value2)
However, some maps can get big really fast, if they are being used to contain all of the project's icon names and their sizes for example.
Therefore splitting a map into multiple lines, like we do it in Ruby with big hashes, would become really handy.
Unfortunately SASS doesn't support multiline maps. There has been an open issue since 2011 and it hasn't been...
How Haml 6 changes attribute rendering, and what to do about it
Haml 6 was a major rewrite with performance in mind. To achieve a performance improvement of 1.7x, some design trade-offs had to be made. The most notable change might be the simplified attribute rendering.
In Haml 5, attribute rendering knew two special cases: an attribute with value true
would be rendered without a value, an attribute with a falsy value would not be rendered at all. All other values would just be rendered as attribute values.
According to the Haml maintai...
Using Capybara finder methods with arbitrary matching conditions
Capybara has a variety of finder methods like find_button
to help you look up DOM elements. There are also matchers like have_field
to make expectations during tests.
These methods also have a number of options to influence the lookup. E.g. the :disabled
option lets you control whether Capybara will match disabled fields.
If you have a matching condition that cannot be expressed by the existing Capybara opt...
Rails: Your index actions probably want strict_loading
By activating strict_loading
you force developers to address n+1 queries by preloading all associations used in the index view. Using an association that is not preloaded will raise an ActiveRecord::StrictLoadingViolationError
.
I think it's a good default to activate strict_loading
in your controllers' #index
actions. This way, when a change introduces an n+1 query, you...
How to exclusively lock file access in ruby
We will achieve this by creating a block accepting method to optionally create and then lock a .lock
File of the underlying accessed file.
Why create a .lock
file?
- The main advantage of creating a
.lock
file is that#flock
might block some operations and require the index node of the file to be consistent. Some operations might change that index node. - In some cases it might also be convenient to just read/write the lock file first and update the other file afterwards or vice versa, such that breaking of a process does not...
How to enable template coverage support for simplecov
Since Ruby 3.2.0 you can measure coverage support for eval
statements and support has been added for the simplecov gem as well.
This allows to track coverage across ruby templates such as haml, erb, ...
Simply set this within simplecov
SimpleCov.start do
enable_coverage_for_eval
end
Heads up: Deployment with newly generated SSH key (using ED25519) might fail
If you use a newer SSH key generated with the ED25519 algorithm instead of RSA (see Create a new SSH key pair), the deployment with Capistrano may fail with the following message:
The deploy has failed with an error: unsupported key type `ssh-ed25519'
net-ssh requires the following gems for ed25519 support:
* ed25519 (>= 1.2, < 2.0)
* bcrypt_pbkdf (>= 1.0, < 2.0)
See https://github.com/net-ssh/net-ssh/issues/565 for more information
Gem::LoadError : "ed25519 i...
Issue Checklist Template
This is a checklist I use to work on issues. For this purpose I extracted several cards related to the makandra process and ported them into a check list and refined that over time a little bit.
This task list is divided by the Gate keeping process in the following steps:
1. Starting a new feature
2. Working on the issue
3. Finishing a feature
4. After Review
Here are some ti...
Rails: Using database default values for boolean attributes
In the past we validate and set default values for boolean attributes in Rails and not the database itself.
Reasons for this:
- Older Rails didn't support database defaults when creating new records
- Application logic is "hidden" in the database
An alternative approach, which currently reflects more the general opinion of the Rails upstream on constraints in the database, is adding default values in the schema of the database itself. We also ...
HTTP headers can only transport US-ASCII characters safely
HTTP header values must only contain low-ASCII (7-bit) characters for safe transport. From RFC 7230:
Historically, HTTP has allowed field content with text in the ISO-8859-1 charset [ISO-8859-1], supporting other charsets only through use of [RFC2047] encoding. In practice, most HTTP header field values use only a subset of the US-ASCII charset [USASCII]. Newly defined header fields SHOULD limit their field values to US-ASCII octets.
If you need to transport 8-bit+ characters (e.g...
git: find the version of a gem that releases a certain commit
Sometimes I ran across a GitHub merge request of a gem where it was not completely obvious in which version the change was released. This might be the case for a bugfix PR that you want to add to your project.
Git can help you to find the next git tag that was set in the branch. This usually has the name of the version in it (as the rake release
task automatically creates a git tag during release).
git name-rev --tags <commit ref>
Note
The more commonly used
git describe
command will return the last tag before a c...
Postgres: DISTINCT ON lets you select only one record per ordered attribute(s) for each group
-
To retrieve only unique combinations of the selected attributes: You can omit rows, where all selected columns are equal with the
DISTINCT
statement. -
To retrieve the group wise maximum of certain columns: You can keep only one record for each group with the
DISTINCT ON
statement, to omit equal rows within each specified group.
Use case
You have a query where you want only one record for a set of specifically ordered attributes.
How to use?
Let's say we look at the example how to query only the latest post for each user:
...
Dynamic super-overridable methods in Ruby – The Pug Automatic
How a macro can dynamically define a method that can be overridden with super
in the same class.
You can use the with_module_inheritance
helper below if you want. It can be handy to make parts of a modularity trait super
-able.
# ./lib/ext/module/with_module_inheritance.rb
#
# This macro allows you to define methods in a modularity trait that can be
# modified using the `super` keyword
# See https://thepugautomatic.com/2013/07/dsom/
module WithModuleInheritance
def with_module_inher...
Fixing wall of net/protocol warnings
After upgrading to Rails 6.1.7.2 one of our apps printed a wall of warnings while booting:
/var/www/app/shared/bundle/ruby/2.6.0/gems/net-protocol-0.2.1/lib/net/protocol.rb:68: warning: already initialized constant Net::ProtocRetryError
/home/deploy-app/.rbenv/versions/2.6.10/lib/ruby/2.6.0/net/protocol.rb:66: warning: previous definition of ProtocRetryError was here
/var/www/app/shared/bundle/ruby/2.6.0/gems/net-protocol-0.2.1/lib/net/protocol.rb:214: warning: already initialized constant Net::BufferedIO::BUFSIZE
/home/deploy-app/.rben...
Signed URLs with Ruby on Rails
Using ActiveRecord's #signed_id
and .find_signed
methods you can create URLs that expire after some time. No conditionals or additional database columns required.
Why Sidekiq Jobs should never be enqueued in an `after_create` or `after_save` callback
When an object is created / updated, various callbacks are executed in this order:
before_validation
after_validation
before_save
around_save
before_create
around_create
after_create
after_save
after_commit / after_rollback
Thus, each of these callbacks is executed at a specific time in the life cycle of the object. This is important because this point in time determ...
Bundler 2.3 honors the version specified in `BUNDLED_WITH`
Bundler so far ignored the version specified under BUNDLED_WITH
in the Gemfile.lock
. This had two annoying consequences:
- If the bundler version on your system was lower than in the
Gemfile.lock
, you got an error message and had to manually install the correct version. - If the bundler version on your system was higher than in the
Gemfile.lock
, bundler silently updated the version in theGemfile.lock
to your system's bundler version. To avoid this, you had to always specify, which version you want to use for each bundler c...
Ruby object equality
TLDR
if you define a equality method for a class you must also implement
def hash
.
Ruby has a lot of methods that have to do something with equality, like ==
, ===
, eql?
, equal?
. This card should help you differentiate between those and give you hints on how to implement your own equality methods in a safe manner.
Differences between the methods
for everyday use: ==
When you compare two objects in ruby, you most often see the use of foo == bar
. By default the ==
operator inherits from Object
and is impl...
Timecop: reset after each test
Timecop is a great gem to set the current time in tests. However, it is easy to introduce flakyness to your test suite when you forget to reset the time after the test.
This might be the case if:
- a test freezes time and a later test does not work for frozen time
- a later test needs the real current date to work correctly
Often you only notice these kinds of errors in rare cases when tests are executed in a particular order.
A way to avoid this is by using block notation (`Timecop.travel(...) ...