grape 0.3.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of grape might be problematic. Click here for more details.

Files changed (101) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +8 -0
  3. data/.rubocop.yml +70 -0
  4. data/.travis.yml +7 -6
  5. data/CHANGELOG.md +134 -4
  6. data/CONTRIBUTING.md +118 -0
  7. data/Gemfile +5 -2
  8. data/README.md +551 -116
  9. data/RELEASING.md +105 -0
  10. data/Rakefile +29 -8
  11. data/UPGRADING.md +124 -0
  12. data/grape.gemspec +3 -3
  13. data/lib/grape/api.rb +207 -88
  14. data/lib/grape/cookies.rb +4 -8
  15. data/lib/grape/endpoint.rb +198 -144
  16. data/lib/grape/error_formatter/base.rb +5 -7
  17. data/lib/grape/error_formatter/json.rb +3 -5
  18. data/lib/grape/error_formatter/txt.rb +1 -3
  19. data/lib/grape/error_formatter/xml.rb +4 -6
  20. data/lib/grape/exceptions/base.rb +9 -9
  21. data/lib/grape/exceptions/incompatible_option_values.rb +10 -0
  22. data/lib/grape/exceptions/invalid_formatter.rb +1 -4
  23. data/lib/grape/exceptions/invalid_versioner_option.rb +1 -5
  24. data/lib/grape/exceptions/invalid_with_option_for_represent.rb +1 -6
  25. data/lib/grape/exceptions/missing_mime_type.rb +1 -5
  26. data/lib/grape/exceptions/missing_option.rb +1 -4
  27. data/lib/grape/exceptions/missing_vendor_option.rb +1 -4
  28. data/lib/grape/exceptions/unknown_options.rb +1 -5
  29. data/lib/grape/exceptions/unknown_validator.rb +1 -3
  30. data/lib/grape/exceptions/validation.rb +13 -3
  31. data/lib/grape/exceptions/validation_errors.rb +43 -0
  32. data/lib/grape/formatter/base.rb +5 -7
  33. data/lib/grape/formatter/json.rb +0 -3
  34. data/lib/grape/formatter/serializable_hash.rb +15 -15
  35. data/lib/grape/formatter/txt.rb +0 -2
  36. data/lib/grape/formatter/xml.rb +0 -2
  37. data/lib/grape/http/request.rb +26 -0
  38. data/lib/grape/locale/en.yml +8 -5
  39. data/lib/grape/middleware/auth/base.rb +30 -0
  40. data/lib/grape/middleware/auth/basic.rb +3 -20
  41. data/lib/grape/middleware/auth/digest.rb +2 -19
  42. data/lib/grape/middleware/auth/oauth2.rb +31 -24
  43. data/lib/grape/middleware/base.rb +7 -7
  44. data/lib/grape/middleware/error.rb +36 -22
  45. data/lib/grape/middleware/filter.rb +3 -3
  46. data/lib/grape/middleware/formatter.rb +99 -61
  47. data/lib/grape/middleware/globals.rb +13 -0
  48. data/lib/grape/middleware/versioner/accept_version_header.rb +67 -0
  49. data/lib/grape/middleware/versioner/header.rb +22 -16
  50. data/lib/grape/middleware/versioner/param.rb +9 -11
  51. data/lib/grape/middleware/versioner/path.rb +10 -13
  52. data/lib/grape/middleware/versioner.rb +3 -1
  53. data/lib/grape/namespace.rb +23 -0
  54. data/lib/grape/parser/base.rb +3 -5
  55. data/lib/grape/parser/json.rb +0 -2
  56. data/lib/grape/parser/xml.rb +0 -2
  57. data/lib/grape/path.rb +70 -0
  58. data/lib/grape/route.rb +10 -6
  59. data/lib/grape/util/content_types.rb +2 -1
  60. data/lib/grape/util/deep_merge.rb +5 -5
  61. data/lib/grape/util/hash_stack.rb +13 -2
  62. data/lib/grape/validations/coerce.rb +11 -10
  63. data/lib/grape/validations/default.rb +25 -0
  64. data/lib/grape/validations/presence.rb +7 -3
  65. data/lib/grape/validations/regexp.rb +2 -5
  66. data/lib/grape/validations/values.rb +17 -0
  67. data/lib/grape/validations.rb +161 -54
  68. data/lib/grape/version.rb +1 -1
  69. data/lib/grape.rb +19 -4
  70. data/spec/grape/api_spec.rb +897 -268
  71. data/spec/grape/endpoint_spec.rb +283 -66
  72. data/spec/grape/entity_spec.rb +132 -29
  73. data/spec/grape/exceptions/missing_mime_type_spec.rb +3 -9
  74. data/spec/grape/exceptions/validation_errors_spec.rb +19 -0
  75. data/spec/grape/middleware/auth/basic_spec.rb +8 -8
  76. data/spec/grape/middleware/auth/digest_spec.rb +5 -5
  77. data/spec/grape/middleware/auth/oauth2_spec.rb +81 -36
  78. data/spec/grape/middleware/base_spec.rb +8 -13
  79. data/spec/grape/middleware/error_spec.rb +13 -17
  80. data/spec/grape/middleware/exception_spec.rb +47 -27
  81. data/spec/grape/middleware/formatter_spec.rb +103 -41
  82. data/spec/grape/middleware/versioner/accept_version_header_spec.rb +121 -0
  83. data/spec/grape/middleware/versioner/header_spec.rb +76 -51
  84. data/spec/grape/middleware/versioner/param_spec.rb +18 -18
  85. data/spec/grape/middleware/versioner/path_spec.rb +6 -6
  86. data/spec/grape/middleware/versioner_spec.rb +5 -2
  87. data/spec/grape/path_spec.rb +229 -0
  88. data/spec/grape/util/hash_stack_spec.rb +31 -32
  89. data/spec/grape/validations/coerce_spec.rb +116 -51
  90. data/spec/grape/validations/default_spec.rb +123 -0
  91. data/spec/grape/validations/presence_spec.rb +42 -44
  92. data/spec/grape/validations/regexp_spec.rb +9 -9
  93. data/spec/grape/validations/values_spec.rb +138 -0
  94. data/spec/grape/validations/zh-CN.yml +4 -3
  95. data/spec/grape/validations_spec.rb +681 -48
  96. data/spec/shared/versioning_examples.rb +22 -6
  97. data/spec/spec_helper.rb +3 -2
  98. data/spec/support/basic_auth_encode_helpers.rb +0 -1
  99. data/spec/support/content_type_helpers.rb +11 -0
  100. data/spec/support/versioned_helpers.rb +13 -5
  101. metadata +34 -84
