apipie-dsl 2.0.0

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