apipie-dsl 2.0.0

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 (66) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +485 -0
  4. data/app/controllers/apipie_dsl/apipie_dsls_controller.rb +190 -0
  5. data/app/helpers/apipie_dsl_helper.rb +110 -0
  6. data/app/public/apipie_dsl/javascripts/apipie_dsl.js +6 -0
  7. data/app/public/apipie_dsl/javascripts/bundled/bootstrap-collapse.js +138 -0
  8. data/app/public/apipie_dsl/javascripts/bundled/bootstrap.js +1726 -0
  9. data/app/public/apipie_dsl/javascripts/bundled/jquery.js +5 -0
  10. data/app/public/apipie_dsl/javascripts/bundled/prettify.js +28 -0
  11. data/app/public/apipie_dsl/stylesheets/application.css +7 -0
  12. data/app/public/apipie_dsl/stylesheets/bundled/bootstrap-responsive.min.css +12 -0
  13. data/app/public/apipie_dsl/stylesheets/bundled/bootstrap.min.css +689 -0
  14. data/app/public/apipie_dsl/stylesheets/bundled/prettify.css +30 -0
  15. data/app/views/apipie_dsl/apipie_dsls/_index_class_meth.erb +11 -0
  16. data/app/views/apipie_dsl/apipie_dsls/_index_class_prop.erb +13 -0
  17. data/app/views/apipie_dsl/apipie_dsls/_languages.erb +6 -0
  18. data/app/views/apipie_dsl/apipie_dsls/_metadata.erb +1 -0
  19. data/app/views/apipie_dsl/apipie_dsls/_method.erb +34 -0
  20. data/app/views/apipie_dsl/apipie_dsls/_method_detail.erb +58 -0
  21. data/app/views/apipie_dsl/apipie_dsls/_params.html.erb +47 -0
  22. data/app/views/apipie_dsl/apipie_dsls/_params_plain.html.erb +19 -0
  23. data/app/views/apipie_dsl/apipie_dsls/_property.erb +24 -0
  24. data/app/views/apipie_dsl/apipie_dsls/_property_detail.erb +35 -0
  25. data/app/views/apipie_dsl/apipie_dsls/_raises.html.erb +23 -0
  26. data/app/views/apipie_dsl/apipie_dsls/_returns.html.erb +53 -0
  27. data/app/views/apipie_dsl/apipie_dsls/apipie_dsl_404.html.erb +17 -0
  28. data/app/views/apipie_dsl/apipie_dsls/class.html.erb +75 -0
  29. data/app/views/apipie_dsl/apipie_dsls/custom_help.html.erb +10 -0
  30. data/app/views/apipie_dsl/apipie_dsls/getting_started.html.erb +4 -0
  31. data/app/views/apipie_dsl/apipie_dsls/index.html.erb +72 -0
  32. data/app/views/apipie_dsl/apipie_dsls/method.html.erb +52 -0
  33. data/app/views/apipie_dsl/apipie_dsls/plain.html.erb +116 -0
  34. data/app/views/apipie_dsl/apipie_dsls/static.html.erb +158 -0
  35. data/app/views/layouts/apipie_dsl/apipie_dsl.html.erb +26 -0
  36. data/lib/apipie-dsl.rb +3 -0
  37. data/lib/apipie_dsl.rb +28 -0
  38. data/lib/apipie_dsl/Rakefile +6 -0
  39. data/lib/apipie_dsl/apipie_dsl_module.rb +51 -0
  40. data/lib/apipie_dsl/application.rb +321 -0
  41. data/lib/apipie_dsl/class_description.rb +107 -0
  42. data/lib/apipie_dsl/configuration.rb +87 -0
  43. data/lib/apipie_dsl/dsl.rb +596 -0
  44. data/lib/apipie_dsl/errors.rb +68 -0
  45. data/lib/apipie_dsl/exception_description.rb +39 -0
  46. data/lib/apipie_dsl/markup.rb +41 -0
  47. data/lib/apipie_dsl/method_description.rb +112 -0
  48. data/lib/apipie_dsl/parameter_description.rb +152 -0
  49. data/lib/apipie_dsl/railtie.rb +15 -0
  50. data/lib/apipie_dsl/return_description.rb +71 -0
  51. data/lib/apipie_dsl/routing.rb +17 -0
  52. data/lib/apipie_dsl/see_description.rb +35 -0
  53. data/lib/apipie_dsl/static_dispatcher.rb +70 -0
  54. data/lib/apipie_dsl/tag_list_description.rb +11 -0
  55. data/lib/apipie_dsl/tasks/apipie_dsl/cache.rake +43 -0
  56. data/lib/apipie_dsl/tasks/apipie_dsl/static.rake +43 -0
  57. data/lib/apipie_dsl/tasks/apipie_dsl/static_json.rake +29 -0
  58. data/lib/apipie_dsl/tasks_utils.rb +137 -0
  59. data/lib/apipie_dsl/utils.rb +83 -0
  60. data/lib/apipie_dsl/validator.rb +479 -0
  61. data/lib/apipie_dsl/version.rb +5 -0
  62. data/lib/generators/apipie_dsl/install/install_generator.rb +21 -0
  63. data/lib/generators/apipie_dsl/install/templates/initializer.rb.erb +9 -0
  64. data/lib/generators/apipie_dsl/views_generator.rb +14 -0
  65. data/test/test_helper.rb +6 -0
  66. metadata +220 -0
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'apipie_dsl/version'
4
+ require 'apipie_dsl/errors'
5
+ require 'apipie_dsl/markup'
6
+ require 'apipie_dsl/configuration'
7
+ require 'apipie_dsl/apipie_dsl_module'
8
+ require 'apipie_dsl/see_description'
9
+ require 'apipie_dsl/tag_list_description'
10
+ require 'apipie_dsl/exception_description'
11
+ require 'apipie_dsl/parameter_description'
12
+ require 'apipie_dsl/method_description'
13
+ require 'apipie_dsl/class_description'
14
+ require 'apipie_dsl/dsl'
15
+ require 'apipie_dsl/return_description'
16
+ require 'apipie_dsl/validator'
17
+
18
+ module ApipieDSL
19
+ require 'fileutils'
20
+ if defined?(Rails)
21
+ require 'apipie_dsl/railtie'
22
+ require 'apipie_dsl/routing'
23
+ end
24
+
25
+ def self.root
26
+ File.dirname(__dir__)
27
+ end
28
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'apipie-dsl'
4
+
5
+ path = File.expand_path(__dir__)
6
+ Dir.glob("#{path}/tasks/**/*.rake").each { |f| import f }
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'apipie_dsl/utils'
4
+ require 'apipie_dsl/application'
5
+
6
+ module ApipieDSL
7
+ extend ApipieDSL::Utils
8
+
9
+ def self.app
10
+ @app ||= ApipieDSL::Application.new
11
+ end
12
+
13
+ def self.configure
14
+ yield configuration
15
+ end
16
+
17
+ def self.configuration
18
+ @configuration ||= Configuration.new
19
+ end
20
+
21
+ def self.docs(version = nil, class_name = nil, method_name = nil, lang = nil, section = nil)
22
+ version ||= configuration.default_version
23
+ section ||= configuration.default_section
24
+ app.docs(version, class_name, method_name, lang, section)
25
+ end
26
+
27
+ def self.debug(message)
28
+ puts message if configuration.debug
29
+ end
30
+
31
+ # All calls delegated to ApipieDSL::Application instance
32
+ def self.method_missing(method, *args, &block)
33
+ app.respond_to?(method) ? app.send(method, *args, &block) : super
34
+ end
35
+
36
+ def self.app_info(version = nil, lang = nil)
37
+ info = if app_info_version_valid?(version)
38
+ translate(configuration.app_info[version], lang)
39
+ elsif app_info_version_valid?(configuration.default_version)
40
+ translate(configuration.app_info[configuration.default_version], lang)
41
+ else
42
+ 'Another DSL description'
43
+ end
44
+
45
+ ApipieDSL.markup_to_html(info)
46
+ end
47
+
48
+ def self.app_info_version_valid?(version)
49
+ version && configuration.app_info.key?(version)
50
+ end
51
+ end
@@ -0,0 +1,321 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ApipieDSL
4
+ class Application
5
+ if defined? Rails
6
+ require 'apipie_dsl/static_dispatcher'
7
+
8
+ class Engine < Rails::Engine
9
+ initializer 'static assets', :before => :build_middleware_stack do |app|
10
+ app.middleware.use ::ApipieDSL::StaticDispatcher, "#{root}/app/public"
11
+ end
12
+ end
13
+ end
14
+ attr_reader :class_descriptions, :refs
15
+
16
+ def initialize
17
+ initialize_environment
18
+ end
19
+
20
+ def available_versions
21
+ @class_descriptions.keys.sort
22
+ end
23
+
24
+ def set_class_versions(klass, versions)
25
+ @class_versions[klass.to_s] = versions
26
+ end
27
+
28
+ def class_versions(klass)
29
+ ret = @class_versions[klass.to_s]
30
+ return ret unless ret.empty?
31
+ return [ApipieDSL.configuration.default_version] if [Class, Module].include?(klass) || klass.nil?
32
+
33
+ class_versions(ApipieDSL.superclass_for(klass))
34
+ end
35
+
36
+ def get_class_name(klass)
37
+ class_name = klass.respond_to?(:name) ? klass.name : klass
38
+ raise ArgumentError, "ApipieDSL: Can not resolve class #{klass} name." unless class_name.is_a?(String)
39
+ return class_name if ApipieDSL.configuration.class_full_names?
40
+
41
+ class_name.split('::').last
42
+ end
43
+
44
+ def define_param_group(klass, name, &block)
45
+ key = "#{klass.name}##{name}"
46
+ @param_groups[key] = block
47
+ end
48
+
49
+ def get_param_group(klass, name)
50
+ key = "#{klass.name}##{name}"
51
+ raise StandardError, "Param group #{key} is not defined" unless @param_groups.key?(key)
52
+
53
+ @param_groups[key]
54
+ end
55
+
56
+ def define_prop_group(klass, name, &block)
57
+ key = "#{klass.name}##{name}"
58
+ @prop_groups[key] = block
59
+ end
60
+
61
+ def get_prop_group(klass, name)
62
+ key = "#{klass.name}##{name}"
63
+ raise StandardError, "Prop group #{key} is not defined" unless @prop_groups.key?(key)
64
+
65
+ @prop_groups[key]
66
+ end
67
+
68
+ def define_method_description(klass, method_name, dsl_data)
69
+ return if ignored?(klass, method_name)
70
+
71
+ ret_method_description = nil
72
+ versions = dsl_data[:dsl_versions] || []
73
+ versions = class_versions(klass) if versions.empty?
74
+
75
+ versions.each do |version|
76
+ class_name_with_version = "#{version}##{get_class_name(klass)}"
77
+ class_description = get_class_description(class_name_with_version)
78
+
79
+ if class_description.nil?
80
+ class_dsl_data = { sections: [ApipieDSL.configuration.default_section] }
81
+ class_description = define_class_description(klass, version, class_dsl_data)
82
+ end
83
+
84
+ method_description = ApipieDSL::MethodDescription.new(method_name, class_description, dsl_data)
85
+
86
+ # Create separate method description for each version in
87
+ # case the method belongs to more versions. Return just one
88
+ # because the version doesn't matter for the purpose it's used
89
+ # (to wrap the original version with validators)
90
+ ret_method_description ||= method_description
91
+ class_description.add_method_description(method_description)
92
+ end
93
+
94
+ ret_method_description
95
+ end
96
+
97
+ def define_class_description(klass, version, dsl_data = {})
98
+ return if ignored?(klass)
99
+
100
+ class_name = dsl_data[:class_name] || get_class_name(klass)
101
+ class_description = @class_descriptions[version][class_name]
102
+ if class_description
103
+ # Already defined the class somewhere (probably in
104
+ # some method. Updating just meta data from dsl
105
+ class_description.update_from_dsl_data(dsl_data) unless dsl_data.empty?
106
+ else
107
+ class_description = ApipieDSL::ClassDescription.new(klass, class_name, dsl_data, version)
108
+ ApipieDSL.debug("@class_descriptions[#{version}][#{class_name}] = #{class_description}")
109
+ @class_descriptions[version][class_description.id] ||= class_description
110
+ end
111
+ class_description.refs.each do |ref|
112
+ if @refs[version][ref]
113
+ ApipieDSL.debug("Overwriting refs for #{class_name}")
114
+ end
115
+
116
+ @refs[version][ref] = class_description
117
+ end
118
+ class_description
119
+ end
120
+
121
+ # get method
122
+ #
123
+ # There are two ways how this method can be used:
124
+ # 1) Specify both parameters
125
+ # class_name:
126
+ # class - IO
127
+ # string with resource name and version - "v1#io"
128
+ # method_name: name of the method (string or symbol)
129
+ #
130
+ # 2) Specify only first parameter:
131
+ # class_name: string containing both class and method name joined
132
+ # with '#' symbol.
133
+ # - "io#puts" get default version
134
+ # - "v2#io#puts" get specific version
135
+ def get_method_description(class_name, method_name = nil)
136
+ ApipieDSL.debug("Getting #{class_name}##{method_name} documentation")
137
+ crumbs = class_name.split('#')
138
+ method_name = crumbs.pop if method_name.nil?
139
+ class_name = crumbs.join('#')
140
+ class_description = get_class_description(class_name)
141
+ raise ArgumentError, "Class #{class_name} does not exists." if class_description.nil?
142
+
143
+ class_description.method_description(method_name.to_sym)
144
+ end
145
+
146
+ # options:
147
+ # => "io"
148
+ # => "v2#io"
149
+ # => V2::IO
150
+ def get_class_description(klass, version = nil)
151
+ return nil if [NilClass, nil].include?(klass)
152
+
153
+ if klass.is_a?(String)
154
+ crumbs = klass.split('#')
155
+ version = crumbs.first if crumbs.size == 2
156
+ version ||= ApipieDSL.configuration.default_version
157
+ return @class_descriptions[version][crumbs.last] if @class_descriptions.key?(version)
158
+ else
159
+ class_name = get_class_name(klass)
160
+ class_name = "#{version}##{class_name}" if version
161
+ return nil if class_name.nil?
162
+
163
+ class_description = get_class_description(class_name)
164
+ return class_description if class_description && class_description.klass.to_s == klass.to_s
165
+ end
166
+ end
167
+
168
+ # get all versions of class description
169
+ def get_class_descriptions(klass)
170
+ available_versions.map do |version|
171
+ get_class_description(klass, version)
172
+ end.compact
173
+ end
174
+
175
+ # get all versions of method description
176
+ def get_method_descriptions(klass, method)
177
+ get_class_descriptions(klass).map do |class_description|
178
+ class_description.method_description(method.to_sym)
179
+ end.compact
180
+ end
181
+
182
+ def remove_method_description(klass, versions, method_name)
183
+ versions.each do |version|
184
+ klass = get_class_name(klass)
185
+ if (class_description = class_description("#{version}##{klass}"))
186
+ class_description.remove_method_description(method_name)
187
+ end
188
+ end
189
+ end
190
+
191
+ def docs(version, class_name, method_name, lang, section = nil)
192
+ empty_doc = empty_docs(version, lang)
193
+ return empty_doc unless valid_search_args?(version, class_name, method_name)
194
+
195
+ classes =
196
+ if class_name.nil?
197
+ class_descriptions[version].each_with_object({}) do |(name, description), result|
198
+ result[name] = description.docs(section, nil, lang)
199
+ result
200
+ end
201
+ else
202
+ [@class_descriptions[version][class_name].docs(section, method_name, lang)]
203
+ end
204
+ empty_doc[:docs][:classes] = classes
205
+ empty_doc
206
+ end
207
+
208
+ def dsl_classes_paths
209
+ ApipieDSL.configuration.dsl_classes_matchers.map { |m| Dir.glob(m) }
210
+ .concat(Dir.glob(ApipieDSL.configuration.dsl_classes_matcher))
211
+ .flatten.uniq
212
+ end
213
+
214
+ def translate(str, locale)
215
+ if ApipieDSL.configuration.translate
216
+ ApipieDSL.configuration.translate.call(str, locale)
217
+ else
218
+ str
219
+ end
220
+ end
221
+
222
+ def ignored?(klass, method = nil)
223
+ ignored = ApipieDSL.configuration.ignored
224
+ class_name = get_class_name(klass.name)
225
+ return true if ignored.include?(class_name)
226
+ return true if ignored.include?("#{class_name}##{method}")
227
+ end
228
+
229
+ def reload_documentation
230
+ # don't load translated strings, we'll translate them later
231
+ old_locale = locale
232
+ reset_locale(ApipieDSL.configuration.default_locale)
233
+
234
+ rails_mark_classes_for_reload if defined?(Rails)
235
+
236
+ dsl_classes_paths.each do |file|
237
+ begin
238
+ ApipieDSL.debug("Loading #{file}")
239
+ if defined?(Rails)
240
+ load_class_from_file(file)
241
+ else
242
+ load(file)
243
+ end
244
+ rescue StandardError
245
+ # Some constant the file uses may not be defined
246
+ # before it's loaded
247
+ # TODO: better loading
248
+ end
249
+ end
250
+
251
+ reset_locale(old_locale)
252
+ end
253
+
254
+ def load_documentation
255
+ return if @documentation_loaded && !ApipieDSL.configuration.reload_dsl?
256
+
257
+ ApipieDSL.reload_documentation
258
+ @documentation_loaded = true
259
+ end
260
+
261
+ def locale
262
+ ApipieDSL.configuration.locale.call(nil) if ApipieDSL.configuration.locale
263
+ end
264
+
265
+ def reset_locale(loc)
266
+ ApipieDSL.configuration.locale.call(loc) if ApipieDSL.configuration.locale
267
+ end
268
+
269
+ private
270
+
271
+ def initialize_environment
272
+ @class_descriptions ||= Hash.new { |h, version| h[version] = {} }
273
+ @class_versions ||= Hash.new { |h, klass| h[klass.to_s] = [] }
274
+ @refs ||= Hash.new { |h, version| h[version] = {} }
275
+ @param_groups ||= {}
276
+ @prop_groups ||= {}
277
+ end
278
+
279
+ def empty_docs(version, lang)
280
+ url_args = ApipieDSL.configuration.version_in_url ? version : ''
281
+ {
282
+ docs: {
283
+ name: ApipieDSL.configuration.app_name,
284
+ info: ApipieDSL.app_info(version, lang),
285
+ copyright: ApipieDSL.configuration.copyright,
286
+ doc_url: ApipieDSL.full_url(url_args),
287
+ classes: {}
288
+ }
289
+ }
290
+ end
291
+
292
+ def load_class_from_file(class_file)
293
+ require_dependency class_file
294
+ end
295
+
296
+ # Since Rails 3.2, the classes are reloaded only on file change.
297
+ # We need to reload all the classes to rebuild the
298
+ # docs, therefore we just force to reload all the code.
299
+ def rails_mark_classes_for_reload
300
+ unless Rails.application.config.cache_classes
301
+ Rails::VERSION::MAJOR == 4 ? ActionDispatch::Reloader.cleanup! : Rails.application.reloader.reload!
302
+ initialize_environment
303
+ Rails::VERSION::MAJOR == 4 ? ActionDispatch::Reloader.prepare! : Rails.application.reloader.prepare!
304
+ end
305
+ end
306
+
307
+ def valid_search_args?(version, class_name, method_name)
308
+ return false unless class_descriptions.key?(version)
309
+
310
+ if class_name
311
+ return false unless class_descriptions[version].key?(class_name)
312
+
313
+ if method_name
314
+ class_description = class_descriptions[version][class_name]
315
+ return false unless class_description.valid_method_name?(method_name)
316
+ end
317
+ end
318
+ true
319
+ end
320
+ end
321
+ end
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ApipieDSL
4
+ class ClassDescription
5
+ attr_reader :klass, :short_description, :full_description, :methods,
6
+ :deprecated, :show, :refs, :sections
7
+
8
+ def initialize(klass, class_name, dsl_data = nil, version = nil)
9
+ @klass = klass
10
+ @name = class_name
11
+ @methods = {}
12
+ @properties = []
13
+ @version = version || ApipieDSL.configuration.default_version
14
+ @parent = ApipieDSL.get_class_description(ApipieDSL.superclass_for(klass), version)
15
+ @refs = []
16
+ @sections = []
17
+ @show = true
18
+ update_from_dsl_data(dsl_data) if dsl_data
19
+ end
20
+
21
+ def update_from_dsl_data(dsl_data)
22
+ @name = dsl_data[:class_name] if dsl_data[:class_name]
23
+ @full_description = ApipieDSL.markup_to_html(dsl_data[:description])
24
+ @short_description = dsl_data[:short_description]
25
+ @tag_list = dsl_data[:tag_list]
26
+ @metadata = dsl_data[:meta]
27
+ @deprecated = dsl_data[:deprecated] || false
28
+ @show = dsl_data[:show] || @show
29
+ @properties = (dsl_data[:properties] || []).map do |args|
30
+ ApipieDSL::MethodDescription.from_dsl_data(self, args)
31
+ end
32
+ @refs = dsl_data[:refs] || [@name]
33
+ @sections = dsl_data[:sections] || @sections
34
+ return unless dsl_data[:app_info]
35
+
36
+ ApipieDSL.configuration.app_info[version] = dsl_data[:app_info]
37
+ end
38
+
39
+ def id
40
+ return @klass.name if ApipieDSL.configuration.class_full_names?
41
+
42
+ @name
43
+ end
44
+
45
+ def version
46
+ @version || @parent.try(:version) || ApipieDSL.configuration.default_version
47
+ end
48
+
49
+ def add_method_description(method_description)
50
+ ApipieDSL.debug "@resource_descriptions[#{version}][#{@name}].methods[#{method_description.name}] = #{method_description}"
51
+ @methods[method_description.name.to_sym] = method_description
52
+ end
53
+
54
+ def method_description(method_name)
55
+ @methods[method_name.to_sym]
56
+ end
57
+
58
+ def remove_method_description(method_name)
59
+ return unless @methods.key?(method_name)
60
+
61
+ @methods.delete(method_name)
62
+ end
63
+
64
+ def method_descriptions
65
+ @methods.values
66
+ end
67
+
68
+ def property_descriptions
69
+ @properties
70
+ end
71
+
72
+ def doc_url(section = nil)
73
+ crumbs = []
74
+ crumbs << version if ApipieDSL.configuration.version_in_url
75
+ crumbs << section if section
76
+ crumbs << id
77
+ ApipieDSL.full_url(crumbs.join('/'))
78
+ end
79
+
80
+ def valid_method_name?(method_name)
81
+ @methods.keys.map(&:to_s).include?(method_name.to_s)
82
+ end
83
+
84
+ def docs(section = nil, method_name = nil, lang = nil)
85
+ raise "Method #{method_name} not found for class #{id}" if method_name && !valid_method_name?(method_name)
86
+
87
+ methods = if method_name.nil?
88
+ @methods.map { |_key, method_desc| method_desc.docs(section, lang) }
89
+ else
90
+ [@methods[method_name.to_sym].docs(section, lang)]
91
+ end
92
+ {
93
+ id: id,
94
+ name: @name,
95
+ doc_url: doc_url(section),
96
+ short_description: ApipieDSL.translate(@short_description, lang),
97
+ full_description: ApipieDSL.translate(@full_description, lang),
98
+ version: version,
99
+ metadata: @metadata,
100
+ properties: @properties.map { |prop_desc| prop_desc.docs(section, lang) },
101
+ methods: methods,
102
+ deprecated: @deprecated,
103
+ show: @show
104
+ }
105
+ end
106
+ end
107
+ end