oas_rails 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +147 -0
  4. data/Rakefile +8 -0
  5. data/app/assets/config/oas_rails_manifest.js +1 -0
  6. data/app/assets/stylesheets/oas_rails/application.css +15 -0
  7. data/app/controllers/oas_rails/application_controller.rb +4 -0
  8. data/app/controllers/oas_rails/oas_rails_controller.rb +9 -0
  9. data/app/helpers/oas_rails/application_helper.rb +4 -0
  10. data/app/helpers/oas_rails/oas_rails_helper.rb +4 -0
  11. data/app/helpers/oas_rails/test_helper.rb +4 -0
  12. data/app/jobs/oas_rails/application_job.rb +4 -0
  13. data/app/mailers/oas_rails/application_mailer.rb +6 -0
  14. data/app/models/oas_rails/application_record.rb +5 -0
  15. data/app/views/layouts/oas_rails/application.html.erb +18 -0
  16. data/app/views/oas_rails/oas_rails/index.html.erb +1 -0
  17. data/app/views/oas_rails/test/show.html.erb +1 -0
  18. data/config/routes.rb +4 -0
  19. data/lib/generators/oas_rails/config/config_generator.rb +11 -0
  20. data/lib/generators/oas_rails/config/templates/oas_rails_initializer.rb +49 -0
  21. data/lib/oas_rails/configuration.rb +28 -0
  22. data/lib/oas_rails/contact.rb +12 -0
  23. data/lib/oas_rails/engine.rb +5 -0
  24. data/lib/oas_rails/info.rb +60 -0
  25. data/lib/oas_rails/license.rb +11 -0
  26. data/lib/oas_rails/media_type.rb +57 -0
  27. data/lib/oas_rails/oas_base.rb +29 -0
  28. data/lib/oas_rails/oas_route.rb +143 -0
  29. data/lib/oas_rails/operation.rb +118 -0
  30. data/lib/oas_rails/parameter.rb +47 -0
  31. data/lib/oas_rails/path_item.rb +25 -0
  32. data/lib/oas_rails/paths.rb +19 -0
  33. data/lib/oas_rails/request_body.rb +29 -0
  34. data/lib/oas_rails/response.rb +12 -0
  35. data/lib/oas_rails/responses.rb +20 -0
  36. data/lib/oas_rails/route_extractor.rb +87 -0
  37. data/lib/oas_rails/server.rb +10 -0
  38. data/lib/oas_rails/specification.rb +42 -0
  39. data/lib/oas_rails/tag.rb +17 -0
  40. data/lib/oas_rails/version.rb +3 -0
  41. data/lib/oas_rails/yard/oas_yard_factory.rb +160 -0
  42. data/lib/oas_rails.rb +120 -0
  43. metadata +159 -0