@@ -1,12 +1,11 @@
1
1
  module Grape
2
2
  module Parser
3
3
  module Base
4
-
5
4
  class << self
6
-
7
5
  PARSERS = {
8
- :json => Grape::Parser::Json,
9
- :xml => Grape::Parser::Xml
6
+ json: Grape::Parser::Json,
7
+ jsonapi: Grape::Parser::Json,
8
+ xml: Grape::Parser::Xml
10
9
  }
11
10
 
12
11
  def parsers(options)
@@ -24,7 +23,6 @@ module Grape
24
23
  spec
25
24
  end
26
25
  end
27
-
28
26
  end
29
27
  end
30
28
  end
@@ -2,11 +2,9 @@ module Grape
2
2
  module Parser
3
3
  module Json
4
4
  class << self
5
-
6
5
  def call(object, env)
7
6
  MultiJson.load(object)
8
7
  end
9
-
10
8
  end
11
9
  end
12
10
  end
@@ -2,11 +2,9 @@ module Grape
2
2
  module Parser
3
3
  module Xml
4
4
  class << self
5
-
6
5
  def call(object, env)
7
6
  MultiXml.parse(object)
8
7
  end
9
-
10
8
  end
11
9
  end
12
10
  end
data/lib/grape/path.rb ADDED
@@ -0,0 +1,70 @@
1
+ module Grape
2
+ class Path
3
+ def self.prepare(raw_path, namespace, settings)
4
+ Path.new(raw_path, namespace, settings).path_with_suffix
5
+ end
6
+
7
+ attr_reader :raw_path, :namespace, :settings
8
+
9
+ def initialize(raw_path, namespace, settings)
10
+ @raw_path = raw_path
11
+ @namespace = namespace
12
+ @settings = settings
13
+ end
14
+
15
+ def mount_path
16
+ split_setting(:mount_path, '/')
17
+ end
18
+
19
+ def root_prefix
20
+ split_setting(:root_prefix, '/')
21
+ end
22
+
23
+ def uses_path_versioning?
24
+ !!(settings[:version] && settings[:version_options][:using] == :path)
25
+ end
26
+
27
+ def has_namespace?
28
+ namespace && namespace.to_s =~ /^\S/ && namespace != '/'
29
+ end
30
+
31
+ def has_path?
32
+ raw_path && raw_path.to_s =~ /^\S/ && raw_path != '/'
33
+ end
34
+
35
+ def suffix
36
+ if !uses_path_versioning? || (has_namespace? || has_path?)
37
+ '(.:format)'
38
+ else
39
+ '(/.:format)'
40
+ end
41
+ end
42
+
43
+ def path
44
+ Rack::Mount::Utils.normalize_path(parts.join('/'))
45
+ end
46
+
47
+ def path_with_suffix
48
+ "#{path}#{suffix}"
49
+ end
50
+
51
+ def to_s
52
+ path_with_suffix
53
+ end
54
+
55
+ private
56
+
57
+ def parts
58
+ parts = [mount_path, root_prefix].compact
59
+ parts << ':version' if uses_path_versioning?
60
+ parts << namespace.to_s
61
+ parts << raw_path.to_s
62
+ parts.flatten.reject { |part| part == '/' }
63
+ end
64
+
65
+ def split_setting(key, delimiter)
66
+ return if settings[key].nil?
67
+ settings[key].to_s.split("/")
68
+ end
69
+ end
70
+ end
data/lib/grape/route.rb CHANGED
@@ -1,23 +1,27 @@
1
1
  module Grape
