From 34f3ef27de376a23fa9d185c2a7f759071840c13 Mon Sep 17 00:00:00 2001 From: Brian Durand Date: Mon, 5 Jan 2026 19:59:18 -0800 Subject: [PATCH 1/6] Hook data sync into Rails db setup tasks --- CHANGELOG.md | 8 +- README.md | 23 ++++- VERSION | 2 +- lib/support_table_data.rb | 99 +++++++++++-------- lib/support_table_data/railtie.rb | 21 +++- lib/support_table_data/validation_error.rb | 16 +++ spec/models/color.rb | 2 +- spec/support_table_data_spec.rb | 10 +- test_app/app/models/application_record.rb | 5 + .../models/secondary_application_record.rb | 7 ++ test_app/app/models/status.rb | 4 +- test_app/app/models/thing.rb | 10 ++ test_app/config/database.yml | 18 +++- test_app/config/environments/development.rb | 2 +- test_app/config/environments/test.rb | 11 +++ .../20260104000001_create_things.rb | 7 ++ test_app/db/secondary_schema.rb | 25 +++++ test_app/db/support_tables/things.yml | 5 + test_app/lib/tasks/database.rake | 11 +++ 19 files changed, 227 insertions(+), 59 deletions(-) create mode 100644 lib/support_table_data/validation_error.rb create mode 100644 test_app/app/models/application_record.rb create mode 100644 test_app/app/models/secondary_application_record.rb create mode 100644 test_app/app/models/thing.rb create mode 100644 test_app/config/environments/test.rb create mode 100644 test_app/db/secondary_migrate/20260104000001_create_things.rb create mode 100644 test_app/db/secondary_schema.rb create mode 100644 test_app/db/support_tables/things.yml create mode 100644 test_app/lib/tasks/database.rake diff --git a/CHANGELOG.md b/CHANGELOG.md index 5efdde9..200c767 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,16 +4,18 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## 1.4.1 +## 1.5.0 ### Added -- The default data directory in a Rails application can be set with the `config.support_table_data_directory` option in the Rails application configuration. +- The default data directory in a Rails application can be set with the `config.support_table.data_directory` option in the Rails application configuration. - Added rake task `support_table_data:add_yard_docs` for Rails applications that will add YARD documentation to support table models for the named instance helpers. +- The data synchronization task is now automatically attached to several Rails tasks: `db:seed`, `db:seed:replant`, `db:prepare`, `db:test:prepare`, `db:fixtures:load`. Support tables will be synced after running any of these tasks. This can be disabled by setting `config.support_table.auto_sync = false` in the Rails application configuration. ### Changed -- The default data directory is now set in a Railtie and can be overridden with the `config.support_table_data_directory` option in the Rails application configuration. +- The default data directory is now set in a Railtie and can be overridden with the `config.support_table.data_directory` option in the Rails application configuration. +- The `support_table_key_attribute` method now returns the primary key of the model if not explicitly set instead of implicitly interpreting `nil` as the primary key. This makes the behavior more consistent and explicit. ## 1.4.0 diff --git a/README.md b/README.md index e2cb683..e8c50fd 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ You cannot update the value of the key attribute in a record in the data file. I You can specify data files as relative paths. This can be done by setting the `SupportTableData.data_directory` value. You can override this value for a model by setting the `support_table_data_directory` attribute on its class. Otherwise, relative file paths will be resolved from the current working directory. You must define the directory to load relative files from before loading your model classes. -In a Rails application, `SupportTableData.data_directory` will be automatically set to `db/support_tables/`. This can be overridden by setting the `config.support_table_data_directory` option in the Rails application configuration. +In a Rails application, `SupportTableData.data_directory` will be automatically set to `db/support_tables/`. This can be overridden by setting the `config.support_table.data_directory` option in the Rails application configuration. **Note**: If you're using CSV files and Ruby 3.4 or higher, you'll need to include the `csv` gem in your Gemfile since it was removed from the standard library in Ruby 3.4. @@ -260,18 +260,33 @@ end If you use a method to set a `has_many` association on your model, you **must** set the `autosave` option to `true` on the association (see the above example). This will ensure the association records are always saved even if there were no changes to the parent record. -You need to call `SupportTableData.sync_all!` when deploying your application. This gem includes a rake task `support_table_data:sync` that is suitable for hooking into deploy scripts. An easy way to hook it into a Rails application is by enhancing the `db:migrate` task so that the sync task runs immediately after database migrations are run. You can do this by adding code to a Rakefile in your application's `lib/tasks` directory: +You will need to call `SupportTableData.sync_all!` when deploying your application or running your test suite. This gem includes a rake task `support_table_data:sync` that is suitable for hooking into deploy or CI scripts. + +This task is automatically run whenever you run any of these Rails tasks so if these are already part of you deploy or CI scripts, then no additional setup is required: + +- `db:seed` +- `db:seed:replant` +- `db:prepare` +- `db:test:prepare` +- `db:fixtures:load` + +You can disable these task enhancements by setting `config.support_table.auto_sync = false` in your Rails application configuration. + +> ![TIP] +> If you also want to hook into the `db:migrate` task so that syncs are run immediately after database migrations, you can do this by adding code to a Rakefile in your application's `lib/tasks` directory. Migrations do funny things with the database connection especially when using multiple databases so you need to re-establish the connection before syncing the support table data. ```ruby if Rake::Task.task_defined?("db:migrate") Rake::Task["db:migrate"].enhance do + # The main database connection may have artifacts from the migration, so re-establish it + # to get a clean connection before syncing support table data. + ActiveRecord::Base.establish_connection + Rake::Task["support_table_data:sync"].invoke end end ``` -Enhancing the `db:migrate` task also ensures that local development environments will stay up to date. - ### Testing You must also call `SupportTableData.sync_all!` before running your test suite. This method should be called in the test suite setup code after any data in the test database has been purged and before any tests are run. diff --git a/VERSION b/VERSION index 347f583..bc80560 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.4.1 +1.5.0 diff --git a/lib/support_table_data.rb b/lib/support_table_data.rb index 12bb920..7d57092 100644 --- a/lib/support_table_data.rb +++ b/lib/support_table_data.rb @@ -19,9 +19,13 @@ module SupportTableData @support_table_instance_keys = nil @support_table_dependencies = [] - # Define the attribute used as the key of the hash in the data files. - # This should be a value that never changes. By default the key attribute will be the id. - class_attribute :support_table_key_attribute, instance_accessor: false + # Private class attritebute to hold the key attribute name. Use `support_table_key_attribute` instead. + # @private + class_attribute :_support_table_key_attribute, instance_accessor: false + class << self + private :_support_table_key_attribute= + private :_support_table_key_attribute + end # Define the directory where data files should be loaded from. This value will override the global # value set by SupportTableData.data_directory. This is only used if relative paths are passed @@ -30,6 +34,17 @@ module SupportTableData end class_methods do + # Define the attribute used as the key of the hash in the data files. + # This should be an attribute with values that never change. + # By default the key attribute will be the table's primary key. + def support_table_key_attribute=(attribute_name) + self._support_table_key_attribute = attribute_name&.to_s + end + + def support_table_key_attribute + _support_table_key_attribute || primary_key + end + # Synchronize the rows in the table with the values defined in the data files added with # `add_support_table_data`. Note that rows will not be deleted if they are no longer in # the data files. @@ -38,36 +53,41 @@ module SupportTableData def sync_table_data! return unless table_exists? - key_attribute = (support_table_key_attribute || primary_key).to_s - canonical_data = support_table_data.each_with_object({}) { |attributes, hash| hash[attributes[key_attribute].to_s] = attributes } - records = where(key_attribute => canonical_data.keys) + canonical_data = support_table_data.each_with_object({}) do |attributes, hash| + hash[attributes[support_table_key_attribute].to_s] = attributes + end + records = where(support_table_key_attribute => canonical_data.keys) changes = [] - ActiveSupport::Notifications.instrument("support_table_data.sync", class: self) do - transaction do - records.each do |record| - key = record[key_attribute].to_s - attributes = canonical_data.delete(key) - attributes&.each do |name, value| - record.send(:"#{name}=", value) if record.respond_to?(:"#{name}=", true) + begin + ActiveSupport::Notifications.instrument("support_table_data.sync", class: self) do + transaction do + records.each do |record| + key = record[support_table_key_attribute].to_s + attributes = canonical_data.delete(key) + attributes&.each do |name, value| + record.send(:"#{name}=", value) if record.respond_to?(:"#{name}=", true) + end + if support_table_record_changed?(record) + changes << record.changes + record.save! + end end - if support_table_record_changed?(record) + + canonical_data.each_value do |attributes| + class_name = attributes[inheritance_column] + klass = class_name ? sti_class_for(class_name) : self + record = klass.new + attributes.each do |name, value| + record.send(:"#{name}=", value) if record.respond_to?(:"#{name}=", true) + end changes << record.changes record.save! end end - - canonical_data.each_value do |attributes| - class_name = attributes[inheritance_column] - klass = class_name ? sti_class_for(class_name) : self - record = klass.new - attributes.each do |name, value| - record.send(:"#{name}=", value) if record.respond_to?(:"#{name}=", true) - end - changes << record.changes - record.save! - end end + rescue ActiveRecord::RecordInvalid => e + raise SupportTableData::ValidationError.new(e.record) end changes @@ -118,14 +138,12 @@ def support_table_attribute_helpers # @return [Array] List of attributes for all records in the data files. def support_table_data data = {} - key_attribute = (support_table_key_attribute || primary_key).to_s - @support_table_data_files.each do |data_file_path| file_data = support_table_parse_data_file(data_file_path) file_data = file_data.values if file_data.is_a?(Hash) file_data = Array(file_data).flatten file_data.each do |attributes| - key_value = attributes[key_attribute].to_s + key_value = attributes[support_table_key_attribute].to_s existing = data[key_value] if existing existing.merge!(attributes) @@ -173,9 +191,8 @@ def instance_names # @return [ActiveRecord::Base] The instance loaded from the database. # @raise [ActiveRecord::RecordNotFound] If the instance does not exist. def named_instance(instance_name) - key_attribute = (support_table_key_attribute || primary_key).to_s instance_name = instance_name.to_s - find_by!(key_attribute => @support_table_instance_names[instance_name]) + find_by!(support_table_key_attribute => @support_table_instance_names[instance_name]) end # Get the key values for all instances loaded from the data files. @@ -183,13 +200,12 @@ def named_instance(instance_name) # @return [Array] List of all the key attribute values. def instance_keys if @support_table_instance_keys.nil? - key_attribute = (support_table_key_attribute || primary_key).to_s values = [] support_table_data.each do |attributes| - key_value = attributes[key_attribute] + key_value = attributes[support_table_key_attribute] instance = new - instance.send(:"#{key_attribute}=", key_value) - values << instance.send(key_attribute) + instance.send(:"#{support_table_key_attribute}=", key_value) + values << instance.send(support_table_key_attribute) end @support_table_instance_keys = values.uniq end @@ -200,14 +216,12 @@ def instance_keys # # @return [Boolean] def protected_instance?(instance) - key_attribute = (support_table_key_attribute || primary_key).to_s - unless defined?(@protected_keys) - keys = support_table_data.collect { |attributes| attributes[key_attribute].to_s } + keys = support_table_data.collect { |attributes| attributes[support_table_key_attribute].to_s } @protected_keys = keys end - @protected_keys.include?(instance[key_attribute].to_s) + @protected_keys.include?(instance[support_table_key_attribute].to_s) end # Explicitly define other support tables that this model depends on. A support table depends @@ -251,12 +265,11 @@ def define_support_table_named_instance_methods(name, attributes) raise ArgumentError.new("Cannot define named instance #{method_name} on #{name}; name contains illegal characters") end - key_attribute = (support_table_key_attribute || primary_key).to_s - key_value = attributes[key_attribute] + key_value = attributes[support_table_key_attribute] unless @support_table_instance_names.include?(method_name) - define_support_table_instance_helper(method_name, key_attribute, key_value) - define_support_table_predicates_helper("#{method_name}?", key_attribute, key_value) + define_support_table_instance_helper(method_name, support_table_key_attribute, key_value) + define_support_table_predicates_helper("#{method_name}?", support_table_key_attribute, key_value) @support_table_instance_names = @support_table_instance_names.merge(method_name => key_value) end @@ -467,6 +480,8 @@ def protected_instance? end end +require_relative "support_table_data/validation_error" + if defined?(Rails::Railtie) require_relative "support_table_data/railtie" end diff --git a/lib/support_table_data/railtie.rb b/lib/support_table_data/railtie.rb index ea84142..4e0dd33 100644 --- a/lib/support_table_data/railtie.rb +++ b/lib/support_table_data/railtie.rb @@ -2,14 +2,29 @@ module SupportTableData class Railtie < Rails::Railtie - config.support_table_data_directory = "db/support_tables" + unless config.respond_to?(:support_table) && config.support_table + config.support_table = ActiveSupport::OrderedOptions.new + end + + config.support_table.data_directory ||= "db/support_tables" + config.support_table.auto_sync ||= true initializer "support_table_data" do |app| - SupportTableData.data_directory ||= app.root.join(app.config.support_table_data_directory).to_s + SupportTableData.data_directory ||= app.root.join(app.config.support_table&.data_directory).to_s end - rake_tasks do + rake_tasks do |app| load File.expand_path("../tasks/support_table_data.rake", __dir__) + + if app.config.support_table.auto_sync + ["db:seed", "db:seed:replant", "db:prepare", "db:test:prepare", "db:fixtures:load"].each do |task_name| + next unless Rake::Task.task_defined?(task_name) + + Rake::Task[task_name].enhance do + Rake::Task["support_table_data:sync"].invoke + end + end + end end end end diff --git a/lib/support_table_data/validation_error.rb b/lib/support_table_data/validation_error.rb new file mode 100644 index 0000000..4a0d82d --- /dev/null +++ b/lib/support_table_data/validation_error.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module SupportTableData + # Error class that is raised when validation fails when loading support table data. + # It provides more context than the standard ActiveRecord::RecordInvalid to help identify + # which record caused the validation failure. + class ValidationError < StandardError + def initialize(invalid_record) + key_attribute = invalid_record.class.support_table_key_attribute + key_value = invalid_record[key_attribute] + message = "Validation failed for #{invalid_record.class} with #{key_attribute}: #{key_value.inspect} - " \ + "#{invalid_record.errors.full_messages.join(", ")}" + super(message) + end + end +end diff --git a/spec/models/color.rb b/spec/models/color.rb index d429ca7..9f59448 100644 --- a/spec/models/color.rb +++ b/spec/models/color.rb @@ -19,7 +19,7 @@ class Color < ActiveRecord::Base # Intentionally invalid association belongs_to :non_existent, class_name: "NonExistent" - validates_uniqueness_of :name + validates :name, presence: true, uniqueness: true def group_name=(value) self.group = Group.named_instance(value) diff --git a/spec/support_table_data_spec.rb b/spec/support_table_data_spec.rb index e00dfa6..8ff48a7 100644 --- a/spec/support_table_data_spec.rb +++ b/spec/support_table_data_spec.rb @@ -49,11 +49,19 @@ expect(Polygon.rectangle).to be_a Rectangle expect(Polygon.pentagon).to be_a Polygon end + it "honors the single table inheritance column when creating new records" do allow(Polygon).to receive(:support_table_data).and_return([ {"name" => "Triangle", "type" => "Triangle", "side_count" => 4} ]) - expect { Polygon.sync_table_data! }.to raise_error(ActiveRecord::RecordInvalid) + expect { Polygon.sync_table_data! }.to raise_error(SupportTableData::ValidationError) + end + + it "reraises validation errors with more context" do + allow(Color).to receive(:support_table_data).and_return([ + {"id" => 20, "name" => nil, "hex" => "0x123456"} + ]) + expect { Color.sync_table_data! }.to raise_error(SupportTableData::ValidationError, /Validation failed for Color with id: 20 - Name can't be blank/) end end diff --git a/test_app/app/models/application_record.rb b/test_app/app/models/application_record.rb new file mode 100644 index 0000000..71fbba5 --- /dev/null +++ b/test_app/app/models/application_record.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +class ApplicationRecord < ActiveRecord::Base + self.abstract_class = true +end diff --git a/test_app/app/models/secondary_application_record.rb b/test_app/app/models/secondary_application_record.rb new file mode 100644 index 0000000..d601839 --- /dev/null +++ b/test_app/app/models/secondary_application_record.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class SecondaryApplicationRecord < ApplicationRecord + self.abstract_class = true + + connects_to database: {writing: :secondary, reading: :secondary} +end diff --git a/test_app/app/models/status.rb b/test_app/app/models/status.rb index 3d3ebae..631bb4f 100644 --- a/test_app/app/models/status.rb +++ b/test_app/app/models/status.rb @@ -1,9 +1,11 @@ # frozen_string_literal: true -class Status < ActiveRecord::Base +class Status < ApplicationRecord include SupportTableData self.support_table_key_attribute = :code add_support_table_data "statuses.yml" named_instance_attribute_helpers :name + + validates :code, presence: true, uniqueness: true end diff --git a/test_app/app/models/thing.rb b/test_app/app/models/thing.rb new file mode 100644 index 0000000..6cdb20c --- /dev/null +++ b/test_app/app/models/thing.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +class Thing < SecondaryApplicationRecord + include SupportTableData + + self.support_table_key_attribute = :id + add_support_table_data "things.yml" + + validates :name, presence: true, uniqueness: true +end diff --git a/test_app/config/database.yml b/test_app/config/database.yml index 576bcf4..c6d84ec 100644 --- a/test_app/config/database.yml +++ b/test_app/config/database.yml @@ -1,3 +1,17 @@ development: - adapter: sqlite3 - database: db/development.sqlite3 + primary: + adapter: sqlite3 + database: db/development.sqlite3 + secondary: + adapter: sqlite3 + database: db/secondary_development.sqlite3 + migrations_paths: db/secondary_migrate + +test: + primary: + adapter: sqlite3 + database: db/test.sqlite3 + secondary: + adapter: sqlite3 + database: db/secondary_test.sqlite3 + migrations_paths: db/secondary_migrate diff --git a/test_app/config/environments/development.rb b/test_app/config/environments/development.rb index cdc965b..7ec98b7 100644 --- a/test_app/config/environments/development.rb +++ b/test_app/config/environments/development.rb @@ -1,4 +1,4 @@ -require "active_support/core_ext/integer/time" +# frozen_string_literal: true Rails.application.configure do # Settings specified here will take precedence over those in config/application.rb. diff --git a/test_app/config/environments/test.rb b/test_app/config/environments/test.rb new file mode 100644 index 0000000..7ec98b7 --- /dev/null +++ b/test_app/config/environments/test.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +Rails.application.configure do + # Settings specified here will take precedence over those in config/application.rb. + + # Make code changes take effect immediately without server restart. + config.enable_reloading = true + + # Do not eager load code on boot. + config.eager_load = false +end diff --git a/test_app/db/secondary_migrate/20260104000001_create_things.rb b/test_app/db/secondary_migrate/20260104000001_create_things.rb new file mode 100644 index 0000000..a7906d6 --- /dev/null +++ b/test_app/db/secondary_migrate/20260104000001_create_things.rb @@ -0,0 +1,7 @@ +class CreateThings < ActiveRecord::Migration[8.1] + def change + create_table :things do |t| + t.string :name, null: false, index: {unique: true} + end + end +end diff --git a/test_app/db/secondary_schema.rb b/test_app/db/secondary_schema.rb new file mode 100644 index 0000000..f79d6d9 --- /dev/null +++ b/test_app/db/secondary_schema.rb @@ -0,0 +1,25 @@ +# This file is auto-generated from the current state of the database. Instead +# of editing this file, please use the migrations feature of Active Record to +# incrementally modify your database, and then regenerate this schema definition. +# +# This file is the source Rails uses to define your schema when running `bin/rails +# db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to +# be faster and is potentially less error prone than running all of your +# migrations from scratch. Old migrations may fail to apply correctly if those +# migrations use external dependencies or application code. +# +# It's strongly recommended that you check this file into your version control system. + +ActiveRecord::Schema[8.1].define(version: 2026_01_04_000001) do + create_table "statuses", force: :cascade do |t| + t.string "code", null: false + t.string "name", null: false + t.index ["code"], name: "index_statuses_on_code", unique: true + t.index ["name"], name: "index_statuses_on_name", unique: true + end + + create_table "things", force: :cascade do |t| + t.string "name", null: false + t.index ["name"], name: "index_things_on_name", unique: true + end +end diff --git a/test_app/db/support_tables/things.yml b/test_app/db/support_tables/things.yml new file mode 100644 index 0000000..5427f9c --- /dev/null +++ b/test_app/db/support_tables/things.yml @@ -0,0 +1,5 @@ +- id: 1 + name: Thing One + +- id: 2 + name: Thing Two diff --git a/test_app/lib/tasks/database.rake b/test_app/lib/tasks/database.rake new file mode 100644 index 0000000..7126745 --- /dev/null +++ b/test_app/lib/tasks/database.rake @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +if Rake::Task.task_defined?("db:migrate") + Rake::Task["db:migrate"].enhance do + # The main database connection may have artifacts from the migration, so re-establish it + # to get a clean connection before syncing support table data. + ActiveRecord::Base.establish_connection + + Rake::Task["support_table_data:sync"].invoke + end +end From ac7e53a1213aa11fa9b003c76efdcc316ce7430c Mon Sep 17 00:00:00 2001 From: Brian Durand Date: Mon, 5 Jan 2026 20:53:22 -0800 Subject: [PATCH 2/6] update README --- README.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/README.md b/README.md index e8c50fd..bb39c0b 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,19 @@ This gem provides a mixin for ActiveRecord support table models that allows you These kinds of models blur the line between data and code. You'll often end up with constants and application logic based on specific values that need to exist in the table. By using this gem, you can easily define methods for loading and comparing specific instances. This can give you cleaner code that reads far more naturally. You can also avoid defining dozens of constants or referencing magic values (i.e. no more hard-coded strings or ids in the code to look up specific records). +## Table of Contents + +- [Usage](#usage) + - [Specifying Data Files](#specifying-data-files) + - [Named Instances](#named-instances) + - [Documenting Named Instance Helpers](#documenting-named-instance-helpers) + - [Caching](#caching) + - [Loading Data](#loading-data) + - [Testing](#testing) +- [Installation](#installation) +- [Contributing](#contributing) +- [License](#license) + ## Usage In the examples below, suppose we have a simple `Status` model in which each row has an id and a name, and the name can only have a handful of statuses: "Pending", "In Progress", and "Completed". @@ -212,6 +225,9 @@ class Thing < ApplicationRecord end ``` +> ![TIP] +> The [support_table](https://github.com/bdurand/support_table) gem combines both gems in a drop in solution for Rails applications. + ### Loading Data Calling `sync_table_data!` on your model class will synchronize the data in the database table with the values from the data files. From c3742036faba42437152d1d9348ee115dba0b920 Mon Sep 17 00:00:00 2001 From: Brian Durand Date: Mon, 5 Jan 2026 21:06:53 -0800 Subject: [PATCH 3/6] Fix markdown --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index bb39c0b..8e0d0b5 100644 --- a/README.md +++ b/README.md @@ -225,7 +225,7 @@ class Thing < ApplicationRecord end ``` -> ![TIP] +> [!TIP] > The [support_table](https://github.com/bdurand/support_table) gem combines both gems in a drop in solution for Rails applications. ### Loading Data @@ -288,7 +288,7 @@ This task is automatically run whenever you run any of these Rails tasks so if t You can disable these task enhancements by setting `config.support_table.auto_sync = false` in your Rails application configuration. -> ![TIP] +> [!TIP] > If you also want to hook into the `db:migrate` task so that syncs are run immediately after database migrations, you can do this by adding code to a Rakefile in your application's `lib/tasks` directory. Migrations do funny things with the database connection especially when using multiple databases so you need to re-establish the connection before syncing the support table data. ```ruby From 2f1b97867918afd5b359694384802c9a84f0c6cc Mon Sep 17 00:00:00 2001 From: Brian Durand Date: Mon, 5 Jan 2026 22:01:24 -0800 Subject: [PATCH 4/6] Update README.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8e0d0b5..a330b32 100644 --- a/README.md +++ b/README.md @@ -278,7 +278,7 @@ If you use a method to set a `has_many` association on your model, you **must** You will need to call `SupportTableData.sync_all!` when deploying your application or running your test suite. This gem includes a rake task `support_table_data:sync` that is suitable for hooking into deploy or CI scripts. -This task is automatically run whenever you run any of these Rails tasks so if these are already part of you deploy or CI scripts, then no additional setup is required: +This task is automatically run whenever you run any of these Rails tasks so if these are already part of your deploy or CI scripts, then no additional setup is required: - `db:seed` - `db:seed:replant` From 75c69a22b83085ef3a13e1001197fd7b1bca7c56 Mon Sep 17 00:00:00 2001 From: Brian Durand Date: Mon, 5 Jan 2026 22:01:36 -0800 Subject: [PATCH 5/6] Update lib/support_table_data.rb Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- lib/support_table_data.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/support_table_data.rb b/lib/support_table_data.rb index 7d57092..7e5a5d3 100644 --- a/lib/support_table_data.rb +++ b/lib/support_table_data.rb @@ -19,7 +19,7 @@ module SupportTableData @support_table_instance_keys = nil @support_table_dependencies = [] - # Private class attritebute to hold the key attribute name. Use `support_table_key_attribute` instead. + # Private class attribute to hold the key attribute name. Use `support_table_key_attribute` instead. # @private class_attribute :_support_table_key_attribute, instance_accessor: false class << self From b0854df0feb1359298ba6d7b1a535afa541d4c98 Mon Sep 17 00:00:00 2001 From: Brian Durand Date: Mon, 5 Jan 2026 22:21:32 -0800 Subject: [PATCH 6/6] ruby 4 tweaks --- .github/workflows/continuous_integration.yml | 2 +- Gemfile | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/continuous_integration.yml b/.github/workflows/continuous_integration.yml index 008301d..e3358dc 100644 --- a/.github/workflows/continuous_integration.yml +++ b/.github/workflows/continuous_integration.yml @@ -62,7 +62,7 @@ jobs: bundle config set gemfile "gemfiles/${{ matrix.appraisal }}.gemfile" - name: Install gems run: | - bundle update + bundle install - name: Run Tests run: bundle exec rake - name: standardrb diff --git a/Gemfile b/Gemfile index d90009d..25d24cc 100644 --- a/Gemfile +++ b/Gemfile @@ -4,6 +4,7 @@ gemspec gem "rspec", "~> 3.0" gem "rake" +gem "irb" gem "sqlite3" gem "appraisal" gem "standard", "~>1.0"