grape 1.4.0 → 1.5.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.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +19 -3
  3. data/README.md +56 -8
  4. data/UPGRADING.md +43 -4
  5. data/lib/grape/api.rb +2 -2
  6. data/lib/grape/dsl/helpers.rb +1 -0
  7. data/lib/grape/dsl/inside_route.rb +26 -38
  8. data/lib/grape/dsl/routing.rb +2 -4
  9. data/lib/grape/middleware/base.rb +2 -1
  10. data/lib/grape/middleware/error.rb +10 -12
  11. data/lib/grape/middleware/stack.rb +17 -4
  12. data/lib/grape/request.rb +1 -1
  13. data/lib/grape/router.rb +1 -1
  14. data/lib/grape/router/attribute_translator.rb +2 -2
  15. data/lib/grape/util/base_inheritable.rb +2 -2
  16. data/lib/grape/util/lazy_value.rb +1 -0
  17. data/lib/grape/validations/params_scope.rb +2 -1
  18. data/lib/grape/validations/types/custom_type_coercer.rb +13 -1
  19. data/lib/grape/validations/validators/as.rb +1 -1
  20. data/lib/grape/validations/validators/base.rb +2 -4
  21. data/lib/grape/validations/validators/default.rb +3 -4
  22. data/lib/grape/validations/validators/except_values.rb +1 -1
  23. data/lib/grape/validations/validators/values.rb +1 -1
  24. data/lib/grape/version.rb +1 -1
  25. data/spec/grape/api_spec.rb +10 -0
  26. data/spec/grape/dsl/inside_route_spec.rb +7 -0
  27. data/spec/grape/endpoint/declared_spec.rb +590 -0
  28. data/spec/grape/endpoint_spec.rb +0 -534
  29. data/spec/grape/entity_spec.rb +6 -0
  30. data/spec/grape/middleware/error_spec.rb +1 -1
  31. data/spec/grape/middleware/stack_spec.rb +3 -1
  32. data/spec/grape/validations/params_scope_spec.rb +26 -0
  33. data/spec/grape/validations/validators/coerce_spec.rb +24 -0
  34. data/spec/grape/validations/validators/default_spec.rb +49 -0
  35. data/spec/grape/validations/validators/except_values_spec.rb +1 -0
  36. data/spec/spec_helper.rb +0 -10
  37. data/spec/support/chunks.rb +14 -0
  38. data/spec/support/versioned_helpers.rb +3 -5
  39. metadata +9 -5
@@ -181,6 +181,7 @@ describe Grape::Entity do
181
181
  subject.get '/example' do
182
182
  c = Class.new do
183
183
  attr_reader :id
184
+
184
185
  def initialize(id)
185
186
  @id = id
186
187
  end
@@ -202,6 +203,7 @@ describe Grape::Entity do
202
203
  subject.get '/examples' do
203
204
  c = Class.new do
204
205
  attr_reader :id
206
+
205
207
  def initialize(id)
206
208
  @id = id
207
209
  end
@@ -226,6 +228,7 @@ describe Grape::Entity do
226
228
  subject.get '/example' do
227
229
  c = Class.new do
228
230
  attr_reader :name
231
+
229
232
  def initialize(args)
230
233
  @name = args[:name] || 'no name set'
231
234
  end
@@ -255,6 +258,7 @@ XML
255
258
  subject.get '/example' do
256
259
  c = Class.new do
257
260
  attr_reader :name
261
+
258
262
  def initialize(args)
259
263
  @name = args[:name] || 'no name set'
260
264
  end
@@ -284,6 +288,7 @@ XML
284
288
  subject.get '/example' do
285
289
  c = Class.new do
286
290
  attr_reader :name
291
+
287
292
  def initialize(args)
288
293
  @name = args[:name] || 'no name set'
289
294
  end
@@ -302,6 +307,7 @@ XML
302
307
  it 'present with multiple entities using optional symbol' do
303
308
  user = Class.new do
304
309
  attr_reader :name
