Module: Generator::SchemaHelpers

Includes:
Formatter
Included in:
DataKiosk, Feed, Notification, Report, Type
Defined in:
lib/generator/support/schema_helpers.rb

Overview

Shared helper methods for schema-based generators (Notification, Report, Feed, Type)

Constant Summary

Constants included from Formatter

Formatter::MAX_LINE_LENGTH

Instance Method Summary collapse

Methods included from Formatter

#convert_doc_links_to_full_url, #convert_html_links_to_yard, #format_method_definition, #split_long_comment_line

Instance Method Details

#api_name_for_type_resolverObject

Hook for subclasses to provide the api_name for type resolver Should be overridden in Notification/Report/Feed

Raises:

  • (NotImplementedError)


96
97
98
# File 'lib/generator/support/schema_helpers.rb', line 96

def api_name_for_type_resolver
  raise NotImplementedError, "Subclass must implement api_name_for_type_resolver"
end

#attribute_name_for(prop_name, prop_def) ⇒ Object

Generate attribute name (underscore the property name) For boolean attributes, strip is_ prefix for more idiomatic Ruby



16
17
18
# File 'lib/generator/support/schema_helpers.rb', line 16

def attribute_name_for(prop_name, prop_def)
  Naming.attribute_name(prop_name, prop_def)
end

#format_property_comment(prop_def, base_indent: 0) ⇒ Object

Format property comment with description base_indent can be overridden by subclasses



64
65
66
67
68
69
70
71
72
73
74
# File 'lib/generator/support/schema_helpers.rb', line 64

def format_property_comment(prop_def, base_indent: 0)
  return_type = ruby_type_for(prop_def, for_comment: true)
  if prop_def["description"] && !prop_def["description"].empty? && !generic_placeholder?(prop_def["description"])
    description = convert_html_links_to_yard(prop_def["description"])
    description = convert_doc_links_to_full_url(description)
    split_long_comment_line("@return [#{return_type}] #{description}", base_indent: base_indent, wrap_indent: 2)
  else
    indent = " " * base_indent
    "#{indent}# @return [#{return_type}]"
  end
end

#generate_nested_types!Object

Generate nested types using the Type generator Returns hash with :files (array of file paths) and :types (array of Type instances)



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/generator/support/schema_helpers.rb', line 102

def generate_nested_types!
  extractor = JsonSchemaExtractor.new(schema, send(:name))
  extracted_types = extractor.extract_types

  # Convert to OpenAPI-style specification for Type generator
  openapi_spec = {
    "definitions" => extracted_types,
  }

  # Generate each nested type and collect written files and Type instances
  written_files = []
  type_instances = []

  extracted_types.each do |type_name, type_def|
    api_name = api_name_for_type_resolver
    type = Type.new(type_name, type_def, api_name, openapi_spec)
    written_files << type.generate
    type_instances << type
  end

  { files: written_files, types: type_instances }
end

#generate_rbs!(all_types) ⇒ Object

Generate consolidated RBS signature for all types



126
127
128
129
# File 'lib/generator/support/schema_helpers.rb', line 126

def generate_rbs!(all_types)
  api_name = api_name_for_type_resolver
  Generator::RBS::Types.new(api_name, all_types).generate
end

#generic_placeholder?(description) ⇒ Boolean

Check if description is a generic placeholder from Amazon's schema generator

Returns:

  • (Boolean)


21
22
23
24
25
# File 'lib/generator/support/schema_helpers.rb', line 21

def generic_placeholder?(description)
  return false unless description

  description.match?(/^An explanation about the purpose of this instance\.?$/i)
end

#needs_money?(extracted_types = nil) ⇒ Boolean

Check if any types use Money

Returns:

  • (Boolean)


28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/generator/support/schema_helpers.rb', line 28

def needs_money?(extracted_types = nil)
  # If extracted_types provided, check them directly
  if extracted_types
    return extracted_types.any? do |_type_name, type_def|
      type_def["properties"]&.any? do |_prop_name, prop_def|
        prop_def["$ref"] == "#/definitions/Money" ||
            (prop_def["items"].is_a?(Hash) && prop_def["items"]["$ref"] == "#/definitions/Money")
      end
    end
  end

  # Otherwise check properties (for Type class)
  return false unless respond_to?(:properties)

  properties.any? do |_prop_name, prop_def|
    if prop_def["$ref"]
      type_name = prop_def["$ref"].split("/").last
      MoneyDetector.money_type?(type_name)
    else
      false
    end
  end
end

#nested_type_filesObject

Get list of all nested type file names for requiring This method requires schema and a name method to be available



54
55
56
57
58
59
60
# File 'lib/generator/support/schema_helpers.rb', line 54

def nested_type_files
  extractor = JsonSchemaExtractor.new(schema, send(:name))
  extracted_types = extractor.extract_types

  # Return list of underscored type names for requiring
  extracted_types.keys.map(&:underscore).sort
end

#ruby_type_for(prop_def, for_comment: false, for_rbs: false, prop_name: nil) ⇒ Object

Convert JSON Schema type to Ruby type This method requires type_resolver to be available



78
79
80
# File 'lib/generator/support/schema_helpers.rb', line 78

def ruby_type_for(prop_def, for_comment: false, for_rbs: false, prop_name: nil)
  type_resolver.resolve(prop_def, for_comment: for_comment, for_rbs: for_rbs, prop_name: prop_name)
end

#type_resolverObject

Build type resolver - this should be overridden by subclasses if needed Default implementation for Notification/Report/Feed



84
85
86
87
88
89
90
91
92
# File 'lib/generator/support/schema_helpers.rb', line 84

def type_resolver
  @type_resolver ||= begin
    # Build specification with extracted types for resolver
    extractor = JsonSchemaExtractor.new(schema, send(:name))
    extracted_types = extractor.extract_types
    spec = { "definitions" => extracted_types }
    TypeResolver.new(send(:name), spec, send(:api_name_for_type_resolver))
  end
end