grape 0.1.1 → 2.4.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 (197) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +1167 -0
  3. data/CONTRIBUTING.md +156 -0
  4. data/LICENSE +1 -1
  5. data/README.md +4192 -0
  6. data/UPGRADING.md +1697 -0
  7. data/grape.gemspec +28 -105
  8. data/grape.png +0 -0
  9. data/lib/grape/api/helpers.rb +9 -0
  10. data/lib/grape/api/instance.rb +247 -0
  11. data/lib/grape/api.rb +158 -194
  12. data/lib/grape/content_types.rb +31 -0
  13. data/lib/grape/cookies.rb +50 -0
  14. data/lib/grape/dry_types.rb +10 -0
  15. data/lib/grape/dsl/api.rb +17 -0
  16. data/lib/grape/dsl/callbacks.rb +69 -0
  17. data/lib/grape/dsl/configuration.rb +15 -0
  18. data/lib/grape/dsl/desc.rb +124 -0
  19. data/lib/grape/dsl/headers.rb +21 -0
  20. data/lib/grape/dsl/helpers.rb +108 -0
  21. data/lib/grape/dsl/inside_route.rb +454 -0
  22. data/lib/grape/dsl/logger.rb +22 -0
  23. data/lib/grape/dsl/middleware.rb +54 -0
  24. data/lib/grape/dsl/parameters.rb +266 -0
  25. data/lib/grape/dsl/request_response.rb +171 -0
  26. data/lib/grape/dsl/routing.rb +248 -0
  27. data/lib/grape/dsl/settings.rb +179 -0
  28. data/lib/grape/dsl/validations.rb +57 -0
  29. data/lib/grape/endpoint.rb +398 -68
  30. data/lib/grape/env.rb +20 -0
  31. data/lib/grape/error_formatter/base.rb +68 -0
  32. data/lib/grape/error_formatter/json.rb +28 -0
  33. data/lib/grape/error_formatter/serializable_hash.rb +7 -0
  34. data/lib/grape/error_formatter/txt.rb +21 -0
  35. data/lib/grape/error_formatter/xml.rb +11 -0
  36. data/lib/grape/error_formatter.rb +15 -0
  37. data/lib/grape/exceptions/base.rb +76 -0
  38. data/lib/grape/exceptions/conflicting_types.rb +11 -0
  39. data/lib/grape/exceptions/empty_message_body.rb +11 -0
  40. data/lib/grape/exceptions/incompatible_option_values.rb +11 -0
  41. data/lib/grape/exceptions/invalid_accept_header.rb +11 -0
  42. data/lib/grape/exceptions/invalid_formatter.rb +11 -0
  43. data/lib/grape/exceptions/invalid_message_body.rb +11 -0
  44. data/lib/grape/exceptions/invalid_parameters.rb +11 -0
  45. data/lib/grape/exceptions/invalid_response.rb +11 -0
  46. data/lib/grape/exceptions/invalid_version_header.rb +11 -0
  47. data/lib/grape/exceptions/invalid_versioner_option.rb +11 -0
  48. data/lib/grape/exceptions/invalid_with_option_for_represent.rb +11 -0
  49. data/lib/grape/exceptions/method_not_allowed.rb +11 -0
  50. data/lib/grape/exceptions/missing_group_type.rb +13 -0
  51. data/lib/grape/exceptions/missing_mime_type.rb +11 -0
  52. data/lib/grape/exceptions/missing_option.rb +11 -0
  53. data/lib/grape/exceptions/missing_vendor_option.rb +11 -0
  54. data/lib/grape/exceptions/too_deep_parameters.rb +11 -0
  55. data/lib/grape/exceptions/too_many_multipart_files.rb +11 -0
  56. data/lib/grape/exceptions/unknown_auth_strategy.rb +11 -0
  57. data/lib/grape/exceptions/unknown_options.rb +11 -0
  58. data/lib/grape/exceptions/unknown_parameter.rb +11 -0
  59. data/lib/grape/exceptions/unknown_params_builder.rb +11 -0
  60. data/lib/grape/exceptions/unknown_validator.rb +11 -0
  61. data/lib/grape/exceptions/unsupported_group_type.rb +13 -0
  62. data/lib/grape/exceptions/validation.rb +25 -0
  63. data/lib/grape/exceptions/validation_array_errors.rb +14 -0
  64. data/lib/grape/exceptions/validation_errors.rb +53 -0
  65. data/lib/grape/extensions/active_support/hash_with_indifferent_access.rb +24 -0
  66. data/lib/grape/extensions/hash.rb +27 -0
  67. data/lib/grape/extensions/hashie/mash.rb +24 -0
  68. data/lib/grape/formatter/base.rb +16 -0
  69. data/lib/grape/formatter/json.rb +13 -0
  70. data/lib/grape/formatter/serializable_hash.rb +39 -0
  71. data/lib/grape/formatter/txt.rb +11 -0
  72. data/lib/grape/formatter/xml.rb +13 -0
  73. data/lib/grape/formatter.rb +17 -0
  74. data/lib/grape/json.rb +10 -0
  75. data/lib/grape/locale/en.yml +59 -0
  76. data/lib/grape/middleware/auth/base.rb +23 -0
  77. data/lib/grape/middleware/auth/dsl.rb +39 -0
  78. data/lib/grape/middleware/auth/strategies.rb +25 -0
  79. data/lib/grape/middleware/auth/strategy_info.rb +15 -0
  80. data/lib/grape/middleware/base.rb +89 -18
  81. data/lib/grape/middleware/error.rb +129 -10
  82. data/lib/grape/middleware/filter.rb +19 -0
  83. data/lib/grape/middleware/formatter.rb +124 -82
  84. data/lib/grape/middleware/globals.rb +14 -0
  85. data/lib/grape/middleware/stack.rb +109 -0
  86. data/lib/grape/middleware/versioner/accept_version_header.rb +38 -0
  87. data/lib/grape/middleware/versioner/base.rb +74 -0
  88. data/lib/grape/middleware/versioner/header.rb +127 -0
  89. data/lib/grape/middleware/versioner/param.rb +32 -0
  90. data/lib/grape/middleware/versioner/path.rb +40 -0
  91. data/lib/grape/middleware/versioner.rb +21 -21
  92. data/lib/grape/namespace.rb +46 -0
  93. data/lib/grape/params_builder/base.rb +18 -0
  94. data/lib/grape/params_builder/hash.rb +11 -0
  95. data/lib/grape/params_builder/hash_with_indifferent_access.rb +11 -0
  96. data/lib/grape/params_builder/hashie_mash.rb +11 -0
  97. data/lib/grape/params_builder.rb +32 -0
  98. data/lib/grape/parser/base.rb +16 -0
  99. data/lib/grape/parser/json.rb +14 -0
  100. data/lib/grape/parser/xml.rb +14 -0
  101. data/lib/grape/parser.rb +15 -0
  102. data/lib/grape/path.rb +76 -0
  103. data/lib/grape/presenters/presenter.rb +11 -0
  104. data/lib/grape/railtie.rb +9 -0
  105. data/lib/grape/request.rb +190 -0
  106. data/lib/grape/router/base_route.rb +39 -0
  107. data/lib/grape/router/greedy_route.rb +20 -0
  108. data/lib/grape/router/pattern.rb +81 -0
  109. data/lib/grape/router/route.rb +62 -0
  110. data/lib/grape/router.rb +190 -0
  111. data/lib/grape/serve_stream/file_body.rb +36 -0
  112. data/lib/grape/serve_stream/sendfile_response.rb +21 -0
  113. data/lib/grape/serve_stream/stream_response.rb +23 -0
  114. data/lib/grape/types/invalid_value.rb +8 -0
  115. data/lib/grape/util/base_inheritable.rb +43 -0
  116. data/lib/grape/util/cache.rb +17 -0
  117. data/lib/grape/util/endpoint_configuration.rb +8 -0
  118. data/lib/grape/util/header.rb +13 -0
  119. data/lib/grape/util/inheritable_setting.rb +100 -0
  120. data/lib/grape/util/inheritable_values.rb +29 -0
  121. data/lib/grape/util/lazy/block.rb +29 -0
  122. data/lib/grape/util/lazy/value.rb +38 -0
  123. data/lib/grape/util/lazy/value_array.rb +21 -0
  124. data/lib/grape/util/lazy/value_enumerable.rb +34 -0
  125. data/lib/grape/util/lazy/value_hash.rb +21 -0
  126. data/lib/grape/util/media_type.rb +70 -0
  127. data/lib/grape/util/registry.rb +27 -0
  128. data/lib/grape/util/reverse_stackable_values.rb +15 -0
  129. data/lib/grape/util/stackable_values.rb +36 -0
  130. data/lib/grape/util/strict_hash_configuration.rb +108 -0
  131. data/lib/grape/validations/attributes_doc.rb +60 -0
  132. data/lib/grape/validations/attributes_iterator.rb +62 -0
  133. data/lib/grape/validations/contract_scope.rb +34 -0
  134. data/lib/grape/validations/multiple_attributes_iterator.rb +13 -0
  135. data/lib/grape/validations/params_scope.rb +538 -0
  136. data/lib/grape/validations/single_attribute_iterator.rb +26 -0
  137. data/lib/grape/validations/types/array_coercer.rb +61 -0
  138. data/lib/grape/validations/types/custom_type_coercer.rb +164 -0
  139. data/lib/grape/validations/types/custom_type_collection_coercer.rb +56 -0
  140. data/lib/grape/validations/types/dry_type_coercer.rb +66 -0
  141. data/lib/grape/validations/types/file.rb +31 -0
  142. data/lib/grape/validations/types/invalid_value.rb +17 -0
  143. data/lib/grape/validations/types/json.rb +71 -0
  144. data/lib/grape/validations/types/multiple_type_coercer.rb +57 -0
  145. data/lib/grape/validations/types/primitive_coercer.rb +73 -0
  146. data/lib/grape/validations/types/set_coercer.rb +35 -0
  147. data/lib/grape/validations/types/variant_collection_coercer.rb +51 -0
  148. data/lib/grape/validations/types.rb +213 -0
  149. data/lib/grape/validations/validator_factory.rb +15 -0
  150. data/lib/grape/validations/validators/all_or_none_of_validator.rb +16 -0
  151. data/lib/grape/validations/validators/allow_blank_validator.rb +20 -0
  152. data/lib/grape/validations/validators/as_validator.rb +14 -0
  153. data/lib/grape/validations/validators/at_least_one_of_validator.rb +15 -0
  154. data/lib/grape/validations/validators/base.rb +88 -0
  155. data/lib/grape/validations/validators/coerce_validator.rb +75 -0
  156. data/lib/grape/validations/validators/contract_scope_validator.rb +41 -0
  157. data/lib/grape/validations/validators/default_validator.rb +37 -0
  158. data/lib/grape/validations/validators/exactly_one_of_validator.rb +17 -0
  159. data/lib/grape/validations/validators/except_values_validator.rb +24 -0
  160. data/lib/grape/validations/validators/length_validator.rb +49 -0
  161. data/lib/grape/validations/validators/multiple_params_base.rb +34 -0
  162. data/lib/grape/validations/validators/mutual_exclusion_validator.rb +16 -0
  163. data/lib/grape/validations/validators/presence_validator.rb +15 -0
  164. data/lib/grape/validations/validators/regexp_validator.rb +16 -0
  165. data/lib/grape/validations/validators/same_as_validator.rb +29 -0
  166. data/lib/grape/validations/validators/values_validator.rb +60 -0
  167. data/lib/grape/validations.rb +21 -0
  168. data/lib/grape/version.rb +6 -0
  169. data/lib/grape/xml.rb +10 -0
  170. data/lib/grape.rb +81 -17
  171. metadata +250 -192
  172. data/.document +0 -5
  173. data/.gitignore +0 -23
  174. data/.rspec +0 -2
  175. data/.rvmrc +0 -1
  176. data/Gemfile +0 -21
  177. data/Gemfile.lock +0 -58
  178. data/README.markdown +0 -75
  179. data/Rakefile +0 -70
  180. data/VERSION +0 -1
  181. data/autotest/discover.rb +0 -1
  182. data/lib/grape/middleware/auth/basic.rb +0 -30
  183. data/lib/grape/middleware/auth/oauth2.rb +0 -55
  184. data/lib/grape/middleware/prefixer.rb +0 -21
  185. data/lib/grape/middleware_stack.rb +0 -35
  186. data/spec/grape/api_spec.rb +0 -343
  187. data/spec/grape/endpoint_spec.rb +0 -104
  188. data/spec/grape/middleware/auth/basic_spec.rb +0 -31
  189. data/spec/grape/middleware/auth/oauth2_spec.rb +0 -88
  190. data/spec/grape/middleware/base_spec.rb +0 -63
  191. data/spec/grape/middleware/error_spec.rb +0 -49
  192. data/spec/grape/middleware/formatter_spec.rb +0 -87
  193. data/spec/grape/middleware/prefixer_spec.rb +0 -30
  194. data/spec/grape/middleware/versioner_spec.rb +0 -40
  195. data/spec/grape/middleware_stack_spec.rb +0 -47
  196. data/spec/grape_spec.rb +0 -1
  197. data/spec/spec_helper.rb +0 -20
