Module: Generator::SchemaHelpers

Includes:
Formatter
Included in:
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)


99
100
101
# File 'lib/generator/support/schema_helpers.rb', line 99

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



14
15
16
17
18
19
20
21
# File 'lib/generator/support/schema_helpers.rb', line 14

def attribute_name_for(prop_name, prop_def)
  underscored = prop_name.underscore
  if prop_def && (prop_def["type"] == "boolean" || prop_def["type"] == "bool")
    underscored.sub(/^is_/, "")
  else
    underscored
  end
end

#format_property_comment(prop_def, base_indent: 0) ⇒ Object

Format property comment with description base_indent can be overridden by subclasses



67
68
69
70
71
72
73
74
75
76
77
# File 'lib/generator/support/schema_helpers.rb', line 67

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)
  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)



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

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



129
130
131
132
# File 'lib/generator/support/schema_helpers.rb', line 129

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)


24
25
26
27
28
# File 'lib/generator/support/schema_helpers.rb', line 24

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)


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

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
      TypeResolver::MONEY_TYPES.include?(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



57
58
59
60
61
62
63
# File 'lib/generator/support/schema_helpers.rb', line 57

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



81
82
83
# File 'lib/generator/support/schema_helpers.rb', line 81

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



87
88
89
90
91
92
93
94
95
# File 'lib/generator/support/schema_helpers.rb', line 87

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