Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow a schema version to declare no fields #102

Merged
merged 1 commit into from
Oct 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading