zipmark/rspec_api_documentation

View on GitHub
lib/rspec_api_documentation/views/api_blueprint_index.rb

Summary

Maintainability
A
2 hrs
Test Coverage
module RspecApiDocumentation
  module Views
    class ApiBlueprintIndex < MarkupIndex
      def initialize(index, configuration)
        super
        self.template_name = "rspec_api_documentation/api_blueprint_index"
      end

      def sections
        super.map do |section|
          routes = section[:examples].group_by { |e| "#{e.route_uri}#{e.route_optionals}#{e.route_name}" }.map do |route, examples|
            attrs  = fields(:attributes, examples)
            params = fields(:parameters, examples)

            methods = examples.group_by(&:http_method).map do |http_method, examples|
              {
                http_method: http_method,
                description: examples.first.respond_to?(:action_name) && examples.first.action_name,
                examples: examples
              }
            end

            {
              "has_attributes?".to_sym => attrs.size > 0,
              "has_parameters?".to_sym => params.size > 0,
              route: format_route(examples[0]),
              route_name: examples[0][:route_name],
              attributes: attrs,
              parameters: params,
              http_methods: methods
            }
          end

          section.merge({
            routes: routes
          })
        end
      end

      def examples
        @index.examples.map do |example|
          ApiBlueprintExample.new(example, @configuration)
        end
      end

      private

      # APIB follows the RFC 6570 to format URI templates.
      # According to it, simple string expansion (used to perform variable
      # expansion) should be represented by `{var}` and not by `/:var`
      # For example `/posts/:id` should become `/posts/{id}`
      # cf. https://github.com/apiaryio/api-blueprint/blob/format-1A/API%20Blueprint%20Specification.md#431-resource-section
      # cf. https://tools.ietf.org/html/rfc6570#section-3.2.6
      def format_route(example)
        route_uri = example[:route_uri].gsub(/:(.*?)([.\/?{]|$)/, '{\1}\2')
        "#{route_uri}#{example[:route_optionals]}"
      end

      # APIB has both `parameters` and `attributes`. This generates a hash
      # with all of its properties, like name, description, required.
      #   {
      #     required: true,
      #     example: "1",
      #     type: "string",
      #     name: "id",
      #     description: "The id",
      #     properties_description: "required, string"
      #   }
      def fields(property_name, examples)
        examples
          .map { |example| example.metadata[property_name] }
          .flatten
          .compact
          .uniq { |property| property[:name] }
          .map do |property|
            properties = []
            if property[:required] == true
              properties << 'required'
            else
              properties << 'optional'
            end
            properties << property[:type] if property[:type]
            if properties.count > 0
              property[:properties_description] = properties.join(", ")
            else
              property[:properties_description] = nil
            end

            property[:has_default?] = true if property[:default]
            property[:has_enum?] = true if property[:enum]

            property[:annotations] = property[:annotation].lines.map(&:chomp) if property[:annotation]

            property[:description] = nil if description_blank?(property)
            property
          end
      end

      # When no `description` was specified for a parameter, the DSL class
      # is making `description = "#{scope} #{name}"`, which is bad because it
      # assumes that all formats want this behavior. To avoid changing there
      # and breaking everything, I do my own check here and if description
      # equals the name, I assume it is blank.
      def description_blank?(property)
        !property[:description] ||
          property[:description].to_s.strip == property[:name].to_s.strip
      end
    end
  end
end