apipie-rails 0.5.20 → 1.2.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (179) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/build.yml +12 -21
  3. data/.github/workflows/rubocop-challenger.yml +28 -0
  4. data/.github/workflows/rubocop.yml +18 -0
  5. data/.gitignore +1 -0
  6. data/.rubocop.yml +128 -0
  7. data/.rubocop_todo.yml +2062 -0
  8. data/.vscode/settings.json +3 -0
  9. data/CHANGELOG.md +144 -0
  10. data/Gemfile +20 -0
  11. data/README.rst +106 -15
  12. data/Rakefile +0 -5
  13. data/apipie-rails.gemspec +18 -9
  14. data/app/controllers/apipie/apipies_controller.rb +14 -29
  15. data/app/helpers/apipie_helper.rb +1 -1
  16. data/app/public/apipie/javascripts/bundled/bootstrap-collapse.js +70 -41
  17. data/app/public/apipie/javascripts/bundled/bootstrap.js +1033 -479
  18. data/app/public/apipie/javascripts/bundled/jquery.js +5 -5
  19. data/app/public/apipie/stylesheets/bundled/bootstrap-responsive.min.css +9 -12
  20. data/app/public/apipie/stylesheets/bundled/bootstrap.min.css +9 -689
  21. data/app/views/apipie/apipies/_deprecation.html.erb +16 -0
  22. data/app/views/apipie/apipies/_params.html.erb +7 -1
  23. data/config/locales/en.yml +8 -0
  24. data/config/locales/ko.yml +31 -0
  25. data/gemfiles/Gemfile.tools +9 -0
  26. data/lib/apipie/apipie_module.rb +7 -7
  27. data/lib/apipie/application.rb +132 -97
  28. data/lib/apipie/configuration.rb +43 -33
  29. data/lib/apipie/dsl_definition.rb +39 -27
  30. data/lib/apipie/error_description.rb +3 -3
  31. data/lib/apipie/errors.rb +16 -16
  32. data/lib/apipie/extractor/collector.rb +4 -5
  33. data/lib/apipie/extractor/recorder.rb +33 -6
  34. data/lib/apipie/extractor/writer.rb +14 -14
  35. data/lib/apipie/extractor.rb +6 -9
  36. data/lib/apipie/generator/config.rb +12 -0
  37. data/lib/apipie/generator/generator.rb +2 -0
  38. data/lib/apipie/generator/swagger/computed_interface_id.rb +23 -0
  39. data/lib/apipie/generator/swagger/config.rb +80 -0
  40. data/lib/apipie/generator/swagger/context.rb +38 -0
  41. data/lib/apipie/generator/swagger/method_description/api_decorator.rb +20 -0
  42. data/lib/apipie/generator/swagger/method_description/api_schema_service.rb +89 -0
  43. data/lib/apipie/generator/swagger/method_description/decorator.rb +22 -0
  44. data/lib/apipie/generator/swagger/method_description/parameters_service.rb +139 -0
  45. data/lib/apipie/generator/swagger/method_description/response_schema_service.rb +46 -0
  46. data/lib/apipie/generator/swagger/method_description/response_service.rb +58 -0
  47. data/lib/apipie/generator/swagger/method_description.rb +2 -0
  48. data/lib/apipie/generator/swagger/operation_id.rb +51 -0
  49. data/lib/apipie/generator/swagger/param_description/builder.rb +105 -0
  50. data/lib/apipie/generator/swagger/param_description/composite.rb +119 -0
  51. data/lib/apipie/generator/swagger/param_description/description.rb +15 -0
  52. data/lib/apipie/generator/swagger/param_description/in.rb +37 -0
  53. data/lib/apipie/generator/swagger/param_description/name.rb +18 -0
  54. data/lib/apipie/generator/swagger/param_description/path_params_composite.rb +61 -0
  55. data/lib/apipie/generator/swagger/param_description/referenced_composite.rb +36 -0
  56. data/lib/apipie/generator/swagger/param_description/type.rb +115 -0
  57. data/lib/apipie/generator/swagger/param_description.rb +18 -0
  58. data/lib/apipie/generator/swagger/path_decorator.rb +36 -0
  59. data/lib/apipie/generator/swagger/referenced_definitions.rb +17 -0
  60. data/lib/apipie/generator/swagger/resource_description_collection.rb +30 -0
  61. data/lib/apipie/generator/swagger/resource_description_composite.rb +56 -0
  62. data/lib/apipie/generator/swagger/schema.rb +63 -0
  63. data/lib/apipie/generator/swagger/swagger.rb +2 -0
  64. data/lib/apipie/generator/swagger/type.rb +16 -0
  65. data/lib/apipie/generator/swagger/type_extractor.rb +51 -0
  66. data/lib/apipie/generator/swagger/warning.rb +74 -0
  67. data/lib/apipie/generator/swagger/warning_writer.rb +54 -0
  68. data/lib/apipie/helpers.rb +3 -3
  69. data/lib/apipie/markup.rb +9 -8
  70. data/lib/apipie/method_description/api.rb +12 -0
  71. data/lib/apipie/method_description/apis_service.rb +82 -0
  72. data/lib/apipie/method_description.rb +12 -56
  73. data/lib/apipie/param_description/deprecation.rb +24 -0
  74. data/lib/apipie/param_description.rb +57 -24
  75. data/lib/apipie/resource_description.rb +42 -14
  76. data/lib/apipie/response_description.rb +3 -3
  77. data/lib/apipie/response_description_adapter.rb +10 -8
  78. data/lib/apipie/routing.rb +1 -1
  79. data/lib/apipie/rspec/response_validation_helper.rb +3 -3
  80. data/lib/apipie/static_dispatcher.rb +3 -1
  81. data/lib/apipie/swagger_generator.rb +28 -691
  82. data/lib/apipie/validator.rb +40 -10
  83. data/lib/apipie/version.rb +1 -1
  84. data/lib/apipie-rails.rb +36 -5
  85. data/lib/generators/apipie/install/install_generator.rb +1 -1
  86. data/lib/generators/apipie/views_generator.rb +1 -1
  87. data/lib/tasks/apipie.rake +35 -30
  88. data/spec/controllers/api/v2/architectures_controller_spec.rb +10 -3
  89. data/spec/controllers/api/v2/empty_middle_controller_spec.rb +23 -0
  90. data/spec/controllers/api/v2/nested/resources_controller_spec.rb +18 -2
  91. data/spec/controllers/api/v2/sub/footguns_controller_spec.rb +19 -0
  92. data/spec/controllers/included_param_group_controller_spec.rb +13 -0
  93. data/spec/{lib/swagger/response_validation_spec.rb → controllers/pets_controller_spec.rb} +26 -32
  94. data/spec/controllers/users_controller_spec.rb +47 -6
  95. data/spec/dummy/Rakefile +1 -1
  96. data/spec/dummy/app/controllers/api/v2/architectures_controller.rb +2 -1
  97. data/spec/dummy/app/controllers/api/v2/base_controller.rb +6 -0
  98. data/spec/dummy/app/controllers/api/v2/empty_middle_controller.rb +14 -0
  99. data/spec/dummy/app/controllers/api/v2/nested/resources_controller.rb +2 -2
  100. data/spec/dummy/app/controllers/api/v2/sub/footguns_controller.rb +30 -0
  101. data/spec/dummy/app/controllers/concerns_controller.rb +1 -1
  102. data/spec/dummy/app/controllers/{concerns/extending_concern.rb → extending_concern.rb} +0 -2
  103. data/spec/dummy/app/controllers/included_param_group_controller.rb +19 -0
  104. data/spec/dummy/app/controllers/overridden_concerns_controller.rb +2 -2
  105. data/spec/dummy/app/controllers/pets_controller.rb +4 -4
  106. data/spec/dummy/app/controllers/pets_using_self_describing_classes_controller.rb +2 -2
  107. data/spec/dummy/app/controllers/{concerns/sample_controller.rb → sample_controller.rb} +0 -2
  108. data/spec/dummy/app/controllers/twitter_example_controller.rb +2 -2
  109. data/spec/dummy/app/controllers/users_controller.rb +17 -5
  110. data/spec/dummy/app/helpers/random_param_group.rb +8 -0
  111. data/spec/dummy/components/test_engine/test_engine.gemspec +1 -1
  112. data/spec/dummy/config/application.rb +2 -5
  113. data/spec/dummy/config/boot.rb +2 -2
  114. data/spec/dummy/config/environment.rb +1 -1
  115. data/spec/dummy/config/environments/development.rb +0 -3
  116. data/spec/dummy/config/environments/production.rb +0 -3
  117. data/spec/dummy/config/environments/test.rb +0 -5
  118. data/spec/dummy/config/initializers/apipie.rb +2 -2
  119. data/spec/dummy/config/routes.rb +8 -0
  120. data/spec/dummy/config.ru +1 -1
  121. data/spec/dummy/script/rails +2 -2
  122. data/spec/{controllers → lib/apipie}/apipies_controller_spec.rb +95 -23
  123. data/spec/lib/apipie/application_spec.rb +62 -0
  124. data/spec/lib/apipie/configuration_spec.rb +38 -0
  125. data/spec/lib/apipie/extractor/collector_spec.rb +57 -0
  126. data/spec/lib/apipie/extractor/recorder_spec.rb +77 -0
  127. data/spec/lib/{extractor → apipie/extractor}/writer_spec.rb +8 -6
  128. data/spec/lib/{file_handler_spec.rb → apipie/file_handler_spec.rb} +7 -0
  129. data/spec/lib/apipie/generator/swagger/config_spec.rb +19 -0
  130. data/spec/lib/apipie/generator/swagger/context_spec.rb +56 -0
  131. data/spec/lib/apipie/generator/swagger/method_description/api_schema_service_spec.rb +119 -0
  132. data/spec/lib/apipie/generator/swagger/method_description/response_schema_service_spec.rb +105 -0
  133. data/spec/lib/apipie/generator/swagger/operation_id_spec.rb +63 -0
  134. data/spec/lib/apipie/generator/swagger/param_description/builder_spec.rb +203 -0
  135. data/spec/lib/apipie/generator/swagger/param_description/composite_spec.rb +95 -0
  136. data/spec/lib/apipie/generator/swagger/param_description/description_spec.rb +79 -0
  137. data/spec/lib/apipie/generator/swagger/param_description/in_spec.rb +86 -0
  138. data/spec/lib/apipie/generator/swagger/param_description/name_spec.rb +81 -0
  139. data/spec/lib/apipie/generator/swagger/param_description/type_spec.rb +178 -0
  140. data/spec/lib/apipie/generator/swagger/param_description_spec.rb +28 -0
  141. data/spec/lib/apipie/generator/swagger/path_decorator_spec.rb +57 -0
  142. data/spec/lib/apipie/generator/swagger/referenced_definitions_spec.rb +35 -0
  143. data/spec/lib/apipie/generator/swagger/resource_description_composite_spec.rb +37 -0
  144. data/spec/lib/apipie/generator/swagger/resource_descriptions_collection_spec.rb +57 -0
  145. data/spec/lib/apipie/generator/swagger/schema_spec.rb +89 -0
  146. data/spec/lib/apipie/generator/swagger/type_extractor_spec.rb +38 -0
  147. data/spec/lib/apipie/generator/swagger/warning_spec.rb +51 -0
  148. data/spec/lib/apipie/generator/swagger/warning_writer_spec.rb +71 -0
  149. data/spec/lib/apipie/method_description/apis_service_spec.rb +60 -0
  150. data/spec/lib/apipie/method_description_spec.rb +133 -0
  151. data/spec/lib/apipie/no_documented_method_spec.rb +17 -0
  152. data/spec/lib/apipie/param_description/deprecation_spec.rb +31 -0
  153. data/spec/lib/{param_description_spec.rb → apipie/param_description_spec.rb} +332 -6
  154. data/spec/lib/{param_group_spec.rb → apipie/param_group_spec.rb} +6 -5
  155. data/spec/lib/apipie/resource_description_spec.rb +91 -0
  156. data/spec/lib/apipie/response_does_not_match_swagger_schema_spec.rb +35 -0
  157. data/spec/lib/apipie/swagger_generator_spec.rb +94 -0
  158. data/spec/lib/{validator_spec.rb → apipie/validator_spec.rb} +48 -12
  159. data/spec/lib/rake_spec.rb +3 -5
  160. data/spec/lib/swagger/openapi_2_0_schema.json +8 -1
  161. data/spec/lib/swagger/rake_swagger_spec.rb +24 -9
  162. data/spec/lib/swagger/swagger_dsl_spec.rb +18 -12
  163. data/spec/lib/validators/array_validator_spec.rb +1 -1
  164. data/spec/spec_helper.rb +10 -32
  165. data/spec/support/custom_bool_validator.rb +17 -0
  166. data/spec/{controllers → test_engine}/memes_controller_spec.rb +1 -1
  167. metadata +169 -122
  168. data/Gemfile +0 -1
  169. data/gemfiles/Gemfile.rails42 +0 -14
  170. data/gemfiles/Gemfile.rails42.lock +0 -160
  171. data/gemfiles/Gemfile.rails52 +0 -9
  172. data/gemfiles/Gemfile.rails60 +0 -10
  173. data/gemfiles/Gemfile.rails61 +0 -10
  174. data/spec/lib/application_spec.rb +0 -49
  175. data/spec/lib/method_description_spec.rb +0 -98
  176. data/spec/lib/resource_description_spec.rb +0 -48
  177. data/spec/support/rails-42-ruby-26.rb +0 -15
  178. /data/spec/lib/{extractor → apipie/extractor/recorder}/middleware_spec.rb +0 -0
  179. /data/spec/lib/{extractor → apipie}/extractor_spec.rb +0 -0
