Merge pull request #2344 from xhh/ruby-doc

[Ruby] Add auto-generated documentation in Markdown to Ruby client
This commit is contained in:
wing328
2016-03-12 16:08:07 +08:00
37 changed files with 2647 additions and 1085 deletions

View File

@@ -3,15 +3,21 @@ package io.swagger.codegen.languages;
import io.swagger.codegen.CliOption;
import io.swagger.codegen.CodegenConfig;
import io.swagger.codegen.CodegenConstants;
import io.swagger.codegen.CodegenOperation;
import io.swagger.codegen.CodegenParameter;
import io.swagger.codegen.CodegenType;
import io.swagger.codegen.DefaultCodegen;
import io.swagger.codegen.SupportingFile;
import io.swagger.models.Model;
import io.swagger.models.Operation;
import io.swagger.models.Swagger;
import io.swagger.models.properties.*;
import java.io.File;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
@@ -40,6 +46,8 @@ public class RubyClientCodegen extends DefaultCodegen implements CodegenConfig {
protected String gemDescription = "This gem maps to a swagger API";
protected String gemAuthor = "";
protected String gemAuthorEmail = "";
protected String apiDocPath = "docs/";
protected String modelDocPath = "docs/";
protected static int emptyMethodNameCounter = 0;
@@ -50,6 +58,8 @@ public class RubyClientCodegen extends DefaultCodegen implements CodegenConfig {
outputFolder = "generated-code" + File.separator + "ruby";
modelTemplateFiles.put("model.mustache", ".rb");
apiTemplateFiles.put("api.mustache", ".rb");
modelDocTemplateFiles.put("model_doc.mustache", ".md");
apiDocTemplateFiles.put("api_doc.mustache", ".md");
embeddedTemplateDir = templateDir = "ruby";
modelTestTemplateFiles.put("model_test.mustache", ".rb");
@@ -194,6 +204,9 @@ public class RubyClientCodegen extends DefaultCodegen implements CodegenConfig {
setGemAuthorEmail((String) additionalProperties.get(GEM_AUTHOR_EMAIL));
}
// make api and model doc path available in mustache template
additionalProperties.put("apiDocPath", apiDocPath);
additionalProperties.put("modelDocPath", modelDocPath);
// use constant model/api package (folder path)
setModelPackage("models");
@@ -206,8 +219,39 @@ public class RubyClientCodegen extends DefaultCodegen implements CodegenConfig {
supportingFiles.add(new SupportingFile("api_error.mustache", gemFolder, "api_error.rb"));
supportingFiles.add(new SupportingFile("configuration.mustache", gemFolder, "configuration.rb"));
supportingFiles.add(new SupportingFile("version.mustache", gemFolder, "version.rb"));
supportingFiles.add(new SupportingFile("README.mustache", "", "README.md"));
supportingFiles.add(new SupportingFile("gitignore.mustache", "", ".gitignore"));
}
@Override
public CodegenOperation fromOperation(String path, String httpMethod, Operation operation, Map<String, Model> definitions, Swagger swagger) {
CodegenOperation op = super.fromOperation(path, httpMethod, operation, definitions, swagger);
// Set vendor-extension to be used in template:
// x-codegen-hasMoreRequired
// x-codegen-hasMoreOptional
// x-codegen-hasRequiredParams
CodegenParameter lastRequired = null;
CodegenParameter lastOptional = null;
for (CodegenParameter p : op.allParams) {
if (p.required != null && p.required) {
lastRequired = p;
} else {
lastOptional = p;
}
}
for (CodegenParameter p : op.allParams) {
if (p == lastRequired) {
p.vendorExtensions.put("x-codegen-hasMoreRequired", false);
} else if (p == lastOptional) {
p.vendorExtensions.put("x-codegen-hasMoreOptional", false);
} else {
p.vendorExtensions.put("x-codegen-hasMoreRequired", true);
p.vendorExtensions.put("x-codegen-hasMoreOptional", true);
}
}
op.vendorExtensions.put("x-codegen-hasRequiredParams", lastRequired != null);
return op;
}
@Override
public CodegenType getTag() {
@@ -271,6 +315,16 @@ public class RubyClientCodegen extends DefaultCodegen implements CodegenConfig {
return outputFolder + File.separator + specFolder + File.separator + modelPackage.replace("/", File.separator);
}
@Override
public String apiDocFileFolder() {
return (outputFolder + "/" + apiDocPath).replace('/', File.separatorChar);
}
@Override
public String modelDocFileFolder() {
return (outputFolder + "/" + modelDocPath).replace('/', File.separatorChar);
}
@Override
public String getTypeDeclaration(Property p) {
if (p instanceof ArrayProperty) {
@@ -328,7 +382,7 @@ public class RubyClientCodegen extends DefaultCodegen implements CodegenConfig {
String type = null;
if (typeMapping.containsKey(swaggerType)) {
type = typeMapping.get(swaggerType);
if (languageSpecificPrimitives.contains(type)) {
if (languageSpecificPrimitives.contains(type)) {
return type;
}
} else {
@@ -414,6 +468,11 @@ public class RubyClientCodegen extends DefaultCodegen implements CodegenConfig {
return underscore(name);
}
@Override
public String toModelDocFilename(String name) {
return toModelName(name);
}
@Override
public String toApiFilename(String name) {
// replace - with _ e.g. created-at => created_at
@@ -423,6 +482,11 @@ public class RubyClientCodegen extends DefaultCodegen implements CodegenConfig {
return underscore(name) + "_api";
}
@Override
public String toApiDocFilename(String name) {
return toApiName(name);
}
@Override
public String toApiTestFilename(String name) {
return toApiFilename(name) + "_spec";
@@ -466,6 +530,69 @@ public class RubyClientCodegen extends DefaultCodegen implements CodegenConfig {
return gemName + "/" + apiPackage() + "/" + toApiFilename(name);
}
@Override
public void setParameterExampleValue(CodegenParameter p) {
String example;
if (p.defaultValue == null) {
example = p.example;
} else {
example = p.defaultValue;
}
String type = p.baseType;
if (type == null) {
type = p.dataType;
}
if ("String".equals(type)) {
if (example == null) {
example = p.paramName + "_example";
}
example = "\"" + escapeText(example) + "\"";
} else if ("Integer".equals(type)) {
if (example == null) {
example = "56";
}
} else if ("Float".equals(type)) {
if (example == null) {
example = "3.4";
}
} else if ("BOOLEAN".equals(type)) {
if (example == null) {
example = "true";
}
} else if ("File".equals(type)) {
if (example == null) {
example = "/path/to/file";
}
example = "File.new(\"" + escapeText(example) + "\")";
} else if ("Date".equals(type)) {
if (example == null) {
example = "2013-10-20";
}
example = "Date.parse(\"" + escapeText(example) + "\")";
} else if ("DateTime".equals(type)) {
if (example == null) {
example = "2013-10-20T19:20:30+01:00";
}
example = "DateTime.parse(\"" + escapeText(example) + "\")";
} else if (!languageSpecificPrimitives.contains(type)) {
// type is a model class, e.g. User
example = moduleName + "::" + type + ".new";
}
if (example == null) {
example = "nil";
} else if (Boolean.TRUE.equals(p.isListContainer)) {
example = "[" + example + "]";
} else if (Boolean.TRUE.equals(p.isMapContainer)) {
example = "{'key': " + example + "}";
}
p.example = example;
}
public void setGemName(String gemName) {
this.gemName = gemName;
}

View File

@@ -0,0 +1,107 @@
# {{gemName}}
{{moduleName}} - the Ruby gem for the {{appName}}
Version: {{gemVersion}}
Automatically generated by the Ruby Swagger Codegen project:
- Build date: {{generatedDate}}
- Build package: {{generatorClass}}
## Installation
### Build a gem
You can build the generated client into a gem:
```shell
gem build {{{gemName}}}.gemspec
```
Then you can either install the gem:
```shell
gem install ./{{{gemName}}}-{{{gemVersion}}}.gem
```
or publish the gem to a gem server like [RubyGems](https://rubygems.org/).
Finally add this to your Gemfile:
gem '{{{gemName}}}', '~> {{{gemVersion}}}'
### Host as a git repository
You can also choose to host the generated client as a git repository, e.g. on github:
https://github.com/YOUR_USERNAME/YOUR_REPO
Then you can reference it in Gemfile:
gem '{{{gemName}}}', :git => 'https://github.com/YOUR_USERNAME/YOUR_REPO.git'
### Use without installation
You can also use the client directly like this:
```shell
ruby -Ilib script.rb
```
## Getting Started
```ruby
require '{{{gemName}}}'
{{{moduleName}}}.configure do |config|
# Use the line below to configure API key authorization if needed:
#config.api_key['api_key'] = 'your api key'
config.host = 'petstore.swagger.io'
config.base_path = '/v2'
# Enable debugging (default is disabled).
config.debugging = true
end
# Assuming there's a `PetApi` containing a `get_pet_by_id` method
# which returns a model object:
pet_api = {{{moduleName}}}::PetApi.new
pet = pet_api.get_pet_by_id(5)
puts pet.to_body
```
## Documentation for API Endpoints
All URIs are relative to *{{basePath}}*
Class | Method | HTTP request | Description
------------ | ------------- | ------------- | -------------
{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}*{{moduleName}}::{{classname}}* | [**{{operationId}}**]({{apiDocPath}}{{classname}}.md#{{operationId}}) | **{{httpMethod}}** {{path}} | {{#summary}}{{summary}}{{/summary}}
{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}}
## Documentation for Models
{{#models}}{{#model}} - [{{moduleName}}::{{classname}}]({{modelDocPath}}{{classname}}.md)
{{/model}}{{/models}}
## Documentation for Authorization
{{^authMethods}} All endpoints do not require authorization.
{{/authMethods}}{{#authMethods}}{{#last}} Authentication schemes defined for the API:{{/last}}{{/authMethods}}
{{#authMethods}}### {{name}}
{{#isApiKey}}- **Type**: API key
- **API key parameter name**: {{keyParamName}}
- **Location**: {{#isKeyInQuery}}URL query string{{/isKeyInQuery}}{{#isKeyInHeader}}HTTP header{{/isKeyInHeader}}
{{/isApiKey}}
{{#isBasic}}- **Type**: HTTP basic authentication
{{/isBasic}}
{{#isOAuth}}- **Type**: OAuth
- **Flow**: {{flow}}
- **Authorizatoin URL**: {{authorizationUrl}}
- **Scopes**: {{^scopes}}N/A{{/scopes}}
{{#scopes}}-- {{scope}}: {{description}}
{{/scopes}}
{{/isOAuth}}
{{/authMethods}}

View File

@@ -0,0 +1,73 @@
# {{moduleName}}::{{classname}}{{#description}}
{{description}}{{/description}}
All URIs are relative to *{{basePath}}*
Method | HTTP request | Description
------------- | ------------- | -------------
{{#operations}}{{#operation}}[**{{operationId}}**]({{classname}}.md#{{operationId}}) | **{{httpMethod}}** {{path}} | {{#summary}}{{summary}}{{/summary}}
{{/operation}}{{/operations}}
{{#operations}}
{{#operation}}
# **{{operationId}}**
> {{#returnType}}{{returnType}} {{/returnType}}{{operationId}}{{#hasParams}}({{#allParams}}{{#required}}{{{paramName}}}{{#vendorExtensions.x-codegen-hasMoreRequired}}, {{/vendorExtensions.x-codegen-hasMoreRequired}}{{/required}}{{/allParams}}{{#hasOptionalParams}}{{#vendorExtensions.x-codegen-hasRequiredParams}}, {{/vendorExtensions.x-codegen-hasRequiredParams}}opts{{/hasOptionalParams}}){{/hasParams}}
{{summary}}{{#notes}}
{{notes}}{{/notes}}
### Example
```ruby{{#hasAuthMethods}}
{{{moduleName}}}.configure do |config|{{#authMethods}}{{#isBasic}}
# Configure HTTP basic authorization: {{{name}}}
config.username = 'YOUR USERNAME'
config.password = 'YOUR PASSWORD'{{/isBasic}}{{#isApiKey}}
# Configure API key authorization: {{{name}}}
config.api_key['{{{keyParamName}}}'] = "YOUR API KEY"
# Uncomment the following line to set a prefix for the API key, e.g. "Token" (defaults to nil)
#config.api_key_prefix['{{{keyParamName}}}'] = "Token"{{/isApiKey}}{{#isOAuth}}
# Configure OAuth2 access token for authorization: {{{name}}}
config.access_token = "YOUR ACCESS TOKEN"{{/isOAuth}}
{{/authMethods}}end
{{/hasAuthMethods}}
api = {{{moduleName}}}::{{{classname}}}.new{{#hasParams}}
{{#vendorExtensions.x-codegen-hasRequiredParams}}{{#allParams}}{{#required}}
{{{paramName}}} = {{{example}}} # [{{{dataType}}}] {{{description}}}
{{/required}}{{/allParams}}{{/vendorExtensions.x-codegen-hasRequiredParams}}{{#hasOptionalParams}}
opts = { {{#allParams}}{{^required}}
{{{paramName}}}: {{{example}}}{{#vendorExtensions.x-codegen-hasMoreOptional}},{{/vendorExtensions.x-codegen-hasMoreOptional}} # [{{{dataType}}}] {{{description}}}{{/required}}{{/allParams}}
}{{/hasOptionalParams}}{{/hasParams}}
begin
{{#returnType}}result = {{/returnType}}api.{{{operationId}}}{{#hasParams}}({{#allParams}}{{#required}}{{{paramName}}}{{#vendorExtensions.x-codegen-hasMoreRequired}}, {{/vendorExtensions.x-codegen-hasMoreRequired}}{{/required}}{{/allParams}}{{#hasOptionalParams}}{{#vendorExtensions.x-codegen-hasRequiredParams}}, {{/vendorExtensions.x-codegen-hasRequiredParams}}opts{{/hasOptionalParams}}){{/hasParams}}
rescue {{{moduleName}}}::ApiError => e
puts "Exception when calling {{{operationId}}}: #{e}"
end
```
### Parameters
{{^allParams}}This endpoint does not need any parameter.{{/allParams}}{{#allParams}}{{#-last}}
Name | Type | Description | Notes
------------- | ------------- | ------------- | -------------{{/-last}}{{/allParams}}
{{#allParams}} **{{paramName}}** | {{#isPrimitiveType}}**{{dataType}}**{{/isPrimitiveType}}{{^isPrimitiveType}}{{#isFile}}**{{dataType}}**{{/isFile}}{{^isFile}}[**{{dataType}}**]({{baseType}}.md){{/isFile}}{{/isPrimitiveType}}| {{description}} | {{^required}}[optional] {{/required}}{{#defaultValue}}[default to {{defaultValue}}]{{/defaultValue}}
{{/allParams}}
### Return type
{{#returnType}}{{#returnTypeIsPrimitive}}**{{returnType}}**{{/returnTypeIsPrimitive}}{{^returnTypeIsPrimitive}}[**{{returnType}}**]({{returnBaseType}}.md){{/returnTypeIsPrimitive}}{{/returnType}}{{^returnType}}nil (empty response body){{/returnType}}
### Authorization
{{^authMethods}}No authorization required{{/authMethods}}{{#authMethods}}[{{name}}](../README.md#{{name}}){{^-last}}, {{/-last}}{{/authMethods}}
### HTTP reuqest headers
- **Content-Type**: {{#consumes}}{{mediaType}}{{#hasMore}}, {{/hasMore}}{{/consumes}}{{^consumes}}Not defined{{/consumes}}
- **Accept**: {{#produces}}{{mediaType}}{{#hasMore}}, {{/hasMore}}{{/produces}}{{^produces}}Not defined{{/produces}}
{{/operation}}
{{/operations}}

View File

@@ -0,0 +1,36 @@
*.gem
*.rbc
/.config
/coverage/
/InstalledFiles
/pkg/
/spec/reports/
/spec/examples.txt
/test/tmp/
/test/version_tmp/
/tmp/
## Specific to RubyMotion:
.dat*
.repl_history
build/
## Documentation cache and generated files:
/.yardoc/
/_yardoc/
/doc/
/rdoc/
## Environment normalization:
/.bundle/
/vendor/bundle
/lib/bundler/man/
# for a library or gem, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# Gemfile.lock
# .ruby-version
# .ruby-gemset
# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
.rvmrc

View File

@@ -0,0 +1,9 @@
{{#models}}{{#model}}# {{moduleName}}::{{classname}}
## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
{{#vars}}**{{name}}** | {{#isPrimitiveType}}**{{datatype}}**{{/isPrimitiveType}}{{^isPrimitiveType}}[**{{datatype}}**]({{complexType}}.md){{/isPrimitiveType}} | {{description}} | {{^required}}[optional] {{/required}}{{#readOnly}}[readonly] {{/readOnly}}{{#defaultValue}}[default to {{defaultValue}}]{{/defaultValue}}
{{/vars}}
{{/model}}{{/models}}