Skip to content

Commit 54f253b

Browse files
committed
fix: fixes issue where nil couldn't be deliberately provided
1 parent 712843b commit 54f253b

File tree

2 files changed

+33
-9
lines changed

2 files changed

+33
-9
lines changed

lib/rapid/argument_set.rb

+29-8
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,16 @@
99
module Rapid
1010
class ArgumentSet
1111

12+
# This is a constant that represents a missing value where `nil` means
13+
# the user actually wanted to send null/nil.
14+
class MissingValue
15+
16+
def self.singleton
17+
@singleton ||= new
18+
end
19+
20+
end
21+
1222
extend Defineable
1323

1424
class << self
@@ -57,12 +67,13 @@ def initialize(hash, path: [], request: nil)
5767
@source = self.class.definition.arguments.each_with_object({}) do |(arg_key, argument), source|
5868
given_value = lookup_value(hash, arg_key, argument, request)
5969

60-
next if given_value.nil? && !argument.required?
61-
62-
if given_value.nil?
70+
if argument.required? && (given_value.nil? || given_value.is_a?(MissingValue))
6371
raise MissingArgumentError.new(argument, path: @path + [argument])
6472
end
6573

74+
# If the given value is missing, we'll just skip adding this to the hash
75+
next if given_value.is_a?(MissingValue)
76+
6677
given_value = parse_value(argument, given_value)
6778
validation_errors = argument.validate_value(given_value)
6879
unless validation_errors.empty?
@@ -112,12 +123,19 @@ def lookup_value(hash, key, argument, request)
112123
elsif hash.key?(key.to_sym)
113124
hash[key.to_sym]
114125
else
115-
value_from_route(argument, request) || argument.default
126+
route_value = value_from_route(argument, request)
127+
return route_value unless route_value.is_a?(MissingValue)
128+
return argument.default unless argument.default.nil?
129+
130+
MissingValue.singleton
116131
end
117132
end
118133

119134
def parse_value(argument, value, index: nil, in_array: false)
120-
if argument.array? && value.is_a?(Array)
135+
if value.nil?
136+
nil
137+
138+
elsif argument.array? && value.is_a?(Array)
121139
value.each_with_index.map do |v, i|
122140
parse_value(argument, v, index: i, in_array: true)
123141
end
@@ -167,12 +185,15 @@ def check_for_missing_required_arguments
167185
end
168186

169187
def value_from_route(argument, request)
170-
return if request.nil?
171-
return if request.route.nil?
188+
return MissingValue.singleton if request.nil?
189+
return MissingValue.singleton if request.route.nil?
172190

173191
route_args = request.route.extract_arguments(request.api_path)
174-
value_for_arg = route_args[argument.name.to_s]
192+
unless route_args.key?(argument.name.to_s)
193+
return MissingValue.singleton
194+
end
175195

196+
value_for_arg = route_args[argument.name.to_s]
176197
return nil if value_for_arg.nil?
177198

178199
if argument.type.argument_set?

spec/specs/rapid/argument_set_spec.rb

+4-1
Original file line numberDiff line numberDiff line change
@@ -347,11 +347,14 @@
347347
as = Rapid::ArgumentSet.create('ExampleSet') do
348348
argument :name, type: :string
349349
argument :age, type: :integer
350+
argument :favourite_color, type: :string
350351
end
351-
instance = as.new({ name: 'Dave', age: 40, invalid: '999' })
352+
instance = as.new({ name: 'Dave', age: 40, invalid: '999', favourite_color: nil })
352353
expect(instance.to_hash).to be_a Hash
353354
expect(instance.to_hash[:name]).to eq 'Dave'
354355
expect(instance.to_hash[:age]).to eq 40
356+
expect(instance.to_hash[:favourite_color]).to be nil
357+
expect(instance.to_hash.keys).to include :favourite_color
355358
expect(instance.to_hash.keys).to_not include :invalid
356359
end
357360

0 commit comments

Comments
 (0)