swaggard 1.0.1 → 1.2.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: b3e1aa159808f0d01c9e6759727b97aff8a0cb4c383f441a3ca602e6b302da8b
4
- data.tar.gz: 4f3b2511fbd15049e5e8746af31174705544a477b70cc0cbbe30d24563897ee7
3
+ metadata.gz: 7a49ea847a04d150e7b42923bf6a8290950f5fa18c53deba16907c36a59a664f
4
+ data.tar.gz: 3986acba75f1b962c13ca5b93ceb2f1481609f3516b5438689822d0916675952
5
5
  SHA512:
6
- metadata.gz: 244da8fb5d6d234aab95d0b6e1b6bf13fd1dd7f519daf93aa2ff1f21dd05e2d0a886aa1e80f07ae7181d7541fc853b83bf5f711465976577cea725175bb0be36
7
- data.tar.gz: c82e666367b0555c8d7de02f785c9045aa53c77c591855afa5e1c093f294c8b69cff7cbcc5a9eb70cdda1004f855cf149148fede20be4f4296515878855aa34c
6
+ metadata.gz: 3fd5f67a5c03861c8e78a77088f1aa4712a75d9fb251a2c1acb2e597f5609ae81b935d5ac090f5e49ae9dba7ab0a0ceb17be40a553ea183185d3fc88cf14c13f
7
+ data.tar.gz: db5042eb41674f3f85f94aa860743eed9861eb49d0bc44b9ed02bc36dcb578816552ff44dc2016a61bb01c5f90d71c0b3a31b23f55fd538d8b53403a9ac3cc85
data/README.md CHANGED
@@ -11,6 +11,8 @@ the supported rails version.
11
11
 
12
12
  Swaggard Version | Swagger UI Version | Supported Rails Versions
13
13
  ---------------- | ------------------- | ------------------------
14
+ 1.1.x | 2.2.8 | 4 - 6
15
+ 1.0.x | 2.2.8 | 4 - 5
14
16
  0.5.x | 2.2.8 | 4 - 5
15
17
  0.4.x | 2.2.8 | 4
16
18
  0.3.x | 2.1.3 | 4
@@ -43,6 +45,13 @@ Mount your engine
43
45
 
44
46
  mount Swaggard::Engine, at: '/api_docs/swagger/'
45
47
 
48
+ Make sure the asset pipeline is enabled by either requiring all railties
49
+ or just the sprockets one:
50
+
51
+ # config/application.rb
52
+
53
+ require 'sprockets/railtie'
54
+
46
55
  Access your service documentation
47
56
 
48
57
  open http://localhost:3000/api_docs/swagger/