2
-
3
2
  # A compiled route for inspection.
4
3
  class Route
5
-
6
4
  def initialize(options = {})
7
5
  @options = options || {}
8
6
  end
9
-
7
+
10
8
  def method_missing(method_id, *arguments)
11
- if match = /route_([_a-zA-Z]\w*)/.match(method_id.to_s)
9
+ match = /route_([_a-zA-Z]\w*)/.match(method_id.to_s)
10
+ if match
12
11
  @options[match.captures.last.to_sym]
13
12
  else
14
13
  super
15
14
  end
16
15
  end
17
-
16
+
18
17
  def to_s
19
18
  "version=#{route_version}, method=#{route_method}, path=#{route_path}"
20
19
  end
21
-
20
+
21
+ private
22
+
23
+ def to_ary
24
+ nil
25
+ end
22
26
  end
23
27
  end
@@ -5,10 +5,11 @@ module Grape
5
5
  :xml, 'application/xml',
6
6
  :serializable_hash, 'application/json',
7
7
  :json, 'application/json',
8
+ :jsonapi, 'application/vnd.api+json',
8
9
  :atom, 'application/atom+xml',
9
10
  :rss, 'application/rss+xml',
10
11
  :txt, 'text/plain',
11
- ]
12
+ ]
12
13
 
13
14
  def self.content_types_for(from_settings)
14
15
  from_settings || Grape::ContentTypes::CONTENT_TYPES
