swaggard 1.1.0 → 1.3.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3dd5439e8bfa3577b6585d2f5bc85eca2de86b7f41df02425f7d3083f97db6c4
4
- data.tar.gz: 472f444aa3a5f99f0d6f867d55a80141a81848a24af6ca29366f979dfc8c4656
3
+ metadata.gz: 8f1057b0a7cf432113d8b58f3bb1857dd4f3fc72b2f916e7dd7342878b6f0192
4
+ data.tar.gz: 732909ca2cab511875bf16403f83d4f5a00e796ad211ce49e83dd5af96eceb4b
5
5
  SHA512:
6
- metadata.gz: d7ccfd568ffde678cafae6be6b2551823d24ba2e3f2d5ecaf8f04231c14d271b25223c9dcaab12ab4dd97ac1b93d1490045f6b6812af662def8d3faf628443c0
7
- data.tar.gz: 7c08c85b64918c6fe0806af1e224e0e3daeec20704f1617f65b21f8908bc79881d1a9ae01f4b25afe14efd7d7070bb97603a0523bbaf685724679f15b866998f
6
+ metadata.gz: 778991418f4c1640172aa9cb306070a5562b4cc1f9736b6f0bbed659e8a9f4c10d269dec050389cb8c4e8e8d480d164bc6f75fbf8b53a002bf057e55a1debe41
7
+ data.tar.gz: c1c8c9944b66ef1f736d1a323d3484aaab883f62fba843e5bcf442d0ff60fbb3fab728b367be25772e794def8f3c604bae7f64d66a3c1a8ba8dcf3de127c55f8
@@ -7,7 +7,7 @@ module Swaggard
7
7
  def initialize
8
8
  @paths = {}
9
9
  @tags = {}
10
- @definitions = []
10
+ @definitions = {}
11
11
  end
12
12
 
13
13
  def add_tag(tag)
@@ -19,7 +19,7 @@ module Swaggard
19
19
  def add_operation(operation)
20
20
  @paths[operation.path] ||= Swagger::Path.new(operation.path)
21
21
  @paths[operation.path].add_operation(operation)
22
- @definitions.concat(operation.definitions)
22
+ @definitions.merge!(operation.definitions)
23
23
  end
24
24
 
25
25
  def ignore_put_if_patch!
@@ -51,7 +51,7 @@ module Swaggard
51
51
  'produces' => Swaggard.configuration.api_formats.map { |format| "application/#{format}" },
52
52
  'tags' => @tags.map { |_, tag| tag.to_doc },
53
53
  'paths' => Hash[@paths.values.map { |path| [format_path(path.path), path.to_doc] }],
54
- 'definitions' => Hash[@definitions.map { |definition| [definition.id, definition.to_doc] }]
54
+ 'definitions' => Hash[@definitions.merge(Swaggard.configuration.definitions).map { |id, definition| [id, definition.to_doc(@definitions)] }]
55
55
  }
56
56
  end
57
57
 
@@ -23,8 +23,6 @@ module Swaggard
23
23
  :default_response_status_code, :excluded_paths, :path_parameter_description,
24
24
  :ignore_put_if_patch_exists
25
25
 
26
- attr_reader :custom_types
27
-
28
26
  def swagger_version
29
27
  @swagger_version ||= '2.0'
30
28
  end
@@ -149,6 +147,14 @@ module Swaggard
149
147
  custom_types[name] = definition
150
148
  end
151
149
 
150
+ def definitions
151
+ @definitions ||= {}
152
+ end
153
+
154
+ def add_definition(definition)
155
+ definitions[definition.id] = definition
156
+ end
157
+
152
158
  def path_parameter_description
153
159
  @path_parameter_description ||= ->(path_parameter) { "Scope response to #{path_parameter.name}" }
154
160
  end
@@ -1,19 +1,23 @@
1
- unless Rails::Application.instance_methods.include?(:assets_manifest)
2
- warn <<~END
3
- [Swaggard] It seems you are using an api only rails setup, but swaggard
4
- [Swaggard] web app needs sprockets in order to work. Make sure to add
5
- [Swaggard] require 'sprockets/railtie'.
6
- [Swaggard] If you plan to use it
7
- END
8
- end
9
-
10
1
  module Swaggard
11
2
  class Engine < ::Rails::Engine
12
3
  isolate_namespace Swaggard
13
4
 
5
+ def rake?
6
+ File.basename($PROGRAM_NAME) == 'rake'
7
+ end
8
+
14
9
  initializer 'swaggard.finisher_hook', after: :finisher_hook do |app|
15
10
  app.reload_routes!
16
11
 