data/lib/grape/api.rb CHANGED
@@ -1,214 +1,178 @@
1
- require 'rack/mount'
2
- require 'rack/auth/basic'
3
- require 'logger'
1
+ # frozen_string_literal: true
4
2
 
5
3
  module Grape
6
- # The API class is the primary entry point for
7
- # creating Grape APIs. Users should subclass this
8
- # class in order to build an API.
4
+ # The API class is the primary entry point for creating Grape APIs. Users
5
+ # should subclass this class in order to build an API.
9
6
  class API
7
+ # Class methods that we want to call on the API rather than on the API object
8
+ NON_OVERRIDABLE = %i[call call! configuration compile! inherited recognize_path].freeze
9
+
10
+ class Boolean
11
+ def self.build(val)
12
+ return nil if val != true && val != false
13
+
14
+ new
15
+ end
16
+ end
17
+
18
+ class Instance
19
+ Boolean = Grape::API::Boolean
20
+ end
21
+
10
22
  class << self
11
- attr_reader :route_set
12
-
13
- def logger
14
- @logger ||= Logger.new($STDOUT)
15
- end
16
-
17
- def reset!
18
- @settings = [{}]
19
- @route_set = Rack::Mount::RouteSet.new
20
- @prototype = nil
21
- end
22
-
23
- def call(env)
24
- logger.info "#{env['REQUEST_METHOD']} #{env['PATH_INFO']}"
25
- route_set.freeze.call(env)
26
- end
27
-
28
- # Settings are a stack, so when we
29
- # want to access them they are merged
30
- # in the order pushed.
31
- def settings
32
- @settings.inject({}){|f,h| f.merge!(h); f}
33
- end
34
-
35
- def settings_stack
36
- @settings
37
- end
38
-
39
- # Set a configuration value for this
40
- # namespace.
41
- #
42
- # @param key [Symbol] The key of the configuration variable.
43
- # @param value [Object] The value to which to set the configuration variable.
44
- def set(key, value)
45
- @settings.last[key.to_sym] = value
46
- end
47
-
48
- # Define a root prefix for your entire
49
- # API. For instance, if you had an api
50
- # that you wanted to be namespaced at
51
- # `/api/` you would do this:
52
- #
53
- # prefix '/api'
54
- def prefix(prefix = nil)
55
- prefix ? set(:root_prefix, prefix) : settings[:root_prefix]
56
- end
57
-
58
- def version(*new_versions, &block)
59
- new_versions.any? ? nest(block){ set(:version, new_versions) } : settings[:version]
60
- end
61
-
62
- # Specify the default format for the API's
63
- # serializers. Currently only `:json` is
64
- # supported.
65
- def default_format(new_format = nil)
66
- new_format ? set(:default_format, new_format.to_sym) : settings[:default_format]
67
- end
68
-
69
- # Add helper methods that will be accessible from any
70
- # endpoint within this namespace (and child namespaces).
71
- #
72
- # class ExampleAPI
73
- # helpers do
74
- # def current_user
75
- # User.find_by_id(params[:token])
76
- # end
77
- # end
78
- # end
79
- def helpers(&block)
23
+ extend Forwardable
24
+ attr_accessor :base_instance, :instances
25
+
26
+ delegate_missing_to :base_instance
27
+ def_delegators :base_instance, :new, :configuration
28
+
29
+ # This is the interface point between Rack and Grape; it accepts a request
30
+ # from Rack and ultimately returns an array of three values: the status,
31
+ # the headers, and the body. See [the rack specification]
32
+ # (http://www.rubydoc.info/github/rack/rack/master/file/SPEC) for more.
33
+ # NOTE: This will only be called on an API directly mounted on RACK
34
+ def_delegators :instance_for_rack, :call, :compile!
35
+
36
+ # When inherited, will create a list of all instances (times the API was mounted)
37
+ # It will listen to the setup required to mount that endpoint, and replicate it on any new instance
38
+ def inherited(api)
39
+ super
40
+
41
+ api.initial_setup(self == Grape::API ? Grape::API::Instance : @base_instance)
42
+ api.override_all_methods!
43
+ end
44
+
45
+ # Initialize the instance variables on the remountable class, and the base_instance
46
+ # an instance that will be used to create the set up but will not be mounted
47
+ def initial_setup(base_instance_parent)
48
+ @instances = []
49
+ @setup = []
50
+ @base_parent = base_instance_parent
51
+ @base_instance = mount_instance
52
+ end
53
+
54
+ # Redefines all methods so that are forwarded to add_setup and be recorded
55
+ def override_all_methods!
56
+ (base_instance.methods - Class.methods - NON_OVERRIDABLE).each do |method_override|
57
+ define_singleton_method(method_override) do |*args, &block|
58
+ add_setup(method: method_override, args: args, block: block)
59
+ end
60
+ end
61
+ end
62
+
63
+ # Configure an API from the outside. If a block is given, it'll pass a
64
+ # configuration hash to the block which you can use to configure your
65
+ # API. If no block is given, returns the configuration hash.
66
+ # The configuration set here is accessible from inside an API with
67
+ # `configuration` as normal.
68
+ def configure
69
+ config = @base_instance.configuration
80
70
  if block_given?
