grape 0.9.0 → 0.10.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.
Potentially problematic release.
This version of grape might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.rubocop.yml +1 -66
- data/.rubocop_todo.yml +78 -17
- data/.travis.yml +7 -3
- data/Appraisals +7 -0
- data/CHANGELOG.md +24 -0
- data/CONTRIBUTING.md +7 -0
- data/Gemfile +1 -7
- data/Guardfile +1 -1
- data/README.md +560 -94
- data/RELEASING.md +1 -1
- data/Rakefile +10 -11
- data/UPGRADING.md +211 -3
- data/gemfiles/rails_3.gemfile +14 -0
- data/gemfiles/rails_4.gemfile +14 -0
- data/grape.gemspec +10 -9
- data/lib/backports/active_support/deep_dup.rb +49 -0
- data/lib/backports/active_support/duplicable.rb +88 -0
- data/lib/grape.rb +29 -2
- data/lib/grape/api.rb +59 -65
- data/lib/grape/dsl/api.rb +19 -0
- data/lib/grape/dsl/callbacks.rb +6 -4
- data/lib/grape/dsl/configuration.rb +49 -5
- data/lib/grape/dsl/helpers.rb +7 -8
- data/lib/grape/dsl/inside_route.rb +22 -10
- data/lib/grape/dsl/middleware.rb +5 -5
- data/lib/grape/dsl/parameters.rb +6 -2
- data/lib/grape/dsl/request_response.rb +23 -20
- data/lib/grape/dsl/routing.rb +52 -49
- data/lib/grape/dsl/settings.rb +110 -0
- data/lib/grape/dsl/validations.rb +14 -6
- data/lib/grape/endpoint.rb +104 -88
- data/lib/grape/exceptions/base.rb +2 -2
- data/lib/grape/exceptions/incompatible_option_values.rb +1 -1
- data/lib/grape/exceptions/invalid_formatter.rb +1 -1
- data/lib/grape/exceptions/invalid_versioner_option.rb +1 -1
- data/lib/grape/exceptions/invalid_with_option_for_represent.rb +1 -1
- data/lib/grape/exceptions/missing_mime_type.rb +1 -1
- data/lib/grape/exceptions/missing_option.rb +1 -1
- data/lib/grape/exceptions/missing_vendor_option.rb +1 -1
- data/lib/grape/exceptions/unknown_options.rb +1 -1
- data/lib/grape/exceptions/unknown_validator.rb +1 -1
- data/lib/grape/exceptions/validation.rb +1 -1
- data/lib/grape/exceptions/validation_errors.rb +2 -2
- data/lib/grape/formatter/serializable_hash.rb +1 -1
- data/lib/grape/formatter/xml.rb +1 -1
- data/lib/grape/locale/en.yml +2 -0
- data/lib/grape/middleware/auth/dsl.rb +26 -21
- data/lib/grape/middleware/auth/strategies.rb +1 -1
- data/lib/grape/middleware/auth/strategy_info.rb +0 -2
- data/lib/grape/middleware/base.rb +2 -2
- data/lib/grape/middleware/error.rb +1 -1
- data/lib/grape/middleware/formatter.rb +5 -5
- data/lib/grape/middleware/versioner.rb +1 -1
- data/lib/grape/middleware/versioner/header.rb +3 -3
- data/lib/grape/middleware/versioner/param.rb +2 -2
- data/lib/grape/middleware/versioner/path.rb +1 -1
- data/lib/grape/namespace.rb +1 -1
- data/lib/grape/path.rb +9 -3
- data/lib/grape/util/content_types.rb +16 -8
- data/lib/grape/util/inheritable_setting.rb +74 -0
- data/lib/grape/util/inheritable_values.rb +51 -0
- data/lib/grape/util/stackable_values.rb +52 -0
- data/lib/grape/util/strict_hash_configuration.rb +106 -0
- data/lib/grape/validations.rb +0 -220
- data/lib/grape/validations/attributes_iterator.rb +21 -0
- data/lib/grape/validations/params_scope.rb +176 -0
- data/lib/grape/validations/validators/all_or_none.rb +20 -0
- data/lib/grape/validations/validators/allow_blank.rb +30 -0
- data/lib/grape/validations/validators/at_least_one_of.rb +20 -0
- data/lib/grape/validations/validators/base.rb +37 -0
- data/lib/grape/validations/{coerce.rb → validators/coerce.rb} +3 -3
- data/lib/grape/validations/{default.rb → validators/default.rb} +1 -1
- data/lib/grape/validations/validators/exactly_one_of.rb +20 -0
- data/lib/grape/validations/validators/multiple_params_base.rb +26 -0
- data/lib/grape/validations/validators/mutual_exclusion.rb +25 -0
- data/lib/grape/validations/{presence.rb → validators/presence.rb} +2 -2
- data/lib/grape/validations/validators/regexp.rb +12 -0
- data/lib/grape/validations/validators/values.rb +26 -0
- data/lib/grape/version.rb +1 -1
- data/spec/grape/api_spec.rb +522 -343
- data/spec/grape/dsl/callbacks_spec.rb +4 -4
- data/spec/grape/dsl/configuration_spec.rb +48 -9
- data/spec/grape/dsl/helpers_spec.rb +6 -13
- data/spec/grape/dsl/inside_route_spec.rb +43 -4
- data/spec/grape/dsl/middleware_spec.rb +1 -10
- data/spec/grape/dsl/parameters_spec.rb +8 -1
- data/spec/grape/dsl/request_response_spec.rb +16 -22
- data/spec/grape/dsl/routing_spec.rb +21 -5
- data/spec/grape/dsl/settings_spec.rb +219 -0
- data/spec/grape/dsl/validations_spec.rb +8 -11
- data/spec/grape/endpoint_spec.rb +115 -86
- data/spec/grape/entity_spec.rb +33 -33
- data/spec/grape/exceptions/invalid_formatter_spec.rb +3 -5
- data/spec/grape/exceptions/invalid_versioner_option_spec.rb +4 -6
- data/spec/grape/exceptions/missing_mime_type_spec.rb +5 -6
- data/spec/grape/exceptions/missing_option_spec.rb +3 -5
- data/spec/grape/exceptions/unknown_options_spec.rb +3 -5
- data/spec/grape/exceptions/unknown_validator_spec.rb +3 -5
- data/spec/grape/exceptions/validation_errors_spec.rb +5 -5
- data/spec/grape/loading_spec.rb +44 -0
- data/spec/grape/middleware/auth/base_spec.rb +0 -4
- data/spec/grape/middleware/auth/dsl_spec.rb +2 -4
- data/spec/grape/middleware/auth/strategies_spec.rb +5 -6
- data/spec/grape/middleware/exception_spec.rb +8 -10
- data/spec/grape/middleware/formatter_spec.rb +13 -15
- data/spec/grape/middleware/versioner/accept_version_header_spec.rb +10 -10
- data/spec/grape/middleware/versioner/header_spec.rb +25 -25
- data/spec/grape/middleware/versioner/param_spec.rb +15 -17
- data/spec/grape/middleware/versioner/path_spec.rb +1 -2
- data/spec/grape/middleware/versioner_spec.rb +0 -1
- data/spec/grape/path_spec.rb +66 -45
- data/spec/grape/util/inheritable_setting_spec.rb +217 -0
- data/spec/grape/util/inheritable_values_spec.rb +63 -0
- data/spec/grape/util/stackable_values_spec.rb +115 -0
- data/spec/grape/util/strict_hash_configuration_spec.rb +38 -0
- data/spec/grape/validations/attributes_iterator_spec.rb +4 -0
- data/spec/grape/validations/params_scope_spec.rb +57 -0
- data/spec/grape/validations/validators/all_or_none_spec.rb +60 -0
- data/spec/grape/validations/validators/allow_blank_spec.rb +170 -0
- data/spec/grape/validations/{at_least_one_of_spec.rb → validators/at_least_one_of_spec.rb} +7 -3
- data/spec/grape/validations/{coerce_spec.rb → validators/coerce_spec.rb} +8 -11
- data/spec/grape/validations/{default_spec.rb → validators/default_spec.rb} +7 -9
- data/spec/grape/validations/{exactly_one_of_spec.rb → validators/exactly_one_of_spec.rb} +15 -11
- data/spec/grape/validations/{mutual_exclusion_spec.rb → validators/mutual_exclusion_spec.rb} +11 -9
- data/spec/grape/validations/{presence_spec.rb → validators/presence_spec.rb} +30 -30
- data/spec/grape/validations/{regexp_spec.rb → validators/regexp_spec.rb} +2 -4
- data/spec/grape/validations/{values_spec.rb → validators/values_spec.rb} +95 -23
- data/spec/grape/validations/{zh-CN.yml → validators/zh-CN.yml} +0 -0
- data/spec/grape/validations_spec.rb +335 -70
- data/spec/shared/versioning_examples.rb +7 -8
- data/spec/spec_helper.rb +2 -0
- data/spec/support/basic_auth_encode_helpers.rb +1 -1
- data/spec/support/content_type_helpers.rb +1 -1
- data/spec/support/versioned_helpers.rb +3 -3
- metadata +80 -33
- data/lib/grape/util/deep_merge.rb +0 -23
- data/lib/grape/util/hash_stack.rb +0 -120
- data/lib/grape/validations/at_least_one_of.rb +0 -25
- data/lib/grape/validations/exactly_one_of.rb +0 -26
- data/lib/grape/validations/mutual_exclusion.rb +0 -25
- data/lib/grape/validations/regexp.rb +0 -12
- data/lib/grape/validations/values.rb +0 -23
- data/spec/grape/util/hash_stack_spec.rb +0 -132
| @@ -3,7 +3,7 @@ module Grape | |
| 3 3 | 
             
              module Exceptions
         | 