@@ -3,18 +3,18 @@ class Hash
3
3
  # activesupport/lib/active_support/core_ext/hash/deep_merge.rb
4
4
  # Returns a new hash with +self+ and +other_hash+ merged recursively.
5
5
  #
6
- # h1 = {:x => {:y => [4,5,6]}, :z => [7,8,9]}
7
- # h2 = {:x => {:y => [7,8,9]}, :z => "xyz"}
6
+ # h1 = {x: {y: [4,5,6]}, z: [7,8,9]}
7
+ # h2 = {x: {y: [7,8,9]}, z: "xyz"}
8
8
  #
9
- # h1.deep_merge(h2) #=> { :x => {:y => [7, 8, 9]}, :z => "xyz" }
10
- # h2.deep_merge(h1) #=> { :x => {:y => [4, 5, 6]}, :z => [7, 8, 9] }
9
+ # h1.deep_merge(h2) #=> { x: {y: [7, 8, 9]}, z: "xyz" }
10
+ # h2.deep_merge(h1) #=> { x: {y: [4, 5, 6]}, z: [7, 8, 9] }
11
11
  def deep_merge(other_hash)
12
12
  dup.deep_merge!(other_hash)
13
13
  end
14
14
 
15
15
  # Same as +deep_merge+, but modifies +self+.
16
16
  def deep_merge!(other_hash)
17
- other_hash.each_pair do |k,v|
17
+ other_hash.each_pair do |k, v|
18
18
  tv = self[k]
19
19
  self[k] = tv.is_a?(Hash) && v.is_a?(Hash) ? tv.deep_merge(v) : v
20
20
  end
@@ -42,6 +42,17 @@ module Grape
42
42
  end
43
43
  alias_method :[], :get
44
44
 
45
+ # Looks through the stack for the first frame that matches :key
46
+ #
47
+ # @param key [Symbol] key to look for in hash frames
48
+ # @return true if key exists, false otherwise
49
+ def has_key?(key)
50
+ (@stack.length - 1).downto(0).each do |i|
51
+ return true if @stack[i].key? key
52
+ end
53
+ false
54
+ end
55
+
45
56
  # Replace a value on the top hash of the stack.
46
57
  #
47
58
  # @param key [Symbol] The key to set.
@@ -73,7 +84,7 @@ module Grape
73
84
 
74
85
  # Prepend another HashStack's to self
75
86
  def prepend(hash_stack)
76
- @stack.unshift *hash_stack.stack
87
+ @stack.unshift(*hash_stack.stack)
77
88
  self
78
89
  end
79
90
 
@@ -89,7 +100,7 @@ module Grape
89
100
  # @param key [Symbol] The key to gather
90
101
  # @return [Array]
91
102
  def gather(key)
92
- stack.map{|s| s[key] }.flatten.compact.uniq
103
+ stack.flat_map { |s| s[key] }.compact.uniq
93
104
  end
94
105
 
95
106
  def to_s
@@ -1,24 +1,23 @@
1
1
  module Grape
2
-
3
2
  class API
4
- Boolean = Virtus::Attribute::Boolean
3
+ Boolean = Virtus::Attribute::Boolean # rubocop:disable ConstantName
5
4
  end
6
5
 
7
6
  module Validations
8
-
9
7
  class CoerceValidator < SingleOptionValidator
10
8
  def validate_param!(attr_name, params)
9
+ raise Grape::Exceptions::Validation, param: @scope.full_name(attr_name), message_key: :coerce unless params.is_a? Hash
11
10
  new_value = coerce_value(@option, params[attr_name])
12
11
  if valid_type?(new_value)
13
12
  params[attr_name] = new_value
14
13
  else
15
- raise Grape::Exceptions::Validation, :status => 400,
16
- :param => @scope.full_name(attr_name), :message_key => :coerce
14
+ raise Grape::Exceptions::Validation, param: @scope.full_name(attr_name), message_key: :coerce
17
15
  end