@@ -1,31 +1,45 @@
1
1
  module Apipie
2
2
  class Configuration
3
+ extend Forwardable
3
4
 
4
5
  attr_accessor :app_name, :app_info, :copyright, :compress_examples,
5
6
  :markup, :disqus_shortname,
6
7
  :api_base_url, :doc_base_url, :required_by_default, :layout,
7
8
  :default_version, :debug, :version_in_url, :namespaced_resources,
8
- :validate, :validate_value, :validate_presence, :validate_key, :authenticate, :doc_path,
9
+ :validate, :validate_value, :validate_presence, :validate_key, :action_on_non_validated_keys, :authenticate, :doc_path,
9
10
  :show_all_examples, :process_params, :update_checksum, :checksum_path,
10
11
  :link_extension, :record, :languages, :translate, :locale, :default_locale,
11
- :persist_show_in_doc, :authorize,
12
- :swagger_include_warning_tags, :swagger_content_type_input, :swagger_json_input_uses_refs,
13
- :swagger_suppress_warnings, :swagger_api_host, :swagger_generate_x_computed_id_field,
14
- :swagger_allow_additional_properties_in_response, :swagger_responses_use_refs
15
-
16
- alias_method :validate?, :validate
17
- alias_method :required_by_default?, :required_by_default
18
- alias_method :namespaced_resources?, :namespaced_resources
19
- alias_method :swagger_include_warning_tags?, :swagger_include_warning_tags
20
- alias_method :swagger_json_input_uses_refs?, :swagger_json_input_uses_refs
21
- alias_method :swagger_responses_use_refs?, :swagger_responses_use_refs
22
- alias_method :swagger_generate_x_computed_id_field?, :swagger_generate_x_computed_id_field
12
+ :persist_show_in_doc, :authorize, :ignore_allow_blank_false
13
+
14
+ def_delegators :swagger, *Apipie::Generator::Swagger::Config.deprecated_methods
15
+
16
+ def swagger
17
+ Apipie::Generator::Swagger::Config.instance
18
+ end
19
+
20
+ def generator
21
+ Apipie::Generator::Config.instance
22
+ end
23
+
24
+ alias validate? validate
25
+ alias required_by_default? required_by_default
26
+ alias namespaced_resources? namespaced_resources
23
27
 
