grape 0.12.0 → 0.14.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 (123) hide show
  1. checksums.yaml +4 -4
  2. data/Appraisals +9 -4
  3. data/CHANGELOG.md +265 -215
  4. data/CONTRIBUTING.md +4 -4
  5. data/Gemfile +0 -1
  6. data/Gemfile.lock +166 -0
  7. data/README.md +426 -161
  8. data/RELEASING.md +14 -6
  9. data/Rakefile +30 -33
  10. data/UPGRADING.md +54 -23
  11. data/benchmark/simple.rb +27 -0
  12. data/gemfiles/rack_1.5.2.gemfile +13 -0
  13. data/gemfiles/rails_3.gemfile +2 -2
  14. data/gemfiles/rails_4.gemfile +1 -2
  15. data/grape.gemspec +6 -7
  16. data/lib/grape/api.rb +24 -4
  17. data/lib/grape/dsl/callbacks.rb +20 -0
  18. data/lib/grape/dsl/configuration.rb +59 -2
  19. data/lib/grape/dsl/helpers.rb +8 -3
  20. data/lib/grape/dsl/inside_route.rb +100 -45
  21. data/lib/grape/dsl/parameters.rb +96 -7
  22. data/lib/grape/dsl/request_response.rb +1 -1
  23. data/lib/grape/dsl/routing.rb +17 -4
  24. data/lib/grape/dsl/settings.rb +36 -1
  25. data/lib/grape/dsl/validations.rb +7 -5
  26. data/lib/grape/endpoint.rb +102 -57
  27. data/lib/grape/error_formatter/base.rb +6 -6
  28. data/lib/grape/exceptions/base.rb +5 -5
  29. data/lib/grape/exceptions/invalid_version_header.rb +10 -0
  30. data/lib/grape/exceptions/unknown_parameter.rb +10 -0
  31. data/lib/grape/exceptions/validation_errors.rb +4 -3
  32. data/lib/grape/formatter/serializable_hash.rb +3 -2
  33. data/lib/grape/http/headers.rb +0 -1
  34. data/lib/grape/locale/en.yml +5 -1
  35. data/lib/grape/middleware/auth/base.rb +2 -2
  36. data/lib/grape/middleware/auth/dsl.rb +1 -1
  37. data/lib/grape/middleware/auth/strategies.rb +1 -1
  38. data/lib/grape/middleware/base.rb +8 -4
  39. data/lib/grape/middleware/error.rb +3 -2
  40. data/lib/grape/middleware/filter.rb +1 -1
  41. data/lib/grape/middleware/formatter.rb +64 -45
  42. data/lib/grape/middleware/globals.rb +3 -3
  43. data/lib/grape/middleware/versioner/accept_version_header.rb +5 -7
  44. data/lib/grape/middleware/versioner/header.rb +113 -50
  45. data/lib/grape/middleware/versioner/param.rb +5 -8
  46. data/lib/grape/middleware/versioner/parse_media_type_patch.rb +20 -0
  47. data/lib/grape/middleware/versioner/path.rb +3 -6
  48. data/lib/grape/namespace.rb +13 -2
  49. data/lib/grape/path.rb +4 -3
  50. data/lib/grape/request.rb +40 -0
  51. data/lib/grape/route.rb +5 -0
  52. data/lib/grape/util/content_types.rb +9 -9
  53. data/lib/grape/util/env.rb +22 -0
  54. data/lib/grape/util/file_response.rb +21 -0
  55. data/lib/grape/util/inheritable_setting.rb +23 -2
  56. data/lib/grape/util/inheritable_values.rb +1 -1
  57. data/lib/grape/util/stackable_values.rb +5 -2
  58. data/lib/grape/util/strict_hash_configuration.rb +2 -1
  59. data/lib/grape/validations/attributes_iterator.rb +8 -3
  60. data/lib/grape/validations/params_scope.rb +164 -22
  61. data/lib/grape/validations/types/build_coercer.rb +53 -0
  62. data/lib/grape/validations/types/custom_type_coercer.rb +183 -0
  63. data/lib/grape/validations/types/file.rb +28 -0
  64. data/lib/grape/validations/types/json.rb +65 -0
  65. data/lib/grape/validations/types/multiple_type_coercer.rb +76 -0
  66. data/lib/grape/validations/types/variant_collection_coercer.rb +59 -0
  67. data/lib/grape/validations/types/virtus_collection_patch.rb +16 -0
  68. data/lib/grape/validations/types.rb +144 -0
  69. data/lib/grape/validations/validators/all_or_none.rb +1 -1
  70. data/lib/grape/validations/validators/allow_blank.rb +3 -3
  71. data/lib/grape/validations/validators/base.rb +7 -0
  72. data/lib/grape/validations/validators/coerce.rb +32 -34
  73. data/lib/grape/validations/validators/presence.rb +2 -3
  74. data/lib/grape/validations/validators/regexp.rb +2 -4
  75. data/lib/grape/validations/validators/values.rb +3 -3
  76. data/lib/grape/validations.rb +5 -0
  77. data/lib/grape/version.rb +2 -1
  78. data/lib/grape.rb +15 -12
  79. data/pkg/grape-0.13.0.gem +0 -0
  80. data/spec/grape/api/custom_validations_spec.rb +5 -4
  81. data/spec/grape/api/deeply_included_options_spec.rb +7 -7
  82. data/spec/grape/api/nested_helpers_spec.rb +4 -2
  83. data/spec/grape/api/shared_helpers_spec.rb +8 -8
  84. data/spec/grape/api_spec.rb +151 -54
  85. data/spec/grape/dsl/configuration_spec.rb +13 -0
  86. data/spec/grape/dsl/helpers_spec.rb +16 -2
  87. data/spec/grape/dsl/inside_route_spec.rb +40 -4
  88. data/spec/grape/dsl/parameters_spec.rb +0 -6
  89. data/spec/grape/dsl/routing_spec.rb +1 -1
  90. data/spec/grape/dsl/validations_spec.rb +18 -0
  91. data/spec/grape/endpoint_spec.rb +130 -6
  92. data/spec/grape/entity_spec.rb +10 -8
  93. data/spec/grape/exceptions/invalid_accept_header_spec.rb +1 -15
  94. data/spec/grape/exceptions/validation_errors_spec.rb +28 -0
  95. data/spec/grape/integration/rack_spec.rb +3 -2
  96. data/spec/grape/middleware/base_spec.rb +40 -16
  97. data/spec/grape/middleware/error_spec.rb +16 -15
  98. data/spec/grape/middleware/exception_spec.rb +45 -43
  99. data/spec/grape/middleware/formatter_spec.rb +34 -5
  100. data/spec/grape/middleware/versioner/header_spec.rb +79 -47
  101. data/spec/grape/path_spec.rb +10 -10
  102. data/spec/grape/presenters/presenter_spec.rb +2 -2
  103. data/spec/grape/request_spec.rb +100 -0
  104. data/spec/grape/util/inheritable_values_spec.rb +14 -0
  105. data/spec/grape/util/stackable_values_spec.rb +10 -0
  106. data/spec/grape/validations/params_scope_spec.rb +86 -0
  107. data/spec/grape/validations/types_spec.rb +95 -0
  108. data/spec/grape/validations/validators/coerce_spec.rb +364 -10
  109. data/spec/grape/validations/validators/values_spec.rb +27 -15
  110. data/spec/grape/validations_spec.rb +53 -24
  111. data/spec/shared/versioning_examples.rb +2 -2
  112. data/spec/spec_helper.rb +0 -1
  113. data/spec/support/versioned_helpers.rb +2 -2
  114. metadata +55 -14
  115. data/.gitignore +0 -46
  116. data/.rspec +0 -2
  117. data/.rubocop.yml +0 -7
  118. data/.rubocop_todo.yml +0 -84
  119. data/.travis.yml +0 -20
  120. data/.yardopts +0 -2
  121. data/lib/backports/active_support/deep_dup.rb +0 -49
  122. data/lib/backports/active_support/duplicable.rb +0 -88
  123. data/lib/grape/http/request.rb +0 -27
