grape 1.3.3 → 1.6.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (165) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +111 -2
  3. data/CONTRIBUTING.md +2 -1
  4. data/README.md +135 -23
  5. data/UPGRADING.md +237 -46
  6. data/grape.gemspec +5 -5
  7. data/lib/grape/api/instance.rb +34 -42
  8. data/lib/grape/api.rb +21 -16
  9. data/lib/grape/cookies.rb +2 -0
  10. data/lib/grape/dsl/callbacks.rb +1 -1
  11. data/lib/grape/dsl/desc.rb +3 -5
  12. data/lib/grape/dsl/headers.rb +5 -2
  13. data/lib/grape/dsl/helpers.rb +8 -5
  14. data/lib/grape/dsl/inside_route.rb +72 -53
  15. data/lib/grape/dsl/middleware.rb +4 -4
  16. data/lib/grape/dsl/parameters.rb +11 -7
  17. data/lib/grape/dsl/request_response.rb +9 -6
  18. data/lib/grape/dsl/routing.rb +8 -9
  19. data/lib/grape/dsl/settings.rb +5 -5
  20. data/lib/grape/dsl/validations.rb +18 -1
  21. data/lib/grape/eager_load.rb +1 -1
  22. data/lib/grape/endpoint.rb +29 -42
  23. data/lib/grape/error_formatter/json.rb +2 -6
  24. data/lib/grape/error_formatter/xml.rb +2 -6
  25. data/lib/grape/exceptions/empty_message_body.rb +11 -0
  26. data/lib/grape/exceptions/validation.rb +2 -3
  27. data/lib/grape/exceptions/validation_errors.rb +1 -1
  28. data/lib/grape/formatter/json.rb +1 -0
  29. data/lib/grape/formatter/serializable_hash.rb +2 -1
  30. data/lib/grape/formatter/xml.rb +1 -0
  31. data/lib/grape/locale/en.yml +1 -1
  32. data/lib/grape/middleware/auth/base.rb +3 -3
  33. data/lib/grape/middleware/auth/dsl.rb +7 -1
  34. data/lib/grape/middleware/base.rb +6 -3
  35. data/lib/grape/middleware/error.rb +11 -13
  36. data/lib/grape/middleware/formatter.rb +7 -7
  37. data/lib/grape/middleware/stack.rb +10 -3
  38. data/lib/grape/middleware/versioner/accept_version_header.rb +3 -5
  39. data/lib/grape/middleware/versioner/header.rb +6 -4
  40. data/lib/grape/middleware/versioner/param.rb +1 -0
  41. data/lib/grape/middleware/versioner/parse_media_type_patch.rb +2 -1
  42. data/lib/grape/middleware/versioner/path.rb +2 -0
  43. data/lib/grape/parser/json.rb +1 -1
  44. data/lib/grape/parser/xml.rb +1 -1
  45. data/lib/grape/path.rb +1 -0
  46. data/lib/grape/request.rb +4 -1
  47. data/lib/grape/router/attribute_translator.rb +3 -3
  48. data/lib/grape/router/pattern.rb +1 -1
  49. data/lib/grape/router/route.rb +2 -2
  50. data/lib/grape/router.rb +31 -30
  51. data/lib/grape/{serve_file → serve_stream}/file_body.rb +1 -1
  52. data/lib/grape/{serve_file → serve_stream}/sendfile_response.rb +1 -1
  53. data/lib/grape/{serve_file/file_response.rb → serve_stream/stream_response.rb} +8 -8
  54. data/lib/grape/util/base_inheritable.rb +2 -2
  55. data/lib/grape/util/inheritable_setting.rb +1 -3
  56. data/lib/grape/util/lazy_value.rb +4 -2
  57. data/lib/grape/util/strict_hash_configuration.rb +1 -1
  58. data/lib/grape/validations/attributes_iterator.rb +8 -0
  59. data/lib/grape/validations/multiple_attributes_iterator.rb +1 -1
  60. data/lib/grape/validations/params_scope.rb +97 -62
  61. data/lib/grape/validations/single_attribute_iterator.rb +1 -1
  62. data/lib/grape/validations/types/custom_type_coercer.rb +16 -3
  63. data/lib/grape/validations/types/dry_type_coercer.rb +1 -1
  64. data/lib/grape/validations/types/invalid_value.rb +24 -0
  65. data/lib/grape/validations/types/json.rb +2 -1
  66. data/lib/grape/validations/types/primitive_coercer.rb +4 -5
  67. data/lib/grape/validations/types.rb +1 -4
  68. data/lib/grape/validations/validator_factory.rb +1 -1
  69. data/lib/grape/validations/validators/all_or_none.rb +8 -5
  70. data/lib/grape/validations/validators/allow_blank.rb +9 -7
  71. data/lib/grape/validations/validators/as.rb +6 -8
  72. data/lib/grape/validations/validators/at_least_one_of.rb +7 -4
  73. data/lib/grape/validations/validators/base.rb +74 -69
  74. data/lib/grape/validations/validators/coerce.rb +63 -76
  75. data/lib/grape/validations/validators/default.rb +36 -34
  76. data/lib/grape/validations/validators/exactly_one_of.rb +9 -6
  77. data/lib/grape/validations/validators/except_values.rb +13 -11
  78. data/lib/grape/validations/validators/multiple_params_base.rb +24 -19
  79. data/lib/grape/validations/validators/mutual_exclusion.rb +8 -5
  80. data/lib/grape/validations/validators/presence.rb +7 -4
  81. data/lib/grape/validations/validators/regexp.rb +8 -5
  82. data/lib/grape/validations/validators/same_as.rb +18 -15
  83. data/lib/grape/validations/validators/values.rb +61 -56
  84. data/lib/grape/validations.rb +6 -0
  85. data/lib/grape/version.rb +1 -1
  86. data/lib/grape.rb +7 -3
  87. data/spec/grape/api/custom_validations_spec.rb +77 -45
  88. data/spec/grape/api/deeply_included_options_spec.rb +3 -3
  89. data/spec/grape/api/defines_boolean_in_params_spec.rb +2 -1
  90. data/spec/grape/api/invalid_format_spec.rb +2 -0
  91. data/spec/grape/api/recognize_path_spec.rb +1 -1
  92. data/spec/grape/api/routes_with_requirements_spec.rb +8 -8
  93. data/spec/grape/api/shared_helpers_exactly_one_of_spec.rb +9 -15
  94. data/spec/grape/api_remount_spec.rb +25 -19
  95. data/spec/grape/api_spec.rb +576 -211
  96. data/spec/grape/dsl/callbacks_spec.rb +2 -1
  97. data/spec/grape/dsl/headers_spec.rb +39 -9
  98. data/spec/grape/dsl/helpers_spec.rb +3 -2
  99. data/spec/grape/dsl/inside_route_spec.rb +185 -34
  100. data/spec/grape/dsl/logger_spec.rb +16 -18
  101. data/spec/grape/dsl/middleware_spec.rb +2 -1
  102. data/spec/grape/dsl/parameters_spec.rb +2 -0
  103. data/spec/grape/dsl/request_response_spec.rb +1 -0
  104. data/spec/grape/dsl/routing_spec.rb +10 -7
  105. data/spec/grape/endpoint/declared_spec.rb +848 -0
  106. data/spec/grape/endpoint_spec.rb +77 -589
  107. data/spec/grape/entity_spec.rb +29 -23
  108. data/spec/grape/exceptions/body_parse_errors_spec.rb +3 -0
  109. data/spec/grape/exceptions/invalid_accept_header_spec.rb +61 -22
  110. data/spec/grape/exceptions/validation_errors_spec.rb +13 -10
  111. data/spec/grape/exceptions/validation_spec.rb +5 -3
  112. data/spec/grape/extensions/param_builders/hash_spec.rb +7 -7
  113. data/spec/grape/extensions/param_builders/hash_with_indifferent_access_spec.rb +8 -8
  114. data/spec/grape/extensions/param_builders/hashie/mash_spec.rb +8 -8
  115. data/spec/grape/integration/rack_sendfile_spec.rb +13 -9
  116. data/spec/grape/loading_spec.rb +8 -8
  117. data/spec/grape/middleware/auth/dsl_spec.rb +15 -6
  118. data/spec/grape/middleware/auth/strategies_spec.rb +61 -21
  119. data/spec/grape/middleware/base_spec.rb +24 -15
  120. data/spec/grape/middleware/error_spec.rb +3 -3
  121. data/spec/grape/middleware/exception_spec.rb +111 -161
  122. data/spec/grape/middleware/formatter_spec.rb +28 -7
  123. data/spec/grape/middleware/globals_spec.rb +7 -4
  124. data/spec/grape/middleware/stack_spec.rb +15 -12
  125. data/spec/grape/middleware/versioner/accept_version_header_spec.rb +2 -1
  126. data/spec/grape/middleware/versioner/header_spec.rb +14 -13
  127. data/spec/grape/middleware/versioner/param_spec.rb +7 -1
  128. data/spec/grape/middleware/versioner/path_spec.rb +5 -1
  129. data/spec/grape/middleware/versioner_spec.rb +1 -1
  130. data/spec/grape/parser_spec.rb +4 -0
  131. data/spec/grape/path_spec.rb +52 -52
  132. data/spec/grape/presenters/presenter_spec.rb +7 -6
  133. data/spec/grape/request_spec.rb +6 -4
  134. data/spec/grape/util/inheritable_setting_spec.rb +7 -7
  135. data/spec/grape/util/inheritable_values_spec.rb +3 -2
  136. data/spec/grape/util/reverse_stackable_values_spec.rb +3 -1
  137. data/spec/grape/util/stackable_values_spec.rb +7 -5
  138. data/spec/grape/validations/instance_behaivour_spec.rb +9 -10
  139. data/spec/grape/validations/multiple_attributes_iterator_spec.rb +14 -3
  140. data/spec/grape/validations/params_scope_spec.rb +72 -10
  141. data/spec/grape/validations/single_attribute_iterator_spec.rb +18 -6
  142. data/spec/grape/validations/types/primitive_coercer_spec.rb +63 -7
  143. data/spec/grape/validations/types_spec.rb +8 -8
  144. data/spec/grape/validations/validators/all_or_none_spec.rb +50 -56
  145. data/spec/grape/validations/validators/allow_blank_spec.rb +136 -140
  146. data/spec/grape/validations/validators/at_least_one_of_spec.rb +50 -56
  147. data/spec/grape/validations/validators/coerce_spec.rb +248 -33
  148. data/spec/grape/validations/validators/default_spec.rb +121 -78
  149. data/spec/grape/validations/validators/exactly_one_of_spec.rb +71 -77
  150. data/spec/grape/validations/validators/except_values_spec.rb +4 -3
  151. data/spec/grape/validations/validators/mutual_exclusion_spec.rb +71 -77
  152. data/spec/grape/validations/validators/presence_spec.rb +16 -1
  153. data/spec/grape/validations/validators/regexp_spec.rb +25 -31
  154. data/spec/grape/validations/validators/same_as_spec.rb +14 -20
  155. data/spec/grape/validations/validators/values_spec.rb +183 -178
  156. data/spec/grape/validations_spec.rb +342 -29
  157. data/spec/integration/eager_load/eager_load_spec.rb +15 -0
  158. data/spec/integration/multi_json/json_spec.rb +1 -1
  159. data/spec/integration/multi_xml/xml_spec.rb +1 -1
  160. data/spec/shared/versioning_examples.rb +32 -29
  161. data/spec/spec_helper.rb +12 -12
  162. data/spec/support/basic_auth_encode_helpers.rb +1 -1
  163. data/spec/support/chunks.rb +14 -0
  164. data/spec/support/versioned_helpers.rb +4 -6
  165. metadata +110 -102
