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.
- checksums.yaml +17 -0
- data/.gitignore +14 -0
- data/.rspec +2 -0
- data/.travis.yml +27 -0
- data/APACHE-LICENSE-2.0 +202 -0
- data/CHANGELOG.md +469 -0
- data/Gemfile +1 -0
- data/Gemfile.rails32 +6 -0
- data/Gemfile.rails41 +6 -0
- data/Gemfile.rails42 +11 -0
- data/Gemfile.rails50 +6 -0
- data/Gemfile.rails51 +7 -0
- data/MIT-LICENSE +20 -0
- data/NOTICE +4 -0
- data/PROPOSAL_FOR_RESPONSE_DESCRIPTIONS.md +244 -0
- data/README.rst +1874 -0
- data/Rakefile +13 -0
- data/apipierails3.gemspec +27 -0
- data/app/controllers/apipie/apipies_controller.rb +199 -0
- data/app/helpers/apipie_helper.rb +10 -0
- data/app/public/apipie/javascripts/apipie.js +6 -0
- data/app/public/apipie/javascripts/bundled/bootstrap-collapse.js +138 -0
- data/app/public/apipie/javascripts/bundled/bootstrap.js +1726 -0
- data/app/public/apipie/javascripts/bundled/jquery.js +5 -0
- data/app/public/apipie/javascripts/bundled/prettify.js +28 -0
- data/app/public/apipie/stylesheets/application.css +7 -0
- data/app/public/apipie/stylesheets/bundled/bootstrap-responsive.min.css +12 -0
- data/app/public/apipie/stylesheets/bundled/bootstrap.min.css +689 -0
- data/app/public/apipie/stylesheets/bundled/prettify.css +30 -0
- data/app/views/apipie/apipies/_disqus.html.erb +13 -0
- data/app/views/apipie/apipies/_errors.html.erb +23 -0
- data/app/views/apipie/apipies/_headers.html.erb +26 -0
- data/app/views/apipie/apipies/_languages.erb +6 -0
- data/app/views/apipie/apipies/_metadata.erb +1 -0
- data/app/views/apipie/apipies/_method_detail.erb +61 -0
- data/app/views/apipie/apipies/_params.html.erb +42 -0
- data/app/views/apipie/apipies/_params_plain.html.erb +20 -0
- data/app/views/apipie/apipies/apipie_404.html.erb +17 -0
- data/app/views/apipie/apipies/apipie_checksum.json.erb +1 -0
- data/app/views/apipie/apipies/getting_started.html.erb +6 -0
- data/app/views/apipie/apipies/index.html.erb +56 -0
- data/app/views/apipie/apipies/method.html.erb +41 -0
- data/app/views/apipie/apipies/plain.html.erb +77 -0
- data/app/views/apipie/apipies/resource.html.erb +80 -0
- data/app/views/apipie/apipies/static.html.erb +103 -0
- data/app/views/layouts/apipie/apipie.html.erb +27 -0
- data/config/locales/de.yml +28 -0
- data/config/locales/en.yml +32 -0
- data/config/locales/es.yml +28 -0
- data/config/locales/fr.yml +31 -0
- data/config/locales/it.yml +31 -0
- data/config/locales/ja.yml +31 -0
- data/config/locales/pl.yml +28 -0
- data/config/locales/pt-BR.yml +28 -0
- data/config/locales/ru.yml +28 -0
- data/config/locales/tr.yml +28 -0
- data/config/locales/zh-CN.yml +28 -0
- data/config/locales/zh-TW.yml +28 -0
- data/images/screenshot-1.png +0 -0
- data/images/screenshot-2.png +0 -0
- data/lib/apipie/apipie_module.rb +83 -0
- data/lib/apipie/application.rb +462 -0
- data/lib/apipie/configuration.rb +186 -0
- data/lib/apipie/dsl_definition.rb +607 -0
- data/lib/apipie/error_description.rb +44 -0
- data/lib/apipie/errors.rb +86 -0
- data/lib/apipie/extractor.rb +177 -0
- data/lib/apipie/extractor/collector.rb +117 -0
- data/lib/apipie/extractor/recorder.rb +166 -0
- data/lib/apipie/extractor/writer.rb +454 -0
- data/lib/apipie/helpers.rb +73 -0
- data/lib/apipie/markup.rb +48 -0
- data/lib/apipie/method_description.rb +273 -0
- data/lib/apipie/middleware/checksum_in_headers.rb +35 -0
- data/lib/apipie/param_description.rb +280 -0
- data/lib/apipie/railtie.rb +9 -0
- data/lib/apipie/resource_description.rb +124 -0
- data/lib/apipie/response_description.rb +131 -0
- data/lib/apipie/response_description_adapter.rb +200 -0
- data/lib/apipie/routes_formatter.rb +33 -0
- data/lib/apipie/routing.rb +16 -0
- data/lib/apipie/rspec/response_validation_helper.rb +192 -0
- data/lib/apipie/see_description.rb +39 -0
- data/lib/apipie/static_dispatcher.rb +69 -0
- data/lib/apipie/swagger_generator.rb +707 -0
- data/lib/apipie/tag_list_description.rb +11 -0
- data/lib/apipie/validator.rb +526 -0
- data/lib/apipie/version.rb +3 -0
- data/lib/apipierails3.rb +25 -0
- data/lib/generators/apipie/install/README +6 -0
- data/lib/generators/apipie/install/install_generator.rb +25 -0
- data/lib/generators/apipie/install/templates/initializer.rb.erb +7 -0
- data/lib/generators/apipie/views_generator.rb +11 -0
- data/lib/tasks/apipie.rake +345 -0
- data/rel-eng/packages/.readme +3 -0
- data/rel-eng/packages/rubygem-apipie-rails +1 -0
- data/rel-eng/tito.props +5 -0
- data/spec/controllers/api/v1/architectures_controller_spec.rb +29 -0
- data/spec/controllers/api/v2/architectures_controller_spec.rb +12 -0
- data/spec/controllers/api/v2/nested/resources_controller_spec.rb +11 -0
- data/spec/controllers/apipies_controller_spec.rb +273 -0
- data/spec/controllers/concerns_controller_spec.rb +42 -0
- data/spec/controllers/extended_controller_spec.rb +11 -0
- data/spec/controllers/users_controller_spec.rb +740 -0
- data/spec/dummy/Rakefile +7 -0
- data/spec/dummy/app/controllers/api/base_controller.rb +4 -0
- data/spec/dummy/app/controllers/api/v1/architectures_controller.rb +43 -0
- data/spec/dummy/app/controllers/api/v1/base_controller.rb +11 -0
- data/spec/dummy/app/controllers/api/v2/architectures_controller.rb +30 -0
- data/spec/dummy/app/controllers/api/v2/base_controller.rb +11 -0
- data/spec/dummy/app/controllers/api/v2/nested/architectures_controller.rb +32 -0
- data/spec/dummy/app/controllers/api/v2/nested/resources_controller.rb +33 -0
- data/spec/dummy/app/controllers/application_controller.rb +18 -0
- data/spec/dummy/app/controllers/concerns/extending_concern.rb +11 -0
- data/spec/dummy/app/controllers/concerns/sample_controller.rb +41 -0
- data/spec/dummy/app/controllers/concerns_controller.rb +8 -0
- data/spec/dummy/app/controllers/extended_controller.rb +14 -0
- data/spec/dummy/app/controllers/files_controller.rb +5 -0
- data/spec/dummy/app/controllers/overridden_concerns_controller.rb +31 -0
- data/spec/dummy/app/controllers/pets_controller.rb +408 -0
- data/spec/dummy/app/controllers/pets_using_auto_views_controller.rb +73 -0
- data/spec/dummy/app/controllers/pets_using_self_describing_classes_controller.rb +95 -0
- data/spec/dummy/app/controllers/tagged_cats_controller.rb +32 -0
- data/spec/dummy/app/controllers/tagged_dogs_controller.rb +15 -0
- data/spec/dummy/app/controllers/twitter_example_controller.rb +307 -0
- data/spec/dummy/app/controllers/users_controller.rb +297 -0
- data/spec/dummy/app/views/layouts/application.html.erb +21 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +49 -0
- data/spec/dummy/config/boot.rb +10 -0
- data/spec/dummy/config/database.yml +21 -0
- data/spec/dummy/config/environment.rb +8 -0
- data/spec/dummy/config/environments/development.rb +28 -0
- data/spec/dummy/config/environments/production.rb +52 -0
- data/spec/dummy/config/environments/test.rb +38 -0
- data/spec/dummy/config/initializers/apipie.rb +110 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/inflections.rb +10 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/secret_token.rb +8 -0
- data/spec/dummy/config/initializers/session_store.rb +8 -0
- data/spec/dummy/config/locales/en.yml +5 -0
- data/spec/dummy/config/routes.rb +51 -0
- data/spec/dummy/db/.gitkeep +0 -0
- data/spec/dummy/doc/apipie_examples.json +1 -0
- data/spec/dummy/doc/users/desc_from_file.md +1 -0
- data/spec/dummy/public/404.html +26 -0
- data/spec/dummy/public/422.html +26 -0
- data/spec/dummy/public/500.html +26 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/public/stylesheets/.gitkeep +0 -0
- data/spec/dummy/script/rails +6 -0
- data/spec/lib/application_spec.rb +49 -0
- data/spec/lib/extractor/extractor_spec.rb +9 -0
- data/spec/lib/extractor/middleware_spec.rb +44 -0
- data/spec/lib/extractor/writer_spec.rb +110 -0
- data/spec/lib/file_handler_spec.rb +18 -0
- data/spec/lib/method_description_spec.rb +98 -0
- data/spec/lib/param_description_spec.rb +345 -0
- data/spec/lib/param_group_spec.rb +60 -0
- data/spec/lib/rake_spec.rb +71 -0
- data/spec/lib/resource_description_spec.rb +48 -0
- data/spec/lib/swagger/openapi_2_0_schema.json +1607 -0
- data/spec/lib/swagger/rake_swagger_spec.rb +139 -0
- data/spec/lib/swagger/response_validation_spec.rb +104 -0
- data/spec/lib/swagger/swagger_dsl_spec.rb +658 -0
- data/spec/lib/validator_spec.rb +113 -0
- data/spec/lib/validators/array_validator_spec.rb +85 -0
- data/spec/spec_helper.rb +109 -0
- data/spec/support/rake.rb +21 -0
- 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
|