81
- m = settings_stack.last[:helpers] || Module.new
82
- m.class_eval &block
83
- set(:helpers, m)
71
+ yield config
72
+ self
84
73
  else
85
- m = Module.new
86
- settings_stack.each do |s|
87
- m.send :include, s[:helpers] if s[:helpers]
88
- end
89
- m
74
+ config
75
+ end
76
+ end
77
+
78
+ # The remountable class can have a configuration hash to provide some dynamic class-level variables.
79
+ # For instance, a description could be done using: `desc configuration[:description]` if it may vary
80
+ # depending on where the endpoint is mounted. Use with care, if you find yourself using configuration
81
+ # too much, you may actually want to provide a new API rather than remount it.
82
+ def mount_instance(configuration: nil)
83
+ Class.new(@base_parent).tap do |instance|
84
+ instance.configuration = Grape::Util::EndpointConfiguration.new(configuration || {})
85
+ instance.base = self
86
+ replay_setup_on(instance)
87
+ end
88
+ end
89
+
90
+ private
91
+
92
+ # Replays the set up to produce an API as defined in this class, can be called
93
+ # on classes that inherit from Grape::API
94
+ def replay_setup_on(instance)
95
+ @setup.each do |setup_step|
96
+ replay_step_on(instance, **setup_step)
90
97
  end