@@ -9,6 +9,10 @@ describe Grape::Validations do
9
9
  subject
10
10
  end
11
11
 
12
+ def declared_params
13
+ subject.namespace_stackable(:declared_params).flatten
14
+ end
15
+
12
16
  describe 'params' do
13
17
  context 'optional' do
14
18
  before do
@@ -41,7 +45,7 @@ describe Grape::Validations do
41
45
  subject.params do
42
46
  optional :some_param
43
47
  end
44
- expect(subject.route_setting(:declared_params)).to eq([:some_param])
48
+ expect(declared_params).to eq([:some_param])
45
49
  end
46
50
  end
47
51
 
@@ -61,7 +65,7 @@ describe Grape::Validations do
61
65
 
62
66
  it 'adds entity documentation to declared params' do
63
67
  define_optional_using
64
- expect(subject.route_setting(:declared_params)).to eq(%i[field_a field_b])
68
+ expect(declared_params).to eq(%i[field_a field_b])
65
69
  end
66
70
 
67
71
  it 'works when field_a and field_b are not present' do
@@ -108,7 +112,7 @@ describe Grape::Validations do
108
112
  subject.params do
109
113
  requires :some_param
110
114
  end
111
- expect(subject.route_setting(:declared_params)).to eq([:some_param])
115
+ expect(declared_params).to eq([:some_param])
112
116
  end