@@ -537,16 +537,38 @@ describe Grape::API do
537
537
  expect(last_response.headers['Content-Type']).to eql 'text/plain'
538
538
  end
539
539
 
540
- it 'adds an OPTIONS route that returns a 204, an Allow header and a X-Custom-Header' do
541
- subject.before { header 'X-Custom-Header', 'foo' }
542
- subject.get 'example' do
543
- 'example'
540
+ describe 'adds an OPTIONS route that' do
541
+ before do
542
+ subject.before { header 'X-Custom-Header', 'foo' }
543
+ subject.get 'example' do
544
+ 'example'
545
+ end
546
+ options '/example'
547
+ end
548
+
549
+ it 'returns a 204' do
550
+ expect(last_response.status).to eql 204
551
+ end
552
+
553
+ it 'has an empty body' do
554
+ expect(last_response.body).to be_blank
555
+ end
556
+
557
+ it 'has an Allow header' do
558
+ expect(last_response.headers['Allow']).to eql 'OPTIONS, GET, HEAD'
559
+ end
560
+
561
+ it 'has a X-Custom-Header' do
562
+ expect(last_response.headers['X-Custom-Header']).to eql 'foo'
563
+ end
564
+
565
+ it 'has no Content-Type' do
566
+ expect(last_response.content_type).to be_nil
567
+ end
568
+
569
+ it 'has no Content-Length' do
570
+ expect(last_response.content_length).to be_nil
544
571
  end
