Today I learned

A collection of insights, tips, and lessons learned from our daily work.


Avatar for Daniel Bengl (daniel.bengl@renuo.ch)

Tuesday, February 24, 2026

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.

Avatar for Daniel Bengl (daniel.bengl@renuo.ch)

Saturday, January 31, 2026

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.

Avatar for Daniel Bengl (daniel.bengl@renuo.ch)

Sunday, January 4, 2026

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 }
Avatar for Daniel Bengl (daniel.bengl@renuo.ch)

Saturday, December 20, 2025

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)

Avatar for Daniel Bengl (daniel.bengl@renuo.ch)

Friday, December 19, 2025

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"
Avatar for Alessandro Rodi (alessandro.rodi@renuo.ch)

Thursday, December 11, 2025

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
Avatar for Daniel Bengl (daniel.bengl@renuo.ch)

Monday, December 8, 2025

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.

Avatar for Daniel Bengl (daniel.bengl@renuo.ch)

Friday, October 24, 2025

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
Avatar for Daniel Bengl (daniel.bengl@renuo.ch)

Thursday, September 11, 2025

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"] 
Avatar for Alessandro Rodi (alessandro.rodi@renuo.ch)

Saturday, August 23, 2025

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"
Avatar for Daniel Bengl (daniel.bengl@renuo.ch)

Wednesday, July 9, 2025

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"}
Avatar for Daniel Bengl (daniel.bengl@renuo.ch)

Monday, June 30, 2025

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}%") }
Avatar for Daniel Bengl (daniel.bengl@renuo.ch)

Saturday, June 28, 2025

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,
};
Avatar for Julian Duss (julian.duss@renuo.ch)

Monday, June 16, 2025

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…

Avatar for Daniel Bengl (daniel.bengl@renuo.ch)

Friday, June 13, 2025

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 :relaxed:.

Avatar for Alessandro Rodi (alessandro.rodi@renuo.ch)

Tuesday, June 10, 2025

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. :clap:

Avatar for Daniel Bengl (daniel.bengl@renuo.ch)

Friday, June 6, 2025

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
Avatar for Josua Schmid (josua.schmid@renuo.ch)

Friday, June 6, 2025

Today I learned two things at the same time.

  • FactoryBot is very, very, very slow. Even attributes_for is 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
Avatar for Daniel Bengl (daniel.bengl@renuo.ch)

Thursday, June 5, 2025

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);
Avatar for Daniel Bengl (daniel.bengl@renuo.ch)

Wednesday, May 21, 2025

TIL about interactive git staging git add -i :exploding_head:

➜  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

subscribe via RSS