Skip to content

Commit ce94702

Browse files
committed
Move model parsing logic outside grape-swagger
1 parent 3ee3bf1 commit ce94702

11 files changed

+178
-269
lines changed

.rubocop_todo.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,12 @@ Lint/UselessAssignment:
2323

2424
# Offense count: 27
2525
Metrics/AbcSize:
26-
Max: 68
26+
Max: 69
2727

2828
# Offense count: 3
2929
# Configuration parameters: CountComments.
3030
Metrics/ClassLength:
31-
Max: 234
31+
Max: 284
3232

3333
# Offense count: 11
3434
Metrics/CyclomaticComplexity:

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
* [#405](https://github.com/ruby-grape/grape-swagger/pull/405), [#403](https://github.com/ruby-grape/grape-swagger/issues/403): Added version support matrix - [@LeFnord](https://github.com/LeFnord).
88
* [#408](https://github.com/ruby-grape/grape-swagger/pull/408): Added support for `HEAD` endpoints - [@Bugagazavr](https://github.com/Bugagazavr).
99
* [#408](https://github.com/ruby-grape/grape-swagger/pull/411): Added support for `OPTIONS` endpoints - [@Bugagazavr](https://github.com/Bugagazavr).
10+
* [#411](https://github.com/ruby-grape/grape-swagger/pull/413): Move all model parsing logic to separate gems `grape-swagger-entity` and added representable parser `grape-swagger` - [@Bugagazavr](https://github.com/Bugagazavr)
1011
* Your contribution here.
1112

1213
#### Fixes

Gemfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,5 @@ when 'HEAD'
88
else
99
gem 'grape', version
1010
end
11+
12+
gem 'grape-swagger-entity', github: 'bugagazavr/grape-swagger-entity'

README.md

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,24 @@ The grape-swagger gem provides an autogenerated documentation for your [Grape](h
2727

2828
These screenshot is based on the [Hussars](https://github.com/LeFnord/hussars) sample app.
2929

30+
## Model Parsers
31+
32+
Since 0.21.0, `Grape::Entity` is not a part of grape-swagger, you need to add `grape-swagger-entity` manually to your Gemfile:
33+
34+
```ruby
35+
# For Grape::Entity ( https://github.com/ruby-grape/grape-entity )
36+
gem 'grape-swagger-entity'
37+
# For representable ( https://github.com/apotonick/representable )
38+
gem 'grape-swagger-representable'
39+
```
3040

3141
<a name="related" />
3242
## Related Projects
3343

3444
* [Grape](https://github.com/ruby-grape/grape)
35-
* [Grape Entity](https://github.com/ruby-grape/grape-entity)
45+
* [Grape Swagger Entity](https://github.com/ruby-grape/grape-swagger-entity)
46+
* [Grape Entity](https://github.com/ruby-grape/grape-entity)
47+
* [Grape Swagger Representable](https://github.com/ruby-grape/grape-swagger-representable)
3648
* [Swagger UI](https://github.com/wordnik/swagger-ui)
3749

3850

@@ -46,7 +58,7 @@ grape-swagger | swagger spec | grape | grape-entity
4658
0.10.5 | 1.2 | >= 0.10.0 ... <= 0.14.0 | < 0.5.0
4759
0.11.0 | 1.2 | >= 0.16.2 | < 0.5.0
4860
0.20.1 | 2.0 | >= 0.12.0 ... <= 0.14.0 | <= 0.5.1
49-
0.20.3 (next) | 2.0 | >= 0.12.0 ... <= 0.16.2 | <= 0.5.1
61+
0.21.0 (next) | 2.0 | >= 0.12.0 ... <= 0.16.2 | <= 0.5.1
5062

5163
<a name="swagger-spec" />
5264
## Swagger-Spec

grape-swagger.gemspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ Gem::Specification.new do |s|
1212
s.license = 'MIT'
1313

1414
s.add_runtime_dependency 'grape', '>= 0.12.0'
15-
s.add_runtime_dependency 'grape-entity'
1615
s.add_runtime_dependency 'awesome_print'
1716

17+
s.add_development_dependency 'grape-entity'
1818
s.add_development_dependency 'rake'
1919
s.add_development_dependency 'shoulda'
2020
s.add_development_dependency 'rdoc'

lib/grape-swagger.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,21 @@
77
require 'grape-swagger/errors'
88

99
require 'grape-swagger/doc_methods'
10+
require 'grape-swagger/model_parsers'
1011

1112
require 'grape-swagger/markdown/kramdown_adapter'
1213
require 'grape-swagger/markdown/redcarpet_adapter'
1314

1415
require 'awesome_print'
1516

17+
module GrapeSwagger
18+
class << self
19+
def model_parsers
20+
@model_parsers ||= GrapeSwagger::ModelParsers.new
21+
end
22+
end
23+
end
24+
1625
module Grape
1726
class API
1827
class << self

lib/grape-swagger/endpoint.rb

Lines changed: 9 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -226,55 +226,20 @@ def parse_request_params(required)
226226
end
227227
end
228228

229-
def parse_response_params(params)
230-
return if params.nil?
231-
232-
params.each_with_object({}) do |x, memo|
233-
next if x[1].fetch(:documentation, {}).fetch(:in, nil).to_s == 'header'
234-
x[0] = x.last[:as] if x.last[:as]
235-
236-
model = x.last[:using] if x.last[:using].present?
237-
model ||= x.last[:documentation][:type] if x.last[:documentation] && could_it_be_a_model?(x.last[:documentation])
238-
239-
if model
240-
name = expose_params_from_model(model)
241-
memo[x.first] = if x.last[:documentation] && x.last[:documentation][:is_array]
242-
{ 'type' => 'array', 'items' => { '$ref' => "#/definitions/#{name}" } }
243-
else
244-
{ '$ref' => "#/definitions/#{name}" }
245-
end
246-
else
247-
documented_type = x.last[:type]
248-
documented_type ||= x.last[:documentation][:type] if x.last[:documentation]
249-
data_type = GrapeSwagger::DocMethods::DataType.call(documented_type)
250-
251-
if GrapeSwagger::DocMethods::DataType.primitive?(data_type)
252-
data = GrapeSwagger::DocMethods::DataType.mapping(data_type)
253-
memo[x.first] = { type: data.first, format: data.last }
254-
else
255-
memo[x.first] = { type: data_type }
256-
end
257-
258-
memo[x.first][:enum] = x.last[:values] if x.last[:values] && x.last[:values].is_a?(Array)
259-
end
260-
memo[x.first][:description] = x.last[:documentation][:desc] if x.last[:documentation] && x.last[:documentation][:desc]
261-
end
262-
end
263-
264229
def expose_params_from_model(model)
265230
model_name = model_name(model)
266231

267-
# TODO: this should only be a temporary hack ;)
268-
if GrapeEntity::VERSION =~ /0\.4\.\d/
269-
parameters = model.exposures ? model.exposures : model.documentation
270-
elsif GrapeEntity::VERSION =~ /0\.5\.\d/
271-
parameters = model.root_exposures.each_with_object({}) do |value, memo|
272-
memo[value.attribute] = value.send(:options)
273-
end
232+
properties = {}
233+
GrapeSwagger.model_parsers.each do |klass, ancestor|
234+
next unless model.ancestors.map(&:to_s).include?(ancestor)
235+
236+
model_class = klass.new(model, self)
237+
properties = model_class.call
238+
239+
break
274240
end
275-
properties = parse_response_params(parameters)
276241

277-
@definitions[model_name] = { type: 'object', properties: properties }
242+
@definitions[model_name] = { type: 'object', properties: properties || {} }
278243

279244
model_name
280245
end
@@ -283,17 +248,6 @@ def model_name(name)
283248
name.respond_to?(:name) ? name.name.demodulize.camelize : name.split('::').last
284249
end
285250

286-
def could_it_be_a_model?(value)
287-
(
288-
value[:type].to_s.include?('Entity') || value[:type].to_s.include?('Entities')
289-
) || (
290-
value[:type] &&
291-
value[:type].is_a?(Class) &&
292-
!GrapeSwagger::DocMethods::DataType.primitive?(value[:type].name.downcase) &&
293-
!value[:type] == Array
294-
)
295-
end
296-
297251
def hidden?(route)
298252
route_hidden = route.options[:hidden]
299253
route_hidden = route_hidden.call if route_hidden.is_a?(Proc)

lib/grape-swagger/model_parsers.rb

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
module GrapeSwagger
2+
class ModelParsers
3+
include Enumerable
4+
5+
def initialize
6+
@parsers = {}
7+
end
8+
9+
def register(klass, ancestor)
10+
@parsers[klass] = ancestor.to_s
11+
end
12+
13+
def insert_before(before_klass, klass, ancestor)
14+
subhash = @parsers.except(klass).to_a
15+
insert_at = subhash.index(subhash.assoc(before_klass))
16+
insert_at = subhash.length - 1 if insert_at.nil?
17+
@parsers = Hash[subhash.insert(insert_at, [klass, ancestor])]
18+
end
19+
20+
def insert_after(after_klass, klass, ancestor)
21+
subhash = @parsers.except(klass).to_a
22+
insert_at = subhash.index(subhash.assoc(after_klass))
23+
insert_at = subhash.length - 1 if insert_at.nil?
24+
@parsers = Hash[subhash.insert(insert_at + 1, [klass, ancestor])]
25+
end
26+
27+
def each
28+
@parsers.each_pair do |klass, ancestor|
29+
yield klass, ancestor
30+
end
31+
end
32+
end
33+
end

spec/lib/model_parsers_spec.rb

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
require 'spec_helper'
2+
3+
describe GrapeSwagger::ModelParsers do
4+
let(:model_parsers) { described_class.new }
5+
6+
before do
7+
class SomeModelParser; end
8+
class SomeModelParser2; end
9+
class SomeModelParser3; end
10+
end
11+
12+
describe '#register' do
13+
describe 'successfully register new parser' do
14+
before do
15+
model_parsers.register(SomeModelParser, Class)
16+
end
17+
18+
specify do
19+
expect(model_parsers.to_a).to eq([[SomeModelParser, 'Class']])
20+
end
21+
end
22+
23+
describe 'should be empty if no registred parsers' do
24+
specify do
25+
expect(model_parsers.to_a).to be_empty
26+
end
27+
end
28+
end
29+
30+
describe '#insert_before' do
31+
describe 'SomeModelParser2 should be first parser' do
32+
before do
33+
model_parsers.register(SomeModelParser, Class)
34+
model_parsers.register(SomeModelParser3, Class)
35+
model_parsers.insert_before(SomeModelParser, SomeModelParser2, Class)
36+
end
37+
38+
specify do
39+
expect(model_parsers.count).to eq(3)
40+
expect(model_parsers.to_a.first).to eq([SomeModelParser2, Class])
41+
end
42+
end
43+
44+
describe 'SomeModelParser2 should be inserted anyway if SomeModelParser not registred' do
45+
before do
46+
model_parsers.register(SomeModelParser3, Class)
47+
model_parsers.insert_before(SomeModelParser, SomeModelParser2, Class)
48+
end
49+
50+
specify do
51+
expect(model_parsers.count).to eq(2)
52+
expect(model_parsers.to_a).to include([SomeModelParser2, Class])
53+
end
54+
end
55+
56+
describe 'SomeModelParser2 should be inserted anyway if model parsers is empty' do
57+
before do
58+
model_parsers.insert_before(SomeModelParser, SomeModelParser2, Class)
59+
end
60+
61+
specify do
62+
expect(model_parsers.count).to eq(1)
63+
expect(model_parsers.to_a).to include([SomeModelParser2, Class])
64+
end
65+
end
66+
end
67+
68+
describe '#insert_after' do
69+
describe 'SomeModelParser2 should be second parser' do
70+
before do
71+
model_parsers.register(SomeModelParser, Class)
72+
model_parsers.register(SomeModelParser3, Class)
73+
model_parsers.insert_after(SomeModelParser, SomeModelParser2, Class)
74+
end
75+
76+
specify do
77+
expect(model_parsers.count).to eq(3)
78+
expect(model_parsers.to_a[1]).to eq([SomeModelParser2, Class])
79+
end
80+
end
81+
82+
describe 'SomeModelParser2 should be inserted anyway if SomeModelParser not registred' do
83+
before do
84+
model_parsers.register(SomeModelParser3, Class)
85+
model_parsers.insert_after(SomeModelParser, SomeModelParser2, Class)
86+
end
87+
88+
specify do
89+
expect(model_parsers.count).to eq(2)
90+
expect(model_parsers.to_a).to include([SomeModelParser2, Class])
91+
end
92+
end
93+
94+
describe 'SomeModelParser2 should be inserted anyway if model parsers is empty' do
95+
before do
96+
model_parsers.insert_after(SomeModelParser, SomeModelParser2, Class)
97+
end
98+
99+
specify do
100+
expect(model_parsers.count).to eq(1)
101+
expect(model_parsers.to_a).to include([SomeModelParser2, Class])
102+
end
103+
end
104+
end
105+
end

spec/spec_helper.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@
33
Dir[File.join(Dir.getwd, 'spec/support/**/*.rb')].each { |f| require f }
44

55
require 'grape'
6-
require 'grape-swagger'
76
require 'grape-entity'
7+
require 'grape-swagger'
8+
require 'grape-swagger/entity'
89

910
Bundler.setup :default, :test
1011

0 commit comments

Comments
 (0)