@@ -1,5 +1,8 @@
1
- Rails.application.config.assets.precompile += %w[swaggard/application_print.css
2
- swaggard/favicon-32x32.png
3
- swaggard/favicon-16x16.png
4
- swaggard/lang/*.js
5
- swaggard/logo_small.png]
1
+ if Rails::Application.instance_methods.include?(:assets_manifest)
2
+ Rails.application.config.assets.precompile += %w[swaggard/application_print.css
3
+ swaggard/favicon-32x32.png
4
+ swaggard/favicon-16x16.png
5
+ swaggard/lang/*.js
6
+ swaggard/logo_small.png]
7
+
8
+ end
@@ -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.map { |id, definition| [id, definition.to_doc(@definitions)] }]
55
55
  }
56
56
  end
57
57
 
@@ -1,20 +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] neeeds sprockets in order to work so its going to require it.
5
- [Swaggard] This might have undesired side effects, if thats not the case
6
- [Swaggard] you can ignore this warning.
7
- END
8
- require 'sprockets/railtie'
9
- end
10
-
11
1
  module Swaggard
12
2
  class Engine < ::Rails::Engine
13
3
  isolate_namespace Swaggard
14
4
 
5
+ def rake?
6
+ File.basename($PROGRAM_NAME) == 'rake'
7
+ end
8
+
15
9
  initializer 'swaggard.finisher_hook', after: :finisher_hook do |app|
16
10
  app.reload_routes!
17
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
+
18
21
  Swaggard.configure do |config|
19
22
  unless config.controllers_path
20
23
  config.controllers_path = "#{app.root}/app/controllers/**/*.rb"
@@ -4,23 +4,29 @@ require_relative '../swagger/tag'
4
4
  module Swaggard
5
5
  module Parsers
6
6
  class Controller
7
-
8
7
  def run(yard_objects)
9
- tag = nil
8
+ tags = nil
10
9
  operations = {}
11
10
 
12
11
  yard_objects.each do |yard_object|
13
12
  if yard_object.type == :class
14
- tag = Swagger::Tag.new(yard_object)
15
- elsif tag && yard_object.type == :method
13
+ tags = get_tags(yard_object)
14
+ elsif tags && yard_object.type == :method
16
15
  name = yard_object.name
17
16
  operations[name.to_s] = yard_object
18
17
  end
19
18
  end
20
19
 
21
- return tag, operations
20
+ return tags, operations
22
21
  end
23
22
 
23
+ private
24
+
25
+ def get_tags(yard_object)
26
+ tags = yard_object.tags.select { |tag| tag.tag_name == 'tag' }
27
+
28
+ tags.map { |tag| Swagger::Tag.new(yard_object, tag) }
29
+ end
24
30
  end
25
31
  end
26
32
  end
@@ -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 = Swagger::Property.new(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
@@ -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
 
@@ -32,7 +32,7 @@ module Swaggard
32
32
 
33
33
  case yard_tag.tag_name
34
34
  when 'operation_id'
35
- @operation_id = value
35
+ @operation_id = "#{@tag.name}.#{value}"
36
36
  when 'query_parameter'
37
37
  @parameters << Parameters::Query.new(value)
38
38
  when 'form_parameter'
@@ -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,20 +1,20 @@
1
1
  module Swaggard
2
2
  module Swagger
3
3
  class Tag
4
-
5
4
  attr_accessor :name, :description
6
5
 
7
- attr_reader :controller_class
6
+ attr_reader :controller_class, :controller_name, :route
8
7
 
9
- def initialize(yard_object)
8
+ def initialize(yard_object, tag)
10
9
  controller_name = "#{yard_object.namespace}::#{yard_object.name}"
11
10
 
12
11
  @yard_name = yard_object.name
13
12
  @controller_class = controller_name.constantize
14
-
15
- tag = yard_object.tags.find { |tag| tag.tag_name == 'tag' }
13
+ @controller_name = controller_class.controller_path
16
14
 
17
15
  @name = tag ? tag.text : "#{@controller_class.controller_path}"
16
+ @name, @route = @name.split(' ')
17
+
18
18
  @description = yard_object.docstring || ''
19
19
  end
20
20
 
@@ -24,7 +24,6 @@ module Swaggard
24
24
  'description' => @description
25
25
  }
26
26
  end
27
-
28
27
  end
29
28
  end
30
29
  end
@@ -1,3 +1,3 @@
1
1
  module Swaggard
2
- VERSION = '1.0.1'
2
+ VERSION = '1.2.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)
@@ -61,21 +62,25 @@ module Swaggard
61
62
  @api.ignore_put_if_patch! if Swaggard.configuration.ignore_put_if_patch_exists
62
63
  end
63
64
 
65
+ def tag_matches?(tag, controller_name, path)
66
+ matches = tag.controller_name == controller_name
67
+
68
+ return matches unless tag.route.present?
69
+
70
+ matches && Regexp.new(tag.route).match?(path)
71
+ end
72
+
64
73
  def build_operation(path, verb, route)
65
74
  controller_name = route[:controller]
66
75
  action_name = route[:action]
67
76
 
68
- return unless controllers[controller_name]
69
-
70
- controller_tag = controllers[controller_name][:tag]
77
+ controller_tag, controller_operations = tags.find { |tag, _operations| tag_matches?(tag, controller_name, path) }
71
78
 
72
79
  return unless controller_tag
73
80
 
74
- return unless controllers[controller_name][:operations]
75
-
76
- return unless controllers[controller_name][:operations][action_name]
81
+ return unless controller_operations[action_name]
77
82
 
78
- operation_yard_object = controllers[controller_name][:operations][action_name]
83
+ operation_yard_object = controller_operations[action_name]
79
84
 
80
85
  return unless operation_yard_object
81
86
 
@@ -100,23 +105,25 @@ module Swaggard
100
105
  end
101
106
  end
102
107
 
103
- def controllers
104
- return @controllers if @controllers
108
+ def tags
109
+ return @tags if @tags
105
110
 
106
111
  parser = Parsers::Controller.new
107
112
 
108
- @controllers = {}
113
+ @tags = []
109
114
  Dir[configuration.controllers_path].each do |file|
110
115
  yard_objects = get_yard_objects(file)
111
116
 
112
- tag, operations = parser.run(yard_objects)
117
+ tags, operations = parser.run(yard_objects)
113
118
 
114
- next unless tag
119
+ next unless tags
115
120
 
116
- @controllers[tag.controller_class.controller_path] ||= { tag: tag, operations: operations }
121
+ tags.each do |tag|
122
+ @tags << [tag, operations]
123
+ end
117
124
  end
118
125
 
119
- @controllers
126
+ @tags
120
127
  end
121
128
 
122
129
  def routes
@@ -129,12 +136,12 @@ module Swaggard
129
136
  def parse_models
130
137
  parser = Parsers::Models.new
131
138
 
132
- definitions =[]
139
+ definitions = {}
133
140
  configuration.models_paths.each do |path|
134
141
  Dir[path].each do |file|
135
142
  yard_objects = get_yard_objects(file)
136
143
 
137
- definitions.concat(parser.run(yard_objects))
144
+ definitions.merge!(parser.run(yard_objects))
138
145
  end
139
146
 
140
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.0.1
4
+ version: 1.2.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: 2019-07-31 00:00:00.000000000 Z
11
+ date: 2021-12-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -19,7 +19,7 @@ dependencies:
19
19
  version: '4.0'
20
20
  - - "<"
21
21
  - !ruby/object:Gem::Version
22
- version: '6.0'
22
+ version: '7.0'
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
@@ -29,7 +29,7 @@ dependencies:
29
29
  version: '4.0'
30
30
  - - "<"
31
31
  - !ruby/object:Gem::Version
32
- version: '6.0'
32
+ version: '7.0'
33
33
  - !ruby/object:Gem::Dependency
34
34
  name: sass-rails
35
35
  requirement: !ruby/object:Gem::Requirement
@@ -192,7 +192,6 @@ files:
192
192
  - spec/fixtures/dummy/config/application.rb
193
193
  - spec/fixtures/dummy/config/environments/development.rb
194
194
  - spec/fixtures/dummy/config/routes.rb
195
- - spec/fixtures/dummy/log/development.log
196
195
  - spec/fixtures/swagger_schema.json
197
196
  - spec/integration/swaggard_spec.rb
198
197
  - spec/spec_helper.rb
@@ -200,7 +199,7 @@ homepage: https://github.com/adrian-gomez/swaggard
200
199
  licenses:
201
200
  - MIT
202
201
  metadata: {}
203
- post_install_message:
202
+ post_install_message:
204
203
  rdoc_options: []
205
204
  require_paths:
206
205
  - lib
@@ -215,19 +214,18 @@ required_rubygems_version: !ruby/object:Gem::Requirement
215
214
  - !ruby/object:Gem::Version
216
215
  version: '0'
217
216
  requirements: []
218
- rubygems_version: 3.0.3
219
- signing_key:
217
+ rubygems_version: 3.2.15
218
+ signing_key:
220
219
  specification_version: 4
221
220
  summary: 'Swaggard: Swagger Rails REST API doc using yard YARD'
222
221
  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
222
+ - spec/fixtures/api.json
228
223
  - spec/fixtures/dummy/app/controllers/admin/pets_controller.rb
229
- - spec/fixtures/dummy/config/routes.rb
230
- - spec/fixtures/dummy/config/environments/development.rb
224
+ - spec/fixtures/dummy/app/controllers/application_controller.rb
225
+ - spec/fixtures/dummy/app/controllers/pets_controller.rb
231
226
  - spec/fixtures/dummy/config/application.rb
232
- - spec/fixtures/dummy/log/development.log
233
- - spec/fixtures/api.json
227
+ - spec/fixtures/dummy/config/environments/development.rb
228
+ - spec/fixtures/dummy/config/routes.rb
229
+ - spec/fixtures/swagger_schema.json
230
+ - spec/integration/swaggard_spec.rb
231
+ - spec/spec_helper.rb
File without changes