apipie-rails 0.8.2 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/rubocop-challenger.yml +28 -0
  3. data/.rubocop.yml +37 -0
  4. data/.rubocop_todo.yml +2001 -0
  5. data/CHANGELOG.md +14 -0
  6. data/README.rst +2 -2
  7. data/Rakefile +0 -5
  8. data/apipie-rails.gemspec +10 -7
  9. data/lib/apipie/dsl_definition.rb +3 -3
  10. data/lib/apipie/extractor/writer.rb +2 -2
  11. data/lib/apipie/generator/generator.rb +2 -0
  12. data/lib/apipie/generator/swagger/swagger.rb +2 -0
  13. data/lib/apipie/generator/swagger/type.rb +16 -0
  14. data/lib/apipie/generator/swagger/type_extractor.rb +70 -0
  15. data/lib/apipie/generator/swagger/warning.rb +77 -0
  16. data/lib/apipie/generator/swagger/warning_writer.rb +48 -0
  17. data/lib/apipie/method_description/api.rb +12 -0
  18. data/lib/apipie/method_description/apis_service.rb +82 -0
  19. data/lib/apipie/method_description.rb +1 -46
  20. data/lib/apipie/swagger_generator.rb +76 -81
  21. data/lib/apipie/validator.rb +4 -0
  22. data/lib/apipie/version.rb +1 -1
  23. data/lib/apipie-rails.rb +9 -1
  24. data/lib/generators/apipie/install/install_generator.rb +1 -1
  25. data/lib/generators/apipie/views_generator.rb +1 -1
  26. data/lib/tasks/apipie.rake +2 -2
  27. data/spec/dummy/Rakefile +1 -1
  28. data/spec/dummy/components/test_engine/test_engine.gemspec +1 -1
  29. data/spec/dummy/config/application.rb +1 -1
  30. data/spec/dummy/config/boot.rb +2 -2
  31. data/spec/dummy/config/environment.rb +1 -1
  32. data/spec/dummy/config/routes.rb +1 -0
  33. data/spec/dummy/config.ru +1 -1
  34. data/spec/dummy/script/rails +2 -2
  35. data/spec/lib/application_spec.rb +1 -1
  36. data/spec/lib/extractor/writer_spec.rb +7 -5
  37. data/spec/lib/generator/swagger/type_extractor_spec.rb +61 -0
  38. data/spec/lib/generator/swagger/warning_spec.rb +51 -0
  39. data/spec/lib/generator/swagger/warning_writer_spec.rb +59 -0
  40. data/spec/lib/method_description/apis_service_spec.rb +60 -0
  41. data/spec/lib/rake_spec.rb +1 -1
  42. data/spec/lib/swagger/rake_swagger_spec.rb +4 -4
  43. data/spec/spec_helper.rb +4 -4
  44. metadata +39 -38
data/CHANGELOG.md CHANGED
@@ -4,6 +4,20 @@
4
4
  Also deleted the `Gemfile` that was now a broken symlink.
5
5
  please use `export BUNDLE_GEMFILE='gemfiles/Gemfile.rails61'; bundle exec rspec` to run the test suite
6
6
 