113
117
 
114
118
  it 'works when required field is present but nil' do
@@ -193,7 +197,7 @@ describe Grape::Validations do
193
197
 
194
198
  it 'adds entity documentation to declared params' do
195
199
  define_requires_all
196
- expect(subject.route_setting(:declared_params)).to eq(%i[required_field optional_field])
200
+ expect(declared_params).to eq(%i[required_field optional_field])
197
201
  end
198
202
 
199
203
  it 'errors when required_field is not present' do
@@ -228,7 +232,7 @@ describe Grape::Validations do
228
232
 
229
233
  it 'adds entity documentation to declared params' do
230
234
  define_requires_none
231
- expect(subject.route_setting(:declared_params)).to eq(%i[required_field optional_field])
235
+ expect(declared_params).to eq(%i[required_field optional_field])
232
236
  end
233
237
 
234
238
  it 'errors when required_field is not present' do
@@ -258,7 +262,7 @@ describe Grape::Validations do
258
262
 
259
263
  it 'adds only the entity documentation to declared params, nothing more' do
260
264
  define_requires_all
261
- expect(subject.route_setting(:declared_params)).to eq(%i[required_field optional_field])
265
+ expect(declared_params).to eq(%i[required_field optional_field])
262
266
  end
263
267
  end
264
268
 
