Skip to content

Instantly share code, notes, and snippets.

View henrik's full-sized avatar

Henrik Nyh henrik

View GitHub Profile
@henrik
henrik / admin_controls.liquid
Created July 15, 2025 12:56
Shopify custom Liquid: Link to admin edit page; focal point preview with a link to the image page where it can be edited; weight preview.
{% comment %}
Note that the condition checking if you're an admin is very brittle.
Use at your own peril.
We mostly wanted this before launching the store, to quickly go through imported products.
{% endcomment %}
{% if content_for_header contains 'preview-bar' %}
<div style="background:#efefef">
<b>Admin:</b>
@henrik
henrik / README.md
Last active July 9, 2025 15:19
`t_html_safely` for Ruby/Rails i18n that respects the HTML safety of interpolated values.

Ruby/Rails i18n does not (per 2025-07-07) respect the HTML-safety of interpolated values, so you need to do this:

I18n.t("foo.bar", link: link_to("Help", help_path)).html_safe

Even though link_to returns a html_safe? string, it will be escaped unless we flag the entire string as safe.

Flagging the whole string is at best inelegant, but also opens up for XSS injection attacks.

Less likely, a translator will inject evil HTML into the translation strings themselves. (But they should not be able to, however unlikely.)

@henrik
henrik / 0_example.erb
Last active May 25, 2025 01:17
Rails ViewComponent convenience, e.g. `c.heading("Hi")`
Before:
<%= render Admin::HeadingComponent.new("Hi") %>
After:
<%= c.heading "Hi" %>
@henrik
henrik / only_cat_api_example.rb
Created March 16, 2025 23:02
OnlyCat Ruby API client proof-of-concept.
# OnlyCat API Ruby proof-of-concept.
# By Henrik Nyh <https://henrik.nyh.se> 2025-03-16 under the MIT License.
#
# Based on https://community.home-assistant.io/t/home-assistant-integration-for-onlycat-catflap/814906.
require "bundler/inline"
require "json"
gemfile do
source "https://rubygems.org"
@henrik
henrik / 0_README.md
Last active December 22, 2024 21:00
Rails `with_conditions` proof-of-concept. Similar to `with_options`, but combines `if` and `unless` conditions.
@henrik
henrik / part1.rb
Last active December 5, 2023 18:56
Advent of Code day 5
data = DATA.read
seeds = data[/seeds: (.+)/, 1].split.map(&:to_i)
maps = data.scan(/map:\n(.+?)(?:\n\n|\z)/m).map { |(x)| x.lines.map { _1.split.map(&:to_i) } }
locations = seeds.map { |seed|
maps.reduce(seed) { |input, map|
d, s, _r = map.find { |_d, s, r| (s..(s + r)).cover?(input) }
d ? d + (input - s) : input
}
@henrik
henrik / part1.rb
Last active December 5, 2023 01:03
Advent of Code day 4
puts DATA.readlines.sum { |line|
_, winning_numbers, my_numbers = line.split(/[:|]/).map(&:split)
wins = (winning_numbers & my_numbers).length
wins.zero? ? 0 : 2**(wins - 1)
}
__END__
Data goes here
@henrik
henrik / part1_take1.rb
Last active December 3, 2023 19:12
Advent of Code day 3
grid = DATA.readlines.map(&:chomp)
max_line = grid.length - 1
max_col = grid.first.length - 1
sum = 0
grid.each_with_index do |line, i|
tokens = line.scan(/(\D*)(\d+)/).flatten
offset = 0
tokens.each.with_index do |token, j|
@henrik
henrik / part1_take1.rb
Last active December 2, 2023 14:20
Advent of Code day 2
CONSTRAINTS = { red: 12, green: 13, blue: 14 }
puts DATA.readlines.sum { |line|
game, rounds = line.match(/Game (\d+): (.+)/).captures
rounds = rounds.split("; ").map { _1.scan(/(\d+) (\w+)/) }
next 0 unless rounds.all? { |round| round.all? { |count, color| count.to_i <= CONSTRAINTS[color.to_sym] } }
game.to_i
}
@henrik
henrik / take1.rb
Last active December 2, 2023 08:35
Advent of Code day 1 part 2
# The no-frills take.
words = %w[ one two three four five six seven eight nine ]
hash = words.flat_map.with_index(1) { |word, index| [ [ word, index ], [ index.to_s, index ] ] }.to_h
puts DATA.readlines.sum { |line|
_, first_digit = hash.min_by { |string, _| line.index(string) || 999 }
_, last_digit = hash.max_by { |string, _| line.rindex(string) || -1 }
first_digit * 10 + last_digit