grape 2.4.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +40 -0
  3. data/CONTRIBUTING.md +1 -9
  4. data/README.md +72 -31
  5. data/UPGRADING.md +34 -0
  6. data/grape.gemspec +4 -4
  7. data/lib/grape/api/instance.rb +49 -72
  8. data/lib/grape/api.rb +24 -34
  9. data/lib/grape/dry_types.rb +48 -4
  10. data/lib/grape/dsl/callbacks.rb +8 -58
  11. data/lib/grape/dsl/desc.rb +8 -67
  12. data/lib/grape/dsl/helpers.rb +59 -64
  13. data/lib/grape/dsl/inside_route.rb +20 -43
  14. data/lib/grape/dsl/logger.rb +3 -6
  15. data/lib/grape/dsl/middleware.rb +22 -40
  16. data/lib/grape/dsl/parameters.rb +7 -16
  17. data/lib/grape/dsl/request_response.rb +136 -139
  18. data/lib/grape/dsl/routing.rb +229 -201
  19. data/lib/grape/dsl/settings.rb +22 -134
  20. data/lib/grape/dsl/validations.rb +37 -45
  21. data/lib/grape/endpoint.rb +64 -96
  22. data/lib/grape/error_formatter/base.rb +2 -0
  23. data/lib/grape/exceptions/base.rb +1 -1
  24. data/lib/grape/exceptions/missing_group_type.rb +0 -2
  25. data/lib/grape/exceptions/unsupported_group_type.rb +0 -2
  26. data/lib/grape/middleware/auth/dsl.rb +5 -6
  27. data/lib/grape/middleware/error.rb +1 -11
  28. data/lib/grape/middleware/formatter.rb +4 -2
  29. data/lib/grape/middleware/stack.rb +2 -2
  30. data/lib/grape/middleware/versioner/accept_version_header.rb +1 -1
  31. data/lib/grape/middleware/versioner/base.rb +24 -42
  32. data/lib/grape/middleware/versioner/header.rb +1 -1
  33. data/lib/grape/middleware/versioner/param.rb +2 -2
  34. data/lib/grape/middleware/versioner/path.rb +1 -1
  35. data/lib/grape/namespace.rb +11 -0
  36. data/lib/grape/params_builder/base.rb +2 -0
  37. data/lib/grape/router.rb +4 -3
  38. data/lib/grape/util/api_description.rb +56 -0
  39. data/lib/grape/util/base_inheritable.rb +5 -2
  40. data/lib/grape/util/inheritable_setting.rb +7 -0
  41. data/lib/grape/util/media_type.rb +1 -1
  42. data/lib/grape/util/registry.rb +1 -1
  43. data/lib/grape/validations/contract_scope.rb +2 -2
  44. data/lib/grape/validations/params_documentation.rb +50 -0
  45. data/lib/grape/validations/params_scope.rb +38 -53
  46. data/lib/grape/validations/types/array_coercer.rb +2 -3
  47. data/lib/grape/validations/types/dry_type_coercer.rb +4 -11
  48. data/lib/grape/validations/types/primitive_coercer.rb +1 -28
  49. data/lib/grape/validations/types.rb +10 -25
  50. data/lib/grape/validations/validators/base.rb +0 -7
  51. data/lib/grape/version.rb +1 -1
  52. data/lib/grape.rb +7 -10
  53. metadata +24 -14
  54. data/lib/grape/api/helpers.rb +0 -9
  55. data/lib/grape/dsl/api.rb +0 -17
  56. data/lib/grape/dsl/configuration.rb +0 -15
  57. data/lib/grape/types/invalid_value.rb +0 -8
  58. data/lib/grape/util/strict_hash_configuration.rb +0 -108
  59. data/lib/grape/validations/attributes_doc.rb +0 -60
@@ -3,168 +3,165 @@
3
3
  module Grape
4
4
  module DSL
5
5
  module RequestResponse
6
- extend ActiveSupport::Concern
6
+ # Specify the default format for the API's serializers.
7
+ # May be `:json` or `:txt` (default).
8
+ def default_format(new_format = nil)
9
+ return inheritable_setting.namespace_inheritable[:default_format] if new_format.nil?
7
10
 
8
- include Grape::DSL::Configuration
11
+ inheritable_setting.namespace_inheritable[:default_format] = new_format.to_sym
12
+ end
9
13
 