18
16
  end
19
17
 
20
18
  class InvalidValue; end
21
- private
19
+
20
+ private
22
21
 
23
22
  def _valid_array_type?(type, values)
24
23
  values.all? do |val|
@@ -47,16 +46,18 @@ module Grape
47
46
  end
48
47
 
49
48
  def coerce_value(type, val)
50
- converter = Virtus::Attribute.build(:a, type)
49
+ # Don't coerce things other than nil to Arrays or Hashes
50
+ return val || [] if type == Array
51
+ return val || {} if type == Hash
52
+
53
+ converter = Virtus::Attribute.build(type)
51
54
  converter.coerce(val)
52
55
 
53
56
  # not the prettiest but some invalid coercion can currently trigger
54
- # errors in Virtus (see coerce_spec.rb)
57
+ # errors in Virtus (see coerce_spec.rb:75)
55
58
  rescue
56
59
  InvalidValue.new
57
60
  end
58
-
59
61
  end
60
-
61
62
  end
62
63
  end
@@ -0,0 +1,25 @@
1
+ module Grape
2
+ module Validations
3
+ class DefaultValidator < Validator
4
+ def initialize(attrs, options, required, scope)
5
+ @default = options
6
+ super
7
+ end
8
+
9
+ def validate_param!(attr_name, params)
10
+ params[attr_name] = @default.is_a?(Proc) ? @default.call : @default unless params.has_key?(attr_name)
11
+ end
12
+
13
+ def validate!(params)
14
+ attrs = AttributesIterator.new(self, @scope, params)
15
+ parent_element = @scope.element
16
+ attrs.each do |resource_params, attr_name|
17
+ if resource_params[attr_name].nil?
18
+ validate_param!(attr_name, resource_params)
19
+ params[parent_element] = resource_params if parent_element && params[parent_element].nil?
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -1,10 +1,14 @@
1
1
  module Grape
2
2
  module Validations
3
3
  class PresenceValidator < Validator
4
+ def validate!(params)
5
+ return unless @scope.should_validate?(params)
6
+ super
7
+ end
8
+
4
9
  def validate_param!(attr_name, params)
5
- unless params.has_key?(attr_name)
6
- raise Grape::Exceptions::Validation, :status => 400,
7
- :param => @scope.full_name(attr_name), :message_key => :presence
10
+ unless params.respond_to?(:has_key?) && params.has_key?(attr_name)
11
+ raise Grape::Exceptions::Validation, param: @scope.full_name(attr_name), message_key: :presence
8
12
  end
9
13
  end
10
14
  end
@@ -1,14 +1,11 @@
1
1
  module Grape
2
2
  module Validations
3
-
4
3
  class RegexpValidator < SingleOptionValidator
5
4
  def validate_param!(attr_name, params)
6
- if params[attr_name] && !( params[attr_name].to_s =~ @option )
7
- raise Grape::Exceptions::Validation, :status => 400,
8
- :param => @scope.full_name(attr_name), :message_key => :regexp
5
+ if params[attr_name] && !(params[attr_name].to_s =~ @option)
6
+ raise Grape::Exceptions::Validation, param: @scope.full_name(attr_name), message_key: :regexp
9
7
  end
10
8
  end
11
9
  end
12
-
13
10
  end
14
11
  end
@@ -0,0 +1,17 @@
1
+ module Grape
2
+ module Validations
3
+ class ValuesValidator < Validator
4
+ def initialize(attrs, options, required, scope)
5
+ @values = options
6
+ @required = required
7
+ super
8
+ end
9
+
10
+ def validate_param!(attr_name, params)
11
+ if (params[attr_name] || @required) && !(@values.is_a?(Proc) ? @values.call : @values).include?(params[attr_name])
12
+ raise Grape::Exceptions::Validation, param: @scope.full_name(attr_name), message_key: :values
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -1,7 +1,5 @@
1
1
  module Grape
