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.
- checksums.yaml +7 -0
 - data/LICENSE +21 -0
 - data/README.md +485 -0
 - data/app/controllers/apipie_dsl/apipie_dsls_controller.rb +190 -0
 - data/app/helpers/apipie_dsl_helper.rb +110 -0
 - data/app/public/apipie_dsl/javascripts/apipie_dsl.js +6 -0
 - data/app/public/apipie_dsl/javascripts/bundled/bootstrap-collapse.js +138 -0
 - data/app/public/apipie_dsl/javascripts/bundled/bootstrap.js +1726 -0
 - data/app/public/apipie_dsl/javascripts/bundled/jquery.js +5 -0
 - data/app/public/apipie_dsl/javascripts/bundled/prettify.js +28 -0
 - data/app/public/apipie_dsl/stylesheets/application.css +7 -0
 - data/app/public/apipie_dsl/stylesheets/bundled/bootstrap-responsive.min.css +12 -0
 - data/app/public/apipie_dsl/stylesheets/bundled/bootstrap.min.css +689 -0
 - data/app/public/apipie_dsl/stylesheets/bundled/prettify.css +30 -0
 - data/app/views/apipie_dsl/apipie_dsls/_index_class_meth.erb +11 -0
 - data/app/views/apipie_dsl/apipie_dsls/_index_class_prop.erb +13 -0
 - data/app/views/apipie_dsl/apipie_dsls/_languages.erb +6 -0
 - data/app/views/apipie_dsl/apipie_dsls/_metadata.erb +1 -0
 - data/app/views/apipie_dsl/apipie_dsls/_method.erb +34 -0
 - data/app/views/apipie_dsl/apipie_dsls/_method_detail.erb +58 -0
 - data/app/views/apipie_dsl/apipie_dsls/_params.html.erb +47 -0
 - data/app/views/apipie_dsl/apipie_dsls/_params_plain.html.erb +19 -0
 - data/app/views/apipie_dsl/apipie_dsls/_property.erb +24 -0
 - data/app/views/apipie_dsl/apipie_dsls/_property_detail.erb +35 -0
 - data/app/views/apipie_dsl/apipie_dsls/_raises.html.erb +23 -0
 - data/app/views/apipie_dsl/apipie_dsls/_returns.html.erb +53 -0
 - data/app/views/apipie_dsl/apipie_dsls/apipie_dsl_404.html.erb +17 -0
 - data/app/views/apipie_dsl/apipie_dsls/class.html.erb +75 -0
 - data/app/views/apipie_dsl/apipie_dsls/custom_help.html.erb +10 -0
 - data/app/views/apipie_dsl/apipie_dsls/getting_started.html.erb +4 -0
 - data/app/views/apipie_dsl/apipie_dsls/index.html.erb +72 -0
 - data/app/views/apipie_dsl/apipie_dsls/method.html.erb +52 -0
 - data/app/views/apipie_dsl/apipie_dsls/plain.html.erb +116 -0
 - data/app/views/apipie_dsl/apipie_dsls/static.html.erb +158 -0
 - data/app/views/layouts/apipie_dsl/apipie_dsl.html.erb +26 -0
 - data/lib/apipie-dsl.rb +3 -0
 - data/lib/apipie_dsl.rb +28 -0
 - data/lib/apipie_dsl/Rakefile +6 -0
 - data/lib/apipie_dsl/apipie_dsl_module.rb +51 -0
 - data/lib/apipie_dsl/application.rb +321 -0
 - data/lib/apipie_dsl/class_description.rb +107 -0
 - data/lib/apipie_dsl/configuration.rb +87 -0
 - data/lib/apipie_dsl/dsl.rb +596 -0
 - data/lib/apipie_dsl/errors.rb +68 -0
 - data/lib/apipie_dsl/exception_description.rb +39 -0
 - data/lib/apipie_dsl/markup.rb +41 -0
 - data/lib/apipie_dsl/method_description.rb +112 -0
 - data/lib/apipie_dsl/parameter_description.rb +152 -0
 - data/lib/apipie_dsl/railtie.rb +15 -0
 - data/lib/apipie_dsl/return_description.rb +71 -0
 - data/lib/apipie_dsl/routing.rb +17 -0
 - data/lib/apipie_dsl/see_description.rb +35 -0
 - data/lib/apipie_dsl/static_dispatcher.rb +70 -0
 - data/lib/apipie_dsl/tag_list_description.rb +11 -0
 - data/lib/apipie_dsl/tasks/apipie_dsl/cache.rake +43 -0
 - data/lib/apipie_dsl/tasks/apipie_dsl/static.rake +43 -0
 - data/lib/apipie_dsl/tasks/apipie_dsl/static_json.rake +29 -0
 - data/lib/apipie_dsl/tasks_utils.rb +137 -0
 - data/lib/apipie_dsl/utils.rb +83 -0
 - data/lib/apipie_dsl/validator.rb +479 -0
 - data/lib/apipie_dsl/version.rb +5 -0
 - data/lib/generators/apipie_dsl/install/install_generator.rb +21 -0
 - data/lib/generators/apipie_dsl/install/templates/initializer.rb.erb +9 -0
 - data/lib/generators/apipie_dsl/views_generator.rb +14 -0
 - data/test/test_helper.rb +6 -0
 - 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
         
     |