rails-openapi-gen 0.0.2 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +40 -0
- data/CLAUDE.md +17 -5
- data/README.md +25 -0
- data/lib/rails-openapi-gen/ast_nodes/array_node.rb +101 -0
- data/lib/rails-openapi-gen/ast_nodes/base_node.rb +139 -0
- data/lib/rails-openapi-gen/ast_nodes/comment_data.rb +180 -0
- data/lib/rails-openapi-gen/ast_nodes/node_factory.rb +206 -0
- data/lib/rails-openapi-gen/ast_nodes/object_node.rb +129 -0
- data/lib/rails-openapi-gen/ast_nodes/partial_node.rb +111 -0
- data/lib/rails-openapi-gen/ast_nodes/property_node.rb +74 -0
- data/lib/rails-openapi-gen/ast_nodes.rb +129 -0
- data/lib/rails-openapi-gen/configuration.rb +154 -22
- data/lib/rails-openapi-gen/debug_helpers.rb +185 -0
- data/lib/rails-openapi-gen/engine.rb +1 -1
- data/lib/rails-openapi-gen/generators/yaml_generator.rb +242 -27
- data/lib/rails-openapi-gen/generators.rb +5 -0
- data/lib/rails-openapi-gen/importer.rb +164 -145
- data/lib/rails-openapi-gen/parsers/comment_parser.rb +1 -1
- data/lib/rails-openapi-gen/parsers/comment_parsers/attribute_parser.rb +7 -7
- data/lib/rails-openapi-gen/parsers/comment_parsers/base_attribute_parser.rb +5 -9
- data/lib/rails-openapi-gen/parsers/comment_parsers/body_parser.rb +6 -6
- data/lib/rails-openapi-gen/parsers/comment_parsers/conditional_parser.rb +1 -1
- data/lib/rails-openapi-gen/parsers/comment_parsers/operation_parser.rb +5 -5
- data/lib/rails-openapi-gen/parsers/comment_parsers/param_parser.rb +6 -6
- data/lib/rails-openapi-gen/parsers/comment_parsers/query_parser.rb +6 -6
- data/lib/rails-openapi-gen/parsers/controller_parser.rb +64 -20
- data/lib/rails-openapi-gen/parsers/jbuilder/ast_parser.rb +914 -0
- data/lib/rails-openapi-gen/parsers/jbuilder/call_detectors/array_call_detector.rb +103 -0
- data/lib/rails-openapi-gen/parsers/jbuilder/call_detectors/base_detector.rb +107 -0
- data/lib/rails-openapi-gen/parsers/jbuilder/call_detectors/cache_call_detector.rb +112 -0
- data/lib/rails-openapi-gen/parsers/jbuilder/call_detectors/json_call_detector.rb +91 -0
- data/lib/rails-openapi-gen/parsers/jbuilder/call_detectors/key_format_detector.rb +27 -0
- data/lib/rails-openapi-gen/parsers/jbuilder/call_detectors/null_handling_detector.rb +27 -0
- data/lib/rails-openapi-gen/parsers/jbuilder/call_detectors/object_manipulation_detector.rb +27 -0
- data/lib/rails-openapi-gen/parsers/jbuilder/call_detectors/partial_call_detector.rb +125 -0
- data/lib/rails-openapi-gen/parsers/jbuilder/call_detectors.rb +95 -0
- data/lib/rails-openapi-gen/parsers/jbuilder/jbuilder_parser.rb +39 -0
- data/lib/rails-openapi-gen/parsers/jbuilder/operation_comment_parser.rb +26 -0
- data/lib/rails-openapi-gen/parsers/jbuilder/processors/array_processor.rb +266 -0
- data/lib/rails-openapi-gen/parsers/jbuilder/processors/base_processor.rb +235 -0
- data/lib/rails-openapi-gen/parsers/jbuilder/processors/composite_processor.rb +97 -0
- data/lib/rails-openapi-gen/parsers/jbuilder/processors/object_processor.rb +176 -0
- data/lib/rails-openapi-gen/parsers/jbuilder/processors/partial_processor.rb +69 -0
- data/lib/rails-openapi-gen/parsers/jbuilder/processors/property_processor.rb +68 -0
- data/lib/rails-openapi-gen/parsers/jbuilder/processors.rb +10 -0
- data/lib/rails-openapi-gen/parsers/jbuilder/property_comment_parser.rb +26 -0
- data/lib/rails-openapi-gen/parsers/jbuilder.rb +10 -0
- data/lib/rails-openapi-gen/parsers/routes_parser.rb +83 -9
- data/lib/rails-openapi-gen/parsers/template_processors/jbuilder_template_processor.rb +125 -131
- data/lib/rails-openapi-gen/parsers/template_processors/response_template_processor.rb +8 -12
- data/lib/rails-openapi-gen/parsers/template_processors.rb +6 -0
- data/lib/rails-openapi-gen/parsers.rb +9 -0
- data/lib/rails-openapi-gen/processors/ast_to_schema_processor.rb +226 -0
- data/lib/rails-openapi-gen/processors/base_processor.rb +124 -0
- data/lib/rails-openapi-gen/processors/component_schema_processor.rb +35 -0
- data/lib/rails-openapi-gen/processors/openapi_schema_processor.rb +218 -0
- data/lib/rails-openapi-gen/processors.rb +7 -0
- data/lib/rails-openapi-gen/railtie.rb +1 -1
- data/lib/rails-openapi-gen/tasks/openapi.rake +4 -4
- data/lib/rails-openapi-gen/version.rb +1 -1
- data/lib/rails-openapi-gen.rb +169 -196
- data/lib/tasks/openapi_import.rake +35 -36
- data/rails-openapi-gen.gemspec +6 -5
- metadata +62 -23
- data/lib/rails-openapi-gen/parsers/jbuilder_parser.rb +0 -529
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0d59044c5ff98fab8b4ca1e9de7dd53e71eea91ffa2d5a8d37f0acb9f335f059
|
4
|
+
data.tar.gz: cc88029ac40811d0181ca65892ea8c09dd43134dd08a2d668bcd0285e2003f3d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2578b9fd575a89a3432ea01f64e86e8f4e6922343f606c8f246a092f5a7a6dfe8da98b47717159238982f0140377ebdfdf0dc90bac879ba2b14727782e5ca297
|
7
|
+
data.tar.gz: 0cc4b985a493cf04edb7d1cb689e24d244ac33572d457f7b0c8fea49d8562a2c778ee13c5972fe14ddecd09ba026ee943edcc2c5924c6585717ce4952b69eff7
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
All notable changes to this project will be documented in this file.
|
4
|
+
|
5
|
+
## [0.0.3] - 2025-01-13
|
6
|
+
|
7
|
+
### Added
|
8
|
+
- Ruby 3.1 compatibility with intelligent fallback parsing system
|
9
|
+
- Robust error handling for Parser gem version mismatches
|
10
|
+
- Support for root array structures in Jbuilder templates
|
11
|
+
- Component reference system for partials
|
12
|
+
- Enhanced debug output with RAILS_OPENAPI_DEBUG environment variable
|
13
|
+
|
14
|
+
### Fixed
|
15
|
+
- Parser gem compatibility issues between Ruby 3.1.3 and 3.1.7
|
16
|
+
- RuboCop compliance with all Lint rules enabled
|
17
|
+
- Test failures related to boolean symbols and unused variables
|
18
|
+
- Proper handling of nested partials and array structures
|
19
|
+
- Component naming conflicts in test environments
|
20
|
+
|
21
|
+
### Changed
|
22
|
+
- Parser gem dependency updated to ~> 3.1.0 for better compatibility
|
23
|
+
- Improved AST parsing with multiple fallback strategies
|
24
|
+
- Enhanced error messages and debugging capabilities
|
25
|
+
- Removed project-specific naming patterns from tests
|
26
|
+
|
27
|
+
## [0.0.2] - Previous version
|
28
|
+
|
29
|
+
### Initial features
|
30
|
+
- Basic OpenAPI generation from Rails routes
|
31
|
+
- Jbuilder template parsing with @openapi comments
|
32
|
+
- Controller action detection
|
33
|
+
- YAML output generation
|
34
|
+
|
35
|
+
## [0.0.1] - Initial release
|
36
|
+
|
37
|
+
### Features
|
38
|
+
- Initial implementation of rails-openapi-gen
|
39
|
+
- Basic route parsing
|
40
|
+
- Simple Jbuilder template support
|
data/CLAUDE.md
CHANGED
@@ -6,12 +6,18 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
|
6
6
|
|
7
7
|
Rails OpenAPI Gen is a Ruby gem that automatically generates OpenAPI 3.0 specifications from Rails applications. It uses AST parsing to analyze routes, controllers, and Jbuilder templates, relying on `# @openapi` comments as the source of truth for type information.
|
8
8
|
|
9
|
+
**Requirements:**
|
10
|
+
- Ruby 3.0 or higher
|
11
|
+
- Rails 6.0 or higher
|
12
|
+
- Parser gem 3.0+ for AST processing
|
13
|
+
|
9
14
|
## Development Commands
|
10
15
|
|
11
16
|
**Testing:**
|
12
17
|
```bash
|
13
|
-
rspec
|
14
|
-
rspec spec/specific_spec.rb # Run specific test file
|
18
|
+
bundle exec rspec # Run all tests (required for parser gem dependencies)
|
19
|
+
bundle exec rspec spec/specific_spec.rb # Run specific test file
|
20
|
+
rspec # Alternative (may have parser gem loading issues)
|
15
21
|
```
|
16
22
|
|
17
23
|
**Linting:**
|
@@ -29,7 +35,7 @@ bin/rails openapi:check # Check for missing comments and uncommitted changes
|
|
29
35
|
|
30
36
|
**Gem Development:**
|
31
37
|
```bash
|
32
|
-
bundle install # Install dependencies
|
38
|
+
bundle install # Install dependencies (requires Ruby 3.0+)
|
33
39
|
rake -T # List available rake tasks
|
34
40
|
```
|
35
41
|
|
@@ -66,19 +72,25 @@ rake -T # List available rake tasks
|
|
66
72
|
The gem uses `# @openapi` comments in Jbuilder templates:
|
67
73
|
|
68
74
|
```ruby
|
69
|
-
# @openapi id:integer
|
75
|
+
# @openapi id:integer description:"User ID"
|
70
76
|
json.id @user.id
|
71
77
|
|
72
78
|
# @openapi status:string enum:[active,inactive] description:"User status"
|
73
79
|
json.status @user.status
|
80
|
+
|
81
|
+
# @openapi email:string required:false description:"Optional email"
|
82
|
+
json.email @user.email
|
74
83
|
```
|
75
84
|
|
76
85
|
**Supported attributes:**
|
77
86
|
- `type`: Data type (string, integer, boolean, number, array, object)
|
78
|
-
- `required`: Whether field is required (
|
87
|
+
- `required`: Whether field is required (false to make optional; defaults to required)
|
79
88
|
- `enum`: Allowed values for the field
|
80
89
|
- `description`: Human-readable description
|
81
90
|
|
91
|
+
**Required Field Behavior:**
|
92
|
+
Properties are **required by default** unless explicitly marked with `required:false`. This ensures comprehensive API documentation where all fields are documented as required unless specifically marked as optional.
|
93
|
+
|
82
94
|
### Conditional Rendering Support
|
83
95
|
|
84
96
|
For properties that are conditionally rendered (e.g., inside `if` statements), use the `# @openapi conditional:true` comment:
|
data/README.md
CHANGED
@@ -2,6 +2,31 @@
|
|
2
2
|
|
3
3
|
Rails comment-driven OpenAPI specification generator.
|
4
4
|
|
5
|
+
## Requirements
|
6
|
+
|
7
|
+
- **Ruby 3.0 or higher**
|
8
|
+
- **Rails 6.0 or higher**
|
9
|
+
|
10
|
+
## Installation
|
11
|
+
|
12
|
+
Add this line to your application's Gemfile:
|
13
|
+
|
14
|
+
```ruby
|
15
|
+
gem 'rails-openapi-gen'
|
16
|
+
```
|
17
|
+
|
18
|
+
And then execute:
|
19
|
+
|
20
|
+
```bash
|
21
|
+
$ bundle install
|
22
|
+
```
|
23
|
+
|
24
|
+
Or install it yourself as:
|
25
|
+
|
26
|
+
```bash
|
27
|
+
$ gem install rails-openapi-gen
|
28
|
+
```
|
29
|
+
|
5
30
|
## Overview
|
6
31
|
|
7
32
|
rails-openapi-gen analyzes your Rails application's routes.rb, controllers, and jbuilder templates to automatically generate OpenAPI documentation. It uses AST parsing to extract JSON structure and relies on `# @openapi` comments for accurate type information.
|
@@ -0,0 +1,101 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsOpenapiGen::AstNodes
|
4
|
+
# Represents an array node in Jbuilder template (json.array! or json.property @collection)
|
5
|
+
class ArrayNode < BaseNode
|
6
|
+
attr_reader :property_name, :comment_data, :is_conditional, :is_root_array
|
7
|
+
|
8
|
+
def initialize(property_name: nil, comment_data: nil, is_conditional: false, is_root_array: false, parent: nil, metadata: {})
|
9
|
+
super(parent: parent, metadata: metadata)
|
10
|
+
@property_name = property_name || (is_root_array ? 'items' : nil)
|
11
|
+
@comment_data = comment_data || CommentData.new(type: 'array')
|
12
|
+
@is_conditional = is_conditional
|
13
|
+
@is_root_array = is_root_array
|
14
|
+
end
|
15
|
+
|
16
|
+
# Add an item property to this array
|
17
|
+
# @param item_node [BaseNode] Item node to add
|
18
|
+
# @return [BaseNode] The added item node
|
19
|
+
def add_item(item_node)
|
20
|
+
add_child(item_node)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Get all item properties in this array
|
24
|
+
# @return [Array<BaseNode>] Array item nodes
|
25
|
+
def items
|
26
|
+
@children
|
27
|
+
end
|
28
|
+
|
29
|
+
# Check if this array is required in OpenAPI schema
|
30
|
+
# @return [Boolean] True if array is required
|
31
|
+
def required?
|
32
|
+
@comment_data.required? && !@is_conditional
|
33
|
+
end
|
34
|
+
|
35
|
+
# Check if this array is optional in OpenAPI schema
|
36
|
+
# @return [Boolean] True if array is optional
|
37
|
+
def optional?
|
38
|
+
!required?
|
39
|
+
end
|
40
|
+
|
41
|
+
# Get the OpenAPI type for array items
|
42
|
+
# @return [String] OpenAPI type for items
|
43
|
+
def item_type
|
44
|
+
return 'object' if items.any?
|
45
|
+
|
46
|
+
# Check comment data for item type specification
|
47
|
+
if @comment_data.items
|
48
|
+
case @comment_data.items
|
49
|
+
when Hash
|
50
|
+
@comment_data.items[:type] || 'object'
|
51
|
+
when String
|
52
|
+
@comment_data.items
|
53
|
+
else
|
54
|
+
'object'
|
55
|
+
end
|
56
|
+
else
|
57
|
+
'object'
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Get the description for this array
|
62
|
+
# @return [String, nil] Array description
|
63
|
+
def description
|
64
|
+
@comment_data.description
|
65
|
+
end
|
66
|
+
|
67
|
+
# Check if this is a root array (json.array! at template root)
|
68
|
+
# @return [Boolean] True if this is a root array
|
69
|
+
def root_array?
|
70
|
+
@is_root_array
|
71
|
+
end
|
72
|
+
|
73
|
+
# Convert to hash representation
|
74
|
+
# @return [Hash] Hash representation
|
75
|
+
def to_h
|
76
|
+
items_hash = items.map { |item| item.respond_to?(:to_h) ? item.to_h : item }
|
77
|
+
super.merge(
|
78
|
+
property_name: @property_name,
|
79
|
+
comment_data: @comment_data&.to_h,
|
80
|
+
is_conditional: @is_conditional,
|
81
|
+
is_root_array: @is_root_array,
|
82
|
+
required: required?,
|
83
|
+
openapi_type: 'array',
|
84
|
+
item_type: item_type,
|
85
|
+
description: description,
|
86
|
+
items: items_hash,
|
87
|
+
# Backward compatibility - also provide array_item_properties for Generator
|
88
|
+
array_item_properties: items_hash,
|
89
|
+
# Backward compatibility - also provide is_array_root for Generator
|
90
|
+
is_array_root: @is_root_array
|
91
|
+
).compact
|
92
|
+
end
|
93
|
+
|
94
|
+
# Accept visitor for visitor pattern
|
95
|
+
# @param visitor [Object] Visitor object
|
96
|
+
# @return [Object] Result from visitor
|
97
|
+
def accept(visitor)
|
98
|
+
visitor.visit_array(self)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,139 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsOpenapiGen
|
4
|
+
module AstNodes
|
5
|
+
# Base class for all AST nodes in the Jbuilder parser
|
6
|
+
# Provides common interface and basic functionality for tree structure
|
7
|
+
class BaseNode
|
8
|
+
attr_accessor :parent
|
9
|
+
attr_reader :children, :metadata
|
10
|
+
|
11
|
+
def initialize(parent: nil, metadata: {})
|
12
|
+
@parent = parent
|
13
|
+
@children = []
|
14
|
+
@metadata = metadata
|
15
|
+
end
|
16
|
+
|
17
|
+
# Add a child node to this node
|
18
|
+
# @param child [BaseNode] Child node to add
|
19
|
+
# @return [BaseNode] The added child node
|
20
|
+
def add_child(child)
|
21
|
+
child.parent = self if child.respond_to?(:parent=)
|
22
|
+
@children << child
|
23
|
+
child
|
24
|
+
end
|
25
|
+
|
26
|
+
# Remove a child node from this node
|
27
|
+
# @param child [BaseNode] Child node to remove
|
28
|
+
# @return [BaseNode, nil] The removed child node or nil
|
29
|
+
def remove_child(child)
|
30
|
+
@children.delete(child)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Get all descendants (children and their children recursively)
|
34
|
+
# @return [Array<BaseNode>] All descendant nodes
|
35
|
+
def descendants
|
36
|
+
result = []
|
37
|
+
@children.each do |child|
|
38
|
+
result << child
|
39
|
+
result.concat(child.descendants) if child.respond_to?(:descendants)
|
40
|
+
end
|
41
|
+
result
|
42
|
+
end
|
43
|
+
|
44
|
+
# Check if this node is a leaf (has no children)
|
45
|
+
# @return [Boolean] True if this node has no children
|
46
|
+
def leaf?
|
47
|
+
@children.empty?
|
48
|
+
end
|
49
|
+
|
50
|
+
# Check if this node is the root (has no parent)
|
51
|
+
# @return [Boolean] True if this node has no parent
|
52
|
+
def root?
|
53
|
+
@parent.nil?
|
54
|
+
end
|
55
|
+
|
56
|
+
# Get the root node of the tree
|
57
|
+
# @return [BaseNode] The root node
|
58
|
+
def root
|
59
|
+
return self if root?
|
60
|
+
|
61
|
+
@parent.root
|
62
|
+
end
|
63
|
+
|
64
|
+
# Convert node to hash representation
|
65
|
+
# @return [Hash] Hash representation of the node
|
66
|
+
def to_h
|
67
|
+
{
|
68
|
+
node_type: self.class.name.split('::').last.downcase.gsub('node', ''),
|
69
|
+
metadata: @metadata,
|
70
|
+
children: @children.map { |child| child.respond_to?(:to_h) ? child.to_h : child }
|
71
|
+
}
|
72
|
+
end
|
73
|
+
|
74
|
+
# Accept a visitor for visitor pattern implementation
|
75
|
+
# @param visitor [Object] Visitor object
|
76
|
+
# @return [Object] Result from visitor
|
77
|
+
def accept(visitor)
|
78
|
+
visitor.visit(self)
|
79
|
+
end
|
80
|
+
|
81
|
+
# Pretty print the AST tree structure for debugging
|
82
|
+
# @param indent [Integer] Current indentation level
|
83
|
+
# @return [void]
|
84
|
+
def pretty_print(indent = 0)
|
85
|
+
pad = ' ' * indent
|
86
|
+
node_name = self.class.name.split('::').last
|
87
|
+
summary = summary_attributes
|
88
|
+
|
89
|
+
puts "#{pad}#{tree_symbol(indent)} #{node_name}#{summary.empty? ? '' : " (#{summary})"}"
|
90
|
+
|
91
|
+
@children.each_with_index do |child, _index|
|
92
|
+
child.pretty_print(indent + 1) if child.respond_to?(:pretty_print)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# Print just this node without children
|
97
|
+
# @return [String] Single line representation
|
98
|
+
def debug_line
|
99
|
+
node_name = self.class.name.split('::').last
|
100
|
+
summary = summary_attributes
|
101
|
+
"#{node_name}#{summary.empty? ? '' : " (#{summary})"}"
|
102
|
+
end
|
103
|
+
|
104
|
+
# Generate summary attributes for debugging display
|
105
|
+
# @return [String] Summary of key attributes
|
106
|
+
def summary_attributes
|
107
|
+
attrs = []
|
108
|
+
|
109
|
+
# Common attributes that most nodes might have
|
110
|
+
attrs << "name=#{property_name}" if respond_to?(:property_name) && property_name
|
111
|
+
|
112
|
+
if respond_to?(:comment_data) && comment_data
|
113
|
+
attrs << "type=#{comment_data.type}" if comment_data.type
|
114
|
+
attrs << "required=#{comment_data.required?}" if comment_data.respond_to?(:required?)
|
115
|
+
attrs << "desc=#{comment_data.description[0..30]}..." if comment_data.description && comment_data.description.length > 30
|
116
|
+
attrs << "desc=#{comment_data.description}" if comment_data.description && comment_data.description.length <= 30
|
117
|
+
end
|
118
|
+
|
119
|
+
attrs << "children=#{@children.size}" if @children.size > 0
|
120
|
+
attrs << "conditional" if respond_to?(:is_conditional) && is_conditional
|
121
|
+
|
122
|
+
attrs.join(', ')
|
123
|
+
end
|
124
|
+
|
125
|
+
private
|
126
|
+
|
127
|
+
# Generate tree symbol for pretty printing
|
128
|
+
# @param indent [Integer] Current indentation level
|
129
|
+
# @return [String] Tree symbol (├── or └──)
|
130
|
+
def tree_symbol(indent)
|
131
|
+
return '└─' if indent == 0
|
132
|
+
|
133
|
+
'├─'
|
134
|
+
end
|
135
|
+
|
136
|
+
# No longer need protected attr_writer since we have attr_accessor above
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
@@ -0,0 +1,180 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsOpenapiGen::AstNodes
|
4
|
+
# Represents comment data parsed from @openapi annotations
|
5
|
+
# Encapsulates all OpenAPI-related metadata extracted from comments
|
6
|
+
class CommentData
|
7
|
+
attr_reader :type, :description, :required, :enum, :field_name, :items, :conditional, :format, :example
|
8
|
+
|
9
|
+
def initialize(
|
10
|
+
type: nil,
|
11
|
+
description: nil,
|
12
|
+
required: true,
|
13
|
+
enum: nil,
|
14
|
+
field_name: nil,
|
15
|
+
items: nil,
|
16
|
+
conditional: false,
|
17
|
+
format: nil,
|
18
|
+
example: nil
|
19
|
+
)
|
20
|
+
@type = type
|
21
|
+
@description = description
|
22
|
+
@required = required
|
23
|
+
@enum = enum
|
24
|
+
@field_name = field_name
|
25
|
+
@items = items
|
26
|
+
@conditional = conditional
|
27
|
+
@format = format
|
28
|
+
@example = example
|
29
|
+
end
|
30
|
+
|
31
|
+
# Check if the property is required
|
32
|
+
# @return [Boolean] True if property is required
|
33
|
+
def required?
|
34
|
+
@required != false && @required != 'false'
|
35
|
+
end
|
36
|
+
|
37
|
+
# Check if the property is optional
|
38
|
+
# @return [Boolean] True if property is optional
|
39
|
+
def optional?
|
40
|
+
!required?
|
41
|
+
end
|
42
|
+
|
43
|
+
# Check if the property is conditional
|
44
|
+
# @return [Boolean] True if property is conditional
|
45
|
+
def conditional?
|
46
|
+
@conditional == true || @conditional == 'true'
|
47
|
+
end
|
48
|
+
|
49
|
+
# Check if the property has enum values
|
50
|
+
# @return [Boolean] True if property has enum values
|
51
|
+
def has_enum?
|
52
|
+
@enum && !@enum.empty?
|
53
|
+
end
|
54
|
+
|
55
|
+
# Check if the property has a specific format
|
56
|
+
# @return [Boolean] True if property has format specification
|
57
|
+
def has_format?
|
58
|
+
# Auto-detect format from invalid types that were converted
|
59
|
+
return true if auto_format_from_invalid_type
|
60
|
+
|
61
|
+
@format && !@format.empty?
|
62
|
+
end
|
63
|
+
|
64
|
+
# Check if the property has an example
|
65
|
+
# @return [Boolean] True if property has example
|
66
|
+
def has_example?
|
67
|
+
@example
|
68
|
+
end
|
69
|
+
|
70
|
+
# Get OpenAPI type, defaulting to string if not specified
|
71
|
+
# @return [String] OpenAPI type
|
72
|
+
def openapi_type
|
73
|
+
# Handle common invalid types and auto-correct them
|
74
|
+
case @type
|
75
|
+
when 'date-time', 'datetime', 'date', 'time'
|
76
|
+
# These are not valid OpenAPI types, should be string with format
|
77
|
+
'string'
|
78
|
+
else
|
79
|
+
@type || 'string'
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# Get format for the property, including auto-detected formats
|
84
|
+
# @return [String, nil] Format specification
|
85
|
+
def format_value
|
86
|
+
# Auto-detect format from invalid types that were converted
|
87
|
+
auto_format = auto_format_from_invalid_type
|
88
|
+
return auto_format if auto_format
|
89
|
+
|
90
|
+
@format
|
91
|
+
end
|
92
|
+
|
93
|
+
# Get items specification for arrays
|
94
|
+
# @return [Hash, nil] Items specification
|
95
|
+
def array_items
|
96
|
+
return nil unless @type == 'array'
|
97
|
+
|
98
|
+
@items || { 'type' => 'object' }
|
99
|
+
end
|
100
|
+
|
101
|
+
# Convert to hash representation suitable for OpenAPI schema
|
102
|
+
# @return [Hash] Hash representation
|
103
|
+
def to_openapi_schema
|
104
|
+
schema = { 'type' => openapi_type }
|
105
|
+
schema['description'] = @description if @description
|
106
|
+
schema['enum'] = @enum if has_enum?
|
107
|
+
schema['format'] = format_value if has_format?
|
108
|
+
schema['example'] = @example if has_example?
|
109
|
+
schema['items'] = array_items if @type == 'array' && array_items
|
110
|
+
schema
|
111
|
+
end
|
112
|
+
|
113
|
+
# Convert to hash representation for internal use
|
114
|
+
# @return [Hash] Hash representation
|
115
|
+
def to_h
|
116
|
+
{
|
117
|
+
type: @type,
|
118
|
+
description: @description,
|
119
|
+
required: @required,
|
120
|
+
enum: @enum,
|
121
|
+
field_name: @field_name,
|
122
|
+
items: @items,
|
123
|
+
conditional: @conditional,
|
124
|
+
format: @format,
|
125
|
+
example: @example
|
126
|
+
}.compact
|
127
|
+
end
|
128
|
+
|
129
|
+
# Merge with another CommentData, giving precedence to non-nil values
|
130
|
+
# @param other [CommentData] Other comment data to merge
|
131
|
+
# @return [CommentData] New merged comment data
|
132
|
+
def merge(other)
|
133
|
+
return self unless other.is_a?(CommentData)
|
134
|
+
|
135
|
+
CommentData.new(
|
136
|
+
type: other.type || @type,
|
137
|
+
description: other.description || @description,
|
138
|
+
required: other.required.nil? ? @required : other.required,
|
139
|
+
enum: other.enum || @enum,
|
140
|
+
field_name: other.field_name || @field_name,
|
141
|
+
items: other.items || @items,
|
142
|
+
conditional: other.conditional.nil? ? @conditional : other.conditional,
|
143
|
+
format: other.format || @format,
|
144
|
+
example: other.example || @example
|
145
|
+
)
|
146
|
+
end
|
147
|
+
|
148
|
+
# Create a copy with updated attributes
|
149
|
+
# @param attributes [Hash] Attributes to update
|
150
|
+
# @return [CommentData] New comment data with updated attributes
|
151
|
+
def with(**attributes)
|
152
|
+
CommentData.new(
|
153
|
+
type: attributes.fetch(:type, @type),
|
154
|
+
description: attributes.fetch(:description, @description),
|
155
|
+
required: attributes.fetch(:required, @required),
|
156
|
+
enum: attributes.fetch(:enum, @enum),
|
157
|
+
field_name: attributes.fetch(:field_name, @field_name),
|
158
|
+
items: attributes.fetch(:items, @items),
|
159
|
+
conditional: attributes.fetch(:conditional, @conditional),
|
160
|
+
format: attributes.fetch(:format, @format),
|
161
|
+
example: attributes.fetch(:example, @example)
|
162
|
+
)
|
163
|
+
end
|
164
|
+
|
165
|
+
private
|
166
|
+
|
167
|
+
# Auto-detect format from invalid types that were converted to string
|
168
|
+
# @return [String, nil] Format specification
|
169
|
+
def auto_format_from_invalid_type
|
170
|
+
case @type
|
171
|
+
when 'date-time', 'datetime'
|
172
|
+
'date-time'
|
173
|
+
when 'date'
|
174
|
+
'date'
|
175
|
+
when 'time'
|
176
|
+
'time'
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|