@@ -324,7 +328,7 @@ describe Grape::Validations do
324
328
  requires :key
325
329
  end
326
330
  end
327
- expect(subject.route_setting(:declared_params)).to eq([items: [:key]])
331
+ expect(declared_params).to eq([items: [:key]])
328
332
  end
329
333
  end
330
334
 
@@ -396,7 +400,7 @@ describe Grape::Validations do
396
400
  requires :key
397
401
  end
398
402
  end
399
- expect(subject.route_setting(:declared_params)).to eq([items: [:key]])
403
+ expect(declared_params).to eq([items: [:key]])
400
404
  end
401
405
  end
402
406
 
@@ -459,7 +463,7 @@ describe Grape::Validations do
459
463
  requires :key
460
464
  end
461
465
  end
462
- expect(subject.route_setting(:declared_params)).to eq([items: [:key]])
466
+ expect(declared_params).to eq([items: [:key]])
463
467
  end
464
468
  end
465
469
 
@@ -485,17 +489,24 @@ describe Grape::Validations do
485
489
  end
486
490
 
487
491
  context 'custom validator for a Hash' do
488
- module ValuesSpec
489
- module DateRangeValidations
490
- class DateRangeValidator < Grape::Validations::Base
491
- def validate_param!(attr_name, params)
492
- return if params[attr_name][:from] <= params[attr_name][:to]
493
- raise Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: "'from' must be lower or equal to 'to'")
494
- end
492
+ let(:date_range_validator) do
493
+ Class.new(Grape::Validations::Validators::Base) do
494
+ def validate_param!(attr_name, params)
495
+ return if params[attr_name][:from] <= params[attr_name][:to]
496
+
497
+ raise Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: "'from' must be lower or equal to 'to'")
495
498
  end
496
499
  end
497
500
  end
498
501
 
502
+ before do
503
+ described_class.register_validator('date_range', date_range_validator)
504
+ end
505
+
506
+ after do
507
+ described_class.deregister_validator('date_range')
508
+ end
509
+
499
510
  before do
500
511
  subject.params do
501
512
  optional :date_range, date_range: true, type: Hash do
@@ -813,7 +824,7 @@ describe Grape::Validations do
813
824
  requires :key
814
825
  end
815
826
  end
816
- expect(subject.route_setting(:declared_params)).to eq([items: [:key]])
827
+ expect(declared_params).to eq([items: [:key]])
817
828
  end
818
829
  end
819
830
 
@@ -877,7 +888,284 @@ describe Grape::Validations do
877
888
  requires(:required_subitems, type: Array) { requires :value }
878
889
  end
879
890
  end