310
+
305
311
  def initialize(args)
306
312
  @name = args[:name] || 'no name set'
307
313
  end
@@ -30,7 +30,7 @@ describe Grape::Middleware::Error do
30
30
  opts = options
31
31
  Rack::Builder.app do
32
32
  use Spec::Support::EndpointFaker
33
- use Grape::Middleware::Error, opts
33
+ use Grape::Middleware::Error, **opts
34
34
  run ErrorSpec::ErrApp
35
35
  end
36
36
  end
@@ -8,6 +8,7 @@ describe Grape::Middleware::Stack do
8
8
  class BarMiddleware; end
9
9
  class BlockMiddleware
10
10
  attr_reader :block
11
+
11
12
  def initialize(&block)
12
13
  @block = block
13
14
  end
@@ -34,7 +35,8 @@ describe Grape::Middleware::Stack do
34
35
  expect { subject.use StackSpec::BarMiddleware, false, my_arg: 42 }
35
36
  .to change { subject.size }.by(1)
36
37
  expect(subject.last).to eq(StackSpec::BarMiddleware)
37
- expect(subject.last.args).to eq([false, { my_arg: 42 }])
38
+ expect(subject.last.args).to eq([false])
39
+ expect(subject.last.opts).to eq(my_arg: 42)
38
40
  end
39
41
 
40
42
  it 'pushes a middleware class with block arguments onto the stack' do
@@ -633,6 +633,32 @@ describe Grape::Validations::ParamsScope do
633
633
  expect(last_response.status).to eq(200)
634
634
  end
635
635
 
636
+ it 'detect unmet nested dependency' do
637
+ subject.params do
638
+ requires :a, type: String, allow_blank: false, values: %w[x y z]
639
+ given a: ->(val) { val == 'z' } do
640
+ requires :inner3, type: Array, allow_blank: false do
641
+ requires :bar, type: String, allow_blank: false
642
+ given bar: ->(val) { val == 'b' } do
643
+ requires :baz, type: Array do
644
+ optional :baz_category, type: String
645
+ end
646
+ end
647
+ given bar: ->(val) { val == 'c' } do
648
+ requires :baz, type: Array do
649
+ requires :baz_category, type: String
650
+ end
651
+ end
652
+ end
653
+ end
654
+ end
655
+ subject.get('/nested-dependency') { declared(params).to_json }
656
+
657
+ get '/nested-dependency', a: 'z', inner3: [{ bar: 'c', baz: [{ unrelated: 'nope' }] }]
658
+ expect(last_response.status).to eq(400)
659
+ expect(last_response.body).to eq 'inner3[0][baz][0][baz_category] is missing'
660
+ end
661
+
636
662
  it 'includes the parameter within #declared(params)' do
637
663
  get '/test', a: true, b: true
638
664
 
@@ -620,6 +620,30 @@ describe Grape::Validations::CoerceValidator do
620
620
  expect(JSON.parse(last_response.body)).to eq(%w[a b c d])
621
621
  end
622
622
 
623
+ it 'parses parameters with Array[Array[String]] type and coerce_with' do
624
+ subject.params do
625
+ requires :values, type: Array[Array[String]], coerce_with: ->(val) { val.is_a?(String) ? [val.split(/,/).map(&:strip)] : val }
626
+ end
627
+ subject.post '/coerce_nested_strings' do
628
+ params[:values]
629
+ end
630
+
631
+ post '/coerce_nested_strings', ::Grape::Json.dump(values: 'a,b,c,d'), 'CONTENT_TYPE' => 'application/json'
632
+ expect(last_response.status).to eq(201)
633
+ expect(JSON.parse(last_response.body)).to eq([%w[a b c d]])
634
+
635
+ post '/coerce_nested_strings', ::Grape::Json.dump(values: [%w[a c], %w[b]]), 'CONTENT_TYPE' => 'application/json'
636
+ expect(last_response.status).to eq(201)
637
+ expect(JSON.parse(last_response.body)).to eq([%w[a c], %w[b]])
638
+
639
+ post '/coerce_nested_strings', ::Grape::Json.dump(values: [[]]), 'CONTENT_TYPE' => 'application/json'
640
+ expect(last_response.status).to eq(201)
641
+ expect(JSON.parse(last_response.body)).to eq([[]])
642
+
643
+ post '/coerce_nested_strings', ::Grape::Json.dump(values: [['a', { bar: 0 }], ['b']]), 'CONTENT_TYPE' => 'application/json'
644
+ expect(last_response.status).to eq(400)
645
+ end
646
+
623
647
  it 'parses parameters with Array[Integer] type' do
