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.

Files changed (144) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -66
  3. data/.rubocop_todo.yml +78 -17
  4. data/.travis.yml +7 -3
  5. data/Appraisals +7 -0
  6. data/CHANGELOG.md +24 -0
  7. data/CONTRIBUTING.md +7 -0
  8. data/Gemfile +1 -7
  9. data/Guardfile +1 -1
  10. data/README.md +560 -94
  11. data/RELEASING.md +1 -1
  12. data/Rakefile +10 -11
  13. data/UPGRADING.md +211 -3
  14. data/gemfiles/rails_3.gemfile +14 -0
  15. data/gemfiles/rails_4.gemfile +14 -0
  16. data/grape.gemspec +10 -9
  17. data/lib/backports/active_support/deep_dup.rb +49 -0
  18. data/lib/backports/active_support/duplicable.rb +88 -0
  19. data/lib/grape.rb +29 -2
  20. data/lib/grape/api.rb +59 -65
  21. data/lib/grape/dsl/api.rb +19 -0
  22. data/lib/grape/dsl/callbacks.rb +6 -4
  23. data/lib/grape/dsl/configuration.rb +49 -5
  24. data/lib/grape/dsl/helpers.rb +7 -8
  25. data/lib/grape/dsl/inside_route.rb +22 -10
  26. data/lib/grape/dsl/middleware.rb +5 -5
  27. data/lib/grape/dsl/parameters.rb +6 -2
  28. data/lib/grape/dsl/request_response.rb +23 -20
  29. data/lib/grape/dsl/routing.rb +52 -49
  30. data/lib/grape/dsl/settings.rb +110 -0
  31. data/lib/grape/dsl/validations.rb +14 -6
  32. data/lib/grape/endpoint.rb +104 -88
  33. data/lib/grape/exceptions/base.rb +2 -2
  34. data/lib/grape/exceptions/incompatible_option_values.rb +1 -1
  35. data/lib/grape/exceptions/invalid_formatter.rb +1 -1
  36. data/lib/grape/exceptions/invalid_versioner_option.rb +1 -1
  37. data/lib/grape/exceptions/invalid_with_option_for_represent.rb +1 -1
  38. data/lib/grape/exceptions/missing_mime_type.rb +1 -1
  39. data/lib/grape/exceptions/missing_option.rb +1 -1
  40. data/lib/grape/exceptions/missing_vendor_option.rb +1 -1
  41. data/lib/grape/exceptions/unknown_options.rb +1 -1
  42. data/lib/grape/exceptions/unknown_validator.rb +1 -1
  43. data/lib/grape/exceptions/validation.rb +1 -1
  44. data/lib/grape/exceptions/validation_errors.rb +2 -2
  45. data/lib/grape/formatter/serializable_hash.rb +1 -1
  46. data/lib/grape/formatter/xml.rb +1 -1
  47. data/lib/grape/locale/en.yml +2 -0
  48. data/lib/grape/middleware/auth/dsl.rb +26 -21
  49. data/lib/grape/middleware/auth/strategies.rb +1 -1
  50. data/lib/grape/middleware/auth/strategy_info.rb +0 -2
  51. data/lib/grape/middleware/base.rb +2 -2
  52. data/lib/grape/middleware/error.rb +1 -1
  53. data/lib/grape/middleware/formatter.rb +5 -5
  54. data/lib/grape/middleware/versioner.rb +1 -1
  55. data/lib/grape/middleware/versioner/header.rb +3 -3
  56. data/lib/grape/middleware/versioner/param.rb +2 -2
  57. data/lib/grape/middleware/versioner/path.rb +1 -1
  58. data/lib/grape/namespace.rb +1 -1
  59. data/lib/grape/path.rb +9 -3
  60. data/lib/grape/util/content_types.rb +16 -8
  61. data/lib/grape/util/inheritable_setting.rb +74 -0
  62. data/lib/grape/util/inheritable_values.rb +51 -0
  63. data/lib/grape/util/stackable_values.rb +52 -0
  64. data/lib/grape/util/strict_hash_configuration.rb +106 -0
  65. data/lib/grape/validations.rb +0 -220
  66. data/lib/grape/validations/attributes_iterator.rb +21 -0
  67. data/lib/grape/validations/params_scope.rb +176 -0
  68. data/lib/grape/validations/validators/all_or_none.rb +20 -0
  69. data/lib/grape/validations/validators/allow_blank.rb +30 -0
  70. data/lib/grape/validations/validators/at_least_one_of.rb +20 -0
  71. data/lib/grape/validations/validators/base.rb +37 -0
  72. data/lib/grape/validations/{coerce.rb → validators/coerce.rb} +3 -3
  73. data/lib/grape/validations/{default.rb → validators/default.rb} +1 -1
  74. data/lib/grape/validations/validators/exactly_one_of.rb +20 -0
  75. data/lib/grape/validations/validators/multiple_params_base.rb +26 -0
  76. data/lib/grape/validations/validators/mutual_exclusion.rb +25 -0
  77. data/lib/grape/validations/{presence.rb → validators/presence.rb} +2 -2
  78. data/lib/grape/validations/validators/regexp.rb +12 -0
  79. data/lib/grape/validations/validators/values.rb +26 -0
  80. data/lib/grape/version.rb +1 -1
  81. data/spec/grape/api_spec.rb +522 -343
  82. data/spec/grape/dsl/callbacks_spec.rb +4 -4
  83. data/spec/grape/dsl/configuration_spec.rb +48 -9
  84. data/spec/grape/dsl/helpers_spec.rb +6 -13
  85. data/spec/grape/dsl/inside_route_spec.rb +43 -4
  86. data/spec/grape/dsl/middleware_spec.rb +1 -10
  87. data/spec/grape/dsl/parameters_spec.rb +8 -1
  88. data/spec/grape/dsl/request_response_spec.rb +16 -22
  89. data/spec/grape/dsl/routing_spec.rb +21 -5
  90. data/spec/grape/dsl/settings_spec.rb +219 -0
  91. data/spec/grape/dsl/validations_spec.rb +8 -11
  92. data/spec/grape/endpoint_spec.rb +115 -86
  93. data/spec/grape/entity_spec.rb +33 -33
  94. data/spec/grape/exceptions/invalid_formatter_spec.rb +3 -5
  95. data/spec/grape/exceptions/invalid_versioner_option_spec.rb +4 -6
  96. data/spec/grape/exceptions/missing_mime_type_spec.rb +5 -6
  97. data/spec/grape/exceptions/missing_option_spec.rb +3 -5
  98. data/spec/grape/exceptions/unknown_options_spec.rb +3 -5
  99. data/spec/grape/exceptions/unknown_validator_spec.rb +3 -5
  100. data/spec/grape/exceptions/validation_errors_spec.rb +5 -5
  101. data/spec/grape/loading_spec.rb +44 -0
  102. data/spec/grape/middleware/auth/base_spec.rb +0 -4
  103. data/spec/grape/middleware/auth/dsl_spec.rb +2 -4
  104. data/spec/grape/middleware/auth/strategies_spec.rb +5 -6
  105. data/spec/grape/middleware/exception_spec.rb +8 -10
  106. data/spec/grape/middleware/formatter_spec.rb +13 -15
  107. data/spec/grape/middleware/versioner/accept_version_header_spec.rb +10 -10
  108. data/spec/grape/middleware/versioner/header_spec.rb +25 -25
  109. data/spec/grape/middleware/versioner/param_spec.rb +15 -17
  110. data/spec/grape/middleware/versioner/path_spec.rb +1 -2
  111. data/spec/grape/middleware/versioner_spec.rb +0 -1
  112. data/spec/grape/path_spec.rb +66 -45
  113. data/spec/grape/util/inheritable_setting_spec.rb +217 -0
  114. data/spec/grape/util/inheritable_values_spec.rb +63 -0
  115. data/spec/grape/util/stackable_values_spec.rb +115 -0
  116. data/spec/grape/util/strict_hash_configuration_spec.rb +38 -0
  117. data/spec/grape/validations/attributes_iterator_spec.rb +4 -0
  118. data/spec/grape/validations/params_scope_spec.rb +57 -0
  119. data/spec/grape/validations/validators/all_or_none_spec.rb +60 -0
  120. data/spec/grape/validations/validators/allow_blank_spec.rb +170 -0
  121. data/spec/grape/validations/{at_least_one_of_spec.rb → validators/at_least_one_of_spec.rb} +7 -3
  122. data/spec/grape/validations/{coerce_spec.rb → validators/coerce_spec.rb} +8 -11
  123. data/spec/grape/validations/{default_spec.rb → validators/default_spec.rb} +7 -9
  124. data/spec/grape/validations/{exactly_one_of_spec.rb → validators/exactly_one_of_spec.rb} +15 -11
  125. data/spec/grape/validations/{mutual_exclusion_spec.rb → validators/mutual_exclusion_spec.rb} +11 -9
  126. data/spec/grape/validations/{presence_spec.rb → validators/presence_spec.rb} +30 -30
  127. data/spec/grape/validations/{regexp_spec.rb → validators/regexp_spec.rb} +2 -4
  128. data/spec/grape/validations/{values_spec.rb → validators/values_spec.rb} +95 -23
  129. data/spec/grape/validations/{zh-CN.yml → validators/zh-CN.yml} +0 -0
  130. data/spec/grape/validations_spec.rb +335 -70
  131. data/spec/shared/versioning_examples.rb +7 -8
  132. data/spec/spec_helper.rb +2 -0
  133. data/spec/support/basic_auth_encode_helpers.rb +1 -1
  134. data/spec/support/content_type_helpers.rb +1 -1
  135. data/spec/support/versioned_helpers.rb +3 -3
  136. metadata +80 -33
  137. data/lib/grape/util/deep_merge.rb +0 -23
  138. data/lib/grape/util/hash_stack.rb +0 -120
  139. data/lib/grape/validations/at_least_one_of.rb +0 -25
  140. data/lib/grape/validations/exactly_one_of.rb +0 -26
  141. data/lib/grape/validations/mutual_exclusion.rb +0 -25
  142. data/lib/grape/validations/regexp.rb +0 -12
  143. data/lib/grape/validations/values.rb +0 -23
  144. 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("unknown_validator", validator_type: validator_type))
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
- raise "Params are missing:" unless args.key? :params
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
- "grape.errors.format".to_sym,
49
- default: "%{attributes} %{message}",
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 { |o| o.serializable_hash }
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)
@@ -4,7 +4,7 @@ module Grape
4
4
  class << self