91
98
  end
92
-
93
- # Add an authentication type to the API. Currently
94
- # only `:http_basic` is supported.
95
- def auth(type = nil, options = {}, &block)
96
- if type
97
- set(:auth, {:type => type.to_sym, :proc => block}.merge(options))
99
+
100
+ def instance_for_rack
101
+ if never_mounted?
102
+ base_instance
98
103
  else
99
- settings[:auth]
104
+ mounted_instances.first
100
105
  end
101
106
  end
102
-
103
- # Add HTTP Basic authorization to the API.
104
- #
105
- # @param [Hash] options A hash of options.
106
- # @option options [String] :realm "API Authorization" The HTTP Basic realm.
107
- def http_basic(options = {}, &block)
108
- options[:realm] ||= "API Authorization"
109
- auth :http_basic, options, &block
110
- end
111
-
112
- # Defines a route that will be recognized
113
- # by the Grape API.
114
- #
115
- # @param methods [HTTP Verb(s)] One or more HTTP verbs that are accepted by this route. Set to `:any` if you want any verb to be accepted.
116
- # @param paths [String(s)] One or more strings representing the URL segment(s) for this route.
117
- # @param block [Proc] The code to be executed
118
- def route(methods, paths, &block)
119
- methods = Array(methods)
120
- paths = ['/'] if paths == []
121
- paths = Array(paths)
122
- endpoint = build_endpoint(&block)
123
-
124
- methods.each do |method|
125
- paths.each do |path|
126
- path = Rack::Mount::Strexp.compile(compile_path(path))
127
- route_set.add_route(endpoint,
128
- :path_info => path,
129
- :request_method => (method.to_s.upcase unless method == :any)
130
- )
131
- end
107
+
108
+ # Adds a new stage to the set up require to get a Grape::API up and running
109
+ def add_setup(step)
110
+ @setup << step
111
+ last_response = nil
112
+ @instances.each do |instance|
113
+ last_response = replay_step_on(instance, **step)
132
114
  end
