grape-swagger 0.10.2 → 0.10.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5a6996205661a00c64c62bb85fe5c68ea79c0acf
4
- data.tar.gz: 446c20aa4754161e0fee6aaff5894af24ec06f76
3
+ metadata.gz: 4c3b14815684b5e4e77c01b12a1c64029b4cc367
4
+ data.tar.gz: ecd30bdba3542f6a776c7f94683aab2a2eb32989
5
5
  SHA512:
6
- metadata.gz: 4968dde338ad1282e6100ec9d7b318c76267346f9048d01d8027065fa7e1e7468ff80038448ebf5184d809512c9b8dbaa5584f7996085b63ed76b5092a7de45c
7
- data.tar.gz: 468c21cc9341a54105c7c892432b9e2818b4811bb3add16047b847cac8d6be45beaddfa8b6b41bd08087333b46f1c90317583cf43c3b276cdec1f4b185fdec54
6
+ metadata.gz: 5b2baa995bd32ba699ea6ffc21fb3816b5a6742a2b3be229df6a47e3cdf85622a52468899c1e5cee3b7f77973fe5dcedfa1330b390a981d09ebca7a47b6bdfbd
7
+ data.tar.gz: 96a44e9923a0921f2f368f6f128681fa40796c3987fc46a7de54aec2599d00f0de33b42db4f19b5062857155fbf7395f50c25c95f112f43c86db0a525b9e0920
data/.rubocop_todo.yml CHANGED
@@ -1,55 +1,76 @@
1
- # This configuration was generated by `rubocop --auto-gen-config`
2
- # on 2015-04-01 08:13:45 -0400 using RuboCop version 0.27.0.
1
+ # This configuration was generated by
2
+ # `rubocop --auto-gen-config`
3
+ # on 2015-08-19 12:17:53 -0400 using RuboCop version 0.33.0.
3
4
  # The point is for the user to remove these configuration records
4
5
  # one by one as the offenses are removed from the code base.
5
6
  # Note that changes in the inspected code, or installation of new
6
7
  # versions of RuboCop, may require this file to be generated again.
7
8
 
8
- # Offense count: 11
9
+ # Offense count: 10
9
10
  Metrics/AbcSize:
10
- Max: 360
11
+ Max: 214
11
12
 
12
13
  # Offense count: 1
13
14
  # Configuration parameters: CountComments.
14
15
  Metrics/ClassLength:
15
- Max: 509
16
+ Max: 147
16
17
 
17
18
  # Offense count: 6
18
19
  Metrics/CyclomaticComplexity:
19
- Max: 102
20
+ Max: 39
20
21
 
21
- # Offense count: 312
22
+ # Offense count: 304
22
23
  # Configuration parameters: AllowURI, URISchemes.
23
24
  Metrics/LineLength:
24
- Max: 254
25
+ Max: 242
25
26
 
26
27
  # Offense count: 21
27
28
  # Configuration parameters: CountComments.
28
29
  Metrics/MethodLength:
29
- Max: 381
30
+ Max: 170
30
31
 
31
- # Offense count: 5
32
+ # Offense count: 1
33
+ # Configuration parameters: CountComments.
34
+ Metrics/ModuleLength:
35
+ Max: 470
36
+
37
+ # Offense count: 4
32
38
  Metrics/PerceivedComplexity:
33
- Max: 105
39
+ Max: 41
34
40
 
35
41
  # Offense count: 8
36
42
  Style/ClassVars:
37
- Enabled: false
43
+ Exclude:
44
+ - 'example/api.rb'
45
+ - 'lib/grape-swagger/doc_methods.rb'
38
46
 
39
- # Offense count: 81
47
+ # Offense count: 90
40
48
  Style/Documentation:
41
49
  Enabled: false
42
50
 
43
51
  # Offense count: 2
44
52
  Style/DoubleNegation:
45
- Enabled: false
53
+ Exclude:
54
+ - 'lib/grape-swagger/doc_methods.rb'
46
55
 
47
56
  # Offense count: 3
48
57
  # Configuration parameters: Exclude.
49
58
  Style/FileName:
50
- Enabled: false
59
+ Exclude:
60
+ - 'lib/grape-swagger.rb'
61
+ - 'spec/grape-swagger_helper_spec.rb'
62
+ - 'spec/grape-swagger_spec.rb'
51
63
 
