Class: TsSchema::SchemaGenerator

Inherits:
Object
  • Object
show all
Defined in:
lib/ts_schema/schema_generator.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(config = nil) ⇒ SchemaGenerator

Returns a new instance of SchemaGenerator.



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# File 'lib/ts_schema/schema_generator.rb', line 9

def initialize(config = nil)
  @config = config || TsSchema::Configuration.new
  @config.field_overrides = @config.field_overrides.stringify_keys
  @config.field_type_overrides = @config.field_type_overrides.stringify_keys
  @models = []

  Rails.application.eager_load!
  @config.parent_classes.each do |parent|
    @models.concat(get_subclasses(parent.to_s.constantize))
  end
  unless @config.additional_models.empty?
    @models.concat(@config.additional_models.map do |m|
      m.to_s.constantize
    end)
  end
  @models.sort_by! { |c| c.name }

  @types = @config.types.stringify_keys.merge(@config.custom_types.stringify_keys || {})
end

Instance Attribute Details

#configObject

Returns the value of attribute config.



7
8
9
# File 'lib/ts_schema/schema_generator.rb', line 7

def config
  @config
end

#modelsObject

Returns the value of attribute models.



7
8
9
# File 'lib/ts_schema/schema_generator.rb', line 7

def models
  @models
end

Instance Method Details

#generateObject



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/ts_schema/schema_generator.rb', line 39

def generate
  type_template = ""

  @models.each do |model|
    columns = map_column_types(model)
    columns.concat(map_associations(model)) if @config.include_associated

    type_template += <<~TYPESCRIPT
      #{@config.schema_type} #{model.model_name.param_key.camelize} #{@config.schema_type.to_sym == :type ? '= ' : ''}{
      #{columns.map { |column| "#{indent_as_str}#{column_name_cased(column[:name])}: #{column[:ts_type]};" }.join("\n")}
      }\n
    TYPESCRIPT
  end

  type_template = <<~TPL
    declare namespace #{@config.namespace} {
    #{indent_wrapper(type_template)}
    }
  TPL
end

#get_subclasses(model) ⇒ Object



29
30
31
32
33
34
35
36
37
# File 'lib/ts_schema/schema_generator.rb', line 29

def get_subclasses(model)
  models = model.send(:subclasses)
  unless nested_empty?(models)
    models.concat(models.flat_map do |m|
      get_subclasses(m)
    end)
  end
  models
end

#map_associations(model) ⇒ Object



105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/ts_schema/schema_generator.rb', line 105

def map_associations(model)
  model.reflect_on_all_associations.reject(&:polymorphic?).map do |association|
    if association.has_one? || association.belongs_to?
      {
        name: "#{association.name}?",
        ts_type: association.class_name.constantize.model_name.param_key.camelize
      }
    elsif association.collection?
      {
        name: "#{association.name}?",
        ts_type: "#{association.class_name.constantize.model_name.param_key.camelize}[]"
      }
    end
  rescue StandardError => e
    print "#{model} has an incorrect association: #{association.name}\n"
    print "Association details:\n"
    print JSON.parse(association.to_json)
    print "\n\n"
    print e
  end
end

#map_column_types(model) ⇒ Object



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/ts_schema/schema_generator.rb', line 70

def map_column_types(model)
  model.columns.map { |column|
    column_name = column.name.to_s
    next if @config.field_overrides[column_name] == :omit

    type_override = config.field_type_overrides[column_name]

    type = type_override || @types[column.type.to_s] || @config.default_type
    name = map_name(column.name)
    null = column.null
    null = true if @config.field_overrides[name]&.to_s == "optional"

    if (enum = model.defined_enums[name])
      type = enum.keys.map { |k| "'#{k}'" }.join("|")
    end

    {
      name: "#{name}#{'?' if null}",
      ts_type: "#{type}#{' | null' if @config.export_nulls && null}"
    }
  }.compact
end

#map_name(name) ⇒ Object



93
94
95
96
97
98
99
100
101
102
103
# File 'lib/ts_schema/schema_generator.rb', line 93

def map_name(name)
  final_name = name.to_s
  return final_name unless @config.field_overrides[final_name]

  if @config.field_overrides[final_name]&.to_s != "optional"
    final_name = @config.field_overrides[final_name]&.to_s

    final_name = map_name(final_name) if @config.field_overrides[final_name]
  end
  final_name
end

#output_fileObject



60
61
62
63
64
65
66
67
68
# File 'lib/ts_schema/schema_generator.rb', line 60

def output_file
  path = @config.output
  FileUtils.mkdir_p(File.dirname(path))

  content = generate
  return if File.exist?(path) && File.read(path) == content

  File.write(path, content)
end