grape 0.14.0 → 0.15.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 +4 -4
  2. data/CHANGELOG.md +32 -4
  3. data/Gemfile.lock +13 -13
  4. data/README.md +290 -12
  5. data/UPGRADING.md +68 -1
  6. data/gemfiles/rails_3.gemfile +1 -1
  7. data/lib/grape.rb +8 -2
  8. data/lib/grape/api.rb +40 -34
  9. data/lib/grape/dsl/configuration.rb +2 -115
  10. data/lib/grape/dsl/desc.rb +101 -0
  11. data/lib/grape/dsl/headers.rb +16 -0
  12. data/lib/grape/dsl/helpers.rb +5 -9
  13. data/lib/grape/dsl/inside_route.rb +3 -11
  14. data/lib/grape/dsl/logger.rb +20 -0
  15. data/lib/grape/dsl/parameters.rb +12 -10
  16. data/lib/grape/dsl/request_response.rb +17 -4
  17. data/lib/grape/dsl/routing.rb +24 -7
  18. data/lib/grape/dsl/settings.rb +8 -2
  19. data/lib/grape/endpoint.rb +30 -26
  20. data/lib/grape/error_formatter.rb +31 -0
  21. data/lib/grape/error_formatter/base.rb +0 -28
  22. data/lib/grape/error_formatter/json.rb +13 -2
  23. data/lib/grape/error_formatter/txt.rb +3 -1
  24. data/lib/grape/error_formatter/xml.rb +3 -1
  25. data/lib/grape/exceptions/base.rb +11 -4
  26. data/lib/grape/exceptions/incompatible_option_values.rb +1 -1
  27. data/lib/grape/exceptions/invalid_accept_header.rb +1 -1
  28. data/lib/grape/exceptions/invalid_formatter.rb +1 -1
  29. data/lib/grape/exceptions/invalid_message_body.rb +1 -1
  30. data/lib/grape/exceptions/invalid_version_header.rb +1 -1
  31. data/lib/grape/exceptions/invalid_versioner_option.rb +1 -1
  32. data/lib/grape/exceptions/invalid_with_option_for_represent.rb +1 -1
  33. data/lib/grape/exceptions/method_not_allowed.rb +10 -0
  34. data/lib/grape/exceptions/missing_group_type.rb +1 -1
  35. data/lib/grape/exceptions/missing_mime_type.rb +1 -1
  36. data/lib/grape/exceptions/missing_option.rb +1 -1
  37. data/lib/grape/exceptions/missing_vendor_option.rb +1 -1
  38. data/lib/grape/exceptions/unknown_options.rb +1 -1
  39. data/lib/grape/exceptions/unknown_parameter.rb +1 -1
  40. data/lib/grape/exceptions/unknown_validator.rb +1 -1
  41. data/lib/grape/exceptions/unsupported_group_type.rb +1 -1
  42. data/lib/grape/exceptions/validation.rb +2 -1
  43. data/lib/grape/formatter.rb +31 -0
  44. data/lib/grape/middleware/base.rb +28 -2
  45. data/lib/grape/middleware/error.rb +24 -1
  46. data/lib/grape/middleware/formatter.rb +4 -3
  47. data/lib/grape/middleware/versioner/param.rb +13 -2
  48. data/lib/grape/parser.rb +29 -0
  49. data/lib/grape/util/sendfile_response.rb +19 -0
  50. data/lib/grape/util/strict_hash_configuration.rb +1 -1
  51. data/lib/grape/validations/params_scope.rb +39 -9
  52. data/lib/grape/validations/types.rb +16 -0
  53. data/lib/grape/validations/validators/all_or_none.rb +1 -1
  54. data/lib/grape/validations/validators/allow_blank.rb +2 -2
  55. data/lib/grape/validations/validators/at_least_one_of.rb +1 -1
  56. data/lib/grape/validations/validators/base.rb +26 -0
  57. data/lib/grape/validations/validators/coerce.rb +16 -14
  58. data/lib/grape/validations/validators/default.rb +1 -1
  59. data/lib/grape/validations/validators/exactly_one_of.rb +10 -1
  60. data/lib/grape/validations/validators/mutual_exclusion.rb +1 -1
  61. data/lib/grape/validations/validators/presence.rb +1 -1
  62. data/lib/grape/validations/validators/regexp.rb +2 -2
  63. data/lib/grape/validations/validators/values.rb +2 -2
  64. data/lib/grape/version.rb +1 -1
  65. data/spec/grape/api/custom_validations_spec.rb +156 -21
  66. data/spec/grape/api/namespace_parameters_in_route_spec.rb +38 -0
  67. data/spec/grape/api/optional_parameters_in_route_spec.rb +43 -0
  68. data/spec/grape/api/required_parameters_in_route_spec.rb +37 -0
  69. data/spec/grape/api_spec.rb +118 -60
  70. data/spec/grape/dsl/configuration_spec.rb +0 -75
  71. data/spec/grape/dsl/desc_spec.rb +77 -0
  72. data/spec/grape/dsl/headers_spec.rb +32 -0
  73. data/spec/grape/dsl/inside_route_spec.rb +0 -18
  74. data/spec/grape/dsl/logger_spec.rb +26 -0
  75. data/spec/grape/dsl/parameters_spec.rb +13 -7
  76. data/spec/grape/dsl/request_response_spec.rb +17 -3
  77. data/spec/grape/dsl/routing_spec.rb +8 -1
  78. data/spec/grape/dsl/settings_spec.rb +42 -0
  79. data/spec/grape/endpoint_spec.rb +60 -9
  80. data/spec/grape/exceptions/validation_errors_spec.rb +2 -2
  81. data/spec/grape/exceptions/validation_spec.rb +7 -0
  82. data/spec/grape/integration/rack_sendfile_spec.rb +44 -0
  83. data/spec/grape/middleware/base_spec.rb +100 -0
  84. data/spec/grape/middleware/exception_spec.rb +1 -2
  85. data/spec/grape/middleware/formatter_spec.rb +12 -2
  86. data/spec/grape/middleware/versioner/accept_version_header_spec.rb +1 -1
  87. data/spec/grape/middleware/versioner/header_spec.rb +11 -1
  88. data/spec/grape/middleware/versioner/param_spec.rb +105 -1
  89. data/spec/grape/validations/params_scope_spec.rb +77 -0
  90. data/spec/grape/validations/validators/allow_blank_spec.rb +277 -0
  91. data/spec/grape/validations/validators/coerce_spec.rb +91 -0
  92. data/spec/grape/validations/validators/default_spec.rb +6 -0
  93. data/spec/grape/validations/validators/presence_spec.rb +27 -0
  94. data/spec/grape/validations/validators/regexp_spec.rb +36 -0
  95. data/spec/grape/validations/validators/values_spec.rb +44 -0
  96. data/spec/grape/validations_spec.rb +149 -4
  97. data/spec/spec_helper.rb +1 -0
  98. metadata +26 -5
  99. data/lib/grape/formatter/base.rb +0 -31
  100. data/lib/grape/parser/base.rb +0 -29
  101. data/pkg/grape-0.13.0.gem +0 -0