24
28
  # matcher to be used in Dir.glob to find controllers to be reloaded e.g.
25
29
  #
26
30
  # "#{Rails.root}/app/controllers/api/*.rb"
27
31
  attr_accessor :api_controllers_matcher
28
32
 
33
+ # An object that responds to a `.call(controller)` method responsible for
34
+ # matching the correct controller action
35
+ attr_reader :api_action_matcher
36
+
37
+ def api_action_matcher=(callable)
38
+ raise 'Must implement .call method' unless callable.respond_to?(:call)
39
+
40
+ @api_action_matcher = callable
41
+ end
42
+
29
43
  # set to true if you want to reload the controllers at each refresh of the
30
44
  # documentation. It requires +:api_controllers_matcher+ to be set to work
31
45
  # properly.
@@ -44,35 +58,36 @@ module Apipie
44
58
 
45
59
  def reload_controllers?
46
60
  @reload_controllers = Rails.env.development? unless defined? @reload_controllers
47
- return @reload_controllers && @api_controllers_matcher
61
+
62
+ @reload_controllers && @api_controllers_matcher
48
63
  end
49
64
 
50
65
  def validate_value
51
- return (validate? && @validate_value)
66
+ validate? && @validate_value
52
67
  end
53
- alias_method :validate_value?, :validate_value
68
+ alias validate_value? validate_value
54
69
 