5
5
  def call(object, env)
6
6
  return object.to_xml if object.respond_to?(:to_xml)
7
- raise Grape::Exceptions::InvalidFormatter.new(object.class, 'xml')
7
+ fail Grape::Exceptions::InvalidFormatter.new(object.class, 'xml')
8
8
  end
9
9
  end
10
10
  end
@@ -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
- # Add an authentication type to the API. Currently
8
- # only `:http_basic`, `:http_digest` are supported.
9
- def auth(type = nil, options = {}, &block)
10
- if type
11
- set(:auth, { type: type.to_sym, proc: block }.merge(options))
12
- use Grape::Middleware::Auth::Base, settings[:auth]
13
- else
14
- settings[:auth]
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
- # Add HTTP Basic authorization to the API.
19
- #
20
- # @param [Hash] options A hash of options.
21
- # @option options [String] :realm "API Authorization" The HTTP Basic realm.
22
- def http_basic(options = {}, &block)
23
- options[:realm] ||= "API Authorization"
24
- auth :http_basic, options, &block
25
- end
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
- def http_digest(options = {}, &block)
28
- options[:realm] ||= "API Authorization"
29
- options[:opaque] ||= "secret"
30
- auth :http_digest, options, &block
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({}) { |(k, v), types_without_params|
56
+ content_types.each_with_object({}) do |(k, v), types_without_params|
57
57
  types_without_params[k] = v.split(';').first
58
- }.invert
58
+ end.invert
59
59
  end