2
-
3
2
  module Validations
4
-
5
3
  ##
6
4
  # All validators must inherit from this class.
7
5
  #
@@ -19,25 +17,40 @@ module Grape
19
17
  end
20
18
 
21
19
  def validate!(params)
22
- params = @scope.params(params)
20
+ attributes = AttributesIterator.new(self, @scope, params)
21
+ attributes.each do |resource_params, attr_name|
22
+ if @required || resource_params.has_key?(attr_name)
23
+ validate_param!(attr_name, resource_params)
24
+ end
25
+ end
26
+ end
27
+
28
+ class AttributesIterator
29
+ include Enumerable
30
+
31
+ def initialize(validator, scope, params)
32
+ @attrs = validator.attrs
33
+ @params = scope.params(params)
34
+ @params = (@params.is_a?(Array) ? @params : [@params])
35
+ end
23
36
 
24
- (params.is_a?(Array) ? params : [params]).each do |resource_params|
25
- @attrs.each do |attr_name|
26
- if @required || resource_params.has_key?(attr_name)
27
- validate_param!(attr_name, resource_params)
37
+ def each
38
+ @params.each do |resource_params|
39
+ @attrs.each do |attr_name|
40
+ yield resource_params, attr_name
28
41
  end
29
42
  end
30
43
  end
31
44
  end
32
45
 
33
- private
46
+ private
34
47
 
35
48
  def self.convert_to_short_name(klass)
36
- ret = klass.name.gsub(/::/, '/').
37
- gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
38
- gsub(/([a-z\d])([A-Z])/,'\1_\2').
39
- tr("-", "_").
40
- downcase
49
+ ret = klass.name.gsub(/::/, '/')
50
+ .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
51
+ .gsub(/([a-z\d])([A-Z])/, '\1_\2')
52
+ .tr("-", "_")
53
+ .downcase
41
54
  File.basename(ret, '_validator')
42
55
  end
43
56
  end
@@ -49,7 +62,6 @@ module Grape
49
62
  @option = options
50
63
  super
51
64
  end
52
-
53
65
  end
54
66
 
55
67
  # We define Validator::inherited here so SingleOptionValidator
@@ -57,7 +69,7 @@ module Grape
57
69
  class Validator
58
70
  def self.inherited(klass)
59
71
  short_name = convert_to_short_name(klass)
60
- Validations::register_validator(short_name, klass)
72
+ Validations.register_validator(short_name, klass)
61
73
  end
62
74
  end
63
75
 
@@ -74,71 +86,173 @@ module Grape
74
86
  class ParamsScope
75
87
  attr_accessor :element, :parent
76
88
 
77
- def initialize(api, element, parent, &block)
78
- @element = element
79
- @parent = parent
80
- @api = api
89
+ def initialize(opts, &block)
90
+ @element = opts[:element]
91
+ @parent = opts[:parent]
92
+ @api = opts[:api]
93
+ @optional = opts[:optional] || false
94
+ @type = opts[:type]
95
+ @declared_params = []
96
+
81
97
  instance_eval(&block)
82
- end
83
98
 
84
- def requires(*attrs)
85
- validations = {:presence => true}
86
- if attrs.last.is_a?(Hash)
87
- validations.merge!(attrs.pop)
88
- end
99
+ configure_declared_params
100
+ end
89
101
 
90
- push_declared_params(attrs)
91
- validates(attrs, validations)
102
+ def should_validate?(parameters)
103
+ return false if @optional && params(parameters).respond_to?(:all?) && params(parameters).all?(&:blank?)
104
+ return true if parent.nil?
105
+ parent.should_validate?(parameters)
92
106
  end
93
107
 
