Class: LcpRuby::Types::BuiltInTypes

Inherits:
Object
  • Object
show all
Defined in:
lib/lcp_ruby/types/built_in_types.rb

Constant Summary collapse

RESERVED_ENUM_NAMES =

Field names that cause AR’s ‘enum` macro to raise `ArgumentError` at boot. AR’s ‘enum :name, […]` generates a class method named after the pluralized field and rejects when that pluralized form is already a class method on the AR class.

Empirically (Rails 8.x), only ‘value` triggers this — AR defines `Klass.values` via the attributes/Relation surface, so `enum :value, […]` would generate a clashing `Klass.values`. Other plausible-sounding names (where, find, count, etc.) DO NOT clash: their pluralized forms (`wheres`, `finds`, `counts`) aren’t AR class methods, so AR’s enum macro accepts them. The single known clash is listed here as a tested baseline.

Correctness is enforced by ‘built_in_types_spec.rb` “RESERVED_ENUM_NAMES correctness” — a runtime test that exercises AR’s actual ‘enum` macro for each entry, asserting an ArgumentError. If a future Rails version adds new clashes, the test catches them and this list is extended.

%w[value].freeze
DEFINITIONS =

Phase 1 invariant: NO ‘validations:` key on any BASE_TYPES backfill entry. `apply_type_default_validations` (validation_applicator.rb:22) gates only on `if field.type_definition`; once a string field has any type_definition, that path runs. A `validations:` array on a backfilled base type would silently apply those validators to every typed field at runtime — breaking the “Phase 1 is behavior-neutral” claim.

{
  # ----- BASE_TYPES backfill (registry-shaping, behavior-neutral) -----
  "string"      => { name: "string",      base_type: "string" },
  "text"        => {
    name: "text",
    base_type: "text",
    renderer: "truncate",
    default_index_visible: false
  },
  "integer"     => { name: "integer",     base_type: "integer" },
  "float"       => { name: "float",       base_type: "float" },
  "decimal"     => { name: "decimal",     base_type: "decimal" },
  "boolean"     => {
    name: "boolean",
    base_type: "boolean",
    renderer: "boolean_icon",
    null_false_validation: [
      { "type" => "inclusion", "options" => { "in" => [ true, false ] } }
    ]
  },
  "date"        => { name: "date",        base_type: "date" },
  "datetime"    => { name: "datetime",    base_type: "datetime" },
  "enum"        => {
    name: "enum",
    base_type: "enum",
    renderer: "badge",
    reserved_clashes: RESERVED_ENUM_NAMES
  },
  "file"        => {
    name: "file",
    base_type: "file",
    default_index_visible: false
  },
  "rich_text"   => {
    name: "rich_text",
    base_type: "rich_text",
    renderer: "rich_text",
    default_index_visible: false
  },
  "json"        => {
    name: "json",
    base_type: "json",
    renderer: "code",
    default_index_visible: false
  },
  "uuid"        => { name: "uuid",        base_type: "uuid" },
  "attachment"  => {
    name: "attachment",
    base_type: "attachment",
    renderer: "attachment_preview",
    default_index_visible: false
  },
  "array"       => {
    name: "array",
    base_type: "array",
    default_index_visible: false
  },

  # ----- Business types (existing) -----
  "email" => {
    name: "email",
    base_type: "string",
    transforms: %w[strip downcase],
    validations: [
      { "type" => "format", "options" => { "with" => '\A[^@\s]+@[^@\s]+\z', "allow_blank" => true } }
    ],
    input_type: "email",
    renderer: "email_link",
    column_options: { limit: 255 }
  },
  "phone" => {
    name: "phone",
    base_type: "string",
    transforms: %w[strip normalize_phone],
    validations: [
      { "type" => "format", "options" => { "with" => '\A\+?\d{7,15}\z', "allow_blank" => true } }
    ],
    input_type: "tel",
    renderer: "phone_link",
    column_options: { limit: 50 }
  },
  "url" => {
    name: "url",
    base_type: "string",
    transforms: %w[strip normalize_url],
    validations: [
      { "type" => "format", "options" => { "with" => '\A(https?|ftp)://[^\s/$.?#].[^\s]*\z', "allow_blank" => true } }
    ],
    input_type: "url",
    renderer: "url_link",
    column_options: { limit: 2048 }
  },
  "color" => {
    name: "color",
    base_type: "string",
    transforms: %w[strip downcase],
    validations: [
      { "type" => "format", "options" => { "with" => '\A#[0-9a-f]{6}\z', "allow_blank" => true } }
    ],
    input_type: "color",
    renderer: "color_swatch",
    column_options: { limit: 7 }
  },
  # Use for FK columns that reference Rails' default bigint primary
  # key — `:integer` would silently create a 4-byte INT column that
  # mismatches the 8-byte BIGINT on JOINs.
  "bigint" => {
    name: "bigint",
    base_type: "integer",
    column_type: :bigint
  }
}.freeze

Class Method Summary collapse

Class Method Details

.register_all!Object

Idempotent: safe to call multiple times. Each entry is re-built and ‘TypeRegistry.register` overwrites by name. Called by the engine at boot (`engine.rb:246`) and lazily from the entity generator (`entity_generator.rb#parse_fields`) for the standalone `rails generate` path where the engine doesn’t fully boot.



148
149
150
151
152
153
# File 'lib/lcp_ruby/types/built_in_types.rb', line 148

def register_all!
  DEFINITIONS.each do |name, attrs|
    type_def = TypeDefinition.new(**attrs)
    TypeRegistry.register(name, type_def)
  end
end