@@ -0,0 +1,160 @@
1
+ module OasRails
2
+ module Yard
3
+ class RequestBodyTag < ::YARD::Tags::Tag
4
+ attr_accessor :klass, :schema, :required
5
+
6
+ def initialize(tag_name, text, klass, schema: {}, required: false)
7
+ # initialize(tag_name, text, types = nil, name = nil)
8
+ super(tag_name, text, nil, nil)
9
+ @klass = klass
10
+ @schema = schema
11
+ @required = required
12
+ end
13
+ end
14
+
15
+ class RequestBodyExampleTag < ::YARD::Tags::Tag
16
+ attr_accessor :content
17
+
18
+ def initialize(tag_name, text, content: {})
19
+ super(tag_name, text, nil, nil)
20
+ @content = content
21
+ end
22
+ end
23
+
24
+ class ParameterTag < ::YARD::Tags::Tag
25
+ attr_accessor :schema, :required, :location
26
+
27
+ def initialize(tag_name, name, text, schema, location, required: false)
28
+ super(tag_name, text, nil, name)
29
+ @schema = schema
30
+ @required = required
31
+ @location = location
32
+ end
33
+ end
34
+
35
+ class ResponseTag < ::YARD::Tags::Tag
36
+ attr_accessor :schema
37
+
38
+ def initialize(tag_name, name, text, schema)
39
+ super(tag_name, text, nil, name)
40
+ @schema = schema
41
+ end
42
+ end
43
+
44
+ class OasYardFactory < ::YARD::Tags::DefaultFactory
45
+ ## parse_tag is a prefix used by YARD
46
+
47
+ def parse_tag_with_request_body(tag_name, text)
48
+ match = text.match(/^(.*?)\s*\[(.*?)\]\s*(.*)$/)
49
+ if !match.nil?
50
+ text = match[1].strip
51
+ type, required = parse_type(match[2].strip)
52
+
53
+ if type.constantize == Hash
54
+ hash_string = match[3].strip
55
+
56
+ begin
57
+ hash = eval(hash_string)
58
+ hash = OasRails.hash_to_json_schema(hash)
59
+ rescue StandardError
60
+ hash = {}
61
+ end
62
+ end
63
+
64
+ RequestBodyTag.new(tag_name, text, type.constantize, schema: hash, required:)
65
+ else
66
+ Rails.logger.debug("Error parsing request_body tag: #{text}")
67
+ end
68
+ end
69
+
70
+ def parse_tag_with_request_body_example(tag_name, text)
71
+ match = text.match(/^(.*?)\s*\[(.*?)\]\s*(.*)$/)
72
+ if !match.nil?
73
+ text = match[1].strip
74
+ type = match[2]
75
+
76
+ if type.constantize == Hash
77
+ hash_string = match[3].strip
78
+
79
+ begin
80
+ hash = eval(hash_string)
81
+ rescue StandardError
82
+ hash = {}
83
+ end
84
+ end
85
+ RequestBodyExampleTag.new(tag_name, text, content: hash)
86
+ else
87
+ Rails.logger.debug("Error parsing request body example tag: #{text}")
88
+ end
89
+ end
90
+
91
+ def parse_tag_with_parameter(tag_name, text)
92
+ match = text.match(/^(.*?)\s*\[(.*?)\]\s*(.*)$/)
93
+ if !match.nil?
94
+ text = match[1].strip
95
+ name, location = parse_position_name(text)
96
+ type, required = parse_type(match[2].strip)
97
+ schema = OasRails.type_to_schema(type)
98
+
99
+ ParameterTag.new(tag_name, name, match[3].strip, schema, location, required:)
100
+ else
101
+ Rails.logger.debug("Error parsing request body example tag: #{text}")
102
+ end
103
+ end
104
+
105
+ def parse_tag_with_response(tag_name, text)
106
+ match = text.match(/^(.*?)\s*\[(.*?)\]\s*(.*)$/)
107
+ if !match.nil?
108
+ name, code = parse_position_name(match[1].strip)
109
+
110
+ begin
111
+ hash = parse_str_to_hash(match[3].strip)
112
+ hash = OasRails.hash_to_json_schema(hash)
113
+ rescue StandardError
114
+ hash = {}
115
+ end
116
+
117
+ ResponseTag.new(tag_name, code, name, hash)
118
+ else
119
+ Rails.logger.debug("Error parsing request body example tag: #{text}")
120
+ end
121
+ end
122
+
123
+ def parse_position_name(input)
124
+ return unless input =~ /^([^(]+)\((.*)\)$/
125
+
126
+ name = ::Regexp.last_match(1)
127
+ location = ::Regexp.last_match(2)
128
+ [name, location]
129
+ end
130
+
131
+ def parse_type(type_string)
132
+ if type_string.end_with?('!')
133
+ [type_string.chomp('!'), true]
134
+ else
135
+ [type_string, false]
136
+ end
137
+ end
138
+
139
+ def parse_str_to_hash(str)
140
+ str = str.gsub(/^\{|\}$/, '') # Remove leading { and trailing }
141
+ pairs = str.split(',').map(&:strip)
142
+
143
+ pairs.each_with_object({}) do |pair, hash|
144
+ key, value = pair.split(':').map(&:strip)
145
+ key = key.to_sym
146
+ hash[key] = case value
147
+ when 'String' then String
148
+ when 'Integer' then Integer
149
+ when 'Float' then Float
150
+ when 'Boolean' then [TrueClass, FalseClass]
151
+ when 'Array' then Array
152
+ when 'Hash' then Hash
153
+ when 'DateTime' then DateTime
154
+ else value
155
+ end
156
+ end
157
+ end
158
+ end
159
+ end
160
+ end
data/lib/oas_rails.rb ADDED
@@ -0,0 +1,120 @@
1
+ require "yard"
2
+ require "method_source"
3
+ require "esquema"
4
+
5
+ require_relative 'oas_rails/version'
6
+ require_relative 'oas_rails/engine'
7
+ require_relative 'oas_rails/oas_base'
8
+ require_relative 'oas_rails/configuration'
9
+ require_relative 'oas_rails/specification'
10
+ require_relative 'oas_rails/route_extractor'
11
+ require_relative 'oas_rails/oas_route'
12
+ require_relative 'oas_rails/operation'
13
+
14
+ require_relative 'oas_rails/info'
15
+ require_relative 'oas_rails/contact'
16
+ require_relative 'oas_rails/paths'
17
+ require_relative 'oas_rails/path_item'
18
+ require_relative 'oas_rails/parameter'
19
+ require_relative 'oas_rails/tag'
20
+ require_relative 'oas_rails/license'
21
+ require_relative 'oas_rails/server'
22
+ require_relative "oas_rails/request_body"
23
+ require_relative "oas_rails/media_type"
24
+ require_relative 'oas_rails/yard/oas_yard_factory'
25
+ require_relative "oas_rails/response"
26
+ require_relative "oas_rails/responses"
27
+
28
+ module OasRails
29
+ class << self
30
+ def configure
31
+ yield config
32
+ end
33
+
34
+ def config
35
+ @config ||= Configuration.new
36
+ end
37
+
38
+ def configure_yard!
39
+ ::YARD::Tags::Library.default_factory = Yard::OasYardFactory
40
+ yard_tags = {
41
+ 'Request body' => [:request_body, :with_request_body],
42
+ 'Request body Example' => [:request_body_example, :with_request_body_example],
43
+ 'Parameter' => [:parameter, :with_parameter],
44
+ 'Response' => [:response, :with_response],
45
+ 'Endpoint Tags' => [:tags],
46
+ 'Summary' => [:summary]
47
+ }
48
+ yard_tags.each do |tag_name, (method_name, handler)|
49
+ ::YARD::Tags::Library.define_tag(tag_name, method_name, handler)
50
+ end
51
+ end
52
+
53
+ def configure_esquema!
54
+ Esquema.configure do |config|
55
+ config.exclude_associations = true
56
+ config.exclude_foreign_keys = true
57
+ config.excluded_columns = %i[id created_at updated_at deleted_at]
58
+ end
59
+ end
60
+
61
+ def detect_test_framework
62
+ if defined?(FactoryBot)
63
+ :factory_bot
64
+ elsif ActiveRecord::Base.connection.table_exists?('ar_internal_metadata')
65
+ :fixtures
66
+ else
67
+ :unknown
68
+ end
69
+ end
70
+
71
+ TYPE_MAPPING = {
72
+ 'String' => 'string',
73
+ 'Integer' => 'number',
74
+ 'Float' => 'number',
75
+ 'TrueClass' => 'boolean',
76
+ 'FalseClass' => 'boolean',
77
+ 'Boolean' => 'boolean',
78
+ 'NilClass' => 'null',
79
+ 'Hash' => 'object',
80
+ 'Object' => 'object',
81
+ 'DateTime' => 'string'
82
+ }.freeze
83
+
84
+ def type_to_schema(type_string)
85
+ if type_string.start_with?('Array<')
86
+ inner_type = type_string[/Array<(.+)>$/, 1]
87
+ {
88
+ "type" => "array",
89
+ "items" => type_to_schema(inner_type)
90
+ }
91
+ else
92
+ { "type" => TYPE_MAPPING.fetch(type_string, 'string') }
93
+ end
94
+ end
95
+
96
+ def hash_to_json_schema(hash)
97
+ {
98
+ type: 'object',
99
+ properties: hash_to_properties(hash),
100
+ required: []
101
+ }
102
+ end
103
+
104
+ def hash_to_properties(hash)
105
+ hash.transform_values do |value|
106
+ if value.is_a?(Hash)
107
+ hash_to_json_schema(value)
108
+ elsif value.is_a?(Class)
109
+ { type: ruby_type_to_json_type(value.name) }
110
+ else
111
+ { type: ruby_type_to_json_type(value.class.name) }
112
+ end
113
+ end
114
+ end
115
+
116
+ def ruby_type_to_json_type(ruby_type)
117
+ TYPE_MAPPING.fetch(ruby_type, 'string')
118
+ end
119
+ end
120
+ end
metadata ADDED
@@ -0,0 +1,159 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: oas_rails
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - a-chacon
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2024-07-27 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: esquema
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.1.2
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.1.2
27
+ - !ruby/object:Gem::Dependency
28
+ name: method_source
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rails
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '7.0'
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: 7.0.0
51
+ type: :runtime
52
+ prerelease: false
53
+ version_requirements: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - "~>"
56
+ - !ruby/object:Gem::Version
57
+ version: '7.0'
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: 7.0.0
61
+ - !ruby/object:Gem::Dependency
62
+ name: yard
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '0.9'
68
+ type: :runtime
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '0.9'
75
+ description: |2+
76
+ # Open API Specification For Rails
77
+
78
+ OasRails is a Rails engine for generating **automatic interactive documentation for your Rails APIs**. It generates an **OAS 3.1** document and displays it using **[RapiDoc](https://rapidocweb.com)**.
79
+
80
+ ## What Sets OasRails Apart?
81
+
82
+ - **Dynamic**: No command required to generate docs
83
+ - **Simple**: Complement default documentation with a few comments; no need to learn a complex DSL
84
+ - **Pure Ruby on Rails APIs**: No additional frameworks needed (e.g., Grape, RSpec)
85
+
86
+ email:
87
+ - andres.ch@protonmail.com
88
+ executables: []
89
+ extensions: []
90
+ extra_rdoc_files: []
91
+ files:
92
+ - MIT-LICENSE
93
+ - README.md
94
+ - Rakefile
95
+ - app/assets/config/oas_rails_manifest.js
96
+ - app/assets/stylesheets/oas_rails/application.css
97
+ - app/controllers/oas_rails/application_controller.rb
98
+ - app/controllers/oas_rails/oas_rails_controller.rb
99
+ - app/helpers/oas_rails/application_helper.rb
100
+ - app/helpers/oas_rails/oas_rails_helper.rb
101
+ - app/helpers/oas_rails/test_helper.rb
102
+ - app/jobs/oas_rails/application_job.rb
103
+ - app/mailers/oas_rails/application_mailer.rb
104
+ - app/models/oas_rails/application_record.rb
105
+ - app/views/layouts/oas_rails/application.html.erb
106
+ - app/views/oas_rails/oas_rails/index.html.erb
107
+ - app/views/oas_rails/test/show.html.erb
108
+ - config/routes.rb
109
+ - lib/generators/oas_rails/config/config_generator.rb
110
+ - lib/generators/oas_rails/config/templates/oas_rails_initializer.rb
111
+ - lib/oas_rails.rb
112
+ - lib/oas_rails/configuration.rb
113
+ - lib/oas_rails/contact.rb
114
+ - lib/oas_rails/engine.rb
115
+ - lib/oas_rails/info.rb
116
+ - lib/oas_rails/license.rb
117
+ - lib/oas_rails/media_type.rb
118
+ - lib/oas_rails/oas_base.rb
119
+ - lib/oas_rails/oas_route.rb
120
+ - lib/oas_rails/operation.rb
121
+ - lib/oas_rails/parameter.rb
122
+ - lib/oas_rails/path_item.rb
123
+ - lib/oas_rails/paths.rb
124
+ - lib/oas_rails/request_body.rb
125
+ - lib/oas_rails/response.rb
126
+ - lib/oas_rails/responses.rb
127
+ - lib/oas_rails/route_extractor.rb
128
+ - lib/oas_rails/server.rb
129
+ - lib/oas_rails/specification.rb
130
+ - lib/oas_rails/tag.rb
131
+ - lib/oas_rails/version.rb
132
+ - lib/oas_rails/yard/oas_yard_factory.rb
133
+ homepage: https://github.com/a-chacon/oas_rails
134
+ licenses:
135
+ - GPL-3.0-only
136
+ metadata:
137
+ homepage_uri: https://github.com/a-chacon/oas_rails
138
+ post_install_message:
139
+ rdoc_options: []
140
+ require_paths:
141
+ - lib
142
+ required_ruby_version: !ruby/object:Gem::Requirement
143
+ requirements:
144
+ - - ">="
145
+ - !ruby/object:Gem::Version
146
+ version: '3.0'
147
+ required_rubygems_version: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - ">="
150
+ - !ruby/object:Gem::Version
151
+ version: '0'
152
+ requirements: []
153
+ rubygems_version: 3.5.15
154
+ signing_key:
155
+ specification_version: 4
156
+ summary: OasRails is a Rails engine for generating automatic interactive documentation
157
+ for your Rails APIs. It generates an OAS document and displays it using a nice UI.
158
+ test_files: []
159
+ ...