apipierails3 0.0.1

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 (171) hide show
  1. checksums.yaml +17 -0
  2. data/.gitignore +14 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +27 -0
  5. data/APACHE-LICENSE-2.0 +202 -0
  6. data/CHANGELOG.md +469 -0
  7. data/Gemfile +1 -0
  8. data/Gemfile.rails32 +6 -0
  9. data/Gemfile.rails41 +6 -0
  10. data/Gemfile.rails42 +11 -0
  11. data/Gemfile.rails50 +6 -0
  12. data/Gemfile.rails51 +7 -0
  13. data/MIT-LICENSE +20 -0
  14. data/NOTICE +4 -0
  15. data/PROPOSAL_FOR_RESPONSE_DESCRIPTIONS.md +244 -0
  16. data/README.rst +1874 -0
  17. data/Rakefile +13 -0
  18. data/apipierails3.gemspec +27 -0
  19. data/app/controllers/apipie/apipies_controller.rb +199 -0
  20. data/app/helpers/apipie_helper.rb +10 -0
  21. data/app/public/apipie/javascripts/apipie.js +6 -0
  22. data/app/public/apipie/javascripts/bundled/bootstrap-collapse.js +138 -0
  23. data/app/public/apipie/javascripts/bundled/bootstrap.js +1726 -0
  24. data/app/public/apipie/javascripts/bundled/jquery.js +5 -0
  25. data/app/public/apipie/javascripts/bundled/prettify.js +28 -0
  26. data/app/public/apipie/stylesheets/application.css +7 -0
  27. data/app/public/apipie/stylesheets/bundled/bootstrap-responsive.min.css +12 -0
  28. data/app/public/apipie/stylesheets/bundled/bootstrap.min.css +689 -0
  29. data/app/public/apipie/stylesheets/bundled/prettify.css +30 -0
  30. data/app/views/apipie/apipies/_disqus.html.erb +13 -0
  31. data/app/views/apipie/apipies/_errors.html.erb +23 -0
  32. data/app/views/apipie/apipies/_headers.html.erb +26 -0
  33. data/app/views/apipie/apipies/_languages.erb +6 -0
  34. data/app/views/apipie/apipies/_metadata.erb +1 -0
  35. data/app/views/apipie/apipies/_method_detail.erb +61 -0
  36. data/app/views/apipie/apipies/_params.html.erb +42 -0
  37. data/app/views/apipie/apipies/_params_plain.html.erb +20 -0
  38. data/app/views/apipie/apipies/apipie_404.html.erb +17 -0
  39. data/app/views/apipie/apipies/apipie_checksum.json.erb +1 -0
  40. data/app/views/apipie/apipies/getting_started.html.erb +6 -0
  41. data/app/views/apipie/apipies/index.html.erb +56 -0
  42. data/app/views/apipie/apipies/method.html.erb +41 -0
  43. data/app/views/apipie/apipies/plain.html.erb +77 -0
  44. data/app/views/apipie/apipies/resource.html.erb +80 -0
  45. data/app/views/apipie/apipies/static.html.erb +103 -0
  46. data/app/views/layouts/apipie/apipie.html.erb +27 -0
  47. data/config/locales/de.yml +28 -0
  48. data/config/locales/en.yml +32 -0
  49. data/config/locales/es.yml +28 -0
  50. data/config/locales/fr.yml +31 -0
  51. data/config/locales/it.yml +31 -0
  52. data/config/locales/ja.yml +31 -0
  53. data/config/locales/pl.yml +28 -0
  54. data/config/locales/pt-BR.yml +28 -0
  55. data/config/locales/ru.yml +28 -0
  56. data/config/locales/tr.yml +28 -0
  57. data/config/locales/zh-CN.yml +28 -0
  58. data/config/locales/zh-TW.yml +28 -0
  59. data/images/screenshot-1.png +0 -0
  60. data/images/screenshot-2.png +0 -0
  61. data/lib/apipie/apipie_module.rb +83 -0
  62. data/lib/apipie/application.rb +462 -0
  63. data/lib/apipie/configuration.rb +186 -0
  64. data/lib/apipie/dsl_definition.rb +607 -0
  65. data/lib/apipie/error_description.rb +44 -0
  66. data/lib/apipie/errors.rb +86 -0
  67. data/lib/apipie/extractor.rb +177 -0
  68. data/lib/apipie/extractor/collector.rb +117 -0
  69. data/lib/apipie/extractor/recorder.rb +166 -0
  70. data/lib/apipie/extractor/writer.rb +454 -0
  71. data/lib/apipie/helpers.rb +73 -0
  72. data/lib/apipie/markup.rb +48 -0
  73. data/lib/apipie/method_description.rb +273 -0
  74. data/lib/apipie/middleware/checksum_in_headers.rb +35 -0
  75. data/lib/apipie/param_description.rb +280 -0
  76. data/lib/apipie/railtie.rb +9 -0
  77. data/lib/apipie/resource_description.rb +124 -0
  78. data/lib/apipie/response_description.rb +131 -0
  79. data/lib/apipie/response_description_adapter.rb +200 -0
  80. data/lib/apipie/routes_formatter.rb +33 -0
  81. data/lib/apipie/routing.rb +16 -0
  82. data/lib/apipie/rspec/response_validation_helper.rb +192 -0
  83. data/lib/apipie/see_description.rb +39 -0
  84. data/lib/apipie/static_dispatcher.rb +69 -0
  85. data/lib/apipie/swagger_generator.rb +707 -0
  86. data/lib/apipie/tag_list_description.rb +11 -0
  87. data/lib/apipie/validator.rb +526 -0
  88. data/lib/apipie/version.rb +3 -0
  89. data/lib/apipierails3.rb +25 -0
  90. data/lib/generators/apipie/install/README +6 -0
  91. data/lib/generators/apipie/install/install_generator.rb +25 -0
  92. data/lib/generators/apipie/install/templates/initializer.rb.erb +7 -0
  93. data/lib/generators/apipie/views_generator.rb +11 -0
  94. data/lib/tasks/apipie.rake +345 -0
  95. data/rel-eng/packages/.readme +3 -0
  96. data/rel-eng/packages/rubygem-apipie-rails +1 -0
  97. data/rel-eng/tito.props +5 -0
  98. data/spec/controllers/api/v1/architectures_controller_spec.rb +29 -0
  99. data/spec/controllers/api/v2/architectures_controller_spec.rb +12 -0
  100. data/spec/controllers/api/v2/nested/resources_controller_spec.rb +11 -0
  101. data/spec/controllers/apipies_controller_spec.rb +273 -0
  102. data/spec/controllers/concerns_controller_spec.rb +42 -0
  103. data/spec/controllers/extended_controller_spec.rb +11 -0
  104. data/spec/controllers/users_controller_spec.rb +740 -0
  105. data/spec/dummy/Rakefile +7 -0
  106. data/spec/dummy/app/controllers/api/base_controller.rb +4 -0
  107. data/spec/dummy/app/controllers/api/v1/architectures_controller.rb +43 -0
  108. data/spec/dummy/app/controllers/api/v1/base_controller.rb +11 -0
  109. data/spec/dummy/app/controllers/api/v2/architectures_controller.rb +30 -0
  110. data/spec/dummy/app/controllers/api/v2/base_controller.rb +11 -0
  111. data/spec/dummy/app/controllers/api/v2/nested/architectures_controller.rb +32 -0
  112. data/spec/dummy/app/controllers/api/v2/nested/resources_controller.rb +33 -0
  113. data/spec/dummy/app/controllers/application_controller.rb +18 -0
  114. data/spec/dummy/app/controllers/concerns/extending_concern.rb +11 -0
  115. data/spec/dummy/app/controllers/concerns/sample_controller.rb +41 -0
  116. data/spec/dummy/app/controllers/concerns_controller.rb +8 -0
  117. data/spec/dummy/app/controllers/extended_controller.rb +14 -0
  118. data/spec/dummy/app/controllers/files_controller.rb +5 -0
  119. data/spec/dummy/app/controllers/overridden_concerns_controller.rb +31 -0
  120. data/spec/dummy/app/controllers/pets_controller.rb +408 -0
  121. data/spec/dummy/app/controllers/pets_using_auto_views_controller.rb +73 -0
  122. data/spec/dummy/app/controllers/pets_using_self_describing_classes_controller.rb +95 -0
  123. data/spec/dummy/app/controllers/tagged_cats_controller.rb +32 -0
  124. data/spec/dummy/app/controllers/tagged_dogs_controller.rb +15 -0
  125. data/spec/dummy/app/controllers/twitter_example_controller.rb +307 -0
  126. data/spec/dummy/app/controllers/users_controller.rb +297 -0
  127. data/spec/dummy/app/views/layouts/application.html.erb +21 -0
  128. data/spec/dummy/config.ru +4 -0
  129. data/spec/dummy/config/application.rb +49 -0
  130. data/spec/dummy/config/boot.rb +10 -0
  131. data/spec/dummy/config/database.yml +21 -0
  132. data/spec/dummy/config/environment.rb +8 -0
  133. data/spec/dummy/config/environments/development.rb +28 -0
  134. data/spec/dummy/config/environments/production.rb +52 -0
  135. data/spec/dummy/config/environments/test.rb +38 -0
  136. data/spec/dummy/config/initializers/apipie.rb +110 -0
  137. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  138. data/spec/dummy/config/initializers/inflections.rb +10 -0
  139. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  140. data/spec/dummy/config/initializers/secret_token.rb +8 -0
  141. data/spec/dummy/config/initializers/session_store.rb +8 -0
  142. data/spec/dummy/config/locales/en.yml +5 -0
  143. data/spec/dummy/config/routes.rb +51 -0
  144. data/spec/dummy/db/.gitkeep +0 -0
  145. data/spec/dummy/doc/apipie_examples.json +1 -0
  146. data/spec/dummy/doc/users/desc_from_file.md +1 -0
  147. data/spec/dummy/public/404.html +26 -0
  148. data/spec/dummy/public/422.html +26 -0
  149. data/spec/dummy/public/500.html +26 -0
  150. data/spec/dummy/public/favicon.ico +0 -0
  151. data/spec/dummy/public/stylesheets/.gitkeep +0 -0
  152. data/spec/dummy/script/rails +6 -0
  153. data/spec/lib/application_spec.rb +49 -0
  154. data/spec/lib/extractor/extractor_spec.rb +9 -0
  155. data/spec/lib/extractor/middleware_spec.rb +44 -0
  156. data/spec/lib/extractor/writer_spec.rb +110 -0
  157. data/spec/lib/file_handler_spec.rb +18 -0
  158. data/spec/lib/method_description_spec.rb +98 -0
  159. data/spec/lib/param_description_spec.rb +345 -0
  160. data/spec/lib/param_group_spec.rb +60 -0
  161. data/spec/lib/rake_spec.rb +71 -0
  162. data/spec/lib/resource_description_spec.rb +48 -0
  163. data/spec/lib/swagger/openapi_2_0_schema.json +1607 -0
  164. data/spec/lib/swagger/rake_swagger_spec.rb +139 -0
  165. data/spec/lib/swagger/response_validation_spec.rb +104 -0
  166. data/spec/lib/swagger/swagger_dsl_spec.rb +658 -0
  167. data/spec/lib/validator_spec.rb +113 -0
  168. data/spec/lib/validators/array_validator_spec.rb +85 -0
  169. data/spec/spec_helper.rb +109 -0
  170. data/spec/support/rake.rb +21 -0
  171. metadata +415 -0