55
70
  def validate_presence
56
- return (validate? && @validate_presence)
71
+ validate? && @validate_presence
57
72
  end
58
- alias_method :validate_presence?, :validate_presence
73
+ alias validate_presence? validate_presence
59
74
 
60
75
  def validate_key
61
- return (validate? && @validate_key)
76
+ validate? && @validate_key
62
77
  end
63
- alias_method :validate_key?, :validate_key
78
+ alias validate_key? validate_key
64
79
 
65
80
  def process_value?
66
81
  @process_params
67
82
  end
68
83
  # set to true if you want to use pregenerated documentation cache and avoid
69
- # generating the documentation on runtime (usefull for production
84
+ # generating the documentation on runtime (useful for production
70
85
  # environment).
71
86
  # You can generate the cache by running
72
87
  #
73
88
  # rake apipie:cache
74
89
  attr_accessor :use_cache
75
- alias_method :use_cache?, :use_cache
90
+ alias use_cache? use_cache
76
91
 
77
92
  attr_writer :cache_dir
78
93
  def cache_dir
@@ -98,7 +113,7 @@ module Apipie
98
113
  end
99
114
 
100
115
  # array of controller names (strings) (might include actions as well)
101
- # to be ignored # when generationg the documentation
116
+ # to be ignored # when generating the documentation
102
117
  # e.g. %w[Api::CommentsController Api::PostsController#post]
103
118
  attr_writer :ignored
104
119
  def ignored
@@ -146,19 +161,22 @@ module Apipie
146
161
  def initialize
147
162
  @markup = Apipie::Markup::RDoc.new
148
163
  @app_name = "Another API"
149
- @app_info = HashWithIndifferentAccess.new
164
+ @app_info = ActiveSupport::HashWithIndifferentAccess.new
150
165
  @copyright = nil
151
166
  @validate = :implicitly
152
167
  @validate_value = true
153
168
  @validate_presence = true
154
169
  @validate_key = false
170
+ @action_on_non_validated_keys = :raise
155
171
  @required_by_default = false
156
- @api_base_url = HashWithIndifferentAccess.new
172
+ @api_base_url = ActiveSupport::HashWithIndifferentAccess.new
173
+ @api_action_matcher = proc { |controller| controller.params[:action] }
157
174
  @doc_base_url = "/apipie"
158
175
  @layout = "apipie/apipie"
159
176
  @disqus_shortname = nil
160
177
  @default_version = "1.0"
161
178
  @debug = false
179
+ @ignore_allow_blank_false = false
162
180
  @version_in_url = true
163
181
  @namespaced_resources = false
164
182
  @doc_path = "doc"
@@ -173,14 +191,6 @@ module Apipie
173
191
  @translate = lambda { |str, locale| str }
174
192
  @persist_show_in_doc = false
175
193
  @routes_formatter = RoutesFormatter.new
176
- @swagger_content_type_input = :form_data # this can be :json or :form_data
177
- @swagger_json_input_uses_refs = false
178
- @swagger_include_warning_tags = false
179
- @swagger_suppress_warnings = false #[105,100,102]
180
- @swagger_api_host = "localhost:3000"
181
- @swagger_generate_x_computed_id_field = false
182
- @swagger_allow_additional_properties_in_response = false
183
- @swagger_responses_use_refs = true
184
194
  end
185
195
  end
186
196
  end
@@ -51,9 +51,6 @@ module Apipie
51
51
  end
52
52
 
53
53
  module Resource
54
- # by default, the resource id is derived from controller_name
55
- # it can be overwritten with.
56
- #
57
54
  # resource_id "my_own_resource_id"
58
55
  def resource_id(resource_id)
59
56
  Apipie.set_resource_id(@controller, resource_id)
@@ -70,7 +67,7 @@ module Apipie
70
67
  def short(short)
71
68
  _apipie_dsl_data[:short_description] = short
72
69
  end
73
- alias :short_description :short
70
+ alias short_description short
74
71
 
75
72
  def path(path)
76
73
  _apipie_dsl_data[:path] = path
@@ -96,7 +93,7 @@ module Apipie
96
93
  # # load paths from routes and don't provide description
97
94
  # api
98
95
  #
99
- def api(method, path, desc = nil, options={}) #:doc:
96
+ def api(method, path, desc = nil, options = {}) #:doc:
100
97
  return unless Apipie.active_dsl?
101
98
  _apipie_dsl_data[:api] = true
102
99
  _apipie_dsl_data[:api_args] << [method, path, desc, options]
@@ -105,7 +102,7 @@ module Apipie
105
102
  # # load paths from routes
106
103
  # api! "short description",
107
104
  #