545
- options '/example'
546
- expect(last_response.status).to eql 204
547
- expect(last_response.body).to eql ''
548
- expect(last_response.headers['Allow']).to eql 'OPTIONS, GET, HEAD'
549
- expect(last_response.headers['X-Custom-Header']).to eql 'foo'
550
572
  end
551
573
 
552
574
  it 'allows HEAD on a GET request' do
@@ -640,7 +662,7 @@ describe Grape::API do
640
662
  end
641
663
 
642
664
  it 'adds a after_validation filter' do
643
- subject.after_validation { @foo = "first #{params[:id] }:#{params[:id].class}" }
665
+ subject.after_validation { @foo = "first #{params[:id]}:#{params[:id].class}" }
644
666
  subject.after_validation { @bar = 'second' }
645
667
  subject.params do
646
668
  requires :id, type: Integer
@@ -787,7 +809,7 @@ describe Grape::API do
787
809
 
788
810
  it 'returns raw data when content type binary' do
789
811
  image_filename = 'grape.png'
790
- file = File.open(image_filename, 'rb') { |io| io.read }
812
+ file = File.open(image_filename, 'rb', &:read)
791
813
  subject.format :binary
792
814
  subject.get('/binary_file') { File.binread(image_filename) }
793
815
  get '/binary_file'
@@ -795,6 +817,37 @@ describe Grape::API do
795
817
  expect(last_response.body).to eq(file)
796
818
  end
797
819
 
820
+ it 'returns the content of the file with file' do
821
+ file_content = 'This is some file content'
822
+ test_file = Tempfile.new('test')
823
+ test_file.write file_content
824
+ test_file.rewind
825
+
826
+ subject.get('/file') { file test_file }
827
+ get '/file'
828
+ expect(last_response.headers['Content-Length']).to eq('25')
829
+ expect(last_response.headers['Content-Type']).to eq('text/plain')
830
+ expect(last_response.body).to eq(file_content)
831
+ end
832
+
833
+ it 'streams the content of the file with stream' do
834
+ test_stream = Enumerator.new do |blk|
835
+ blk.yield 'This is some'
836
+ blk.yield ' file content'
837
+ end
838
+
839
+ subject.use Rack::Chunked
840
+ subject.get('/stream') { stream test_stream }
841
+ get '/stream', {}, 'HTTP_VERSION' => 'HTTP/1.1'
842
+
843
+ expect(last_response.headers['Content-Type']).to eq('text/plain')
844
+ expect(last_response.headers['Content-Length']).to eq(nil)
845
+ expect(last_response.headers['Cache-Control']).to eq('no-cache')
846
+ expect(last_response.headers['Transfer-Encoding']).to eq('chunked')
847
+
848
+ expect(last_response.body).to eq("c\r\nThis is some\r\nd\r\n file content\r\n0\r\n\r\n")
849
+ end
850
+
798
851
  it 'sets content type for error' do
