Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

accepting proc for options #441

Merged
merged 1 commit into from
May 28, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand All @@ -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

Expand Down
128 changes: 67 additions & 61 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
* [Swagger-Spec](#swagger-spec)
* [Installation](#install)
* [Usage](#usage)
* [Model Parsers](#model_parsers)
* [Configure](#configure)
* [Routes Configuration](#routes)
* [Markdown](#md_usage)
Expand All @@ -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`.

<a name="related" />
## Related Projects

Expand Down Expand Up @@ -114,8 +55,9 @@ grape-swagger | swagger spec | grape | grape-entity | represen
<a name="swagger-spec" />
## 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).

<!-- validating: http://bigstickcarpet.com/swagger-parser/www/index.html -->

<a name="install" />
## Installation
Expand Down Expand Up @@ -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).


<a name="model_parsers" />
## 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.
Expand Down Expand Up @@ -216,6 +220,8 @@ add_swagger_documentation \
base_path: '/super/api'
```

`host` and `base_path` are also accepting a `proc` or `lambda`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make it into a full sentence, like "The host and base_path options also accept ..".

Copy link
Contributor

@Lordnibbler Lordnibbler May 29, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In addition to that, I think providing some trivial usage example would be super helpful in the docs here:

add_swagger_documentation(
  base_path: proc { |foo| foo.host =~ /^example/ ? '/api-example' : '/api' }
)


<a name="mount_path" />
#### mount_path:
The path where the API documentation is loaded, default is: `/swagger_doc`.
Expand Down
16 changes: 14 additions & 2 deletions lib/grape-swagger/doc_methods/optional_object.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not in love with this. IMO this should be an API that takes a fixed number of parameters. Maybe we should also make the parameter a hash right away so that we can pass more than just the request in here?

end

def default_values
{
host: 'host_with_port',
base_path: 'script_name'
}
end
end
end
end
Expand Down
4 changes: 2 additions & 2 deletions lib/grape-swagger/endpoint.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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? }
Expand Down
26 changes: 15 additions & 11 deletions spec/lib/optional_object_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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