115
+
116
+ refresh_mount_step if step[:method] != :mount
117
+ last_response
133
118
  end
134
-
135
- def get(*paths, &block); route('GET', paths, &block) end
136
- def post(*paths, &block); route('POST', paths, &block) end
137
- def put(*paths, &block); route('PUT', paths, &block) end
138
- def head(*paths, &block); route('HEAD', paths, &block) end
139
- def delete(*paths, &block); route('DELETE', paths, &block) end
140
-
141
- def namespace(space = nil, &block)
142
- if space || block_given?
143
- nest(block) do
144
- set(:namespace, space.to_s) if space
119
+
120
+ # Updating all previously mounted classes in the case that new methods have been executed.
121
+ def refresh_mount_step
122
+ @setup.each do |setup_step|
123
+ next if setup_step[:method] != :mount
124
+
125
+ refresh_mount_step = setup_step.merge(method: :refresh_mounted_api)
126
+ @setup << refresh_mount_step
127
+ @instances.each do |instance|
128
+ replay_step_on(instance, **refresh_mount_step)
145
129
  end
146
- else
147
- Rack::Mount::Utils.normalize_path(settings_stack.map{|s| s[:namespace]}.join('/'))
148
130
  end
149
131
  end
150
-
151
- alias_method :group, :namespace
152
- alias_method :resource, :namespace
153
- alias_method :resources, :namespace
154
-
155
- # Create a scope without affecting the URL.
156
- #
157
- # @param name [Symbol] Purely placebo, just allows to to name the scope to make the code more readable.
158
- def scope(name = nil, &block)
159
- nest(block)
160
- end
161
-
162
- protected
163
-
164
- # Execute first the provided block, then each of the
165
- # block passed in. Allows for simple 'before' setups
166
- # of settings stack pushes.
167
- def nest(*blocks, &block)
168
- blocks.reject!{|b| b.nil?}
169
- if blocks.any?
170
- settings_stack << {}
171
- instance_eval &block if block_given?
172
- blocks.each{|b| instance_eval &b}
173
- settings_stack.pop
132
+
133
+ def replay_step_on(instance, method:, args:, block:)
134
+ return if skip_immediate_run?(instance, args)
135
+
136
+ eval_args = evaluate_arguments(instance.configuration, *args)
137
+ response = instance.send(method, *eval_args, &block)
138
+ if skip_immediate_run?(instance, [response])
139
+ response
174
140
  else