10
- module ClassMethods
11
- # Specify the default format for the API's serializers.
12
- # May be `:json` or `:txt` (default).
13
- def default_format(new_format = nil)
14
- namespace_inheritable(:default_format, new_format.nil? ? nil : new_format.to_sym)
15
- end
14
+ # Specify the format for the API's serializers.
15
+ # May be `:json`, `:xml`, `:txt`, etc.
16
+ def format(new_format = nil)
17
+ return inheritable_setting.namespace_inheritable[:format] if new_format.nil?
16
18
 
17
- # Specify the format for the API's serializers.
18
- # May be `:json`, `:xml`, `:txt`, etc.
19
- def format(new_format = nil)
20
- return namespace_inheritable(:format) unless new_format
19
+ symbolic_new_format = new_format.to_sym
20
+ inheritable_setting.namespace_inheritable[:format] = symbolic_new_format
21
+ inheritable_setting.namespace_inheritable[:default_error_formatter] = Grape::ErrorFormatter.formatter_for(symbolic_new_format)
21
22
 
22
- symbolic_new_format = new_format.to_sym
23
- namespace_inheritable(:format, symbolic_new_format)
24
- namespace_inheritable(:default_error_formatter, Grape::ErrorFormatter.formatter_for(symbolic_new_format))
23
+ content_type = content_types[symbolic_new_format]
24
+ raise Grape::Exceptions::MissingMimeType.new(new_format) unless content_type
25
25
 
26
- content_type = content_types[symbolic_new_format]
27
- raise Grape::Exceptions::MissingMimeType.new(new_format) unless content_type
26
+ inheritable_setting.namespace_stackable[:content_types] = { symbolic_new_format => content_type }
27
+ end
28
28
 
29
- namespace_stackable(:content_types, symbolic_new_format => content_type)
30
- end
29
+ # Specify a custom formatter for a content-type.
30
+ def formatter(content_type, new_formatter)
31
+ inheritable_setting.namespace_stackable[:formatters] = { content_type.to_sym => new_formatter }
32
+ end
31
33
 
32
- # Specify a custom formatter for a content-type.
33
- def formatter(content_type, new_formatter)
34
- namespace_stackable(:formatters, content_type.to_sym => new_formatter)
35
- end
34
+ # Specify a custom parser for a content-type.
35
+ def parser(content_type, new_parser)
36
+ inheritable_setting.namespace_stackable[:parsers] = { content_type.to_sym => new_parser }
37
+ end
36
38
 
37
- # Specify a custom parser for a content-type.
38
- def parser(content_type, new_parser)
39
- namespace_stackable(:parsers, content_type.to_sym => new_parser)
40
- end
39
+ # Specify a default error formatter.
40
+ def default_error_formatter(new_formatter_name = nil)
41
+ return inheritable_setting.namespace_inheritable[:default_error_formatter] if new_formatter_name.nil?
41
42
 
42
- # Specify a default error formatter.
43
- def default_error_formatter(new_formatter_name = nil)
44
- return namespace_inheritable(:default_error_formatter) unless new_formatter_name
43
+ new_formatter = Grape::ErrorFormatter.formatter_for(new_formatter_name)
44
+ inheritable_setting.namespace_inheritable[:default_error_formatter] = new_formatter
45
+ end
45
46
 
46
- new_formatter = Grape::ErrorFormatter.formatter_for(new_formatter_name)
47
- namespace_inheritable(:default_error_formatter, new_formatter)
48
- end
47
+ def error_formatter(format, options)
48
+ formatter = if options.is_a?(Hash) && options.key?(:with)
49
+ options[:with]
50
+ else
51
+ options
52
+ end
49
53
 
50
- def error_formatter(format, options)
51
- formatter = if options.is_a?(Hash) && options.key?(:with)
52
- options[:with]
53
- else
54
- options
55
- end
54
+ inheritable_setting.namespace_stackable[:error_formatters] = { format.to_sym => formatter }
55
+ end
56
56
 
57
- namespace_stackable(:error_formatters, format.to_sym => formatter)
58
- end
57
+ # Specify additional content-types, e.g.:
58
+ # content_type :xls, 'application/vnd.ms-excel'
59
+ def content_type(key, val)
60
+ inheritable_setting.namespace_stackable[:content_types] = { key.to_sym => val }
61
+ end
59
62
 
