diff --git a/src/Tables.jl b/src/Tables.jl index 22be836..a940157 100644 --- a/src/Tables.jl +++ b/src/Tables.jl @@ -207,7 +207,27 @@ columnaccess(::Type{<:AbstractColumns}) = true columns(x::AbstractColumns) = x schema(x::AbstractColumns) = nothing -# default definitions +""" + Tables.isrowtable(x) => Bool + +For convenience, some table objects that are naturally "row oriented" can +define `Tables.isrowtable(::Type{TableType}) = true` to simplify satisfying the +Tables.jl interface. Requirements for defining `isrowtable` include: + * `Tables.rows(x) === x`, i.e. the table object itself is a `Row` iterator + * If the table object is mutable, it should support: + * `push!(x, row)`: allow pushing a single row onto table + * `append!(x, rows)`: allow appending set of rows onto table + * If table object is mutable and indexable, it should support: + * `x[i] = row`: allow replacing of a row with another row by index + +A table object that defines `Tables.isrowtable` will have definitions for +`Tables.istable`, `Tables.rowaccess`, and `Tables.rows` automatically defined. +""" +function isrowtable end + +isrowtable(::T) where {T} = isrowtable(T) +isrowtable(::Type{T}) where {T} = false + """ Tables.istable(x) => Bool @@ -220,7 +240,7 @@ of knowing that the generator is a table. function istable end istable(x::T) where {T} = istable(T) || TableTraits.isiterabletable(x) === true -istable(::Type{T}) where {T} = false +istable(::Type{T}) where {T} = isrowtable(T) """ Tables.rowaccess(x) => Bool @@ -239,7 +259,7 @@ natural for them to *consume* instead of worrying about what and how the input p function rowaccess end rowaccess(x::T) where {T} = rowaccess(T) -rowaccess(::Type{T}) where {T} = false +rowaccess(::Type{T}) where {T} = isrowtable(T) """ Tables.columnaccess(x) => Bool diff --git a/src/fallbacks.jl b/src/fallbacks.jl index 946bfe5..e921fcc 100644 --- a/src/fallbacks.jl +++ b/src/fallbacks.jl @@ -77,6 +77,7 @@ end # this is our generic Tables.rows fallback definition function rows(x::T) where {T} + isrowtable(x) && return x # because this method is being called, we know `x` didn't define it's own Tables.rows # first check if it supports column access, and if so, wrap it in a RowIterator if columnaccess(T) diff --git a/test/runtests.jl b/test/runtests.jl index 2a49a7f..c28b42e 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -741,3 +741,24 @@ Tables.columnnames(r::Columns) = fieldnames(Columns) @test Tables.schema(col) === nothing @test isequal(Tables.columntable(col), ct) end + +struct IsRowTable + rows::Vector{NamedTuple} +end + +Base.iterate(x::IsRowTable) = iterate(x.rows) +Base.iterate(x::IsRowTable, st) = iterate(x.rows, st) +Base.length(x::IsRowTable) = length(x.rows) + +Tables.isrowtable(::Type{IsRowTable}) = true + +@testset "Tables.isrowtable" begin + + nt = (a=1, b=3.14, c="hey") + rt = IsRowTable([nt, nt, nt]) + @test Tables.istable(rt) + @test Tables.rowaccess(rt) + @test Tables.rows(rt) === rt + @test Tables.columntable(rt) == Tables.columntable([nt, nt, nt]) + +end \ No newline at end of file