Correct the article in generated documents

For example:

  "a Ingress" > "an Ingress"
This commit is contained in:
Angus Salkeld 2016-10-26 09:54:02 +10:00
parent 8ca348a7a0
commit 3a08cf0619
11 changed files with 135 additions and 32 deletions

View File

@ -1197,7 +1197,7 @@
}
},
"post": {
"description": "create a Endpoints",
"description": "create Endpoints",
"consumes": [
"*/*"
],
@ -1405,7 +1405,7 @@
}
},
"delete": {
"description": "delete a Endpoints",
"description": "delete Endpoints",
"consumes": [
"*/*"
],
@ -1580,7 +1580,7 @@
}
},
"post": {
"description": "create a Event",
"description": "create an Event",
"consumes": [
"*/*"
],
@ -1788,7 +1788,7 @@
}
},
"delete": {
"description": "delete a Event",
"description": "delete an Event",
"consumes": [
"*/*"
],
@ -3353,7 +3353,7 @@
},
"/api/v1/namespaces/{namespace}/pods/{name}/eviction": {
"post": {
"description": "create eviction of a Eviction",
"description": "create eviction of an Eviction",
"consumes": [
"*/*"
],
@ -20515,7 +20515,7 @@
}
},
"post": {
"description": "create a Ingress",
"description": "create an Ingress",
"consumes": [
"*/*"
],
@ -20723,7 +20723,7 @@
}
},
"delete": {
"description": "delete a Ingress",
"description": "delete an Ingress",
"consumes": [
"*/*"
],

View File

@ -1107,7 +1107,7 @@
}
},
"post": {
"description": "create a Endpoints",
"description": "create Endpoints",
"consumes": [
"*/*"
],
@ -1303,7 +1303,7 @@
}
},
"delete": {
"description": "delete a Endpoints",
"description": "delete Endpoints",
"consumes": [
"*/*"
],
@ -1469,7 +1469,7 @@
}
},
"post": {
"description": "create a Event",
"description": "create an Event",
"consumes": [
"*/*"
],
@ -1665,7 +1665,7 @@
}
},
"delete": {
"description": "delete a Event",
"description": "delete an Event",
"consumes": [
"*/*"
],
@ -3143,7 +3143,7 @@
},
"/api/v1/namespaces/{namespace}/pods/{name}/eviction": {
"post": {
"description": "create eviction of a Eviction",
"description": "create eviction of an Eviction",
"consumes": [
"*/*"
],

View File

@ -3184,7 +3184,7 @@
{
"type": "v1beta1.Ingress",
"method": "POST",
"summary": "create a Ingress",
"summary": "create an Ingress",
"nickname": "createNamespacedIngress",
"parameters": [
{
@ -3578,7 +3578,7 @@
{
"type": "unversioned.Status",
"method": "DELETE",
"summary": "delete a Ingress",
"summary": "delete an Ingress",
"nickname": "deleteNamespacedIngress",
"parameters": [
{

View File

@ -1062,7 +1062,7 @@
{
"type": "v1.Endpoints",
"method": "POST",
"summary": "create a Endpoints",
"summary": "create Endpoints",
"nickname": "createNamespacedEndpoints",
"parameters": [
{
@ -1456,7 +1456,7 @@
{
"type": "unversioned.Status",
"method": "DELETE",
"summary": "delete a Endpoints",
"summary": "delete Endpoints",
"nickname": "deleteNamespacedEndpoints",
"parameters": [
{
@ -1851,7 +1851,7 @@
{
"type": "v1.Event",
"method": "POST",
"summary": "create a Event",
"summary": "create an Event",
"nickname": "createNamespacedEvent",
"parameters": [
{
@ -2245,7 +2245,7 @@
{
"type": "unversioned.Status",
"method": "DELETE",
"summary": "delete a Event",
"summary": "delete an Event",
"nickname": "deleteNamespacedEvent",
"parameters": [
{
@ -8615,7 +8615,7 @@
{
"type": "v1alpha1.Eviction",
"method": "POST",
"summary": "create eviction of a Eviction",
"summary": "create eviction of an Eviction",
"nickname": "createNamespacedEvictionEviction",
"parameters": [
{

View File

@ -5986,7 +5986,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
</div>
</div>
<div class="sect2">
<h3 id="_create_a_ingress">create a Ingress</h3>
<h3 id="_create_an_ingress">create an Ingress</h3>
<div class="listingblock">
<div class="content">
<pre>POST /apis/extensions/v1beta1/namespaces/{namespace}/ingresses</pre>
@ -6367,7 +6367,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
</div>
</div>
<div class="sect2">
<h3 id="_delete_a_ingress">delete a Ingress</h3>
<h3 id="_delete_an_ingress">delete an Ingress</h3>
<div class="listingblock">
<div class="content">
<pre>DELETE /apis/extensions/v1beta1/namespaces/{namespace}/ingresses/{name}</pre>

View File

@ -3078,7 +3078,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
</div>
</div>
<div class="sect2">
<h3 id="_create_a_endpoints">create a Endpoints</h3>
<h3 id="_create_endpoints">create Endpoints</h3>
<div class="listingblock">
<div class="content">
<pre>POST /api/v1/namespaces/{namespace}/endpoints</pre>
@ -3459,7 +3459,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
</div>
</div>
<div class="sect2">
<h3 id="_delete_a_endpoints">delete a Endpoints</h3>
<h3 id="_delete_endpoints">delete Endpoints</h3>
<div class="listingblock">
<div class="content">
<pre>DELETE /api/v1/namespaces/{namespace}/endpoints/{name}</pre>
@ -4027,7 +4027,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
</div>
</div>
<div class="sect2">
<h3 id="_create_a_event">create a Event</h3>
<h3 id="_create_an_event">create an Event</h3>
<div class="listingblock">
<div class="content">
<pre>POST /api/v1/namespaces/{namespace}/events</pre>
@ -4408,7 +4408,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
</div>
</div>
<div class="sect2">
<h3 id="_delete_a_event">delete a Event</h3>
<h3 id="_delete_an_event">delete an Event</h3>
<div class="listingblock">
<div class="content">
<pre>DELETE /api/v1/namespaces/{namespace}/events/{name}</pre>
@ -8311,7 +8311,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
</div>
</div>
<div class="sect2">
<h3 id="_create_eviction_of_a_eviction">create eviction of a Eviction</h3>
<h3 id="_create_eviction_of_an_eviction">create eviction of an Eviction</h3>
<div class="listingblock">
<div class="content">
<pre>POST /api/v1/namespaces/{namespace}/pods/{name}/eviction</pre>

View File

@ -4360,7 +4360,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
</div>
</div>
<div class="sect2">
<h3 id="_create_a_ingress">create a Ingress</h3>
<h3 id="_create_an_ingress">create an Ingress</h3>
<div class="listingblock">
<div class="content">
<pre>POST /apis/extensions/v1beta1/namespaces/{namespace}/ingresses</pre>
@ -4741,7 +4741,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
</div>
</div>
<div class="sect2">
<h3 id="_delete_a_ingress">delete a Ingress</h3>
<h3 id="_delete_an_ingress">delete an Ingress</h3>
<div class="listingblock">
<div class="content">
<pre>DELETE /apis/extensions/v1beta1/namespaces/{namespace}/ingresses/{name}</pre>

View File

@ -51,6 +51,7 @@ go_library(
"//pkg/util/proxy:go_default_library",
"//pkg/util/runtime:go_default_library",
"//pkg/util/strategicpatch:go_default_library",
"//pkg/util/strings:go_default_library",
"//pkg/util/wsstream:go_default_library",
"//pkg/watch:go_default_library",
"//pkg/watch/versioned:go_default_library",

View File

@ -34,6 +34,7 @@ import (
"k8s.io/kubernetes/pkg/apiserver/metrics"
"k8s.io/kubernetes/pkg/conversion"
"k8s.io/kubernetes/pkg/runtime"
utilstrings "k8s.io/kubernetes/pkg/util/strings"
"github.com/emicklei/go-restful"
)
@ -610,9 +611,10 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
handler = CreateResource(creater, reqScope, a.group.Typer, admit)
}
handler = metrics.InstrumentRouteFunc(action.Verb, resource, handler)
doc := "create a " + kind
article := utilstrings.GetArticleForNoun(kind, " ")
doc := "create" + article + kind
if hasSubresource {
doc = "create " + subresource + " of a " + kind
doc = "create " + subresource + " of" + article + kind
}
route := ws.POST(action.Path).To(handler).
Doc(doc).
@ -625,9 +627,10 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
addParams(route, action.Params)
ws.Route(route)
case "DELETE": // Delete a resource.
doc := "delete a " + kind
article := utilstrings.GetArticleForNoun(kind, " ")
doc := "delete" + article + kind
if hasSubresource {
doc = "delete " + subresource + " of a " + kind
doc = "delete " + subresource + " of" + article + kind
}
handler := metrics.InstrumentRouteFunc(action.Verb, resource, DeleteResource(gracefulDeleter, isGracefulDeleter, reqScope, admit))
route := ws.DELETE(action.Path).To(handler).

View File

@ -17,8 +17,10 @@ limitations under the License.
package strings
import (
"fmt"
"path"
"strings"
"unicode"
)
// Splits a fully qualified name and returns its namespace and name.
@ -45,3 +47,30 @@ func ShortenString(str string, n int) string {
return str[:n]
}
}
// GetArticleForNoun returns the article needed for the given noun.
func GetArticleForNoun(noun string, padding string) string {
if noun[len(noun)-2:] != "ss" && noun[len(noun)-1:] == "s" {
// Plurals don't have an article.
// Don't catch words like class
return fmt.Sprintf("%v", padding)
}
article := "a"
if isVowel(rune(noun[0])) {
article = "an"
}
return fmt.Sprintf("%s%s%s", padding, article, padding)
}
// isVowel returns true if the rune is a vowel (case insensitive).
func isVowel(c rune) bool {
vowels := []rune{'a', 'e', 'i', 'o', 'u'}
for _, value := range vowels {
if value == unicode.ToLower(c) {
return true
}
}
return false
}

View File

@ -71,3 +71,73 @@ func TestShortenString(t *testing.T) {
}
}
}
func TestIsVowel(t *testing.T) {
tests := []struct {
name string
arg rune
want bool
}{
{
name: "yes",
arg: 'E',
want: true,
},
{
name: "no",
arg: 'n',
want: false,
},
}
for _, tt := range tests {
if got := isVowel(tt.arg); got != tt.want {
t.Errorf("%q. IsVowel() = %v, want %v", tt.name, got, tt.want)
}
}
}
func TestGetArticleForNoun(t *testing.T) {
type args struct {
}
tests := []struct {
noun string
padding string
want string
}{
{
noun: "Frog",
padding: " ",
want: " a ",
},
{
noun: "frogs",
padding: " ",
want: " ",
},
{
noun: "apple",
padding: "",
want: "an",
},
{
noun: "Apples",
padding: " ",
want: " ",
},
{
noun: "Ingress",
padding: " ",
want: " an ",
},
{
noun: "Class",
padding: " ",
want: " a ",
},
}
for _, tt := range tests {
if got := GetArticleForNoun(tt.noun, tt.padding); got != tt.want {
t.Errorf("%q. GetArticleForNoun() = %v, want %v", tt.noun, got, tt.want)
}
}
}