Skip to content

Commit

Permalink
Support for Grape 0.16.0. Closes ruby-grape#407.
Browse files Browse the repository at this point in the history
  • Loading branch information
dblock committed May 6, 2016
1 parent b7cdab9 commit 14a1677
Show file tree
Hide file tree
Showing 14 changed files with 70 additions and 48 deletions.
8 changes: 4 additions & 4 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ matrix:
env: GRAPE_VERSION=0.14.0
- rvm: 2.3.1
env: GRAPE_VERSION=0.15.0
# - rvm: 2.3.1
# env: GRAPE_VERSION=0.16.0
# - rvm: 2.3.1
# env: GRAPE_VERSION=HEAD
- rvm: 2.3.1
env: GRAPE_VERSION=0.16.0
- rvm: 2.3.1
env: GRAPE_VERSION=HEAD
- rvm: 2.3.0
- rvm: 2.2.5
- rvm: 2.1
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
* [#408](https://github.com/ruby-grape/grape-swagger/pull/408): Added support for `HEAD` endpoints - [@Bugagazavr](https://github.com/Bugagazavr).
* [#408](https://github.com/ruby-grape/grape-swagger/pull/411): Added support for `OPTIONS` endpoints - [@Bugagazavr](https://github.com/Bugagazavr).
* [#407](https://github.com/ruby-grape/grape-swagger/issues/407): Added support for Grape 0.15.x - [@dblock](https://github.com/dblock).
* [#407](https://github.com/ruby-grape/grape-swagger/issues/407): Added support for Grape 0.15.x and 0.16.x - [@dblock](https://github.com/dblock).
* Your contribution here.

#### Fixes
Expand Down
2 changes: 1 addition & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ source 'http://rubygems.org'

gemspec

case version = ENV['GRAPE_VERSION'] || '~> 0.15.0'
case version = ENV['GRAPE_VERSION'] || '~> 0.16.2'
when 'HEAD'
gem 'grape', github: 'ruby-grape/grape'
else
Expand Down
2 changes: 1 addition & 1 deletion grape-swagger.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Gem::Specification.new do |s|
s.summary = 'A simple way to add auto generated documentation to your Grape API that can be displayed with Swagger.'
s.license = 'MIT'

s.add_runtime_dependency 'grape', ['>= 0.12.0', '< 0.16.0']
s.add_runtime_dependency 'grape', '>= 0.12.0'
s.add_runtime_dependency 'grape-entity'
s.add_runtime_dependency 'awesome_print'

Expand Down
15 changes: 9 additions & 6 deletions lib/grape-swagger.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
require 'grape'

require 'grape-swagger/grape/route'

require 'grape-swagger/version'
require 'grape-swagger/endpoint'
require 'grape-swagger/errors'
Expand Down Expand Up @@ -48,16 +51,16 @@ def version_for(options)

def combine_routes(app, doc_klass)
app.routes.each do |route|
route_path = route.route_path
route_match = route_path.split(/^.*?#{route.route_prefix.to_s}/).last
route_path = route.path
route_match = route_path.split(/^.*?#{route.prefix.to_s}/).last
next unless route_match
route_match = route_match.match('\/([\w|-]*?)[\.\/\(]') || route_match.match('\/([\w|-]*)$')
next unless route_match
resource = route_match.captures.first
next if resource.empty?
resource.downcase!
@target_class.combined_routes[resource] ||= []
next if doc_klass.hide_documentation_path && route.route_path.match(/#{doc_klass.mount_path}($|\/|\(\.)/)
next if doc_klass.hide_documentation_path && route.path.match(/#{doc_klass.mount_path}($|\/|\(\.)/)
@target_class.combined_routes[resource] << route
end
end
Expand Down Expand Up @@ -141,10 +144,10 @@ def route_instance_variable_equals?(route, name)
end

def route_path_start_with?(route, name)
route_prefix = route.route_prefix ? "/#{route.route_prefix}/#{name}" : "/#{name}"
route_versioned_prefix = route.route_prefix ? "/#{route.route_prefix}/:version/#{name}" : "/:version/#{name}"
route_prefix = route.prefix ? "/#{route.prefix}/#{name}" : "/#{name}"
route_versioned_prefix = route.prefix ? "/#{route.prefix}/:version/#{name}" : "/:version/#{name}"

route.route_path.start_with?(route_prefix, route_versioned_prefix)
route.path.start_with?(route_prefix, route_versioned_prefix)
end

def standalone_sub_namespaces(name, namespaces)
Expand Down
6 changes: 3 additions & 3 deletions lib/grape-swagger/doc_methods/extensions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ class Extensions
class << self
def add(path, definitions, route)
@route = route
description = route.route_settings[:description]
description = route.settings[:description]
add_extension_to(path[method], extension(description)) if description && extended?(description, :x)

settings = route.route_settings
settings = route.settings
add_extensions_to_path(settings, path) if settings && extended?(settings, :x_path)
add_extensions_to_definition(settings, path, definitions) if settings && extended?(settings, :x_def)
end
Expand Down Expand Up @@ -67,7 +67,7 @@ def extension(part, identifier = :x)
end

def method
@route.route_method.downcase.to_sym
@route.request_method.downcase.to_sym
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/grape-swagger/doc_methods/headers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module DocMethods
class Headers
class << self
def parse(route)
route.route_headers.to_a.map do |route_header|
route.headers.to_a.map do |route_header|
route_header.tap do |header|
hash = header[1]
description = hash.delete('description')
Expand Down
2 changes: 1 addition & 1 deletion lib/grape-swagger/doc_methods/operation_id.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module DocMethods
class OperationId
class << self
def build(route, path = nil)
verb = route.route_method.to_s.downcase
verb = route.request_method.to_s.downcase

operation = manipulate(path) unless path.nil?

Expand Down
4 changes: 2 additions & 2 deletions lib/grape-swagger/doc_methods/parse_params.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ module DocMethods
class ParseParams
class << self
def call(param, settings, route)
path = route.route_path
method = route.route_method
path = route.path
method = route.request_method

data_type = GrapeSwagger::DocMethods::DataType.call(settings)
additional_documentation = settings[:documentation]
Expand Down
2 changes: 1 addition & 1 deletion lib/grape-swagger/doc_methods/tag_name_description.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ def build(options = {})
namespace_routes = target_class.combined_namespace_routes

namespace_routes.keys.map do |local_route|
next if namespace_routes[local_route].map(&:route_hidden).all? { |value| value.respond_to?(:call) ? value.call : value }
next if namespace_routes[local_route].map { |route| route.options[:hidden] }.all? { |value| value.respond_to?(:call) ? value.call : value }

original_namespace_name = target_class.combined_namespace_identifiers.key?(local_route) ? target_class.combined_namespace_identifiers[local_route] : local_route
description = namespaces[original_namespace_name] && namespaces[original_namespace_name].options[:desc]
Expand Down
50 changes: 24 additions & 26 deletions lib/grape-swagger/endpoint.rb
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,8 @@ def path_item(routes, options)
routes.each do |route|
next if hidden?(route)

@item, path = GrapeSwagger::DocMethods::PathString.build(route.route_path, options)
@entity = route.route_entity || route.route_success
@item, path = GrapeSwagger::DocMethods::PathString.build(route.path, options)
@entity = route.entity || route.options[:success]

verb, method_object = method_object(route, options, path)

Expand All @@ -112,14 +112,14 @@ def method_object(route, options, path)
method[:operationId] = GrapeSwagger::DocMethods::OperationId.build(route, path)
method.delete_if { |_, value| value.blank? }

[route.route_method.downcase.to_sym, method]
[route.request_method.downcase.to_sym, method]
end

def description_object(route, markdown)
description = route.route_desc if route.route_desc.present?
description = route.route_description if route.route_description.present?
description = route.options[:desc] if route.options.key?(:desc)
description = route.description if route.description.present?
description = "# #{description} " if markdown
description += "\n #{route.route_detail}" if route.route_detail
description += "\n #{route.options[:detail]}" if route.options.key?(:detail)
description = markdown.markdown(description.to_s).chomp if markdown

description
Expand All @@ -128,17 +128,17 @@ def description_object(route, markdown)
def produces_object(route, format)
mime_types = GrapeSwagger::DocMethods::ProducesConsumes.call(format)

route_mime_types = [:route_formats, :route_content_types, :route_produces].map do |producer|
possible = route.send(producer)
route_mime_types = [:formats, :content_types, :produces].map do |producer|
possible = route.options[producer]
GrapeSwagger::DocMethods::ProducesConsumes.call(possible) if possible.present?
end.flatten.compact.uniq

route_mime_types.present? ? route_mime_types : mime_types
end

def consumes_object(route, format)
method = route.route_method.downcase.to_sym
format = route.route_settings[:description][:consumes] if route.route_settings[:description] && route.route_settings[:description][:consumes]
method = route.request_method.downcase.to_sym
format = route.settings[:description][:consumes] if route.settings[:description] && route.settings[:description][:consumes]
mime_types = GrapeSwagger::DocMethods::ProducesConsumes.call(format) if [:post, :put].include?(method)

mime_types
Expand All @@ -153,11 +153,11 @@ def params_object(route)
end

def response_object(route, markdown)
default_code = GrapeSwagger::DocMethods::StatusCodes.get[route.route_method.downcase.to_sym]
default_code = GrapeSwagger::DocMethods::StatusCodes.get[route.request_method.downcase.to_sym]
default_code[:model] = @entity if @entity
default_code[:message] = route.route_description || default_code[:message].sub('{item}', @item)
default_code[:message] = route.description || default_code[:message].sub('{item}', @item)

codes = [default_code] + (route.route_http_codes || route.route_failure || [])
codes = [default_code] + (route.http_codes || route.options[:failure] || [])
codes.map! { |x| x.is_a?(Array) ? { code: x[0], message: x[1], model: x[2] } : x }

codes.each_with_object({}) do |value, memo|
Expand All @@ -166,7 +166,7 @@ def response_object(route, markdown)
response_model = @item
response_model = expose_params_from_model(value[:model]) if value[:model]

if memo.key?(200) && route.route_method == 'DELETE' && value[:model].nil?
if memo.key?(200) && route.request_method == 'DELETE' && value[:model].nil?
memo[204] = memo.delete(200)
value[:code] = 204
end
Expand All @@ -177,7 +177,7 @@ def response_object(route, markdown)

@definitions[response_model][:description] = description_object(route, markdown)
# TODO: proof that the definition exist, if model isn't specified
memo[value[:code]][:schema] = if route.route_is_array
memo[value[:code]][:schema] = if route.options[:is_array]
{ 'type' => 'array', 'items' => { '$ref' => "#/definitions/#{response_model}" } }
else
{ '$ref' => "#/definitions/#{response_model}" }
Expand All @@ -186,23 +186,23 @@ def response_object(route, markdown)
end

def tag_object(route, version)
Array(route.route_path.split('{')[0].split('/').reject(&:empty?).delete_if { |i| ((i == route.route_prefix.to_s) || (i == version)) }.first)
Array(route.path.split('{')[0].split('/').reject(&:empty?).delete_if { |i| ((i == route.prefix.to_s) || (i == version)) }.first)
end

private

def partition_params(route)
declared_params = route.route_settings[:declared_params] if route.route_settings[:declared_params].present?
required, exposed = route.route_params.partition { |x| x.first.is_a? String }
required.concat GrapeSwagger::DocMethods::Headers.parse(route) unless route.route_headers.nil?
declared_params = route.settings[:declared_params] if route.settings[:declared_params].present?
required, exposed = route.params.partition { |x| x.first.is_a? String }
required.concat GrapeSwagger::DocMethods::Headers.parse(route) unless route.headers.nil?
default_type(required)
default_type(exposed)

unless declared_params.nil? && route.route_headers.nil?
unless declared_params.nil? && route.headers.nil?
request_params = parse_request_params(required)
end

return route.route_params if route.route_params.present? && !route.route_settings[:declared_params].present?
return route.params if route.params.present? && !route.settings[:declared_params].present?
request_params || {}
end

Expand Down Expand Up @@ -295,11 +295,9 @@ def could_it_be_a_model?(value)
end

def hidden?(route)
if route.route_hidden
return route.route_hidden.is_a?(Proc) ? route.route_hidden.call : route.route_hidden
end

false
route_hidden = route.options[:hidden]
route_hidden = route_hidden.call if route_hidden.is_a?(Proc)
route_hidden
end
end
end
16 changes: 16 additions & 0 deletions lib/grape-swagger/grape/route.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# backwards compatibility for Grape < 0.16.0
module Grape
class Route
[:path, :prefix, :entity, :description, :settings, :params, :headers, :http_codes].each do |m|
define_method m do
send "route_#{m}"
end
end

def request_method
route_method
end

attr_reader :options
end
end if defined?(Grape::VERSION) && Gem::Version.new(::Grape::VERSION) < Gem::Version.new('0.16.0')
6 changes: 5 additions & 1 deletion spec/lib/operation_id_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@
specify { expect(subject).to respond_to :build }

describe 'build' do
let(:route) { Grape::Route.new(method: method) }
if defined?(Grape::VERSION) && Gem::Version.new(::Grape::VERSION) < Gem::Version.new('0.16.0')
let(:route) { Grape::Route.new(method: method) }
else
let(:route) { Grape::Router::Route.new(method, '/path', requirements: {}) }
end

describe 'GET' do
let(:method) { 'GET' }
Expand Down
2 changes: 1 addition & 1 deletion spec/swagger_v2/api_swagger_v2_request_params_fix_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def app
end

specify do
expect(subject['paths']['/bookings/{id}']['put']['parameters']).to eql(
expect(subject['paths']['/bookings/{id}']['put']['parameters'].sort_by { |p| p['name'] }).to eql(
[
{ 'in' => 'path', 'name' => 'id', 'type' => 'integer', 'format' => 'int32', 'required' => true },
{ 'in' => 'formData', 'name' => 'name', 'type' => 'string', 'required' => false }
Expand Down

0 comments on commit 14a1677

Please sign in to comment.