880
- expect(subject.route_setting(:declared_params)).to eq([items: [:key, { optional_subitems: [:value] }, { required_subitems: [:value] }]])
891
+ expect(declared_params).to eq([items: [:key, { optional_subitems: [:value] }, { required_subitems: [:value] }]])
892
+ end
893
+
894
+ context <<~DESC do
895
+ Issue occurs whenever:
896
+ * param structure with at least three levels
897
+ * 1st level item is a required Array that has >1 entry with an optional item present and >1 entry with an optional item missing#{' '}
898
+ * 2nd level is an optional Array or Hash#{' '}
899
+ * 3rd level is a required item (can be any type)
900
+ * additional levels do not effect the issue from occuring
901
+ DESC
902
+
903
+ it 'example based off actual real world use case' do
904
+ subject.params do
905
+ requires :orders, type: Array do
906
+ requires :id, type: Integer
907
+ optional :drugs, type: Array do
908
+ requires :batches, type: Array do
909
+ requires :batch_no, type: String
910
+ end
911
+ end
912
+ end
913
+ end
914
+
915
+ subject.get '/validate_required_arrays_under_optional_arrays' do
916
+ 'validate_required_arrays_under_optional_arrays works!'
917
+ end
918
+
919
+ data = {
920
+ orders: [
921
+ { id: 77, drugs: [{ batches: [{ batch_no: 'A1234567' }] }] },
922
+ { id: 70 }
923
+ ]
924
+ }
925
+
926
+ get '/validate_required_arrays_under_optional_arrays', data
927
+ expect(last_response.body).to eq('validate_required_arrays_under_optional_arrays works!')
928
+ expect(last_response.status).to eq(200)
929
+ end
930
+
931
+ it 'simplest example using Array -> Array -> Hash -> String' do
932
+ subject.params do
933
+ requires :orders, type: Array do
934
+ requires :id, type: Integer
935
+ optional :drugs, type: Array do
936
+ requires :batch_no, type: String
937
+ end
938
+ end
939
+ end
940
+
941
+ subject.get '/validate_required_arrays_under_optional_arrays' do
942
+ 'validate_required_arrays_under_optional_arrays works!'
943
+ end
944
+
945
+ data = {
946
+ orders: [
947
+ { id: 77, drugs: [{ batch_no: 'A1234567' }] },
948
+ { id: 70 }
949
+ ]
950
+ }
951
+
952
+ get '/validate_required_arrays_under_optional_arrays', data
953
+ expect(last_response.body).to eq('validate_required_arrays_under_optional_arrays works!')
954
+ expect(last_response.status).to eq(200)
955
+ end
956
+
957
+ it 'simplest example using Array -> Hash -> String' do
958
+ subject.params do
959
+ requires :orders, type: Array do
960
+ requires :id, type: Integer
961
+ optional :drugs, type: Hash do
962
+ requires :batch_no, type: String
963
+ end
964
+ end
965
+ end
966
+
967
+ subject.get '/validate_required_arrays_under_optional_arrays' do
968
+ 'validate_required_arrays_under_optional_arrays works!'
969
+ end
970
+
971
+ data = {
972
+ orders: [
973
+ { id: 77, drugs: { batch_no: 'A1234567' } },
974
+ { id: 70 }
975
+ ]
976
+ }
977
+
978
+ get '/validate_required_arrays_under_optional_arrays', data
979
+ expect(last_response.body).to eq('validate_required_arrays_under_optional_arrays works!')
980
+ expect(last_response.status).to eq(200)
981
+ end
982
+
983
+ it 'correctly indexes invalida data' do
984
+ subject.params do
985
+ requires :orders, type: Array do
986
+ requires :id, type: Integer
987
+ optional :drugs, type: Array do
988
+ requires :batch_no, type: String
989
+ requires :quantity, type: Integer
990
+ end
991
+ end
992
+ end
993
+
994
+ subject.get '/correctly_indexes' do
995
+ 'correctly_indexes works!'
996
+ end
997
+
998
+ data = {
999
+ orders: [
1000
+ { id: 70 },
1001
+ { id: 77, drugs: [{ batch_no: 'A1234567', quantity: 12 }, { batch_no: 'B222222' }] }
1002
+ ]
1003
+ }
1004
+
1005
+ get '/correctly_indexes', data
1006
+ expect(last_response.body).to eq('orders[1][drugs][1][quantity] is missing')
1007
+ expect(last_response.status).to eq(400)
1008
+ end
1009
+
1010
+ context 'multiple levels of optional and requires settings' do
1011
+ before do
1012
+ subject.params do
1013
+ requires :top, type: Array do
1014
+ requires :top_id, type: Integer, allow_blank: false
1015
+ optional :middle_1, type: Array do
1016
+ requires :middle_1_id, type: Integer, allow_blank: false
1017
+ optional :middle_2, type: Array do
1018
+ requires :middle_2_id, type: String, allow_blank: false
1019
+ optional :bottom, type: Array do
1020
+ requires :bottom_id, type: Integer, allow_blank: false
1021
+ end
1022
+ end
1023
+ end
1024
+ end
1025
+ end
1026
+
1027
+ subject.get '/multi_level' do
1028
+ 'multi_level works!'
1029
+ end
1030
+ end
1031
+
1032
+ it 'with valid data' do
1033
+ data = {
1034
+ top: [
1035
+ { top_id: 1, middle_1: [
1036
+ { middle_1_id: 11 }, { middle_1_id: 12, middle_2: [
1037
+ { middle_2_id: 121 }, { middle_2_id: 122, bottom: [{ bottom_id: 1221 }] }
1038
+ ] }
1039
+ ] },
1040
+ { top_id: 2, middle_1: [
1041
+ { middle_1_id: 21 }, { middle_1_id: 22, middle_2: [
1042
+ { middle_2_id: 221 }
1043
+ ] }
1044
+ ] },
1045
+ { top_id: 3, middle_1: [
1046
+ { middle_1_id: 31 }, { middle_1_id: 32 }
1047
+ ] },
1048
+ { top_id: 4 }
1049
+ ]
1050
+ }
1051
+
1052
+ get '/multi_level', data
1053
+ expect(last_response.body).to eq('multi_level works!')
1054
+ expect(last_response.status).to eq(200)
1055
+ end
1056
+
1057
+ it 'with invalid data' do
1058
+ data = {
1059
+ top: [
1060
+ { top_id: 1, middle_1: [
1061
+ { middle_1_id: 11 }, { middle_1_id: 12, middle_2: [
1062
+ { middle_2_id: 121 }, { middle_2_id: 122, bottom: [{ bottom_id: nil }] }
1063
+ ] }
1064
+ ] },
1065
+ { top_id: 2, middle_1: [
1066
+ { middle_1_id: 21 }, { middle_1_id: 22, middle_2: [{ middle_2_id: nil }] }
1067
+ ] },
1068
+ { top_id: 3, middle_1: [
1069
+ { middle_1_id: nil }, { middle_1_id: 32 }
1070
+ ] },
1071
+ { top_id: nil, missing_top_id: 4 }
1072
+ ]
1073
+ }
1074
+ # debugger
1075
+ get '/multi_level', data
1076
+ expect(last_response.body.split(', ')).to match_array([
1077
+ 'top[3][top_id] is empty',
1078
+ 'top[2][middle_1][0][middle_1_id] is empty',
1079
+ 'top[1][middle_1][1][middle_2][0][middle_2_id] is empty',
1080
+ 'top[0][middle_1][1][middle_2][1][bottom][0][bottom_id] is empty'
1081
+ ])
1082
+ expect(last_response.status).to eq(400)
1083
+ end
1084
+ end
1085
+ end
1086
+
1087
+ it 'exactly_one_of' do
1088
+ subject.params do
1089
+ requires :orders, type: Array do
1090
+ requires :id, type: Integer
1091
+ optional :drugs, type: Hash do
1092
+ optional :batch_no, type: String
1093
+ optional :batch_id, type: String
1094
+ exactly_one_of :batch_no, :batch_id
1095
+ end
1096
+ end
1097
+ end
1098
+
1099
+ subject.get '/exactly_one_of' do
1100
+ 'exactly_one_of works!'
1101
+ end
1102
+
1103
+ data = {
1104
+ orders: [
1105
+ { id: 77, drugs: { batch_no: 'A1234567' } },
1106
+ { id: 70 }
1107
+ ]
1108
+ }
1109
+
1110
+ get '/exactly_one_of', data
1111
+ expect(last_response.body).to eq('exactly_one_of works!')
1112
+ expect(last_response.status).to eq(200)
1113
+ end
1114
+
1115
+ it 'at_least_one_of' do
1116
+ subject.params do
1117
+ requires :orders, type: Array do
1118
+ requires :id, type: Integer
1119
+ optional :drugs, type: Hash do
1120
+ optional :batch_no, type: String
1121
+ optional :batch_id, type: String
1122
+ at_least_one_of :batch_no, :batch_id
1123
+ end
1124
+ end
1125
+ end
1126
+
1127
+ subject.get '/at_least_one_of' do
1128
+ 'at_least_one_of works!'
1129
+ end
1130
+
1131
+ data = {
1132
+ orders: [
1133
+ { id: 77, drugs: { batch_no: 'A1234567' } },
1134
+ { id: 70 }
1135
+ ]
1136
+ }
1137
+
1138
+ get '/at_least_one_of', data
1139
+ expect(last_response.body).to eq('at_least_one_of works!')
1140
+ expect(last_response.status).to eq(200)
1141
+ end
1142
+
1143
+ it 'all_or_none_of' do
1144
+ subject.params do
1145
+ requires :orders, type: Array do
1146
+ requires :id, type: Integer
1147
+ optional :drugs, type: Hash do
1148
+ optional :batch_no, type: String
1149
+ optional :batch_id, type: String
1150
+ all_or_none_of :batch_no, :batch_id
1151
+ end
1152
+ end
1153
+ end
1154
+
1155
+ subject.get '/all_or_none_of' do
1156
+ 'all_or_none_of works!'
1157
+ end
1158
+
1159
+ data = {
1160
+ orders: [
1161
+ { id: 77, drugs: { batch_no: 'A1234567', batch_id: '12' } },
1162
+ { id: 70 }
1163
+ ]
1164
+ }
1165
+
1166
+ get '/all_or_none_of', data
1167
+ expect(last_response.body).to eq('all_or_none_of works!')
1168
+ expect(last_response.status).to eq(200)
881
1169
  end
