From 7c76b6938d9637f23b848f8518db62663aae76e0 Mon Sep 17 00:00:00 2001 From: Joshua Paine Date: Sun, 12 Apr 2026 19:46:43 -0400 Subject: [PATCH 1/3] daisyui for form styling --- app/frontend/styles/application.css | 1 + app/views/sessions/new.html.erb | 12 ++-- .../town_hall/configurations/_form.html.erb | 22 +++---- .../town_hall/password_resets/edit.html.erb | 18 +++--- .../town_hall/password_resets/new.html.erb | 10 +-- .../profile_questions/_form.html.erb | 32 +++++----- app/views/town_hall/profiles/edit.html.erb | 56 ++++++++--------- app/views/town_hall/sessions/new.html.erb | 18 +++--- app/views/town_hall/stewards/new.html.erb | 62 +++++++++---------- app/views/town_hall/villagers/_form.html.erb | 28 ++++----- package-lock.json | 33 +++------- package.json | 1 + 12 files changed, 140 insertions(+), 153 deletions(-) diff --git a/app/frontend/styles/application.css b/app/frontend/styles/application.css index 30c2b64..cf512a3 100644 --- a/app/frontend/styles/application.css +++ b/app/frontend/styles/application.css @@ -1,5 +1,6 @@ @import "tailwindcss"; @source "../../views"; +@plugin "daisyui"; @theme { --font-sans: "Inter", sans-serif; diff --git a/app/views/sessions/new.html.erb b/app/views/sessions/new.html.erb index aa6fbac..940d23d 100644 --- a/app/views/sessions/new.html.erb +++ b/app/views/sessions/new.html.erb @@ -3,13 +3,11 @@

Enter your email and we'll send you a login link.

<%= form_with url: session_path, method: :post, class: "space-y-4" do |f| %> -
- <%= f.label :email, "Email address", class: "block text-sm font-medium text-gray-700" %> - <%= f.email_field :email, required: true, autofocus: true, class: "mt-1 block w-full rounded border-gray-300 shadow-sm" %> -
+
+ Email address + <%= f.email_field :email, required: true, autofocus: true, class: "input" %> +
-
- <%= f.submit "Send login link", class: "w-full bg-[#2672B5] text-white py-2 px-4 rounded font-medium hover:bg-[#3a6291] cursor-pointer" %> -
+ <%= f.submit "Send login link", class: "btn w-full bg-[#2672B5] text-white hover:bg-[#3a6291]" %> <% end %> diff --git a/app/views/town_hall/configurations/_form.html.erb b/app/views/town_hall/configurations/_form.html.erb index c914366..f1beade 100644 --- a/app/views/town_hall/configurations/_form.html.erb +++ b/app/views/town_hall/configurations/_form.html.erb @@ -9,22 +9,22 @@ <% end %> <%= form_with model: configuration, url: configuration.persisted? ? town_hall_configuration_path(configuration) : town_hall_configurations_path, class: "space-y-4" do |f| %> -
- <%= f.label :name, class: "block text-sm font-medium text-gray-700" %> - <%= f.text_field :name, required: true, class: "mt-1 block w-full rounded border-gray-300 shadow-sm" %> -
+
+ Name + <%= f.text_field :name, required: true, class: "input" %> +
-
- <%= f.label :value, class: "block text-sm font-medium text-gray-700" %> +
+ Value <% if configuration.persisted? && configuration.secret? %> - <%= f.text_field :value, required: true, value: "", placeholder: "Enter new value", class: "mt-1 block w-full rounded border-gray-300 shadow-sm" %> + <%= f.text_field :value, required: true, value: "", placeholder: "Enter new value", class: "input" %> <% else %> - <%= f.text_field :value, required: true, class: "mt-1 block w-full rounded border-gray-300 shadow-sm" %> + <%= f.text_field :value, required: true, class: "input" %> <% end %> -
+
- <%= f.submit class: "bg-indigo-600 text-white py-2 px-4 rounded hover:bg-indigo-700 cursor-pointer" %> - <%= link_to "Cancel", town_hall_configurations_path, class: "py-2 px-4 text-gray-600 hover:underline" %> + <%= f.submit class: "btn bg-indigo-600 text-white hover:bg-indigo-700" %> + <%= link_to "Cancel", town_hall_configurations_path, class: "btn btn-ghost" %>
<% end %> diff --git a/app/views/town_hall/password_resets/edit.html.erb b/app/views/town_hall/password_resets/edit.html.erb index b29f3f8..83ed898 100644 --- a/app/views/town_hall/password_resets/edit.html.erb +++ b/app/views/town_hall/password_resets/edit.html.erb @@ -12,16 +12,16 @@ <% end %> <%= form_with url: town_hall_password_reset_path(token: params[:token]), method: :patch, class: "space-y-4" do |f| %> -
- <%= f.label :password, "New password", class: "block text-sm font-medium text-gray-700" %> - <%= f.password_field :password, autofocus: true, required: true, class: "mt-1 block w-full rounded border-gray-300 shadow-sm" %> -
+
+ New Password + <%= f.password_field :password, autofocus: true, required: true, class: "input" %> +
-
- <%= f.label :password_confirmation, class: "block text-sm font-medium text-gray-700" %> - <%= f.password_field :password_confirmation, required: true, class: "mt-1 block w-full rounded border-gray-300 shadow-sm" %> -
+
+ Password Confirmation + <%= f.password_field :password_confirmation, required: true, class: "input" %> +
- <%= f.submit "Update password", class: "w-full bg-indigo-600 text-white py-2 px-4 rounded hover:bg-indigo-700 cursor-pointer" %> + <%= f.submit "Update password", class: "btn w-full bg-indigo-600 text-white hover:bg-indigo-700" %> <% end %> diff --git a/app/views/town_hall/password_resets/new.html.erb b/app/views/town_hall/password_resets/new.html.erb index 9034cbb..2ee8b30 100644 --- a/app/views/town_hall/password_resets/new.html.erb +++ b/app/views/town_hall/password_resets/new.html.erb @@ -4,12 +4,12 @@

Enter your email and we'll send you a reset link.

<%= form_with url: town_hall_password_reset_path, method: :post, class: "space-y-4" do |f| %> -
- <%= f.label :email, class: "block text-sm font-medium text-gray-700" %> - <%= f.email_field :email, autofocus: true, required: true, class: "mt-1 block w-full rounded border-gray-300 shadow-sm" %> -
+
+ Email + <%= f.email_field :email, autofocus: true, required: true, class: "input" %> +
- <%= f.submit "Send reset link", class: "w-full bg-indigo-600 text-white py-2 px-4 rounded hover:bg-indigo-700 cursor-pointer" %> + <%= f.submit "Send reset link", class: "btn w-full bg-indigo-600 text-white hover:bg-indigo-700" %> <% end %>

diff --git a/app/views/town_hall/profile_questions/_form.html.erb b/app/views/town_hall/profile_questions/_form.html.erb index c7116d3..59e08eb 100644 --- a/app/views/town_hall/profile_questions/_form.html.erb +++ b/app/views/town_hall/profile_questions/_form.html.erb @@ -9,29 +9,29 @@ <% end %> <%= form_with model: profile_question, url: profile_question.persisted? ? town_hall_profile_question_path(profile_question) : town_hall_profile_questions_path, class: "space-y-4" do |f| %> -

- <%= f.label :question, class: "block text-sm font-medium text-gray-700" %> - <%= f.text_area :question, required: true, rows: 2, class: "mt-1 block w-full rounded border-gray-300 shadow-sm" %> -
+
+ Question + <%= f.text_area :question, required: true, rows: 2, class: "textarea" %> +
-
- <%= f.label :llm_prompt, "LLM Prompt", class: "block text-sm font-medium text-gray-700" %> - <%= f.text_area :llm_prompt, rows: 8, class: "mt-1 block w-full rounded border-gray-300 shadow-sm" %> -

Prompt sent to the LLM when generating profile images for this question. Use %s to interpolate the name of the animal.

-
+
+ LLM Prompt + <%= f.text_area :llm_prompt, rows: 8, class: "textarea" %> +

Prompt sent to the LLM when generating profile images for this question. Use %s to interpolate the name of the animal.

+
-
- <%= f.label :position, class: "block text-sm font-medium text-gray-700" %> - <%= f.number_field :position, class: "mt-1 block w-24 rounded border-gray-300 shadow-sm" %> -
+
+ Position + <%= f.number_field :position, class: "input w-24" %> +
- <%= f.check_box :active, class: "rounded border-gray-300" %> + <%= f.check_box :active, class: "checkbox" %> <%= f.label :active, "Active", class: "text-sm font-medium text-gray-700" %>
- <%= f.submit class: "bg-indigo-600 text-white py-2 px-4 rounded hover:bg-indigo-700 cursor-pointer" %> - <%= link_to "Cancel", town_hall_profile_questions_path, class: "py-2 px-4 text-gray-600 hover:underline" %> + <%= f.submit class: "btn bg-indigo-600 text-white hover:bg-indigo-700" %> + <%= link_to "Cancel", town_hall_profile_questions_path, class: "btn btn-ghost" %>
<% end %> diff --git a/app/views/town_hall/profiles/edit.html.erb b/app/views/town_hall/profiles/edit.html.erb index 6b69694..551834d 100644 --- a/app/views/town_hall/profiles/edit.html.erb +++ b/app/views/town_hall/profiles/edit.html.erb @@ -12,42 +12,42 @@ <% end %> <%= form_with model: @steward, url: town_hall_profile_path, method: :patch, class: "space-y-4" do |f| %> -
- <%= f.label :first_name, class: "block text-sm font-medium text-gray-700" %> - <%= f.text_field :first_name, required: true, class: "mt-1 block w-full rounded border-gray-300 shadow-sm" %> -
- -
- <%= f.label :last_name, class: "block text-sm font-medium text-gray-700" %> - <%= f.text_field :last_name, required: true, class: "mt-1 block w-full rounded border-gray-300 shadow-sm" %> -
- -
- <%= f.label :email, class: "block text-sm font-medium text-gray-700" %> - <%= f.email_field :email, required: true, class: "mt-1 block w-full rounded border-gray-300 shadow-sm" %> -
- -
- <%= f.label :mobile_phone, class: "block text-sm font-medium text-gray-700" %> - <%= f.phone_field :mobile_phone, class: "mt-1 block w-full rounded border-gray-300 shadow-sm" %> -
+
+ First Name + <%= f.text_field :first_name, required: true, class: "input" %> +
+ +
+ Last Name + <%= f.text_field :last_name, required: true, class: "input" %> +
+ +
+ Email + <%= f.email_field :email, required: true, class: "input" %> +
+ +
+ Mobile Phone + <%= f.phone_field :mobile_phone, class: "input" %> +

Leave blank to keep your current password.

-
- <%= f.label :password, "New password", class: "block text-sm font-medium text-gray-700" %> - <%= f.password_field :password, class: "mt-1 block w-full rounded border-gray-300 shadow-sm" %> -
+
+ New Password + <%= f.password_field :password, class: "input" %> +
-
- <%= f.label :password_confirmation, class: "block text-sm font-medium text-gray-700" %> - <%= f.password_field :password_confirmation, class: "mt-1 block w-full rounded border-gray-300 shadow-sm" %> -
+
+ Password Confirmation + <%= f.password_field :password_confirmation, class: "input" %> +
- <%= f.submit "Save", class: "bg-indigo-600 text-white py-2 px-4 rounded hover:bg-indigo-700 cursor-pointer" %> + <%= f.submit "Save", class: "btn bg-indigo-600 text-white hover:bg-indigo-700" %>
<% end %> diff --git a/app/views/town_hall/sessions/new.html.erb b/app/views/town_hall/sessions/new.html.erb index 07c288f..bf625fc 100644 --- a/app/views/town_hall/sessions/new.html.erb +++ b/app/views/town_hall/sessions/new.html.erb @@ -2,17 +2,17 @@

Sign in to Town Hall

<%= form_with url: town_hall_session_path, method: :post, class: "space-y-4" do |f| %> -
- <%= f.label :email, class: "block text-sm font-medium text-gray-700" %> - <%= f.email_field :email, autofocus: true, autocomplete: "email", required: true, class: "mt-1 block w-full rounded border-gray-300 shadow-sm" %> -
+
+ Email + <%= f.email_field :email, autofocus: true, autocomplete: "email", required: true, class: "input" %> +
-
- <%= f.label :password, class: "block text-sm font-medium text-gray-700" %> - <%= f.password_field :password, autocomplete: "current-password", required: true, class: "mt-1 block w-full rounded border-gray-300 shadow-sm" %> -
+
+ Password + <%= f.password_field :password, autocomplete: "current-password", required: true, class: "input" %> +
- <%= f.submit "Sign in", class: "w-full bg-indigo-600 text-white py-2 px-4 rounded hover:bg-indigo-700 cursor-pointer" %> + <%= f.submit "Sign in", class: "btn w-full bg-indigo-600 text-white hover:bg-indigo-700" %> <% end %>

diff --git a/app/views/town_hall/stewards/new.html.erb b/app/views/town_hall/stewards/new.html.erb index b477e42..cbf8cc2 100644 --- a/app/views/town_hall/stewards/new.html.erb +++ b/app/views/town_hall/stewards/new.html.erb @@ -12,39 +12,39 @@ <% end %> <%= form_with model: @steward, url: town_hall_stewards_path, class: "space-y-4" do |f| %> -

- <%= f.label :first_name, class: "block text-sm font-medium text-gray-700" %> - <%= f.text_field :first_name, required: true, class: "mt-1 block w-full rounded border-gray-300 shadow-sm" %> -
- -
- <%= f.label :last_name, class: "block text-sm font-medium text-gray-700" %> - <%= f.text_field :last_name, required: true, class: "mt-1 block w-full rounded border-gray-300 shadow-sm" %> -
- -
- <%= f.label :email, class: "block text-sm font-medium text-gray-700" %> - <%= f.email_field :email, required: true, class: "mt-1 block w-full rounded border-gray-300 shadow-sm" %> -
- -
- <%= f.label :mobile_phone, class: "block text-sm font-medium text-gray-700" %> - <%= f.phone_field :mobile_phone, class: "mt-1 block w-full rounded border-gray-300 shadow-sm" %> -
- -
- <%= f.label :password, class: "block text-sm font-medium text-gray-700" %> - <%= f.password_field :password, required: true, class: "mt-1 block w-full rounded border-gray-300 shadow-sm" %> -
- -
- <%= f.label :password_confirmation, class: "block text-sm font-medium text-gray-700" %> - <%= f.password_field :password_confirmation, required: true, class: "mt-1 block w-full rounded border-gray-300 shadow-sm" %> -
+
+ First Name + <%= f.text_field :first_name, required: true, class: "input" %> +
+ +
+ Last Name + <%= f.text_field :last_name, required: true, class: "input" %> +
+ +
+ Email + <%= f.email_field :email, required: true, class: "input" %> +
+ +
+ Mobile Phone + <%= f.phone_field :mobile_phone, class: "input" %> +
+ +
+ Password + <%= f.password_field :password, required: true, class: "input" %> +
+ +
+ Password Confirmation + <%= f.password_field :password_confirmation, required: true, class: "input" %> +
- <%= f.submit "Add steward", class: "bg-indigo-600 text-white py-2 px-4 rounded hover:bg-indigo-700 cursor-pointer" %> - <%= link_to "Cancel", town_hall_stewards_path, class: "py-2 px-4 text-gray-600 hover:underline" %> + <%= f.submit "Add steward", class: "btn bg-indigo-600 text-white hover:bg-indigo-700" %> + <%= link_to "Cancel", town_hall_stewards_path, class: "btn btn-ghost" %>
<% end %> diff --git a/app/views/town_hall/villagers/_form.html.erb b/app/views/town_hall/villagers/_form.html.erb index f8c49cb..15a4c49 100644 --- a/app/views/town_hall/villagers/_form.html.erb +++ b/app/views/town_hall/villagers/_form.html.erb @@ -9,23 +9,23 @@ <% end %> <%= form_with model: villager, url: villager.persisted? ? town_hall_villager_path(villager) : town_hall_villagers_path, class: "space-y-4" do |f| %> -
- <%= f.label :first_name, class: "block text-sm font-medium text-gray-700" %> - <%= f.text_field :first_name, required: true, class: "mt-1 block w-full rounded border-gray-300 shadow-sm" %> -
+
+ First Name + <%= f.text_field :first_name, required: true, class: "input" %> +
-
- <%= f.label :last_name, class: "block text-sm font-medium text-gray-700" %> - <%= f.text_field :last_name, required: true, class: "mt-1 block w-full rounded border-gray-300 shadow-sm" %> -
+
+ Last Name + <%= f.text_field :last_name, required: true, class: "input" %> +
-
- <%= f.label :email, class: "block text-sm font-medium text-gray-700" %> - <%= f.email_field :email, required: true, class: "mt-1 block w-full rounded border-gray-300 shadow-sm" %> -
+
+ Email + <%= f.email_field :email, required: true, class: "input" %> +
- <%= f.submit class: "bg-indigo-600 text-white py-2 px-4 rounded hover:bg-indigo-700 cursor-pointer" %> - <%= link_to "Cancel", town_hall_villagers_path, class: "py-2 px-4 text-gray-600 hover:underline" %> + <%= f.submit class: "btn bg-indigo-600 text-white hover:bg-indigo-700" %> + <%= link_to "Cancel", town_hall_villagers_path, class: "btn btn-ghost" %>
<% end %> diff --git a/package-lock.json b/package-lock.json index fe499e7..b89445f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,6 +7,7 @@ "dependencies": { "@sveltejs/vite-plugin-svelte": "^7.0.0", "@tailwindcss/vite": "^4.2.2", + "daisyui": "^5.5.19", "svelte": "^5.55.0", "tailwindcss": "^4.2.2" }, @@ -16,29 +17,6 @@ "vite-plugin-ruby": "^5.2.1" } }, - "node_modules/@emnapi/core": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.1.tgz", - "integrity": "sha512-mukuNALVsoix/w1BJwFzwXBN/dHeejQtuVzcDsfOEsdpCumXb/E9j8w11h5S54tT1xhifGfbbSm/ICrObRb3KA==", - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "@emnapi/wasi-threads": "1.2.0", - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.1.tgz", - "integrity": "sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA==", - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/@emnapi/wasi-threads": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.0.tgz", @@ -864,6 +842,15 @@ "node": ">=6" } }, + "node_modules/daisyui": { + "version": "5.5.19", + "resolved": "https://registry.npmjs.org/daisyui/-/daisyui-5.5.19.tgz", + "integrity": "sha512-pbFAkl1VCEh/MPCeclKL61I/MqRIFFhNU7yiXoDDRapXN4/qNCoMxeCCswyxEEhqL5eiTTfwHvucFtOE71C9sA==", + "license": "MIT", + "funding": { + "url": "https://github.com/saadeghi/daisyui?sponsor=1" + } + }, "node_modules/deepmerge": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", diff --git a/package.json b/package.json index 0f6028d..2cb738a 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "dependencies": { "@sveltejs/vite-plugin-svelte": "^7.0.0", "@tailwindcss/vite": "^4.2.2", + "daisyui": "^5.5.19", "svelte": "^5.55.0", "tailwindcss": "^4.2.2" } From c4a7654902665e9e870dc8164c0ddc718a046639 Mon Sep 17 00:00:00 2001 From: Joshua Paine Date: Sun, 12 Apr 2026 20:07:12 -0400 Subject: [PATCH 2/3] Configuration#expect(:foo, :bar) w/ block is called once and whenever :foo or :bar config value changes --- app/models/configuration.rb | 37 ++++++--- test/models/configuration_test.rb | 123 ++++++++++++++++++++++++++++++ 2 files changed, 149 insertions(+), 11 deletions(-) diff --git a/app/models/configuration.rb b/app/models/configuration.rb index ef4fada..dfb20c9 100644 --- a/app/models/configuration.rb +++ b/app/models/configuration.rb @@ -2,11 +2,28 @@ class Configuration < ApplicationRecord SECRET_SEGMENTS = /(?:^|_)(key|secret|token)(?:_|$)/i validates :name, presence: true, uniqueness: true + after_commit :apply_callbacks - def self.expected_names - @expected_names ||= Set.new + def self.expect(*names, &block) + names = names.map(&:to_s) + expected_names.merge(names) + case [names, block] + in [name], nil then self[name] + in Array, nil then values_at(*names) + else + callback = -> { block.call(*values_at(*names)) } + @callbacks ||= Hash.new { |h, k| h[k] = [] } + names.each do |name| + @callbacks[name] << callback + end + callback.call + end end + def self.expected_names = @expected_names ||= Set.new + + def self.callbacks = @callbacks.dup + def self.all_and_expected existing = order(:name).to_a existing_names = existing.map(&:name).to_set @@ -15,9 +32,7 @@ def self.all_and_expected end # -- Hash-like class interface -- - def self.[](name) - find_by(name: name)&.value - end + def self.[](name) = find_by(name: name)&.value def self.fetch(name, *args, &block) name = name.to_s @@ -43,19 +58,19 @@ def self.to_h pluck(:name, :value).to_h end - def self.to_hash - to_h - end + def self.to_hash = to_h # -- Instance methods -- - def secret? - SECRET_SEGMENTS.match?(name) - end + def secret? = SECRET_SEGMENTS.match?(name) def display_value return value unless secret? && value.present? && value.length > 4 "#{"*" * (value.length - 4)}#{value.last(4)}" end + + private + + def apply_callbacks = self.class.callbacks&.[](name)&.each(&:call) end diff --git a/test/models/configuration_test.rb b/test/models/configuration_test.rb index 2819f45..707a5d1 100644 --- a/test/models/configuration_test.rb +++ b/test/models/configuration_test.rb @@ -153,6 +153,118 @@ class ConfigurationTest < ActiveSupport::TestCase assert_equal names.sort, names end + # -- expect (no block) -- + + test "expect with single name registers it as expected and returns value" do + with_clean_expect_state do + result = Configuration.expect(:site_name) + assert_equal "Ashevillagers", result + assert_includes Configuration.expected_names, "site_name" + end + end + + test "expect with single name returns nil for missing configuration" do + with_clean_expect_state do + result = Configuration.expect(:nonexistent) + assert_nil result + assert_includes Configuration.expected_names, "nonexistent" + end + end + + test "expect with multiple names returns values_at" do + with_clean_expect_state do + result = Configuration.expect(:site_name, :max_attendees) + assert_equal ["Ashevillagers", "500"], result + end + end + + test "expect accepts string names" do + with_clean_expect_state do + result = Configuration.expect("site_name") + assert_equal "Ashevillagers", result + assert_includes Configuration.expected_names, "site_name" + end + end + + # -- expect (with block / callbacks) -- + + test "expect with block calls it immediately with current values" do + with_clean_expect_state do + received = nil + Configuration.expect(:site_name) { |val| received = val } + assert_equal "Ashevillagers", received + end + end + + test "expect with block and multiple names passes all values" do + with_clean_expect_state do + received = nil + Configuration.expect(:site_name, :max_attendees) { |a, b| received = [a, b] } + assert_equal ["Ashevillagers", "500"], received + end + end + + test "expect with block registers callback" do + with_clean_expect_state do + Configuration.expect(:site_name) { |_| } + assert Configuration.callbacks.key?("site_name") + assert_equal 1, Configuration.callbacks["site_name"].size + end + end + + test "expect with block and multiple names registers callback under each name" do + with_clean_expect_state do + Configuration.expect(:site_name, :max_attendees) { |_a, _b| } + assert_equal 1, Configuration.callbacks["site_name"].size + assert_equal 1, Configuration.callbacks["max_attendees"].size + end + end + + # -- after_commit fires callbacks -- + + test "creating a configuration fires registered callbacks" do + with_clean_expect_state do + call_count = 0 + Configuration.expect(:new_setting) { |_| call_count += 1 } + assert_equal 1, call_count # immediate call + + Configuration.create!(name: "new_setting", value: "hello") + assert_equal 2, call_count + end + end + + test "updating a configuration fires registered callbacks" do + with_clean_expect_state do + received = nil + Configuration.expect(:site_name) { |val| received = val } + assert_equal "Ashevillagers", received + + configurations(:site_name).update!(value: "New Name") + assert_equal "New Name", received + end + end + + test "callback receives updated values for multi-name expect" do + with_clean_expect_state do + received = nil + Configuration.expect(:site_name, :max_attendees) { |a, b| received = [a, b] } + + configurations(:site_name).update!(value: "Changed") + assert_equal ["Changed", "500"], received + end + end + + test "committing an unrelated configuration does not fire callback" do + with_clean_expect_state do + call_count = 0 + Configuration.expect(:site_name) { |_| call_count += 1 } + assert_equal 1, call_count + + configurations(:max_attendees).update!(value: "999") + assert_equal 1, call_count + end + end + # -- secret? -- test "secret? is true when name contains key" do @@ -206,6 +318,17 @@ class ConfigurationTest < ActiveSupport::TestCase private + def with_clean_expect_state + saved_expected = Configuration.instance_variable_get(:@expected_names) + saved_callbacks = Configuration.instance_variable_get(:@callbacks) + Configuration.instance_variable_set(:@expected_names, Set.new) + Configuration.instance_variable_set(:@callbacks, nil) + yield + ensure + Configuration.instance_variable_set(:@expected_names, saved_expected) + Configuration.instance_variable_set(:@callbacks, saved_callbacks) + end + def count_queries(&block) count = 0 counter = ->(_name, _started, _finished, _unique_id, payload) { From edcb654e30b07050024bf5637f6aed8f1f4305af Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Apr 2026 00:08:25 +0000 Subject: [PATCH 3/3] Bump puma from 7.2.0 to 8.0.0 Bumps [puma](https://github.com/puma/puma) from 7.2.0 to 8.0.0. - [Release notes](https://github.com/puma/puma/releases) - [Changelog](https://github.com/puma/puma/blob/main/History.md) - [Commits](https://github.com/puma/puma/compare/v7.2.0...v8.0.0) --- updated-dependencies: - dependency-name: puma dependency-version: 8.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index af4aacf..2741165 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -256,7 +256,7 @@ GEM date stringio public_suffix (7.0.5) - puma (7.2.0) + puma (8.0.0) nio4r (~> 2.0) raabro (1.4.0) racc (1.8.1)