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,454 @@
1
+ require 'set'
2
+
3
+ module Apipie
4
+ module Extractor
5
+ class Writer
6
+ class << self
7
+ def compressed
8
+ Apipie.configuration.compress_examples
9
+ end
10
+
11
+ def update_action_description(controller, action)
12
+ updater = ActionDescriptionUpdater.new(controller, action)
13
+ yield updater
14
+ updater.write!
15
+ rescue ActionDescriptionUpdater::ControllerNotFound
16
+ logger.warn("REST_API: Couldn't find controller file for #{controller}")
17
+ rescue ActionDescriptionUpdater::ActionNotFound
18
+ logger.warn("REST_API: Couldn't find action #{action} in #{controller}")
19
+ end
20
+
21
+ def write_recorded_examples(examples)
22
+ FileUtils.mkdir_p(File.dirname(examples_file))
23
+ content = serialize_examples(examples)
24
+ content = Zlib::Deflate.deflate(content).force_encoding('utf-8') if compressed
25
+ File.open(examples_file, 'w') { |f| f << content }
26
+ end
27
+
28
+ def load_recorded_examples
29
+ return {} unless File.exist?(examples_file)
30
+ load_json_examples
31
+ end
32
+
33
+ def examples_file
34
+ pure_path = Rails.root.join(
35
+ Apipie.configuration.doc_path, 'apipie_examples.json'
36
+ )
37
+ zipped_path = pure_path.to_s + '.gz'
38
+ return zipped_path if compressed
39
+ pure_path.to_s
40
+ end
41
+
42
+ protected
43
+
44
+ def serialize_examples(examples)
45
+ JSON.pretty_generate(
46
+ OrderedHash[*examples.sort_by(&:first).flatten(1)]
47
+ )
48
+ end
49
+
50
+ def deserialize_examples(examples_string)
51
+ examples = JSON.parse(examples_string)
52
+ return {} if examples.nil?
53
+ examples.each_value do |records|
54
+ records.each do |record|
55
+ record['verb'] = record['verb'].to_sym if record['verb']
56
+ end
57
+ end
58
+ end
59
+
60
+ def load_json_examples
61
+ raw = IO.read(examples_file)
62
+ raw = Zlib::Inflate.inflate(raw).force_encoding('utf-8') if compressed
63
+ deserialize_examples(raw)
64
+ end
65
+
66
+ def logger
67
+ Extractor.logger
68
+ end
69
+ end
70
+
71
+ def initialize(collector)
72
+ @collector = collector
73
+ end
74
+
75
+
76
+ def write_examples
77
+ merged_examples = merge_old_new_examples
78
+ self.class.write_recorded_examples(merged_examples)
79
+ end
80
+
81
+ def write_docs
82
+ descriptions = @collector.finalize_descriptions
83
+ descriptions.each do |_, desc|
84
+ if desc[:api].empty?
85
+ logger.warn("REST_API: Couldn't find any path for #{desc_to_s(desc)}")
86
+ next
87
+ end
88
+ self.class.update_action_description(desc[:controller], desc[:action]) do |u|
89
+ u.update_generated_description desc
90
+ end
91
+ end
92
+ end
93
+
94
+
95
+ protected
96
+
97
+
98
+ def desc_to_s(description)
99
+ "#{description[:controller].name}##{description[:action]}"
100
+ end
101
+
102
+ def ordered_call(call)
103
+ call = call.stringify_keys
104
+ ordered_call = OrderedHash.new
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)
107
+ ordered_call[k] = case call[k]
108
+ when ActiveSupport::HashWithIndifferentAccess
109
+ convert_file_value(call[k]).to_hash
110
+ else
111
+ call[k]
112
+ end
113
+ end
114
+ return ordered_call
115
+ end
116
+
117
+ def convert_file_value hash
118
+ hash.each do |k, v|
119
+ if (v.is_a?(Rack::Test::UploadedFile) || v.is_a?(ActionDispatch::Http::UploadedFile))
120
+ hash[k] = "<FILE CONTENT '#{v.original_filename}'>"
121
+ elsif v.is_a?(Hash)
122
+ hash[k] = convert_file_value(v)
123
+ end
124
+ end
125
+ hash
126
+ end
127
+
128
+ def load_recorded_examples
129
+ self.class.load_recorded_examples
130
+ end
131
+
132
+ def merge_old_new_examples
133
+ new_examples = self.load_new_examples
134
+ old_examples = self.load_recorded_examples
135
+ merged_examples = []
136
+ (new_examples.keys + old_examples.keys).uniq.each do |key|
137
+ if new_examples.has_key?(key)
138
+ if old_examples.has_key?(key)
139
+ records = deep_merge_examples(new_examples[key], old_examples[key])
140
+ else
141
+ records = new_examples[key]
142
+ end
143
+ else
144
+ records = old_examples[key]
145
+ end
146
+ merged_examples << [key, records.map { |r| ordered_call(r) } ]
147
+ end
148
+ return merged_examples
149
+ end
150
+
151
+ def deep_merge_examples(new_examples, old_examples)
152
+ new_examples.map do |new_example|
153
+ # Use ordered to get compareble output (mainly for the :query)
154
+ new_example_ordered = ordered_call(new_example.dup)
155
+
156
+ # Comparing verb, versions and query
157
+ if old_example = old_examples.find{ |old_example| old_example["verb"] == new_example_ordered["verb"] && old_example["versions"] == new_example_ordered["versions"] && old_example["query"] == new_example_ordered["query"]}
158
+
159
+ # Take the 'show in doc' attribute from the old example if it is present and the configuration requests the value to be persisted.
160
+ new_example[:show_in_doc] = old_example["show_in_doc"] if Apipie.configuration.persist_show_in_doc && old_example["show_in_doc"].to_i > 0
161
+
162
+ # Always take the title from the old example if it exists.
163
+ new_example[:title] ||= old_example["title"] if old_example["title"].present?
164
+ end
165
+ new_example
166
+ end
167
+ end
168
+
169
+ def load_new_examples
170
+ @collector.records.reduce({}) do |h, (method, calls)|
171
+ showed_in_versions = Set.new
172
+ # we have already shown some example
173
+ recorded_examples = calls.map do |call|
174
+ method_descriptions = Apipie.get_method_descriptions(call[:controller], call[:action])
175
+ call[:versions] = method_descriptions.map(&:version)
176
+
177
+ if Apipie.configuration.show_all_examples
178
+ show_in_doc = 1
179
+ elsif call[:versions].any? { |v| ! showed_in_versions.include?(v) }
180
+ call[:versions].each { |v| showed_in_versions << v }
181
+ show_in_doc = 1
182
+ else
183
+ show_in_doc = 0
184
+ end
185
+ example = call.merge(:show_in_doc => show_in_doc.to_i, :recorded => true)
186
+ example
187
+ end
188
+ h.update(method => recorded_examples)
189
+ end
190
+ end
191
+
192
+ def load_old_examples
193
+ if File.exists?(@examples_file)
194
+ if defined? SafeYAML
195
+ return YAML.load_file(@examples_file, :safe=>false)
196
+ else
197
+ return YAML.load_file(@examples_file)
198
+ end
199
+ end
200
+ return {}
201
+ end
202
+
203
+ def logger
204
+ self.class.logger
205
+ end
206
+
207
+ def showable_in_doc?(call)
208
+ # we don't want to mess documentation with too large examples
209
+ if hash_nodes_count(call["request_data"]) + hash_nodes_count(call["response_data"]) > 100
210
+ return false
211
+ else
212
+ return 1
213
+ end
214
+ end
215
+
216
+ def hash_nodes_count(node)
217
+ case node
218
+ when Hash
219
+ 1 + (node.values.map { |v| hash_nodes_count(v) }.reduce(:+) || 0)
220
+ when Array
221
+ node.map { |v| hash_nodes_count(v) }.reduce(:+) || 1
222
+ else
223
+ 1
224
+ end
225
+ end
226
+
227
+ end
228
+
229
+ class ActionDescriptionUpdater
230
+
231
+ class ControllerNotFound < StandardError; end
232
+
233
+ class ActionNotFound < StandardError; end
234
+
235
+ def initialize(controller, action)
236
+ @controller = controller
237
+ @action = action
238
+ end
239
+
240
+ def generated?
241
+ old_header.include?(Apipie.configuration.generated_doc_disclaimer)
242
+ end
243
+
244
+ def update_apis(apis)
245
+ new_header = ""
246
+ new_header << Apipie.configuration.generated_doc_disclaimer << "\n" if generated?
247
+ new_header << generate_apis_code(apis)
248
+ new_header << ensure_line_breaks(old_header.lines).reject do |line|
249
+ line.include?(Apipie.configuration.generated_doc_disclaimer) ||
250
+ line =~ /^api/
251
+ end.join
252
+ overwrite_header(new_header)
253
+ end
254
+
255
+ def update_generated_description(desc)
256
+ if generated? || old_header.empty?
257
+ new_header = generate_code(desc)
258
+ overwrite_header(new_header)
259
+ end
260
+ end
261
+
262
+ def update(new_header)
263
+ overwrite_header(new_header)
264
+ end
265
+
266
+ def old_header
267
+ return @old_header if defined? @old_header
268
+ @old_header = lines_above_method[/^\s*?#{Regexp.escape(Apipie.configuration.generated_doc_disclaimer)}.*/m]
269
+ @old_header ||= lines_above_method[/^\s*?\b(api|params|error|example)\b.*/m]
270
+ @old_header ||= ""
271
+ @old_header.sub!(/\A\s*\n/,"")
272
+ @old_header = align_indented(@old_header)
273
+ end
274
+
275
+ def write!
276
+ File.open(controller_path, "w") { |f| f << @controller_content }
277
+ @changed=false
278
+ end
279
+
280
+ protected
281
+
282
+ def logger
283
+ Extractor.logger
284
+ end
285
+
286
+ def action_line
287
+ return @action_line if defined? @action_line
288
+ @action_line = ensure_line_breaks(controller_content.lines).find_index { |line| line =~ /def \b#{@action}\b/ }
289
+ raise ActionNotFound unless @action_line
290
+ @action_line
291
+ end
292
+
293
+ def controller_path
294
+ @controller_path ||= Apipie::Extractor.controller_path(@controller.controller_path)
295
+ end
296
+
297
+ def controller_content
298
+ raise ControllerNotFound.new unless controller_path && File.exists?(controller_path)
299
+ @controller_content ||= File.read(controller_path)
300
+ end
301
+
302
+ def controller_content=(new_content)
303
+ return if @controller_name == new_content
304
+ remove_instance_variable("@action_line")
305
+ remove_instance_variable("@old_header")
306
+ @controller_content=new_content
307
+ @changed = true
308
+ end
309
+
310
+ def generate_code(desc)
311
+ code = "#{Apipie.configuration.generated_doc_disclaimer}\n"
312
+ code << generate_apis_code(desc[:api])
313
+ code << generate_params_code(desc[:params])
314
+ code << generate_errors_code(desc[:errors])
315
+ return code
316
+ end
317
+
318
+ def generate_apis_code(apis)
319
+ code = ""
320
+ apis.sort_by {|a| a[:path] }.each do |api|
321
+ desc = api[:desc]
322
+ name = @controller.controller_name.gsub("_", " ")
323
+ desc ||= case @action.to_s
324
+ when "show", "create", "update", "destroy"
325
+ name = name.singularize
326
+ "#{@action.capitalize} #{name =~ /^[aeiou]/ ? "an" : "a"} #{name}"
327
+ when "index"
328
+ "List #{name}"
329
+ end
330
+
331
+ code << "api :#{api[:method]}, '#{api[:path]}'"
332
+ code << ", '#{desc}'" if desc
333
+ code << "\n"
334
+ end
335
+ return code
336
+ end
337
+
338
+ def generate_params_code(params, indent = "")
339
+ code = ""
340
+ params.sort_by {|n,_| n }.each do |(name, desc)|
341
+ desc[:type] = (desc[:type] && desc[:type].first) || Object
342
+ code << "#{indent}param"
343
+ if name =~ /\W/
344
+ code << " :'#{name}'"
345
+ else
346
+ code << " :#{name}"
347
+ end
348
+ code << ", #{desc[:type].inspect}"
349
+ if desc[:allow_nil]
350
+ code << ", allow_nil: true"
351
+ end
352
+ if desc[:required]
353
+ code << ", required: true"
354
+ end
355
+ if desc[:nested]
356
+ code << " do\n"
357
+ code << generate_params_code(desc[:nested], indent + " ")
358
+ code << "#{indent}end"
359
+ else
360
+ end
361
+ code << "\n"
362
+ end
363
+ code
364
+ end
365
+
366
+ def generate_errors_code(errors)
367
+ code = ""
368
+ errors.sort_by {|e| e[:code] }.each do |error|
369
+ code << "error code: #{error[:code]}\n"
370
+ end
371
+ code
372
+ end
373
+
374
+ def align_indented(text)
375
+ shift_left = ensure_line_breaks(text.lines).map { |l| l[/^\s*/].size }.min
376
+ ensure_line_breaks(text.lines).map { |l| l[shift_left..-1] }.join
377
+ end
378
+
379
+ def overwrite_header(new_header)
380
+ overwrite_line_from = action_line
381
+ overwrite_line_to = action_line
382
+ unless old_header.empty?
383
+ overwrite_line_from -= ensure_line_breaks(old_header.lines).count
384
+ end
385
+ lines = ensure_line_breaks(controller_content.lines).to_a
386
+ indentation = lines[action_line][/^\s*/]
387
+ self.controller_content= (lines[0...overwrite_line_from] +
388
+ [new_header.gsub(/^/,indentation)] +
389
+ lines[overwrite_line_to..-1]).join
390
+ end
391
+
392
+ # returns all the lines before the method that might contain the restpi descriptions
393
+ # not bulletproof but working for conventional Ruby code
394
+ def lines_above_method
395
+ added_lines = []
396
+ lines_to_add = []
397
+ block_level = 0
398
+ ensure_line_breaks(controller_content.lines).first(action_line).reverse_each do |line|
399
+ if line =~ /\s*\bend\b\s*/
400
+ block_level += 1
401
+ end
402
+ if block_level > 0
403
+ lines_to_add << line
404
+ else
405
+ added_lines << line
406
+ end
407
+ if line =~ /\s*\b(module|class|def)\b /
408
+ break
409
+ 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
416
+ end
417
+ end
418
+ return added_lines.reverse.join
419
+ end
420
+
421
+ # this method would be totally useless unless some clever guy
422
+ # desided that it would be great idea to change a behavior of
423
+ # "".lines method to not include end of lines.
424
+ #
425
+ # For more details:
426
+ # https://github.com/puppetlabs/puppet/blob/0dc44695/lib/puppet/util/monkey_patches.rb
427
+ def ensure_line_breaks(lines)
428
+ if lines.to_a.size > 1 && lines.first !~ /\n\Z/
429
+ lines.map { |l| l !~ /\n\Z/ ? (l << "\n") : l }.to_enum
430
+ else
431
+ lines
432
+ end
433
+ end
434
+ end
435
+
436
+ # Used to keep apipie_examples.yml params in order
437
+ class OrderedHash < ActiveSupport::OrderedHash
438
+
439
+ def to_yaml_type
440
+ "!tag:yaml.org,2002:map"
441
+ end
442
+
443
+ def to_yaml(opts = {})
444
+ YAML.quick_emit(self, opts) do |out|
445
+ out.map(taguri) do |map|
446
+ each do |k, v|
447
+ map.add(k, v)
448
+ end
449
+ end
450
+ end
451
+ end
452
+ end
453
+ end
454
+ end