52
64
  # Offense count: 1
53
65
  # Configuration parameters: NamePrefix, NamePrefixBlacklist.
54
66
  Style/PredicateName:
55
- Enabled: false
67
+ Exclude:
68
+ - 'lib/grape-swagger/doc_methods.rb'
69
+
70
+ # Offense count: 4
71
+ # Cop supports --auto-correct.
72
+ # Configuration parameters: EnforcedStyle, SupportedStyles, AllowInnerSlashes.
73
+ Style/RegexpLiteral:
74
+ Exclude:
75
+ - 'lib/grape-swagger.rb'
76
+ - 'lib/grape-swagger/doc_methods.rb'
data/.travis.yml CHANGED
@@ -17,4 +17,5 @@ env:
17
17
  - GRAPE_VERSION=0.11.0
18
18
  - GRAPE_VERSION=0.12.0
19
19
  - GRAPE_VERSION=0.13.0
20
+ - GRAPE_VERSION=0.14.0
20
21
  - GRAPE_VERSION=HEAD
data/CHANGELOG.md CHANGED
@@ -1,3 +1,13 @@
1
+ ### 0.10.4 (December 7, 2015)
2
+
3
+ * [#315](https://github.com/ruby-grape/grape-swagger/pull/315): Require `grape-entity` < 0.5.0 - [@dblock](https://github.com/dblock).
4
+
5
+ ### 0.10.3 (December 7, 2015)
6
+
7
+ * [#292](https://github.com/ruby-grape/grape-swagger/pull/292): Support i18n - [@calfzhou](https://github.com/calfzhou).
8
+ * [#297](https://github.com/ruby-grape/grape-swagger/pull/297): Correct use of documentation param_type - [@fab-girard](https://github.com/fab-girard).
9
+ * [#305](https://github.com/ruby-grape/grape-swagger/pull/305): Speedup by parsing models smarter, not harder - [@jhollinger](https://github.com/jhollinger).
10
+
1
11
  ### 0.10.2 (August 19, 2015)
2
12
 
3
13
  #### Features
data/README.md CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/grape-swagger.svg)](http://badge.fury.io/rb/grape-swagger)
4
4
  [![Build Status](https://travis-ci.org/ruby-grape/grape-swagger.svg?branch=master)](https://travis-ci.org/ruby-grape/grape-swagger)
5
+ [![Dependency Status](https://gemnasium.com/ruby-grape/grape-swagger.svg)](https://gemnasium.com/ruby-grape/grape-swagger)
5
6
  [![Code Climate](https://codeclimate.com/github/ruby-grape/grape-swagger.svg)](https://codeclimate.com/github/ruby-grape/grape-swagger)
6
7
 
7
8
  ## What is grape-swagger?
@@ -92,6 +93,10 @@ API class name.
92
93
 
93
94
  Allow markdown in `detail`/`notes`, default is `nil`. (disabled) See below for details.
94
95
 
96
+ #### i18n_scope
97
+
98
+ Translations scope (or array of scopes) default is `:api`. [See below for details](#i18n).
99
+
95
100
  #### hide_format
96
101
 
97
102
  Don't add `.(format)` to the end of URLs, default is `false`.
@@ -305,7 +310,7 @@ Grape uses the option `default` to set a default value for optional parameters.
305
310
 
306
311
  ## Grape Entities
307
312
 
308
- Add the [grape-entity](https://github.com/agileanimal/grape-entity) gem to our Gemfile.
313
+ Add the [grape-entity](https://github.com/ruby-grape/grape-entity) gem to our Gemfile.
309
314
 
310
315
  The following example exposes statuses. And exposes statuses documentation adding :type and :desc.
311
316
 
@@ -511,6 +516,195 @@ get '/', http_codes: [
511
516
  end
512
517
  ```
513
518
 
519
+ ## I18n
520
+
521
+ Grape-swagger supports I18n for most messages of the JSON documentation, including:
522
+
523
+ * API [info](#info)
524
+ * Namespaces/resources description
525
+ * Endpoints description and detail/notes
526
+ * Params (including headers) description
527
+ * Models/entities description
528
+
529
+ By default, grape-swagger will lookup translations inside `:api` scope. This can be configured by
530
+ set [`i18n_scope`](#i18n_scope) to a custom scope (or an array of scopes) when calling
531
+ `add_swagger_documentation`.
532
+
533
+ ### Translation and Default Message
534
+
535
+ To localize your API document, you can add a locale file containing translated messages.
536
+ Grape-swagger will try to look up translation for each message. If a translation cannot be found,
537
+ it will use the message specified in the API code, then default to blank.
538
+
539
+ Take the following sample API for example:
540
+
541
+ ```ruby
542
+ desc 'Gets all kittens' do
543
+ detail 'This will expose all the kittens.'
544
+ end
545
+ params do
546
+ optional :sort
547
+ end
548
+ get :kittens do
549
+ end
550
+ ```
551
+
552
+ To generate I18n documentation, you can add a locale file with translated messages:
553
+
554
+ ```yaml
555
+ api:
556
+ kittens:
557
+ get:
558
+ detail: This will return a full list of kittens, and you can change the way of sorting them.
559
+ params:
560
+ sort: Specifies the order of results
561
+ ```
562
+
563
+ The endpoint description in generated API document will be "Gets all kittens" - specified in source
564
+ code - because there is no translation defined. And endpoint details will be overwritten by locale
565
+ message - "This will return a full list of kittens, and you can change the way of sorting them.".
566
+ Parameter `:sort` will have a description from locale file too.
567
+
568
+ ### Default Lookup Keys
569
+
570
+ The following lookup keys are all within the scope given by `i18n_scope`.
571
+
572
+ #### API Info
573
+
574
+ The default lookup key of a field in API info is `info.<field>`, i.e.:
575
+
576
+ * `info.title` for `:title` field.
577
+ * `info.desc` or `info.description` for `:description` field.
578
+ * `info.contact` for `:contact` field.
579
+ * `info.license` for `:license` field.
580
+ * `info.license_url` for `:license_url` field.
581
+ * `info.terms_of_service_url` for `:terms_of_service_url` field.
582
+
583
+ #### Namespaces/Resources Description
584
+
585
+ Grape-swagger will give each root namespace a default description, such as "*Operations about
586
+ users*", where `users` is a namespace (pluralized). But you can change it by provide your own
587
+ message under key `<namespace>.desc` or `<namespace>.description` in your locale file. And the
588
+ namespace itself is available as a variable for interpolation.
589
+
590
+ ### Endpoints Description and Detail/Notes
591
+
592
+ An endpoint's default lookup key is in format of `<namespace>.<path>.<http_method>`. And if there
593
+ are nested namespaces or multi-level path, all parts will be join by `.`. For example for this
594
+ endpoint:
595
+
596
+ ```ruby
597
+ namespace :users do
598
+ route_params :id do
599
+ get :'password/strength' do
600
+ end
601
+ end
602
+ end
603
+ ```
604
+
605
+ Its corresponding lookup key will be `users.:id.password.strength.get`, let's say that this is
606
+ the key of this endpoint.
607
+
608
+ So an endpoint's description message can be given under `<endpoint_key>.desc` or
609
+ `<endpoint_key>.description`. And use `<endpoint_key>.detail` or `<endpoint_key>.notes` for the
610
+ message of its further details.
611
+
612
+ #### Params Description
613
+
614
+ The first default key of endpoint parameter's description translation is
615
+ `<endpoint_key>.params.<param_name>`. But considering that some endpoints could usually share a
616
+ same parameter, it will be a little annoyed to duplicate its description message everywhere. So if
617
+ no translation found in the first default key, grape-swagger will try to find all its parent keys.
618
+ Take above endpoint for example, it may have a parameter named `:id`, then the lookup keys are:
619
+
620
+ 1. `users.:id.password.strength.get.params.id`
621
+ 1. `users.:id.password.strength.params.id`
622
+ 1. `users.:id.password.params.id`
623
+ 1. `users.:id.params.id`
624
+ 1. `users.params.id`
625
+ 1. `params.id`
626
+
627
+ #### Models/Entities Description
628
+
629
+ A model class' lookup key is by default its class name, without module part, and underscored. Each
630
+ property's key is then `entities.<class_key>.<property_name>`. When not found, grape-swagger will
631
+ try to check the its ancestors, up to a class whose name is `Entity`.
632
+
633
+ Say if there is model class `User` inherits `Grape::Entity`, and another model `AdminUser` inherits
634
+ `User`, to translate a property named `:email` of `AdminUser`, the lookup keys are:
635
+
636
+ 1. `entities.admin_user.email`
637
+ 1. `entities.user.email`
638
+ 1. `entities.default.email`
639
+
640
+ #### Example Locale File
641
+
642
+ ```yaml
643
+ en:
644
+ api:
645
+ info:
646
+ title: My Awesome API
647
+ desc: Some detail information about this API.
648
+ entities:
649
+ default:
650
+ id: Resource identifier
651
+ user:
652
+ name: User's real name
653
+ email: User's login email address
654
+ sign_up_at: When the user signed up
655
+ admin_user:
656
+ level: Which level the admin is
657
+ password_strength:
658
+ level: A 0~4 integer indicates `very_weak` to `strong`
659
+ crack_time: An estimated time for force cracking the password, in seconds
660
+ params:
661
+ locale: Used to change locale of endpoint's responding message
662
+ sort: To specify the order of result list, default is %{default_sort}
663
+ users:
664
+ desc: Operations about not-disabled users
665
+ get:
666
+ desc: Gets a list of users
667
+ detail: You can control how to sort the results.
668
+ ':id':
669
+ params:
670
+ id: User id
671
+ get:
672
+ desc: Finds user by id
673
+ email:
674
+ put:
675
+ desc: Changes a user's email
676
+ params:
677
+ email: A new email
678
+ password:
679
+ strength:
680
+ get:
681
+ desc: Gets the strength estimation of a user's password
682
+ detail: The estimation is done by a well-known algorithm when he changed his password
683
+ swagger_doc:
684
+ desc: Endpoints for API documents
685
+ get:
686
+ desc: Gets root API document
687
+ ':name':
688
+ get:
689
+ desc: Gets specific resource API document
690
+ params:
691
+ name: Resource name
692
+ ```
693
+
694
+ ### Customization
695
+
696
+ The translation can be customized by using different kind/format of message in source code.
697
+
698
+ * A string or `nil`: It will become the default message when a translation is not defined.
699
+ * A symbol: Looks up translation using the symbol as a key, but will fallback to default key(s).
700
+ * A hash with the following keys:
701
+ * key: A symbol, defines custom lookup key.
702
+ * default: A string, defines the default message when translation can not be found.
703
+ * translate: A boolean (default is `true`), set to `false` to skip translation for this message.
704
+ * scope: A symbol, or a string, or an array of them. Used to replace the `i18n_scope` config
705
+ value for a custom lookup scope.
706
+ * Any other key/value will be sent to the translation function for interpolation.
707
+
514
708
  ## Contributing to grape-swagger
515
709
 
516
710
  See [CONTRIBUTING](CONTRIBUTING.md).
data/example/config.ru CHANGED
@@ -2,7 +2,7 @@ require 'rack/cors'
2
2
  use Rack::Cors do
3
3
  allow do
4
4
  origins '*'
5
- resource '*', headers: :any, methods: [ :get, :post, :put, :delete, :options ]
5
+ resource '*', headers: :any, methods: [:get, :post, :put, :delete, :options]
6
6
  end
7
7
  end
8
8
 
@@ -12,7 +12,7 @@ Gem::Specification.new do |s|
12
12
  s.license = 'MIT'
13
13
 
14
14
  s.add_runtime_dependency 'grape', '>= 0.8.0'
15
- s.add_runtime_dependency 'grape-entity'
15
+ s.add_runtime_dependency 'grape-entity', '< 0.5.0'
16
16
 
17
17
  s.add_development_dependency 'rake'
18
18
  s.add_development_dependency 'shoulda'
@@ -21,7 +21,7 @@ Gem::Specification.new do |s|
21
21
  s.add_development_dependency 'bundler'
22
22
  s.add_development_dependency 'rack-test'
23
23
  s.add_development_dependency 'rack-cors'
24
- s.add_development_dependency 'rubocop', '0.27.0'
24
+ s.add_development_dependency 'rubocop', '0.33.0'
25
25
  s.add_development_dependency 'kramdown', '~> 1.4.1'
26
26
  s.add_development_dependency 'redcarpet', '~> 3.1.2' unless RUBY_PLATFORM.eql? 'java'
27
27
  s.add_development_dependency 'rouge', '~> 1.6.1'
data/lib/grape-swagger.rb CHANGED
@@ -10,6 +10,7 @@ module Grape
10
10
  class API
11
11
  class << self
12
12
  attr_accessor :combined_routes, :combined_namespaces, :combined_namespace_routes, :combined_namespace_identifiers
13
+ attr_accessor :endpoint_mapping
13
14
 
14
15
  def add_swagger_documentation(options = {})
15
16
  documentation_class = create_documentation_class
@@ -35,6 +36,7 @@ module Grape
35
36
  @target_class.combined_routes[resource] << route
36
37
  end
37
38
 
39
+ @target_class.endpoint_mapping = {}
38
40
  @target_class.combined_namespaces = {}
39
41
  combine_namespaces(@target_class)
40
42
 
@@ -60,6 +62,10 @@ module Grape
60
62
  # and strip leading slash
61
63
  @target_class.combined_namespaces[endpoint.namespace.sub(/^\//, '')] = ns if ns
62
64
 
65
+ endpoint.routes.each do |route|
66
+ @target_class.endpoint_mapping[route.to_s.sub('(.:format)', '')] = endpoint
67
+ end
68
+
63
69
  combine_namespaces(endpoint.options[:app]) if endpoint.options[:app]
64
70
  end
65
71
  end
@@ -79,9 +85,9 @@ module Grape
79
85
  if namespace.options.key?(:swagger) && namespace.options[:swagger][:nested] == false
80
86
  # Namespace shall appear as standalone resource, use specified name or use normalized path as name
81
87
  if namespace.options[:swagger].key?(:name)
82
- identifier = namespace.options[:swagger][:name].gsub(/ /, '-')
88
+ identifier = namespace.options[:swagger][:name].tr(' ', '-')
83
89
  else
84
- identifier = name.gsub(/_/, '-').gsub(/\//, '_')
90
+ identifier = name.tr('_', '-').gsub(/\//, '_')
85
91
  end
86
92
  @target_class.combined_namespace_identifiers[identifier] = name
87
93
  @target_class.combined_namespace_routes[identifier] = namespace_routes
@@ -1,3 +1,5 @@
1
+ require 'set'
2
+
1
3
  module GrapeSwagger
2
4
  module DocMethods
3
5
  PRIMITIVE_MAPPINGS = {
@@ -14,11 +16,43 @@ module GrapeSwagger
14
16
  @@class_name
15
17
  end
16
18
 
19
+ def translate(message, scope, default, params = {})
20
+ if message.is_a?(String)
21
+ text = message
22
+ elsif message.is_a?(Symbol)
23
+ key = message
24
+ elsif message.is_a?(Hash)
25
+ message = message.dup
26
+ key = message.delete(:key)
27
+ text = message.delete(:default)
28
+ skip_translate = !message.delete(:translate) if message.key?(:translate)
29
+ scope = message.delete(:scope) if message.key?(:scope)
30
+ params = params.merge(message) unless message.empty?
31
+ end
32
+
33
+ return text if skip_translate
34
+
35
+ default = Array(default).dup << (text || '')
36
+ I18n.t(key, params.merge(scope: scope, default: default))
37
+ end
38
+
39
+ def expand_scope(scope)
40
+ scopes = []
41
+ scope = scope.to_s
42
+ until scope.blank?
43
+ scopes << scope.to_sym
44
+ scope = scope.rpartition('.')[0]
45
+ end
46
+ scopes << :''
47
+ end
48
+
17
49
  def as_markdown(description)
18
50
  description && @@markdown ? @@markdown.as_markdown(strip_heredoc(description)) : description
19
51
  end
20
52
 
21
- def parse_params(params, path, method)
53
+ def parse_params(params, path, method, options = {})
54
+ scope = options[:scope]
55
+ i18n_keys = expand_scope(options[:key])
22
56
  params ||= []
23
57
 
24
58
  parsed_array_params = parse_array_params(params)
@@ -71,27 +105,29 @@ module GrapeSwagger
71
105
  values = value.is_a?(Hash) ? value[:values] : nil
72
106
  enum_or_range_values = parse_enum_or_range_values(values)
73
107
 
74
- if value.is_a?(Hash) && value.key?(:documentation) && value[:documentation].key?(:param_type)
75
- param_type = value[:documentation][:param_type]
108
+ if value.is_a?(Hash) && value.key?(:param_type)
109
+ param_type = value[:param_type]
76
110
  if is_array
77
111
  items = { '$ref' => data_type }
78
112
  data_type = 'array'
79
113
  end
80
114
  else
81
- param_type = case
82
- when path.include?(":#{param}")
83
- 'path'
84
- when %w(POST PUT PATCH).include?(method)
85
- if is_primitive?(data_type)
86
- 'form'
87
- else
88
- 'body'
89
- end
90
- else
91
- 'query'
115
+ param_type = case
116
+ when path.include?(":#{param}")
117
+ 'path'
118
+ when %w(POST PUT PATCH).include?(method)
119
+ if is_primitive?(data_type)
120
+ 'form'
121
+ else
122
+ 'body'
123
+ end
124
+ else
125
+ 'query'
92
126
  end
93
127
  end
94
128
  name = (value.is_a?(Hash) && value[:full_name]) || param
129
+ description = translate(description, scope,
130
+ i18n_keys.map { |key| :"#{key}.params.#{name}" })
95
131
 
96
132
  parsed_params = {
97
133
  paramType: param_type,
@@ -99,14 +135,14 @@ module GrapeSwagger
99
135
  description: as_markdown(description),
100
136
  type: data_type,
101
137
  required: required,
102
- allowMultiple: is_array
138
+ allowMultiple: is_array && data_type != 'array' && %w(query header path).include?(param_type)
103
139
  }
104
140
 
105
141
  if PRIMITIVE_MAPPINGS.key?(data_type)
106
142
  parsed_params[:type], parsed_params[:format] = PRIMITIVE_MAPPINGS[data_type]
107
143
  end
108
144
 
109
- parsed_params[:items] = items if items.present?
145
+ parsed_params[:items] = items if items.present?
110
146
 
111
147
  parsed_params[:defaultValue] = example if example
112
148
  if default_value && example.blank?
@@ -130,18 +166,22 @@ module GrapeSwagger
130
166
  content_types.uniq
131
167
  end
132
168
 
133
- def parse_info(info)
169
+ def parse_info(info, options = {})
170
+ scope = options[:scope]
171
+
134
172
  {
135
- contact: info[:contact],
136
- description: as_markdown(info[:description]),
137
- license: info[:license],
138
- licenseUrl: info[:license_url],
139
- termsOfServiceUrl: info[:terms_of_service_url],
140
- title: info[:title]
173
+ contact: translate(info[:contact], scope, :'info.contact'),
174
+ description: as_markdown(translate(info[:description], scope, [:'info.desc', :'info.description'])),
175
+ license: translate(info[:license], scope, :'info.license'),
176
+ licenseUrl: translate(info[:license_url], scope, :'info.license_url'),
177
+ termsOfServiceUrl: translate(info[:terms_of_service_url], scope, :'info.terms_of_service_url'),
178
+ title: translate(info[:title], scope, :'info.title')
141
179
  }.delete_if { |_, value| value.blank? }
142
180
  end
143
181
 
144
- def parse_header_params(params)
182
+ def parse_header_params(params, options = {})
183
+ scope = options[:scope]
184
+ i18n_keys = expand_scope(options[:key])
145
185
  params ||= []
146
186
 
147
187
  params.map do |param, value|
@@ -151,6 +191,9 @@ module GrapeSwagger
151
191
  default_value = value.is_a?(Hash) ? value[:default] : nil
152
192
  param_type = 'header'
153
193
 
194
+ description = translate(description, scope,
195
+ i18n_keys.map { |key| :"#{key}.params.#{param}" })
196
+
154
197
  parsed_params = {
155
198
  paramType: param_type,
156
199
  name: param,
@@ -191,13 +234,22 @@ module GrapeSwagger
191
234
  end
192
235
  end
193
236
 
194
- def parse_entity_models(models)
237
+ def parse_entity_models(models, options = {})
238
+ scope = options[:scope]
195
239
  result = {}
196
240
  models.each do |model|
197
241
  name = (model.instance_variable_get(:@root) || parse_entity_name(model))
198
242
  properties = {}
199
243
  required = []
200
244
 
245
+ i18n_keys = []
246
+ klass = model
247
+ until %w(entity object).include? klass.name.demodulize.underscore
248
+ i18n_keys << klass.name.demodulize.underscore.to_sym
249
+ klass = klass.superclass
250
+ end
251
+ i18n_keys << :default
252
+
201
253
  model.documentation.each do |property_name, property_info|
202
254
  p = property_info.dup
203
255
 
@@ -219,7 +271,9 @@ module GrapeSwagger
219
271
 
220
272
  # rename Grape Entity's "desc" to "description"
221
273
  property_description = p.delete(:desc)
222
- p[:description] = property_description if property_description
274
+ property_description = translate(property_description, scope,
275
+ i18n_keys.map { |key| :"entities.#{key}.#{property_name}" })
276
+ p[:description] = property_description unless property_description.blank?
223
277
 
224
278
  # rename Grape's 'values' to 'enum'
225
279
  select_values = p.delete(:values)
@@ -324,6 +378,7 @@ module GrapeSwagger
324
378
  base_path: nil,
325
379
  api_version: '0.1',
326
380
  markdown: nil,
381
+ i18n_scope: :api,
327
382
  hide_documentation_path: false,
328
383
  hide_format: false,
329
384
  format: nil,
@@ -339,7 +394,7 @@ module GrapeSwagger
339
394
 
340
395
  target_class = options[:target_class]
341
396
  @@mount_path = options[:mount_path]
342
- @@class_name = options[:class_name] || options[:mount_path].gsub('/', '')
397
+ @@class_name = options[:class_name] || options[:mount_path].delete('/')
343
398
  @@markdown = options[:markdown] ? GrapeSwagger::Markdown.new(options[:markdown]) : nil
344
399
  @@hide_format = options[:hide_format]
345
400
  api_version = options[:api_version]
@@ -349,6 +404,7 @@ module GrapeSwagger
349
404
  api_doc = options[:api_documentation].dup
350
405
  specific_api_doc = options[:specific_api_documentation].dup
351
406
  @@models = options[:models] || []
407
+ i18n_scope = options[:i18n_scope]
352
408
 
353
409
  @@hide_documentation_path = options[:hide_documentation_path]
354
410
 
@@ -361,7 +417,11 @@ module GrapeSwagger
361
417
  @@documentation_class = self
362
418
 
363
419
  desc api_doc.delete(:desc), api_doc
420
+ params do
421
+ optional :locale, type: Symbol, desc: 'Locale of API documentation'
422
+ end
364
423
  get @@mount_path do
424
+ I18n.locale = params[:locale] || I18n.default_locale
365
425
  header['Access-Control-Allow-Origin'] = '*'
366
426
  header['Access-Control-Request-Method'] = '*'
367
427
 
@@ -375,14 +435,23 @@ module GrapeSwagger
375
435
  namespace_routes_array = namespace_routes.keys.map do |local_route|
376
436
  next if namespace_routes[local_route].map(&:route_hidden).all? { |value| value.respond_to?(:call) ? value.call : value }
377
437
 
378
- url_format = '.{format}' unless @@hide_format
438
+ url_format = '.{format}' unless @@hide_format
439
+ url_locale = "?locale=#{params[:locale]}" unless params[:locale].blank?
379
440
 
380
441
  original_namespace_name = target_class.combined_namespace_identifiers.key?(local_route) ? target_class.combined_namespace_identifiers[local_route] : local_route
381
442
  description = namespaces[original_namespace_name] && namespaces[original_namespace_name].options[:desc]
382
443
  description ||= "Operations about #{original_namespace_name.pluralize}"
444
+ description = @@documentation_class.translate(
445
+ description, i18n_scope,
446
+ [
447
+ :"#{original_namespace_name}.desc",
448
+ :"#{original_namespace_name}.description"
449
+ ],
450
+ namespace: original_namespace_name.pluralize
451
+ )
383
452
 
384
453
  {
385
- path: "/#{local_route}#{url_format}",
454
+ path: "/#{local_route}#{url_format}#{url_locale}",
386
455
  description: description
387
456
  }
388
457
  end.compact
@@ -392,7 +461,7 @@ module GrapeSwagger
392
461
  swaggerVersion: '1.2',
393
462
  produces: @@documentation_class.content_types_for(target_class),
394
463
  apis: namespace_routes_array,
395
- info: @@documentation_class.parse_info(extra_info)
464
+ info: @@documentation_class.parse_info(extra_info, scope: i18n_scope)
396
465
  }
397
466
 
398
467
  output[:authorizations] = authorizations unless authorizations.nil? || authorizations.empty?
@@ -403,13 +472,15 @@ module GrapeSwagger
403
472
  desc specific_api_doc.delete(:desc), { params:
404
473
  specific_api_doc.delete(:params) || {} }.merge(specific_api_doc)
405
474
  params do
475
+ optional :locale, type: Symbol, desc: 'Locale of API documentation'
406
476
  requires :name, type: String, desc: 'Resource name of mounted API'
407
477
  end
408
478
  get "#{@@mount_path}/:name" do
479
+ I18n.locale = params[:locale] || I18n.default_locale
409
480
  header['Access-Control-Allow-Origin'] = '*'
410
481
  header['Access-Control-Request-Method'] = '*'
411
482
 
412
- models = []
483
+ models = Set.new(@@models.dup)
413
484
  routes = target_class.combined_namespace_routes[params[:name]]
414
485
  error!('Not Found', 404) unless routes
415
486
 
@@ -427,22 +498,33 @@ module GrapeSwagger
427
498
 
428
499
  ops.each do |path, op_routes|
429
500
  operations = op_routes.map do |route|
430
- notes = @@documentation_class.as_markdown(route.route_detail || route.route_notes)
501
+ endpoint = target_class.endpoint_mapping[route.to_s.sub('(.:format)', '')]
502
+ endpoint_path = endpoint.options[:path] unless endpoint.nil?
503
+ i18n_key = [route.route_namespace, endpoint_path, route.route_method.downcase].flatten.join('/')
504
+ i18n_key = i18n_key.split('/').reject(&:empty?).join('.')
505
+
506
+ summary = @@documentation_class.translate(
507
+ route.route_description, i18n_scope,
508
+ [:"#{i18n_key}.desc", :"#{i18n_key}.description"]
509
+ )
510
+ notes = @@documentation_class.translate(
511
+ route.route_detail || route.route_notes, i18n_scope,
512
+ [:"#{i18n_key}.detail", :"#{i18n_key}.notes"]
513
+ )
514
+ notes = @@documentation_class.as_markdown(notes)
431
515
 
432
516
  http_codes = @@documentation_class.parse_http_codes(route.route_http_codes, models)
433
517
 
434
- models |= @@models if @@models.present?
435
-
436
- models |= Array(route.route_entity) if route.route_entity.present?
437
-
438
- models = @@documentation_class.models_with_included_presenters(models.flatten.compact)
518
+ models.merge(Array(route.route_entity)) if route.route_entity.present?
439
519
 
440
520
  operation = {
441
521
  notes: notes.to_s,
442
- summary: route.route_description || '',
522
+ summary: summary,
443
523
  nickname: route.route_nickname || (route.route_method + route.route_path.gsub(/[\/:\(\)\.]/, '-')),
444
524
  method: route.route_method,
445
- parameters: @@documentation_class.parse_header_params(route.route_headers) + @@documentation_class.parse_params(route.route_params, route.route_path, route.route_method),
525
+ parameters: @@documentation_class.parse_header_params(route.route_headers, scope: i18n_scope, key: i18n_key) +
526
+ @@documentation_class.parse_params(route.route_params, route.route_path, route.route_method,
527
+ scope: i18n_scope, key: i18n_key),
446
528
  type: route.route_is_array ? 'array' : 'void'
447
529
  }
448
530
  operation[:authorizations] = route.route_authorizations unless route.route_authorizations.nil? || route.route_authorizations.empty?
@@ -469,6 +551,8 @@ module GrapeSwagger
469
551
  }
470
552
  end
471
553
 
554
+ models = @@documentation_class.models_with_included_presenters(models.to_a.flatten.compact)
555
+
472
556
  # use custom resource naming if available
473
557
  if target_class.combined_namespace_identifiers.key? params[:name]
474
558
  resource_path = target_class.combined_namespace_identifiers[params[:name]]
@@ -485,7 +569,7 @@ module GrapeSwagger
485
569
 
486
570
  base_path = @@documentation_class.parse_base_path(options[:base_path], request)
487
571
  api_description[:basePath] = base_path if base_path && base_path.size > 0 && root_base_path != false
488
- api_description[:models] = @@documentation_class.parse_entity_models(models) unless models.empty?
572
+ api_description[:models] = @@documentation_class.parse_entity_models(models, scope: i18n_scope) unless models.empty?
489
573
  api_description[:authorizations] = authorizations if authorizations
490
574
 
491
575
  api_description