Class: Odin::Validation::SchemaSerializer

Inherits:
Object
  • Object
show all
Defined in:
lib/odin/validation/schema_serializer.rb

Instance Method Summary collapse

Instance Method Details

#serialize(schema) ⇒ Object

Serialize an OdinSchema back to ODIN text



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
42
43
44
45
46
47
48
49
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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/odin/validation/schema_serializer.rb', line 9

def serialize(schema)
  lines = []

  # 1. Metadata header
  unless schema..empty?
    lines << "{$}"
    schema..each do |key, value|
      lines << "#{key} = \"#{escape_string(value.to_s)}\""
    end
    lines << ""
  end

  # 2. Import directives
  schema.imports.each do |imp|
    if imp.alias_name
      lines << "@import \"#{imp.path}\" as #{imp.alias_name}"
    else
      lines << "@import \"#{imp.path}\""
    end
  end
  lines << "" unless schema.imports.empty?

  # 3. Type definitions
  schema.types.each do |type_name, schema_type|
    lines << "{@#{type_name}}"

    # Composition
    if schema_type.composition
      lines << "= #{schema_type.composition}"
    end

    schema_type.fields.each do |field_name, field|
      lines << serialize_field(field_name, field)
    end

    # Object constraints for this type
    if schema.object_constraints[type_name]
      schema.object_constraints[type_name].each do |constraint|
        lines << serialize_object_constraint(constraint)
      end
    end

    lines << ""
  end

  # 4. Root fields
  unless schema.fields.empty?
    # Group by section
    root_fields = {}
    sectioned_fields = Hash.new { |h, k| h[k] = {} }

    schema.fields.each do |path, field|
      parts = path.split(".", 2)
      if parts.length > 1
        sectioned_fields[parts[0]][parts[1]] = field
      else
        root_fields[path] = field
      end
    end

    root_fields.each do |name, field|
      lines << serialize_field(name, field)
    end
    lines << "" unless root_fields.empty?

    sectioned_fields.each do |section, fields|
      lines << "{#{section}}"
      fields.each do |name, field|
        lines << serialize_field(name, field)
      end

      if schema.object_constraints[section]
        schema.object_constraints[section].each do |constraint|
          lines << serialize_object_constraint(constraint)
        end
      end

      lines << ""
    end
  end

  # 5. Array definitions
  schema.arrays.each do |array_path, schema_array|
    header = "{#{array_path}[]"
    if schema_array.columns && !schema_array.columns.empty?
      header += " : #{schema_array.columns.join(', ')}"
    end
    header += "}"
    lines << header

    # Array-level constraints
    bounds_parts = []
    if schema_array.min_items || schema_array.max_items
      min_str = schema_array.min_items&.to_s || ""
      max_str = schema_array.max_items&.to_s || ""
      bounds_parts << ":(#{min_str}..#{max_str})"
    end
    bounds_parts << ":unique" if schema_array.unique
    lines << bounds_parts.join("") unless bounds_parts.empty?

    schema_array.item_fields.each do |field_name, field|
      lines << serialize_field(field_name, field)
    end

    if schema.object_constraints[array_path]
      schema.object_constraints[array_path].each do |constraint|
        lines << serialize_object_constraint(constraint)
      end
    end

    lines << ""
  end

  # 6. Orphan object constraints (not already output with types/fields/arrays)
  outputted_scopes = Set.new
  schema.types.each_key { |k| outputted_scopes.add(k) }
  schema.arrays.each_key { |k| outputted_scopes.add(k) }
  # Sectioned fields
  schema.fields.each_key do |path|
    parts = path.split(".", 2)
    outputted_scopes.add(parts[0]) if parts.length > 1
  end
  outputted_scopes.add("") # root constraints handled inline

  schema.object_constraints.each do |scope, constraints|
    next if outputted_scopes.include?(scope)
    lines << "{#{scope}}" unless scope.empty?
    constraints.each do |constraint|
      lines << serialize_object_constraint(constraint)
    end
    lines << ""
  end

  lines.join("\n").rstrip + "\n"
end