grape 1.2.4 → 1.2.5
Sign up to get free protection for your applications and to get access to all the features.
- 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
|