From bcf4d97e3cee0f0bf85bea540dabf8ca165c9263 Mon Sep 17 00:00:00 2001 From: Patrick Hurley Date: Wed, 4 Jun 2025 14:02:26 -0400 Subject: [PATCH 1/9] THUND-107: Add support for a deprecitated ltf table --- .ruby-version | 2 +- .tool-versions | 2 +- lib/large_text_field/named_text_value.rb | 5 +- lib/large_text_field/owner.rb | 74 ++++++++++++++++--- .../app/models/dummy_large_text_value.rb | 6 ++ test/dummy/app/models/person.rb | 14 ++++ test/dummy/config/storage.yml | 34 +++++++++ ...02151745_create_dummy_large_text_fields.rb | 19 +++++ .../migrate/20250604150254_create_people.rb | 9 +++ test/dummy/db/schema.rb | 23 +++++- test/dummy/db/storage.yml | 7 ++ 11 files changed, 178 insertions(+), 17 deletions(-) create mode 100644 test/dummy/app/models/dummy_large_text_value.rb create mode 100644 test/dummy/app/models/person.rb create mode 100644 test/dummy/config/storage.yml create mode 100644 test/dummy/db/migrate/20250602151745_create_dummy_large_text_fields.rb create mode 100644 test/dummy/db/migrate/20250604150254_create_people.rb create mode 100644 test/dummy/db/storage.yml diff --git a/.ruby-version b/.ruby-version index 9cec716..37d02a6 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -3.1.6 +3.3.8 diff --git a/.tool-versions b/.tool-versions index a4023dc..057186b 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1 +1 @@ -ruby 2.7.5 +ruby 3.3.8 diff --git a/lib/large_text_field/named_text_value.rb b/lib/large_text_field/named_text_value.rb index 1ac144c..08e3005 100644 --- a/lib/large_text_field/named_text_value.rb +++ b/lib/large_text_field/named_text_value.rb @@ -5,7 +5,8 @@ module LargeTextField class NamedTextValue < ApplicationRecord # Schema - # field_name :string, :limit => 255 + # field_name :string, :limit => 255 + # foo :string # value :text, :null=>true, :limit => MYSQL_MEDIUM_TEXT_UTF8_LIMIT # # index [ :owner_type, :owner_id, :field_name ], :name => 'large_text_field_by_owner_field', :unique=>true @@ -14,4 +15,4 @@ class NamedTextValue < ApplicationRecord self.table_name = "large_text_fields" end -end +end \ No newline at end of file diff --git a/lib/large_text_field/owner.rb b/lib/large_text_field/owner.rb index cb7489b..4a3f503 100644 --- a/lib/large_text_field/owner.rb +++ b/lib/large_text_field/owner.rb @@ -8,20 +8,25 @@ module Owner include ActiveSupport::Callbacks included do - has_many( - :large_text_fields, - class_name: "LargeTextField::NamedTextValue", - as: :owner, - autosave: true, - dependent: :destroy, - inverse_of: :owner - ) validate :validate_large_text_fields before_save :write_large_text_field_changes define_callbacks :large_text_field_save class_attribute :large_text_field_options self.large_text_field_options = {} + + class_attribute :initialized + self.initialized = false + + class_attribute :large_text_field_class_name + self.large_text_field_class_name = "LargeTextField::NamedTextValue" + class_attribute :large_text_field_class + self.large_text_field_class = LargeTextField::NamedTextValue + + class_attribute :large_text_field_depreciated_class_name + self.large_text_field_depreciated_class_name = nil + class_attribute :large_text_field_depreciated_class + self.large_text_field_depreciated_class = nil end def dup @@ -42,7 +47,13 @@ def reload(options = nil) end def text_field_hash - @text_field_hash ||= large_text_fields.build_hash { |text_field| [text_field.field_name, text_field] } + unless @text_field_hash + @text_field_hash = large_text_fields.build_hash { |text_field| [text_field.field_name, text_field] } + if large_text_field_depreciated_class_name + legacy_large_text_fields.each { |text_field| @text_field_hash[text_field.field_name] ||= text_field } + end + end + @text_field_hash end def text_field_hash_loaded? @@ -61,7 +72,7 @@ def set_text_field(original_field_name, original_value) if (field = text_field_hash[field_name]) field.value = value else - text_field_hash[field_name] = LargeTextField::NamedTextValue.new(owner: self, field_name: field_name, value: value) + text_field_hash[field_name] = large_text_field_class.new(owner: self, field_name:, value:) end end @@ -91,7 +102,10 @@ def validate_large_text_fields def write_large_text_field_changes run_callbacks(:large_text_field_save) - @text_field_hash = text_field_hash.compact.select { |_key, value| value.value.presence } + @text_field_hash = text_field_hash + .compact + .select { |_key, value| value.value.presence } + .transform_values { |value| value.is_a?(large_text_field_class) ? value : large_text_field_class.new(owner: self, field_name: value.field_name, value: value.value) } self.large_text_fields = text_field_hash.values.compact true end @@ -103,7 +117,43 @@ def _clear_text_field_on_dup end module ClassMethods + def large_text_field_class_name_override(value) + self.large_text_field_class_name = value + self.large_text_field_class = Object.const_get(value) + end + + def large_text_field_depreciated_class_name_override(value) + self.large_text_field_depreciated_class_name = value + self.large_text_field_depreciated_class = Object.const_get(value) + end + + def initialize_large_text_field + return if initialized # skip if already initialized + + has_many( + :large_text_fields, + class_name: large_text_field_class_name, + as: :owner, + autosave: true, + dependent: :destroy, + inverse_of: :owner + ) + if large_text_field_depreciated_class_name + has_many( + :legacy_large_text_fields, + class_name: large_text_field_depreciated_class_name, + as: :owner, + autosave: true, + dependent: :destroy, + inverse_of: :owner + ) + end + self.initialized = true + end + def large_text_field(field_name, maximum: nil, singularize_errors: false) + initialize_large_text_field # ensure the association is initialized + field_name = field_name.to_s # validate custom maximum @@ -117,7 +167,7 @@ def large_text_field(field_name, maximum: nil, singularize_errors: false) end end - large_text_field_options[field_name] = { maximum: maximum, singularize_errors: singularize_errors } + large_text_field_options[field_name] = { maximum:, singularize_errors: } define_method(field_name) { get_text_field(field_name) } define_method("#{field_name}=") { |value| set_text_field(field_name, value) } define_method("#{field_name}_changed?") { text_field_changed(field_name) } diff --git a/test/dummy/app/models/dummy_large_text_value.rb b/test/dummy/app/models/dummy_large_text_value.rb new file mode 100644 index 0000000..3720e8e --- /dev/null +++ b/test/dummy/app/models/dummy_large_text_value.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +class DummyLargeTextValue < ApplicationRecord + belongs_to :owner, polymorphic: true, inverse_of: :large_text_fields + self.table_name = "dummy_large_text_fields" +end diff --git a/test/dummy/app/models/person.rb b/test/dummy/app/models/person.rb new file mode 100644 index 0000000..f9075a3 --- /dev/null +++ b/test/dummy/app/models/person.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +class Person < ApplicationRecord + include ::LargeTextField::Owner + + # Schema + # name :string, :limit => 255 + + large_text_field_class_name_override "DummyLargeTextValue" + large_text_field_depreciated_class_name_override "LargeTextField::NamedTextValue" + + large_text_field :story + large_text_field :resume +end diff --git a/test/dummy/config/storage.yml b/test/dummy/config/storage.yml new file mode 100644 index 0000000..d32f76e --- /dev/null +++ b/test/dummy/config/storage.yml @@ -0,0 +1,34 @@ +test: + service: Disk + root: <%= Rails.root.join("tmp/storage") %> + +local: + service: Disk + root: <%= Rails.root.join("storage") %> + +# Use rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key) +# amazon: +# service: S3 +# access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %> +# secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %> +# region: us-east-1 +# bucket: your_own_bucket + +# Remember not to checkin your GCS keyfile to a repository +# google: +# service: GCS +# project: your_project +# credentials: <%= Rails.root.join("path/to/gcs.keyfile") %> +# bucket: your_own_bucket + +# Use rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key) +# microsoft: +# service: AzureStorage +# storage_account_name: your_account_name +# storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %> +# container: your_container_name + +# mirror: +# service: Mirror +# primary: local +# mirrors: [ amazon, google, microsoft ] diff --git a/test/dummy/db/migrate/20250602151745_create_dummy_large_text_fields.rb b/test/dummy/db/migrate/20250602151745_create_dummy_large_text_fields.rb new file mode 100644 index 0000000..5707166 --- /dev/null +++ b/test/dummy/db/migrate/20250602151745_create_dummy_large_text_fields.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +class CreateDummyLargeTextFields < (Rails::VERSION::MAJOR >= 5 ? ActiveRecord::Migration[4.2] : ActiveRecord::Migration) + def self.up + # rubocop:disable Rails/CreateTableWithTimestamps + create_table :dummy_large_text_fields do |t| + t.string :field_name, null: false + t.text :value, char_limit: 5_592_405, limit: 16_777_215 + t.integer :owner_id, null: false + t.string :owner_type, null: false + end + # rubocop:enable Rails/CreateTableWithTimestamps + add_index :dummy_large_text_fields, %i[owner_type owner_id field_name], unique: true, name: 'dummy_large_text_field_by_owner_field' + end + + def self.down + drop_table :dummy_large_text_fields + end +end diff --git a/test/dummy/db/migrate/20250604150254_create_people.rb b/test/dummy/db/migrate/20250604150254_create_people.rb new file mode 100644 index 0000000..bbec02c --- /dev/null +++ b/test/dummy/db/migrate/20250604150254_create_people.rb @@ -0,0 +1,9 @@ +class CreatePeople < ActiveRecord::Migration[6.1] + def change + create_table :people do |t| + t.string :name + + t.timestamps + end + end +end diff --git a/test/dummy/db/schema.rb b/test/dummy/db/schema.rb index b6b62a1..27ae42f 100644 --- a/test/dummy/db/schema.rb +++ b/test/dummy/db/schema.rb @@ -10,7 +10,22 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2016_02_17_033529) do +ActiveRecord::Schema.define(version: 2025_06_04_150254) do + + create_table "dummu_large_text_fields", id: :integer, charset: "utf8mb3", force: :cascade do |t| + t.string "field_name", null: false + t.text "value", size: :medium + t.integer "owner_id", null: false + t.string "owner_type", null: false + end + + create_table "dummy_large_text_fields", id: :integer, charset: "utf8mb3", force: :cascade do |t| + t.string "field_name", null: false + t.text "value", size: :medium + t.integer "owner_id", null: false + t.string "owner_type", null: false + t.index ["owner_type", "owner_id", "field_name"], name: "dummy_large_text_field_by_owner_field", unique: true + end create_table "large_text_fields", id: :integer, charset: "utf8mb3", force: :cascade do |t| t.string "field_name", null: false @@ -24,4 +39,10 @@ t.string "name", null: false end + create_table "people", charset: "utf8mb3", force: :cascade do |t| + t.string "name" + t.datetime "created_at", precision: 6, null: false + t.datetime "updated_at", precision: 6, null: false + end + end diff --git a/test/dummy/db/storage.yml b/test/dummy/db/storage.yml new file mode 100644 index 0000000..e900ef0 --- /dev/null +++ b/test/dummy/db/storage.yml @@ -0,0 +1,7 @@ +test: + service: Disk + root: <%= Rails.root.join("tmp/storage") %> + +local: + service: Disk + root: <%= Rails.root.join("storage") %> \ No newline at end of file From 73e249b6d33863779b5be5f299cd9827c4f30d60 Mon Sep 17 00:00:00 2001 From: Patrick Hurley Date: Wed, 4 Jun 2025 15:08:13 -0400 Subject: [PATCH 2/9] THUND-107: Add missing test --- test/dummy/test/models/person_test.rb | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 test/dummy/test/models/person_test.rb diff --git a/test/dummy/test/models/person_test.rb b/test/dummy/test/models/person_test.rb new file mode 100644 index 0000000..357c9af --- /dev/null +++ b/test/dummy/test/models/person_test.rb @@ -0,0 +1,7 @@ +require "test_helper" + +class PersonTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end From 1784132ade66b5c35b17da1f5f3ce884d2021b0f Mon Sep 17 00:00:00 2001 From: Patrick Hurley Date: Wed, 4 Jun 2025 15:08:54 -0400 Subject: [PATCH 3/9] THUND-107: Add missing test --- test/unit/dummy/person_test.rb | 43 ++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 test/unit/dummy/person_test.rb diff --git a/test/unit/dummy/person_test.rb b/test/unit/dummy/person_test.rb new file mode 100644 index 0000000..c7f78ea --- /dev/null +++ b/test/unit/dummy/person_test.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +require 'test_helper' + +module LargeTextField + class PersonTest < ActiveSupport::TestCase + should "be able to construct a Person" do + p = Person.new(name: "Obama") + p.save! + + resume_text = "Obama's resume is very long and detailed, covering his early life, education, political career, and achievements. It includes numerous accomplishments and accolades that highlight his leadership skills and public service." + p.resume = resume_text + p.save! + + assert_equal resume_text, p.resume + + p = Person.find(p.id) + + assert_equal resume_text, p.resume + end + + should "find fields from depreciated large text field table" do + p = Person.new(name: "Obama") + p.story = "Story about Obama" + p.save! + NamedTextValue.create!(field_name: "resume", value: "Old value", owner: p) + p = Person.find(p.id) + + assert_equal "Old value", p.resume + assert_equal "Story about Obama", p.story + + # Ensure that the story was saved in the new table + assert_not_predicate DummyLargeTextValue.where(owner: p, field_name: "resume"), :exists? + assert_predicate DummyLargeTextValue.where(owner: p, field_name: "story"), :exists? + + p.resume = "New resume value" + p.save! + + # Ensure that the updated resume was saved in the new table + assert_predicate DummyLargeTextValue.where(owner: p, field_name: "resume"), :exists? + end + end +end From c0a1a69dc89d3cf090922ec77c06009c64f41bfa Mon Sep 17 00:00:00 2001 From: Patrick Hurley Date: Wed, 4 Jun 2025 16:08:04 -0400 Subject: [PATCH 4/9] THUND-107: Light cleanup --- .ruby-version | 2 +- .tool-versions | 2 +- Gemfile.lock | 2 +- lib/large_text_field/named_text_value.rb | 2 +- lib/large_text_field/version.rb | 2 +- test/dummy/db/schema.rb | 7 ------- test/dummy/db/storage.yml | 2 +- test/dummy/test/models/person_test.rb | 7 ------- 8 files changed, 6 insertions(+), 20 deletions(-) delete mode 100644 test/dummy/test/models/person_test.rb diff --git a/.ruby-version b/.ruby-version index 37d02a6..9cec716 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -3.3.8 +3.1.6 diff --git a/.tool-versions b/.tool-versions index 057186b..13369f6 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1 +1 @@ -ruby 3.3.8 +ruby 3.1.6 diff --git a/Gemfile.lock b/Gemfile.lock index b29554e..10ea20a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - large_text_field (1.2.0) + large_text_field (1.3.0) invoca-utils (~> 0.3) rails (>= 6.0) diff --git a/lib/large_text_field/named_text_value.rb b/lib/large_text_field/named_text_value.rb index 08e3005..67a502e 100644 --- a/lib/large_text_field/named_text_value.rb +++ b/lib/large_text_field/named_text_value.rb @@ -15,4 +15,4 @@ class NamedTextValue < ApplicationRecord self.table_name = "large_text_fields" end -end \ No newline at end of file +end diff --git a/lib/large_text_field/version.rb b/lib/large_text_field/version.rb index b2af9cf..171cf8e 100644 --- a/lib/large_text_field/version.rb +++ b/lib/large_text_field/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module LargeTextField - VERSION = "1.2.0" + VERSION = "1.3.0" end diff --git a/test/dummy/db/schema.rb b/test/dummy/db/schema.rb index 27ae42f..073f2ab 100644 --- a/test/dummy/db/schema.rb +++ b/test/dummy/db/schema.rb @@ -12,13 +12,6 @@ ActiveRecord::Schema.define(version: 2025_06_04_150254) do - create_table "dummu_large_text_fields", id: :integer, charset: "utf8mb3", force: :cascade do |t| - t.string "field_name", null: false - t.text "value", size: :medium - t.integer "owner_id", null: false - t.string "owner_type", null: false - end - create_table "dummy_large_text_fields", id: :integer, charset: "utf8mb3", force: :cascade do |t| t.string "field_name", null: false t.text "value", size: :medium diff --git a/test/dummy/db/storage.yml b/test/dummy/db/storage.yml index e900ef0..695f17b 100644 --- a/test/dummy/db/storage.yml +++ b/test/dummy/db/storage.yml @@ -4,4 +4,4 @@ test: local: service: Disk - root: <%= Rails.root.join("storage") %> \ No newline at end of file + root: <%= Rails.root.join("storage") %> diff --git a/test/dummy/test/models/person_test.rb b/test/dummy/test/models/person_test.rb deleted file mode 100644 index 357c9af..0000000 --- a/test/dummy/test/models/person_test.rb +++ /dev/null @@ -1,7 +0,0 @@ -require "test_helper" - -class PersonTest < ActiveSupport::TestCase - # test "the truth" do - # assert true - # end -end From bb4eab13495185fa868b8f50aac9b6d2d6d7414f Mon Sep 17 00:00:00 2001 From: Patrick Hurley Date: Wed, 4 Jun 2025 16:09:58 -0400 Subject: [PATCH 5/9] THUND-107: Cleanups --- lib/large_text_field/owner.rb | 20 ++++++++++---------- test/dummy/app/models/person.rb | 2 +- test/unit/dummy/person_test.rb | 2 +- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/large_text_field/owner.rb b/lib/large_text_field/owner.rb index 4a3f503..91ffad4 100644 --- a/lib/large_text_field/owner.rb +++ b/lib/large_text_field/owner.rb @@ -23,10 +23,10 @@ module Owner class_attribute :large_text_field_class self.large_text_field_class = LargeTextField::NamedTextValue - class_attribute :large_text_field_depreciated_class_name - self.large_text_field_depreciated_class_name = nil - class_attribute :large_text_field_depreciated_class - self.large_text_field_depreciated_class = nil + class_attribute :large_text_field_deprecated_class_name + self.large_text_field_deprecated_class_name = nil + class_attribute :large_text_field_deprecated_class + self.large_text_field_deprecated_class = nil end def dup @@ -49,7 +49,7 @@ def reload(options = nil) def text_field_hash unless @text_field_hash @text_field_hash = large_text_fields.build_hash { |text_field| [text_field.field_name, text_field] } - if large_text_field_depreciated_class_name + if large_text_field_deprecated_class_name legacy_large_text_fields.each { |text_field| @text_field_hash[text_field.field_name] ||= text_field } end end @@ -122,9 +122,9 @@ def large_text_field_class_name_override(value) self.large_text_field_class = Object.const_get(value) end - def large_text_field_depreciated_class_name_override(value) - self.large_text_field_depreciated_class_name = value - self.large_text_field_depreciated_class = Object.const_get(value) + def large_text_field_deprecated_class_name_override(value) + self.large_text_field_deprecated_class_name = value + self.large_text_field_deprecated_class = Object.const_get(value) end def initialize_large_text_field @@ -138,10 +138,10 @@ def initialize_large_text_field dependent: :destroy, inverse_of: :owner ) - if large_text_field_depreciated_class_name + if large_text_field_deprecated_class_name has_many( :legacy_large_text_fields, - class_name: large_text_field_depreciated_class_name, + class_name: large_text_field_deprecated_class_name, as: :owner, autosave: true, dependent: :destroy, diff --git a/test/dummy/app/models/person.rb b/test/dummy/app/models/person.rb index f9075a3..81bde7a 100644 --- a/test/dummy/app/models/person.rb +++ b/test/dummy/app/models/person.rb @@ -7,7 +7,7 @@ class Person < ApplicationRecord # name :string, :limit => 255 large_text_field_class_name_override "DummyLargeTextValue" - large_text_field_depreciated_class_name_override "LargeTextField::NamedTextValue" + large_text_field_deprecated_class_name_override "LargeTextField::NamedTextValue" large_text_field :story large_text_field :resume diff --git a/test/unit/dummy/person_test.rb b/test/unit/dummy/person_test.rb index c7f78ea..aa42028 100644 --- a/test/unit/dummy/person_test.rb +++ b/test/unit/dummy/person_test.rb @@ -19,7 +19,7 @@ class PersonTest < ActiveSupport::TestCase assert_equal resume_text, p.resume end - should "find fields from depreciated large text field table" do + should "find fields from deprecated large text field table" do p = Person.new(name: "Obama") p.story = "Story about Obama" p.save! From 688181a4d0aca82f2e1215deaa723b294f515b88 Mon Sep 17 00:00:00 2001 From: Patrick Hurley Date: Wed, 4 Jun 2025 16:23:03 -0400 Subject: [PATCH 6/9] THUND-107: Update readme/changelog --- CHANGELOG.md | 21 ++++++++++++++++++++- README.md | 37 ++++++++++++++++++++++++------------- 2 files changed, 44 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ae96dc..18969fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,40 +4,59 @@ Inspired by [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). Note: This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.3.0] - 2025-06-04 + +### Added + +- Added large_text_field_class_name_override and large_text_field_deprecated_class_name_override class methods allowing a model to use a different table for large_text_field support + ## [1.1.0] - 2023-05-17 + ### Fixed + - Relaxed dependency on rails to allow rails 7. ### Added + - Added support for rails 7 - Added appraisal tests for rails 7 ### Removed + - Rails 4 and Ruby 2.6 are no longer supported. Automated testing of those versions has been removed. ## [1.0.2] - 2021-10-22 + ### Fixed + - Bug where reload didn't accept an argument even though ActiveRecord::Persistence#reload can. ## [1.0.1] - 2021-02-16 + ### Fixed + - Fixed migration to work with Rails 6 (by adding [4.2] suffix). - Set rails version to < 6.1 since that moved some files around and broke requires. ## [1.0.0] - 2020-05-15 + ### Added + - Added support for rails 5 and 6. - Added appraisal tests for all supported rails version: 4/5/6 ### Removed + - Support for `protected_parameters` has been removed ## [0.3.2] - 2020-05-03 + ### Changed + - Replaced dependence on hobo_support with invoca_utils - Make invoca_utils a declared dependency. (It always was, it just wasn't declared) - +[1.3.0]: https://github.com/Invoca/large_text_field/compare/v1.1.0...v1.3.0 [1.1.0]: https://github.com/Invoca/large_text_field/compare/v1.0.2...v1.1.0 [1.0.2]: https://github.com/Invoca/large_text_field/compare/v1.0.1...v1.0.2 [1.0.1]: https://github.com/Invoca/large_text_field/compare/v1.0.0...v1.0.1 diff --git a/README.md b/README.md index cc6c53d..2d6cc90 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ characters. Defining new fields on models does not require database migrations. central table that is polymorphically associated with the model, but they act like a column on the same model. ## How do I use it? + In you Gemfile add: ``` @@ -13,8 +14,8 @@ In you Gemfile add: There will be database migration you need to run to define the table, so go ahead and run that... -Any class that wants to define a large text field should include **::LargeTextField::Owner**, -and then define text fields by calling the **large_text_field** macro. +Any class that wants to define a large text field should include **::LargeTextField::Owner**, +and then define text fields by calling the **large_text_field** macro. For example the following is a `Library` class that has large text fields for `notes` and `description`. @@ -27,25 +28,35 @@ class Library < ApplicationRecord end ``` -That's it! You can then access `notes` and `description` as if they were columns on your class. +That's it! You can then access `notes` and `description` as if they were columns on your class. The `large_text_field` macro takes the following options... - * **maximum: number** - The maximum length of a large text field. By default this is 5,000,000 characters, -but it can be set to less using this option. - * **singularize_errors: boolean** - should validation messages be singularized. +- **maximum: number** - The maximum length of a large text field. By default this is 5,000,000 characters, + but it can be set to less using this option. +- **singularize_errors: boolean** - should validation messages be singularized. -Large text fields defaults to an empty string. You cannot store `nil` in a large text field. +Large text fields defaults to an empty string. You cannot store `nil` in a large text field. -**Please note:** Large text field uses the *before_save* callback on the class that is the owner for book-keeping. -Callbacks are great, but if there are multiple handlers for the same callback the order in which they are called is -not predictable. If you want to make changes to large_text_field values in the before_save callback, use the -**large_text_field_save** callback instead. This will be called before the large text field book-keeping so your -changes will be saved. For example, this will call the `save_preprocess` method on your class before the large text +**Please note:** Large text field uses the _before_save_ callback on the class that is the owner for book-keeping. +Callbacks are great, but if there are multiple handlers for the same callback the order in which they are called is +not predictable. If you want to make changes to large_text_field values in the before_save callback, use the +**large_text_field_save** callback instead. This will be called before the large text field book-keeping so your +changes will be saved. For example, this will call the `save_preprocess` method on your class before the large text fields are saved... ```ruby set_callback(:large_text_field_save, :before, :save_preprocess) ``` -This project rocks and uses MIT-LICENSE. You should too. +Added class methods: + +```ruby + large_text_field_deprecated_class_name_override "LargeTextField::NamedTextValue" + large_text_field_class_name_override "MyCustomLargeTextField" +``` + +You will not generally need this support; however, it can be helpful when trying to separate a model into a +different database. + +This project rocks and uses MIT-LICENSE. You should too. From 4de9a2472e0fc8321ab6c0876bb877de1057809e Mon Sep 17 00:00:00 2001 From: Patrick Hurley Date: Thu, 5 Jun 2025 08:56:42 -0400 Subject: [PATCH 7/9] THUND-107: Add test, make naming more consistent --- lib/large_text_field/owner.rb | 4 ++-- test/unit/dummy/person_test.rb | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/lib/large_text_field/owner.rb b/lib/large_text_field/owner.rb index 91ffad4..5aa92af 100644 --- a/lib/large_text_field/owner.rb +++ b/lib/large_text_field/owner.rb @@ -50,7 +50,7 @@ def text_field_hash unless @text_field_hash @text_field_hash = large_text_fields.build_hash { |text_field| [text_field.field_name, text_field] } if large_text_field_deprecated_class_name - legacy_large_text_fields.each { |text_field| @text_field_hash[text_field.field_name] ||= text_field } + deprecated_large_text_fields.each { |text_field| @text_field_hash[text_field.field_name] ||= text_field } end end @text_field_hash @@ -140,7 +140,7 @@ def initialize_large_text_field ) if large_text_field_deprecated_class_name has_many( - :legacy_large_text_fields, + :deprecated_large_text_fields, class_name: large_text_field_deprecated_class_name, as: :owner, autosave: true, diff --git a/test/unit/dummy/person_test.rb b/test/unit/dummy/person_test.rb index aa42028..a687983 100644 --- a/test/unit/dummy/person_test.rb +++ b/test/unit/dummy/person_test.rb @@ -39,5 +39,20 @@ class PersonTest < ActiveSupport::TestCase # Ensure that the updated resume was saved in the new table assert_predicate DummyLargeTextValue.where(owner: p, field_name: "resume"), :exists? end + + should "find fields from deprecated large text field table, updates are written to default table" do + p = Person.new(name: "Obama") + NamedTextValue.create!(field_name: "resume", value: "Old value", owner: p) + p = Person.find(p.id) + + p.resume = "Retired" + p.save! + + # Ensure that the resume was saved in the new table + assert_predicate DummyLargeTextValue.where(owner: p, field_name: "resume"), :exists? + + # Does not currently delete the old value + assert_predicate NamedTextValue.where(owner: p, field_name: "resume"), :exists? + end end end From f4c86fca7359edbd581a10c9223d3ab6f4e3e1c4 Mon Sep 17 00:00:00 2001 From: Patrick Hurley Date: Mon, 9 Jun 2025 11:00:53 -0600 Subject: [PATCH 8/9] THUND-107: Add excellent code review suggestions --- CHANGELOG.md | 1 + test/unit/dummy/person_test.rb | 9 +++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 18969fc..bb0bc9c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ Note: This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0 ### Added - Added large_text_field_class_name_override and large_text_field_deprecated_class_name_override class methods allowing a model to use a different table for large_text_field support +- Previously the association to the large text file model was created when the module was included. The creation of this association is now delayed until the first field is defined. This allows for a cleaner syntax for setting options. ## [1.1.0] - 2023-05-17 diff --git a/test/unit/dummy/person_test.rb b/test/unit/dummy/person_test.rb index a687983..879aabb 100644 --- a/test/unit/dummy/person_test.rb +++ b/test/unit/dummy/person_test.rb @@ -45,14 +45,15 @@ class PersonTest < ActiveSupport::TestCase NamedTextValue.create!(field_name: "resume", value: "Old value", owner: p) p = Person.find(p.id) - p.resume = "Retired" + new_value = "Retired" + p.resume = new_value p.save! # Ensure that the resume was saved in the new table - assert_predicate DummyLargeTextValue.where(owner: p, field_name: "resume"), :exists? + assert_predicate DummyLargeTextValue.where(owner: p, field_name: "resume", value: new_value), :exists? - # Does not currently delete the old value - assert_predicate NamedTextValue.where(owner: p, field_name: "resume"), :exists? + # Both values are currently maintained, but the new table is the source of truth + assert_predicate NamedTextValue.where(owner: p, field_name: "resume", value: new_value), :exists? end end end From f991d210b278f2e69f48aa7f4ccd882a9b20fcf8 Mon Sep 17 00:00:00 2001 From: Patrick Hurley Date: Mon, 9 Jun 2025 11:03:49 -0600 Subject: [PATCH 9/9] THUND-107: Update date in CHANGELOG --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bb0bc9c..a777694 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ Inspired by [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). Note: This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [1.3.0] - 2025-06-04 +## [1.3.0] - 2025-07-09 ### Added