60
- # Specify additional content-types, e.g.:
61
- # content_type :xls, 'application/vnd.ms-excel'
62
- def content_type(key, val)
63
- namespace_stackable(:content_types, key.to_sym => val)
64
- end
63
+ # All available content types.
64
+ def content_types
65
+ c_types = inheritable_setting.namespace_stackable_with_hash(:content_types)
66
+ Grape::ContentTypes.content_types_for c_types
67
+ end
65
68
 
66
- # All available content types.
67
- def content_types
68
- c_types = namespace_stackable_with_hash(:content_types)
69
- Grape::ContentTypes.content_types_for c_types
70
- end
69
+ # Specify the default status code for errors.
70
+ def default_error_status(new_status = nil)
71
+ return inheritable_setting.namespace_inheritable[:default_error_status] if new_status.nil?
71
72
 
72
- # Specify the default status code for errors.
73
- def default_error_status(new_status = nil)
74
- namespace_inheritable(:default_error_status, new_status)
75
- end
73
+ inheritable_setting.namespace_inheritable[:default_error_status] = new_status
74
+ end
76
75
 
77
- # Allows you to rescue certain exceptions that occur to return
78
- # a grape error rather than raising all the way to the
79
- # server level.
80
- #
81
- # @example Rescue from custom exceptions
82
- # class ExampleAPI < Grape::API
83
- # class CustomError < StandardError; end
84
- #
85
- # rescue_from CustomError
86
- # end
87
- #
88
- # @overload rescue_from(*exception_classes, **options)
89
- # @param [Array] exception_classes A list of classes that you want to rescue, or
90
- # the symbol :all to rescue from all exceptions.
91
- # @param [Block] block Execution block to handle the given exception.
92
- # @param [Hash] options Options for the rescue usage.
93
- # @option options [Boolean] :backtrace Include a backtrace in the rescue response.
94
- # @option options [Boolean] :rescue_subclasses Also rescue subclasses of exception classes
95
- # @param [Proc] handler Execution proc to handle the given exception as an
96
- # alternative to passing a block.
97
- def rescue_from(*args, &block)
98
- if args.last.is_a?(Proc)
99
- handler = args.pop
100
- elsif block
101
- handler = block
102
- end
103
-
104
- options = args.extract_options!
105
- raise ArgumentError, 'both :with option and block cannot be passed' if block && options.key?(:with)
106
-
107
- handler ||= extract_with(options)
108
-
109
- if args.include?(:all)
110
- namespace_inheritable(:rescue_all, true)
111
- namespace_inheritable(:all_rescue_handler, handler)
112
- elsif args.include?(:grape_exceptions)
113
- namespace_inheritable(:rescue_all, true)
114
- namespace_inheritable(:rescue_grape_exceptions, true)
115
- namespace_inheritable(:grape_exceptions_rescue_handler, handler)
116
- else
117
- handler_type =
118
- case options[:rescue_subclasses]
119
- when nil, true
120
- :rescue_handlers
121
- else
122
- :base_only_rescue_handlers
123
- end
124
-
125
- namespace_reverse_stackable(handler_type, args.to_h { |arg| [arg, handler] })
126
- end
127
-
128
- namespace_stackable(:rescue_options, options)
76
+ # Allows you to rescue certain exceptions that occur to return
77
+ # a grape error rather than raising all the way to the
78
+ # server level.
79
+ #
80
+ # @example Rescue from custom exceptions
81
+ # class ExampleAPI < Grape::API
82
+ # class CustomError < StandardError; end
83
+ #
84
+ # rescue_from CustomError
85
+ # end
86
+ #
87
+ # @overload rescue_from(*exception_classes, **options)
88
+ # @param [Array] exception_classes A list of classes that you want to rescue, or
89
+ # the symbol :all to rescue from all exceptions.
90
+ # @param [Block] block Execution block to handle the given exception.
91
+ # @param [Hash] options Options for the rescue usage.
92
+ # @option options [Boolean] :backtrace Include a backtrace in the rescue response.
93
+ # @option options [Boolean] :rescue_subclasses Also rescue subclasses of exception classes
94
+ # @param [Proc] handler Execution proc to handle the given exception as an
95
+ # alternative to passing a block.
96
+ def rescue_from(*args, **options, &block)
97
+ if args.last.is_a?(Proc)
98
+ handler = args.pop
99
+ elsif block
100
+ handler = block
129
101
  end