12
+ if Rails.env.development? && !rake? && !app.methods.include?(:assets_manifest)
13
+ warn <<~END
14
+ [Swaggard] It seems you are using an api only rails setup, but swaggard
15
+ [Swaggard] web app needs sprockets in order to work. Make sure to add
16
+ [Swaggard] require 'sprockets/railtie'.
17
+ [Swaggard] If you plan to use it
18
+ END
19
+ end
20
+
17
21
  Swaggard.configure do |config|
18
22
  unless config.controllers_path
19
23
  config.controllers_path = "#{app.root}/app/controllers/**/*.rb"
@@ -6,24 +6,33 @@ module Swaggard
6
6
  class Models
7
7
 
8
8
  def run(yard_objects)
9
- definitions = []
9
+ definitions = {}
10
10
 
11
11
  yard_objects.each do |yard_object|
12
- next unless yard_object.type == :class
12
+ definition = parse_yard_object(yard_object)
13
13
 
14
- definition = Swagger::Definition.new(yard_object.path)
14
+ definitions[definition.id] = definition if definition
15
+ end
16
+
17
+ definitions
18
+ end
15
19
 
20
+ def parse_yard_object(yard_object)
21
+ return unless yard_object.type == :class
22
+
23
+ Swagger::Definition.new(yard_object.path, ancestors: yard_object.inheritance_tree.map(&:path)).tap do |definition|
16
24
  yard_object.tags.each do |tag|
17
- property = Swagger::Property.new(tag)
18
- definition.add_property(property)
25
+ case tag.tag_name
26
+ when 'attr'
27
+ property = Swaggard::Parsers::Property.run(tag)
28
+ definition.add_property(property)
29
+ when 'ignore_inherited'
30
+ definition.ignore_inherited = true
31
+ end
19
32
  end
20
-
21
- definitions << definition
22
33
  end
23
-
24
- definitions
25
34
  end
26
35
 
27
36
  end
28
37
  end
29
- end
38
+ end
@@ -0,0 +1,21 @@
1
+ require_relative '../swagger/property'
2
+
3
+ module Swaggard
4
+ module Parsers
5
+ module Property
6
+ def self.run(yard_object)
7
+ name = yard_object.name.dup
8
+ options_and_description = yard_object.text&.dup || ''
9
+
10
+ options_and_description.gsub!("\n", ' ')
11
+ options, description = options_and_description.match(/\A(\[.*\])?(.*)\Z/).captures
12
+ options = options ? options.gsub(/\[?\]?\s?/, '').split(',') : []
13
+ description = description.strip
14
+ required = name.gsub!(/^!/, '')
15
+ type = Parsers::Type.run(yard_object.types.first)
16
+
17
+ Swaggard::Swagger::Property.new(name, type, description, required.present?, options)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,15 @@
1
+ require_relative '../swagger/type'
2
+
3
+ module Swaggard
4
+ module Parsers
5
+ module Type
6
+ def self.run(string)
7
+ parts = string.split(/[<>]/)
8
+ name = parts.last
9
+ is_array = parts.grep(/array/i).any?
10
+
11
+ Swaggard::Swagger::Type.new(name, is_array)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -3,13 +3,15 @@ module Swaggard
3
3
  class Definition
4
4
 
5
5
  attr_reader :id
6
- attr_writer :description, :title
6
+ attr_writer :description, :title, :ignore_inherited
7
7
 
8
- def initialize(id)
8
+ def initialize(id, ancestors: [])
9
9
  @id = id
10
10
  @title = ''
11
11
  @properties = []
12
12
  @description = ''
13
+ @ancestors = ancestors
14
+ @ignore_inherited = false
13
15
  end
14
16
 
15
17
  def add_property(property)
@@ -20,15 +22,35 @@ module Swaggard
20
22
  @properties.empty?
21
23
  end
22
24
 
23
- def to_doc
25
+ def properties(definitions)
26
+ inherited_properties(definitions)
27
+ .concat(@properties)
28
+ .uniq { |property| property.id }
29
+ end
30
+
31
+ def inherited_properties(definitions)
32
+ return [] if @ignore_inherited
33
+
34
+ @ancestors.flat_map do |ancestor|
35
+ definition = definitions[ancestor]
36
+
37
+ next unless definition && definition.id != id
38
+
39
+ definition.properties(definitions)
40
+ end.compact
41
+ end
42
+
43
+ def to_doc(definitions)
24
44
  {}.tap do |doc|
25
45
  doc['title'] = @title if @title.present?
26
46
  doc['type'] = 'object'
27
47
 
28
48
  doc['description'] = @description if @description.present?
29
49
 
30
- doc['properties'] = Hash[@properties.map { |property| [property.id, property.to_doc] }]
31
- required_properties = @properties.select(&:required?).map(&:id)
50
+ all_properties = properties(definitions)
51
+
52
+ doc['properties'] = Hash[all_properties.map { |property| [property.id, property.to_doc] }]
53
+ required_properties = all_properties.select(&:required?).map(&:id)
32
54
  doc['required'] = required_properties if required_properties.any?
