Deployment: Merge consecutive commits without cherry-picking
You want to deploy new features but the latest commits are not ready for production? Then use git merge master~n
to skip the n-last commits.
Tip
A big advantage of merging vs. cherry-picking is that cherry-picking will create copies of all picked commits. When you eventually do merge the branch after cherry-picking, you will have duplicate commit messages in your history.
Example
It's time for a production deployment!
git log --pretty=format:"%h - %s" --reverse origin/production..origin/master
0e6ab39f - Feature A
6396...
The Framework Field Guide - Fundamentals | Unicorn Utterances
I used two lab days to read the The framework field guide - Fundamentals, the first of a three part series to learn the basics of frontend technologies. I can highly suggest it for learning the fundamentals. 'The framework field guide' is written by Unicron Utterances and there side has many high quality articles on web development and computer science related to programming.
[The Framework Field Guide](https://unicorn-ut...
RSpec: Applying stubs only within a block
When you mocked method calls in RSpec, they are mocked until the end of a spec, or until you explicitly release them.
You can use RSpec::Mocks.with_temporary_scope
to have all mocks applied inside a block to be released when the block ends.
Example:
RSpec::Mocks.with_temporary_scope do
allow(Rails).to receive(:env).and_return('production'.inquiry)
puts Rails.env # prints "production"
end
puts Rails.env # prints "test"
Note that, when overriding pre-existing mocks inside the block, they are not reverted to the previously ...
Configuring ActionMailer host and protocol for URL generation
When you generate a URL in a mailer view, ActionMailer
will raise an error unless you previously configured it which hostname to use.
There are two options to set the default_url_options
of ActionMailer:
- Hardcoded solution (preferred solution when using Rails with ActiveJob/Sidekiq or Cronjobs)
- Dynamic solution
1. Hardcoded solution
When you are sending mails from outside the request cycle, e.g. ActiveJob/Sidekiq or Cronjobs, y...
Modern HTTP Status codes for redirecting
Formerly 301 (Moved Permanently) and 302 (Found) were used for redirecting. Browsers did implement them in different ways, so since HTTP 1.1 there are some new status codes which allow for finer distinctions.
The interesting part is how non-GET requests are handled by the redirect. It is preferrable to use the newer status code to avoid unexpected behavior.
303 See Other
The response to the request can be found under anot...
How to list updateable dependencies with Bundler and Yarn
Bundler
bundle outdated [--filter-major|--filter-minor|--filter-patch]
Example output for bundle outdated --filter-major
Other examples
A useful flag is --strict
as it will only list versions that are allowed by your Gemfile requirements (e.g. does not show rails update to 6 if your Gemfile has the line gem 'rails', '~>5.2'
).
I also experienced that doing upgrades per group (test, development) are easier to do. Thus --groups
might also be helpful.
$ bundle...
Caveat when using Rails' new "strict locals" feature
In Rails 7.1 it has become possible to annotate partials with the locals they expect:
# partial _user_name.erb
<%# locals: (user:) %>
<%= user.name %>
# view
<%= render 'user_name' %> <%# this raises an ArgumentError %>
Unfortunately, when some other code in that template raises an ArgumentError
(for example an error in the User#name
method) you will end up with a confusing stacktrace that looks like you have an error in your render
call.
If th...
How to tell ActiveRecord how to preload associations (either JOINs or separate queries)
Remember why preloading associations "randomly" uses joined tables or multiple queries?
If you don't like the cleverness of this behavior, you can explicitely tell ActiveRecord how to preload associations with either JOINs
or separate queries.
This card gives an overview of the different options to preload associations, but
__Whic...
Async control flow in JavaScript: Promises, Microtasks, async/await
Slides for Henning's talk on Sep 21st 2017.
Understanding sync vs. async control flow
Talking to synchronous (or "blocking") API
print('script start')
html = get('/foo')
print(html)
print('script end')
Script outputs 'script start'
, (long delay), '<html>...</html>'
, 'script end'
.
Talking to asynchronous (or "evented") API
print('script start')
get('foo', done: function(html) {
print(html)
})
print('script end')
Script outputs 'script start'
, 'script end'
, (long ...
Authorize allowed values with assignable_values
All our projects have enum-like requirements like this:
- An attribute value must be included in a given set of values.
- The list of allowed values must be retrievable in order to render
<select>
boxes. - Each value has a humanized label.
- Sometimes there is a default value.
Most of the time, this requirement is also needed:
- The list of assignable values depends on the user who is currently signed in.
In our past projects there are many different solutions for these related requirements, e.g. ChoiceTrait
, methods like `available_...
Searchkick: async reindexing fails for rails 7 with redis 4
After an upgrade to rails 7 I noticed that async reindexing jobs of Searchkick were failing for Model.reindex(mode: :async, wait: true)
:
/home/a_user/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/searchkick-5.3.1/lib/searchkick/relation_indexer.rb:142:in `block in batch_job': undefined method `call' for nil (NoMethodError)
Searchkick.with_redis { |r| r.call("SADD", batches_key, [batch_id]) }
^^^^^
from /home/a_user/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/searchkick-5.3.1/lib/searchkick.r...
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...
Don't name columns like counter_cache columns in Rails pre v4.2.4
< Rails v4.2.4
ActiveRecord has a feature called counter caching where the containing record in a has_many
relationship caches the number of its children. E.g. when you have House has_many :rooms
, Rails can cache the number of rooms in House#rooms_count
.
Mind that when a model has a column that looks to Rails like a counter-cache column, Rails will apply counter-cache logic to your model, even if you're not using counter caches.
E.g. you have a house with 12...
Ruby: You can nest regular expressions
Ruby lets you re-use existing RegExp
objects by interpolating it into new patterns:
locales_pattern = /de|en|fr|es/i
html_tag_pattern = /<html lang="#{locales_pattern}">/
Any modifiers like /i
or /x
will be preserved within the interpolated region, which is pretty cool. So in the example above only the interpolated locales are case-insensitive, while the pattern around it (/<html .../
) remains case-sensitive.
routing-filter is broken with Rails 7.1
If you are using the routing-filter gem in your Rails 7.1 app for managing URL segments for locales or suffixes, you will notice that the filters do no longer apply, routes are broken and the necessary parameters are no longer extracted. That is because routing-filter patches Rails' find_routes
-method to get the current path and apply its defined filters on it. These filters then modify the params that are handed over to your controller action. This way you receive a locale
parameter from a ...
Active Record: Never use optional with a symbol, lambda or proc
tl;dr
Do not use the option
optional
on association declarations with a symbol, lambda or proc.
Explanation
Association declarations like belongs_to
support the option optional
. This option does not support symbols, lambdas or procs. If you do so, this will always result in optional: true
. So your records can miss a presence validation if optional
is used with a symbol, lambda or proc.
If you set t...
Bash script to list git commits by Linear ID
As we're switching from PT to Linear, I've updated the existing bash script to work for commits that are referencing Linear IDs.
A core benefit of our convention to prefix commits by their corresponding issue ID is that we can easily detect commits that belong to the same issue. You can either do that manually or use the bash script below. It can either be placed in your .bashrc
or a...
Ruby: How to use global variables for a conditional debugger
You can share a state in Ruby with global variables. Even if you should avoid them whenever possible, for debugging an application this could be temporary quite handy.
Example:
class User
after_save { byebug if $debug; nil }
def lock
self.locked = true
save
end
end
Rspec.describe User do
let(:user) { create(:user) }
before do
# Many users are created and saved in this hook, but we don't want the debugger to stop for them...
Heads up: Sidekiq per default silently fails when retries are exhausted!
For Sidekiq to be able to retry your jobs it has to be able to catch errors that occur while a job is executed.
Per default, Sidekiq will not raise / notify you if the retry count is exhausted. It will only copy the job to the dead queue (see wiki).
If you want to get notified, you have to implement it in your worker explicitly with a sidekiq_retries_exhausted
-block, e.g. like this:
class DownloadWorker
include Sidekiq::Worker
# Import jobs are retried a few time...
RSpec: Using helpers in view specs
If an view spec crashes due to undefined helper methods, you can enable this option:
# config/application.rb
config.action_controller.include_all_helpers = true
If you cannot use this setting, your spec can include individual helper modules like this:
describe 'some view', type: :view do
helper SomeHelper
helper OtherHelper
it 'renders' do
render 'view_that_uses_helpers'
end
end
Alternatively you can also explicitly include *all help...
Rails: Integrating shoelace components
shoelace is a library of web components. Here is a proof of concept how a integration (slightly different as the official docs for Rails) might look like in Rails + webpack + Unpoly. Also see the HN discussion for pro and cons.
Capybara: Quick checking for element presence (without retries or timeout)
Element finding is a central feature of Capybara. Since #find
is normally used to get elements from the current page and interact with them, it's a good thing that some Capybara drivers (e.g. Selenium) will wait an amount of time until the expected element shows up. But if Capybara cannot #find
it at all, you'll get an error.
if page.find('.that-element')
# Do something
else
# Never happens because #find raises
end
In order to simply check whether an element is present, without errors raised, you can use #has_css?
. It...
Nested ActiveRecord transaction pitfalls
When working with custom transactions and use ActiveRecord::Rollback
you may encounter unexpected behaviour if you try to roll back your changes.
tl;dr
When using nested transactions, ActiveRecord::Rollback
might not do what you expect, since it will only roll back the inner, but not the outer transaction.
You can fix this behavior by using transaction(joinable: false)
but this leads to a bunch of different problems.
When you don't need an explicit ActiveRecord::Rollback
, don't worry about any of this and just use a plan `tran...
Capybara: Find an element that contains a string
There is no CSS selector for matching elements that contains a given string ยน. Luckily, Capybara offers the :text
option to go along with your selector:
page.find('div', text: 'Expected content')
You can also pass a regular expression!
page.find('div', text: /Expected contents?/i)
Note that if your CSS selector is as generic as div
, you might get a lot more results than you expect. E.g. a <div class="container">
that surrounds your entire layout will probably also contain that text (in a descendant) and ...