@@ -9,81 +9,6 @@ module Grape
9
9
  end
10
10
  describe Configuration do
11
11
  subject { Class.new(ConfigurationSpec::Dummy) }
12
- let(:logger) { double(:logger) }
13
-
14
- describe '.logger' do
15
- it 'sets a logger' do
16
- subject.logger logger
17
- expect(subject.logger).to eq logger
18
- end
19
-
20
- it 'returns a logger' do
21
- expect(subject.logger logger).to eq logger
22
- end
23
- end
24
-
25
- describe '.desc' do
26
- it 'sets a description' do
27
- desc_text = 'The description'
28
- options = { message: 'none' }
29
- subject.desc desc_text, options
30
- expect(subject.namespace_setting(:description)).to eq(options.merge(description: desc_text))
31
- expect(subject.route_setting(:description)).to eq(options.merge(description: desc_text))
32
- end
33
-
34
- it 'can be set with a block' do
35
- expected_options = {
36
- description: 'The description',
37
- detail: 'more details',
38
- params: { first: :param },
39
- entity: Object,
40
- http_codes: [[401, 'Unauthorized', 'Entities::Error']],
41
- named: 'My named route',
42
- headers: [XAuthToken: {
43
- description: 'Valdates your identity',
44
- required: true
45
- },
46
- XOptionalHeader: {
47
- description: 'Not really needed',
48
- required: false
49
- }
50
- ]
51
- }
52
-
53
- subject.desc 'The description' do
54
- detail 'more details'
55
- params(first: :param)
56
- success Object
57
- failure [[401, 'Unauthorized', 'Entities::Error']]
58
- named 'My named route'
59
- headers [XAuthToken: {
60
- description: 'Valdates your identity',
61
- required: true
62
- },
63
- XOptionalHeader: {
64
- description: 'Not really needed',
65
- required: false
66
- }
67
- ]
68
- end
69
-
70
- expect(subject.namespace_setting(:description)).to eq(expected_options)
71
- expect(subject.route_setting(:description)).to eq(expected_options)
72
- end
73
-
74
- it 'can be set with options and a block' do
75
- expect(subject).to receive(:warn).with('[DEPRECATION] Passing a options hash and a block to `desc` is deprecated. Move all hash options to block.')
76
-
77
- desc_text = 'The description'
78
- detail_text = 'more details'
79
- options = { message: 'none' }
80
- subject.desc desc_text, options do
81
- detail detail_text
82
- end
83
- expect(subject.namespace_setting(:description)).to eq(description: desc_text, detail: detail_text)
84
- expect(subject.route_setting(:description)).to eq(description: desc_text, detail: detail_text)
85
- end
86
- end
87
12
  end
