grape 1.3.3 → 1.6.2

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 (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