130
102
 
131
- # Allows you to specify a default representation entity for a
132
- # class. This allows you to map your models to their respective
133
- # entities once and then simply call `present` with the model.
134
- #
135
- # @example
136
- # class ExampleAPI < Grape::API
137
- # represent User, with: Entity::User
138
- #
139
- # get '/me' do
140
- # present current_user # with: Entity::User is assumed
141
- # end
142
- # end
143
- #
144
- # Note that Grape will automatically go up the class ancestry to
145
- # try to find a representing entity, so if you, for example, define
146
- # an entity to represent `Object` then all presented objects will
147
- # bubble up and utilize the entity provided on that `represent` call.
148
- #
149
- # @param model_class [Class] The model class that will be represented.
150
- # @option options [Class] :with The entity class that will represent the model.
151
- def represent(model_class, options)
152
- raise Grape::Exceptions::InvalidWithOptionForRepresent.new unless options[:with].is_a?(Class)
153
-
154
- namespace_stackable(:representations, model_class => options[:with])
103
+ raise ArgumentError, 'both :with option and block cannot be passed' if block && options.key?(:with)
104
+
105
+ handler ||= extract_with(options)
106
+
107
+ if args.include?(:all)
108
+ inheritable_setting.namespace_inheritable[:rescue_all] = true
109
+ inheritable_setting.namespace_inheritable[:all_rescue_handler] = handler
110
+ elsif args.include?(:grape_exceptions)
111
+ inheritable_setting.namespace_inheritable[:rescue_all] = true
112
+ inheritable_setting.namespace_inheritable[:rescue_grape_exceptions] = true
113
+ inheritable_setting.namespace_inheritable[:grape_exceptions_rescue_handler] = handler
114
+ else
115
+ handler_type =
116
+ case options[:rescue_subclasses]
117
+ when nil, true
118
+ :rescue_handlers
119
+ else
120
+ :base_only_rescue_handlers
121
+ end
122
+
123
+ inheritable_setting.namespace_reverse_stackable[handler_type] = args.to_h { |arg| [arg, handler] }
155
124
  end
156
125
 
157
- private
126
+ inheritable_setting.namespace_stackable[:rescue_options] = options
127
+ end
158
128
 
159
- def extract_with(options)
160
- return unless options.key?(:with)
129
+ # Allows you to specify a default representation entity for a
130
+ # class. This allows you to map your models to their respective
131
+ # entities once and then simply call `present` with the model.
132
+ #
133
+ # @example
134
+ # class ExampleAPI < Grape::API
135
+ # represent User, with: Entity::User
136
+ #
137
+ # get '/me' do
138
+ # present current_user # with: Entity::User is assumed
139
+ # end
140
+ # end
141
+ #
142
+ # Note that Grape will automatically go up the class ancestry to
143
+ # try to find a representing entity, so if you, for example, define
144
+ # an entity to represent `Object` then all presented objects will
145
+ # bubble up and utilize the entity provided on that `represent` call.
146
+ #
147
+ # @param model_class [Class] The model class that will be represented.
148
+ # @option options [Class] :with The entity class that will represent the model.
149
+ def represent(model_class, options)
150
+ raise Grape::Exceptions::InvalidWithOptionForRepresent.new unless options[:with].is_a?(Class)
151
+
152
+ inheritable_setting.namespace_stackable[:representations] = { model_class => options[:with] }
153
+ end
161
154
 
162
- with_option = options.delete(:with)
163
- return with_option if with_option.instance_of?(Proc)
164
- return with_option.to_sym if with_option.instance_of?(Symbol) || with_option.instance_of?(String)
155
+ private
165
156
 
166
- raise ArgumentError, "with: #{with_option.class}, expected Symbol, String or Proc"
167
- end
157
+ def extract_with(options)
158
+ return unless options.key?(:with)
159
+
160
+ with_option = options.delete(:with)
161
+ return with_option if with_option.instance_of?(Proc)
162
+ return with_option.to_sym if with_option.instance_of?(Symbol) || with_option.instance_of?(String)
163
+
164
+ raise ArgumentError, "with: #{with_option.class}, expected Symbol, String or Proc"
168
165
  end
169
166
  end
170
167
  end