60
60
  end
61
61
  end
@@ -6,7 +6,7 @@ module Grape
6
6
  def default_options
7
7
  {
8
8
  default_status: 500, # default status returned on error
9
- default_message: "",
9
+ default_message: '',
10
10
  format: :txt,
11
11
  formatters: {},
12
12
  error_formatters: {},
@@ -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["Content-Type"]] || env['api.format']
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
- (!request.form_data? || !request.media_type) &&
52
- (!request.parseable_data?) &&
53
- (request.content_length.to_i > 0 || request.env['HTTP_TRANSFER_ENCODING'] == 'chunked')
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'])["format"]
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
@@ -24,7 +24,7 @@ module Grape
24
24
  when :accept_version_header
25
25
  AcceptVersionHeader
26
26
  else
27
- raise Grape::Exceptions::InvalidVersionerOption.new(strategy)
27
+ fail Grape::Exceptions::InvalidVersionerOption.new(strategy)
28
28
  end
29
29
  end
30
30
  end
@@ -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'] = $1
56
- env['api.version'] = $2
57
- env['api.format'] = $3 # weird that Grape::Middleware::Formatter also does this
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: "apiver"
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: "404 API Version Not Found", headers: { 'X-Cascade' => 'pass' }
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: "404 API Version Not Found"
38
+ throw :error, status: 404, message: '404 API Version Not Found'
39
39
  end
40
40
  env['api.version'] = potential_version
41
41
  end
@@ -13,7 +13,7 @@ module Grape
13
13
  end
14
14
 
15
15
  def self.joined_space(settings)
16
- settings.gather(:namespace).map(&:space).join("/")
16
+ (settings || []).map(&:space).join('/')
17
17
  end
18
18
 
19
19
  def self.joined_space_path(settings)
data/lib/grape/path.rb CHANGED
@@ -13,13 +13,17 @@ module Grape
13
13
  end
14
14
 
15
15
  def mount_path
16
- split_setting(:mount_path, '/')
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 !uses_path_versioning? || (has_namespace? || has_path?)
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
- :xml, 'application/xml',
6
- :serializable_hash, 'application/json',
7
- :json, 'application/json',
8
- :jsonapi, 'application/vnd.api+json',
9
- :atom, 'application/atom+xml',
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 || Grape::ContentTypes::CONTENT_TYPES
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