Class: Generator::API

Inherits:
Object
  • Object
show all
Includes:
FileWriter, Formatter
Defined in:
lib/generator/api.rb

Constant Summary

Constants included from Formatter

Formatter::MAX_LINE_LENGTH

Instance Attribute Summary collapse

Class Method Summary collapse

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

Methods included from FileWriter

#format_files, #write_file

Constructor Details

#initialize(file) ⇒ API

Returns a new instance of API.



53
54
55
56
# File 'lib/generator/api.rb', line 53

def initialize(file)
  @file = file
  @written_files = []
end

Instance Attribute Details

#fileObject (readonly)

Returns the value of attribute file.



24
25
26
# File 'lib/generator/api.rb', line 24

def file
  @file
end

Class Method Details

.apisObject



34
35
36
37
38
39
# File 'lib/generator/api.rb', line 34

def apis
  api_models = Dir.glob(File.join(Config::BASE_PATH, "selling-partner-api-models/models/**/*.json"))
  APINameResolver.validate_no_unmapped_collisions!(api_models)

  api_models.map { |file| new(file) }
end

.cleanup!Object



41
42
43
44
45
46
47
48
49
50
# File 'lib/generator/api.rb', line 41

def cleanup!
  ["lib", "sig"].each do |base|
    apis_path = File.join(Config::BASE_PATH, base, "peddler/apis")
    FileUtils.mkdir_p(apis_path)
    # Delete only generated API subdirectories, preserve all files (money.rb, etc.)
    Dir.glob(File.join(apis_path, "*")).select { |p| File.directory?(p) }.each do |dir|
      FileUtils.rm_rf(dir)
    end
  end
end

.generateObject



27
28
29
30
31
32
# File 'lib/generator/api.rb', line 27

def generate
  cleanup!

  # Generate each API in parallel
  Parallel.each(apis, &:generate)
end

Instance Method Details

#class_nameObject



143
144
145
# File 'lib/generator/api.rb', line 143

def class_name
  name_with_version.camelize
end

#convenience_method_nameObject



165
166
167
# File 'lib/generator/api.rb', line 165

def convenience_method_name
  name
end

#descriptionObject



119
120
121
122
123
124
125
# File 'lib/generator/api.rb', line 119

def description
  description = model["info"]["description"]
  description = convert_html_links_to_yard(description)
  description = convert_doc_links_to_full_url(description)

  split_long_comment_line(description, base_indent: 4)
end

#file_pathObject



187
188
189
# File 'lib/generator/api.rb', line 187

def file_path
  File.join(Config::BASE_PATH, "lib/#{library_name}.rb")
end

#generateObject



58
59
60
61
62
63
64
65
66
67
68
# File 'lib/generator/api.rb', line 58

def generate
  generate_api_class!
  generate_types!

  # Reload to pick up newly generated files for RBS introspection
  IntrospectionLoader.reload
  generate_rbs!
  format_files(@written_files)

  Generator.logger.info("Generated #{name_with_version}")
end

#generate_api_class!Object



70
71
72
# File 'lib/generator/api.rb', line 70

def generate_api_class!
  @written_files << write_file(file_path, render)
end

#generate_rbs!Object



95
96
97
98
# File 'lib/generator/api.rb', line 95

def generate_rbs!
  # Generate unified RBS file with both API operations and type definitions
  @written_files << RBS::Unified.new(self, name_with_version, @api_types || []).generate
end

#generate_types!Object



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/generator/api.rb', line 74

def generate_types!
  api_types = types
  return [] if api_types.empty?

  # Detect circular dependencies across all types for this API
  detector = CircularDependencyDetector.new(api_types)
  detector.detect
  api_types.each do |type|
    type.circular_dependencies = detector.circular_deps
    type.cycle_edges = detector.cycle_edges
  end

  # Generate each type Ruby file
  api_types.each do |type|
    @written_files << type.generate
  end

  # Store types for RBS generation
  @api_types = api_types
end

#github_model_filenameObject



195
196
197
198
# File 'lib/generator/api.rb', line 195

def github_model_filename
  parts = file.split("/")
  "#{parts[-2]}/#{parts[-1]}"
end

#has_helper?Boolean

Returns:

  • (Boolean)


131
132
133
# File 'lib/generator/api.rb', line 131

def has_helper?
  File.exist?("lib/" + helper_library_name + ".rb")
end

#helper_class_nameObject



135
136
137
# File 'lib/generator/api.rb', line 135

def helper_class_name
  "Peddler::Helpers::#{name_with_version.camelize}"
end

#helper_library_nameObject



139
140
141
# File 'lib/generator/api.rb', line 139

def helper_library_name
  "peddler/helpers/#{name_with_version}"
end

#latest_version?Boolean

Returns:

  • (Boolean)


159
160
161
162
163
# File 'lib/generator/api.rb', line 159

def latest_version?
  all_apis = self.class.apis.select { |api| api.name == name }
  latest_version = VersionSelector.find_latest_version(all_apis.map(&:version))
  version == latest_version
end

#library_nameObject



127
128
129
# File 'lib/generator/api.rb', line 127

def library_name
  "peddler/apis/#{name_with_version}"
end

#nameObject



147
148
149
# File 'lib/generator/api.rb', line 147

def name
  @name ||= APINameResolver.new(file).name
end

#name_with_versionObject



155
156
157
# File 'lib/generator/api.rb', line 155

def name_with_version
  [name, version].join("_")
end

#openapi_specObject



191
192
193
# File 'lib/generator/api.rb', line 191

def openapi_spec
  model
end

#operationsObject



169
170
171
172
173
174
# File 'lib/generator/api.rb', line 169

def operations
  @operations ||= begin
    ops = paths.flat_map { |path| path.operations(name_with_version) }.compact
    deduplicate_operations(ops)
  end
end

#pathsObject



183
184
185
# File 'lib/generator/api.rb', line 183

def paths
  model["paths"].map { |path, methods| Path.new(path, methods) }
end

#titleObject



115
116
117
# File 'lib/generator/api.rb', line 115

def title
  split_long_comment_line(model["info"]["title"], base_indent: 4)
end

#type_namesObject



176
177
178
179
180
181
# File 'lib/generator/api.rb', line 176

def type_names
  openapi_spec["definitions"]
    .select { |name, def_| def_["type"] == "object" && !TypeResolver::MONEY_TYPES.include?(name) && !def_["additionalProperties"] }
    .keys
    .sort
end

#typesObject



100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/generator/api.rb', line 100

def types
  arr = []
  openapi_spec["definitions"].each do |name, definition|
    # Skip non-object definitions but allow allOf compositions and arrays
    next unless definition["type"] == "object" || definition["allOf"] || definition["type"] == "array"
    # Skip Money types as we use the custom Money type for these
    next if TypeResolver.money?(name)
    # Skip types with ONLY additionalProperties (no defined properties) - they'll be referenced as Hash
    next if definition["additionalProperties"] && !definition["properties"] && !definition["allOf"]

    arr << Type.new(name, definition, name_with_version, openapi_spec)
  end
  arr
end

#versionObject



151
152
153
# File 'lib/generator/api.rb', line 151

def version
  model["info"]["version"].tr("-", "_")
end