Class: ActiveRecord::ConnectionAdapters::VirgodbTimestamp

Inherits:
Type::DateTime
  • Object
show all
Defined in:
lib/active_record/connection_adapters/virgodb_adapter.rb

Overview

virgodb's raw "Timestamp" type (crates/compact/src/schema.rs) is plain UNIX seconds -- an Integer, not a real SQL DATETIME/TIMESTAMP string -- but it still matches Rails' generic %r(time)i registration (see VirgodbAdapter.initialize_type_map below), landing on the stock ActiveRecord::Type::DateTime, which only knows how to parse a real date/time STRING (or an already-real Time/Date/Hash). Two confirmed, real failure modes from that mismatch, not just cosmetic ones:

  1. Type::DateTime.new.cast("0") -- "0" being a perfectly valid epoch-seconds default (e.g. exit_page_at/split_test_id_at's own DEFAULT 0 sentinel, see db/migrate_virgodb/20260727000002_create_ahoy_visits.rb in hintpot) -- returns nil. Column#default itself is unaffected (DateTime is mutable?, so Column#initialize keeps the raw string rather than deserializing it -- see ActiveModel::Type::DateTime#mutable?'s own comment), which is why the schema_versions capture above was never wrong. But Model.column_defaults (what a real host app's own schema-annotation tooling reads) DOES deserialize through the cast type, surfacing as a misleading default(NULL) in a real model's annotation for a column that has a perfectly real default.
  2. Type::DateTime.new.cast(1753000000) (a real Integer write-time value) ALSO doesn't produce a real Time object -- DateTime#cast only recognizes String/Time/Date/Hash input, so an Integer just passes through unchanged. Harmless today (nothing in this codebase reads a Timestamp column back through a real ActiveRecord attribute -- see Virgodb::Registry.query, which returns raw parsed JSON, bypassing AR type casting entirely), but a real latent gap the moment anything ever does.

Subclassing ActiveRecord::Type::DateTime (not building from scratch) keeps .type == :datetime -- the semantically correct label for a timestamp column, unlike the columns String fix above where Rails had no keyword at all -- and keeps every other DateTime behavior (real date strings, Time/Date input, timezone handling) working exactly as before; only the epoch-seconds-as-Integer-or-String case needed a fix. deserialize isn't overridden separately -- ActiveModel::Type ::Value#deserialize just calls cast by default (confirmed: neither ActiveModel::Type::DateTime nor ActiveRecord::Type::DateTime override it), so fixing cast alone is enough for both directions.

Instance Method Summary collapse

Instance Method Details

#cast(value) ⇒ Object



84
85
86
87
88
89
# File 'lib/active_record/connection_adapters/virgodb_adapter.rb', line 84

def cast(value)
  return value if value.nil? || value.is_a?(::Time) || value.is_a?(::DateTime)

  seconds = Float(value, exception: false)
  seconds ? ::Time.at(seconds).utc : super
end

#serialize(value) ⇒ Object



91
92
93
# File 'lib/active_record/connection_adapters/virgodb_adapter.rb', line 91

def serialize(value)
  value.respond_to?(:to_time) ? value.to_time.to_i : value
end