When working with macros, you might have one that allows specifying a name, type, and an optional default value.
It might look something like this
param query : String = "default query"
The code for the macro will take in one parameter and that parameter will be a TypeDeclaration
.
macro param(type_decl)
# ...
end
If you imagine that this param
macro is for a web framework and used to define the query params for an endpoint, it makes sense that it would raise an error if the query is not nilable and no default value is given in the method that it defines.
The code for that could be (forgive me for this mess)
macro param(type_decl)
def {{ type_decl.var }} : {{ type_decl.type }}
{% is_nilable_type = type_decl.type.is_a?(Union) %}
val = params.get?(:{{ type_declaration.var.id }})
return val unless val.nil?
{% if is_nilable_type %}
{{ type_declaration.value || nil }}
{% else %}
default_or_nil = {{ type_declaration.value || nil }}
if default_or_nil.nil?
raise MissingParamError.new("{{ type_declaration.var.id }}")
else
default_or_nil
end
{% end %}
end
end
end
Can you see the problem with this?
What if a param was this instead param show_deleted : Bool = false
?
The problem is the line default_or_nil = {{ type_declaration.value || nil }}
. It means that any time the value is not provided, it uses the default which is false
and is obviously falsey which means that default_or_nil
evaluates to nil and ends up raising the error.
The solution is to check if the value is a Nop
instead of checking the truthiness of the default value since value is a Nop
when it isn't defined.
Instead of
default_or_nil = {{ type_declaration.value || nil }}
it should be
default_or_nil = {{ type_declaration.value.is_a?(Nop) ? nil : type_declaration.value }}
Here's the relevant PR for the fix made in Lucky.
Top comments (0)