882
1170
  end
883
1171
 
@@ -901,15 +1189,24 @@ describe Grape::Validations do
901
1189
  end
902
1190
 
903
1191
  context 'custom validation' do
904
- module CustomValidations
905
- class Customvalidator < Grape::Validations::Base
1192
+ let(:custom_validator) do
1193
+ Class.new(Grape::Validations::Validators::Base) do
906
1194
  def validate_param!(attr_name, params)
907
1195
  return if params[attr_name] == 'im custom'
1196
+
908
1197
  raise Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: 'is not custom!')
909
1198
  end
910
1199
  end
911
1200
  end
912
1201
 
1202
+ before do
1203
+ described_class.register_validator('customvalidator', custom_validator)
1204
+ end
1205
+
1206
+ after do
1207
+ described_class.deregister_validator('customvalidator')
1208
+ end
1209
+
913
1210
  context 'when using optional with a custom validator' do
914
1211
  before do
915
1212
  subject.params do
@@ -1049,15 +1346,24 @@ describe Grape::Validations do
1049
1346
  end
1050
1347
 
1051
1348
  context 'when using options on param' do
1052
- module CustomValidations
1053
- class CustomvalidatorWithOptions < Grape::Validations::Base
1349
+ let(:custom_validator_with_options) do
1350
+ Class.new(Grape::Validations::Validators::Base) do
1054
1351
  def validate_param!(attr_name, params)
