grape 0.3.0 → 0.7.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 (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
@@ -14,10 +14,10 @@ describe Grape::Validations::PresenceValidator do
14
14
  end
15
15
 
16
16
  params do
17
- requires :id, :regexp => /^[0-9]+$/
17
+ requires :id, regexp: /^[0-9]+$/
18
18
  end
19
19
  post do
20
- {:ret => params[:id]}
20
+ { ret: params[:id] }
21
21
  end
22
22
 
23
23
  params do
@@ -28,8 +28,9 @@ describe Grape::Validations::PresenceValidator do
28
28
  end
29
29
 
30
30
  params do
31
- group :user do
32
- requires :first_name, :last_name
31
+ requires :user, type: Hash do
32
+ requires :first_name
33
+ requires :last_name
33
34
  end
34
35
  end
35
36
  get '/nested' do
@@ -37,11 +38,12 @@ describe Grape::Validations::PresenceValidator do
37
38
  end
38
39
 
39
40
  params do
40
- group :admin do
41
+ requires :admin, type: Hash do
41
42
  requires :admin_name
42
- group :super do
43
- group :user do
44
- requires :first_name, :last_name
43
+ requires :super, type: Hash do
44
+ requires :user, type: Hash do
45
+ requires :first_name
46
+ requires :last_name
45
47
  end
46
48
  end
47
49
  end
@@ -58,87 +60,83 @@ describe Grape::Validations::PresenceValidator do
58
60
  end
59
61
 
60
62
  it 'does not validate for any params' do
61
- get("/bacons")
63
+ get "/bacons"
62
64
  last_response.status.should == 200
63
- last_response.body.should == "All the bacon"
65
+ last_response.body.should == "All the bacon".to_json
64
66
  end
65
67
 
66
68
  it 'validates id' do
67
69
  post '/'
68
70
  last_response.status.should == 400
69
- last_response.body.should == '{"error":"missing parameter: id"}'
71
+ last_response.body.should == '{"error":"id is missing"}'
70
72
 
71
73
  io = StringIO.new('{"id" : "a56b"}')
72
- post '/', {}, 'rack.input' => io,
73
- 'CONTENT_TYPE' => 'application/json',
74
- 'CONTENT_LENGTH' => io.length
75
- last_response.body.should == '{"error":"invalid parameter: id"}'
74
+ post '/', {}, 'rack.input' => io, 'CONTENT_TYPE' => 'application/json', 'CONTENT_LENGTH' => io.length
75
+ last_response.body.should == '{"error":"id is invalid"}'
76
76
  last_response.status.should == 400
77
77
 
78
78
  io = StringIO.new('{"id" : 56}')
79
- post '/', {}, 'rack.input' => io,
80
- 'CONTENT_TYPE' => 'application/json',
81
- 'CONTENT_LENGTH' => io.length
79
+ post '/', {}, 'rack.input' => io, 'CONTENT_TYPE' => 'application/json', 'CONTENT_LENGTH' => io.length
82
80
  last_response.body.should == '{"ret":56}'
83
81
  last_response.status.should == 201
84
82
  end
85
83
 
86
84
  it 'validates name, company' do
87
- get('/')
85
+ get '/'
88
86
  last_response.status.should == 400
89
- last_response.body.should == '{"error":"missing parameter: name"}'
87
+ last_response.body.should == '{"error":"name is missing"}'
90
88
 
91
- get('/', :name => "Bob")
89
+ get '/', name: "Bob"
92
90
  last_response.status.should == 400
93
- last_response.body.should == '{"error":"missing parameter: company"}'
91
+ last_response.body.should == '{"error":"company is missing"}'
94
92
 
95
- get('/', :name => "Bob", :company => "TestCorp")
93
+ get '/', name: "Bob", company: "TestCorp"
96
94
  last_response.status.should == 200
97
- last_response.body.should == "Hello"
95
+ last_response.body.should == "Hello".to_json
98
96
  end
99
97
 
100
98
  it 'validates nested parameters' do
101
- get('/nested')
99
+ get '/nested'
102
100
  last_response.status.should == 400
103
- last_response.body.should == '{"error":"missing parameter: user[first_name]"}'
101
+ last_response.body.should == '{"error":"user is missing, user[first_name] is missing, user[last_name] is missing"}'
104
102
 
105
- get('/nested', :user => {:first_name => "Billy"})
103
+ get '/nested', user: { first_name: "Billy" }
106
104
  last_response.status.should == 400
107
- last_response.body.should == '{"error":"missing parameter: user[last_name]"}'
105
+ last_response.body.should == '{"error":"user[last_name] is missing"}'
108
106
 
109
- get('/nested', :user => {:first_name => "Billy", :last_name => "Bob"})
107
+ get '/nested', user: { first_name: "Billy", last_name: "Bob" }
110
108
  last_response.status.should == 200
111
- last_response.body.should == "Nested"
109
+ last_response.body.should == "Nested".to_json
112
110
  end
113
111
 
114
112
  it 'validates triple nested parameters' do
115
- get('/nested_triple')
113
+ get '/nested_triple'
116
114
  last_response.status.should == 400
117
- last_response.body.should == '{"error":"missing parameter: admin[admin_name]"}'
115
+ last_response.body.should include '{"error":"admin is missing'
118
116
 
119
- get('/nested_triple', :user => {:first_name => "Billy"})
117
+ get '/nested_triple', user: { first_name: "Billy" }
120
118
  last_response.status.should == 400
121
- last_response.body.should == '{"error":"missing parameter: admin[admin_name]"}'
119
+ last_response.body.should include '{"error":"admin is missing'
122
120
 
123
- get('/nested_triple', :admin => {:super => {:first_name => "Billy"}})
121
+ get '/nested_triple', admin: { super: { first_name: "Billy" } }
124
122
  last_response.status.should == 400
125
- last_response.body.should == '{"error":"missing parameter: admin[admin_name]"}'
123
+ last_response.body.should == '{"error":"admin[admin_name] is missing, admin[super][user] is missing, admin[super][user][first_name] is missing, admin[super][user][last_name] is missing"}'
126
124
 
127
- get('/nested_triple', :super => {:user => {:first_name => "Billy", :last_name => "Bob"}})
125
+ get '/nested_triple', super: { user: { first_name: "Billy", last_name: "Bob" } }
128
126
  last_response.status.should == 400
129
- last_response.body.should == '{"error":"missing parameter: admin[admin_name]"}'
127
+ last_response.body.should include '{"error":"admin is missing'
130
128
 
131
- get('/nested_triple', :admin => {:super => {:user => {:first_name => "Billy"}}})
129
+ get '/nested_triple', admin: { super: { user: { first_name: "Billy" } } }
132
130
  last_response.status.should == 400
133
- last_response.body.should == '{"error":"missing parameter: admin[admin_name]"}'
131
+ last_response.body.should == '{"error":"admin[admin_name] is missing, admin[super][user][last_name] is missing"}'
134
132
 
135
- get('/nested_triple', :admin => { :admin_name => 'admin', :super => {:user => {:first_name => "Billy"}}})
133
+ get '/nested_triple', admin: { admin_name: 'admin', super: { user: { first_name: "Billy" } } }
136
134
  last_response.status.should == 400
137
- last_response.body.should == '{"error":"missing parameter: admin[super][user][last_name]"}'
135
+ last_response.body.should == '{"error":"admin[super][user][last_name] is missing"}'
138
136
 
139
- get('/nested_triple', :admin => { :admin_name => 'admin', :super => {:user => {:first_name => "Billy", :last_name => "Bob"}}})
137
+ get '/nested_triple', admin: { admin_name: 'admin', super: { user: { first_name: "Billy", last_name: "Bob" } } }
140
138
  last_response.status.should == 200
141
- last_response.body.should == "Nested triple"
139
+ last_response.body.should == "Nested triple".to_json
142
140
  end
143
141
 
144
142
  end
@@ -5,29 +5,29 @@ describe Grape::Validations::RegexpValidator do
5
5
  module RegexpValidatorSpec
6
6
  class API < Grape::API
7
7
  default_format :json
8
-
8
+
9
9
  params do
10
- requires :name, :regexp => /^[a-z]+$/
10
+ requires :name, regexp: /^[a-z]+$/
11
11
  end
12
12
  get do
13
-
13
+
14
14
  end
15
15
  end
16
16
  end
17
17
  end
18
-
18
+
19
19
  def app
20
20
  ValidationsSpec::RegexpValidatorSpec::API
21
21
  end
22
-
22
+
23
23
  it 'refuses invalid input' do
24
- get '/', :name => "invalid name"
24
+ get '/', name: "invalid name"
25
25
  last_response.status.should == 400
26
26
  end
27
-
27
+
28
28
  it 'accepts valid input' do
29
- get '/', :name => "bob"
29
+ get '/', name: "bob"
30
30
  last_response.status.should == 200
31
31
  end
32
-
32
+
33
33
  end
@@ -0,0 +1,138 @@
1
+ require 'spec_helper'
2
+
3
+ describe Grape::Validations::ValuesValidator do
4
+
5
+ class ValuesModel
6
+ DEFAULT_VALUES = ['valid-type1', 'valid-type2', 'valid-type3']
7
+ class << self
8
+ def values
9
+ @values ||= []
10
+ [DEFAULT_VALUES + @values].flatten.uniq
11
+ end
12
+
13
+ def add_value(value)
14
+ @values ||= []
15
+ @values << value
16
+ end
17
+ end
18
+ end
19
+
20
+ module ValidationsSpec
21
+ module ValuesValidatorSpec
22
+ class API < Grape::API
23
+ default_format :json
24
+
25
+ params do
26
+ requires :type, values: ValuesModel.values
27
+ end
28
+ get '/' do
29
+ { type: params[:type] }
30
+ end
31
+
32
+ params do
33
+ optional :type, values: ValuesModel.values, default: 'valid-type2'
34
+ end
35
+ get '/default/valid' do
36
+ { type: params[:type] }
37
+ end
38
+
39
+ params do
40
+ optional :type, values: -> { ValuesModel.values }, default: 'valid-type2'
41
+ end
42
+ get '/lambda' do
43
+ { type: params[:type] }
44
+ end
45
+
46
+ params do
47
+ requires :type, type: Integer, desc: "An integer", values: [10, 11], default: 10
48
+ end
49
+ get '/values/coercion' do
50
+ { type: params[:type] }
51
+ end
52
+ end
53
+ end
54
+ end
55
+
56
+ def app
57
+ ValidationsSpec::ValuesValidatorSpec::API
58
+ end
59
+
60
+ it 'allows a valid value for a parameter' do
61
+ get("/", type: 'valid-type1')
62
+ last_response.status.should eq 200
63
+ last_response.body.should eq({ type: "valid-type1" }.to_json)
64
+ end
65
+
66
+ it 'does not allow an invalid value for a parameter' do
67
+ get("/", type: 'invalid-type')
68
+ last_response.status.should eq 400
69
+ last_response.body.should eq({ error: "type does not have a valid value" }.to_json)
70
+ end
71
+
72
+ it 'does not allow nil value for a parameter' do
73
+ get("/", type: nil)
74
+ last_response.status.should eq 400
75
+ last_response.body.should eq({ error: "type does not have a valid value" }.to_json)
76
+ end
77
+
78
+ it 'allows a valid default value' do
79
+ get("/default/valid")
80
+ last_response.status.should eq 200
81
+ last_response.body.should eq({ type: "valid-type2" }.to_json)
82
+ end
83
+
84
+ it 'allows a proc for values' do
85
+ get('/lambda', type: 'valid-type1')
86
+ last_response.status.should eq 200
87
+ last_response.body.should eq({ type: "valid-type1" }.to_json)
88
+ end
89
+
90
+ it 'does not validate updated values without proc' do
91
+ ValuesModel.add_value('valid-type4')
92
+
93
+ get('/', type: 'valid-type4')
94
+ last_response.status.should eq 400
95
+ last_response.body.should eq({ error: "type does not have a valid value" }.to_json)
96
+ end
97
+
98
+ it 'validates against values in a proc' do
99
+ ValuesModel.add_value('valid-type4')
100
+
101
+ get('/lambda', type: 'valid-type4')
102
+ last_response.status.should eq 200
103
+ last_response.body.should eq({ type: "valid-type4" }.to_json)
104
+ end
105
+
106
+ it 'does not allow an invalid value for a parameter using lambda' do
107
+ get("/lambda", type: 'invalid-type')
108
+ last_response.status.should eq 400
109
+ last_response.body.should eq({ error: "type does not have a valid value" }.to_json)
110
+ end
111
+
112
+ it 'raises IncompatibleOptionValues on an invalid default value' do
113
+ subject = Class.new(Grape::API)
114
+ expect {
115
+ subject.params { optional :type, values: ['valid-type1', 'valid-type2', 'valid-type3'], default: 'invalid-type' }
116
+ }.to raise_error Grape::Exceptions::IncompatibleOptionValues
117
+ end
118
+
119
+ it 'raises IncompatibleOptionValues when type is incompatible with values array' do
120
+ subject = Class.new(Grape::API)
121
+ expect {
122
+ subject.params { optional :type, values: ['valid-type1', 'valid-type2', 'valid-type3'], type: Symbol }
123
+ }.to raise_error Grape::Exceptions::IncompatibleOptionValues
124
+ end
125
+
126
+ it 'allows values to be a kind of the coerced type not just an instance of it' do
127
+ get("/values/coercion", type: 10)
128
+ last_response.status.should eq 200
129
+ last_response.body.should eq({ type: 10 }.to_json)
130
+ end
131
+
132
+ it 'raises IncompatibleOptionValues when values contains a value that is not a kind of the type' do
133
+ subject = Class.new(Grape::API)
134
+ expect {
135
+ subject.params { requires :type, values: [10.5, 11], type: Integer }
136
+ }.to raise_error Grape::Exceptions::IncompatibleOptionValues
137
+ end
138
+ end
@@ -1,9 +1,10 @@
1
1
  zh-CN:
2
2
  grape:
3
3
  errors:
4
+ format: ! '%{attribute}%{message}'
4
5
  attributes:
5
6
  age: 年龄
6
7
  messages:
7
- coerce: '%{attribute}格式不正确'
8
- presence: '请填写{attribute}'
9
- regexp: '%{attribute}格式不正确'
8
+ coerce: '格式不正确'
9
+ presence: '请填写'
10
+ regexp: '格式不正确'