Skip to content

Typed schema for better integrate with json column#162

Open
jasl wants to merge 5 commits into
sergiobayona:mainfrom
jasl:typed-schema
Open

Typed schema for better integrate with json column#162
jasl wants to merge 5 commits into
sergiobayona:mainfrom
jasl:typed-schema

Conversation

@jasl
Copy link
Copy Markdown
Contributor

@jasl jasl commented Jan 28, 2026

Inspired by https://github.com/DmitryTsepelev/store_model

It helps model structured JSON columns.

@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented Jan 28, 2026

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 87.39496% with 15 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
lib/easy_talk/active_model_type.rb 87.25% 13 Missing ⚠️
lib/easy_talk/model.rb 66.66% 1 Missing ⚠️
lib/easy_talk/schema.rb 66.66% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9fff3653b9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +47 to +49
def changed_in_place?(raw_old_value, new_value)
cast_value(raw_old_value) != new_value
end
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid always-dirty attributes in ActiveRecord

When ActiveRecord checks whether a JSON attribute changed, it calls changed_in_place? with the raw DB value and the current (type-cast) value. Here cast_value(raw_old_value) always builds a new schema instance, and EasyTalk schema instances only implement == for Hash (otherwise they use object identity). That means two instances with identical data will still compare unequal, so the attribute is marked dirty on every save even if nothing changed. This breaks dirty tracking/partial updates for records that haven’t been modified.

Useful? React with 👍 / 👎.

Comment on lines 271 to 275
value = record.public_send(prop_name)
next if value.blank? || !value.is_a?(String)

Time.parse(value)
Time.zone.parse(value)
record.errors.add(prop_name, 'must be a valid time in HH:MM:SS format') unless value.match?(/\A\d{2}:\d{2}:\d{2}/)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Handle nil Time.zone in time format validation

Switching from Time.parse to Time.zone.parse introduces a crash when Time.zone is nil (common outside Rails or before time zone configuration). In that case Time.zone.parse raises NoMethodError, which isn’t rescued here, so validations blow up instead of adding an error. This regression affects ActiveModel-only usage or early initialization paths.

Useful? React with 👍 / 👎.

@jasl
Copy link
Copy Markdown
Contributor Author

jasl commented Feb 2, 2026

kindly ping @sergiobayona

@sergiobayona
Copy link
Copy Markdown
Owner

sergiobayona commented Feb 3, 2026

@jasl thanks! could you write a basic example showing how ActiveModelType would be used in a real scenario?

@jasl
Copy link
Copy Markdown
Contributor Author

jasl commented Feb 3, 2026

@jasl thanks! could you write a basic example showing how ActiveModelType would be used in a real scenario?

I'm working on a hobby project https://github.com/jasl/tavern_kit/tree/main/playground which is a SillyTavern clone, and it's heavily using JSONB columns to store very complex but structured configurations, for example https://github.com/jasl/tavern_kit/blob/main/playground/app/models/conversation_settings/character_settings.rb

Without this PR, I need to use serialize with a custom coder https://github.com/jasl/tavern_kit/blob/main/playground/app/models/concerns/easy_talk_coder.rb

serialize :data, coder: EasyTalkCoder.new(TavernKit::Character::Schema)

I need to handle its state myself.

WIth the PR, no custom code needed, just attribute :data, TavernKit::Character::Schema.to_type and Rails would take care of everything.

jasl added 5 commits February 7, 2026 00:22
Fallback to Time.parse when Time.zone is unset so time format validation
adds errors instead of raising for ActiveModel-only usage.
Use a frozen BOOLEAN_VALUES constant instead of allocating [true, false] repeatedly during validation.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants