From 6fa6c111fad9616321e89db7831306dac07c009e Mon Sep 17 00:00:00 2001 From: LeFnord Date: Sat, 28 May 2016 03:30:53 +0200 Subject: [PATCH] adds handling of `proc`s for optional object updates readme and changelog --- .travis.yml | 4 +- CHANGELOG.md | 2 +- README.md | 128 +++++++++--------- .../doc_methods/optional_object.rb | 16 ++- lib/grape-swagger/endpoint.rb | 4 +- spec/lib/optional_object_spec.rb | 26 ++-- 6 files changed, 101 insertions(+), 79 deletions(-) diff --git a/.travis.yml b/.travis.yml index afb1878b..ed3c19bd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,8 +21,8 @@ matrix: - rvm: 2.3.1 env: GRAPE_VERSION=HEAD - rvm: 2.3.0 - - rvm: 2.2.5 - - rvm: 2.1.7 + - rvm: 2.2 + - rvm: 2.1 - rvm: ruby-head - rvm: rbx-2 - rvm: jruby-9.0.5.0 diff --git a/CHANGELOG.md b/CHANGELOG.md index 773f8015..017e3f1b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ #### Features +* [#441](https://github.com/ruby-grape/grape-swagger/pull/441): Accepting `String`, `lambda` and `proc` for `host` and `base_path` - [@LeFnord](https://github.com/LeFnord). * [#434](https://github.com/ruby-grape/grape-swagger/pull/434): Add summary to the operation object generator to be more compliant with [OpenAPI v2](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#operation-object) - [@aschuster3](https://github.com/aschuster3). * Your contribution here. @@ -14,7 +15,6 @@ #### Features * [#413](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). -* Your contribution here. #### Fixes diff --git a/README.md b/README.md index c8d8243c..271024ec 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ * [Swagger-Spec](#swagger-spec) * [Installation](#install) * [Usage](#usage) +* [Model Parsers](#model_parsers) * [Configure](#configure) * [Routes Configuration](#routes) * [Markdown](#md_usage) @@ -27,66 +28,6 @@ The grape-swagger gem provides an autogenerated documentation for your [Grape](h These screenshot is based on the [Hussars](https://github.com/LeFnord/hussars) sample app. -## Model Parsers - -Since 0.20.4, `Grape::Entity` is not a part of grape-swagger, you need to add `grape-swagger-entity` manually to your Gemfile. -Also added support for [representable](https://github.com/apotonick/representable) via `grape-swagger-representable`. - -```ruby -# For Grape::Entity ( https://github.com/ruby-grape/grape-entity ) -gem 'grape-swagger-entity' -# For representable ( https://github.com/apotonick/representable ) -gem 'grape-swagger-representable' -``` - -If you are not using Rails, make sure to load the parser inside your application initialization logic, e.g., via `require 'grape-swagger/entity'` or `require 'grape-swagger/representable`. - -##### Custom Model Parsers - -You can create your own model parser, for example for [roar](https://github.com/apotonick/roar). - -```rb -module GrapeSwagger - module Roar - class Parser - attr_reader :model - attr_reader :endpoint - - def initialize(model, endpoint) - @model = model - @endpoint = endpoint - end - - def call - # Parse your model and return hash with model schema for swagger - end - end - end -end -``` - -Then you should register your custom parser. - -```rb -GrapeSwagger.model_parsers.register(GrapeSwagger::Roar::Parser, Roar::Decorator) -``` - -To control model parsers sequence, you can insert your parser before or after another parser. - -#### insert_before - -```rb -GrapeSwagger.model_parsers.insert_before(GrapeSwagger::Representable::Parser, GrapeSwagger::Roar::Parser, Roar::Decorator) -``` - -#### insert_after - -```rb -GrapeSwagger.model_parsers.insert_after(GrapeSwagger::Roar::Parser, GrapeSwagger::Representable::Parser, Representable::Decorator) -``` - -As we know, `Roar::Decorator` uses as superclass `Representable::Decorator`, this allow to avoid problem when Roar objects will be processed by `GrapeSwagger::Representable::Parser`, instead `GrapeSwagger::Roar::Parser`. - ## Related Projects @@ -114,8 +55,9 @@ grape-swagger | swagger spec | grape | grape-entity | represen ## Swagger-Spec -Grape-swagger generates documentation per [Swagger Spec 2.0](https://github.com/swagger-api/swagger-spec/blob/master/versions/2.0.md). +Grape-swagger generates documentation per [Swagger / OpenAPI Spec 2.0](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md). + ## Installation @@ -154,6 +96,68 @@ end To explore your API, either download [Swagger UI](https://github.com/wordnik/swagger-ui) and set it up yourself or go to the [online swagger demo](http://petstore.swagger.wordnik.com/) and enter your localhost url documentation root in the url field (probably something in the line of http://localhost:3000/swagger_doc). + +## Model Parsers + +Since 0.20.4, `Grape::Entity` is not a part of grape-swagger, you need to add `grape-swagger-entity` manually to your Gemfile. +Also added support for [representable](https://github.com/apotonick/representable) via `grape-swagger-representable`. + +```ruby +# For Grape::Entity ( https://github.com/ruby-grape/grape-entity ) +gem 'grape-swagger-entity' +# For representable ( https://github.com/apotonick/representable ) +gem 'grape-swagger-representable' +``` + +If you are not using Rails, make sure to load the parser inside your application initialization logic, e.g., via `require 'grape-swagger/entity'` or `require 'grape-swagger/representable`. + +### Custom Model Parsers + +You can create your own model parser, for example for [roar](https://github.com/apotonick/roar). + +```rb +module GrapeSwagger + module Roar + class Parser + attr_reader :model + attr_reader :endpoint + + def initialize(model, endpoint) + @model = model + @endpoint = endpoint + end + + def call + # Parse your model and return hash with model schema for swagger + end + end + end +end +``` + +Then you should register your custom parser. + +```rb +GrapeSwagger.model_parsers.register(GrapeSwagger::Roar::Parser, Roar::Decorator) +``` + +To control model parsers sequence, you can insert your parser before or after another parser. + +#### insert_before + +```rb +GrapeSwagger.model_parsers.insert_before(GrapeSwagger::Representable::Parser, GrapeSwagger::Roar::Parser, Roar::Decorator) +``` + +#### insert_after + +```rb +GrapeSwagger.model_parsers.insert_after(GrapeSwagger::Roar::Parser, GrapeSwagger::Representable::Parser, Representable::Decorator) +``` + +As we know, `Roar::Decorator` uses as superclass `Representable::Decorator`, this allow to avoid problem when Roar objects will be processed by `GrapeSwagger::Representable::Parser`, instead `GrapeSwagger::Roar::Parser`. + + ### CORS If you use the online demo, make sure your API supports foreign requests by enabling CORS in Grape, otherwise you'll see the API description, but requests on the API won't return. Use [rack-cors](https://github.com/cyu/rack-cors) to enable CORS. @@ -216,6 +220,8 @@ add_swagger_documentation \ base_path: '/super/api' ``` +`host` and `base_path` are also accepting a `proc` or `lambda` + #### mount_path: The path where the API documentation is loaded, default is: `/swagger_doc`. diff --git a/lib/grape-swagger/doc_methods/optional_object.rb b/lib/grape-swagger/doc_methods/optional_object.rb index c146204a..6c0c92b3 100644 --- a/lib/grape-swagger/doc_methods/optional_object.rb +++ b/lib/grape-swagger/doc_methods/optional_object.rb @@ -4,11 +4,23 @@ class OptionalObject class << self def build(key, options, request = nil) if options[key] - options[key].is_a?(Proc) ? options[key].call : options[key] + return evaluate(key, options, request) if options[key].is_a?(Proc) + options[key] else - request + request.send(default_values[key]) end end + + def evaluate(key, options, request) + options[key].arity == 0 ? options[key].call : options[key].call(request) + end + + def default_values + { + host: 'host_with_port', + base_path: 'script_name' + } + end end end end diff --git a/lib/grape-swagger/endpoint.rb b/lib/grape-swagger/endpoint.rb index 82bd437e..5b05b9c0 100644 --- a/lib/grape-swagger/endpoint.rb +++ b/lib/grape-swagger/endpoint.rb @@ -24,8 +24,8 @@ def swagger_object(target_class, request, options) swagger: '2.0', produces: content_types_for(target_class), authorizations: options[:authorizations], - host: GrapeSwagger::DocMethods::OptionalObject.build(:host, options, request.host_with_port), - basePath: GrapeSwagger::DocMethods::OptionalObject.build(:base_path, options, request.env['SCRIPT_NAME']), + host: GrapeSwagger::DocMethods::OptionalObject.build(:host, options, request), + basePath: GrapeSwagger::DocMethods::OptionalObject.build(:base_path, options, request), tags: GrapeSwagger::DocMethods::TagNameDescription.build(options), schemes: options[:schemes].is_a?(String) ? [options[:schemes]] : options[:schemes] }.delete_if { |_, value| value.blank? } diff --git a/spec/lib/optional_object_spec.rb b/spec/lib/optional_object_spec.rb index f9ba35e3..9ecf79ac 100644 --- a/spec/lib/optional_object_spec.rb +++ b/spec/lib/optional_object_spec.rb @@ -7,33 +7,37 @@ specify { expect(subject).to respond_to :build } describe 'build' do - let(:key) { :bar } - let(:request) { 'somes/request/string' } + let(:key) { :host } + let!(:request) { Rack::Request.new(Rack::MockRequest.env_for('http://example.com:8080/')) } - describe 'no option given for key' do + describe 'no option given for host, take from request' do let(:options) { { foo: 'foo' } } specify do - expect(subject.build(key, options)).to be_nil - expect(subject.build(key, options, request)).to eql request + expect(subject.build(key, options, request)).to eql request.host_with_port end end - let(:value) { 'some optional value' } + let(:value) { 'grape-swagger.example.com' } describe 'option is a string' do - let(:options) { { bar: value } } + let(:options) { { host: value } } specify do - expect(subject.build(key, options)).to eql value expect(subject.build(key, options, request)).to eql value end end - describe 'option is a proc' do - let(:options) { { bar: -> { value } } } + describe 'option is a lambda' do + let(:options) { { host: -> { value } } } specify do - expect(subject.build(key, options)).to eql value expect(subject.build(key, options, request)).to eql value end end + + describe 'option is a proc' do + let(:options) { { host: proc { |foo| foo.host =~ /^example/ ? '/api-example' : '/api' } } } + specify do + expect(subject.build(key, options, request)).to eql '/api-example' + end + end end end