1055
1352
  return if params[attr_name] == @option[:text]
1353
+
1056
1354
  raise Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: message)
1057
1355
  end
1058
1356
  end
1059
1357
  end
1060
1358
 
1359
+ before do
1360
+ described_class.register_validator('customvalidator_with_options', custom_validator_with_options)
1361
+ end
1362
+
1363
+ after do
1364
+ described_class.deregister_validator('customvalidator_with_options')
1365
+ end
1366
+
1061
1367
  before do
1062
1368
  subject.params do
1063
1369
  optional :custom, customvalidator_with_options: { text: 'im custom with options', message: 'is not custom with options!' }
@@ -1122,14 +1428,14 @@ describe Grape::Validations do
1122
1428
  subject.params do
1123
1429
  use :pagination
1124
1430
  end
1125
- expect(subject.route_setting(:declared_params)).to eq %i[page per_page]
1431
+ expect(declared_params).to eq %i[page per_page]
1126
1432
  end
1127
1433
 
1128
1434
  it 'by #use with multiple params' do
1129
1435
  subject.params do
1130
1436
  use :pagination, :period
1131
1437
  end
1132
- expect(subject.route_setting(:declared_params)).to eq %i[page per_page start_date end_date]
1438
+ expect(declared_params).to eq %i[page per_page start_date end_date]
1133
1439
  end
1134
1440
  end
1135
1441
 
@@ -1152,21 +1458,25 @@ describe Grape::Validations do
1152
1458
  }
1153
1459
  end
1154
1460
  end
1461
+
1155
1462
  it 'returns defaults' do
1156
1463
  get '/order'
1157
1464
  expect(last_response.status).to eq(200)
1158
1465
  expect(last_response.body).to eq({ order: :asc, order_by: :created_at }.to_json)
1159
1466
  end