624
648
  subject.params do
625
649
  requires :values, type: Array[Integer], coerce_with: ->(val) { val.split(/\s+/).map(&:to_i) }
@@ -419,4 +419,53 @@ describe Grape::Validations::DefaultValidator do
419
419
  end
420
420
  end
421
421
  end
422
+
423
+ context 'array with default values and given conditions' do
424
+ subject do
425
+ Class.new(Grape::API) do
426
+ default_format :json
427
+ end
428
+ end
429
+
430
+ def app
431
+ subject
432
+ end
433
+
434
+ it 'applies the default values only if the conditions are met' do
435
+ subject.params do
436
+ requires :ary, type: Array do
437
+ requires :has_value, type: Grape::API::Boolean
438
+ given has_value: ->(has_value) { has_value } do
439
+ optional :type, type: String, values: %w[str int], default: 'str'
440
+ given type: ->(type) { type == 'str' } do
441
+ optional :str, type: String, default: 'a'
442
+ end
443
+ given type: ->(type) { type == 'int' } do
444
+ optional :int, type: Integer, default: 1
445
+ end
446
+ end
447
+ end
448
+ end
449
+ subject.post('/nested_given_and_default') { declared(self.params) }
450
+
451
+ params = {
452
+ ary: [
453
+ { has_value: false },
454
+ { has_value: true, type: 'int', int: 123 },
455
+ { has_value: true, type: 'str', str: 'b' }
456
+ ]
457
+ }
458
+ expected = {
459
+ 'ary' => [
460
+ { 'has_value' => false, 'type' => nil, 'int' => nil, 'str' => nil },
461
+ { 'has_value' => true, 'type' => 'int', 'int' => 123, 'str' => nil },
462
+ { 'has_value' => true, 'type' => 'str', 'int' => nil, 'str' => 'b' }
463
+ ]
464
+ }
465
+
466
+ post '/nested_given_and_default', params
467
+ expect(last_response.status).to eq(201)
468
+ expect(JSON.parse(last_response.body)).to eq(expected)
469
+ end
470
+ end
422
471
  end
@@ -8,6 +8,7 @@ describe Grape::Validations::ExceptValuesValidator do
8
8
  DEFAULT_EXCEPTS = ['invalid-type1', 'invalid-type2', 'invalid-type3'].freeze
9
9
  class << self
10
10
  attr_accessor :excepts
11
+
11
12
  def excepts
12
13
  @excepts ||= []
13
14
  [DEFAULT_EXCEPTS + @excepts].flatten.uniq
@@ -20,17 +20,7 @@ eager_load!
20
20
  # so it should be set to true here as well to reflect that.
21
21
  I18n.enforce_available_locales = true
22
22
 
23
- module Chunks
24
- def read_chunks(body)
25
- buffer = []
26
- body.each { |chunk| buffer << chunk }
27
-
28
- buffer
29
- end
30
- end
31
-
32
23
  RSpec.configure do |config|
33
- config.include Chunks
34
24
  config.include Rack::Test::Methods
35
25
  config.include Spec::Support::Helpers
36
26
  config.raise_errors_for_deprecations!
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Chunks
4
+ def read_chunks(body)
5
+ buffer = []
6
+ body.each { |chunk| buffer << chunk }
7
+
8
+ buffer
9
+ end
10
+ end
11
+
12
+ RSpec.configure do |config|
13
+ config.include Chunks
14
+ end
@@ -6,7 +6,7 @@ module Spec
6
6
  module Helpers