108
- def api!(desc = nil, options={}) #:doc:
105
+ def api!(desc = nil, options = {}) #:doc:
109
106
  return unless Apipie.active_dsl?
110
107
  _apipie_dsl_data[:api] = true
111
108
  _apipie_dsl_data[:api_from_routes] = { :desc => desc, :options =>options }
@@ -139,20 +136,20 @@ module Apipie
139
136
  #
140
137
  # Example:
141
138
  # api :desc => "Show user profile", :path => "/users/", :version => '1.0 - 3.4.2012'
142
- # param :id, Fixnum, :desc => "User ID", :required => true
139
+ # param :id, Integer, :desc => "User ID", :required => true
143
140
  # desc <<-EOS
144
141
  # Long description...
145
142
  # EOS
146
143
  def resource_description(options = {}, &block) #:doc:
147
144
  return unless Apipie.active_dsl?
148
- raise ArgumentError, "Block expected" unless block_given?
145
+ raise ArgumentError, "Block expected" unless block
149
146
 
150
147
  dsl_data = ResourceDescriptionDsl.eval_dsl(self, &block)
151
148
  versions = dsl_data[:api_versions]
149
+ Apipie.set_controller_versions(self, versions)
152
150
  @apipie_resource_descriptions = versions.map do |version|
153
151
  Apipie.define_resource_description(self, version, dsl_data)
154
152
  end
155
- Apipie.set_controller_versions(self, versions)
156
153
  end
157
154
  end
158
155
 
@@ -160,7 +157,7 @@ module Apipie
160
157
  def api_versions(*versions)
161
158
  _apipie_dsl_data[:api_versions].concat(versions)
162
159
  end
163
- alias :api_version :api_versions
160
+ alias api_version api_versions
164
161
 
165
162
  # Describe the next method.
166
163
  #
@@ -177,8 +174,8 @@ module Apipie
177
174
  end
178
175
  _apipie_dsl_data[:description] = description
179
176
  end
180
- alias :description :desc
181
- alias :full_description :desc
177
+ alias description desc
178
+ alias full_description desc
182
179
 
183
180
  # describe next method with document in given path
184
181
  # in convension, these doc located under "#{Rails.root}/doc"
@@ -218,7 +215,7 @@ module Apipie
218
215
  # puts "hello world"
219
216
  # end
220
217
  #
221
- def error(code_or_options, desc=nil, options={}) #:doc:
218
+ def error(code_or_options, desc = nil, options = {}) #:doc:
222
219
  return unless Apipie.active_dsl?
223
220
  _apipie_dsl_data[:errors] << [code_or_options, desc, options]
224
221
  end
@@ -244,16 +241,18 @@ module Apipie
244
241
  method_params = self.class._apipie_get_method_params(action_name)
245
242
 
246
243
  if Apipie.configuration.validate_presence?
247
- method_params.each do |_, param|
248
- # check if required parameters are present
249
- raise ParamMissing.new(param) if param.required && !params.has_key?(param.name)
244
+ Validator::BaseValidator.raise_if_missing_params do |missing|
245
+ method_params.each do |_, param|
246
+ # check if required parameters are present
247
+ missing << param if param.required && !params.key?(param.name)
248
+ end
250
249
  end
251
250
  end
252
251
 
253
252
  if Apipie.configuration.validate_value?
254
253
  method_params.each do |_, param|
255
254
  # params validations
256
- param.validate(params[:"#{param.name}"]) if params.has_key?(param.name)
255
+ param.validate(params[:"#{param.name}"]) if params.key?(param.name)
257
256
  end
258
257
  end
259
258
 
@@ -262,7 +261,9 @@ module Apipie
262
261
  if Apipie.configuration.validate_key?
263
262
  params.reject{|k,_| %w[format controller action].include?(k.to_s) }.each_pair do |param, _|
264
263
  # params allowed
265
- raise UnknownParam.new(param) if method_params.select {|_,p| p.name.to_s == param.to_s}.empty?
264
+ if method_params.none? {|_,p| p.name.to_s == param.to_s}
265
+ self.class._apipie_handle_validate_key_error params, param
266
+ end
266
267
  end
267
268
  end
268
269
 
@@ -270,13 +271,13 @@ module Apipie
270
271
  @api_params ||= {}
271
272
  method_params.each do |_, param|
272
273
  # params processing
273
- @api_params[param.as] = param.process_value(params[:"#{param.name}"]) if params.has_key?(param.name)
274
+ @api_params[param.as] = param.process_value(params[:"#{param.name}"]) if params.key?(param.name)
274
275
  end
275
276
  end
276
277
  end
277
278
  end
278
279
 
279
- if (Apipie.configuration.validate == :implicitly || Apipie.configuration.validate == true)
280
+ if Apipie.configuration.validate == :implicitly || Apipie.configuration.validate == true
280
281
  old_method = instance_method(description.method)
281
282
 
282
283
  define_method(description.method) do |*args|
@@ -286,7 +287,16 @@ module Apipie
286
287
  old_method.bind(self).call(*args)
287
288
  end
288
289
  end
290
+ end
291
+ end
289
292
 
293
+ def _apipie_handle_validate_key_error params, param
294
+ case Apipie.configuration.action_on_non_validated_keys
295
+ when :raise
296
+ raise UnknownParam, param
297
+ when :skip
298
+ params.delete(param)
299
+ Rails.logger.warn(UnknownParam.new(param).to_s)
290
300
  end
291
301
  end
292
302
 
@@ -349,7 +359,7 @@ module Apipie
349
359
  # Reuses param group for this method. The definition is looked up
