apipierails3 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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