799
852
  subject.get('/error') { error!('error in plain text', 500) }
800
853
  get '/error'
@@ -1035,7 +1088,7 @@ describe Grape::API do
1035
1088
 
1036
1089
  subject.get(:hello) { 'Hello, world.' }
1037
1090
  get '/hello', {}, 'HTTP_AUTHORIZATION' => encode_basic_auth('allow', 'whatever')
1038
- expect(basic_auth_context).to be_an_instance_of(Grape::Endpoint)
1091
+ expect(basic_auth_context).to be_a_kind_of(Grape::Endpoint)
1039
1092
  end
1040
1093
 
1041
1094
  it 'has access to helper methods' do
@@ -1278,21 +1331,23 @@ describe Grape::API do
1278
1331
 
1279
1332
  context 'CustomError subclass of Grape::Exceptions::Base' do
1280
1333
  before do
1281
- class CustomError < Grape::Exceptions::Base; end
1334
+ module ApiSpec
1335
+ class CustomError < Grape::Exceptions::Base; end
1336
+ end
1282
1337
  end
1283
1338
 
1284
1339
  it 'does not re-raise exceptions of type Grape::Exceptions::Base' do
1285
- subject.get('/custom_exception') { fail CustomError }
1340
+ subject.get('/custom_exception') { fail ApiSpec::CustomError }
1286
1341
 
1287
1342
  expect { get '/custom_exception' }.not_to raise_error
1288
1343
  end
1289
1344
 
1290
1345
  it 'rescues custom grape exceptions' do
1291
- subject.rescue_from CustomError do |e|
1346
+ subject.rescue_from ApiSpec::CustomError do |e|
1292
1347
  rack_response('New Error', e.status)
1293
1348
  end
1294
1349
  subject.get '/custom_error' do
1295
- fail CustomError, status: 400, message: 'Custom Error'
1350
+ fail ApiSpec::CustomError, status: 400, message: 'Custom Error'
1296
1351
  end
1297
1352
 
1298
1353
  get '/custom_error'
@@ -1404,7 +1459,7 @@ describe Grape::API do
1404
1459
 
1405
1460
  describe '.rescue_from klass, lambda' do
1406
1461
  it 'rescues an error with the lambda' do