88
13
  end
89
14
  end
@@ -0,0 +1,77 @@
1
+ require 'spec_helper'
2
+
3
+ module Grape
4
+ module DSL
5
+ module DescSpec
6
+ class Dummy
7
+ extend Grape::DSL::Desc
8
+ end
9
+ end
10
+ describe Desc do
11
+ subject { Class.new(DescSpec::Dummy) }
12
+
13
+ describe '.desc' do
14
+ it 'sets a description' do
15
+ desc_text = 'The description'
16
+ options = { message: 'none' }
17
+ subject.desc desc_text, options
18
+ expect(subject.namespace_setting(:description)).to eq(options.merge(description: desc_text))
19
+ expect(subject.route_setting(:description)).to eq(options.merge(description: desc_text))
20
+ end
21
+
22
+ it 'can be set with a block' do
23
+ expected_options = {
24
+ description: 'The description',
25
+ detail: 'more details',
26
+ params: { first: :param },
27
+ entity: Object,
28
+ http_codes: [[401, 'Unauthorized', 'Entities::Error']],
29
+ named: 'My named route',
30
+ headers: [XAuthToken: {
31
+ description: 'Valdates your identity',
32
+ required: true
33
+ },
34
+ XOptionalHeader: {
35
+ description: 'Not really needed',
36
+ required: false
37
+ }
38
+ ]
39
+ }
40
+
41
+ subject.desc 'The description' do
42
+ detail 'more details'
43
+ params(first: :param)
44
+ success Object
45
+ failure [[401, 'Unauthorized', 'Entities::Error']]
46
+ named 'My named route'
47
+ headers [XAuthToken: {
48
+ description: 'Valdates your identity',
49
+ required: true
50
+ },
51
+ XOptionalHeader: {
52
+ description: 'Not really needed',
53
+ required: false
54
+ }
55
+ ]
56
+ end
57
+
58
+ expect(subject.namespace_setting(:description)).to eq(expected_options)
59
+ expect(subject.route_setting(:description)).to eq(expected_options)
60
+ end
61
+
62
+ it 'can be set with options and a block' do
63
+ expect(subject).to receive(:warn).with('[DEPRECATION] Passing a options hash and a block to `desc` is deprecated. Move all hash options to block.')
64
+
65
+ desc_text = 'The description'
66
+ detail_text = 'more details'
67
+ options = { message: 'none' }
68
+ subject.desc desc_text, options do
69
+ detail detail_text
70
+ end
71
+ expect(subject.namespace_setting(:description)).to eq(description: desc_text, detail: detail_text)
72
+ expect(subject.route_setting(:description)).to eq(description: desc_text, detail: detail_text)
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,32 @@
1
+ require 'spec_helper'
2
+
3
+ module Grape
4
+ module DSL
5
+ module HeadersSpec
6
+ class Dummy
7
+ include Grape::DSL::Headers
8
+ end
9
+ end
10
+ describe Headers do
11
+ subject { HeadersSpec::Dummy.new }
12
+
13
+ describe '#header' do
14
+ describe 'set' do
15
+ before do
16
+ subject.header 'Name', 'Value'
17
+ end
18
+
19
+ it 'returns value' do
20
+ expect(subject.header['Name']).to eq 'Value'
21
+ expect(subject.header('Name')).to eq 'Value'
22
+ end
23
+ end
24
+
25
+ it 'returns nil' do
26
+ expect(subject.header['Name']).to be nil
27
+ expect(subject.header('Name')).to be nil
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -128,24 +128,6 @@ module Grape
128
128
  end