175
- instance_eval &block
141
+ evaluate_arguments(instance.configuration, response).first
176
142
  end
177
143
  end
178
-
179
- def build_endpoint(&block)
180
- b = Rack::Builder.new
181
- b.use Grape::Middleware::Error
182
- b.use Rack::Auth::Basic, settings[:auth][:realm], &settings[:auth][:proc] if settings[:auth] && settings[:auth][:type] == :http_basic
183
- b.use Grape::Middleware::Prefixer, :prefix => prefix if prefix
184
- b.use Grape::Middleware::Versioner, :versions => (version if version.is_a?(Array)) if version
185
- b.use Grape::Middleware::Formatter, :default_format => default_format || :json
186
-
187
- endpoint = Grape::Endpoint.generate(&block)
188
- endpoint.send :include, helpers
189
- b.run endpoint
190
-
191
- b.to_app
192
- end
193
-
194
- def inherited(subclass)
195
- subclass.reset!
196
- end
197
-
198
- def route_set
199
- @route_set ||= Rack::Mount::RouteSet.new
200
- end
201
-
202
- def compile_path(path)
203
- parts = []
204
- parts << prefix if prefix
205
- parts << ':version' if version
206
- parts << namespace if namespace
207
- parts << path
208
- Rack::Mount::Utils.normalize_path(parts.join('/'))
209
- end
210
- end
211
-
212
- reset!
144
+
145
+ # Skips steps that contain arguments to be lazily executed (on re-mount time)
146
+ def skip_immediate_run?(instance, args)
147
+ instance.base_instance? &&
148
+ (any_lazy?(args) || args.any? { |arg| arg.is_a?(Hash) && any_lazy?(arg.values) })
149
+ end
150
+
151
+ def any_lazy?(args)
152
+ args.any? { |argument| argument.try(:lazy?) }
153
+ end
154
+
155
+ def evaluate_arguments(configuration, *args)
156
+ args.map do |argument|
157
+ if argument.try(:lazy?)
158
+ argument.evaluate_from(configuration)
159
+ elsif argument.is_a?(Hash)
160
+ argument.transform_values { |value| evaluate_arguments(configuration, value).first }
161
+ elsif argument.is_a?(Array)
162
+ evaluate_arguments(configuration, *argument)
163
+ else
164
+ argument
165
+ end
166
+ end
167
+ end
168
+
169
+ def never_mounted?
170
+ mounted_instances.empty?
171
+ end
172
+
173
+ def mounted_instances
174
+ instances - [base_instance]
175
+ end
176
+ end
213
177
  end