33
55
  end
34
56
 
@@ -92,8 +92,12 @@ module Swaggard
92
92
  end
93
93
 
94
94
  def definitions
95
- @responses.map(&:definition).compact.tap do |definitions|
96
- definitions << @body_parameter.definition if @body_parameter
95
+ @responses.map(&:definition).compact.inject({}) do |definitions, definition|
96
+ definitions[definition.id] = definition
97
+ end.tap do |definitions|
98
+ next unless @body_parameter
99
+
100
+ definitions[@body_parameter.definition.id] = @body_parameter.definition
97
101
  end
98
102
  end
99
103
 
@@ -1,11 +1,10 @@
1
1
  require_relative 'base'
2
- require_relative '../type'
2
+ require_relative '../../parsers/type'
3
3
 
4
4
  module Swaggard
5
5
  module Swagger
6
6
  module Parameters
7
7
  class Body < Base
8
-
9
8
  attr_reader :definition
10
9
 
11
10
  def initialize(operation_name)
@@ -51,9 +50,7 @@ module Swaggard
51
50
 
52
51
  private
53
52
 
54
-
55
53
  class Property
56
-
57
54
  attr_reader :id
58
55
 
59
56
  def initialize(string)
@@ -83,13 +80,11 @@ module Swaggard
83
80
 
84
81
  @id = name
85
82
  @description = description if description.present?
86
- @type = Type.new([data_type])
83
+ @type = Parsers::Type.run(data_type)
87
84
  @required = required
88
85
  @options = options
89
86
  end
90
-
91
87
  end
92
-
93
88
  end
94
89
  end
95
90
  end
@@ -1,10 +1,10 @@
1
1
  require_relative 'base'
2
+ require_relative '../../parsers/type'
2
3
 
3
4
  module Swaggard
4
5
  module Swagger
5
6
  module Parameters
6
7
  class Query < Base
7
-
8
8
  def initialize(string)
9
9
  @in = 'query'
10
10
  parse(string)
@@ -37,12 +37,11 @@ module Swaggard
37
37
 
38
38
  @name = name
39
39
  @description = description
40
- @type = Type.new([data_type])
40
+ @type = Parsers::Type.run(data_type)
41
41
  @is_required = required.present?
42
42
  @allow_multiple = allow_multiple.present?
43
43
  @options = options
44
44
  end
45
-
46
45
  end
47
46
  end
48
47
  end
@@ -3,23 +3,13 @@ require_relative 'type'
3
3
  module Swaggard
4
4
  module Swagger
5
5
  class Property
6
-
7
6
  attr_reader :id, :type, :description
8
7
 
9
- def initialize(yard_object)
10
- name = yard_object.name.dup
11
- options_and_description = yard_object.text&.dup || ''
12
-
13
- options_and_description.gsub!("\n", ' ')
14
- options, description = options_and_description.match(/\A(\[.*\])?(.*)\Z/).captures
15
- options = options ? options.gsub(/\[?\]?\s?/, '').split(',') : []
16
- description = description.strip
17
- required = name.gsub!(/^!/, '')
18
-
8
+ def initialize(name, type, description = '', required = false, options = [])
19
9
  @id = name
20
- @type = Type.new(yard_object.types)
10
+ @type = type
21
11
  @description = description
22
- @required = required.present?
12
+ @required = required
23
13
  @options = options
24
14
  end
25
15
 
@@ -33,7 +23,6 @@ module Swaggard
33
23
  result['enum'] = @options if @options.present?
34
24
  result
35
25
  end
36
-
37
26
  end
38
27
  end
39
28
  end
@@ -12,14 +12,16 @@ module Swaggard
12
12
  'boolean' => { 'type' => 'boolean' },
13
13
  'date' => { 'type' => 'string', 'format' => 'date' },
14
14
  'date-time' => { 'type' => 'string', 'format' => 'date-time' },
15
+ 'datetime' => { 'type' => 'string', 'format' => 'date-time' },
15
16
  'password' => { 'type' => 'string', 'format' => 'password' },
16
17
  'hash' => { 'type' => 'object' }
17
18
  }
18
19
 
19
20
  attr_reader :name
20
21
 
21
- def initialize(types)
22
- parse(types)
22
+ def initialize(name, is_array = false)
23
+ @name = name.to_s
24
+ @is_array = is_array
23
25
  end
24
26
 
25
27
  def to_doc
@@ -32,14 +34,6 @@ module Swaggard
32
34
 
33
35
  private
34
36
 
35
- def parse(types)
36
- parts = types.first.split(/[<>]/)
37
-
38
- @name = parts.last
39
- @is_array = parts.grep(/array/i).any?
40
- end
41
-
42
-
43
37
  def basic_type?
