Class: Torque::PostgreSQL::Attributes::Struct

Inherits:
Base
  • Object
show all
Defined in:
lib/torque/postgresql/attributes/struct.rb

Defined Under Namespace

Classes: Uninitialized

Class Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Base

#==, #[], #[]=, columns_hash, #empty?, encrypts, #hash, #inspect, #read_attribute_before_type_cast, #read_attribute_for_database, #to_h, #type_for_attribute

Class Attribute Details

.strict=(value) ⇒ Object (writeonly)

Sets the attribute strict

Parameters:

  • value

    the value to set the attribute strict to.



18
19
20
# File 'lib/torque/postgresql/attributes/struct.rb', line 18

def strict=(value)
  @strict = value
end

Class Method Details

._default_attributesObject

Declared properties without a default are uninitialized, so that they are absent from the document until they are written to



30
31
32
33
34
35
# File 'lib/torque/postgresql/attributes/struct.rb', line 30

def _default_attributes
  @_struct_default_attributes ||= super.map do |attribute|
    next attribute if attribute.is_a?(ActiveModel::Attribute::UserProvidedDefault)
    Uninitialized.new(attribute.name, attribute.type)
  end
end

.build_on(model, column, klass, default: nil, array: nil, delegate: nil, backfill: nil, strict: nil) ⇒ Object

Setup the struct column on the given model, adding the proper attribute type, default, validation, and delegations

Raises:

  • (ArgumentError)


50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/torque/postgresql/attributes/struct.rb', line 50

def build_on(model, column, klass, default: nil, array: nil, delegate: nil, backfill: nil, strict: nil)
  return unless model.table_exists?

  column = column.to_s
  has_encryption = model.respond_to?(:encrypted_attributes) &&
    model.encrypted_attributes&.include?(column.to_sym)

  raise ArgumentError, <<~MSG.squish if has_encryption
    Unable to setup the struct column "#{column}" on #{model.name}
    because the column is encrypted. Encryption is supported on
    individual struct attributes instead.
  MSG

  info = model.columns_hash[column]
  return if info.nil?

  raise ArgumentError, <<~MSG.squish unless %i[json jsonb].include?(info.type)
    Unable to setup the struct column "#{column}" on #{model.name}
    because #{info.sql_type} columns cannot hold a document.
  MSG

  klass = klass.constantize if klass.is_a?(String) || klass.is_a?(Symbol)
  klass.strict = !!strict if !strict.nil? && klass.respond_to?(:strict=)

  type =
    if array
      Adapter::OID::StructList.new(klass, type: info.type, backfill: backfill)
    else
      Adapter::OID::Struct.new(klass, type: info.type, backfill: backfill)
    end

  type = Adapter::OID::StructSet.new(type) if info.array?

  # Defaults that can be composed from the record have to be resolved
  # on it, all the others are plain attribute defaults
  if default.is_a?(Proc) || default.is_a?(Symbol)
    model.attribute(column, type)
    resolve_default_on(model, column, default)
  else
    model.attribute(column, type, default: -> { default&.deep_dup || type.blank_document })
  end

  Array.wrap(delegate).flat_map do |property|
    [property, "#{property}="]
  end.then do |delegations|
    model.delegate(*delegations, to: column) if delegations.any?
  end

  model.validates(column, nested: true, allow_blank: true)
end

.include_on(klass, method_name = nil) ⇒ Object

Provide a method on the given class to setup which columns are backed by a struct class



39
40
41
42
43
44
45
46
# File 'lib/torque/postgresql/attributes/struct.rb', line 39

def include_on(klass, method_name = nil)
  method_name ||= PostgreSQL.config.struct.base_method
  klass.define_singleton_method(method_name) do |column, struct_klass, **options|
    Struct.build_on(self, column, struct_klass, **options)
  rescue Interrupt
    # Not able to build the attribute, maybe pending migrations
  end
end

.strict?Boolean

Whether properties that are not declared by the class are rejected

Returns:

  • (Boolean)


21
22
23
24
25
26
# File 'lib/torque/postgresql/attributes/struct.rb', line 21

def strict?
  return !!@strict if defined?(@strict)
  return !!superclass.strict? if superclass.respond_to?(:strict?)

  PostgreSQL.config.struct.default_strict
end

Instance Method Details

#attribute_writer_missing(name, value) ⇒ Object

Properties that are not declared by the class are kept as they are, without any type, which is only allowed when the class is not strict



124
125
126
127
128
129
130
131
# File 'lib/torque/postgresql/attributes/struct.rb', line 124

def attribute_writer_missing(name, value)
  return super if self.class.strict?

  name = name.to_s
  attribute = @attributes[name] if @attributes.key?(name)
  attribute ||= ActiveModel::Attribute.from_database(name, nil, ActiveModel::Type.default_value)
  @attributes[name] = attribute.with_value_from_user(value)
end