350
360
  # in scope of this controller. If the group was defined in
351
361
  # different controller, the second param can be used to specify it.
352
- # when using action_aware parmas, you can specify :as =>
362
+ # when using action_aware params, you can specify :as =>
353
363
  # :create or :update to explicitly say how it should behave
354
364
  def param_group(name, scope_or_options = nil, options = {})
355
365
  if scope_or_options.is_a? Hash
@@ -391,7 +401,7 @@ module Apipie
391
401
  # render json: {user: {name: "Alfred"}}
392
402
  # end
393
403
  #
394
- def returns(pgroup_or_options, desc_or_options=nil, options={}, &block) #:doc:
404
+ def returns(pgroup_or_options, desc_or_options = nil, options = {}, &block) #:doc:
395
405
  return unless Apipie.active_dsl?
396
406
 
397
407
 
@@ -496,7 +506,7 @@ module Apipie
496
506
  end
497
507
 
498
508
  def _apipie_update_meta(method_desc, dsl_data)
499
- return unless dsl_data[:meta] && dsl_data[:meta].is_a?(Hash)
509
+ return unless dsl_data[:meta].is_a?(Hash)
500
510
 
501
511
  method_desc.metadata ||= {}
502
512
  method_desc.metadata.merge!(dsl_data[:meta])
@@ -514,15 +524,17 @@ module Apipie
514
524
  end
515
525
  end
516
526
  # backwards compatibility
517
- alias_method :apipie_update_params, :apipie_update_methods
527
+ alias apipie_update_params apipie_update_methods
518
528
 
519
529
  def _apipie_concern_subst
520
- @_apipie_concern_subst ||= {:controller_path => self.controller_path,
521
- :resource_id => Apipie.get_resource_name(self)}
530
+ @_apipie_concern_subst ||= {
531
+ controller_path: self.controller_path,
532
+ resource_id: Apipie.get_resource_id(self)
533
+ }
522
534
  end
523
535
 
524
536
  def _apipie_perform_concern_subst(string)
525
- return _apipie_concern_subst.reduce(string) do |ret, (key, val)|
537
+ _apipie_concern_subst.reduce(string) do |ret, (key, val)|
526
538
  ret.gsub(":#{key}", val)
527
539
  end
528
540
  end
@@ -10,7 +10,7 @@ module Apipie
10
10
  options)
11
11
  end
12
12
 
13
- def initialize(code_or_options, desc=nil, options={})
13
+ def initialize(code_or_options, desc = nil, options = {})
14
14
  if code_or_options.is_a? Hash
15
15
  code_or_options.symbolize_keys!
16
16
  @code = code_or_options[:code]
@@ -31,10 +31,10 @@ module Apipie
31
31
  end
32
32
  end
33
33
 
34
- def to_json
34
+ def to_json(lang)
35
35
  {
36
36
  :code => code,
37
- :description => description,
37
+ :description => Apipie.app.translate(description, lang),
38
38
  :metadata => metadata
39
39
  }
40
40
  end
data/lib/apipie/errors.rb CHANGED
@@ -24,6 +24,20 @@ module Apipie
24
24
  end
25
25
  end
26
26
 
27
+ class ParamMultipleMissing < ParamError
28
+ attr_accessor :params
29
+
30
+ def initialize(params)
31
+ @params = params
32
+ end
33
+
34
+ def to_s
35
+ params.map do |param|
36
+ ParamMissing.new(param).to_s
37
+ end.join("\n")
38
+ end
39
+ end
40
+
27
41
  class ParamMissing < DefinedParamError
28
42
  def to_s
29
43
  unless @param.options[:missing_message].nil?
@@ -60,27 +74,13 @@ module Apipie
60
74
 
61
75
  class ResponseDoesNotMatchSwaggerSchema < Error
62
76
  def initialize(controller_name, method_name, response_code, error_messages, schema, returned_object)
63
- @controller_name = controller_name
64
- @method_name = method_name
65
- @response_code = response_code
66
- @error_messages = error_messages
67
- @schema = schema
68
- @returned_object = returned_object
69
- end
70
-
71
- def to_s
72
- "Response does not match swagger schema (#{@controller_name}##{@method_name} #{@response_code}): #{@error_messages}\nSchema: #{JSON(@schema)}\nReturned object: #{@returned_object}"
77
+ super("Response does not match swagger schema (#{controller_name}##{method_name} #{response_code}): #{error_messages}\nSchema: #{JSON(schema)}\nReturned object: #{returned_object}")
73
78
  end
74
79
  end
75
80
 
76
81
  class NoDocumentedMethod < Error
77
82
  def initialize(controller_name, method_name)
78
- @method_name = method_name
79
- @controller_name = controller_name
80
- end
81
-
82
- def to_s
83
- "There is no documented method #{@controller_name}##{@method_name}"
83
+ super("There is no documented method #{controller_name}##{method_name}")
84
84
  end
85
85
  end
86
86
  end
@@ -19,21 +19,21 @@ module Apipie
19
19
  def ignore_call?(record)
20
20
  return true unless record[:controller]
21
21
  return true if @ignored.include?(record[:controller].name)
22
- return true if @ignored.include?("#{Apipie.get_resource_name(record[:controller].name)}##{record[:action]}")
22
+ return true if @ignored.include?("#{Apipie.get_resource_id(record[:controller].name)}##{record[:action]}")
23
23
  return true unless @api_controllers_paths.include?(controller_full_path(record[:controller]))
24
24
  end
25
25
 
26
26
  def handle_record(record)
27
- add_to_records(record)
28
27
  if ignore_call?(record)