129
129
  end
130
130
 
131
- describe '#header' do
132
- describe 'set' do
133
- before do
134
- subject.header 'Name', 'Value'
135
- end
136
-
137
- it 'returns value' do
138
- expect(subject.header['Name']).to eq 'Value'
139
- expect(subject.header('Name')).to eq 'Value'
140
- end
141
- end
142
-
143
- it 'returns nil' do
144
- expect(subject.header['Name']).to be nil
145
- expect(subject.header('Name')).to be nil
146
- end
147
- end
148
-
149
131
  describe '#content_type' do
150
132
  describe 'set' do
151
133
  before do
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+
3
+ module Grape
4
+ module DSL
5
+ module LoggerSpec
6
+ class Dummy
7
+ extend Grape::DSL::Logger
8
+ end
9
+ end
10
+ describe Logger do
11
+ subject { Class.new(LoggerSpec::Dummy) }
12
+ let(:logger) { double(:logger) }
13
+
14
+ describe '.logger' do
15
+ it 'sets a logger' do
16
+ subject.logger logger
17
+ expect(subject.logger).to eq logger
18
+ end
19
+
20
+ it 'returns a logger' do
21
+ expect(subject.logger logger).to eq logger
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -30,6 +30,12 @@ module Grape
30
30
  def validates_reader
31
31
  @validates
32
32
  end
33
+
34
+ def extract_message_option(attrs)
35
+ return nil unless attrs.is_a?(Array)
36
+ opts = attrs.last.is_a?(Hash) ? attrs.pop : {}
37
+ (opts.key?(:message) && !opts[:message].nil?) ? opts.delete(:message) : nil
38
+ end
33
39
  end
34
40
  end
35
41
 
@@ -45,13 +51,13 @@ module Grape
45
51
  let(:named_params) { { params_group: proc {} } }
46
52
 
47
53
  it 'calls processes associated with named params' do
48
- allow(Grape::DSL::Configuration).to receive(:stacked_hash_to_hash).and_return(named_params)
54
+ allow(subject.api).to receive(:namespace_stackable_with_hash).and_return(named_params)
49
55
  expect(subject).to receive(:instance_exec).with(options).and_yield
50
56
  subject.use :params_group, options
51
57
  end
52
58
 
53
59
  it 'raises error when non-existent named param is called' do
54
- allow(Grape::DSL::Configuration).to receive(:stacked_hash_to_hash).and_return({})
60
+ allow(subject.api).to receive(:namespace_stackable_with_hash).and_return({})
55
61
  expect { subject.use :params_group }.to raise_error('Params :params_group not found!')
56
62
  end
57
63
  end
@@ -72,7 +78,7 @@ module Grape
72
78
  it 'adds a required parameter' do
73
79
  subject.requires :id, type: Integer, desc: 'Identity.'
74
80
 
75
- expect(subject.validate_attributes_reader).to eq([[:id], { type: Integer, desc: 'Identity.', presence: true }])
81
+ expect(subject.validate_attributes_reader).to eq([[:id], { type: Integer, desc: 'Identity.', presence: { value: true, message: nil } }])
76
82
  expect(subject.push_declared_params_reader).to eq([[:id]])