@@ -0,0 +1,73 @@
1
+ module Apipie
2
+ module Helpers
3
+ def markup_to_html(text)
4
+ return "" if text.nil?
5
+ if Apipie.configuration.markup.respond_to? :to_html
6
+ Apipie.configuration.markup.to_html(text.strip_heredoc)
7
+ else
8
+ text.strip_heredoc
9
+ end
10
+ end
11
+
12
+ attr_accessor :url_prefix
13
+
14
+ def request_script_name
15
+ Thread.current[:apipie_req_script_name] || ""
16
+ end
17
+
18
+ def request_script_name=(script_name)
19
+ Thread.current[:apipie_req_script_name] = script_name
20
+ end
21
+
22
+ def full_url(path)
23
+ unless @url_prefix
24
+ @url_prefix = ""
25
+ @url_prefix << request_script_name
26
+ @url_prefix << Apipie.configuration.doc_base_url
27
+ end
28
+ path = path.sub(/^\//,"")
29
+ ret = "#{@url_prefix}/#{path}"
30
+ ret.insert(0,"/") unless ret =~ /\A[.\/]/
31
+ ret.sub!(/\/*\Z/,"")
32
+ ret
33
+ end
34
+
35
+ def include_javascripts
36
+ %w[ bundled/jquery.js
37
+ bundled/bootstrap-collapse.js
38
+ bundled/prettify.js
39
+ apipie.js ].map do |file|
40
+ "<script type='text/javascript' src='#{Apipie.full_url("javascripts/#{file}")}'></script>"
41
+ end.join("\n").html_safe
42
+ end
43
+
44
+ def include_stylesheets
45
+ %w[ bundled/bootstrap.min.css
46
+ bundled/prettify.css
47
+ bundled/bootstrap-responsive.min.css ].map do |file|
48
+ "<link type='text/css' rel='stylesheet' href='#{Apipie.full_url("stylesheets/#{file}")}'/>"
49
+ end.join("\n").html_safe
50
+ end
51
+
52
+ def label_class_for_error(err)
53
+ case err[:code]
54
+ when 200
55
+ 'label label-info'
56
+ when 201
57
+ 'label label-success'
58
+ when 204
59
+ 'label label-info2'
60
+ when 401
61
+ 'label label-warning'
62
+ when 403
63
+ 'label label-warning2'
64
+ when 422
65
+ 'label label-important'
66
+ when 404
67
+ 'label label-inverse'
68
+ else
69
+ 'label'
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,48 @@
1
+ module Apipie
2
+
3
+ module Markup
4
+
5
+ class RDoc
6
+
7
+ def initialize
8
+ require 'rdoc'
9
+ require 'rdoc/markup/to_html'
10
+ if Gem::Version.new(::RDoc::VERSION) < Gem::Version.new('4.0.0')
11
+ @rdoc ||= ::RDoc::Markup::ToHtml.new()
12
+ else
13
+ @rdoc ||= ::RDoc::Markup::ToHtml.new(::RDoc::Options.new)
14
+ end
15
+ end
16
+
17
+ def to_html(text)
18
+ @rdoc.convert(text)
19
+ end
20
+
21
+ end
22
+
23
+ class Markdown
24
+
25
+ def initialize
26
+ require 'maruku'
27
+ end
28
+
29
+ def to_html(text)
30
+ Maruku.new(text).to_html
31
+ end
32
+
33
+ end
34
+
35
+ class Textile
36
+
37
+ def initialize
38
+ require 'RedCloth'
39
+ end
40
+
41
+ def to_html(text)
42
+ RedCloth.new(text).to_html
43
+ end
44
+
45
+ end
46
+
47
+ end
48
+ end
@@ -0,0 +1,273 @@
1
+ require 'set'
2
+ module Apipie
3
+
4
+ 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
+ attr_reader :full_description, :method, :resource, :apis, :examples, :see, :formats, :metadata, :headers, :show
21
+
22
+ def initialize(method, resource, dsl_data)
23
+ @method = method.to_s
24
+ @resource = resource
25
+ @from_concern = dsl_data[:from_concern]
26
+ @apis = api_data(dsl_data).map do |mthd, path, desc, opts|
27
+ MethodDescription::Api.new(mthd, concern_subst(path), concern_subst(desc), opts)
28
+ end
29
+
30
+ desc = dsl_data[:description] || ''
31
+ @full_description = Apipie.markup_to_html(desc)
32
+
33
+ @errors = dsl_data[:errors].map do |args|
34
+ Apipie::ErrorDescription.from_dsl_data(args)
35
+ end
36
+
37
+ @tag_list = dsl_data[:tag_list]
38
+
39
+ @returns = dsl_data[:returns].map do |code,args|
40
+ Apipie::ResponseDescription.from_dsl_data(self, code, args)
41
+ end
42
+
43
+ @see = dsl_data[:see].map do |args|
44
+ Apipie::SeeDescription.new(args)
45
+ end
46
+
47
+ @formats = dsl_data[:formats]
48
+ @examples = dsl_data[:examples]
49
+ @examples += load_recorded_examples
50
+
51
+ @metadata = dsl_data[:meta]
52
+
53
+ @params_ordered = dsl_data[:params].map do |args|
54
+ Apipie::ParamDescription.from_dsl_data(self, args)
55
+ end.reject{|p| p.response_only? }
56
+
57
+ @params_ordered = ParamDescription.unify(@params_ordered)
58
+ @headers = dsl_data[:headers]
59
+
60
+ @show = if dsl_data.has_key? :show
61
+ dsl_data[:show]
62
+ else
63
+ true
64
+ end
65
+ end
66
+
67
+ def id
68
+ "#{resource._id}##{method}"
69
+ end
70
+
71
+ def params
72
+ params_ordered.reduce(ActiveSupport::OrderedHash.new) { |h,p| h[p.name] = p; h }
73
+ end
74
+
75
+ def params_ordered_self
76
+ @params_ordered
77
+ end
78
+
79
+ def params_ordered
80
+ all_params = []
81
+ parent = Apipie.get_resource_description(@resource.controller.superclass)
82
+
83
+ # get params from parent resource description
84
+ [parent, @resource].compact.each do |resource|
85
+ resource_params = resource._params_args.map do |args|
86
+ Apipie::ParamDescription.from_dsl_data(self, args)
87
+ end
88
+ merge_params(all_params, resource_params)
89
+ end
90
+
91
+ merge_params(all_params, @params_ordered)
92
+ all_params.find_all(&:validator)
93
+ end
94
+
95
+ def returns_self
96
+ @returns
97
+ end
98
+
99
+ def tag_list
100
+ all_tag_list = []
101
+ parent = Apipie.get_resource_description(@resource.controller.superclass)
102
+
103
+ # get tags from parent resource description
104
+ parent_tags = [parent, @resource].compact.flat_map { |resource| resource._tag_list_arg }
105
+ Apipie::TagListDescription.new((parent_tags + @tag_list).uniq.compact)
106
+ end
107
+
108
+ def returns
109
+ all_returns = []
110
+ parent = Apipie.get_resource_description(@resource.controller.superclass)
111
+
112
+ # get response descriptions from parent resource description
113
+ [parent, @resource].compact.each do |resource|
114
+ resource_returns = resource._returns_args.map do |code, args|
115
+ Apipie::ResponseDescription.from_dsl_data(self, code, args)
116
+ end
117
+ merge_returns(all_returns, resource_returns)
118
+ end
119
+
120
+ merge_returns(all_returns, @returns)
121
+ end
122
+
123
+ def errors
124
+ return @merged_errors if @merged_errors
125
+ @merged_errors = []
126
+ if @resource
127
+ resource_errors = @resource._errors_args.map do |args|
128
+ Apipie::ErrorDescription.from_dsl_data(args)
129
+ end
130
+
131
+ # exclude overwritten parent errors
132
+ @merged_errors = resource_errors.find_all do |err|
133
+ !@errors.any? { |e| e.code == err.code }
134
+ end
135
+ end
136
+ @merged_errors.concat(@errors)
137
+ return @merged_errors
138
+ end
139
+
140
+ def version
141
+ resource._version
142
+ end
143
+
144
+ def doc_url
145
+ crumbs = []
146
+ crumbs << @resource._version if Apipie.configuration.version_in_url
147
+ crumbs << @resource._id
148
+ crumbs << @method
149
+ Apipie.full_url crumbs.join('/')
150
+ end
151
+
152
+ def create_api_url(api)
153
+ path = api.path
154
+ unless api.from_routes
155
+ path = "#{@resource._api_base_url}#{path}"
156
+ end
157
+ path = path[0..-2] if path[-1..-1] == '/'
158
+ return path
159
+ end
160
+
161
+ def method_apis_to_json(lang = nil)
162
+ @apis.each.collect do |api|
163
+ {
164
+ :api_url => create_api_url(api),
165
+ :http_method => api.http_method.to_s,
166
+ :short_description => Apipie.app.translate(api.short_description, lang),
167
+ :deprecated => resource._deprecated || api.options[:deprecated]
168
+ }
169
+ end
170
+ end
171
+
172
+ def see
173
+ @see
174
+ end
175
+
176
+ def formats
177
+ @formats || @resource._formats
178
+ end
179
+
180
+ def to_json(lang=nil)
181
+ {
182
+ :doc_url => doc_url,
183
+ :name => @method,
184
+ :apis => method_apis_to_json(lang),
185
+ :formats => formats,
186
+ :full_description => Apipie.app.translate(@full_description, lang),
187
+ :errors => errors.map(&:to_json),
188
+ :params => params_ordered.map{ |param| param.to_json(lang) }.flatten,
189
+ :returns => @returns.map{ |return_item| return_item.to_json(lang) }.flatten,
190
+ :examples => @examples,
191
+ :metadata => @metadata,
192
+ :see => see.map(&:to_json),
193
+ :headers => headers,
194
+ :show => @show
195
+ }
196
+ end
197
+
198
+ # was the description defines in a module instead of directly in controller?
199
+ def from_concern?
200
+ @from_concern
201
+ end
202
+
203
+ private
204
+
205
+ def api_data(dsl_data)
206
+ ret = dsl_data[:api_args].dup
207
+ if dsl_data[:api_from_routes]
208
+ desc = dsl_data[:api_from_routes][:desc]
209
+ options = dsl_data[:api_from_routes][:options]
210
+
211
+ api_from_routes = Apipie.routes_for_action(resource.controller, method, {:desc => desc, :options => options}).map do |route_info|
212
+ [route_info[:verb],
213
+ route_info[:path],
214
+ route_info[:desc],
215
+ (route_info[:options] || {}).merge(:from_routes => true)]
216
+ end
217
+ ret.concat(api_from_routes)
218
+ end
219
+ ret
220
+ end
221
+
222
+ def merge_params(params, new_params)
223
+ new_param_names = Set.new(new_params.map(&:name))
224
+ params.delete_if { |p| new_param_names.include?(p.name) }
225
+ params.concat(new_params)
226
+ end
227
+
228
+ def merge_returns(returns, new_returns)
229
+ new_return_codes = Set.new(new_returns.map(&:code))
230
+ returns.delete_if { |p| new_return_codes.include?(p.code) }
231
+ returns.concat(new_returns)
232
+ end
233
+
234
+ def load_recorded_examples
235
+ (Apipie.recorded_examples[id] || []).
236
+ find_all { |ex| ex["show_in_doc"].to_i > 0 }.
237
+ find_all { |ex| ex["versions"].nil? || ex["versions"].include?(self.version) }.
238
+ sort_by { |ex| ex["show_in_doc"] }.
239
+ map { |ex| format_example(ex.symbolize_keys) }
240
+ end
241
+
242
+ def format_example_data(data)
243
+ case data
244
+ when Array, Hash
245
+ JSON.pretty_generate(data).gsub(/: \[\s*\]/,": []").gsub(/\{\s*\}/,"{}")
246
+ else
247
+ data
248
+ end
249
+ end
250
+
251
+ def format_example(ex)
252
+ example = ""
253
+ example << "// #{ex[:title]}\n" if ex[:title].present?
254
+ example << "#{ex[:verb]} #{ex[:path]}"
255
+ example << "?#{ex[:query]}" unless ex[:query].blank?
256
+ example << "\n" << format_example_data(ex[:request_data]).to_s if ex[:request_data]
257
+ example << "\n" << ex[:code].to_s
258
+ example << "\n" << format_example_data(ex[:response_data]).to_s if ex[:response_data]
259
+ example
260
+ end
261
+
262
+ def concern_subst(string)
263
+ return if string.nil?
264
+ if from_concern?
265
+ resource.controller._apipie_perform_concern_subst(string)
266
+ else
267
+ string
268
+ end
269
+ end
270
+
271
+ end
272
+
273
+ end
@@ -0,0 +1,35 @@
1
+ # Middleware for rails app that adds checksum of JSON in the response headers
2
+ # which can help client to realize when JSON has changed
3
+ #
4
+ # Add the following to your application.rb
5
+ # require 'apipie/middleware/checksum_in_headers'
6
+ # # Add JSON checksum in headers for smarter caching
7
+ # config.middleware.use "Apipie::Middleware::ChecksumInHeaders"
8
+ #
9
+ # And in your apipie initializer allow checksum calculation
10
+ # Apipie.configuration.update_checksum = true
11
+ # and reload documentation
12
+ # Apipie.reload_documentation
13
+ #
14
+ # By default the header is added to requests on /api and /apipie only
15
+ # It can be changed with
16
+ # Apipie.configuration.checksum_path = ['/prefix/api']
17
+ # If set to nil the header is added always
18
+
19
+ module Apipie
20
+ module Middleware
21
+ class ChecksumInHeaders
22
+ def initialize(app)
23
+ @app = app
24
+ end
25
+
26
+ def call(env)
27
+ status, headers, body = @app.call(env)
28
+ if !Apipie.configuration.checksum_path || env['PATH_INFO'].start_with?(*Apipie.configuration.checksum_path)
29
+ headers.merge!( 'Apipie-Checksum' => Apipie.checksum )
30
+ end
31
+ return [status, headers, body]
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,280 @@
1
+ module Apipie
2
+
3
+ # method parameter description
4
+ #
5
+ # name - method name (show)
6
+ # desc - description
7
+ # required - boolean if required
8
+ # validator - Validator::BaseValidator subclass
9
+ class ParamDescription
10
+
11
+ attr_reader :method_description, :name, :desc, :allow_nil, :allow_blank, :validator, :options, :metadata, :show, :as, :validations, :response_only, :request_only
12
+ attr_reader :additional_properties, :is_array
13
+ attr_accessor :parent, :required
14
+
15
+ alias_method :response_only?, :response_only
16
+ alias_method :request_only?, :request_only
17
+ alias_method :is_array?, :is_array
18
+
19
+ def self.from_dsl_data(method_description, args)
20
+ param_name, validator, desc_or_options, options, block = args
21
+ Apipie::ParamDescription.new(method_description,
22
+ param_name,
23
+ validator,
24
+ desc_or_options,
25
+ options,
26
+ &block)
27
+ end
28
+
29
+ def to_s
30
+ "ParamDescription: #{method_description.id}##{name}"
31
+ end
32
+
33
+ def ==(other)
34
+ return false unless self.class == other.class
35
+ if method_description == other.method_description && @options == other.options
36
+ true
37
+ else
38
+ false
39
+ end
40
+ end
41
+
42
+ def initialize(method_description, name, validator, desc_or_options = nil, options = {}, &block)
43
+
44
+ if desc_or_options.is_a?(Hash)
45
+ options = options.merge(desc_or_options)
46
+ elsif desc_or_options.is_a?(String)
47
+ options[:desc] = desc_or_options
48
+ elsif !desc_or_options.nil?
49
+ raise ArgumentError.new("param description: expected description or options as 3rd parameter")
50
+ end
51
+
52
+ options.symbolize_keys!
53
+
54
+ # we save options to know what was passed in DSL
55
+ @options = options
56
+ if @options[:param_group]
57
+ @from_concern = @options[:param_group][:from_concern]
58
+ end
59
+
60
+ @method_description = method_description
61
+ @name = concern_subst(name)
62
+ @as = options[:as] || @name
63
+ @desc = preformat_text(@options[:desc])
64
+
65
+ @parent = @options[:parent]
66
+ @metadata = @options[:meta]
67
+
68
+ @required = is_required?
69
+
70
+ @response_only = (@options[:only_in] == :response)
71
+ @request_only = (@options[:only_in] == :request)
72
+ raise ArgumentError.new("'#{@options[:only_in]}' is not a valid value for :only_in") if (!@response_only && !@request_only) && @options[:only_in].present?
73
+
74
+ @show = if @options.has_key? :show
75
+ @options[:show]
76
+ else
77
+ true
78
+ end
79
+
80
+ @allow_nil = @options[:allow_nil] || false
81
+ @allow_blank = @options[:allow_blank] || false
82
+
83
+ action_awareness
84
+
85
+ if validator
86
+ if (validator != Hash) && (validator.is_a? Hash) && (validator[:array_of])
87
+ @is_array = true
88
+ rest_of_options = validator
89
+ validator = validator[:array_of]
90
+ options.merge!(rest_of_options.select{|k,v| k != :array_of })
91
+ raise "an ':array_of =>' validator is allowed exclusively on response-only fields" unless @response_only
92
+ end
93
+ @validator = Validator::BaseValidator.find(self, validator, @options, block)
94
+ raise "Validator for #{validator} not found." unless @validator
95
+ end
96
+
97
+ @validations = Array(options[:validations]).map {|v| concern_subst(Apipie.markup_to_html(v)) }
98
+
99
+ @additional_properties = @options[:additional_properties]
100
+ end
101
+
102
+ def from_concern?
103
+ method_description.from_concern? || @from_concern
104
+ end
105
+
106
+ def normalized_value(value)
107
+ if value.is_a?(ActionController::Parameters) && !value.is_a?(Hash)
108
+ value.to_unsafe_hash
109
+ elsif value.is_a? Array
110
+ value.map { |v| normalized_value (v) }
111
+ else
112
+ value
113
+ end
114
+ end
115
+
116
+ def validate(value)
117
+ return true if @allow_nil && value.nil?
118
+ return true if @allow_blank && value.blank?
119
+ value = normalized_value(value)
120
+ if (!@allow_nil && value.nil?) || !@validator.valid?(value)
121
+ error = @validator.error
122
+ error = ParamError.new(error) unless error.is_a? StandardError
123
+ raise error
124
+ end
125
+ end
126
+
127
+ def process_value(value)
128
+ value = normalized_value(value)
129
+ if @validator.respond_to?(:process_value)
130
+ @validator.process_value(value)
131
+ else
132
+ value
133
+ end
134
+ end
135
+
136
+ def full_name
137
+ name_parts = parents_and_self.map{|p| p.name if p.show}.compact
138
+ return name.to_s if name_parts.blank?
139
+ return ([name_parts.first] + name_parts[1..-1].map { |n| "[#{n}]" }).join("")
140
+ end
141
+
142
+ # returns an array of all the parents: starting with the root parent
143
+ # ending with itself
144
+ def parents_and_self
145
+ ret = []
146
+ if self.parent
147
+ ret.concat(self.parent.parents_and_self)
148
+ end
149
+ ret << self
150
+ ret
151
+ end
152
+
153
+ def to_json(lang = nil)
154
+ hash = { :name => name.to_s,
155
+ :full_name => full_name,
156
+ :description => preformat_text(Apipie.app.translate(@options[:desc], lang)),
157
+ :required => required,
158
+ :allow_nil => allow_nil,
159
+ :allow_blank => allow_blank,
160
+ :validator => validator.to_s,
161
+ :expected_type => validator.expected_type,
162
+ :metadata => metadata,
163
+ :show => show,
164
+ :validations => validations }
165
+ if sub_params = validator.params_ordered
166
+ hash[:params] = sub_params.map { |p| p.to_json(lang)}
167
+ end
168
+ hash
169
+ end
170
+
171
+ def merge_with(other_param_desc)
172
+ if self.validator && other_param_desc.validator
173
+ self.validator.merge_with(other_param_desc.validator)
174
+ else
175
+ self.validator ||= other_param_desc.validator
176
+ end
177
+ self
178
+ end
179
+
180
+ # merge param descriptions. Allows defining hash params on more places
181
+ # (e.g. in param_groups). For example:
182
+ #
183
+ # def_param_group :user do
184
+ # param :user, Hash do
185
+ # param :name, String
186
+ # end
187
+ # end
188
+ #
189
+ # param_group :user
190
+ # param :user, Hash do
191
+ # param :password, String
192
+ # end
193
+ def self.unify(params)
194
+ ordering = params.map(&:name)
195
+ params.group_by(&:name).map do |name, param_descs|
196
+ param_descs.reduce(&:merge_with)
197
+ end.sort_by { |param| ordering.index(param.name) }
198
+ end
199
+
200
+ def self.merge(target_params, source_params)
201
+ params_to_merge, params_to_add = source_params.partition do |source_param|
202
+ target_params.any? { |target_param| source_param.name == target_param.name }
203
+ end
204
+ unify(target_params + params_to_merge)
205
+ target_params.concat(params_to_add)
206
+ end
207
+
208
+ # action awareness is being inherited from ancestors (in terms of
209
+ # nested params)
210
+ def action_aware?
211
+ if @options.has_key?(:action_aware)
212
+ return @options[:action_aware]
213
+ elsif @parent
214
+ @parent.action_aware?
215
+ else
216
+ false
217
+ end
218
+ end
219
+
220
+ def as_action
221
+ if @options[:param_group] && @options[:param_group][:options] &&
222
+ @options[:param_group][:options][:as]
223
+ @options[:param_group][:options][:as].to_s
224
+ elsif @parent
225
+ @parent.as_action
226
+ else
227
+ @method_description.method
228
+ end
229
+ end
230
+
231
+ # makes modification that are based on the action that the param
232
+ # is defined for. Typical for required and allow_nil variations in
233
+ # crate/update actions.
234
+ def action_awareness
235
+ if action_aware?
236
+ if !@options.has_key?(:allow_nil)
237
+ if @required
238
+ @allow_nil = false
239
+ else
240
+ @allow_nil = true
241
+ end
242
+ end
243
+ if as_action != "create"
244
+ @required = false
245
+ end
246
+ end
247
+ end
248
+
249
+ def concern_subst(string)
250
+ return string if string.nil? or !from_concern?
251
+
252
+ original = string
253
+ string = ":#{original}" if original.is_a? Symbol
254
+
255
+ replaced = method_description.resource.controller._apipie_perform_concern_subst(string)
256
+
257
+ return original if replaced == string
258
+ return replaced.to_sym if original.is_a? Symbol
259
+ return replaced
260
+ end
261
+
262
+ def preformat_text(text)
263
+ concern_subst(Apipie.markup_to_html(text || ''))
264
+ end
265
+
266
+ def is_required?
267
+ if @options.has_key?(:required)
268
+ if (@options[:required] == true) || (@options[:required] == false)
269
+ @options[:required]
270
+ else
271
+ Array(@options[:required]).include?(@method_description.method.to_sym)
272
+ end
273
+ else
274
+ Apipie.configuration.required_by_default?
275
+ end
276
+ end
277
+
278
+ end
279
+
280
+ end