29
28
  Extractor.logger.info("REST_API: skipping #{record_to_s(record)}")
30
29
  else
30
+ add_to_records(record)
31
31
  refine_description(record)
32
32
  end
33
33
  end
34
34
 
35
35
  def add_to_records(record)
36
- key = "#{Apipie.get_resource_name(record[:controller])}##{record[:action]}"
36
+ key = "#{Apipie.get_resource_id(record[:controller])}##{record[:action]}"
37
37
  @records[key] << record
38
38
  end
39
39
 
@@ -96,7 +96,7 @@ module Apipie
96
96
  end
97
97
 
98
98
  def add_routes_info(desc)
99
- api_prefix = Apipie.api_base_url.sub(/\/$/,"")
99
+ api_prefix = Apipie.api_base_url.sub(%r{/$},"")
100
100
  desc[:api] = Apipie::Extractor.apis_from_routes[[desc[:controller].name, desc[:action]]]
101
101
  if desc[:api]
102
102
  desc[:params].each do |name, param|
@@ -114,4 +114,3 @@ module Apipie
114
114
  end
115
115
  end
116
116
  end
117
-
@@ -9,7 +9,7 @@ module Apipie
9
9
 
10
10
  def analyse_env(env)
11
11
  @verb = env["REQUEST_METHOD"].to_sym