44
38
  BASIC_TYPES.has_key?(@name.downcase)
45
39
  end
@@ -1,3 +1,3 @@
1
1
  module Swaggard
2
- VERSION = '1.1.0'
2
+ VERSION = '1.3.0'
3
3
  end
data/lib/swaggard.rb CHANGED
@@ -38,6 +38,7 @@ module Swaggard
38
38
  ::YARD::Tags::Library.define_tag('Response description', :response_description)
39
39
  ::YARD::Tags::Library.define_tag('Response example', :response_example)
40
40
  ::YARD::Tags::Library.define_tag('Response header', :response_header)
41
+ ::YARD::Tags::Library.define_tag('Ignore inherited attributes', :ignore_inherited)
41
42
  end
42
43
 
43
44
  def get_doc(host = nil)
@@ -119,7 +120,6 @@ module Swaggard
119
120
 
120
121
  tags.each do |tag|
121
122
  @tags << [tag, operations]
122
- # [tag.controller_class.controller_path] ||= { tag: tag, operations: operations }
123
123
  end
124
124
  end
125
125
 
@@ -136,12 +136,12 @@ module Swaggard
136
136
  def parse_models
137
137
  parser = Parsers::Models.new
138
138
 
139
- definitions =[]
139
+ definitions = {}
140
140
  configuration.models_paths.each do |path|
141
141
  Dir[path].each do |file|
142
142
  yard_objects = get_yard_objects(file)
143
143
 
144
- definitions.concat(parser.run(yard_objects))
144
+ definitions.merge!(parser.run(yard_objects))
145
145
  end
146
146
 
147
147
  @api.definitions = definitions
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: swaggard
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Adrian Gomez
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-01-23 00:00:00.000000000 Z
11
+ date: 2022-03-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -168,7 +168,9 @@ files:
168
168
  - lib/swaggard/engine.rb
169
169
  - lib/swaggard/parsers/controller.rb
170
170
  - lib/swaggard/parsers/models.rb
171
+ - lib/swaggard/parsers/property.rb
171
172
  - lib/swaggard/parsers/routes.rb
173
+ - lib/swaggard/parsers/type.rb
172
174
  - lib/swaggard/swagger/definition.rb
173
175
  - lib/swaggard/swagger/operation.rb
174
176
  - lib/swaggard/swagger/parameters/base.rb
@@ -192,7 +194,6 @@ files:
192
194
  - spec/fixtures/dummy/config/application.rb
193
195
  - spec/fixtures/dummy/config/environments/development.rb
194
196
  - spec/fixtures/dummy/config/routes.rb
195
- - spec/fixtures/dummy/log/development.log
196
197
  - spec/fixtures/swagger_schema.json
197
198
  - spec/integration/swaggard_spec.rb
198
199
  - spec/spec_helper.rb
@@ -200,7 +201,7 @@ homepage: https://github.com/adrian-gomez/swaggard
200
201
  licenses:
201
202
  - MIT
202
203
  metadata: {}
203
- post_install_message:
204
+ post_install_message:
204
205
  rdoc_options: []
205
206
  require_paths:
206
207
  - lib
@@ -215,19 +216,18 @@ required_rubygems_version: !ruby/object:Gem::Requirement
215
216
  - !ruby/object:Gem::Version
216
217
  version: '0'
217
218
  requirements: []
218
- rubygems_version: 3.0.3
219
- signing_key:
219
+ rubygems_version: 3.2.15
220
+ signing_key:
220
221
  specification_version: 4
221
222
  summary: 'Swaggard: Swagger Rails REST API doc using yard YARD'
222
223
  test_files:
223
- - spec/spec_helper.rb
224
- - spec/integration/swaggard_spec.rb
225
- - spec/fixtures/swagger_schema.json
226
- - spec/fixtures/dummy/app/controllers/pets_controller.rb
227
- - spec/fixtures/dummy/app/controllers/application_controller.rb
224
+ - spec/fixtures/api.json
228
225
  - spec/fixtures/dummy/app/controllers/admin/pets_controller.rb
229
- - spec/fixtures/dummy/config/routes.rb
230
- - spec/fixtures/dummy/config/environments/development.rb
226
+ - spec/fixtures/dummy/app/controllers/application_controller.rb
227
+ - spec/fixtures/dummy/app/controllers/pets_controller.rb
231
228
  - spec/fixtures/dummy/config/application.rb
232
- - spec/fixtures/dummy/log/development.log
233
- - spec/fixtures/api.json
229
+ - spec/fixtures/dummy/config/environments/development.rb
230
+ - spec/fixtures/dummy/config/routes.rb
231
+ - spec/fixtures/swagger_schema.json
232
+ - spec/integration/swaggard_spec.rb
233
+ - spec/spec_helper.rb
File without changes