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,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ApipieDSL
4
+ class Error < StandardError; end
5
+ class ParamError < Error
6
+ attr_accessor :param
7
+
8
+ def initialize(param)
9
+ @param = param
10
+ end
11
+ end
12
+
13
+ class ParamMissing < ParamError
14
+ def to_s
15
+ return "Missing parameter #{@param.name}" if @param.options[:missing_message].nil?
16
+
17
+ if @param.options[:missing_message].is_a?(Proc)
18
+ @param.options[:missing_message].call
19
+ else
20
+ @param.options[:missing_message].to_s
21
+ end
22
+ end
23
+ end
24
+
25
+ class UnknownParam < ParamError
26
+ def to_s
27
+ "Unknown parameter #{@param}"
28
+ end
29
+ end
30
+
31
+ class ParamInvalid < ParamError
32
+ attr_accessor :value, :error
33
+
34
+ def initialize(param, value, error)
35
+ super(param)
36
+ @value = value
37
+ @error = error
38
+ end
39
+
40
+ def to_s
41
+ "Invalid parameter '#{@param}' value #{@value.inspect}: #{@error}"
42
+ end
43
+ end
44
+
45
+ class MultipleDefinitionError < Error
46
+ attr_accessor :value
47
+
48
+ def initialize(value)
49
+ @value = value
50
+ end
51
+
52
+ def to_s
53
+ "Multiple definition of #{@value}"
54
+ end
55
+ end
56
+
57
+ class ReturnsMultipleDefinitionError < Error
58
+ def to_s
59
+ "A 'returns' statement cannot indicate both array_of and type"
60
+ end
61
+ end
62
+
63
+ class MultipleReturnsError < Error
64
+ def to_s
65
+ "A 'returns' statement cannot be used more than once"
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ApipieDSL
4
+ class ExceptionDescription
5
+ attr_reader :error, :description, :metadata
6
+
7
+ def self.from_dsl_data(args)
8
+ error_or_options, desc, options = args
9
+ ApipieDSL::ExceptionDescription.new(error_or_options, desc, options)
10
+ end
11
+
12
+ def initialize(error_or_options, desc = nil, options = {})
13
+ if error_or_options.is_a?(Hash)
14
+ error_or_options = error_or_options.transform_keys(&:to_sym)
15
+ @error = error_or_options[:error]
16
+ @metadata = error_or_options[:meta]
17
+ @description = error_or_options[:desc] || error_or_options[:description]
18
+ else
19
+ @error = if error_or_options.is_a?(Symbol)
20
+ Rack::Utils::SYMBOL_TO_STATUS_CODE[error_or_options]
21
+ else
22
+ error_or_options
23
+ end
24
+ raise ArgumentError, error_or_options unless @error
25
+
26
+ @metadata = options[:meta]
27
+ @description = desc
28
+ end
29
+ end
30
+
31
+ def docs
32
+ {
33
+ error: error,
34
+ description: ApipieDSL.markup_to_html(description),
35
+ metadata: metadata
36
+ }
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ApipieDSL
4
+ module Markup
5
+ class RDoc
6
+ def initialize
7
+ require 'rdoc'
8
+ require 'rdoc/markup/to_html'
9
+ @rdoc ||= if Gem::Version.new(::RDoc::VERSION) < Gem::Version.new('4.0.0')
10
+ ::RDoc::Markup::ToHtml.new
11
+ else
12
+ ::RDoc::Markup::ToHtml.new(::RDoc::Options.new)
13
+ end
14
+ end
15
+
16
+ def to_html(text)
17
+ @rdoc.convert(text)
18
+ end
19
+ end
20
+
21
+ class Markdown
22
+ def initialize
23
+ require 'maruku'
24
+ end
25
+
26
+ def to_html(text)
27
+ Maruku.new(text).to_html
28
+ end
29
+ end
30
+
31
+ class Textile
32
+ def initialize
33
+ require 'RedCloth'
34
+ end
35
+
36
+ def to_html(text)
37
+ RedCloth.new(text).to_html
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ApipieDSL
4
+ class MethodDescription
5
+ attr_reader :name, :klass, :see, :examples
6
+ attr_accessor :full_description, :short_description, :metadata, :show,
7
+ :raises, :returns, :aliases
8
+ alias_method :class_description, :klass
9
+
10
+ def self.from_dsl_data(klass, args)
11
+ name, dsl_data = args
12
+ ApipieDSL::MethodDescription.new(name, klass, dsl_data)
13
+ end
14
+
15
+ def initialize(name, klass, dsl_data)
16
+ @name = name.to_s
17
+ @klass = klass
18
+
19
+ desc = dsl_data[:description] || ''
20
+ @full_description = ApipieDSL.markup_to_html(desc)
21
+
22
+ @short_description = dsl_data[:short_description] || ''
23
+
24
+ @params = (dsl_data[:params] || []).map do |args|
25
+ ApipieDSL::ParameterDescription.from_dsl_data(self, args)
26
+ end
27
+
28
+ @params = ApipieDSL::ParameterDescription.unify(@params)
29
+
30
+ @raises = (dsl_data[:raises] || []).map do |args|
31
+ ApipieDSL::ExceptionDescription.from_dsl_data(args)
32
+ end
33
+
34
+ # Every method in Ruby returns an onject
35
+ dsl_data[:returns] = [{ object_of: Object }] if dsl_data[:returns].nil?
36
+
37
+ @returns = ApipieDSL::ReturnDescription.from_dsl_data(self, dsl_data[:returns])
38
+
39
+ @tag_list = dsl_data[:tag_list]
40
+
41
+ @see = (dsl_data[:see] || []).map do |method, options|
42
+ options[:scope] ||= @klass
43
+ ApipieDSL::SeeDescription.new(method, options)
44
+ end
45
+
46
+ @metadata = dsl_data[:meta]
47
+
48
+ @show = dsl_data[:show].nil? ? true : dsl_data[:show]
49
+
50
+ @examples = (dsl_data[:examples] || []).select do |example|
51
+ next example if example[:for].nil?
52
+
53
+ example[:for].to_s == @name
54
+ end
55
+
56
+ @aliases = dsl_data[:aliases]
57
+ end
58
+
59
+ def id
60
+ "#{klass.id}##{name}"
61
+ end
62
+
63
+ def plain_params
64
+ @params
65
+ end
66
+
67
+ def params
68
+ param_descriptions.each_with_object({}) { |p, h| h[p.name] = p }
69
+ .sort.to_h
70
+ end
71
+
72
+ def param_descriptions
73
+ @params.select(&:validator)
74
+ end
75
+
76
+ def tag_list
77
+ parent = ApipieDSL.get_class_description(ApipieDSL.superclass_for(@klass.class))
78
+ parent_tags = [parent, @klass].compact.flat_map(&:tag_list_arg)
79
+ ApipieDSL::TagListDescription.new((parent_tags + @tag_list).uniq.compact)
80
+ end
81
+
82
+ def version
83
+ klass.version
84
+ end
85
+
86
+ def doc_url(section = nil)
87
+ crumbs = []
88
+ crumbs << @klass.version if ApipieDSL.configuration.version_in_url
89
+ crumbs << section if section
90
+ crumbs << @klass.id
91
+ crumbs << @name
92
+ ApipieDSL.full_url(crumbs.join('/')).gsub('?', '%3F')
93
+ end
94
+
95
+ def docs(section = nil, lang = nil)
96
+ {
97
+ doc_url: doc_url(section),
98
+ name: @name,
99
+ full_description: ApipieDSL.translate(@full_description, lang),
100
+ short_description: ApipieDSL.translate(@short_description, lang),
101
+ params: param_descriptions.map { |param| param.docs(lang) }.flatten,
102
+ raises: raises.map(&:docs),
103
+ returns: @returns.docs(lang),
104
+ metadata: @metadata,
105
+ see: see.map(&:docs),
106
+ show: @show,
107
+ examples: @examples,
108
+ aliases: aliases
109
+ }
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,152 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ApipieDSL
4
+ # method parameter description
5
+ #
6
+ # name - method name (show)
7
+ # desc - description
8
+ # validator - Validator::BaseValidator subclass
9
+ class ParameterDescription
10
+ attr_reader :name, :desc, :type, :options, :method_description,
11
+ :metadata, :show, :is_array, :default_value
12
+ attr_accessor :parent
13
+
14
+ alias_method :is_array?, :is_array
15
+
16
+ def self.from_dsl_data(method_description, args)
17
+ name, validator, desc_or_options, options, block = args
18
+ ApipieDSL::ParameterDescription.new(method_description,
19
+ name,
20
+ validator,
21
+ desc_or_options,
22
+ options,
23
+ &block)
24
+ end
25
+
26
+ def ==(other)
27
+ return false unless self.class == other.class
28
+
29
+ if method_description == other.method_description && @options == other.options
30
+ true
31
+ else
32
+ false
33
+ end
34
+ end
35
+
36
+ # rubocop:disable Metrics/ParameterLists
37
+ def initialize(method_description, name, validator, desc_or_options = nil, options = {}, &block)
38
+ if desc_or_options.is_a?(Hash)
39
+ options = options.merge(desc_or_options)
40
+ elsif desc_or_options.is_a?(String)
41
+ options[:desc] = desc_or_options
42
+ elsif !desc_or_options.nil?
43
+ raise ArgumentError, 'Parameter description: expected description or options as 3rd parameter'
44
+ end
45
+
46
+ @options = options.transform_keys(&:to_sym)
47
+
48
+ @method_description = method_description
49
+ @name = name
50
+ @desc = @options[:desc]
51
+ @type = @options[:type] || :required
52
+ @schema = @options[:schema]
53
+ @default_value = @options[:default]
54
+ @parent = @options[:parent]
55
+ @metadata = @options[:meta]
56
+ @show = @options.key?(:show) ? @options[:show] : true
57
+
58
+ return unless validator
59
+
60
+ @validator = if validator.is_a?(String)
61
+ ApipieDSL::Validator::Lazy.new(self, validator, @options, block)
62
+ else
63
+ ApipieDSL::Validator::BaseValidator.find(self, validator, @options, block)
64
+ end
65
+ raise StandardError, "Validator for #{validator} not found." unless @validator
66
+ end
67
+ # rubocop:enable Metrics/ParameterLists
68
+
69
+ def validator
70
+ return @validator unless @validator.is_a?(ApipieDSL::Validator::Lazy)
71
+
72
+ @validator = @validator.build
73
+ end
74
+
75
+ def validate(value)
76
+ validator.valid?(value)
77
+ end
78
+
79
+ def full_name
80
+ name_parts = parents_and_self.map { |p| p.name if p.show }.compact
81
+ return name.to_s if name_parts.empty?
82
+
83
+ ([name_parts.first] + name_parts[1..-1].map { |n| "[#{n}]" }).join('')
84
+ end
85
+
86
+ # Returns an array of all the parents: starting with the root parent
87
+ # ending with itself
88
+ def parents_and_self
89
+ ret = []
90
+ ret.concat(parent.parents_and_self) if parent
91
+ ret << self
92
+ ret
93
+ end
94
+
95
+ def merge_with(other_param_desc)
96
+ if validator && other_param_desc.validator
97
+ validator.merge_with(other_param_desc.validator)
98
+ else
99
+ self.validator ||= other_param_desc.validator
100
+ end
101
+ self
102
+ end
103
+
104
+ # Merge param descriptions. Allows defining hash params on more places
105
+ # (e.g. in param_groups). For example:
106
+ #
107
+ # def_param_group :user do
108
+ # param :user, Hash do
109
+ # param :name, String
110
+ # end
111
+ # end
112
+ #
113
+ # param_group :user
114
+ # param :user, Hash do
115
+ # param :password, String
116
+ # end
117
+ def self.unify(params)
118
+ ordering = params.map(&:name)
119
+ params.group_by(&:name).map do |_name, description|
120
+ description.reduce(&:merge_with)
121
+ end.sort_by { |param| ordering.index(param.name) }
122
+ end
123
+
124
+ def self.merge(target_params, source_params)
125
+ params_to_merge, params_to_add = source_params.partition do |source_param|
126
+ target_params.any? { |target_param| source_param.name == target_param.name }
127
+ end
128
+ unify(target_params + params_to_merge)
129
+ target_params.concat(params_to_add)
130
+ end
131
+
132
+ def docs(lang = nil)
133
+ hash = {
134
+ name: name.to_s,
135
+ full_name: full_name,
136
+ description: ApipieDSL.markup_to_html(ApipieDSL.translate(@options[:desc], lang)),
137
+ type: type.to_s,
138
+ default: default_value,
139
+ validator: validator.to_s,
140
+ expected_type: validator.expected_type,
141
+ metadata: metadata,
142
+ show: show
143
+ }
144
+ hash.delete(:default) if type == :required
145
+ hash[:schema] = @schema if type == :block
146
+ return hash unless validator.sub_params
147
+
148
+ hash[:params] = validator.sub_params.map { |param| param.docs(lang) }
149
+ hash
150
+ end
151
+ end
152
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'apipie-dsl'
4
+ require 'rails'
5
+
6
+ module ApipieDSL
7
+ class Railtie < Rails::Railtie
8
+ railtie_name :apipie_dsl
9
+
10
+ rake_tasks do
11
+ path = File.expand_path(__dir__)
12
+ Dir.glob("#{path}/tasks/**/*.rake").each { |f| load f }
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ApipieDSL
4
+ class ReturnDescription
5
+ class ReturnObject
6
+ include ApipieDSL::Base
7
+ include ApipieDSL::Parameter
8
+
9
+ def initialize(method_description, options, block)
10
+ @method_description = method_description
11
+ @scope = options[:scope]
12
+ @param_group = { scope: @scope }
13
+ @options = options
14
+ @return_type = (@options.keys & %i[array_of one_of object_of param_group]).first
15
+
16
+ return unless @options[@return_type].is_a?(::Class)
17
+ end
18
+
19
+ # this routine overrides Param#default_param_group_scope
20
+ # and is called if Param#param_group is invoked
21
+ # during the instance_exec call in ReturnObject#initialize
22
+ def default_param_group_scope
23
+ @scope
24
+ end
25
+
26
+ def return_class
27
+ case @return_type
28
+ when :object_of
29
+ @options[@return_type]
30
+ when :one_of, :param_group
31
+ Object
32
+ when :array_of
33
+ Array
34
+ end
35
+ end
36
+
37
+ def docs(lang = nil)
38
+ {
39
+ meta: @return_type,
40
+ class: return_class,
41
+ data: @options[@return_type]
42
+ }
43
+ end
44
+ end
45
+ end
46
+
47
+ class ReturnDescription
48
+ def self.from_dsl_data(method_description, args)
49
+ options, block = args
50
+
51
+ new(method_description, options, block)
52
+ end
53
+
54
+ def initialize(method_description, options, block)
55
+ if options[:array_of] && options[:one_of] && options[:object_of] && options[:param_group]
56
+ raise ReturnsMultipleDefinitionError, options
57
+ end
58
+
59
+ @description = options[:desc]
60
+ @returns_object = ReturnObject.new(method_description, options, block)
61
+ end
62
+
63
+
64
+ def docs(lang = nil)
65
+ {
66
+ description: @description,
67
+ object: @returns_object.docs(lang)
68
+ }
69
+ end
70
+ end
71
+ end