File: request_response.rb

package info (click to toggle)
ruby-grape 1.6.2-3
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 2,156 kB
  • sloc: ruby: 25,265; makefile: 7
file content (176 lines) | stat: -rw-r--r-- 6,772 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# frozen_string_literal: true

require 'active_support/concern'

module Grape
  module DSL
    module RequestResponse
      extend ActiveSupport::Concern

      include Grape::DSL::Configuration

      module ClassMethods
        # Specify the default format for the API's serializers.
        # May be `:json` or `:txt` (default).
        def default_format(new_format = nil)
          namespace_inheritable(:default_format, new_format.nil? ? nil : new_format.to_sym)
        end

        # Specify the format for the API's serializers.
        # May be `:json`, `:xml`, `:txt`, etc.
        def format(new_format = nil)
          if new_format
            namespace_inheritable(:format, new_format.to_sym)
            # define the default error formatters
            namespace_inheritable(:default_error_formatter, Grape::ErrorFormatter.formatter_for(new_format, **{}))
            # define a single mime type
            mime_type = content_types[new_format.to_sym]
            raise Grape::Exceptions::MissingMimeType.new(new_format) unless mime_type

            namespace_stackable(:content_types, new_format.to_sym => mime_type)
          else
            namespace_inheritable(:format)
          end
        end

        # Specify a custom formatter for a content-type.
        def formatter(content_type, new_formatter)
          namespace_stackable(:formatters, content_type.to_sym => new_formatter)
        end

        # Specify a custom parser for a content-type.
        def parser(content_type, new_parser)
          namespace_stackable(:parsers, content_type.to_sym => new_parser)
        end

        # Specify a default error formatter.
        def default_error_formatter(new_formatter_name = nil)
          if new_formatter_name
            new_formatter = Grape::ErrorFormatter.formatter_for(new_formatter_name, **{})
            namespace_inheritable(:default_error_formatter, new_formatter)
          else
            namespace_inheritable(:default_error_formatter)
          end
        end

        def error_formatter(format, options)
          formatter = if options.is_a?(Hash) && options.key?(:with)
                        options[:with]
                      else
                        options
                      end

          namespace_stackable(:error_formatters, format.to_sym => formatter)
        end

        # Specify additional content-types, e.g.:
        #   content_type :xls, 'application/vnd.ms-excel'
        def content_type(key, val)
          namespace_stackable(:content_types, key.to_sym => val)
        end

        # All available content types.
        def content_types
          c_types = namespace_stackable_with_hash(:content_types)
          Grape::ContentTypes.content_types_for c_types
        end

        # Specify the default status code for errors.
        def default_error_status(new_status = nil)
          namespace_inheritable(:default_error_status, new_status)
        end

        # Allows you to rescue certain exceptions that occur to return
        # a grape error rather than raising all the way to the
        # server level.
        #
        # @example Rescue from custom exceptions
        #     class ExampleAPI < Grape::API
        #       class CustomError < StandardError; end
        #
        #       rescue_from CustomError
        #     end
        #
        # @overload rescue_from(*exception_classes, **options)
        #   @param [Array] exception_classes A list of classes that you want to rescue, or
        #     the symbol :all to rescue from all exceptions.
        #   @param [Block] block Execution block to handle the given exception.
        #   @param [Hash] options Options for the rescue usage.
        #   @option options [Boolean] :backtrace Include a backtrace in the rescue response.
        #   @option options [Boolean] :rescue_subclasses Also rescue subclasses of exception classes
        #   @param [Proc] handler Execution proc to handle the given exception as an
        #     alternative to passing a block.
        def rescue_from(*args, &block)
          if args.last.is_a?(Proc)
            handler = args.pop
          elsif block
            handler = block
          end

          options = args.extract_options!
          raise ArgumentError, 'both :with option and block cannot be passed' if block && options.key?(:with)

          handler ||= extract_with(options)

          if args.include?(:all)
            namespace_inheritable(:rescue_all, true)
            namespace_inheritable :all_rescue_handler, handler
          elsif args.include?(:grape_exceptions)
            namespace_inheritable(:rescue_all, true)
            namespace_inheritable(:rescue_grape_exceptions, true)
          else
            handler_type =
              case options[:rescue_subclasses]
              when nil, true
                :rescue_handlers
              else
                :base_only_rescue_handlers
              end

            namespace_reverse_stackable handler_type, args.map { |arg| [arg, handler] }.to_h
          end

          namespace_stackable(:rescue_options, options)
        end

        # Allows you to specify a default representation entity for a
        # class. This allows you to map your models to their respective
        # entities once and then simply call `present` with the model.
        #
        # @example
        #   class ExampleAPI < Grape::API
        #     represent User, with: Entity::User
        #
        #     get '/me' do
        #       present current_user # with: Entity::User is assumed
        #     end
        #   end
        #
        # Note that Grape will automatically go up the class ancestry to
        # try to find a representing entity, so if you, for example, define
        # an entity to represent `Object` then all presented objects will
        # bubble up and utilize the entity provided on that `represent` call.
        #
        # @param model_class [Class] The model class that will be represented.
        # @option options [Class] :with The entity class that will represent the model.
        def represent(model_class, options)
          raise Grape::Exceptions::InvalidWithOptionForRepresent.new unless options[:with].is_a?(Class)

          namespace_stackable(:representations, model_class => options[:with])
        end

        private

        def extract_with(options)
          return unless options.key?(:with)

          with_option = options.delete(:with)
          return with_option if with_option.instance_of?(Proc)
          return with_option.to_sym if with_option.instance_of?(Symbol) || with_option.instance_of?(String)

          raise ArgumentError, "with: #{with_option.class}, expected Symbol, String or Proc"
        end
      end
    end
  end
end