7
+
8
+
9
+ ## [v0.9.0](https://github.com/Apipie/apipie-rails/tree/v0.9.0) (2023-01-03)
10
+ [Full Changelog](https://github.com/Apipie/apipie-rails/compare/v0.8.2...v0.9.0)
11
+ * [Refactor] Move Swagger types and warnings under `/generator` namespace [#803](https://github.com/Apipie/apipie-rails/pull/803) (Panos Dalitsouris)
12
+ * [Refactor] Creates `Apipie::MethodDescription::ApisService` [#805](https://github.com/Apipie/apipie-rails/pull/805) (Panos Dalitsouris)
13
+ * [Refactor] Change output folder to `spec/dummy/tmp/` [#804](https://github.com/Apipie/apipie-rails/pull/804) (Panos Dalitsouris)
14
+ * Fix tiny typo in docs [#798](https://github.com/Apipie/apipie-rails/pull/798) (Erik-B. Ernst)
15
+ * Fix Generated docs.json output [#787](https://github.com/Apipie/apipie-rails/pull/787) (Jeremy Liberman)
16
+ * Rubocop Fixes [#775](https://github.com/Apipie/apipie-rails/pull/775), [#778](https://github.com/Apipie/apipie-rails/pull/778), [#780](https://github.com/Apipie/apipie-rails/pull/780), [#790](https://github.com/Apipie/apipie-rails/pull/790), [#783](https://github.com/Apipie/apipie-rails/pull/783), [#785](https://github.com/Apipie/apipie-rails/pull/785) (RuboCop)
17
+ * Remove/clean up dev dependencies and unused rake tasks [#777](https://github.com/Apipie/apipie-rails/pull/777) (Mathieu Jobin)
18
+ - remove unused rake task
19
+ * Setup Rubocop Challenger [#776](https://github.com/Apipie/apipie-rails/pull/776) (Mathieu Jobin)
20
+
7
21
  ## [v0.8.2](https://github.com/Apipie/apipie-rails/tree/v0.8.2) (2022-09-03)
8
22
  [Full Changelog](https://github.com/Apipie/apipie-rails/compare/v0.8.1...v0.8.2)
9
23
  * Allow custom validators to opt out of allow_blank behavior [#762](https://github.com/Apipie/apipie-rails/pull/762). (Stephen Hanson)
data/README.rst CHANGED
@@ -2,8 +2,8 @@
2
2
  API Documentation Tool
3
3
  ========================
4
4
 
5
- .. image:: https://travis-ci.org/Apipie/apipie-rails.svg?branch=master
6
- :target: https://travis-ci.org/Apipie/apipie-rails
5
+ .. image:: https://github.com/Apipie/apipie-rails/actions/workflows/build.yml/badge.svg
6
+ :target: https://github.com/Apipie/apipie-rails/actions/workflows/build.yml
7
7
  .. image:: https://codeclimate.com/github/Apipie/apipie-rails.svg
8
8
  :target: https://codeclimate.com/github/Apipie/apipie-rails
9
9
  .. image:: https://badges.gitter.im/Apipie/apipie-rails.svg
data/Rakefile CHANGED
@@ -6,8 +6,3 @@ RSpec::Core::RakeTask.new(:spec)
6
6
  desc 'Default: run specs.'
7
7
  task :default => :spec
8
8
 
9
- desc "Generate code coverage"
10
- RSpec::Core::RakeTask.new(:coverage) do |t|
11
- t.rcov = true
12
- t.rcov_opts = ['--exclude', 'spec']
13
- end
data/apipie-rails.gemspec CHANGED
@@ -1,5 +1,5 @@
1
1
  # -*- encoding: utf-8 -*-
2
- $:.push File.expand_path("../lib", __FILE__)
2
+ $:.push File.expand_path('lib', __dir__)
3
3
  require "apipie/version"
4
4
 
5
5
  Gem::Specification.new do |s|
@@ -18,13 +18,16 @@ Gem::Specification.new do |s|
18
18
 
19
19
  s.add_dependency "actionpack", ">= 5.0"
20
20
  s.add_dependency "activesupport", ">= 5.0"
21
+
22
+ # Optional dependencies
23
+ s.add_development_dependency "maruku" # for Markdown support
24
+ s.add_development_dependency "RedCloth" # for Textile support
25
+
26
+ # Dev/tests only dependencies
27
+ s.add_development_dependency "json-schema", "~> 2.8"
21
28
  s.add_development_dependency "rspec-rails", "~> 3.0"
22
- s.add_development_dependency "sqlite3"
23
- s.add_development_dependency "minitest"
24
- s.add_development_dependency "maruku"
25
- s.add_development_dependency "RedCloth"
26
29
  s.add_development_dependency "rake"
27
- s.add_development_dependency "rdoc"
30
+ s.add_development_dependency 'rubocop_challenger'
28
31
  s.add_development_dependency "simplecov"
29
- s.add_development_dependency "json-schema", "~> 2.8"
32
+ s.add_development_dependency "sqlite3"
30
33
  end
@@ -278,7 +278,7 @@ module Apipie
278
278
  end
279
279
  end
280
280
 
281
- if (Apipie.configuration.validate == :implicitly || Apipie.configuration.validate == true)
281
+ if Apipie.configuration.validate == :implicitly || Apipie.configuration.validate == true
282
282
  old_method = instance_method(description.method)
283
283
 
284
284
  define_method(description.method) do |*args|
@@ -360,7 +360,7 @@ module Apipie
360
360
  # Reuses param group for this method. The definition is looked up
361
361
  # in scope of this controller. If the group was defined in
362
362
  # different controller, the second param can be used to specify it.
363
- # when using action_aware parmas, you can specify :as =>
363
+ # when using action_aware params, you can specify :as =>
364
364
  # :create or :update to explicitly say how it should behave
365
365
  def param_group(name, scope_or_options = nil, options = {})
366
366
  if scope_or_options.is_a? Hash
@@ -533,7 +533,7 @@ module Apipie
533
533
  end
534
534
 
535
535
  def _apipie_perform_concern_subst(string)
536
- return _apipie_concern_subst.reduce(string) do |ret, (key, val)|
536
+ _apipie_concern_subst.reduce(string) do |ret, (key, val)|
537
537
  ret.gsub(":#{key}", val)
538
538
  end
539
539
  end
@@ -116,7 +116,7 @@ module Apipie
116
116
 
117
117
  def convert_file_value hash
118
118
  hash.each do |k, v|
119
- if (v.is_a?(Rack::Test::UploadedFile) || v.is_a?(ActionDispatch::Http::UploadedFile))
119
+ if v.is_a?(Rack::Test::UploadedFile) || v.is_a?(ActionDispatch::Http::UploadedFile)
120
120
  hash[k] = "<FILE CONTENT '#{v.original_filename}'>"
121
121
  elsif v.is_a?(Hash)
122
122
  hash[k] = convert_file_value(v)
@@ -323,7 +323,7 @@ module Apipie
323
323
  desc ||= case @action.to_s
324
324
  when "show", "create", "update", "destroy"
325
325
  name = name.singularize
326
- "#{@action.capitalize} #{name =~ /^[aeiou]/ ? "an" : "a"} #{name}"
326
+ "#{@action.capitalize} #{name =~ /^[aeiou]/ ? 'an' : 'a'} #{name}"
327
327
  when "index"
328
328
  "List #{name}"
329
329
  end
@@ -0,0 +1,2 @@
1
+ module Apipie::Generator
2
+ end
@@ -0,0 +1,2 @@
1
+ module Apipie::Generator::Swagger
2
+ end
@@ -0,0 +1,16 @@
1
+ class Apipie::Generator::Swagger::Type
2
+ attr_reader :str_format
3
+
4
+ def initialize(type, str_format = nil)
5
+ @type = type
6
+ @str_format = str_format
7
+ end
8
+
9
+ def to_s
10
+ @type
11
+ end
12
+
13
+ def ==(other)
14
+ other.to_s == self.to_s
15
+ end
16
+ end
@@ -0,0 +1,70 @@
1
+ class Apipie::Generator::Swagger::TypeExtractor
2
+ TYPES = {
3
+ numeric: 'number',
4
+ hash: 'object',
5
+ array: 'array',
6
+ enum: 'enum',
7
+
8
+ # see https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#data-types
9
+ integer: Apipie::Generator::Swagger::Type.new('integer', 'int32'),
10
+ long: Apipie::Generator::Swagger::Type.new('integer', 'int64'),
11
+ number: Apipie::Generator::Swagger::Type.new('number'),
12
+ float: Apipie::Generator::Swagger::Type.new('number', 'float'),
13
+ double: Apipie::Generator::Swagger::Type.new('number', 'double'),
14
+ string: Apipie::Generator::Swagger::Type.new('string'),
15
+ byte: Apipie::Generator::Swagger::Type.new('string', 'byte'),
16
+ binary: Apipie::Generator::Swagger::Type.new('string', 'binary'),
17
+ boolean: Apipie::Generator::Swagger::Type.new('boolean'),
18
+ date: Apipie::Generator::Swagger::Type.new('string', 'date'),
19
+ dateTime: Apipie::Generator::Swagger::Type.new('string', 'date-time'),
20
+ password: Apipie::Generator::Swagger::Type.new('string', 'password')
21
+ }
22
+
23
+ # @param [Apipie::Validator::BaseValidator, ResponseDescriptionAdapter::PropDesc::Validator, nil] validator
24
+ def initialize(validator)
25
+ @validator = validator
26
+ end
27
+
28
+ # @param [Hash<Symbol, Apipie::Generator::Swagger::Warning>] warnings
29
+ def extract_with_warnings(warnings = {})
30
+ if boolean? && warnings[:boolean].present?
31
+ Apipie::Generator::Swagger::WarningWriter.instance.warn(warnings[:boolean])
32
+ end
33
+
34
+ extract
35
+ end
36
+
37
+ private
38
+
39
+ def extract
40
+ expected_type =
41
+ if string?
42
+ :string
43
+ elsif boolean?
44
+ :boolean
45
+ elsif enum?
46
+ :enum
47
+ else
48
+ @validator.expected_type.to_sym
49
+ end
50
+
51
+ TYPES[expected_type] || @validator.expected_type
52
+ end
53
+
54
+ def string?
55
+ @validator.blank?
56
+ end
57
+
58
+ def enum?
59
+ @validator.is_a?(Apipie::Validator::EnumValidator) ||
60
+ (@validator.respond_to?(:is_enum?) && @validator.is_enum?)
61
+ end
62
+
63
+ def boolean?
64
+ @_boolean ||= enum? && boolean_values?
65
+ end
66
+
67
+ def boolean_values?
68
+ @validator.values.to_set == Set.new([true, false])
69
+ end
70
+ end
@@ -0,0 +1,77 @@
1
+ class Apipie::Generator::Swagger::Warning
2
+ MISSING_METHOD_SUMMARY_CODE = 100
3
+ ADDED_MISSING_SLASH_CODE = 101
4
+ NO_RETURN_CODES_SPECIFIED_CODE = 102
5
+ HASH_WITHOUT_INTERNAL_TYPESPEC_CODE = 103
6
+ OPTIONAL_PARAM_IN_PATH_CODE = 104
7
+ OPTIONAL_WITHOUT_DEFAULT_VALUE_CODE = 105
8
+ PARAM_IGNORED_IN_FORM_DATA_CODE = 106
9
+ PATH_PARAM_NOT_DESCRIBED_CODE = 107
10
+ INFERRING_BOOLEAN_CODE = 108
11
+
12
+ CODES = {
13
+ missing_method_summary: MISSING_METHOD_SUMMARY_CODE,
14
+ added_missing_slash: ADDED_MISSING_SLASH_CODE,
15
+ no_return_codes_specified: NO_RETURN_CODES_SPECIFIED_CODE,
16
+ hash_without_internal_typespec: HASH_WITHOUT_INTERNAL_TYPESPEC_CODE,
17
+ optional_param_in_path: OPTIONAL_PARAM_IN_PATH_CODE,
18
+ optional_without_default_value: OPTIONAL_WITHOUT_DEFAULT_VALUE_CODE,
19
+ param_ignored_in_form_data: PARAM_IGNORED_IN_FORM_DATA_CODE,
20
+ path_param_not_described_code: PATH_PARAM_NOT_DESCRIBED_CODE,
21
+ inferring_boolean: INFERRING_BOOLEAN_CODE
22
+ }
23
+
24
+ MESSAGES = {
25
+ MISSING_METHOD_SUMMARY_CODE => "Missing short description for method",
26
+ ADDED_MISSING_SLASH_CODE => "Added missing / at beginning of path: %{path}",
27
+ HASH_WITHOUT_INTERNAL_TYPESPEC_CODE => "The parameter :%{parameter} is a generic Hash without an internal type specification",
28
+ NO_RETURN_CODES_SPECIFIED_CODE => "No return codes ('errors') specified",
29
+ OPTIONAL_PARAM_IN_PATH_CODE => "The parameter :%{parameter} is 'in-path'. Ignoring 'not required' in DSL",
30
+ OPTIONAL_WITHOUT_DEFAULT_VALUE_CODE => "The parameter :%{parameter} is optional but default value is not specified (use :default_value => ...)",
31
+ PARAM_IGNORED_IN_FORM_DATA_CODE => "Ignoring param :%{parameter} -- cannot include Hash without fields in a formData specification",
32
+ PATH_PARAM_NOT_DESCRIBED_CODE => "The parameter :%{name} appears in the path %{path} but is not described",
33
+ INFERRING_BOOLEAN_CODE => "The parameter [%{parameter}] is Enum with [true, false] values. Inferring 'boolean'"
34
+ }
35
+
36
+ attr_reader :code
37
+
38
+ def initialize(code, info_message, method_id)
39
+ @code = code
40
+ @info_message = info_message
41
+ @method_id = method_id
42
+ end
43
+
44
+ def id
45
+ "#{@method_id}#{@code}#{@info_message}"
46
+ end
47
+
48
+ def warning_message
49
+ "WARNING (#{@code}): [#{@method_id}] -- #{@info_message}"
50
+ end
51
+
52
+ def warn
53
+ Warning.warn(warning_message)
54
+ end
55
+
56
+ def warn_through_writer
57
+ Apipie::Generator::Swagger::WarningWriter.instance.warn(self)
58
+ end
59
+
60
+ # @param [Integer] code
61
+ # @param [Hash] message_attributes
62
+ #
63
+ # @return [Apipie::Generator::Swagger::Warning]
64
+ def self.for_code(code, method_id, message_attributes = {})
65
+ if !CODES.values.include?(code)
66
+ raise ArgumentError, 'Unknown warning code'
67
+ end
68
+
69
+ info_message = if message_attributes.present?
70
+ self::MESSAGES[code] % message_attributes
71
+ else
72
+ self::MESSAGES[code]
73
+ end
74
+
75
+ Apipie::Generator::Swagger::Warning.new(code, info_message, method_id)
76
+ end
77
+ end
@@ -0,0 +1,48 @@
1
+ class Apipie::Generator::Swagger::WarningWriter
2
+ include Singleton
3
+
4
+ def initialize
5
+ @issued_warnings = []
6
+ end
7
+
8
+ # @param [Apipie::Generator::Swagger::Warning] warning
9
+ def warn(warning)
10
+ return if muted_warning?(warning)
11
+
12
+ warning.warn
13
+
14
+ @issued_warnings << warning.id
15
+ end
16
+
17
+ def issued_warnings?
18
+ @issued_warnings.count > 0
19
+ end
20
+
21
+ private
22
+
23
+ # @param [Apipie::Generator::Swagger::Warning] warning
24
+ #
25
+ # @return [TrueClass, FalseClass]
26
+ def muted_warning?(warning)
27
+ @issued_warnings.include?(warning.id) ||
28
+ suppressed_warning?(warning.code) ||
29
+ suppress_warnings?
30
+ end
31
+
32
+ # @param [Integer] warning_number
33
+ #
34
+ # @return [TrueClass, FalseClass]
35
+ def suppressed_warning?(warning_number)
36
+ suppress_warnings_config.is_a?(Array) && suppress_warnings_config.include?(warning_number)
37
+ end
38
+
39
+ # @return [TrueClass, FalseClass]
40
+ def suppress_warnings?
41
+ suppress_warnings_config == true
42
+ end
43
+
44
+ # @return [FalseClass, TrueClass, Array]
45
+ def suppress_warnings_config
46
+ Apipie.configuration.swagger_suppress_warnings
47
+ end
48
+ end
@@ -0,0 +1,12 @@
1
+ class Apipie::MethodDescription::Api
2
+ attr_accessor :short_description, :path, :http_method, :from_routes,
3
+ :options, :returns
4
+
5
+ def initialize(method, path, desc, options)
6
+ @http_method = method.to_s
7
+ @path = path
8
+ @short_description = desc
9
+ @from_routes = options[:from_routes]
10
+ @options = options
11
+ end
12
+ end
@@ -0,0 +1,82 @@
1
+ # Service that builds the appropriate Apipie::MethodDescription::Api
2
+ # required by Apipie::MethodDescription
3
+ #
4
+ class Apipie::MethodDescription::ApisService
5
+ # @param [Apipie::ResourceDescription] resource
6
+ # @param [Symbol] controller_action
7
+ # @param [Hash] dsl
8
+ def initialize(resource, controller_action, dsl)
9
+ @resource = resource
10
+ @controller_action = controller_action
11
+ @dsl = dsl
12
+ end
13
+
14
+ # @return [Array<Apipie::MethodDescription::Api>]
15
+ def call
16
+ api_args.map do |http_method, path, desc, opts|
17
+ Apipie::MethodDescription::Api.new(
18
+ http_method,
19
+ concern_subst(path),
20
+ concern_subst(desc),
21
+ opts
22
+ )
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ def concern_subst(path)
29
+ return if path.blank?
30
+
31
+ if from_concern?
32
+ @resource.controller._apipie_perform_concern_subst(path)
33
+ else
34
+ path
35
+ end
36
+ end
37
+
38
+ # @return [Array<Array>]
39
+ def api_args
40
+ return @dsl[:api_args] if !api_from_routes?
41
+
42
+ api_args = @dsl[:api_args].dup
43
+
44
+ api_args_from_routes = routes.map do |route_info|
45
+ [
46
+ route_info[:verb],
47
+ route_info[:path],
48
+ route_info[:desc],
49
+ (route_info[:options] || {}).merge(from_routes: true)
50
+ ]
51
+ end
52
+
53
+ api_args.concat(api_args_from_routes)
54
+ end
55
+
56
+ def api_from_routes?
57
+ @dsl[:api_from_routes].present?
58
+ end
59
+
60
+ def from_concern?
61
+ @dsl[:from_concern] == true
62
+ end
63
+
64
+ def description
65
+ @dsl[:api_from_routes][:desc]
66
+ end
67
+
68
+ def options
69
+ @dsl[:api_from_routes][:options]
70
+ end
71
+
72
+ # @return [Array<Hash>]
73
+ def routes
74
+ Apipie.routes_for_action(
75
+ @resource.controller,
76
+ @controller_action,
77
+ { desc: description, options: options }
78
+ )
79
+ end
80
+ end
81
+
82
+
@@ -1,22 +1,6 @@
1
- require 'set'
2
1
  module Apipie
3
2
 
4
3
  class MethodDescription
5
-
6
- class Api
7
-
8
- attr_accessor :short_description, :path, :http_method, :from_routes, :options, :returns
9
-
10
- def initialize(method, path, desc, options)
11
- @http_method = method.to_s
12
- @path = path
13
- @short_description = desc
14
- @from_routes = options[:from_routes]
15
- @options = options
16
- end
17
-
18
- end
19
-
20
4
  attr_reader :full_description, :method, :resource, :apis, :examples, :see, :formats, :headers, :show
21
5
  attr_accessor :metadata
22
6
 
@@ -24,9 +8,7 @@ module Apipie
24
8
  @method = method.to_s
25
9
  @resource = resource
26
10
  @from_concern = dsl_data[:from_concern]
27
- @apis = api_data(dsl_data).map do |mthd, path, desc, opts|
28
- MethodDescription::Api.new(mthd, concern_subst(path), concern_subst(desc), opts)
29
- end
11
+ @apis = ApisService.new(resource, method, dsl_data).call
30
12
 
31
13
  desc = dsl_data[:description] || ''
32
14
  @full_description = Apipie.markup_to_html(desc)
@@ -203,23 +185,6 @@ module Apipie
203
185
 
204
186
  private
205
187
 
206
- def api_data(dsl_data)
207
- ret = dsl_data[:api_args].dup
208
- if dsl_data[:api_from_routes]
209
- desc = dsl_data[:api_from_routes][:desc]
210
- options = dsl_data[:api_from_routes][:options]
211
-
212
- api_from_routes = Apipie.routes_for_action(resource.controller, method, {:desc => desc, :options => options}).map do |route_info|
213
- [route_info[:verb],
214
- route_info[:path],
215
- route_info[:desc],
216
- (route_info[:options] || {}).merge(:from_routes => true)]
217
- end
218
- ret.concat(api_from_routes)
219
- end
220
- ret
221
- end
222
-
223
188
  def merge_params(params, new_params)
224
189
  new_param_names = Set.new(new_params.map(&:name))
225
190
  params.delete_if { |p| new_param_names.include?(p.name) }
@@ -259,16 +224,6 @@ module Apipie
259
224
  example << "\n" << format_example_data(ex[:response_data]).to_s if ex[:response_data]
260
225
  example
261
226
  end
262
-
263
- def concern_subst(string)
264
- return if string.nil?
265
- if from_concern?
266
- resource.controller._apipie_perform_concern_subst(string)
267
- else
268
- string
269
- end
270
- end
271
-
272
227
  end
273
228
 
274
229
  end