Today I learned
A collection of insights, tips, and lessons learned from our daily work.
TIL about the simple_format method in Rails: https://api.rubyonrails.org/v8.0/classes/ActionView/Helpers/TextHelper.html
TIL about the four types of documentation
TIL about satisfy in RSpec. https://rspec.info/features/3-13/rspec-expectations/built-in-matchers/satisfy/
TIL that true and false don’t exist in standard C version:
gcc ./foo.c -o c && ./c
./foo.c:18:9: error: use of undeclared identifier 'true'
18 | while(true) {
| ^
1 error generated.
The values true and false can be imported by the stdbool library. Which declares them as integers:
// ...
#define true 1
#define false 0
// ...
Any non-zero value gets interpreted as True.
These are the small things that make me appreciate :ruby: and other high level languages.
Today (while debugging tag concatination) I learned that you can create mutable Strings in Ruby using +"foo" and immutable Strings using -"foo"
https://docs.ruby-lang.org/en/4.0/String.html#method-i-2B
- This one concatinates
label(...) + content_tag(...)
- This one returns just a mutable variant of the content tag:
label(...)
+ content_tag(...)
I expected + content_tag(...) to raise.
Thanks for
@Lukas Bischof
TIL that Rust doesn’t support traditional parameter overloading but you can still achieve it by implementing generic traits
This doesn’t compile:
impl Noise {
pub fn new(seed: u32) -> Self {
Self {
seed,
perlin: Perlin::new(seed),
}
}
pub fn get(&self, position: DVec3) -> f64 {
self.perlin.get(position.to_array())
}
pub fn get(&self, position: DVec2) -> f64 {
self.perlin.get(position.to_array())
}
}
This does:
pub trait NoiseSample<T> {
fn get(&self, position: T) -> f64;
}
impl Noise {
pub fn new(seed: u32) -> Self {
Self {
seed,
perlin: Perlin::new(seed),
}
}
}
impl NoiseSample<DVec3> for Noise {
fn get(&self, position: DVec3) -> f64 {
self.perlin.get(position.to_array())
}
}
impl NoiseSample<DVec2> for Noise {
fn get(&self, position: DVec2) -> f64 {
self.perlin.get(position.to_array())
}
}
Would probably be less boilerplate if the library I am using here accepted structs instead of fixed length arrays.
TIL that fzf comes with vim plugin:
brew install fzf
vim.opt.runtimepath:append("/opt/homebrew/opt/fzf")
Open fuzzy search popup in NVIM with current dir:
:FZF
TIL that these two exist:
Today when implementing Quick-Sort in Ruby I learned about the block parameter shorthand _N.
v is the 1st argument so it can be refactored as follows:
- l_part = a.filter { |v| v <= p }
+ l_part = a.filter { _1 <= p }
:vim:
j0w→+
TIL about throwaway worktrees: git worktree add -d **<path>**** **which creates a new worktree with a detached HEAD at the same commit as the current branch
TIL that you can exit VIM using ZZ
TIL about repeated_permutation:
The “Old” Nested Way:
(0..31).each do |x|
(0..31).each do |y|
(0..31).each do |z|
# Runs 32,768 times
puts "Coordinate: #{x}, #{y}, #{z}"
end
end
end
The “New” DRY Way:
(0..31).to_a.repeated_permutation(3) do |x, y, z|
# This automatically nests 3 levels deep
# Runs 32^3 times
puts "Coordinate: #{x}, #{y}, #{z}"
end
(Edit: formatting)
TIL about date.then in Ruby
my_object.date&.then { |d| I18n.l(d) }
As an alternative to:
I18n.l(my_object.date) if my_object.date.present?
Example:
my_object.date = nil
my_object.date&.then { |d| I18n.l(d) } || "-" # => "-"
my_object.date = 1.day.ago # => 2025-12-18 17:47:17.694132000 CET +01:00
my_object.date&.then { |d| I18n.l(d) } || "-" # => "Thu, 18 Dec 2025 17:47:17 +0100"
vs:
my_object.date = nil
my_object.date.present? ? I18n.l(my_object.date) : "-" # => "-"
my_object.date = 1.day.ago # => 2025-12-18 17:47:17.694132000 CET +01:00
my_object.date.present? ? I18n.l(my_object.date) : "-" # => "Thu, 18 Dec 2025 17:47:17 +0100"
color-mix! So I can change the transparency of my color defined in a CSS variable. https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Values/color_value/color-mix
background: color-mix(in srgb, var(--color-brown) 70%, transparent);
TIL that all requests in a controller go through a process method: https://github.com/rails/rails/blob/90a1eaa1b30ba1f2d524e197460e549c03cf5698/actionpack/lib/abstract_controller/base.rb#L145
def process(action, ...)
@_action_name = action.to_s
unless action_name = _find_action_name(@_action_name)
raise ActionNotFound.new("The action '#{action}' could not be found for #{self.class.name}", self, action)
end
@_response_body = nil
process_action(action_name, ...)
end
There is a nice test helper which @Alessandro Rodi made in 2017:
module InjectSession
include Warden::Test::Helpers
def inject_session(hash)
Warden.on_next_request do |proxy|
hash.each do |key, value|
proxy.raw_session[key] = value
end
end
end
end
TIL about the ActiveStorage Mirror Service: https://guides.rubyonrails.org/active_storage_overview.html#mirror-service to migrate assets like https://eidel.io/rails-how-to-migrate-s3-providers-via-activestorage-mirroring/
TIL that in VIM you can pass a buffer to a command and have it replace said buffer.
For example in a db/schema.rb file you could look for all table definitions as follows:
:%!rg 'create_table'
This passes the contents of the current buffer % to the command rg with the argument 'create_table' whose output is piped back to a buffer.
So after entering that command you’d only have lines left containing create_table.
An additional utility I learned about in this context is tac
$ tac --help
Usage: tac [OPTION]... [FILE]...
Write each FILE to standard output, last line first.
With no FILE, or when FILE is -, read standard input.
This way you can reverse lines in your buffer using
:%!tac
current buffer %, ! indicates command, tac is the command being run on the contents of the buffer.
rails routes can be marked as internal: true and they will not be displayed by bin/rails routes.
https://github.com/rails/rails/blob/af73d4ed24afa004cfacc628b17533c70bd7e056/railties/lib/rails/application/finisher.rb#L140
Today I found gold: https://github.com/rails/rails/blob/main/railties/lib/rails/commands/console/irb_console.rb
Here you can:
- alter the
rails consoleprompt - access
ApplicationControllerinstances and helpers - create your new Rails
appwithapp(true)in the Rails console - alter the reload behaviour
TIL that if you want audible feedback for when your shell command has finished executing, you can just append “say”
rake export:generate_pdf && say "pdf generated"
https://github.com/silva96/log_bench can correlate requests
I can run robohash locally https://github.com/e1ven/Robohash
Bcrypt basically cuts the password after the 72nd character. This was discovered in a penetration test in one of our apps. https://github.com/heartcombo/devise/issues/5307
TIL about the Postgres OVERLAPS operator: https://www.postgresql.org/docs/current/functions-datetime.html#:~:text=In%20addition%20to%20these%20fun[…]20OVERLAPS%20operator%20is%20supported%3A
class Promotions::Tile < ApplicationRecord
scope :overlapping, ->(range) {
where("(starts_on, ends_on) OVERLAPS (?, ?)", range.begin, range.end)
}
end
Promotions::Tile.overlapping(Date.today..Date.tomorrow)
Today I learned that Thoughtbot once tried to sell a CMS service called Copycopter: https://web.archive.org/web/20110719112035/https://copycopter.com/ It’s open sourced under https://github.com/copycopter/copycopter-server
I just had one of these moments again: “Oh, I never heard of this Rails feature, let’s look it up, it must be new.”
Outcome: It’s there since ever: https://github.com/rails/rails/commit/889bf198577fd914e2814b9e402b49f972cf9551
It’s called ActionView::DependencyTracker and it tracks dependencies. This was done via regex and can be done with the Ruby parser since Rails 8.1.
SimpleCov was interfering with the coverage reports during subsequent task executions:
Having:
- 1 Spec that covers everything: → 100% line coverage
vs:
- 1 Spec that covers everything
- 1 Spec that skips a block → 70% coverage
The solution was to load the rails application tasks only once by changing the implementation
- before { Rails.application.load_tasks }
+ before(:all) { Rails.application.load_tasks }
Bonus:
- Rake::Task[task_name].reenable
- Rake::Task[task_name].invoke
+ Rake::Task[task_name].execute
I had a look at https://github.com/ankane/ahoy and learned about https://privacypatterns.org/ and the Anonymity Set, which can be used e.g. to identify a user without cookies. Applied here
You can save the commands entered in an iPython environment to a Python file using the %save magic method
In Switzerland, the correct spelling of time is 17.00, not 17:00. In contrast, Germany and Austria do use : as a separator.
TIL about CSS counter-increment property https://developer.mozilla.org/en-US/docs/Web/CSS/counter-increment
TIL about Python dictionary comprehensions:
nums = [0, 1, 2, 3, 4]
even_num_to_square = {x: x ** 2 for x in nums if x % 2 == 0}
print(even_num_to_square)
How to pass default values to variables in bash:
local base_latency="${1:-$BASE_LATENCY}"
takes the first argument or falls back to the variable BASE_LATENCY
TIL about tagged template literals in JavaScript https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#tagged_templates
function sql(strings, ...values) {
const query = strings.reduce((acc, str, i) => {
return acc + str + (i < values.length ? "?" : "");
}, "");
return [query, values];
}
const userId = 42;
const status = "active";
const [query, params] = sql`SELECT * FROM users WHERE id = ${userId} AND status = ${status}`;
console.log(query); // "SELECT * FROM users WHERE id = ? AND status = ?"
console.log(params); // [42, "active"]
TIL that you should be careful with the spread syntax in Javascript. I encountered this when working on 1+ million numbers: https://stackoverflow.com/questions/1669190/find-the-min-max-element-of-an-array-in-javascript#comment45277115_1669222
There will be a Ruby Conference in Italy again next year
Where the name 37signals comes from: https://ui.adsabs.harvard.edu/abs/1993ApJ…415..218H/abstract
I can multiply a String with a Float:
"hello" * 3
=> "hellohellohello"
"hello" * 3.1
=> "hellohellohello"
"hello" * 3.5
=> "hellohellohello"
"hello" * 3.6
=> "hellohellohello"
"hello" * 4.0
=> "hellohellohellohello"
TIL about formnovalidate
https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/button#formnovalidate
TIL that responds_to?() only works for public methods UNLESS you provide the second optional parameter which has to be true
In German, there is a blank space before a “%”: 100 %, not 100%
TWL (Today We Learned) about the case in syntax in Ruby:
response = [201, { 'Content-Type' => 'application/json' }, '{"status":"ok"}']
case response
in [200..299, headers, body]
puts "Success! Body: #{body}"
in [500, _, _]
puts "Server Error!"
else
puts "Unknown response"
end
# produces Success! Body: {"status":"ok"}
Today I learned that sqlite supports json data types.
TIL that you can “visit” a turbo frame:
Turbo.visit(url, { frame: "ctae_table" })
TIL that postgres enum values cannot be removed, only new ones can be added https://www.postgresql.org/docs/current/datatype-enum.html#DATATYPE-ENUM-IMPLEMENTATION-DETAILS
Add to your shell:
function mktouch() {
mkdir -p -- "$(dirname -- "$1")" && touch -- "$1"
}
and then touch files with path (source: https://unix.stackexchange.com/a/688013/21833)
TIL about the unaccent PostgreSQL extension.
Upon enabling it, you can remove accents using the unaccent function.
scope :by_last_name, ->(last_name) { where("unaccent(last_name) ILIKE unaccent(?)", "%#{last_name}%") }
scope :by_first_name, ->(first_name) { where("unaccent(first_name) ILIKE unaccent(?)", "%#{first_name}%") }
TIL that you can perform an empty match in Rust. This allows for pretty one-liners :)
let sprite_type = match () {
_ if tile.is_flagged && !tile.is_revealed => TileSprite::Flag,
_ if !tile.is_revealed => TileSprite::Uncovered,
_ if tile.is_mine => TileSprite::Bomb,
_ if tile.adjacent_mines > 0 => TileSprite::Number(tile.adjacent_mines),
_ => TileSprite::Empty,
};
Today I learned about DISTINCT ON clause in Postgres. Very useful!
TIL about https://github.com/gogrow-dev/safe_polymorphic that allows to specify the polymorphic association types: belongs_to :owner, polymorphic: [Client, SubClient]
without the gem:
ALLOWED_OWNER_TYPES = %w(Client SubClient).freeze
validates :owner_type, presence: true, inclusion: { in: ALLOWED_OWNER_TYPES }
TIL about .presence, which is the same as Object.present ? Object : nil
TIL that params = { "a" => "a" } is NOT the same as params = { :a => "a" } and Ruby doesn’t understand it, when you try to access the String Key with a Symbol of the same name…
TIL that you can set table borders directly via html attributes: https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/table#attributes
Example:
<table rules="rows">
<thead>
<tr>
<th>Product</th>
<th>Price</th>
<th>Quantity</th>
</tr>
</thead>
<tbody>
<tr>
<td>Apples</td>
<td>1.20</td>
<td>10</td>
</tr>
<tr>
<td>Bananas</td>
<td>0.80</td>
<td>5</td>
</tr>
<tr>
<td>Oranges</td>
<td>1.00</td>
<td>8</td>
</tr>
</tbody>
</table>
So much easier to interpret
.
TIL about the deprecated <font> tag.
<font color="blue" face="Arial, sans-serif">
<h1>HTML maagic!</h1>
</font>
It is cool when you don’t feel like writing ANY css. More interesting html properties: https://html-only.netlify.app/#WHY
TIL that when I use a rich text column in Rails, I don’t need to actually have that column in the database :man-facepalming:
It took me at least two hours of debugging and chatGPT helped me in 10 seconds. ![]()
tally method in ruby.
%w[a b c b c a c b].tally # => {"a"=>2, "b"=>3, "c"=>3}
TIL that JavaFX Scene Builder can generate you a JRuby controller skeleton from your fxml layout:
class MainWindowController
include JRubyFX::Controller
# java_import 'com.gluonhq.charm.glisten.control.TextField'
fxml 'MainWindow.fxml'
# @labelTitle: Label
# @textEntry: TextField
# @textHistory: TextArea
def addText(event) # event: ActionEvent
end
def clearTextEntry(event) # event: ActionEvent
end
end
Today I learned two things at the same time.
- FactoryBot is very, very, very slow. Even
attributes_foris not match to a hash. - Usage of Turbo makes tests like this one quite involved
context "when there are many onboarding forms", :performance do
before do
city_and_date = "Winterthur, #{I18n.l(Date.today)}".freeze
codes = 10_000.times.map { SecureRandom.alphanumeric(8) }.uniq
attributes = codes.map.with_index do |code, index|
{
firstname: "Alex #{index}.",
lastname: "Honnold",
birthdate: "17.08.1985",
accept_privacy: true,
city_and_date: city_and_date,
code: code,
status: "draft"
}
end
OnboardingForm.insert_all(attributes)
sign_in user
end
it "loads in under 1 second" do
time = Benchmark.measure do
VCR.use_cassette("dato/onboarding_form") do
get admin_onboarding_forms_path
end
expect(response).to have_http_status(:ok)
end
expect(time.real).to be < 1
expect(response.body).to include("Max")
end
end
TIL that the Java streams reduce method takes in two functions as arguments, allowing for parallel processing.
Example code:
List<String> words = Arrays.asList("hello", " ", "world", "!");
String combinedString = words.parallelStream().reduce("",
(currentString, nextWord) -> currentString + nextWord,
(s1, s2) -> s1 + s2);
System.out.println("Combined String: " + combinedString);
There’s a special breed of pig that looks like a sheep. https://en.wikipedia.org/wiki/Mangalica
TIL about interactive git staging git add -i ![]()
➜ shapehub git:(feature/23869-systematic-therapy) ✗ git add -i
Found existing alias for "git add". You should use: "ga"
staged unstaged path
1: unchanged +0/-34 app/views/layouts/application.html.erb
2: unchanged +0/-50 app/views/layouts/croms.html.erb
3: unchanged +0/-30 app/views/layouts/croms/_header.html.erb
4: unchanged +0/-13 app/views/layouts/mailer.html.erb
5: unchanged +0/-1 app/views/layouts/mailer.text.erb
*** Commands ***
1: status 2: update 3: revert 4: add untracked
5: patch 6: diff 7: quit 8: help
What now> 2
TIL about custom simpleform inputs: https://github.com/heartcombo/simple_form?tab=readme-ov-file#custom-inputs
class TreeSelectInput < SimpleForm::Inputs::CollectionSelectInput
def input(wrapper_options)
super(wrapper_options.merge(data: { controller: "tree-input" }))
end
end
TIL that an .aiignore file is a thing.
Elasticsearch is also based on Lucene. It means everything I know about Solr still applies. ![]()
click_email_link_matching('invivation')
require 'sinatra'
get '/' do
haml :index
end
__END__
@@ layout
%html
= yield
@@ index
%div.title Hello world.
TIL about Vector ‘retain’ in Rust https://doc.rust-lang.org/std/vec/struct.Vec.html#method.retain
subscribe via RSS