77
83
  end
78
84
  end
@@ -90,7 +96,7 @@ module Grape
90
96
  it 'adds an mutally exclusive parameter validation' do
91
97
  subject.mutually_exclusive :media, :audio
92
98
 
93
- expect(subject.validates_reader).to eq([[:media, :audio], { mutual_exclusion: true }])
99
+ expect(subject.validates_reader).to eq([[:media, :audio], { mutual_exclusion: { value: true, message: nil } }])
94
100
  end
95
101
  end
96
102
 
@@ -98,7 +104,7 @@ module Grape
98
104
  it 'adds an exactly of one parameter validation' do
99
105
  subject.exactly_one_of :media, :audio
100
106
 
101
- expect(subject.validates_reader).to eq([[:media, :audio], { exactly_one_of: true }])
107
+ expect(subject.validates_reader).to eq([[:media, :audio], { exactly_one_of: { value: true, message: nil } }])
102
108
  end
103
109
  end
104
110
 
@@ -106,7 +112,7 @@ module Grape
106
112
  it 'adds an at least one of parameter validation' do
107
113
  subject.at_least_one_of :media, :audio
108
114
 
109
- expect(subject.validates_reader).to eq([[:media, :audio], { at_least_one_of: true }])
115
+ expect(subject.validates_reader).to eq([[:media, :audio], { at_least_one_of: { value: true, message: nil } }])
110
116
  end
111
117
  end
112
118
 
@@ -114,7 +120,7 @@ module Grape
114
120
  it 'adds an all or none of parameter validation' do
115
121
  subject.all_or_none_of :media, :audio
116
122
 
117
- expect(subject.validates_reader).to eq([[:media, :audio], { all_or_none_of: true }])
123
+ expect(subject.validates_reader).to eq([[:media, :audio], { all_or_none_of: { value: true, message: nil } }])
118
124
  end
119
125
  end
120
126
 
@@ -124,9 +124,22 @@ module Grape
124
124
  end
125
125
 
126
126
  it 'sets a rescue handler declared through :with option' do
127
+ with_block = -> { 'hello' }
127
128
  expect(subject).to receive(:namespace_inheritable).with(:rescue_all, true)
128
129
  expect(subject).to receive(:namespace_inheritable).with(:all_rescue_handler, an_instance_of(Proc))
129
- subject.rescue_from :all, with: 'ExampleHandler'
130
+ subject.rescue_from :all, with: with_block
131
+ end
132
+
133
+ it 'abort if :with option value is not Symbol, String or Proc' do
134
+ expect { subject.rescue_from :all, with: 1234 }.to raise_error(ArgumentError, 'with: Fixnum, expected Symbol, String or Proc')
135
+ end
136
+
137
+ it 'abort if both :with option and block are passed' do
138
+ expect do
139
+ subject.rescue_from :all, with: -> { 'hello' } do
140
+ error!('bye')
141
+ end
142
+ end.to raise_error(ArgumentError, 'both :with option and block cannot be passed')
130
143
  end
131
144
  end
132
145
 
@@ -158,9 +171,10 @@ module Grape
158
171
  end
159
172
 
160
173
  it 'sets a rescue handler declared through :with option for each key in hash' do
174
+ with_block = -> { 'hello' }
161
175
  expect(subject).to receive(:namespace_stackable).with(:rescue_handlers, StandardError => an_instance_of(Proc))
162
- expect(subject).to receive(:namespace_stackable).with(:rescue_options, with: 'ExampleHandler')
163
- subject.rescue_from StandardError, with: 'ExampleHandler'
176
+ expect(subject).to receive(:namespace_stackable).with(:rescue_options, with: with_block)
177
+ subject.rescue_from StandardError, with: with_block
164
178
  end
165
179
  end
166
180
  end
@@ -31,6 +31,13 @@ module Grape
31
31
  end
32
32
  end
33
33
 
34
+ describe '.scope' do
35
+ it 'create a scope without affecting the URL' do
36
+ expect(subject).to receive(:within_namespace)
37
+ subject.scope {}
38
+ end
39
+ end
40
+
34
41
  describe '.do_not_route_head!' do
