Module: Tama::Model

Defined in:
lib/tama/model.rb

Class Method Summary collapse

Class Method Details

.blank?(value) ⇒ Boolean

Returns:

  • (Boolean)


69
70
71
# File 'lib/tama/model.rb', line 69

def blank?(value)
  value.nil? || (value.is_a?(String) && value.empty?)
end

.define(*fields, required: [], types: {}, defaults: {}, transforms: {}) ⇒ Object



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/tama/model.rb', line 9

def define(*fields, required: [], types: {}, defaults: {}, transforms: {})
  Class.new(Data.define(*fields)) do
    define_singleton_method(:fields) { fields }

    define_method(:initialize) do |**attributes|
      unknown = attributes.keys - fields
      unless unknown.empty?
        raise Tama::Error::ValidationError.new("Unknown attributes: #{unknown.join(", ")}", errors: {attributes: unknown})
      end

      values = fields.to_h { |field| [field, attributes.fetch(field, defaults.fetch(field, nil))] }
      Tama::Model.validate!(values, required:, types:, name: self.class.name)
      super(**values)
    end

    define_singleton_method(:parse) do |input|
      data = Tama::Model.hash(input)
      values = fields.to_h do |field|
        value = data.key?(field.to_s) ? data[field.to_s] : data[field]
        value = transforms.fetch(field).call(value) if !value.nil? && transforms.key?(field)
        [field, value.nil? ? defaults.fetch(field, nil) : value]
      end

      new(**values)
    rescue Tama::Error::ParseError
      raise
    rescue Tama::Error => e
      raise Tama::Error::ParseError, "Invalid #{name || "model"}: #{e.message}"
    rescue => e
      raise Tama::Error::ParseError, "Invalid #{name || "model"}: #{e.message}"
    end
  end
end

.hash(input) ⇒ Object



43
44
45
46
47
48
49
50
# File 'lib/tama/model.rb', line 43

def hash(input)
  input = JSON.parse(input) if input.is_a?(String)
  raise Tama::Error::ParseError, "Expected a JSON object" unless input.is_a?(Hash)

  input
rescue JSON::ParserError => e
  raise Tama::Error::ParseError, "Malformed JSON: #{e.message}"
end

.type_name(expected) ⇒ Object



77
78
79
# File 'lib/tama/model.rb', line 77

def type_name(expected)
  Array(expected).join(" or ")
end

.valid_type?(value, expected) ⇒ Boolean

Returns:

  • (Boolean)


73
74
75
# File 'lib/tama/model.rb', line 73

def valid_type?(value, expected)
  Array(expected).any? { |type| value.is_a?(type) }
end

.validate!(values, required:, types:, name:) ⇒ Object



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/tama/model.rb', line 52

def validate!(values, required:, types:, name:)
  missing = required.select { |field| blank?(values[field]) }
  unless missing.empty?
    raise Tama::Error::ValidationError.new("Missing required fields: #{missing.join(", ")}", errors: {required: missing})
  end

  types.each do |field, expected|
    value = values[field]
    next if value.nil? || valid_type?(value, expected)

    raise Tama::Error::ValidationError.new(
      "#{field} has an invalid type for #{name || "model"}",
      errors: {field => "must be #{type_name(expected)}"}
    )
  end
end