Skip to content

Commit

Permalink
Allow a schema version to declare no fields
Browse files Browse the repository at this point in the history
  • Loading branch information
ararslan committed Oct 3, 2023
1 parent 927b2ac commit fe12c46
Show file tree
Hide file tree
Showing 2 changed files with 20 additions and 7 deletions.
5 changes: 2 additions & 3 deletions src/schemas.jl
Original file line number Diff line number Diff line change
Expand Up @@ -777,7 +777,7 @@ Note that this macro expects to be evaluated within top-level scope.
For more details and examples, please see `Legolas.jl/examples/tour.jl` and the
"Schema-Related Concepts/Conventions" section of the Legolas.jl documentation.
"""
macro version(record_type, declared_fields_block)
macro version(record_type, declared_fields_block=nothing)
# parse `record_type`
if record_type isa Symbol
parent_record_type = nothing
Expand All @@ -798,7 +798,6 @@ macro version(record_type, declared_fields_block)
if declared_fields_block isa Expr && declared_fields_block.head == :block && !isempty(declared_fields_block.args)
declared_field_statements = [f for f in declared_fields_block.args if !(f isa LineNumberNode)]
end
isempty(declared_field_statements) && return :(throw(SchemaVersionDeclarationError("malformed or missing field declaration(s)")))
declared_field_infos = DeclaredFieldInfo[]
for stmt in declared_field_statements
original_stmt = Base.Meta.quot(deepcopy(stmt))
Expand All @@ -817,7 +816,7 @@ macro version(record_type, declared_fields_block)
msg = string("cannot have field name which start with an underscore in `@version` declaration: ", invalid_field_names)
return :(throw(SchemaVersionDeclarationError($msg)))
end
declared_field_names_types = Expr(:tuple, (:($(f.name) = $(esc(f.type))) for f in declared_field_infos)...)
declared_field_names_types = Expr(:tuple, Expr(:parameters, (Expr(:kw, f.name, esc(f.type)) for f in declared_field_infos)...))

return quote
if !isdefined((@__MODULE__), :__legolas_schema_name_from_prefix__)
Expand Down
22 changes: 18 additions & 4 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,9 @@ end
y::String = string(y[1:2])
end

@schema "test.great-grandchild" GreatGrandchild
@version GreatGrandchildV1 > GrandchildV1

@schema "test.nested" Nested
@version NestedV1 begin
gc::GrandchildV1
Expand Down Expand Up @@ -302,10 +305,12 @@ end
b::Union{Int,Missing}
end

@schema "test.empty-inside" EmptyInside

@version EmptyInsideV1

@testset "`Legolas.@version` and associated utilities for declared `Legolas.SchemaVersion`s" begin
@testset "Legolas.SchemaVersionDeclarationError" begin
@test_throws SchemaVersionDeclarationError("malformed or missing field declaration(s)") eval(:(@version(NewV1, $(Expr(:block, LineNumberNode(1, :test))))))
@test_throws SchemaVersionDeclarationError("malformed or missing field declaration(s)") @version(ChildV2, begin end)
@test_throws SchemaVersionDeclarationError("missing prior `@schema` declaration for `Unknown` in current module") @version(UnknownV1 > ChildV1, begin x end)
@test_throws SchemaVersionDeclarationError("provided record type symbol is malformed: Child") @version(Child, begin x end)
@test_throws SchemaVersionDeclarationError("provided record type symbol is malformed: Childv2") @version(Childv2, begin x end)
Expand All @@ -332,28 +337,32 @@ end

@testset "Legolas.declared" begin
@test !Legolas.declared(undeclared)
@test all(Legolas.declared, (ParentV1SchemaVersion(), ChildV1SchemaVersion(), GrandchildV1SchemaVersion()))
@test all(Legolas.declared, (ParentV1SchemaVersion(), ChildV1SchemaVersion(), GrandchildV1SchemaVersion(), GreatGrandchildV1SchemaVersion()))
end

@testset "Legolas.parent" begin
@test isnothing(Legolas.parent(undeclared))
@test isnothing(Legolas.parent(ParentV1SchemaVersion()))
@test Legolas.parent(ChildV1SchemaVersion()) == ParentV1SchemaVersion()
@test Legolas.parent(GrandchildV1SchemaVersion()) == ChildV1SchemaVersion()
@test Legolas.parent(GreatGrandchildV1SchemaVersion()) == GrandchildV1SchemaVersion()
end

@testset "Legolas.identifier" begin
@test_throws Legolas.UnknownSchemaVersionError(undeclared) Legolas.identifier(undeclared)
@test Legolas.identifier(ParentV1SchemaVersion()) == "test.parent@1"
@test Legolas.identifier(ChildV1SchemaVersion()) == "test.child@1>test.parent@1"
@test Legolas.identifier(GrandchildV1SchemaVersion()) == "test.grandchild@1>test.child@1>test.parent@1"
@test Legolas.identifier(GreatGrandchildV1SchemaVersion()) == "test.great-grandchild@1>test.grandchild@1>test.child@1>test.parent@1"
end

@testset "Legolas.declared_fields" begin
@test_throws Legolas.UnknownSchemaVersionError(undeclared) Legolas.declared_fields(undeclared)
@test Legolas.declared_fields(EmptyInsideV1SchemaVersion()) == NamedTuple()
@test Legolas.declared_fields(ParentV1SchemaVersion()) == (x=Vector, y=AbstractString)
@test Legolas.declared_fields(ChildV1SchemaVersion()) == (x=Vector, y=AbstractString, z=Any)
@test Legolas.declared_fields(GrandchildV1SchemaVersion()) == (x=Vector, y=String, z=Any, a=Int32)
@test Legolas.declared_fields(GreatGrandchildV1SchemaVersion()) == (x=Vector, y=String, z=Any, a=Int32)

# xref https://github.com/beacon-biosignals/Legolas.jl/pull/100
@test Legolas.declared_fields(ParentV1SchemaVersion()) == (@test_deprecated Legolas.required_fields(ParentV1SchemaVersion()))
Expand All @@ -366,6 +375,8 @@ end
@test_throws Legolas.UnknownSchemaVersionError(undeclared) Legolas.complies_with(Tables.Schema((:a, :b), (Int, Int)), undeclared)
@test_throws Legolas.UnknownSchemaVersionError(undeclared) Legolas.find_violation(Tables.Schema((:a, :b), (Int, Int)), undeclared)

@test Legolas.complies_with(Tables.Schema((), ()), EmptyInsideV1SchemaVersion())

# Note that many of the basic properties of `find_violation`/`complies_with`/`validate`
# are unit-tested in `examples/tour.jl`; thus, we focus here on testing that these
# functions work as expected w.r.t. schema extension in particular.
Expand All @@ -389,7 +400,8 @@ end

# Multiple missing field violations
t = Tables.Schema((:a, :z), (Int32, Any))
for (s, id) in ((GrandchildV1SchemaVersion(), "test.grandchild@1"),
for (s, id) in ((GreatGrandchildV1SchemaVersion(), "test.great-grandchild@1"),
(GrandchildV1SchemaVersion(), "test.grandchild@1"),
(ChildV1SchemaVersion(), "test.child@1"),
(ParentV1SchemaVersion(), "test.parent@1"))
msg = """
Expand Down Expand Up @@ -449,6 +461,7 @@ end
@test Legolas.declaration(ChildV1SchemaVersion()) == ("test.child@1>test.parent@1" => [DeclaredFieldInfo(:z, :Any, false, :(z::Any = z))])
@test Legolas.declaration(GrandchildV1SchemaVersion()) == ("test.grandchild@1>test.child@1" => [DeclaredFieldInfo(:a, :Int32, false, :(a::Int32 = round(Int32, a))),
DeclaredFieldInfo(:y, :String, false, :(y::String = string(y[1:2])))])
@test Legolas.declaration(GreatGrandchildV1SchemaVersion()) == ("test.great-grandchild@1>test.grandchild@1" => DeclaredFieldInfo[])
@test Legolas.DeclaredFieldInfo === Legolas.RequiredFieldInfo # xref https://github.com/beacon-biosignals/Legolas.jl/pull/100
end

Expand All @@ -457,6 +470,7 @@ end
@test Legolas.record_type(ParentV1SchemaVersion()) == ParentV1
@test Legolas.record_type(ChildV1SchemaVersion()) == ChildV1
@test Legolas.record_type(GrandchildV1SchemaVersion()) == GrandchildV1
@test Legolas.record_type(GreatGrandchildV1SchemaVersion()) == GreatGrandchildV1
end

r0 = (x=[42], y="foo", z=:three, a=1.3)
Expand Down

0 comments on commit fe12c46

Please sign in to comment.