35
42
  it 'sets do not route head option' do
36
43
  expect(subject).to receive(:namespace_inheritable).with(:do_not_route_head, true)
@@ -95,7 +102,7 @@ module Grape
95
102
 
96
103
  it 'generates correct endpoint options' do
97
104
  allow(subject).to receive(:route_setting).with(:description).and_return(fiz: 'baz')
98
- allow(Grape::DSL::Configuration).to receive(:stacked_hash_to_hash).and_return(nuz: 'naz')
105
+ allow(subject).to receive(:namespace_stackable_with_hash).and_return(nuz: 'naz')
99
106
 
100
107
  expect(Grape::Endpoint).to receive(:new) do |_inheritable_setting, endpoint_options|
101
108
  expect(endpoint_options[:method]).to eq :get
@@ -42,6 +42,13 @@ module Grape
42
42
  end
43
43
  end
44
44
 
45
+ describe '#unset_global_setting' do
46
+ it 'delegates to unset' do
47
+ expect(subject).to receive(:unset).with(:global, :dummy)
48
+ subject.unset_global_setting(:dummy)
49
+ end
50
+ end
51
+
45
52
  describe '#route_setting' do
46
53
  it 'delegates to get_or_set' do
47
54
  expect(subject).to receive(:get_or_set).with(:route, :dummy, 1)
@@ -58,6 +65,13 @@ module Grape
58
65
  end
59
66
  end
60
67
 
68
+ describe '#unset_route_setting' do
69
+ it 'delegates to unset' do
70
+ expect(subject).to receive(:unset).with(:route, :dummy)
71
+ subject.unset_route_setting(:dummy)
72
+ end
73
+ end
74
+
61
75
  describe '#namespace_setting' do
62
76
  it 'delegates to get_or_set' do
63
77
  expect(subject).to receive(:get_or_set).with(:namespace, :dummy, 1)
@@ -94,6 +108,13 @@ module Grape
94
108
  end
95
109
  end
96
110
 
111
+ describe '#unset_namespace_setting' do
112
+ it 'delegates to unset' do
113
+ expect(subject).to receive(:unset).with(:namespace, :dummy)
114
+ subject.unset_namespace_setting(:dummy)
115
+ end
116
+ end
117
+
97
118
  describe '#namespace_inheritable' do
98
119
  it 'delegates to get_or_set' do
99
120
  expect(subject).to receive(:get_or_set).with(:namespace_inheritable, :dummy, 1)
@@ -120,6 +141,13 @@ module Grape
120
141
  end
121
142
  end
122
143
 
144
+ describe '#unset_namespace_inheritable' do
145
+ it 'delegates to unset' do
146
+ expect(subject).to receive(:unset).with(:namespace_inheritable, :dummy)
147
+ subject.unset_namespace_inheritable(:dummy)
148
+ end
149
+ end
150
+
123
151
  describe '#namespace_stackable' do
124
152
  it 'delegates to get_or_set' do
125
153
  expect(subject).to receive(:get_or_set).with(:namespace_stackable, :dummy, 1)
@@ -146,6 +174,13 @@ module Grape
146
174
  end
147
175
  end
148
176
 
177
+ describe '#unset_namespace_stackable' do
178
+ it 'delegates to unset' do
179
+ expect(subject).to receive(:unset).with(:namespace_stackable, :dummy)
180
+ subject.unset_namespace_stackable(:dummy)
181
+ end
182
+ end
183
+
149
184
  describe '#api_class_setting' do
150
185
  it 'delegates to get_or_set' do
151
186
  expect(subject).to receive(:get_or_set).with(:api_class, :dummy, 1)
@@ -153,6 +188,13 @@ module Grape
153
188
  end
154
189
  end
155
190
 
191
+ describe '#unset_api_class_setting' do
192
+ it 'delegates to unset' do
193
+ expect(subject).to receive(:unset).with(:api_class, :dummy)
194
+ subject.unset_api_class_setting(:dummy)
195
+ end
196
+ end
197
+
156
198
  describe '#within_namespace' do
157
199
  it 'calls start and end for a namespace' do
158
200
  expect(subject).to receive :namespace_start