214
- end
178
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grape
4
+ module ContentTypes
5
+ module_function
6
+
7
+ # Content types are listed in order of preference.
8
+ DEFAULTS = {
9
+ xml: 'application/xml',
10
+ serializable_hash: 'application/json',
11
+ json: 'application/json',
12
+ binary: 'application/octet-stream',
13
+ txt: 'text/plain'
14
+ }.freeze
15
+
16
+ MIME_TYPES = Grape::ContentTypes::DEFAULTS.except(:serializable_hash).invert.freeze
17
+
18
+ def content_types_for(from_settings)
19
+ from_settings.presence || DEFAULTS
20
+ end
21
+
22
+ def mime_types_for(from_settings)
23
+ return MIME_TYPES if from_settings == Grape::ContentTypes::DEFAULTS
24
+
25
+ from_settings.each_with_object({}) do |(k, v), types_without_params|
26
+ # remove optional parameter
27
+ types_without_params[v.split(';', 2).first] = k
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grape
4
+ class Cookies
5
+ extend Forwardable
6
+
7
+ DELETED_COOKIES_ATTRS = {
8
+ max_age: '0',
9
+ value: '',
10
+ expires: Time.at(0)
11
+ }.freeze
12
+
13
+ def_delegators :cookies, :[], :each
14
+
15
+ def initialize(rack_cookies)
16
+ @cookies = rack_cookies
17
+ @send_cookies = nil
18
+ end
19
+
20
+ def response_cookies
21
+ return unless @send_cookies
22
+
23
+ send_cookies.each do |name|
24
+ yield name, cookies[name]
25
+ end
26
+ end
27
+
28
+ def []=(name, value)
29
+ cookies[name] = value
30
+ send_cookies << name
31
+ end
32
+
33
+ # see https://github.com/rack/rack/blob/main/lib/rack/utils.rb#L338-L340
34
+ def delete(name, **opts)
35
+ self.[]=(name, opts.merge(DELETED_COOKIES_ATTRS))
36
+ end
37
+
38
+ private
39
+
40
+ def cookies
41
+ return @cookies unless @cookies.is_a?(Proc)
42
+
43
+ @cookies = @cookies.call.with_indifferent_access
44
+ end
45
+
46
+ def send_cookies
47
+ @send_cookies ||= Set.new
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grape
4
+ module DryTypes
5
+ # Call +Dry.Types()+ to add all registered types to +DryTypes+ which is
6
+ # a container in this case. Check documentation for more information
7
+ # https://dry-rb.org/gems/dry-types/1.2/getting-started/
8
+ include Dry.Types()
9
+ end
10
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grape
4
+ module DSL
5
+ module API
6
+ extend ActiveSupport::Concern
7
+
8
+ include Grape::DSL::Validations
9
+ include Grape::DSL::Callbacks
10
+ include Grape::DSL::Configuration
11
+ include Grape::DSL::Helpers
12
+ include Grape::DSL::Middleware
13
+ include Grape::DSL::RequestResponse
14
+ include Grape::DSL::Routing
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grape
4
+ module DSL
5
+ # Blocks can be executed before or after every API call, using `before`, `after`,
6
+ # `before_validation` and `after_validation`.
7
+ #
8
+ # Before and after callbacks execute in the following order:
9
+ #
10
+ # 1. `before`
11
+ # 2. `before_validation`
12
+ # 3. _validations_
13
+ # 4. `after_validation`
14
+ # 5. _the API call_
15
+ # 6. `after`
16
+ #
17
+ # Steps 4, 5 and 6 only happen if validation succeeds.
18
+ module Callbacks
19
+ extend ActiveSupport::Concern
20
+
21
+ include Grape::DSL::Configuration
22
+
23
+ module ClassMethods
24
+ # Execute the given block before validation, coercion, or any endpoint
25
+ # code is executed.
26
+ def before(&block)
27
+ namespace_stackable(:befores, block)
28
+ end
29
+
30
+ # Execute the given block after `before`, but prior to validation or
31
+ # coercion.
32
+ def before_validation(&block)
33
+ namespace_stackable(:before_validations, block)
34
+ end
35
+
36
+ # Execute the given block after validations and coercions, but before
37
+ # any endpoint code.
38
+ def after_validation(&block)
39
+ namespace_stackable(:after_validations, block)
40
+ end
41
+
42
+ # Execute the given block after the endpoint code has run.
43
+ def after(&block)
44
+ namespace_stackable(:afters, block)
45
+ end
46
+
47
+ # Allows you to specify a something that will always be executed after a call
48
+ # API call. Unlike the `after` block, this code will run even on
49
+ # unsuccesful requests.
50
+ # @example
51
+ # class ExampleAPI < Grape::API
52
+ # before do
53
+ # ApiLogger.start
54
+ # end
55
+ # finally do
56
+ # ApiLogger.close
57
+ # end
58
+ # end
59
+ #
60
+ # This will make sure that the ApiLogger is opened and closed around every
61
+ # request
62
+ # @param ensured_block [Proc] The block to be executed after every api_call
63
+ def finally(&block)
64
+ namespace_stackable(:finallies, block)
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grape
4
+ module DSL
5
+ module Configuration
6
+ extend ActiveSupport::Concern
7
+
8
+ module ClassMethods
9
+ include Grape::DSL::Settings
10
+ include Grape::DSL::Logger
11
+ include Grape::DSL::Desc
12
+ end
13
+ end
14
+ end
15
+ end