1467
+
1160
1468
  it 'overrides default value for order' do
1161
1469
  get '/order?order=desc'
1162
1470
  expect(last_response.status).to eq(200)
1163
1471
  expect(last_response.body).to eq({ order: :desc, order_by: :created_at }.to_json)
1164
1472
  end
1473
+
1165
1474
  it 'overrides default value for order_by' do
1166
1475
  get '/order?order_by=name'
1167
1476
  expect(last_response.status).to eq(200)
1168
1477
  expect(last_response.body).to eq({ order: :asc, order_by: :name }.to_json)
1169
1478
  end
1479
+
1170
1480
  it 'fails with invalid value' do
1171
1481
  get '/order?order=invalid'
1172
1482
  expect(last_response.status).to eq(400)
@@ -1191,7 +1501,7 @@ describe Grape::Validations do
1191
1501
 
1192
1502
  context 'all or none' do
1193
1503
  context 'optional params' do
1194
- before :each do
1504
+ before do
1195
1505
  subject.resource :custom_message do
1196
1506
  params do
1197
1507
  optional :beer
@@ -1204,17 +1514,20 @@ describe Grape::Validations do
1204
1514
  end
1205
1515
  end
1206
1516
  end
1517
+
1207
1518
  context 'with a custom validation message' do
1208
1519
  it 'errors when any one is present' do
1209
1520
  get '/custom_message/all_or_none', beer: 'string'
1210
1521
  expect(last_response.status).to eq(400)
1211
1522
  expect(last_response.body).to eq 'beer, wine, juice all params are required or none is required'
1212
1523
  end
1524
+
1213
1525
  it 'works when all params are present' do
1214
1526
  get '/custom_message/all_or_none', beer: 'string', wine: 'anotherstring', juice: 'anotheranotherstring'
1215
1527
  expect(last_response.status).to eq(200)
1216
1528
  expect(last_response.body).to eq 'all_or_none works!'
1217
1529
  end
1530
+
1218
1531
  it 'works when none are present' do
1219
1532
  get '/custom_message/all_or_none'
1220
1533
  expect(last_response.status).to eq(200)
@@ -1378,7 +1691,7 @@ describe Grape::Validations do
1378
1691
 
1379
1692
  context 'exactly one of' do
1380
1693
  context 'params' do
1381
- before :each do
1694
+ before do
1382
1695
  subject.resources :custom_message do
1383
1696
  params do
1384
1697
  optional :beer
@@ -1442,7 +1755,7 @@ describe Grape::Validations do
1442
1755
  end
1443
1756
 
1444
1757
  context 'nested params' do
1445
- before :each do
1758
+ before do
1446
1759
  subject.params do
1447
1760
  requires :nested, type: Hash do
1448
1761
  optional :beer_nested
@@ -1484,7 +1797,7 @@ describe Grape::Validations do
1484
1797
 
1485
1798
  context 'at least one of' do
1486
1799
  context 'params' do
1487
- before :each do
1800
+ before do
1488
1801
  subject.resources :custom_message do
1489
1802
  params do
1490
1803
  optional :beer
@@ -1548,7 +1861,7 @@ describe Grape::Validations do
1548
1861
  end
1549
1862
 
1550
1863
  context 'nested params' do
1551
- before :each do
1864
+ before do
1552
1865
  subject.params do
1553
1866
  requires :nested, type: Hash do
1554
1867
  optional :beer
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', '..', 'lib'))
4
+ require 'grape'
5
+
6
+ describe Grape do
7
+ it 'eager_load!' do
8
+ require 'grape/eager_load'
9
+ expect { described_class.eager_load! }.not_to raise_error
10
+ end
11
+
12
+ it 'compile!' do
13
+ expect { Class.new(Grape::API).compile! }.not_to raise_error
14
+ end
15
+ end
@@ -4,6 +4,6 @@ require 'spec_helper'
4
4
 
5
5
  describe Grape::Json do
6
6
  it 'uses multi_json' do
7
- expect(Grape::Json).to eq(::MultiJson)
7
+ expect(described_class).to eq(::MultiJson)
8
8
  end
9
9
  end
@@ -4,6 +4,6 @@ require 'spec_helper'
4
4
 
5
5
  describe Grape::Xml do
6
6
  it 'uses multi_xml' do
7
- expect(Grape::Xml).to eq(::MultiXml)
7
+ expect(described_class).to eq(::MultiXml)
8
8
  end
9
9
  end