Module: RailsContractSync::SchemaInferrer

Defined in:
lib/rails_contract_sync/schema_inferrer.rb

Class Method Summary collapse

Class Method Details

.infer(value) ⇒ Object



5
6
7
8
9
10
11
12
13
14
15
16
# File 'lib/rails_contract_sync/schema_inferrer.rb', line 5

def infer(value)
  case value
  when nil then { "type" => "null" }
  when true, false then { "type" => "boolean" }
  when Integer then { "type" => "integer" }
  when Float then { "type" => "number" }
  when String then { "type" => "string" }
  when Array then infer_array(value)
  when Hash then infer_object(value)
  else { "type" => "string" }
  end
end

.infer_all(values) ⇒ Object



28
29
30
# File 'lib/rails_contract_sync/schema_inferrer.rb', line 28

def infer_all(values)
  values.map { |v| infer(v) }.reduce(nil) { |acc, s| acc ? merge(acc, s) : s } || {}
end

.infer_array(array) ⇒ Object



18
19
20
# File 'lib/rails_contract_sync/schema_inferrer.rb', line 18

def infer_array(array)
  { "type" => "array", "items" => infer_all(array) }
end

.infer_object(hash) ⇒ Object



22
23
24
25
26
# File 'lib/rails_contract_sync/schema_inferrer.rb', line 22

def infer_object(hash)
  props = {}
  hash.each { |k, v| props[k.to_s] = infer(v) }
  { "type" => "object", "properties" => props, "required" => hash.keys.map(&:to_s).sort }
end

.merge(a, b) ⇒ Object



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/rails_contract_sync/schema_inferrer.rb', line 32

def merge(a, b)
  a ||= {}
  b ||= {}
  return b if a.empty?
  return a if b.empty?

  types = (Array(a["type"]) | Array(b["type"])).sort
  # Widen integer + number to just number
  if types == ["integer", "number"]
    types = ["number"]
  end
  result = { "type" => types.length == 1 ? types.first : types }
  result.merge!(merge_object(a, b)) if types.include?("object")
  result["items"] = merge(a["items"] || {}, b["items"] || {}) if types.include?("array")
  result
end

.merge_object(a, b) ⇒ Object



49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/rails_contract_sync/schema_inferrer.rb', line 49

def merge_object(a, b)
  props_a = a["properties"] || {}
  props_b = b["properties"] || {}
  merged = {}
  (props_a.keys | props_b.keys).each do |k|
    merged[k] = if props_a[k] && props_b[k]
      merge(props_a[k], props_b[k])
    else
      props_a[k] || props_b[k]
    end
  end
  required = ((a["required"] || []) & (b["required"] || [])).sort
  { "properties" => merged, "required" => required }
end