1407
- subject.rescue_from ArgumentError, -> {
1462
+ subject.rescue_from ArgumentError, lambda {
1408
1463
  rack_response('rescued with a lambda', 400)
1409
1464
  }
1410
1465
  subject.get('/rescue_lambda') { fail ArgumentError }
@@ -1415,7 +1470,7 @@ describe Grape::API do
1415
1470
  end
1416
1471
 
1417
1472
  it 'can execute the lambda with an argument' do
1418
- subject.rescue_from ArgumentError, ->(e) {
1473
+ subject.rescue_from ArgumentError, lambda { |e|
1419
1474
  rack_response(e.message, 400)
1420
1475
  }
1421
1476
  subject.get('/rescue_lambda') { fail ArgumentError, 'lambda takes an argument' }
@@ -1443,21 +1498,23 @@ describe Grape::API do
1443
1498
 
1444
1499
  describe '.rescue_from klass, rescue_subclasses: boolean' do
1445
1500
  before do
1446
- module APIErrors
1447
- class ParentError < StandardError; end
1448
- class ChildError < ParentError; end
1501
+ module ApiSpec
1502
+ module APIErrors
1503
+ class ParentError < StandardError; end
1504
+ class ChildError < ParentError; end
1505
+ end
1449
1506
  end
1450
1507
  end
1451
1508
 
1452
1509
  it 'rescues error as well as subclass errors with rescue_subclasses option set' do
1453
- subject.rescue_from APIErrors::ParentError, rescue_subclasses: true do |e|
1510
+ subject.rescue_from ApiSpec::APIErrors::ParentError, rescue_subclasses: true do |e|
1454
1511
  rack_response("rescued from #{e.class.name}", 500)
1455
1512
  end
1456
1513
  subject.get '/caught_child' do
1457
- fail APIErrors::ChildError
1514
+ fail ApiSpec::APIErrors::ChildError
1458
1515
  end
1459
1516
  subject.get '/caught_parent' do
1460
- fail APIErrors::ParentError
1517
+ fail ApiSpec::APIErrors::ParentError
1461
1518
  end
1462
1519
  subject.get '/uncaught_parent' do
1463
1520
  fail StandardError
@@ -1471,11 +1528,11 @@ describe Grape::API do
1471
1528
  end
1472
1529
 
1473
1530
  it 'sets rescue_subclasses to true by default' do
1474
- subject.rescue_from APIErrors::ParentError do |e|
1531
+ subject.rescue_from ApiSpec::APIErrors::ParentError do |e|
1475
1532
  rack_response("rescued from #{e.class.name}", 500)
1476
1533
  end
1477
1534
  subject.get '/caught_child' do
1478
- fail APIErrors::ChildError
1535
+ fail ApiSpec::APIErrors::ChildError
1479
1536
  end
1480
1537
 
1481
1538
  get '/caught_child'
@@ -1483,13 +1540,13 @@ describe Grape::API do
1483
1540
  end
1484
1541
 
1485
1542
  it 'does not rescue child errors if rescue_subclasses is false' do
1486
- subject.rescue_from APIErrors::ParentError, rescue_subclasses: false do |e|
1543
+ subject.rescue_from ApiSpec::APIErrors::ParentError, rescue_subclasses: false do |e|
1487
1544
  rack_response("rescued from #{e.class.name}", 500)
1488
1545
  end
1489
1546
  subject.get '/uncaught' do
1490
- fail APIErrors::ChildError
1547
+ fail ApiSpec::APIErrors::ChildError
1491
1548
  end
1492
- expect { get '/uncaught' }.to raise_error(APIErrors::ChildError)
1549
+ expect { get '/uncaught' }.to raise_error(ApiSpec::APIErrors::ChildError)
1493
1550
  end
1494
1551
  end
1495
1552
 
@@ -1541,15 +1598,17 @@ describe Grape::API do
1541
1598
 
1542
1599
  context 'class' do
1543
1600
  before :each do
1544
- class CustomErrorFormatter
1545
- def self.call(message, _backtrace, _options, _env)
1546
- "message: #{message} @backtrace"
1601
+ module ApiSpec
1602
+ class CustomErrorFormatter
1603
+ def self.call(message, _backtrace, _options, _env)
1604
+ "message: #{message} @backtrace"
1605
+ end
1547
1606
  end
1548
1607
  end
1549
1608
  end
1550
1609
  it 'returns a custom error format' do
1551
1610
  subject.rescue_from :all, backtrace: true
1552
- subject.error_formatter :txt, CustomErrorFormatter
1611
+ subject.error_formatter :txt, ApiSpec::CustomErrorFormatter
1553
1612
  subject.get '/exception' do
1554
1613
  fail 'rain!'
1555
1614
  end
@@ -1561,16 +1620,18 @@ describe Grape::API do
1561
1620
  describe 'with' do
1562
1621
  context 'class' do
1563
1622
  before :each do
1564
- class CustomErrorFormatter
1565
- def self.call(message, _backtrace, _option, _env)
1566
- "message: #{message} @backtrace"
1623
+ module ApiSpec
1624
+ class CustomErrorFormatter
1625
+ def self.call(message, _backtrace, _option, _env)
1626
+ "message: #{message} @backtrace"
1627
+ end
1567
1628
  end
1568
1629
  end
1569
1630
  end
1570
1631
 
1571
1632
  it 'returns a custom error format' do
1572
1633
  subject.rescue_from :all, backtrace: true
1573
- subject.error_formatter :txt, with: CustomErrorFormatter
1634
+ subject.error_formatter :txt, with: ApiSpec::CustomErrorFormatter
1574
1635
  subject.get('/exception') { fail 'rain!' }
1575
1636
 
1576
1637
  get '/exception'
@@ -1648,8 +1709,8 @@ describe Grape::API do
1648
1709
  describe '.formatter' do
1649
1710
  context 'multiple formatters' do
1650
1711
  before :each do
1651
- subject.formatter :json, ->(object, _env) { "{\"custom_formatter\":\"#{object[:some] }\"}" }
1652
- subject.formatter :txt, ->(object, _env) { "custom_formatter: #{object[:some] }" }
1712
+ subject.formatter :json, ->(object, _env) { "{\"custom_formatter\":\"#{object[:some]}\"}" }
1713
+ subject.formatter :txt, ->(object, _env) { "custom_formatter: #{object[:some]}" }
1653
1714
  subject.get :simple do
1654
1715
  { some: 'hash' }
1655
1716
  end
@@ -1667,7 +1728,7 @@ describe Grape::API do
1667
1728
  before :each do
1668
1729
  subject.content_type :json, 'application/json'
1669
1730
  subject.content_type :custom, 'application/custom'
1670
- subject.formatter :custom, ->(object, _env) { "{\"custom_formatter\":\"#{object[:some] }\"}" }
1731
+ subject.formatter :custom, ->(object, _env) { "{\"custom_formatter\":\"#{object[:some]}\"}" }
1671
1732
  subject.get :simple do
1672
1733
  { some: 'hash' }
1673
1734
  end
@@ -1682,15 +1743,17 @@ describe Grape::API do
1682
1743
  end
1683
1744
  end
1684
1745
  context 'custom formatter class' do
1685
- module CustomFormatter
1686
- def self.call(object, _env)
1687
- "{\"custom_formatter\":\"#{object[:some] }\"}"
1746
+ module ApiSpec
1747
+ module CustomFormatter
1748
+ def self.call(object, _env)
1749
+ "{\"custom_formatter\":\"#{object[:some]}\"}"
1750
+ end
1688
1751
  end
1689
1752
  end
1690
1753
  before :each do
1691
1754
  subject.content_type :json, 'application/json'
1692
1755
  subject.content_type :custom, 'application/custom'
1693
- subject.formatter :custom, CustomFormatter
1756
+ subject.formatter :custom, ApiSpec::CustomFormatter
1694
1757
  subject.get :simple do
1695
1758
  { some: 'hash' }
1696
1759
  end
@@ -1734,15 +1797,17 @@ describe Grape::API do
1734
1797
  end
1735
1798
  end
1736
1799
  context 'custom parser class' do
1737
- module CustomParser
1738
- def self.call(object, _env)
1739
- { object.to_sym => object.to_s.reverse }
1800
+ module ApiSpec
1801
+ module CustomParser
1802
+ def self.call(object, _env)
1803
+ { object.to_sym => object.to_s.reverse }
1804
+ end
1740
1805
  end
1741
1806
  end
1742
1807
  before :each do
1743
1808
  subject.content_type :txt, 'text/plain'
1744
1809
  subject.content_type :custom, 'text/custom'
1745
- subject.parser :custom, CustomParser
1810
+ subject.parser :custom, ApiSpec::CustomParser
1746
1811
  subject.put :simple do
1747
1812
  params[:simple]
1748
1813
  end
@@ -1767,7 +1832,7 @@ describe Grape::API do
1767
1832
  before :each do
1768
1833
  subject.parser :json, nil
1769
1834
  subject.put 'data' do
1770
- "body: #{env['api.request.body'] }"
1835
+ "body: #{env['api.request.body']}"
1771
1836
  end
1772
1837
  end
1773
1838
  it 'does not parse data' do
@@ -2123,6 +2188,38 @@ describe Grape::API do
2123
2188
  { description: 'Reverses a string.', params: { 's' => { desc: 'string to reverse', type: 'string' } } }
2124
2189
  ]
2125
2190
  end
2191
+ it 'does not inherit param descriptions in consequent namespaces' do
2192
+ subject.desc 'global description'
2193
+ subject.params do
2194
+ requires :param1
2195
+ optional :param2
2196
+ end
2197
+ subject.namespace 'ns1' do
2198
+ get do; end
2199
+ end
2200
+ subject.params do
2201
+ optional :param2
2202
+ end
2203
+ subject.namespace 'ns2' do
2204
+ get do; end
2205
+ end
2206
+ routes_doc = subject.routes.map { |route|
2207
+ { description: route.route_description, params: route.route_params }
2208
+ }
2209
+ expect(routes_doc).to eq [
2210
+ { description: 'global description',
2211
+ params: {
2212
+ 'param1' => { required: true },
2213
+ 'param2' => { required: false }
2214
+ }
2215
+ },
2216
+ { description: 'global description',
2217
+ params: {
2218
+ 'param2' => { required: false }
2219
+ }
2220
+ }
2221
+ ]
2222
+ end
2126
2223
  it 'merges the parameters of the namespace with the parameters of the method' do
2127
2224
  subject.desc 'namespace'
2128
2225
  subject.params do
@@ -2749,10 +2846,10 @@ XML
2749
2846
  end
2750
2847
  it 'hash' do
2751
2848
  subject.get '/example' do
2752
- ActiveSupport::OrderedHash[
2753
- :example1, 'example1',
2754
- :example2, 'example2'
2755
- ]
2849
+ {
2850
+ example1: 'example1',
2851
+ example2: 'example2'
2852
+ }
2756
2853
  end
2757
2854
  get '/example'
2758
2855
  expect(last_response.status).to eq(200)
@@ -2817,7 +2914,7 @@ XML
2817
2914
  [true, false].each do |anchor|
2818
2915
  it "anchor=#{anchor}" do
2819
2916
  subject.route :any, '*path', anchor: anchor do
2820
- error!("Unrecognized request path: #{params[:path] } - #{env['PATH_INFO'] }#{env['SCRIPT_NAME'] }", 404)
2917
+ error!("Unrecognized request path: #{params[:path]} - #{env['PATH_INFO']}#{env['SCRIPT_NAME']}", 404)
2821
2918
  end
2822
2919
  get '/v1/hello'
2823
2920
  expect(last_response.status).to eq(200)
@@ -70,6 +70,19 @@ module Grape
70
70
  expect(subject.namespace_setting(:description)).to eq(expected_options)
71
71
  expect(subject.route_setting(:description)).to eq(expected_options)
72
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
73
86
  end
74
87
  end
75
88
  end
@@ -6,13 +6,20 @@ module Grape
6
6
  class Dummy
7
7
  include Grape::DSL::Helpers
8
8
 
9
- # rubocop:disable TrivialAccessors
10
9
  def self.mod
11
10
  namespace_stackable(:helpers).first
12
11
  end
13
- # rubocop:enable TrivialAccessors
14
12
  end
15
13
  end
14
+
15
+ module BooleanParam
16
+ extend Grape::API::Helpers
17
+
18
+ params :requires_toggle_prm do
19
+ requires :toggle_prm, type: Boolean
20
+ end
21
+ end
22
+
16
23
  describe Helpers do
17
24
  subject { Class.new(HelpersSpec::Dummy) }
18
25
  let(:proc) do
@@ -41,6 +48,13 @@ module Grape
41
48
 
42
49
  expect(subject.mod).to eq mod
43
50
  end
51
+
52
+ context 'with an external file' do
53
+ it 'sets Boolean as a Virtus::Attribute::Boolean' do
54
+ subject.helpers BooleanParam
55
+ expect(subject.mod::Boolean).to eq Virtus::Attribute::Boolean
56
+ end
57
+ end
44
58
  end
45
59
  end
46
60
  end
@@ -201,8 +201,43 @@ module Grape
201
201
  subject.file 'file'
202
202
  end
203
203
 
204
- it 'returns value' do
205
- expect(subject.file).to eq 'file'
204
+ it 'returns value wrapped in FileResponse' do
205
+ expect(subject.file).to eq Grape::Util::FileResponse.new('file')
206
+ end
207
+ end
208
+
209
+ it 'returns default' do
210
+ expect(subject.file).to be nil
211
+ end
212
+ end
213
+
214
+ describe '#stream' do
215
+ describe 'set' do
216
+ before do
217
+ subject.header 'Cache-Control', 'cache'
218
+ subject.header 'Content-Length', 123
219
+ subject.header 'Transfer-Encoding', 'base64'
220
+ subject.stream 'file'
221
+ end
222
+
223
+ it 'returns value wrapped in FileResponse' do
224
+ expect(subject.stream).to eq Grape::Util::FileResponse.new('file')
225
+ end
226
+
227
+ it 'also sets result of file to value wrapped in FileResponse' do
228
+ expect(subject.file).to eq Grape::Util::FileResponse.new('file')
229
+ end
230
+
231
+ it 'sets Cache-Control header to no-cache' do
232
+ expect(subject.header['Cache-Control']).to eq 'no-cache'
233
+ end
234
+
235
+ it 'sets Content-Length header to nil' do
236
+ expect(subject.header['Content-Length']).to eq nil
237
+ end
238
+
239
+ it 'sets Transfer-Encoding header to nil' do
240
+ expect(subject.header['Transfer-Encoding']).to eq nil
206
241
  end
207
242
  end
208
243
 
@@ -308,8 +343,9 @@ module Grape
308
343
  describe '#declared' do
309
344
  # see endpoint_spec.rb#declared for spec coverage
310
345
 
311
- it 'returns an empty hash' do
312
- expect(subject.declared({})).to eq({})
346
+ it 'is not available by default' do
347
+ expect { subject.declared({}) }.to raise_error(
348
+ Grape::DSL::InsideRoute::MethodNotYetAvailable)
313
349
  end
314
350
  end
315
351
  end
@@ -11,31 +11,25 @@ module Grape
11
11
  @validate_attributes = *args
12
12
  end
13
13
 
14
- # rubocop:disable TrivialAccessors
15
14
  def validate_attributes_reader
16
15
  @validate_attributes
17
16
  end
18
- # rubocop:enable TrivialAccessors
19
17
 
20
18
  def push_declared_params(*args)
21
19
  @push_declared_params = args
22
20
  end
23
21
 
24
- # rubocop:disable TrivialAccessors
25
22
  def push_declared_params_reader
26
23
  @push_declared_params
27
24
  end
28
- # rubocop:enable TrivialAccessors
29
25
 
30
26
  def validates(*args)
31
27
  @validates = *args
32
28
  end
33
29
 
34
- # rubocop:disable TrivialAccessors
35
30
  def validates_reader
36
31
  @validates
37
32
  end
38
- # rubocop:enable TrivialAccessors
39
33
  end
40
34
  end
41
35
 
@@ -228,7 +228,7 @@ module Grape
228
228
  end
229
229
 
230
230
  let(:regex) { /(.*)/ }
231
- let!(:options) { { requirements: regex } }
231
+ let!(:options) { { requirements: regex } }
232
232
  it 'nests requirements option under param name' do
233
233
  expect(subject).to receive(:namespace) do |_param, options|
234
234
  expect(options[:requirements][:foo]).to eq regex
@@ -15,9 +15,15 @@ module Grape
15
15
  before do
16
16
  subject.namespace_stackable :declared_params, ['dummy']
17
17
  subject.namespace_stackable :validations, ['dummy']
18
+ subject.namespace_stackable :params, ['dummy']
19
+ subject.route_setting :description, description: 'lol', params: ['dummy']
18
20
  subject.reset_validations!
19
21
  end
20
22
 
23
+ after do
24
+ subject.unset_route_setting :description
25
+ end
26
+
21
27
  it 'resets declared params' do
22
28
  expect(subject.namespace_stackable(:declared_params)).to eq []
23
29
  end
@@ -25,6 +31,18 @@ module Grape
25
31
  it 'resets validations' do
26
32
  expect(subject.namespace_stackable(:validations)).to eq []
27
33
  end
34
+
35
+ it 'resets params' do
36
+ expect(subject.namespace_stackable(:params)).to eq []
37
+ end
38
+
39
+ it 'resets documentation params' do
40
+ expect(subject.route_setting(:description)[:params]).to be_nil
41
+ end
42
+
43
+ it 'does not reset documentation description' do
44
+ expect(subject.route_setting(:description)[:description]).to eq 'lol'
45
+ end
28
46
  end
29
47
 
30
48
  describe '.params' do