7
7
  # Returns the path with options[:version] prefixed if options[:using] is :path.
8
8
  # Returns normal path otherwise.
9
- def versioned_path(options = {})
9
+ def versioned_path(**options)
10
10
  case options[:using]
11
11
  when :path
12
12
  File.join('/', options[:prefix] || '', options[:version], options[:path])
@@ -43,13 +43,11 @@ module Spec
43
43
  end
44
44
  end
45
45
 
46
- def versioned_get(path, version_name, version_options = {})
46
+ def versioned_get(path, version_name, **version_options)
47
47
  path = versioned_path(version_options.merge(version: version_name, path: path))
48
48
  headers = versioned_headers(**version_options.merge(version: version_name))
49
49
  params = {}
50
- if version_options[:using] == :param
51
- params = { version_options[:parameter] => version_name }
52
- end
50
+ params = { version_options[:parameter] => version_name } if version_options[:using] == :param
53
51
  get path, params, headers
54
52
  end
55
53
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: grape
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.0
4
+ version: 1.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Bleigh
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-07-10 00:00:00.000000000 Z
11
+ date: 2020-10-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -282,6 +282,7 @@ files:
282
282
  - spec/grape/dsl/routing_spec.rb
283
283
  - spec/grape/dsl/settings_spec.rb
284
284
  - spec/grape/dsl/validations_spec.rb
285
+ - spec/grape/endpoint/declared_spec.rb
285
286
  - spec/grape/endpoint_spec.rb
286
287
  - spec/grape/entity_spec.rb
287
288
  - spec/grape/exceptions/base_spec.rb
@@ -356,6 +357,7 @@ files:
356
357
  - spec/shared/versioning_examples.rb
357
358
  - spec/spec_helper.rb
358
359
  - spec/support/basic_auth_encode_helpers.rb
360
+ - spec/support/chunks.rb
359
361
  - spec/support/content_type_helpers.rb
360
362
  - spec/support/eager_load.rb
361
363
  - spec/support/endpoint_faker.rb
@@ -367,9 +369,9 @@ licenses:
367
369
  - MIT
368
370
  metadata:
369
371
  bug_tracker_uri: https://github.com/ruby-grape/grape/issues
370
- changelog_uri: https://github.com/ruby-grape/grape/blob/v1.4.0/CHANGELOG.md
371
- documentation_uri: https://www.rubydoc.info/gems/grape/1.4.0
372
- source_code_uri: https://github.com/ruby-grape/grape/tree/v1.4.0
372
+ changelog_uri: https://github.com/ruby-grape/grape/blob/v1.5.0/CHANGELOG.md
373
+ documentation_uri: https://www.rubydoc.info/gems/grape/1.5.0
374
+ source_code_uri: https://github.com/ruby-grape/grape/tree/v1.5.0
373
375
  post_install_message:
374
376
  rdoc_options: []
375
377
  require_paths:
@@ -397,6 +399,7 @@ test_files:
397
399
  - spec/shared/versioning_examples.rb
398
400
  - spec/support/basic_auth_encode_helpers.rb
399
401
  - spec/support/endpoint_faker.rb
402
+ - spec/support/chunks.rb
400
403
  - spec/support/file_streamer.rb
401
404
  - spec/support/versioned_helpers.rb
402
405
  - spec/support/content_type_helpers.rb
@@ -470,6 +473,7 @@ test_files:
470
473
  - spec/grape/extensions/param_builders/hash_with_indifferent_access_spec.rb
471
474
  - spec/grape/parser_spec.rb
472
475
  - spec/grape/request_spec.rb
476
+ - spec/grape/endpoint/declared_spec.rb
473
477
  - spec/grape/api/parameters_modification_spec.rb
474
478
  - spec/grape/api/patch_method_helpers_spec.rb
475
479
  - spec/grape/api/required_parameters_with_invalid_method_spec.rb