12
- @path = env["PATH_INFO"].sub(/^\/*/,"/")
12
+ @path = env["PATH_INFO"].sub(%r{^/*},"/")
13
13
  @query = env["QUERY_STRING"] unless env["QUERY_STRING"].blank?
14
14
  @params = Rack::Utils.parse_nested_query(@query)
15
15
  @params.merge!(env["action_dispatch.request.request_parameters"] || {})
@@ -24,7 +24,7 @@ module Apipie
24
24
 
25
25
  def analyse_controller(controller)
26
26
  @controller = controller.class
27
- @action = controller.params[:action]
27
+ @action = Apipie.configuration.api_action_matcher.call(controller)
28
28
  end
29
29
 
30
30
  def analyse_response(response)
@@ -44,11 +44,13 @@ module Apipie
44
44
  @path = request.path
45
45
  @params = request.request_parameters
46
46
  if [:POST, :PUT, :PATCH, :DELETE].include?(@verb)
47
- @request_data = @params
47
+ @request_data = request.content_type == "multipart/form-data" ? reformat_multipart_data(@params) : @params
48
48
  else
49
49
  @query = request.query_string
50
50
  end
51
- @response_data = parse_data(response.body)
51
+ if response.media_type != 'application/pdf'
52
+ @response_data = parse_data(response.body)
53
+ end
52
54
  @code = response.code
53
55
  end
54
56
 
@@ -64,8 +66,14 @@ module Apipie
64
66
  lines = ["Content-Type: multipart/form-data; boundary=#{MULTIPART_BOUNDARY}",'']
65
67
  boundary = "--#{MULTIPART_BOUNDARY}"
66
68
  form.each do |key, attrs|
67
- if attrs.is_a?(String)
69
+ if attrs.is_a?(String) # rubocop:disable Style/CaseLikeIf
68
70
  lines << boundary << content_disposition(key) << "Content-Length: #{attrs.size}" << '' << attrs
71
+ elsif attrs.is_a?(Rack::Test::UploadedFile) || attrs.is_a?(ActionDispatch::Http::UploadedFile)
72
+ reformat_uploaded_file(boundary, attrs, key, lines)
73
+ elsif attrs.is_a?(Array)
74
+ reformat_array(boundary, attrs, key, lines)
75
+ elsif attrs.is_a?(TrueClass) || attrs.is_a?(FalseClass)
76
+ reformat_boolean(boundary, attrs, key, lines)
69
77
  else
70
78
  reformat_hash(boundary, attrs, lines)
71
79
  end
@@ -86,6 +94,24 @@ module Apipie
86
94
  end
87
95
  end
88
96
 
97
+ def reformat_boolean(boundary, attrs, key, lines)
98
+ lines << boundary << content_disposition(key)
99
+ lines << '' << attrs.to_s
100
+ end
101
+
102
+ def reformat_array(boundary, attrs, key, lines)
103
+ attrs.each do |item|
104
+ lines << boundary << content_disposition("#{key}[]")
105
+ lines << '' << item
106
+ end
107
+ end
108
+
109
+ def reformat_uploaded_file(boundary, file, key, lines)
110
+ lines << boundary << %{#{content_disposition(key)}; filename="#{file.original_filename}"}
111
+ lines << "Content-Length: #{file.size}" << "Content-Type: #{file.content_type}" << "Content-Transfer-Encoding: binary"
112
+ lines << '' << %{... contents of "#{key}" ...}
113
+ end
114
+
89
115
  def content_disposition(name)
90
116
  %{Content-Disposition: form-data; name="#{name}"}
91
117
  end
@@ -150,7 +176,7 @@ module Apipie
150
176
  end
151
177
 
152
178
  module FunctionalTestRecording
153
- def process(*, **) # action, parameters = nil, session = nil, flash = nil, http_method = 'GET')
179
+ def process(*) # action, parameters = nil, session = nil, flash = nil, http_method = 'GET')
154
180
  ret = super
155
181
  if Apipie.configuration.record
156
182
  Apipie::Extractor.call_recorder.analyze_functional_test(self)
@@ -160,6 +186,7 @@ module Apipie
160
186
  ensure
161
187
  Apipie::Extractor.clean_call_recorder
162
188
  end
189
+ ruby2_keywords :process if respond_to?(:ruby2_keywords, true)
163
190
  end
164
191
  end
165
192
  end
@@ -103,7 +103,7 @@ module Apipie
103
103
  call = call.stringify_keys
104
104
  ordered_call = OrderedHash.new
105
105
  %w[title verb path versions query request_data response_data code show_in_doc recorded].each do |k|
106
- next unless call.has_key?(k)
106
+ next unless call.key?(k)
107
107
  ordered_call[k] = case call[k]
108
108
  when ActiveSupport::HashWithIndifferentAccess
109
109
  convert_file_value(call[k]).to_hash
@@ -116,9 +116,10 @@ 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
+ case v
120
+ when Rack::Test::UploadedFile, ActionDispatch::Http::UploadedFile
120
121
  hash[k] = "<FILE CONTENT '#{v.original_filename}'>"
121
- elsif v.is_a?(Hash)
122
+ when Hash
122
123
  hash[k] = convert_file_value(v)
123
124
  end
124
125
  end
@@ -134,8 +135,8 @@ module Apipie
134
135
  old_examples = self.load_recorded_examples
135
136
  merged_examples = []
136
137
  (new_examples.keys + old_examples.keys).uniq.each do |key|
137
- if new_examples.has_key?(key)
138
- if old_examples.has_key?(key)
138
+ if new_examples.key?(key)
139
+ if old_examples.key?(key)
139
140
  records = deep_merge_examples(new_examples[key], old_examples[key])
140
141
  else
141
142
  records = new_examples[key]
@@ -190,7 +191,7 @@ module Apipie
190
191
  end
191
192
 
192
193
  def load_old_examples
193
- if File.exists?(@examples_file)
194
+ if File.exist?(@examples_file)
194
195
  if defined? SafeYAML
195
196
  return YAML.load_file(@examples_file, :safe=>false)
196
197
  else
@@ -295,7 +296,7 @@ module Apipie
295
296
  end
296
297
 
297
298
  def controller_content
298
- raise ControllerNotFound.new unless controller_path && File.exists?(controller_path)
299
+ raise ControllerNotFound.new unless controller_path && File.exist?(controller_path)
299
300
  @controller_content ||= File.read(controller_path)
300
301
  end
301
302
 
@@ -323,7 +324,7 @@ module Apipie
323
324
  desc ||= case @action.to_s
324
325
  when "show", "create", "update", "destroy"
325
326
  name = name.singularize
326
- "#{@action.capitalize} #{name =~ /^[aeiou]/ ? "an" : "a"} #{name}"
327
+ "#{@action.capitalize} #{name =~ /^[aeiou]/ ? 'an' : 'a'} #{name}"
327
328
  when "index"
328
329
  "List #{name}"
329
330
  end
@@ -407,12 +408,11 @@ module Apipie
407
408
  if line =~ /\s*\b(module|class|def)\b /
408
409
  break
409
410
  end
410
- if line =~ /do\s*(\|.*?\|)?\s*$/
411
- block_level -= 1
412
- if block_level == 0
413
- added_lines.concat(lines_to_add)
414
- lines_to_add = []
415
- end
411
+ next unless line =~ /do\s*(\|.*?\|)?\s*$/
412
+ block_level -= 1
413
+ if block_level == 0
414
+ added_lines.concat(lines_to_add)
415
+ lines_to_add = []
416
416
  end
417
417
  end
418
418
  return added_lines.reverse.join
@@ -15,13 +15,10 @@ class Apipie::Railtie
15
15
  end
16
16
  end
17
17
  end
18
- app.middleware.use ::Apipie::Extractor::Recorder::Middleware
19
18
 
20
- if Gem::Version.new(Rails.version) < Gem::Version.new('5.0.0')
21
- ActionController::TestCase::Behavior.instance_eval do
22
- prepend Apipie::Extractor::Recorder::FunctionalTestRecording
23
- end
24
- else
19
+ if Apipie.configuration.record
20
+ app.middleware.use ::Apipie::Extractor::Recorder::Middleware
21
+
25
22
  ActionController::TestCase.send(:prepend, Apipie::Extractor::Recorder::FunctionalTestRecording)
26
23
  ActionController::TestCase::Behavior.send(:prepend, Apipie::Extractor::Recorder::FunctionalTestRecording)
27
24
  end
@@ -87,7 +84,7 @@ module Apipie
87
84
  def apis_from_routes
88
85
  return @apis_from_routes if @apis_from_routes
89
86
 
90
- @api_prefix = Apipie.api_base_url.sub(/\/$/,"")
87
+ @api_prefix = Apipie.api_base_url.sub(%r{/$},"")
91
88
  populate_api_routes
92
89
  update_api_descriptions
93
90
 
@@ -160,10 +157,10 @@ module Apipie
160
157
  def update_api_descriptions
161
158
  apis_from_docs = all_apis_from_docs
162
159
  @apis_from_routes.each do |(controller, action), new_apis|
163
- method_key = "#{Apipie.get_resource_name(controller.safe_constantize || next)}##{action}"
160
+ method_key = "#{Apipie.get_resource_id(controller.safe_constantize || next)}##{action}"
164
161
  old_apis = apis_from_docs[method_key] || []
165
162
  new_apis.each do |new_api|
166
- new_api[:path].sub!(/\(\.:format\)$/,"") if new_api[:path]
163
+ new_api[:path]&.sub!(/\(\.:format\)$/,"")
167
164
  old_api = old_apis.find do |api|
168
165
  api[:path] == "#{@api_prefix}#{new_api[:path]}"
169
166
  end