94
- def optional(*attrs)
95
- validations = {}
96
- if attrs.last.is_a?(Hash)
97
- validations.merge!(attrs.pop)
108
+ def requires(*attrs, &block)
109
+ orig_attrs = attrs.clone
110
+
111
+ opts = attrs.last.is_a?(Hash) ? attrs.pop : nil
112
+
113
+ if opts && opts[:using]
114
+ require_required_and_optional_fields(attrs.first, opts)
115
+ else
116
+ validate_attributes(attrs, opts, &block)
117
+
118
+ block_given? ? new_scope(orig_attrs, &block) :
119
+ push_declared_params(attrs)
98
120
  end
121
+ end
122
+
123
+ def optional(*attrs, &block)
124
+ orig_attrs = attrs
99
125
 
100
- push_declared_params(attrs)
126
+ validations = {}
127
+ validations.merge!(attrs.pop) if attrs.last.is_a?(Hash)
128
+ validations[:type] ||= Array if block_given?
101
129
  validates(attrs, validations)
130
+
131
+ block_given? ? new_scope(orig_attrs, true, &block) :
132
+ push_declared_params(attrs)
102
133
  end
103
134
 
104
- def group(element, &block)
105
- ParamsScope.new(@api, element, self, &block)
135
+ def group(*attrs, &block)
136
+ requires(*attrs, &block)
106
137
  end
107
138
 
108
139
  def params(params)
109
140
  params = @parent.params(params) if @parent
110
- params = params[@element] || {} if @element
141
+ if @element
142
+ if params.is_a?(Array)
143
+ params = params.flat_map { |el| el[@element] || {} }
144
+ elsif params.is_a?(Hash)
145
+ params = params[@element] || {}
146
+ else
147
+ params = {}
148
+ end
149
+ end
111
150
  params
112
151
  end
113
152
 
153
+ def use(*names)
154
+ named_params = @api.settings[:named_params] || {}
155
+ names.each do |name|
156
+ params_block = named_params.fetch(name) do
157
+ raise "Params :#{name} not found!"
158
+ end
159
+ instance_eval(&params_block)
160
+ end
161
+ end
162
+ alias_method :use_scope, :use
163
+ alias_method :includes, :use
164
+
114
165
  def full_name(name)
115
166
  return "#{@parent.full_name(@element)}[#{name}]" if @parent
116
167
  name.to_s
117
168
  end
118
169
 
119
- private
170
+ protected
171
+
172
+ def push_declared_params(attrs)
173
+ @declared_params.concat attrs
174
+ end
175
+
176
+ private
177
+
178
+ def require_required_and_optional_fields(context, opts)
179
+ if context == :all
180
+ optional_fields = Array(opts[:except])
181
+ required_fields = opts[:using].keys - optional_fields
182
+ else # context == :none
183
+ required_fields = Array(opts[:except])
184
+ optional_fields = opts[:using].keys - required_fields
185
+ end
186
+ required_fields.each do |field|
187
+ requires(field, opts[:using][field])
188
+ end
189
+ optional_fields.each do |field|
190
+ optional(field, opts[:using][field])
191
+ end
192
+ end
193
+
194
+ def validate_attributes(attrs, opts, &block)
195
+ validations = { presence: true }
196
+ validations.merge!(opts) if opts
197
+ validations[:type] ||= Array if block
198
+ validates(attrs, validations)
199
+ end
200
+
201
+ def new_scope(attrs, optional = false, &block)
202
+ opts = attrs[1] || { type: Array }
203
+ raise ArgumentError unless opts.keys.to_set.subset? [:type].to_set
204
+ ParamsScope.new(api: @api, element: attrs.first, parent: self, optional: optional, type: opts[:type], &block)
205
+ end
206
+
207
+ # Pushes declared params to parent or settings
208
+ def configure_declared_params
209
+ if @parent
210
+ @parent.push_declared_params [element => @declared_params]
211
+ else
212
+ @api.settings.peek[:declared_params] ||= []
213
+ @api.settings[:declared_params].concat @declared_params
214
+ end
215
+ end
216
+
120
217
  def validates(attrs, validations)
