grape 1.2.4 → 1.2.5
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 +18 -4
- data/README.md +144 -4
- data/grape.gemspec +3 -1
- data/lib/grape.rb +94 -66
- data/lib/grape/api.rb +46 -4
- data/lib/grape/api/instance.rb +23 -12
- data/lib/grape/dsl/desc.rb +11 -2
- data/lib/grape/dsl/validations.rb +4 -3
- data/lib/grape/eager_load.rb +18 -0
- data/lib/grape/endpoint.rb +3 -3
- data/lib/grape/error_formatter.rb +1 -1
- data/lib/grape/exceptions/validation_errors.rb +4 -2
- data/lib/grape/formatter.rb +1 -1
- data/lib/grape/middleware/auth/base.rb +2 -4
- data/lib/grape/middleware/base.rb +2 -0
- data/lib/grape/middleware/helpers.rb +10 -0
- data/lib/grape/parser.rb +1 -1
- data/lib/grape/util/base_inheritable.rb +34 -0
- data/lib/grape/util/inheritable_values.rb +5 -25
- data/lib/grape/util/lazy_block.rb +25 -0
- data/lib/grape/util/lazy_value.rb +5 -0
- data/lib/grape/util/reverse_stackable_values.rb +7 -36
- data/lib/grape/util/stackable_values.rb +19 -22
- data/lib/grape/validations/attributes_iterator.rb +5 -3
- data/lib/grape/validations/multiple_attributes_iterator.rb +11 -0
- data/lib/grape/validations/params_scope.rb +12 -12
- data/lib/grape/validations/single_attribute_iterator.rb +13 -0
- data/lib/grape/validations/validator_factory.rb +6 -11
- data/lib/grape/validations/validators/all_or_none.rb +6 -13
- data/lib/grape/validations/validators/at_least_one_of.rb +5 -13
- data/lib/grape/validations/validators/base.rb +11 -10
- data/lib/grape/validations/validators/coerce.rb +4 -0
- data/lib/grape/validations/validators/default.rb +1 -1
- data/lib/grape/validations/validators/exactly_one_of.rb +6 -23
- data/lib/grape/validations/validators/multiple_params_base.rb +14 -10
- data/lib/grape/validations/validators/mutual_exclusion.rb +6 -18
- data/lib/grape/version.rb +1 -1
- data/spec/grape/api/defines_boolean_in_params_spec.rb +37 -0
- data/spec/grape/api_remount_spec.rb +158 -0
- data/spec/grape/api_spec.rb +72 -0
- data/spec/grape/endpoint_spec.rb +1 -1
- data/spec/grape/exceptions/base_spec.rb +4 -0
- data/spec/grape/exceptions/validation_errors_spec.rb +6 -4
- data/spec/grape/integration/rack_spec.rb +22 -6
- data/spec/grape/middleware/base_spec.rb +8 -0
- data/spec/grape/middleware/formatter_spec.rb +11 -1
- data/spec/grape/validations/multiple_attributes_iterator_spec.rb +29 -0
- data/spec/grape/validations/params_scope_spec.rb +13 -0
- data/spec/grape/validations/single_attribute_iterator_spec.rb +33 -0
- data/spec/grape/validations/validators/all_or_none_spec.rb +138 -30
- data/spec/grape/validations/validators/at_least_one_of_spec.rb +173 -29
- data/spec/grape/validations/validators/coerce_spec.rb +6 -2
- data/spec/grape/validations/validators/exactly_one_of_spec.rb +202 -38
- data/spec/grape/validations/validators/mutual_exclusion_spec.rb +184 -27
- data/spec/grape/validations_spec.rb +32 -20
- metadata +103 -115
- data/Appraisals +0 -28
- 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.lock +0 -232
- data/gemfiles/rack_edge.gemfile +0 -35
- data/gemfiles/rails_3.gemfile +0 -36
- data/gemfiles/rails_3.gemfile.lock +0 -288
- data/gemfiles/rails_4.gemfile +0 -35
- data/gemfiles/rails_4.gemfile.lock +0 -280
- data/gemfiles/rails_5.gemfile +0 -35
- data/gemfiles/rails_5.gemfile.lock +0 -312
- data/gemfiles/rails_edge.gemfile +0 -35
- data/pkg/grape-1.2.0.gem +0 -0
- data/pkg/grape-1.2.1.gem +0 -0
- data/pkg/grape-1.2.3.gem +0 -0
data/lib/grape/api.rb
CHANGED
@@ -6,7 +6,7 @@ module Grape
|
|
6
6
|
# should subclass this class in order to build an API.
|
7
7
|
class API
|
8
8
|
# Class methods that we want to call on the API rather than on the API object
|
9
|
-
NON_OVERRIDABLE = (Class.new.methods + %i[call call! configuration]).freeze
|
9
|
+
NON_OVERRIDABLE = (Class.new.methods + %i[call call! configuration compile!]).freeze
|
10
10
|
|
11
11
|
class << self
|
12
12
|
attr_accessor :base_instance, :instances
|
@@ -42,13 +42,28 @@ module Grape
|
|
42
42
|
end
|
43
43
|
end
|
44
44
|
|
45
|
+
# Configure an API from the outside. If a block is given, it'll pass a
|
46
|
+
# configuration hash to the block which you can use to configure your
|
47
|
+
# API. If no block is given, returns the configuration hash.
|
48
|
+
# The configuration set here is accessible from inside an API with
|
49
|
+
# `configuration` as normal.
|
50
|
+
def configure
|
51
|
+
config = @base_instance.configuration
|
52
|
+
if block_given?
|
53
|
+
yield config
|
54
|
+
self
|
55
|
+
else
|
56
|
+
config
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
45
60
|
# This is the interface point between Rack and Grape; it accepts a request
|
46
61
|
# from Rack and ultimately returns an array of three values: the status,
|
47
62
|
# the headers, and the body. See [the rack specification]
|
48
63
|
# (http://www.rubydoc.info/github/rack/rack/master/file/SPEC) for more.
|
49
64
|
# NOTE: This will only be called on an API directly mounted on RACK
|
50
65
|
def call(*args, &block)
|
51
|
-
|
66
|
+
instance_for_rack.call(*args, &block)
|
52
67
|
end
|
53
68
|
|
54
69
|
# Allows an API to itself be inheritable:
|
@@ -106,8 +121,21 @@ module Grape
|
|
106
121
|
end
|
107
122
|
end
|
108
123
|
|
124
|
+
def compile!
|
125
|
+
require 'grape/eager_load'
|
126
|
+
instance_for_rack.compile! # See API::Instance.compile!
|
127
|
+
end
|
128
|
+
|
109
129
|
private
|
110
130
|
|
131
|
+
def instance_for_rack
|
132
|
+
if never_mounted?
|
133
|
+
base_instance
|
134
|
+
else
|
135
|
+
mounted_instances.first
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
111
139
|
# Adds a new stage to the set up require to get a Grape::API up and running
|
112
140
|
def add_setup(method, *args, &block)
|
113
141
|
setup_step = { method: method, args: args, block: block }
|
@@ -121,7 +149,13 @@ module Grape
|
|
121
149
|
|
122
150
|
def replay_step_on(instance, setup_step)
|
123
151
|
return if skip_immediate_run?(instance, setup_step[:args])
|
124
|
-
|
152
|
+
args = evaluate_arguments(instance.configuration, *setup_step[:args])
|
153
|
+
response = instance.send(setup_step[:method], *args, &setup_step[:block])
|
154
|
+
if skip_immediate_run?(instance, [response])
|
155
|
+
response
|
156
|
+
else
|
157
|
+
evaluate_arguments(instance.configuration, response).first
|
158
|
+
end
|
125
159
|
end
|
126
160
|
|
127
161
|
# Skips steps that contain arguments to be lazily executed (on re-mount time)
|
@@ -137,7 +171,7 @@ module Grape
|
|
137
171
|
def evaluate_arguments(configuration, *args)
|
138
172
|
args.map do |argument|
|
139
173
|
if argument.respond_to?(:lazy?) && argument.lazy?
|
140
|
-
|
174
|
+
argument.evaluate_from(configuration)
|
141
175
|
elsif argument.is_a?(Hash)
|
142
176
|
argument.map { |key, value| [key, evaluate_arguments(configuration, value).first] }.to_h
|
143
177
|
elsif argument.is_a?(Array)
|
@@ -147,6 +181,14 @@ module Grape
|
|
147
181
|
end
|
148
182
|
end
|
149
183
|
end
|
184
|
+
|
185
|
+
def never_mounted?
|
186
|
+
mounted_instances.empty?
|
187
|
+
end
|
188
|
+
|
189
|
+
def mounted_instances
|
190
|
+
instances - [base_instance]
|
191
|
+
end
|
150
192
|
end
|
151
193
|
end
|
152
194
|
end
|
data/lib/grape/api/instance.rb
CHANGED
@@ -13,12 +13,11 @@ module Grape
|
|
13
13
|
attr_accessor :configuration
|
14
14
|
|
15
15
|
def given(conditional_option, &block)
|
16
|
-
evaluate_as_instance_with_configuration(block) if conditional_option && block_given?
|
16
|
+
evaluate_as_instance_with_configuration(block, lazy: true) if conditional_option && block_given?
|
17
17
|
end
|
18
18
|
|
19
19
|
def mounted(&block)
|
20
|
-
|
21
|
-
evaluate_as_instance_with_configuration(block)
|
20
|
+
evaluate_as_instance_with_configuration(block, lazy: true)
|
22
21
|
end
|
23
22
|
|
24
23
|
def base=(grape_api)
|
@@ -61,7 +60,7 @@ module Grape
|
|
61
60
|
# the headers, and the body. See [the rack specification]
|
62
61
|
# (http://www.rubydoc.info/github/rack/rack/master/file/SPEC) for more.
|
63
62
|
def call(env)
|
64
|
-
|
63
|
+
compile!
|
65
64
|
call!(env)
|
66
65
|
end
|
67
66
|
|
@@ -79,9 +78,14 @@ module Grape
|
|
79
78
|
end
|
80
79
|
end
|
81
80
|
|
81
|
+
def compile!
|
82
|
+
return if instance
|
83
|
+
LOCK.synchronize { compile unless instance }
|
84
|
+
end
|
85
|
+
|
82
86
|
# see Grape::Router#recognize_path
|
83
87
|
def recognize_path(path)
|
84
|
-
|
88
|
+
compile!
|
85
89
|
instance.router.recognize_path(path)
|
86
90
|
end
|
87
91
|
|
@@ -105,14 +109,21 @@ module Grape
|
|
105
109
|
end
|
106
110
|
end
|
107
111
|
|
108
|
-
def evaluate_as_instance_with_configuration(block)
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
+
def evaluate_as_instance_with_configuration(block, lazy: false)
|
113
|
+
lazy_block = Grape::Util::LazyBlock.new do |configuration|
|
114
|
+
value_for_configuration = configuration
|
115
|
+
if value_for_configuration.respond_to?(:lazy?) && value_for_configuration.lazy?
|
116
|
+
self.configuration = value_for_configuration.evaluate
|
117
|
+
end
|
118
|
+
response = instance_eval(&block)
|
119
|
+
self.configuration = value_for_configuration
|
120
|
+
response
|
121
|
+
end
|
122
|
+
if base_instance? && lazy
|
123
|
+
lazy_block
|
124
|
+
else
|
125
|
+
lazy_block.evaluate_from(configuration)
|
112
126
|
end
|
113
|
-
response = instance_eval(&block)
|
114
|
-
self.configuration = value_for_configuration
|
115
|
-
response
|
116
127
|
end
|
117
128
|
|
118
129
|
def inherited(subclass)
|
data/lib/grape/dsl/desc.rb
CHANGED
@@ -49,8 +49,17 @@ module Grape
|
|
49
49
|
#
|
50
50
|
def desc(description, options = {}, &config_block)
|
51
51
|
if block_given?
|
52
|
-
|
53
|
-
|
52
|
+
endpoint_configuration = if defined?(configuration)
|
53
|
+
# When the instance is mounted - the configuration is executed on mount time
|
54
|
+
if configuration.respond_to?(:evaluate)
|
55
|
+
configuration.evaluate
|
56
|
+
# Within `given` or `mounted blocks` the configuration is already evaluated
|
57
|
+
elsif configuration.is_a?(Hash)
|
58
|
+
configuration
|
59
|
+
end
|
60
|
+
end
|
61
|
+
endpoint_configuration ||= {}
|
62
|
+
config_class = desc_container(endpoint_configuration)
|
54
63
|
|
55
64
|
config_class.configure do
|
56
65
|
description description
|
@@ -27,10 +27,11 @@ module Grape
|
|
27
27
|
setting = description_field(:params)
|
28
28
|
setting ||= description_field(:params, {})
|
29
29
|
Array(names).each do |name|
|
30
|
-
|
31
|
-
setting[
|
30
|
+
full_name = name[:full_name].to_s
|
31
|
+
setting[full_name] ||= {}
|
32
|
+
setting[full_name].merge!(opts)
|
32
33
|
|
33
|
-
namespace_stackable(:params,
|
34
|
+
namespace_stackable(:params, full_name => opts)
|
34
35
|
end
|
35
36
|
end
|
36
37
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
Grape.eager_load!
|
2
|
+
Grape::Http.eager_load!
|
3
|
+
Grape::Exceptions.eager_load!
|
4
|
+
Grape::Extensions.eager_load!
|
5
|
+
Grape::Extensions::ActiveSupport.eager_load!
|
6
|
+
Grape::Extensions::Hashie.eager_load!
|
7
|
+
Grape::Middleware.eager_load!
|
8
|
+
Grape::Middleware::Auth.eager_load!
|
9
|
+
Grape::Middleware::Versioner.eager_load!
|
10
|
+
Grape::Util.eager_load!
|
11
|
+
Grape::ErrorFormatter.eager_load!
|
12
|
+
Grape::Formatter.eager_load!
|
13
|
+
Grape::Parser.eager_load!
|
14
|
+
Grape::DSL.eager_load!
|
15
|
+
Grape::API.eager_load!
|
16
|
+
Grape::Presenters.eager_load!
|
17
|
+
Grape::ServeFile.eager_load!
|
18
|
+
Rack::Head # AutoLoads the Rack::Head
|
data/lib/grape/endpoint.rb
CHANGED
@@ -200,7 +200,7 @@ module Grape
|
|
200
200
|
end
|
201
201
|
|
202
202
|
def merge_route_options(**default)
|
203
|
-
options[:route_options].clone.merge(**default)
|
203
|
+
options[:route_options].clone.merge!(**default)
|
204
204
|
end
|
205
205
|
|
206
206
|
def map_routes
|
@@ -353,7 +353,7 @@ module Grape
|
|
353
353
|
def run_validators(validator_factories, request)
|
354
354
|
validation_errors = []
|
355
355
|
|
356
|
-
validators = validator_factories.map(
|
356
|
+
validators = validator_factories.map { |options| Grape::Validations::ValidatorFactory.create_validator(options) }
|
357
357
|
|
358
358
|
ActiveSupport::Notifications.instrument('endpoint_run_validators.grape', endpoint: self, validators: validators, request: request) do
|
359
359
|
validators.each do |validator|
|
@@ -363,7 +363,7 @@ module Grape
|
|
363
363
|
validation_errors << e
|
364
364
|
break if validator.fail_fast?
|
365
365
|
rescue Grape::Exceptions::ValidationArrayErrors => e
|
366
|
-
validation_errors
|
366
|
+
validation_errors.concat e.errors
|
367
367
|
break if validator.fail_fast?
|
368
368
|
end
|
369
369
|
end
|
@@ -14,7 +14,7 @@ module Grape
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def formatters(options)
|
17
|
-
builtin_formatters.merge(default_elements).merge(options[:error_formatters] || {})
|
17
|
+
builtin_formatters.merge(default_elements).merge!(options[:error_formatters] || {})
|
18
18
|
end
|
19
19
|
|
20
20
|
def formatter_for(api_format, **options)
|
@@ -39,14 +39,16 @@ module Grape
|
|
39
39
|
end
|
40
40
|
|
41
41
|
def full_messages
|
42
|
-
map { |attributes, error| full_message(attributes, error) }
|
42
|
+
messages = map { |attributes, error| full_message(attributes, error) }
|
43
|
+
messages.uniq!
|
44
|
+
messages
|
43
45
|
end
|
44
46
|
|
45
47
|
private
|
46
48
|
|
47
49
|
def full_message(attributes, error)
|
48
50
|
I18n.t(
|
49
|
-
'grape.errors.format'
|
51
|
+
'grape.errors.format',
|
50
52
|
default: '%{attributes} %{message}',
|
51
53
|
attributes: attributes.count == 1 ? translate_attribute(attributes.first) : translate_attributes(attributes),
|
52
54
|
message: error.message
|
data/lib/grape/formatter.rb
CHANGED
@@ -14,7 +14,7 @@ module Grape
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def formatters(options)
|
17
|
-
builtin_formmaters.merge(default_elements).merge(options[:formatters] || {})
|
17
|
+
builtin_formmaters.merge(default_elements).merge!(options[:formatters] || {})
|
18
18
|
end
|
19
19
|
|
20
20
|
def formatter_for(api_format, **options)
|
@@ -4,6 +4,8 @@ module Grape
|
|
4
4
|
module Middleware
|
5
5
|
module Auth
|
6
6
|
class Base
|
7
|
+
include Helpers
|
8
|
+
|
7
9
|
attr_accessor :options, :app, :env
|
8
10
|
|
9
11
|
def initialize(app, **options)
|
@@ -11,10 +13,6 @@ module Grape
|
|
11
13
|
@options = options
|
12
14
|
end
|
13
15
|
|
14
|
-
def context
|
15
|
-
env[Grape::Env::API_ENDPOINT]
|
16
|
-
end
|
17
|
-
|
18
16
|
def call(env)
|
19
17
|
dup._call(env)
|
20
18
|
end
|
data/lib/grape/parser.rb
CHANGED
@@ -0,0 +1,34 @@
|
|
1
|
+
module Grape
|
2
|
+
module Util
|
3
|
+
# Base for classes which need to operate with own values kept
|
4
|
+
# in the hash and inherited values kept in a Hash-like object.
|
5
|
+
class BaseInheritable
|
6
|
+
attr_accessor :inherited_values
|
7
|
+
attr_accessor :new_values
|
8
|
+
|
9
|
+
# @param inherited_values [Object] An object implementing an interface
|
10
|
+
# of the Hash class.
|
11
|
+
def initialize(inherited_values = {})
|
12
|
+
@inherited_values = inherited_values
|
13
|
+
@new_values = {}
|
14
|
+
end
|
15
|
+
|
16
|
+
def delete(key)
|
17
|
+
new_values.delete key
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize_copy(other)
|
21
|
+
super
|
22
|
+
self.inherited_values = other.inherited_values
|
23
|
+
self.new_values = other.new_values.dup
|
24
|
+
end
|
25
|
+
|
26
|
+
def keys
|
27
|
+
combined = inherited_values.keys
|
28
|
+
combined.concat(new_values.keys)
|
29
|
+
combined.uniq!
|
30
|
+
combined
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -1,14 +1,8 @@
|
|
1
|
+
require_relative 'base_inheritable'
|
2
|
+
|
1
3
|
module Grape
|
2
4
|
module Util
|
3
|
-
class InheritableValues
|
4
|
-
attr_accessor :inherited_values
|
5
|
-
attr_accessor :new_values
|
6
|
-
|
7
|
-
def initialize(inherited_values = {})
|
8
|
-
self.inherited_values = inherited_values
|
9
|
-
self.new_values = {}
|
10
|
-
end
|
11
|
-
|
5
|
+
class InheritableValues < BaseInheritable
|
12
6
|
def [](name)
|
13
7
|
values[name]
|
14
8
|
end
|
@@ -17,26 +11,12 @@ module Grape
|
|
17
11
|
new_values[name] = value
|
18
12
|
end
|
19
13
|
|
20
|
-
def delete(key)
|
21
|
-
new_values.delete key
|
22
|
-
end
|
23
|
-
|
24
14
|
def merge(new_hash)
|
25
|
-
values.merge(new_hash)
|
26
|
-
end
|
27
|
-
|
28
|
-
def keys
|
29
|
-
(new_values.keys + inherited_values.keys).sort.uniq
|
15
|
+
values.merge!(new_hash)
|
30
16
|
end
|
31
17
|
|
32
18
|
def to_hash
|
33
|
-
values
|
34
|
-
end
|
35
|
-
|
36
|
-
def initialize_copy(other)
|
37
|
-
super
|
38
|
-
self.inherited_values = other.inherited_values
|
39
|
-
self.new_values = other.new_values.dup
|
19
|
+
values
|
40
20
|
end
|
41
21
|
|
42
22
|
protected
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Grape
|
2
|
+
module Util
|
3
|
+
class LazyBlock
|
4
|
+
def initialize(&new_block)
|
5
|
+
@block = new_block
|
6
|
+
end
|
7
|
+
|
8
|
+
def evaluate_from(configuration)
|
9
|
+
@block.call(configuration)
|
10
|
+
end
|
11
|
+
|
12
|
+
def evaluate
|
13
|
+
@block.call({})
|
14
|
+
end
|
15
|
+
|
16
|
+
def lazy?
|
17
|
+
true
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_s
|
21
|
+
evaluate.to_s
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|