grape 1.1.0 → 1.5.3
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +278 -44
- data/LICENSE +1 -1
- data/README.md +514 -69
- data/UPGRADING.md +424 -17
- data/grape.gemspec +13 -2
- data/lib/grape.rb +104 -71
- data/lib/grape/api.rb +138 -175
- data/lib/grape/api/helpers.rb +2 -0
- data/lib/grape/api/instance.rb +283 -0
- data/lib/grape/config.rb +34 -0
- data/lib/grape/content_types.rb +34 -0
- data/lib/grape/cookies.rb +2 -0
- data/lib/grape/dsl/api.rb +2 -0
- data/lib/grape/dsl/callbacks.rb +22 -0
- data/lib/grape/dsl/configuration.rb +2 -0
- data/lib/grape/dsl/desc.rb +41 -7
- data/lib/grape/dsl/headers.rb +2 -0
- data/lib/grape/dsl/helpers.rb +5 -2
- data/lib/grape/dsl/inside_route.rb +92 -49
- data/lib/grape/dsl/logger.rb +2 -0
- data/lib/grape/dsl/middleware.rb +9 -0
- data/lib/grape/dsl/parameters.rb +25 -14
- data/lib/grape/dsl/request_response.rb +4 -2
- data/lib/grape/dsl/routing.rb +17 -10
- data/lib/grape/dsl/settings.rb +7 -1
- data/lib/grape/dsl/validations.rb +24 -4
- data/lib/grape/eager_load.rb +20 -0
- data/lib/grape/endpoint.rb +59 -35
- data/lib/grape/error_formatter.rb +4 -2
- data/lib/grape/error_formatter/base.rb +2 -0
- data/lib/grape/error_formatter/json.rb +2 -0
- data/lib/grape/error_formatter/txt.rb +2 -0
- data/lib/grape/error_formatter/xml.rb +2 -0
- data/lib/grape/exceptions/base.rb +20 -14
- data/lib/grape/exceptions/empty_message_body.rb +11 -0
- data/lib/grape/exceptions/incompatible_option_values.rb +2 -0
- data/lib/grape/exceptions/invalid_accept_header.rb +2 -0
- data/lib/grape/exceptions/invalid_formatter.rb +2 -0
- data/lib/grape/exceptions/invalid_message_body.rb +2 -0
- data/lib/grape/exceptions/invalid_response.rb +11 -0
- data/lib/grape/exceptions/invalid_version_header.rb +2 -0
- data/lib/grape/exceptions/invalid_versioner_option.rb +2 -0
- data/lib/grape/exceptions/invalid_with_option_for_represent.rb +2 -0
- data/lib/grape/exceptions/method_not_allowed.rb +2 -0
- data/lib/grape/exceptions/missing_group_type.rb +2 -0
- data/lib/grape/exceptions/missing_mime_type.rb +2 -0
- data/lib/grape/exceptions/missing_option.rb +2 -0
- data/lib/grape/exceptions/missing_vendor_option.rb +2 -0
- data/lib/grape/exceptions/unknown_options.rb +2 -0
- data/lib/grape/exceptions/unknown_parameter.rb +2 -0
- data/lib/grape/exceptions/unknown_validator.rb +2 -0
- data/lib/grape/exceptions/unsupported_group_type.rb +2 -0
- data/lib/grape/exceptions/validation.rb +4 -2
- data/lib/grape/exceptions/validation_array_errors.rb +2 -0
- data/lib/grape/exceptions/validation_errors.rb +16 -13
- data/lib/grape/extensions/active_support/hash_with_indifferent_access.rb +4 -3
- data/lib/grape/extensions/deep_mergeable_hash.rb +2 -0
- data/lib/grape/extensions/deep_symbolize_hash.rb +2 -0
- data/lib/grape/extensions/hash.rb +2 -0
- data/lib/grape/extensions/hashie/mash.rb +2 -0
- data/lib/grape/formatter.rb +5 -3
- data/lib/grape/formatter/json.rb +2 -0
- data/lib/grape/formatter/serializable_hash.rb +2 -0
- data/lib/grape/formatter/txt.rb +2 -0
- data/lib/grape/formatter/xml.rb +2 -0
- data/lib/grape/http/headers.rb +50 -18
- data/lib/grape/locale/en.yml +3 -1
- data/lib/grape/middleware/auth/base.rb +7 -7
- data/lib/grape/middleware/auth/dsl.rb +2 -0
- data/lib/grape/middleware/auth/strategies.rb +2 -0
- data/lib/grape/middleware/auth/strategy_info.rb +2 -0
- data/lib/grape/middleware/base.rb +10 -7
- data/lib/grape/middleware/error.rb +21 -16
- data/lib/grape/middleware/filter.rb +2 -0
- data/lib/grape/middleware/formatter.rb +8 -6
- data/lib/grape/middleware/globals.rb +2 -0
- data/lib/grape/middleware/helpers.rb +12 -0
- data/lib/grape/middleware/stack.rb +13 -3
- data/lib/grape/middleware/versioner.rb +2 -0
- data/lib/grape/middleware/versioner/accept_version_header.rb +2 -0
- data/lib/grape/middleware/versioner/header.rb +10 -8
- data/lib/grape/middleware/versioner/param.rb +3 -1
- data/lib/grape/middleware/versioner/parse_media_type_patch.rb +4 -1
- data/lib/grape/middleware/versioner/path.rb +3 -1
- data/lib/grape/namespace.rb +14 -2
- data/lib/grape/parser.rb +4 -2
- data/lib/grape/parser/json.rb +3 -1
- data/lib/grape/parser/xml.rb +3 -1
- data/lib/grape/path.rb +15 -3
- data/lib/grape/presenters/presenter.rb +2 -0
- data/lib/grape/request.rb +19 -10
- data/lib/grape/router.rb +30 -29
- data/lib/grape/router/attribute_translator.rb +41 -8
- data/lib/grape/router/pattern.rb +20 -16
- data/lib/grape/router/route.rb +14 -28
- data/lib/grape/{serve_file → serve_stream}/file_body.rb +3 -1
- data/lib/grape/{serve_file → serve_stream}/sendfile_response.rb +3 -1
- data/lib/grape/{serve_file/file_response.rb → serve_stream/stream_response.rb} +10 -8
- data/lib/grape/util/base_inheritable.rb +43 -0
- data/lib/grape/util/cache.rb +20 -0
- data/lib/grape/util/endpoint_configuration.rb +8 -0
- data/lib/grape/util/env.rb +19 -17
- data/lib/grape/util/inheritable_setting.rb +2 -0
- data/lib/grape/util/inheritable_values.rb +7 -25
- data/lib/grape/util/json.rb +2 -0
- data/lib/grape/util/lazy_block.rb +27 -0
- data/lib/grape/util/lazy_object.rb +43 -0
- data/lib/grape/util/lazy_value.rb +98 -0
- data/lib/grape/util/registrable.rb +2 -0
- data/lib/grape/util/reverse_stackable_values.rb +10 -35
- data/lib/grape/util/stackable_values.rb +21 -34
- data/lib/grape/util/strict_hash_configuration.rb +2 -0
- data/lib/grape/util/xml.rb +2 -0
- data/lib/grape/validations.rb +2 -0
- data/lib/grape/validations/attributes_iterator.rb +16 -6
- data/lib/grape/validations/multiple_attributes_iterator.rb +13 -0
- data/lib/grape/validations/params_scope.rb +51 -30
- data/lib/grape/validations/single_attribute_iterator.rb +24 -0
- data/lib/grape/validations/types.rb +13 -38
- data/lib/grape/validations/types/array_coercer.rb +65 -0
- data/lib/grape/validations/types/build_coercer.rb +47 -49
- data/lib/grape/validations/types/custom_type_coercer.rb +29 -51
- data/lib/grape/validations/types/custom_type_collection_coercer.rb +10 -25
- data/lib/grape/validations/types/dry_type_coercer.rb +76 -0
- data/lib/grape/validations/types/file.rb +22 -18
- data/lib/grape/validations/types/invalid_value.rb +24 -0
- data/lib/grape/validations/types/json.rb +46 -39
- data/lib/grape/validations/types/multiple_type_coercer.rb +14 -33
- data/lib/grape/validations/types/primitive_coercer.rb +67 -0
- data/lib/grape/validations/types/set_coercer.rb +40 -0
- data/lib/grape/validations/types/variant_collection_coercer.rb +5 -13
- data/lib/grape/validations/validator_factory.rb +8 -11
- data/lib/grape/validations/validators/all_or_none.rb +8 -13
- data/lib/grape/validations/validators/allow_blank.rb +3 -1
- data/lib/grape/validations/validators/as.rb +5 -4
- data/lib/grape/validations/validators/at_least_one_of.rb +7 -13
- data/lib/grape/validations/validators/base.rb +20 -16
- data/lib/grape/validations/validators/coerce.rb +46 -29
- data/lib/grape/validations/validators/default.rb +6 -6
- data/lib/grape/validations/validators/exactly_one_of.rb +10 -23
- data/lib/grape/validations/validators/except_values.rb +4 -2
- data/lib/grape/validations/validators/multiple_params_base.rb +17 -10
- data/lib/grape/validations/validators/mutual_exclusion.rb +8 -18
- data/lib/grape/validations/validators/presence.rb +3 -1
- data/lib/grape/validations/validators/regexp.rb +4 -2
- data/lib/grape/validations/validators/same_as.rb +26 -0
- data/lib/grape/validations/validators/values.rb +18 -6
- data/lib/grape/version.rb +3 -1
- data/spec/grape/api/custom_validations_spec.rb +5 -3
- data/spec/grape/api/deeply_included_options_spec.rb +2 -0
- data/spec/grape/api/defines_boolean_in_params_spec.rb +39 -0
- data/spec/grape/api/inherited_helpers_spec.rb +2 -0
- data/spec/grape/api/instance_spec.rb +104 -0
- data/spec/grape/api/invalid_format_spec.rb +2 -0
- data/spec/grape/api/namespace_parameters_in_route_spec.rb +2 -0
- data/spec/grape/api/nested_helpers_spec.rb +2 -0
- data/spec/grape/api/optional_parameters_in_route_spec.rb +2 -0
- data/spec/grape/api/parameters_modification_spec.rb +3 -1
- data/spec/grape/api/patch_method_helpers_spec.rb +2 -0
- data/spec/grape/api/recognize_path_spec.rb +2 -0
- data/spec/grape/api/required_parameters_in_route_spec.rb +2 -0
- data/spec/grape/api/required_parameters_with_invalid_method_spec.rb +2 -0
- data/spec/grape/api/routes_with_requirements_spec.rb +61 -0
- data/spec/grape/api/shared_helpers_exactly_one_of_spec.rb +2 -0
- data/spec/grape/api/shared_helpers_spec.rb +2 -0
- data/spec/grape/api_remount_spec.rb +473 -0
- data/spec/grape/api_spec.rb +565 -12
- data/spec/grape/config_spec.rb +19 -0
- data/spec/grape/dsl/callbacks_spec.rb +2 -0
- data/spec/grape/dsl/configuration_spec.rb +2 -0
- data/spec/grape/dsl/desc_spec.rb +42 -16
- data/spec/grape/dsl/headers_spec.rb +2 -0
- data/spec/grape/dsl/helpers_spec.rb +4 -2
- data/spec/grape/dsl/inside_route_spec.rb +184 -33
- data/spec/grape/dsl/logger_spec.rb +2 -0
- data/spec/grape/dsl/middleware_spec.rb +10 -0
- data/spec/grape/dsl/parameters_spec.rb +2 -0
- data/spec/grape/dsl/request_response_spec.rb +2 -0
- data/spec/grape/dsl/routing_spec.rb +12 -0
- data/spec/grape/dsl/settings_spec.rb +2 -0
- data/spec/grape/dsl/validations_spec.rb +2 -0
- data/spec/grape/endpoint/declared_spec.rb +601 -0
- data/spec/grape/endpoint_spec.rb +53 -523
- data/spec/grape/entity_spec.rb +9 -1
- data/spec/grape/exceptions/base_spec.rb +67 -0
- data/spec/grape/exceptions/body_parse_errors_spec.rb +2 -0
- data/spec/grape/exceptions/invalid_accept_header_spec.rb +2 -0
- data/spec/grape/exceptions/invalid_formatter_spec.rb +2 -0
- data/spec/grape/exceptions/invalid_response_spec.rb +13 -0
- data/spec/grape/exceptions/invalid_versioner_option_spec.rb +2 -0
- data/spec/grape/exceptions/missing_mime_type_spec.rb +2 -0
- data/spec/grape/exceptions/missing_option_spec.rb +2 -0
- data/spec/grape/exceptions/unknown_options_spec.rb +2 -0
- data/spec/grape/exceptions/unknown_validator_spec.rb +2 -0
- data/spec/grape/exceptions/validation_errors_spec.rb +8 -4
- data/spec/grape/exceptions/validation_spec.rb +3 -1
- data/spec/grape/extensions/param_builders/hash_spec.rb +2 -0
- data/spec/grape/extensions/param_builders/hash_with_indifferent_access_spec.rb +2 -0
- data/spec/grape/extensions/param_builders/hashie/mash_spec.rb +2 -0
- data/spec/grape/integration/global_namespace_function_spec.rb +2 -0
- data/spec/grape/integration/rack_sendfile_spec.rb +14 -8
- data/spec/grape/integration/rack_spec.rb +25 -7
- data/spec/grape/loading_spec.rb +2 -0
- data/spec/grape/middleware/auth/base_spec.rb +2 -0
- data/spec/grape/middleware/auth/dsl_spec.rb +5 -3
- data/spec/grape/middleware/auth/strategies_spec.rb +3 -1
- data/spec/grape/middleware/base_spec.rb +10 -0
- data/spec/grape/middleware/error_spec.rb +3 -1
- data/spec/grape/middleware/exception_spec.rb +4 -2
- data/spec/grape/middleware/formatter_spec.rb +33 -16
- data/spec/grape/middleware/globals_spec.rb +2 -0
- data/spec/grape/middleware/stack_spec.rb +12 -0
- data/spec/grape/middleware/versioner/accept_version_header_spec.rb +3 -1
- data/spec/grape/middleware/versioner/header_spec.rb +9 -1
- data/spec/grape/middleware/versioner/param_spec.rb +3 -1
- data/spec/grape/middleware/versioner/path_spec.rb +3 -1
- data/spec/grape/middleware/versioner_spec.rb +2 -0
- data/spec/grape/named_api_spec.rb +21 -0
- data/spec/grape/parser_spec.rb +7 -5
- data/spec/grape/path_spec.rb +6 -4
- data/spec/grape/presenters/presenter_spec.rb +2 -0
- data/spec/grape/request_spec.rb +26 -0
- data/spec/grape/util/inheritable_setting_spec.rb +2 -0
- data/spec/grape/util/inheritable_values_spec.rb +2 -0
- data/spec/grape/util/reverse_stackable_values_spec.rb +2 -0
- data/spec/grape/util/stackable_values_spec.rb +3 -1
- data/spec/grape/util/strict_hash_configuration_spec.rb +2 -0
- data/spec/grape/validations/attributes_iterator_spec.rb +2 -0
- data/spec/grape/validations/instance_behaivour_spec.rb +5 -3
- data/spec/grape/validations/multiple_attributes_iterator_spec.rb +41 -0
- data/spec/grape/validations/params_scope_spec.rb +213 -9
- data/spec/grape/validations/single_attribute_iterator_spec.rb +58 -0
- data/spec/grape/validations/types/array_coercer_spec.rb +35 -0
- data/spec/grape/validations/types/primitive_coercer_spec.rb +135 -0
- data/spec/grape/validations/types/set_coercer_spec.rb +34 -0
- data/spec/grape/validations/types_spec.rb +9 -36
- data/spec/grape/validations/validators/all_or_none_spec.rb +140 -30
- data/spec/grape/validations/validators/allow_blank_spec.rb +2 -0
- data/spec/grape/validations/validators/at_least_one_of_spec.rb +175 -29
- data/spec/grape/validations/validators/coerce_spec.rb +476 -135
- data/spec/grape/validations/validators/default_spec.rb +172 -0
- data/spec/grape/validations/validators/exactly_one_of_spec.rb +204 -38
- data/spec/grape/validations/validators/except_values_spec.rb +4 -1
- data/spec/grape/validations/validators/mutual_exclusion_spec.rb +186 -27
- data/spec/grape/validations/validators/presence_spec.rb +30 -0
- data/spec/grape/validations/validators/regexp_spec.rb +2 -0
- data/spec/grape/validations/validators/same_as_spec.rb +65 -0
- data/spec/grape/validations/validators/values_spec.rb +30 -5
- data/spec/grape/validations_spec.rb +388 -50
- data/spec/integration/eager_load/eager_load_spec.rb +15 -0
- data/spec/integration/multi_json/json_spec.rb +2 -0
- data/spec/integration/multi_xml/xml_spec.rb +2 -0
- data/spec/shared/versioning_examples.rb +22 -20
- data/spec/spec_helper.rb +12 -1
- data/spec/support/basic_auth_encode_helpers.rb +2 -0
- data/spec/support/chunks.rb +14 -0
- data/spec/support/content_type_helpers.rb +2 -0
- data/spec/support/eager_load.rb +19 -0
- data/spec/support/endpoint_faker.rb +2 -0
- data/spec/support/file_streamer.rb +2 -0
- data/spec/support/integer_helpers.rb +2 -0
- data/spec/support/versioned_helpers.rb +8 -8
- metadata +86 -48
- data/Appraisals +0 -32
- data/Dangerfile +0 -2
- data/Gemfile +0 -33
- data/Gemfile.lock +0 -231
- data/Guardfile +0 -10
- data/RELEASING.md +0 -111
- data/Rakefile +0 -25
- data/benchmark/simple.rb +0 -27
- data/benchmark/simple_with_type_coercer.rb +0 -22
- data/gemfiles/multi_json.gemfile +0 -35
- data/gemfiles/multi_xml.gemfile +0 -35
- data/gemfiles/rack_1.5.2.gemfile +0 -35
- data/gemfiles/rack_edge.gemfile +0 -35
- data/gemfiles/rails_3.gemfile +0 -36
- data/gemfiles/rails_4.gemfile +0 -35
- data/gemfiles/rails_5.gemfile +0 -35
- data/gemfiles/rails_edge.gemfile +0 -35
- data/lib/grape/extensions/deep_hash_with_indifferent_access.rb +0 -18
- data/lib/grape/util/content_types.rb +0 -26
- data/lib/grape/validations/types/virtus_collection_patch.rb +0 -16
- data/pkg/grape-0.17.0.gem +0 -0
- data/pkg/grape-0.19.0.gem +0 -0
data/spec/grape/api_spec.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
require 'shared/versioning_examples'
|
3
5
|
|
@@ -220,6 +222,15 @@ describe Grape::API do
|
|
220
222
|
end
|
221
223
|
end
|
222
224
|
|
225
|
+
describe '.call' do
|
226
|
+
context 'it does not add to the app setup' do
|
227
|
+
it 'calls the app' do
|
228
|
+
expect(subject).not_to receive(:add_setup)
|
229
|
+
subject.call({})
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
223
234
|
describe '.route_param' do
|
224
235
|
it 'adds a parameterized route segment namespace' do
|
225
236
|
subject.namespace :users do
|
@@ -805,6 +816,71 @@ XML
|
|
805
816
|
end
|
806
817
|
end
|
807
818
|
|
819
|
+
describe 'when hook behaviour is controlled by attributes on the route ' do
|
820
|
+
before do
|
821
|
+
subject.before do
|
822
|
+
error!('Access Denied', 401) unless route.options[:secret] == params[:secret]
|
823
|
+
end
|
824
|
+
|
825
|
+
subject.namespace 'example' do
|
826
|
+
before do
|
827
|
+
error!('Access Denied', 401) unless route.options[:namespace_secret] == params[:namespace_secret]
|
828
|
+
end
|
829
|
+
|
830
|
+
desc 'it gets with secret', secret: 'password'
|
831
|
+
get { status(params[:id] == '504' ? 200 : 404) }
|
832
|
+
|
833
|
+
desc 'it post with secret', secret: 'password', namespace_secret: 'namespace_password'
|
834
|
+
post {}
|
835
|
+
end
|
836
|
+
end
|
837
|
+
|
838
|
+
context 'when HTTP method is not defined' do
|
839
|
+
let(:response) { delete('/example') }
|
840
|
+
|
841
|
+
it 'responds with a 405 status' do
|
842
|
+
expect(response.status).to eql 405
|
843
|
+
end
|
844
|
+
end
|
845
|
+
|
846
|
+
context 'when HTTP method is defined with attribute' do
|
847
|
+
let(:response) { post('/example?secret=incorrect_password') }
|
848
|
+
it 'responds with the defined error in the before hook' do
|
849
|
+
expect(response.status).to eql 401
|
850
|
+
end
|
851
|
+
end
|
852
|
+
|
853
|
+
context 'when HTTP method is defined and the underlying before hook expectation is not met' do
|
854
|
+
let(:response) { post('/example?secret=password&namespace_secret=wrong_namespace_password') }
|
855
|
+
it 'ends up in the endpoint' do
|
856
|
+
expect(response.status).to eql 401
|
857
|
+
end
|
858
|
+
end
|
859
|
+
|
860
|
+
context 'when HTTP method is defined and everything is like the before hooks expect' do
|
861
|
+
let(:response) { post('/example?secret=password&namespace_secret=namespace_password') }
|
862
|
+
it 'ends up in the endpoint' do
|
863
|
+
expect(response.status).to eql 201
|
864
|
+
end
|
865
|
+
end
|
866
|
+
|
867
|
+
context 'when HEAD is called for the defined GET' do
|
868
|
+
let(:response) { head('/example?id=504') }
|
869
|
+
|
870
|
+
it 'responds with 401 because before expectations in before hooks are not met' do
|
871
|
+
expect(response.status).to eql 401
|
872
|
+
end
|
873
|
+
end
|
874
|
+
|
875
|
+
context 'when HEAD is called for the defined GET' do
|
876
|
+
let(:response) { head('/example?id=504&secret=password') }
|
877
|
+
|
878
|
+
it 'responds with 200 because before hooks are not called' do
|
879
|
+
expect(response.status).to eql 200
|
880
|
+
end
|
881
|
+
end
|
882
|
+
end
|
883
|
+
|
808
884
|
context 'allows HEAD on a GET request that' do
|
809
885
|
before do
|
810
886
|
subject.get 'example' do
|
@@ -876,6 +952,40 @@ XML
|
|
876
952
|
end
|
877
953
|
end
|
878
954
|
|
955
|
+
describe '.compile!' do
|
956
|
+
it 'requires the grape/eager_load file' do
|
957
|
+
expect(app).to receive(:require).with('grape/eager_load') { nil }
|
958
|
+
app.compile!
|
959
|
+
end
|
960
|
+
|
961
|
+
it 'compiles the instance for rack!' do
|
962
|
+
stubbed_object = double(:instance_for_rack)
|
963
|
+
allow(app).to receive(:instance_for_rack) { stubbed_object }
|
964
|
+
end
|
965
|
+
end
|
966
|
+
|
967
|
+
# NOTE: this method is required to preserve the ability of pre-mounting
|
968
|
+
# the root API into a namespace, it may be deprecated in the future.
|
969
|
+
describe 'instance_for_rack' do
|
970
|
+
context 'when the app was not mounted' do
|
971
|
+
it 'returns the base_instance' do
|
972
|
+
expect(app.send(:instance_for_rack)).to eq app.base_instance
|
973
|
+
end
|
974
|
+
end
|
975
|
+
|
976
|
+
context 'when the app was mounted' do
|
977
|
+
it 'returns the first mounted instance' do
|
978
|
+
mounted_app = app
|
979
|
+
Class.new(Grape::API) do
|
980
|
+
namespace 'new_namespace' do
|
981
|
+
mount mounted_app
|
982
|
+
end
|
983
|
+
end
|
984
|
+
expect(app.send(:instance_for_rack)).to eq app.send(:mounted_instances).first
|
985
|
+
end
|
986
|
+
end
|
987
|
+
end
|
988
|
+
|
879
989
|
describe 'filters' do
|
880
990
|
it 'adds a before filter' do
|
881
991
|
subject.before { @foo = 'first' }
|
@@ -890,7 +1000,7 @@ XML
|
|
890
1000
|
|
891
1001
|
it 'adds a before filter to current and child namespaces only' do
|
892
1002
|
subject.get '/' do
|
893
|
-
"root - #{@foo}"
|
1003
|
+
"root - #{instance_variable_defined?(:@foo) ? @foo : nil}"
|
894
1004
|
end
|
895
1005
|
subject.namespace :blah do
|
896
1006
|
before { @foo = 'foo' }
|
@@ -1039,6 +1149,11 @@ XML
|
|
1039
1149
|
expect(last_response.headers['Content-Type']).to eq('text/plain')
|
1040
1150
|
end
|
1041
1151
|
|
1152
|
+
it 'does not set Cache-Control' do
|
1153
|
+
get '/foo'
|
1154
|
+
expect(last_response.headers['Cache-Control']).to eq(nil)
|
1155
|
+
end
|
1156
|
+
|
1042
1157
|
it 'sets content type for xml' do
|
1043
1158
|
get '/foo.xml'
|
1044
1159
|
expect(last_response.headers['Content-Type']).to eq('application/xml')
|
@@ -1090,7 +1205,7 @@ XML
|
|
1090
1205
|
|
1091
1206
|
subject.use Rack::Chunked
|
1092
1207
|
subject.get('/stream') { stream test_stream }
|
1093
|
-
get '/stream', {}, 'HTTP_VERSION' => 'HTTP/1.1'
|
1208
|
+
get '/stream', {}, 'HTTP_VERSION' => 'HTTP/1.1', 'SERVER_PROTOCOL' => 'HTTP/1.1'
|
1094
1209
|
|
1095
1210
|
expect(last_response.headers['Content-Type']).to eq('text/plain')
|
1096
1211
|
expect(last_response.headers['Content-Length']).to eq(nil)
|
@@ -1348,6 +1463,28 @@ XML
|
|
1348
1463
|
end
|
1349
1464
|
end
|
1350
1465
|
|
1466
|
+
describe '.insert' do
|
1467
|
+
it 'inserts middleware in a specific location in the stack' do
|
1468
|
+
m = Class.new(Grape::Middleware::Base) do
|
1469
|
+
def call(env)
|
1470
|
+
env['phony.args'] ||= []
|
1471
|
+
env['phony.args'] << @options[:message]
|
1472
|
+
@app.call(env)
|
1473
|
+
end
|
1474
|
+
end
|
1475
|
+
|
1476
|
+
subject.use ApiSpec::PhonyMiddleware, 'bye'
|
1477
|
+
subject.insert 0, m, message: 'good'
|
1478
|
+
subject.insert 0, m, message: 'hello'
|
1479
|
+
subject.get '/' do
|
1480
|
+
env['phony.args'].join(' ')
|
1481
|
+
end
|
1482
|
+
|
1483
|
+
get '/'
|
1484
|
+
expect(last_response.body).to eql 'hello good bye'
|
1485
|
+
end
|
1486
|
+
end
|
1487
|
+
|
1351
1488
|
describe '.http_basic' do
|
1352
1489
|
it 'protects any resources on the same scope' do
|
1353
1490
|
subject.http_basic do |u, _p|
|
@@ -1463,6 +1600,11 @@ XML
|
|
1463
1600
|
expect(subject.io).to receive(:write).with(message)
|
1464
1601
|
subject.logger.info 'this will be logged'
|
1465
1602
|
end
|
1603
|
+
|
1604
|
+
it 'does not unnecessarily retain duplicate setup blocks' do
|
1605
|
+
subject.logger
|
1606
|
+
expect { subject.logger }.to_not change(subject.instance_variable_get(:@setup), :size)
|
1607
|
+
end
|
1466
1608
|
end
|
1467
1609
|
|
1468
1610
|
describe '.helpers' do
|
@@ -1597,6 +1739,199 @@ XML
|
|
1597
1739
|
end
|
1598
1740
|
end
|
1599
1741
|
|
1742
|
+
describe 'lifecycle' do
|
1743
|
+
let!(:lifecycle) { [] }
|
1744
|
+
let!(:standard_cycle) do
|
1745
|
+
%i[before before_validation after_validation api_call after finally]
|
1746
|
+
end
|
1747
|
+
|
1748
|
+
let!(:validation_error) do
|
1749
|
+
%i[before before_validation finally]
|
1750
|
+
end
|
1751
|
+
|
1752
|
+
let!(:errored_cycle) do
|
1753
|
+
%i[before before_validation after_validation api_call finally]
|
1754
|
+
end
|
1755
|
+
|
1756
|
+
before do
|
1757
|
+
current_cycle = lifecycle
|
1758
|
+
|
1759
|
+
subject.before do
|
1760
|
+
current_cycle << :before
|
1761
|
+
end
|
1762
|
+
|
1763
|
+
subject.before_validation do
|
1764
|
+
current_cycle << :before_validation
|
1765
|
+
end
|
1766
|
+
|
1767
|
+
subject.after_validation do
|
1768
|
+
current_cycle << :after_validation
|
1769
|
+
end
|
1770
|
+
|
1771
|
+
subject.after do
|
1772
|
+
current_cycle << :after
|
1773
|
+
end
|
1774
|
+
|
1775
|
+
subject.finally do
|
1776
|
+
current_cycle << :finally
|
1777
|
+
end
|
1778
|
+
end
|
1779
|
+
|
1780
|
+
context 'when the api_call succeeds' do
|
1781
|
+
before do
|
1782
|
+
current_cycle = lifecycle
|
1783
|
+
|
1784
|
+
subject.get 'api_call' do
|
1785
|
+
current_cycle << :api_call
|
1786
|
+
end
|
1787
|
+
end
|
1788
|
+
|
1789
|
+
it 'follows the standard life_cycle' do
|
1790
|
+
get '/api_call'
|
1791
|
+
expect(lifecycle).to eq standard_cycle
|
1792
|
+
end
|
1793
|
+
end
|
1794
|
+
|
1795
|
+
context 'when the api_call has a controlled error' do
|
1796
|
+
before do
|
1797
|
+
current_cycle = lifecycle
|
1798
|
+
|
1799
|
+
subject.get 'api_call' do
|
1800
|
+
current_cycle << :api_call
|
1801
|
+
error!(:some_error)
|
1802
|
+
end
|
1803
|
+
end
|
1804
|
+
|
1805
|
+
it 'follows the errored life_cycle (skips after)' do
|
1806
|
+
get '/api_call'
|
1807
|
+
expect(lifecycle).to eq errored_cycle
|
1808
|
+
end
|
1809
|
+
end
|
1810
|
+
|
1811
|
+
context 'when the api_call has an exception' do
|
1812
|
+
before do
|
1813
|
+
current_cycle = lifecycle
|
1814
|
+
|
1815
|
+
subject.get 'api_call' do
|
1816
|
+
current_cycle << :api_call
|
1817
|
+
raise StandardError
|
1818
|
+
end
|
1819
|
+
end
|
1820
|
+
|
1821
|
+
it 'follows the errored life_cycle (skips after)' do
|
1822
|
+
expect { get '/api_call' }.to raise_error(StandardError)
|
1823
|
+
expect(lifecycle).to eq errored_cycle
|
1824
|
+
end
|
1825
|
+
end
|
1826
|
+
|
1827
|
+
context 'when the api_call fails validation' do
|
1828
|
+
before do
|
1829
|
+
current_cycle = lifecycle
|
1830
|
+
|
1831
|
+
subject.params do
|
1832
|
+
requires :some_param, type: String
|
1833
|
+
end
|
1834
|
+
|
1835
|
+
subject.get 'api_call' do
|
1836
|
+
current_cycle << :api_call
|
1837
|
+
end
|
1838
|
+
end
|
1839
|
+
|
1840
|
+
it 'follows the failed_validation cycle (skips after_validation, api_call & after)' do
|
1841
|
+
get '/api_call'
|
1842
|
+
expect(lifecycle).to eq validation_error
|
1843
|
+
end
|
1844
|
+
end
|
1845
|
+
end
|
1846
|
+
|
1847
|
+
describe '.finally' do
|
1848
|
+
let!(:code) { { has_executed: false } }
|
1849
|
+
let(:block_to_run) do
|
1850
|
+
code_to_execute = code
|
1851
|
+
proc do
|
1852
|
+
code_to_execute[:has_executed] = true
|
1853
|
+
end
|
1854
|
+
end
|
1855
|
+
|
1856
|
+
context 'when the ensure block has no exceptions' do
|
1857
|
+
before { subject.finally(&block_to_run) }
|
1858
|
+
|
1859
|
+
context 'when no API call is made' do
|
1860
|
+
it 'has not executed the ensure code' do
|
1861
|
+
expect(code[:has_executed]).to be false
|
1862
|
+
end
|
1863
|
+
end
|
1864
|
+
|
1865
|
+
context 'when no errors occurs' do
|
1866
|
+
before do
|
1867
|
+
subject.get '/no_exceptions' do
|
1868
|
+
'success'
|
1869
|
+
end
|
1870
|
+
end
|
1871
|
+
|
1872
|
+
it 'executes the ensure code' do
|
1873
|
+
get '/no_exceptions'
|
1874
|
+
expect(last_response.body).to eq 'success'
|
1875
|
+
expect(code[:has_executed]).to be true
|
1876
|
+
end
|
1877
|
+
|
1878
|
+
context 'with a helper' do
|
1879
|
+
let(:block_to_run) do
|
1880
|
+
code_to_execute = code
|
1881
|
+
proc do
|
1882
|
+
code_to_execute[:value] = some_helper
|
1883
|
+
end
|
1884
|
+
end
|
1885
|
+
|
1886
|
+
before do
|
1887
|
+
subject.helpers do
|
1888
|
+
def some_helper
|
1889
|
+
'some_value'
|
1890
|
+
end
|
1891
|
+
end
|
1892
|
+
|
1893
|
+
subject.get '/with_helpers' do
|
1894
|
+
'success'
|
1895
|
+
end
|
1896
|
+
end
|
1897
|
+
|
1898
|
+
it 'has access to the helper' do
|
1899
|
+
get '/with_helpers'
|
1900
|
+
expect(code[:value]).to eq 'some_value'
|
1901
|
+
end
|
1902
|
+
end
|
1903
|
+
end
|
1904
|
+
|
1905
|
+
context 'when an unhandled occurs inside the API call' do
|
1906
|
+
before do
|
1907
|
+
subject.get '/unhandled_exception' do
|
1908
|
+
raise StandardError
|
1909
|
+
end
|
1910
|
+
end
|
1911
|
+
|
1912
|
+
it 'executes the ensure code' do
|
1913
|
+
expect { get '/unhandled_exception' }.to raise_error StandardError
|
1914
|
+
expect(code[:has_executed]).to be true
|
1915
|
+
end
|
1916
|
+
end
|
1917
|
+
|
1918
|
+
context 'when a handled error occurs inside the API call' do
|
1919
|
+
before do
|
1920
|
+
subject.rescue_from(StandardError) { error! 'handled' }
|
1921
|
+
subject.get '/handled_exception' do
|
1922
|
+
raise StandardError
|
1923
|
+
end
|
1924
|
+
end
|
1925
|
+
|
1926
|
+
it 'executes the ensure code' do
|
1927
|
+
get '/handled_exception'
|
1928
|
+
expect(code[:has_executed]).to be true
|
1929
|
+
expect(last_response.body).to eq 'handled'
|
1930
|
+
end
|
1931
|
+
end
|
1932
|
+
end
|
1933
|
+
end
|
1934
|
+
|
1600
1935
|
describe '.rescue_from' do
|
1601
1936
|
it 'does not rescue errors when rescue_from is not set' do
|
1602
1937
|
subject.get '/exception' do
|
@@ -1643,9 +1978,9 @@ XML
|
|
1643
1978
|
it 'avoids polluting global namespace' do
|
1644
1979
|
env = Rack::MockRequest.env_for('/')
|
1645
1980
|
|
1646
|
-
expect(a.call(env)[2]
|
1647
|
-
expect(b.call(env)[2]
|
1648
|
-
expect(a.call(env)[2]
|
1981
|
+
expect(read_chunks(a.call(env)[2])).to eq(['foo'])
|
1982
|
+
expect(read_chunks(b.call(env)[2])).to eq(['bar'])
|
1983
|
+
expect(read_chunks(a.call(env)[2])).to eq(['foo'])
|
1649
1984
|
end
|
1650
1985
|
end
|
1651
1986
|
|
@@ -1682,6 +2017,26 @@ XML
|
|
1682
2017
|
expect { get '/unrescued' }.to raise_error(RuntimeError, 'beefcake')
|
1683
2018
|
end
|
1684
2019
|
|
2020
|
+
it 'mimics default ruby "rescue" handler' do
|
2021
|
+
# The exception is matched to the rescue starting at the top, and matches only once
|
2022
|
+
|
2023
|
+
subject.rescue_from ArgumentError do |e|
|
2024
|
+
error!(e, 402)
|
2025
|
+
end
|
2026
|
+
subject.rescue_from StandardError do |e|
|
2027
|
+
error!(e, 401)
|
2028
|
+
end
|
2029
|
+
|
2030
|
+
subject.get('/child_of_standard_error') { raise ArgumentError }
|
2031
|
+
subject.get('/standard_error') { raise StandardError }
|
2032
|
+
|
2033
|
+
get '/child_of_standard_error'
|
2034
|
+
expect(last_response.status).to eql 402
|
2035
|
+
|
2036
|
+
get '/standard_error'
|
2037
|
+
expect(last_response.status).to eql 401
|
2038
|
+
end
|
2039
|
+
|
1685
2040
|
context 'CustomError subclass of Grape::Exceptions::Base' do
|
1686
2041
|
before do
|
1687
2042
|
module ApiSpec
|
@@ -1700,7 +2055,7 @@ XML
|
|
1700
2055
|
rack_response('New Error', e.status)
|
1701
2056
|
end
|
1702
2057
|
subject.get '/custom_error' do
|
1703
|
-
raise ApiSpec::CustomError
|
2058
|
+
raise ApiSpec::CustomError.new(status: 400, message: 'Custom Error')
|
1704
2059
|
end
|
1705
2060
|
|
1706
2061
|
get '/custom_error'
|
@@ -1723,6 +2078,16 @@ XML
|
|
1723
2078
|
expect(last_response.status).to eql 500
|
1724
2079
|
expect(last_response.body).to eq('Formatter Error')
|
1725
2080
|
end
|
2081
|
+
|
2082
|
+
it 'uses default_rescue_handler to handle invalid response from rescue_from' do
|
2083
|
+
subject.rescue_from(:all) { 'error' }
|
2084
|
+
subject.get('/') { raise }
|
2085
|
+
|
2086
|
+
expect_any_instance_of(Grape::Middleware::Error).to receive(:default_rescue_handler).and_call_original
|
2087
|
+
get '/'
|
2088
|
+
expect(last_response.status).to eql 500
|
2089
|
+
expect(last_response.body).to eql 'Invalid response'
|
2090
|
+
end
|
1726
2091
|
end
|
1727
2092
|
|
1728
2093
|
describe '.rescue_from klass, block' do
|
@@ -3177,6 +3542,43 @@ XML
|
|
3177
3542
|
expect { a.mount b }.to_not raise_error
|
3178
3543
|
end
|
3179
3544
|
end
|
3545
|
+
|
3546
|
+
context 'when including a module' do
|
3547
|
+
let(:included_module) do
|
3548
|
+
Module.new do
|
3549
|
+
def self.included(base)
|
3550
|
+
base.extend(ClassMethods)
|
3551
|
+
end
|
3552
|
+
module ClassMethods
|
3553
|
+
def my_method
|
3554
|
+
@test = true
|
3555
|
+
end
|
3556
|
+
end
|
3557
|
+
end
|
3558
|
+
end
|
3559
|
+
|
3560
|
+
it 'should correctly include module in nested mount' do
|
3561
|
+
module_to_include = included_module
|
3562
|
+
v1 = Class.new(Grape::API) do
|
3563
|
+
version :v1, using: :path
|
3564
|
+
include module_to_include
|
3565
|
+
my_method
|
3566
|
+
end
|
3567
|
+
v2 = Class.new(Grape::API) do
|
3568
|
+
version :v2, using: :path
|
3569
|
+
end
|
3570
|
+
segment_base = Class.new(Grape::API) do
|
3571
|
+
mount v1
|
3572
|
+
mount v2
|
3573
|
+
end
|
3574
|
+
|
3575
|
+
Class.new(Grape::API) do
|
3576
|
+
mount segment_base
|
3577
|
+
end
|
3578
|
+
|
3579
|
+
expect(v1.my_method).to be_truthy
|
3580
|
+
end
|
3581
|
+
end
|
3180
3582
|
end
|
3181
3583
|
end
|
3182
3584
|
|
@@ -3192,7 +3594,7 @@ XML
|
|
3192
3594
|
it 'sets the instance' do
|
3193
3595
|
expect(subject.instance).to be_nil
|
3194
3596
|
subject.compile
|
3195
|
-
expect(subject.instance).to be_kind_of(subject)
|
3597
|
+
expect(subject.instance).to be_kind_of(subject.base_instance)
|
3196
3598
|
end
|
3197
3599
|
end
|
3198
3600
|
|
@@ -3350,12 +3752,13 @@ XML
|
|
3350
3752
|
end
|
3351
3753
|
end
|
3352
3754
|
context ':serializable_hash' do
|
3353
|
-
|
3354
|
-
|
3355
|
-
def
|
3356
|
-
{ abc: 'def' }
|
3357
|
-
end
|
3755
|
+
class SerializableHashExample
|
3756
|
+
def serializable_hash
|
3757
|
+
{ abc: 'def' }
|
3358
3758
|
end
|
3759
|
+
end
|
3760
|
+
|
3761
|
+
before(:each) do
|
3359
3762
|
subject.format :serializable_hash
|
3360
3763
|
end
|
3361
3764
|
it 'instance' do
|
@@ -3449,6 +3852,44 @@ XML
|
|
3449
3852
|
end
|
3450
3853
|
end
|
3451
3854
|
|
3855
|
+
describe '.configure' do
|
3856
|
+
context 'when given a block' do
|
3857
|
+
it 'returns self' do
|
3858
|
+
expect(subject.configure {}).to be subject
|
3859
|
+
end
|
3860
|
+
|
3861
|
+
it 'calls the block passing the config' do
|
3862
|
+
call = [false, nil]
|
3863
|
+
subject.configure do |config|
|
3864
|
+
call = [true, config]
|
3865
|
+
end
|
3866
|
+
|
3867
|
+
expect(call[0]).to be true
|
3868
|
+
expect(call[1]).not_to be_nil
|
3869
|
+
end
|
3870
|
+
end
|
3871
|
+
|
3872
|
+
context 'when not given a block' do
|
3873
|
+
it 'returns a configuration object' do
|
3874
|
+
expect(subject.configure).to respond_to(:[], :[]=)
|
3875
|
+
end
|
3876
|
+
end
|
3877
|
+
|
3878
|
+
it 'allows configuring the api' do
|
3879
|
+
subject.configure do |config|
|
3880
|
+
config[:hello] = 'hello'
|
3881
|
+
config[:bread] = 'bread'
|
3882
|
+
end
|
3883
|
+
|
3884
|
+
subject.get '/hello-bread' do
|
3885
|
+
"#{configuration[:hello]} #{configuration[:bread]}"
|
3886
|
+
end
|
3887
|
+
|
3888
|
+
get '/hello-bread'
|
3889
|
+
expect(last_response.body).to eq 'hello bread'
|
3890
|
+
end
|
3891
|
+
end
|
3892
|
+
|
3452
3893
|
context 'catch-all' do
|
3453
3894
|
before do
|
3454
3895
|
api1 = Class.new(Grape::API)
|
@@ -3580,4 +4021,116 @@ XML
|
|
3580
4021
|
end
|
3581
4022
|
end
|
3582
4023
|
end
|
4024
|
+
|
4025
|
+
describe 'normal class methods' do
|
4026
|
+
subject(:grape_api) { Class.new(Grape::API) }
|
4027
|
+
|
4028
|
+
before do
|
4029
|
+
stub_const('MyAPI', grape_api)
|
4030
|
+
end
|
4031
|
+
|
4032
|
+
it 'can find the appropiate name' do
|
4033
|
+
expect(grape_api.name).to eq 'MyAPI'
|
4034
|
+
end
|
4035
|
+
|
4036
|
+
it 'is equal to itself' do
|
4037
|
+
expect(grape_api.itself).to eq grape_api
|
4038
|
+
expect(grape_api).to eq MyAPI
|
4039
|
+
expect(grape_api.eql?(MyAPI))
|
4040
|
+
end
|
4041
|
+
end
|
4042
|
+
|
4043
|
+
describe 'const_missing' do
|
4044
|
+
subject(:grape_api) { Class.new(Grape::API) }
|
4045
|
+
let(:mounted) do
|
4046
|
+
Class.new(Grape::API) do
|
4047
|
+
get '/missing' do
|
4048
|
+
SomeRandomConstant
|
4049
|
+
end
|
4050
|
+
end
|
4051
|
+
end
|
4052
|
+
|
4053
|
+
before { subject.mount mounted => '/const' }
|
4054
|
+
|
4055
|
+
it 'raises an error' do
|
4056
|
+
expect { get '/const/missing' }.to raise_error(NameError).with_message(/SomeRandomConstant/)
|
4057
|
+
end
|
4058
|
+
end
|
4059
|
+
|
4060
|
+
describe 'custom route helpers on nested APIs' do
|
4061
|
+
let(:shared_api_module) do
|
4062
|
+
Module.new do
|
4063
|
+
# rubocop:disable Style/ExplicitBlockArgument because this causes
|
4064
|
+
# the underlying issue in this form
|
4065
|
+
def uniqe_id_route
|
4066
|
+
params do
|
4067
|
+
use :unique_id
|
4068
|
+
end
|
4069
|
+
route_param(:id) do
|
4070
|
+
yield
|
4071
|
+
end
|
4072
|
+
end
|
4073
|
+
# rubocop:enable Style/ExplicitBlockArgument
|
4074
|
+
end
|
4075
|
+
end
|
4076
|
+
let(:shared_api_definitions) do
|
4077
|
+
Module.new do
|
4078
|
+
extend ActiveSupport::Concern
|
4079
|
+
|
4080
|
+
included do
|
4081
|
+
helpers do
|
4082
|
+
params :unique_id do
|
4083
|
+
requires :id, type: String,
|
4084
|
+
allow_blank: false,
|
4085
|
+
regexp: /\d+-\d+/
|
4086
|
+
end
|
4087
|
+
end
|
4088
|
+
end
|
4089
|
+
end
|
4090
|
+
end
|
4091
|
+
let(:orders_root) do
|
4092
|
+
shared = shared_api_definitions
|
4093
|
+
find = orders_find_endpoint
|
4094
|
+
Class.new(Grape::API) do
|
4095
|
+
include shared
|
4096
|
+
|
4097
|
+
namespace(:orders) do
|
4098
|
+
mount find
|
4099
|
+
end
|
4100
|
+
end
|
4101
|
+
end
|
4102
|
+
let(:orders_find_endpoint) do
|
4103
|
+
shared = shared_api_definitions
|
4104
|
+
Class.new(Grape::API) do
|
4105
|
+
include shared
|
4106
|
+
|
4107
|
+
uniqe_id_route do
|
4108
|
+
desc 'Fetch a single order' do
|
4109
|
+
detail 'While specifying the order id on the route'
|
4110
|
+
end
|
4111
|
+
get { params[:id] }
|
4112
|
+
end
|
4113
|
+
end
|
4114
|
+
end
|
4115
|
+
subject(:grape_api) do
|
4116
|
+
Class.new(Grape::API) do
|
4117
|
+
version 'v1', using: :path
|
4118
|
+
end
|
4119
|
+
end
|
4120
|
+
|
4121
|
+
before do
|
4122
|
+
Grape::API::Instance.extend(shared_api_module)
|
4123
|
+
subject.mount orders_root
|
4124
|
+
end
|
4125
|
+
|
4126
|
+
it 'returns an error when the id is bad' do
|
4127
|
+
get '/v1/orders/abc'
|
4128
|
+
expect(last_response.body).to be_eql('id is invalid')
|
4129
|
+
end
|
4130
|
+
|
4131
|
+
it 'returns the given id when it is valid' do
|
4132
|
+
get '/v1/orders/1-2'
|
4133
|
+
expect(last_response.body).to be_eql('1-2')
|
4134
|
+
end
|
4135
|
+
end
|
3583
4136
|
end
|