121
- doc_attrs = { :required => validations.keys.include?(:presence) }
218
+ doc_attrs = { required: validations.keys.include?(:presence) }
122
219
 
123
220
  # special case (type = coerce)
124
- if validations[:type]
125
- validations[:coerce] = validations.delete(:type)
126
- end
221
+ validations[:coerce] = validations.delete(:type) if validations.key?(:type)
222
+
223
+ coerce_type = validations[:coerce]
224
+ doc_attrs[:type] = coerce_type.to_s if coerce_type
225
+
226
+ desc = validations.delete(:desc)
227
+ doc_attrs[:desc] = desc if desc
228
+
229
+ default = validations[:default]
230
+ doc_attrs[:default] = default if default
231
+
232
+ values = validations[:values]
233
+ doc_attrs[:values] = values if values
127
234
 
128
- if coerce_type = validations[:coerce]
129
- doc_attrs[:type] = coerce_type.to_s
235
+ values = (values.is_a?(Proc) ? values.call : values)
236
+
237
+ # default value should be present in values array, if both exist
238
+ if default && values && !values.include?(default)
239
+ raise Grape::Exceptions::IncompatibleOptionValues.new(:default, default, :values, values)
130
240
  end
131
241
 
132
- if desc = validations.delete(:desc)
133
- doc_attrs[:desc] = desc
242
+ # type should be compatible with values array, if both exist
243
+ if coerce_type && values && values.any? { |v| !v.kind_of?(coerce_type) }
244
+ raise Grape::Exceptions::IncompatibleOptionValues.new(:type, coerce_type, :values, values)
134
245
  end
135
246
 
136
- full_attrs = attrs.collect{ |name| { :name => name, :full_name => full_name(name)} }
247
+ doc_attrs[:documentation] = validations.delete(:documentation) if validations.key?(:documentation)
248
+
249
+ full_attrs = attrs.collect { |name| { name: name, full_name: full_name(name) } }
137
250
  @api.document_attribute(full_attrs, doc_attrs)
138
251
 
139
252
  # Validate for presence before any other validators
140
253
  if validations.has_key?(:presence) && validations[:presence]
141
254
  validate('presence', validations[:presence], attrs, doc_attrs)
255
+ validations.delete(:presence)
142
256
  end
143
257
 
144
258
  # Before we run the rest of the validators, lets handle
@@ -155,7 +269,7 @@ module Grape
155
269
  end
156
270
 
157
271
  def validate(type, options, attrs, doc_attrs)
158
- validator_class = Validations::validators[type.to_s]
272
+ validator_class = Validations.validators[type.to_s]
159
273
 
160
274
  if validator_class
161
275
  (@api.settings.peek[:validations] ||= []) << validator_class.new(attrs, options, doc_attrs[:required], self)
@@ -163,11 +277,6 @@ module Grape
163
277
  raise Grape::Exceptions::UnknownValidator.new(type)
164
278
  end
165
279
  end
166
-
167
- def push_declared_params(attrs)
168
- @api.settings.peek[:declared_params] ||= []
169
- @api.settings[:declared_params] += attrs
170
- end
171
280
  end
172
281
 
173
282
  # This module is mixed into the API Class.
@@ -178,7 +287,7 @@ module Grape
178
287
  end
179
288
 
180
289
  def params(&block)
181
- ParamsScope.new(self, nil, nil, &block)
290
+ ParamsScope.new(api: self, type: Hash, &block)
182
291
  end
183
292
 
184
293
  def document_attribute(names, opts)
@@ -189,9 +298,7 @@ module Grape
189
298
  @last_description[:params][name[:full_name].to_s].merge!(opts)
190
299
  end
191
300
  end
192
-
193
301
  end
194
-
195
302
  end
196
303
  end
197
304
 
data/lib/grape/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Grape
2
- VERSION = '0.3.0'
2
+ VERSION = '0.7.0'
3
3
  end