| 4 4 | 
             
                class UnknownValidator < Base
         | 
| 5 5 | 
             
                  def initialize(validator_type)
         | 
| 6 | 
            -
                    super(message: compose_message( | 
| 6 | 
            +
                    super(message: compose_message('unknown_validator', validator_type: validator_type))
         | 
| 7 7 | 
             
                  end
         | 
| 8 8 | 
             
                end
         | 
| 9 9 | 
             
              end
         | 
| @@ -6,7 +6,7 @@ module Grape | |
| 6 6 | 
             
                  attr_accessor :params
         | 
| 7 7 |  | 
| 8 8 | 
             
                  def initialize(args = {})
         | 
| 9 | 
            -
                     | 
| 9 | 
            +
                    fail 'Params are missing:' unless args.key? :params
         | 
| 10 10 | 
             
                    @params = args[:params]
         | 
| 11 11 | 
             
                    args[:message] = translate_message(args[:message_key]) if args.key? :message_key
         | 
| 12 12 | 
             
                    super
         | 
| @@ -45,8 +45,8 @@ module Grape | |
| 45 45 |  | 
| 46 46 | 
             
                  def full_message(attributes, error)
         | 
| 47 47 | 
             
                    I18n.t(
         | 
| 48 | 
            -
                       | 
| 49 | 
            -
                      default:  | 
| 48 | 
            +
                      'grape.errors.format'.to_sym,
         | 
| 49 | 
            +
                      default: '%{attributes} %{message}',
         | 
| 50 50 | 
             
                      attributes: attributes.count == 1 ? translate_attribute(attributes.first) : translate_attributes(attributes),
         | 
| 51 51 | 
             
                      message: error.message
         | 
| 52 52 | 
             
                    )
         | 
| @@ -19,7 +19,7 @@ module Grape | |
| 19 19 | 
             
                      if object.respond_to? :serializable_hash
         | 
| 20 20 | 
             
                        object.serializable_hash
         | 
| 21 21 | 
             
                      elsif object.kind_of?(Array) && !object.map { |o| o.respond_to? :serializable_hash }.include?(false)
         | 
| 22 | 
            -
                        object.map | 
| 22 | 
            +
                        object.map(&:serializable_hash)
         | 
| 23 23 | 
             
                      elsif object.kind_of?(Hash)
         | 
| 24 24 | 
             
                        object.inject({}) do |h, (k, v)|
         | 
| 25 25 | 
             
                          h[k] = serialize(v)
         | 
    
        data/lib/grape/formatter/xml.rb
    CHANGED
    
    
    
        data/lib/grape/locale/en.yml
    CHANGED
    
    | @@ -6,6 +6,7 @@ en: | |
| 6 6 | 
             
                    coerce: 'is invalid'
         | 
| 7 7 | 
             
                    presence: 'is missing'
         | 
| 8 8 | 
             
                    regexp: 'is invalid'
         | 
| 9 | 
            +
                    blank: 'is empty'
         | 
| 9 10 | 
             
                    values: 'does not have a valid value'
         | 
| 10 11 | 
             
                    missing_vendor_option:
         | 
| 11 12 | 
             
                      problem: 'missing :vendor option.'
         | 
| @@ -31,4 +32,5 @@ en: | |
| 31 32 | 
             
                    mutual_exclusion: 'are mutually exclusive'
         | 
| 32 33 | 
             
                    at_least_one: 'are missing, at least one parameter must be provided'
         | 
| 33 34 | 
             
                    exactly_one: 'are missing, exactly one parameter must be provided'
         | 
| 35 | 
            +
                    all_or_none: 'provide all or none of parameters'
         | 
| 34 36 |  | 
| @@ -1,33 +1,38 @@ | |
| 1 1 | 
             
            require 'rack/auth/basic'
         | 
| 2 | 
            +
            require 'active_support/concern'
         | 
| 2 3 |  | 
| 3 4 | 
             
            module Grape
         | 
| 4 5 | 
             
              module Middleware
         | 
| 5 6 | 
             
                module Auth
         | 
| 6 7 | 
             
                  module DSL
         | 
| 7 | 
            -
                     | 
| 8 | 
            -
             | 
| 9 | 
            -
                     | 
| 10 | 
            -
                       | 
| 11 | 
            -
             | 
| 12 | 
            -
             | 
| 13 | 
            -
             | 
| 14 | 
            -
             | 
| 8 | 
            +
                    extend ActiveSupport::Concern
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                    module ClassMethods
         | 
| 11 | 
            +
                      # Add an authentication type to the API. Currently
         | 
| 12 | 
            +
                      # only `:http_basic`, `:http_digest` are supported.
         | 
| 13 | 
            +
                      def auth(type = nil, options = {}, &block)
         | 
| 14 | 
            +
                        if type
         | 
| 15 | 
            +
                          namespace_inheritable(:auth, { type: type.to_sym, proc: block }.merge(options))
         | 
| 16 | 
            +
                          use Grape::Middleware::Auth::Base, namespace_inheritable(:auth)
         | 
| 17 | 
            +
                        else
         | 
| 18 | 
            +
                          namespace_inheritable(:auth)
         | 
| 19 | 
            +
                        end
         | 
| 15 20 | 
             
                      end
         | 
| 16 | 
            -
                    end
         | 
| 17 21 |  | 
| 18 | 
            -
             | 
| 19 | 
            -
             | 
| 20 | 
            -
             | 
| 21 | 
            -
             | 
| 22 | 
            -
             | 
| 23 | 
            -
             | 
| 24 | 
            -
             | 
| 25 | 
            -
             | 
| 22 | 
            +
                      # Add HTTP Basic authorization to the API.
         | 
| 23 | 
            +
                      #
         | 
| 24 | 
            +
                      # @param [Hash] options A hash of options.
         | 
| 25 | 
            +
                      # @option options [String] :realm "API Authorization" The HTTP Basic realm.
         | 
| 26 | 
            +
                      def http_basic(options = {}, &block)
         | 
| 27 | 
            +
                        options[:realm] ||= 'API Authorization'
         | 
| 28 | 
            +
                        auth :http_basic, options, &block
         | 
| 29 | 
            +
                      end
         | 
| 26 30 |  | 
| 27 | 
            -
             | 
| 28 | 
            -
             | 
| 29 | 
            -
             | 
| 30 | 
            -
             | 
| 31 | 
            +
                      def http_digest(options = {}, &block)
         | 
| 32 | 
            +
                        options[:realm] ||= 'API Authorization'
         | 
| 33 | 
            +
                        options[:opaque] ||= 'secret'
         | 
| 34 | 
            +
                        auth :http_digest, options, &block
         | 
| 35 | 
            +
                      end
         | 
| 31 36 | 
             
                    end
         | 
| 32 37 | 
             
                  end
         | 
| 33 38 | 
             
                end
         | 
| @@ -10,7 +10,7 @@ module Grape | |
| 10 10 |  | 
| 11 11 | 
             
                    def auth_strategies
         | 
| 12 12 | 
             
                      @auth_strategies ||= {
         | 
| 13 | 
            -
                        http_basic: StrategyInfo.new(Rack::Auth::Basic, ->(settings) {[settings[:realm]] }),
         | 
| 13 | 
            +
                        http_basic: StrategyInfo.new(Rack::Auth::Basic, ->(settings) { [settings[:realm]] }),
         | 
| 14 14 | 
             
                        http_digest: StrategyInfo.new(Rack::Auth::Digest::MD5, ->(settings) { [settings[:realm], settings[:opaque]] })
         | 
| 15 15 | 
             
                      }
         | 
| 16 16 | 
             
                    end
         | 
| @@ -2,13 +2,11 @@ module Grape | |
| 2 2 | 
             
              module Middleware
         | 
| 3 3 | 
             
                module Auth
         | 
| 4 4 | 
             
                  StrategyInfo = Struct.new(:auth_class, :settings_fetcher) do
         | 
| 5 | 
            -
             | 
| 6 5 | 
             
                    def create(app, options, &block)
         | 
| 7 6 | 
             
                      strategy_args = settings_fetcher.call(options)
         | 
| 8 7 |  | 
| 9 8 | 
             
                      auth_class.new(app, *strategy_args, &block)
         | 
| 10 9 | 
             
                    end
         | 
| 11 | 
            -
             | 
| 12 10 | 
             
                  end
         | 
| 13 11 | 
             
                end
         | 
| 14 12 | 
             
              end
         | 
| @@ -53,9 +53,9 @@ module Grape | |
| 53 53 | 
             
                  end
         | 
| 54 54 |  | 
| 55 55 | 
             
                  def mime_types
         | 
| 56 | 
            -
                    content_types.each_with_object({})  | 
| 56 | 
            +
                    content_types.each_with_object({}) do |(k, v), types_without_params|
         | 
| 57 57 | 
             
                      types_without_params[k] = v.split(';').first
         | 
| 58 | 
            -
                     | 
| 58 | 
            +
                    end.invert
         | 
| 59 59 | 
             
                  end
         | 
| 60 60 | 
             
                end
         | 
| 61 61 | 
             
              end
         | 
| @@ -26,7 +26,7 @@ module Grape | |
| 26 26 | 
             
                  def after
         | 
| 27 27 | 
             
                    status, headers, bodies = *@app_response
         | 
| 28 28 | 
             
                    # allow content-type to be explicitly overwritten
         | 
| 29 | 
            -
                    api_format = mime_types[headers[ | 
| 29 | 
            +
                    api_format = mime_types[headers['Content-Type']] || env['api.format']
         | 
| 30 30 | 
             
                    formatter = Grape::Formatter::Base.formatter_for api_format, options
         | 
| 31 31 | 
             
                    begin
         | 
| 32 32 | 
             
                      bodymap = bodies.collect do |body|
         | 
| @@ -48,9 +48,9 @@ module Grape | |
| 48 48 | 
             
                  # store read input in env['api.request.input']
         | 
| 49 49 | 
             
                  def read_body_input
         | 
| 50 50 | 
             
                    if (request.post? || request.put? || request.patch? || request.delete?) &&
         | 
| 51 | 
            -
             | 
| 52 | 
            -
             | 
| 53 | 
            -
             | 
| 51 | 
            +
                       (!request.form_data? || !request.media_type) &&
         | 
| 52 | 
            +
                       (!request.parseable_data?) &&
         | 
| 53 | 
            +
                       (request.content_length.to_i > 0 || request.env['HTTP_TRANSFER_ENCODING'] == 'chunked')
         | 
| 54 54 |  | 
| 55 55 | 
             
                      if (input = env['rack.input'])
         | 
| 56 56 | 
             
                        input.rewind
         | 
| @@ -113,7 +113,7 @@ module Grape | |
| 113 113 | 
             
                  end
         | 
| 114 114 |  | 
| 115 115 | 
             
                  def format_from_params
         | 
| 116 | 
            -
                    fmt = Rack::Utils.parse_nested_query(env['QUERY_STRING'])[ | 
| 116 | 
            +
                    fmt = Rack::Utils.parse_nested_query(env['QUERY_STRING'])['format']
         | 
| 117 117 | 
             
                    # avoid symbol memory leak on an unknown format
         | 
| 118 118 | 
             
                    return fmt.to_sym if content_type_for(fmt)
         | 
| 119 119 | 
             
                    fmt
         | 
| @@ -52,9 +52,9 @@ module Grape | |
| 52 52 | 
             
                        env['api.subtype'] = subtype
         | 
| 53 53 |  | 
| 54 54 | 
             
                        if /\Avnd\.([a-z0-9*.]+)(?:-([a-z0-9*\-.]+))?(?:\+([a-z0-9*\-.+]+))?\z/ =~ subtype
         | 
| 55 | 
            -
                          env['api.vendor']  =  | 
| 56 | 
            -
                          env['api.version'] =  | 
| 57 | 
            -
                          env['api.format']  =  | 
| 55 | 
            +
                          env['api.vendor']  = Regexp.last_match[1]
         | 
| 56 | 
            +
                          env['api.version'] = Regexp.last_match[2]
         | 
| 57 | 
            +
                          env['api.format']  = Regexp.last_match[3]  # weird that Grape::Middleware::Formatter also does this
         | 
| 58 58 | 
             
                        end
         | 
| 59 59 | 
             
                      # If none of the available content types are acceptable:
         | 
| 60 60 | 
             
                      elsif strict?
         | 
| @@ -21,7 +21,7 @@ module Grape | |
| 21 21 | 
             
                  class Param < Base
         | 
| 22 22 | 
             
                    def default_options
         | 
| 23 23 | 
             
                      {
         | 
| 24 | 
            -
                        parameter:  | 
| 24 | 
            +
                        parameter: 'apiver'
         | 
| 25 25 | 
             
                      }
         | 
| 26 26 | 
             
                    end
         | 
| 27 27 |  | 
| @@ -30,7 +30,7 @@ module Grape | |
| 30 30 | 
             
                      potential_version = Rack::Utils.parse_nested_query(env['QUERY_STRING'])[paramkey]
         | 
| 31 31 | 
             
                      unless potential_version.nil?
         | 
| 32 32 | 
             
                        if options[:versions] && !options[:versions].find { |v| v.to_s == potential_version }
         | 
| 33 | 
            -
                          throw :error, status: 404, message:  | 
| 33 | 
            +
                          throw :error, status: 404, message: '404 API Version Not Found', headers: { 'X-Cascade' => 'pass' }
         | 
| 34 34 | 
             
                        end
         | 
| 35 35 | 
             
                        env['api.version'] = potential_version
         | 
| 36 36 | 
             
                        env['rack.request.query_hash'].delete(paramkey) if env.key? 'rack.request.query_hash'
         | 
| @@ -35,7 +35,7 @@ module Grape | |
| 35 35 | 
             
                      potential_version = pieces[1]
         | 
| 36 36 | 
             
                      if potential_version =~ options[:pattern]
         | 
| 37 37 | 
             
                        if options[:versions] && !options[:versions].find { |v| v.to_s == potential_version }
         | 
| 38 | 
            -
                          throw :error, status: 404, message:  | 
| 38 | 
            +
                          throw :error, status: 404, message: '404 API Version Not Found'
         | 
| 39 39 | 
             
                        end
         | 
| 40 40 | 
             
                        env['api.version'] = potential_version
         | 
| 41 41 | 
             
                      end
         | 
    
        data/lib/grape/namespace.rb
    CHANGED
    
    
    
        data/lib/grape/path.rb
    CHANGED
    
    | @@ -13,13 +13,17 @@ module Grape | |
| 13 13 | 
             
                end
         | 
| 14 14 |  | 
| 15 15 | 
             
                def mount_path
         | 
| 16 | 
            -
                   | 
| 16 | 
            +
                  settings[:mount_path]
         | 
| 17 17 | 
             
                end
         | 
| 18 18 |  | 
| 19 19 | 
             
                def root_prefix
         | 
| 20 20 | 
             
                  split_setting(:root_prefix, '/')
         | 
| 21 21 | 
             
                end
         | 
| 22 22 |  | 
| 23 | 
            +
                def uses_specific_format?
         | 
| 24 | 
            +
                  !!(settings[:format] && settings[:content_types].size == 1)
         | 
| 25 | 
            +
                end
         | 
| 26 | 
            +
             | 
| 23 27 | 
             
                def uses_path_versioning?
         | 
| 24 28 | 
             
                  !!(settings[:version] && settings[:version_options][:using] == :path)
         | 
| 25 29 | 
             
                end
         | 
| @@ -33,7 +37,9 @@ module Grape | |
| 33 37 | 
             
                end
         | 
| 34 38 |  | 
| 35 39 | 
             
                def suffix
         | 
| 36 | 
            -
                  if  | 
| 40 | 
            +
                  if uses_specific_format?
         | 
| 41 | 
            +
                    ''
         | 
| 42 | 
            +
                  elsif !uses_path_versioning? || (has_namespace? || has_path?)
         | 
| 37 43 | 
             
                    '(.:format)'
         | 
| 38 44 | 
             
                  else
         | 
| 39 45 | 
             
                    '(/.:format)'
         | 
| @@ -64,7 +70,7 @@ module Grape | |
| 64 70 |  | 
| 65 71 | 
             
                def split_setting(key, delimiter)
         | 
| 66 72 | 
             
                  return if settings[key].nil?
         | 
| 67 | 
            -
                  settings[key].to_s.split( | 
| 73 | 
            +
                  settings[key].to_s.split('/')
         | 
| 68 74 | 
             
                end
         | 
| 69 75 | 
             
              end
         | 
| 70 76 | 
             
            end
         | 
| @@ -2,17 +2,25 @@ module Grape | |
| 2 2 | 
             
              module ContentTypes
         | 
| 3 3 | 
             
                # Content types are listed in order of preference.
         | 
| 4 4 | 
             
                CONTENT_TYPES = ActiveSupport::OrderedHash[
         | 
| 5 | 
            -
             | 
| 6 | 
            -
             | 
| 7 | 
            -
             | 
| 8 | 
            -
             | 
| 9 | 
            -
             | 
| 10 | 
            -
                  :rss,  'application/rss+xml',
         | 
| 11 | 
            -
                  :txt,  'text/plain',
         | 
| 5 | 
            +
                                :xml,  'application/xml',
         | 
| 6 | 
            +
                                :serializable_hash, 'application/json',
         | 
| 7 | 
            +
                                :json, 'application/json',
         | 
| 8 | 
            +
                                :binary, 'application/octet-stream',
         | 
| 9 | 
            +
                                :txt,  'text/plain'
         | 
| 12 10 | 
             
               ]
         | 
| 13 11 |  | 
| 12 | 
            +
                def self.content_types_for_settings(settings)
         | 
| 13 | 
            +
                  return nil if settings.nil? || settings.blank?
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                  settings.each_with_object(ActiveSupport::OrderedHash.new) { |value, result| result.merge!(value) }
         | 
| 16 | 
            +
                end
         | 
| 17 | 
            +
             | 
| 14 18 | 
             
                def self.content_types_for(from_settings)
         | 
| 15 | 
            -
                  from_settings | 
| 19 | 
            +
                  if from_settings.present?
         | 
| 20 | 
            +
                    from_settings
         | 
| 21 | 
            +
                  else
         | 
| 22 | 
            +
                    Grape::ContentTypes::CONTENT_TYPES
         | 
| 23 | 
            +
                  end
         | 
| 16 24 | 
             
                end
         | 
| 17 25 | 
             
              end
         | 
| 18 26 | 
             
            end
         | 
| @@ -0,0 +1,74 @@ | |
| 1 | 
            +
            module Grape
         | 
| 2 | 
            +
              module Util
         | 
| 3 | 
            +
                class InheritableSetting
         | 
| 4 | 
            +
                  attr_accessor :route, :api_class, :namespace, :namespace_inheritable, :namespace_stackable
         | 
| 5 | 
            +
                  attr_accessor :parent, :point_in_time_copies
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                  def self.global
         | 
| 8 | 
            +
                    @global ||= {}
         | 
| 9 | 
            +
                  end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                  def self.reset_global! # only for testing
         | 
| 12 | 
            +
                    @global = {}
         | 
| 13 | 
            +
                  end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                  def initialize
         | 
| 16 | 
            +
                    self.route = {}
         | 
| 17 | 
            +
                    self.api_class = {}
         | 
| 18 | 
            +
                    self.namespace = InheritableValues.new # only inheritable from a parent when
         | 
| 19 | 
            +
                    # used with a mount, or should every API::Class be a seperate namespace by default?
         | 
| 20 | 
            +
                    self.namespace_inheritable = InheritableValues.new
         | 
| 21 | 
            +
                    self.namespace_stackable = StackableValues.new
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                    self.point_in_time_copies = []
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                    self.parent = nil
         | 
| 26 | 
            +
                  end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                  def global
         | 
| 29 | 
            +
                    self.class.global
         | 
| 30 | 
            +
                  end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                  def inherit_from(parent)
         | 
| 33 | 
            +
                    return if parent.nil?
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                    self.parent = parent
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                    namespace_inheritable.inherited_values = parent.namespace_inheritable
         | 
| 38 | 
            +
                    namespace_stackable.inherited_values = parent.namespace_stackable
         | 
| 39 | 
            +
                    self.route = parent.route.merge(route)
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                    point_in_time_copies.map { |cloned_one| cloned_one.inherit_from parent }
         | 
| 42 | 
            +
                  end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                  def point_in_time_copy
         | 
| 45 | 
            +
                    self.class.new.tap do |new_setting|
         | 
| 46 | 
            +
                      point_in_time_copies << new_setting
         | 
| 47 | 
            +
                      new_setting.point_in_time_copies = []
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                      new_setting.namespace = namespace.clone
         | 
| 50 | 
            +
                      new_setting.namespace_inheritable = namespace_inheritable.clone
         | 
| 51 | 
            +
                      new_setting.namespace_stackable = namespace_stackable.clone
         | 
| 52 | 
            +
                      new_setting.route = route.clone
         | 
| 53 | 
            +
                      new_setting.api_class = api_class
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                      new_setting.inherit_from(parent)
         | 
| 56 | 
            +
                    end
         | 
| 57 | 
            +
                  end
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                  def route_end
         | 
| 60 | 
            +
                    @route = {}
         | 
| 61 | 
            +
                  end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                  def to_hash
         | 
| 64 | 
            +
                    {
         | 
| 65 | 
            +
                      global: global.clone,
         | 
| 66 | 
            +
                      route: route.clone,
         | 
| 67 | 
            +
                      namespace: namespace.to_hash,
         | 
| 68 | 
            +
                      namespace_inheritable: namespace_inheritable.to_hash,
         | 
| 69 | 
            +
                      namespace_stackable: namespace_stackable.to_hash
         | 
| 70 | 
            +
                    }
         | 
| 71 | 
            +
                  end
         | 
| 72 | 
            +
                end
         | 
| 73 | 
            +
              end
         | 
| 74 | 
            +
            end
         | 
| @@ -0,0 +1,51 @@ | |
| 1 | 
            +
            module Grape
         | 
| 2 | 
            +
              module Util
         | 
| 3 | 
            +
                class InheritableValues
         | 
| 4 | 
            +
                  attr_accessor :inherited_values
         | 
| 5 | 
            +
                  attr_accessor :new_values
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                  def initialize(inherited_values = {})
         | 
| 8 | 
            +
                    self.inherited_values = inherited_values
         | 
| 9 | 
            +
                    self.new_values = {}
         | 
| 10 | 
            +
                  end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                  def [](name)
         | 
| 13 | 
            +
                    values[name]
         | 
| 14 | 
            +
                  end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                  def []=(name, value)
         | 
| 17 | 
            +
                    new_values[name] = value
         | 
| 18 | 
            +
                  end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                  def delete(key)
         | 
| 21 | 
            +
                    new_values.delete key
         | 
| 22 | 
            +
                  end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                  def merge(new_hash)
         | 
| 25 | 
            +
                    values.merge(new_hash)
         | 
| 26 | 
            +
                  end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                  def keys
         | 
| 29 | 
            +
                    (new_values.keys + inherited_values.keys).sort.uniq
         | 
| 30 | 
            +
                  end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                  def to_hash
         | 
| 33 | 
            +
                    values.clone
         | 
| 34 | 
            +
                  end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                  def initialize_copy(other)
         | 
| 37 | 
            +
                    super
         | 
| 38 | 
            +
                    self.inherited_values = other.inherited_values
         | 
| 39 | 
            +
                    self.new_values = other.new_values.deep_dup
         | 
| 40 | 
            +
                  end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                  attr_writer :new_values
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                  protected
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                  def values
         | 
| 47 | 
            +
                    @inherited_values.merge(@new_values)
         | 
| 48 | 
            +
                  end
         | 
| 49 | 
            +
                end
         | 
| 50 | 
            +
              end
         | 
| 51 | 
            +
            end
         |