Skip to content

Commit 5946f28

Browse files
authored
fix: correct Product constructor (#13)
test: add test for Product and NoMetadata
1 parent 2967353 commit 5946f28

File tree

7 files changed

+174
-43
lines changed

7 files changed

+174
-43
lines changed

src/dataset.jl

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,5 @@ function DataSet(ld::LDataSet; kwargs...)
7272
end
7373

7474
Base.parent(ds::AbstractDataSet) = ds.data
75-
for f in (:getindex, :iterate, :size, :length, :keys)
76-
@eval Base.$f(var::AbstractDataSet, args...) = $f(parent(var), args...)
77-
end
78-
7975
Base.getindex(ds::DataSet, i::Integer) = _nth(values(ds.data), i)
8076
Base.push!(ds::DataSet, v) = push!(ds.data, v)

src/metadata.jl

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,14 @@ struct NoMetadata end
1313
const NoData = NoMetadata
1414

1515
Base.keys(::NoMetadata) = ()
16+
Base.values(::NoMetadata) = ()
17+
Base.iterate(::NoMetadata) = nothing
1618

1719
# Allow merging NoMetadata with a Dict or keyword arguments
18-
Base.merge(::NoMetadata, d::AbstractDict) = copy(d)
19-
Base.merge(::NoMetadata, p::Base.Pairs) = Dict{Any,Any}(p)
20+
Base.merge(::NoMetadata, d::AbstractDict) = length(d) == 0 ? NoMetadata() : copy(d)
21+
Base.merge(::NoMetadata, p::Base.Pairs) = length(p) == 0 ? NoMetadata() : p
2022
Base.merge(m::NoMetadata, d, rest...) = merge(merge(m, d), rest...)
2123

2224
Base.haskey(::NoMetadata, args...) = false
2325
Base.get(::NoMetadata, key, default=nothing) = default
24-
Base.length(::NoMetadata) = 0
25-
for f in (:NamedTuple, :Dict)
26-
@eval Base.$f(::NoMetadata) = $f()
27-
@eval Base.convert(::Type{$f}, ::NoMetadata) = $f()
28-
end
26+
Base.length(::NoMetadata) = 0

src/product.jl

Lines changed: 18 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
1+
# References:
2+
# https://github.com/MakieOrg/AlgebraOfGraphics.jl/blob/master/src/algebra/layer.jl
3+
14
struct Product{A,F,MD} <: AbstractProduct
25
data::A
36
transformation::F
47
name::Union{String,Symbol}
58
metadata::MD
6-
function Product(data::A, transformation::F, name="", metadata::MD=NoMetadata(); kwargs...) where {A,F,MD}
7-
metadata = merge(metadata, kwargs)
8-
new{A,F,MD}(data, transformation, name, metadata)
9-
end
109
end
1110

12-
function Product(data; transformation=identity, name="", metadata=NoMetadata(), kwargs...)
13-
Product(data, transformation, name, metadata; kwargs...)
11+
function Product(data::A, transformation=identity; name="", metadata=NoMetadata(), kwargs...) where {A}
12+
new_meta = merge(metadata, kwargs)
13+
Product(data, transformation, name, new_meta)
1414
end
1515

16-
data(p::Product) = p.data
17-
func(p::AbstractProduct) = identity
16+
Base.parent(p::Product) = p.data
17+
func(::AbstractProduct) = identity
1818
func(p::Product) = p.transformation
1919

20-
(p::AbstractProduct)(args...; kwargs...) = func(p)(data(p), args...; kwargs...)
20+
(p::AbstractProduct)(args...; kwargs...) = func(p)(parent(p), args...; kwargs...)
2121

2222
"""Create a new product with the composed function"""
2323
(f, p::AbstractProduct) = @set p.transformation = f func(p)
@@ -26,22 +26,19 @@ func(p::Product) = p.transformation
2626
# Allow chaining of transformations with multiple products
2727
(g::AbstractProduct, f::AbstractProduct) = @set g.transformation = func(g) func(f)
2828

29-
function set(ds::AbstractProduct, args...; name=nothing, data=nothing, kwargs...)
30-
!isnothing(name) && (ds = @set ds.name = name)
31-
!isnothing(data) && (ds = @set ds.data = data)
32-
(!isnothing(kwargs) || !isnothing(args)) && (ds = @set ds.metadata = set(ds.metadata, args...; kwargs...))
33-
return ds
29+
function set(p::Product; name=nothing, data=nothing, transformation=nothing, metadata=nothing, kwargs...)
30+
!isnothing(name) && (p = @set p.name = name)
31+
!isnothing(data) && (p = @set p.data = data)
32+
!isnothing(transformation) && (p = @set p.transformation = transformation)
33+
!isnothing(metadata) && (p = @set p.metadata = metadata)
34+
length(kwargs) > 0 && (p = @set p.metadata = merge(p.metadata, kwargs))
35+
return p
3436
end
3537

36-
function set!!(ds::Product, args...; name=nothing, data=nothing, kwargs...)
37-
!isnothing(name) && (ds = @set ds.name = name)
38-
!isnothing(data) && (ds = @set ds.data = data)
39-
set!(ds.metadata, args...; kwargs...)
40-
ds
41-
end
38+
set(p::Product, data, transformation=nothing; kwargs...) = set(p; data, transformation, kwargs...)
4239

4340
function Base.show(io::IO, p::Product)
4441
n = name(p)
45-
isempty(n) ? print(io, data(p)) : print(io, n)
42+
isempty(n) ? print(io, parent(p)) : print(io, n)
4643
func(p) !== identity && print(io, " [", func(p), "]")
4744
end

src/types.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ Base.push!(p::Union{Project,Instrument}, v) = insert!(p, name(v), v)
5757
Base.get(var::AbstractModel, s, d=nothing) = get(meta(var), s, d)
5858
Base.get(f::Function, var::AbstractModel, s) = get(f, meta(var), s)
5959

60+
for f in (:getindex, :iterate, :size, :length, :keys, :values)
61+
@eval Base.$f(var::AbstractProduct, args...) = $f(parent(var), args...)
62+
end
63+
6064
# https://github.com/rafaqz/DimensionalData.jl/blob/main/src/array/show.jl
6165

6266
Base.show(io::IO, p::T) where {T<:AbstractModel} = print(io, name(p))

src/utils.jl

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -89,17 +89,6 @@ function rename!(d::Dict, old_keys::Union{Tuple,Vector}, new_key)
8989
end
9090
end
9191

92-
function set!(d::Dict, args::Pair...; kwargs...)
93-
foreach(args) do (k, v)
94-
d[k] = v
95-
end
96-
merge!(d, kwargs)
97-
end
98-
99-
function set(d::Dict, args::Pair...; kwargs...)
100-
return merge(d, Dict(args...), kwargs)
101-
end
102-
10392
# https://github.com/rafaqz/DimensionalData.jl/blob/main/src/Dimensions/show.jl#L5
10493
function colors(i)
10594
colors = [209, 32, 81, 204, 249, 166, 37]

test/metadata.jl

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
@testitem "NoMetadata" begin
2+
using SpaceDataModel: NoMetadata
3+
nm = NoMetadata()
4+
5+
# Test keys operation
6+
@test keys(nm) == () == keys(NamedTuple())
7+
@test length(keys(nm)) == 0
8+
@info values(nm)
9+
10+
# Test haskey operation
11+
@test haskey(nm, "any_key") == false
12+
@test haskey(nm, :symbol_key) == false
13+
# Test get operation with default
14+
@test get(nm, "key", nothing) == nothing
15+
end
16+
17+
@testitem "NoMetadata Merging Operations" begin
18+
using SpaceDataModel: NoMetadata
19+
20+
nm = NoMetadata()
21+
22+
# Test merging with Dict
23+
dict1 = Dict("key1" => "value1", "key2" => "value2")
24+
merged1 = merge(nm, dict1)
25+
@test merged1 == dict1
26+
@test merged1 !== dict1 # Should be a copy
27+
@test merge(nm, Dict()) == nm # Test merging with empty Dict
28+
end
29+
30+
@testitem "NoMetadata Type Conversions" begin
31+
using SpaceDataModel: NoMetadata
32+
nm = NoMetadata()
33+
# Test conversion to NamedTuple
34+
@test NamedTuple(nm) == (;)
35+
# Test conversion to Dict
36+
@test Dict(pairs(nm)) == Dict()
37+
end
38+
39+
@testitem "NoMetadata in Product Context" begin
40+
using SpaceDataModel: NoMetadata, Product
41+
42+
# Test that NoMetadata works correctly in Product constructor
43+
nm = NoMetadata()
44+
45+
# When kwargs are provided, NoMetadata gets merged into Dict
46+
p2 = Product([1, 2, 3]; metadata=nm, extra_key="value")
47+
@test p2.metadata isa AbstractDict
48+
@test p2.metadata[:extra_key] == "value"
49+
50+
# Test default behavior
51+
p3 = Product([1, 2, 3])
52+
@test p3.metadata isa NoMetadata
53+
end

test/product.jl

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
@testitem "Product Construction" begin
2+
using SpaceDataModel: Product, func, NoMetadata
3+
4+
# Test basic construction with minimal parameters
5+
p1 = Product([1, 2, 3])
6+
@test parent(p1) == [1, 2, 3]
7+
@test func(p1) === identity
8+
@test p1.name == ""
9+
@test p1.metadata isa NoMetadata # NoMetadata() becomes Dict when merged with kwargs
10+
11+
# Test construction with transformation function
12+
p2 = Product([1, 2, 3], x -> x .* 2)
13+
@test parent(p2) == [1, 2, 3]
14+
@test func(p2)([1, 2, 3]) == [2, 4, 6]
15+
16+
# Test construction with name
17+
p3 = Product([1, 2, 3]; name="test_data")
18+
@test p3.name == "test_data"
19+
20+
# Test construction with metadata
21+
p4 = Product([1, 2, 3]; metadata=Dict("units" => "m/s"))
22+
@test p4.metadata["units"] == "m/s"
23+
24+
# Test construction with kwargs that become metadata
25+
p5 = Product([1, 2, 3]; units="m/s", description="velocity")
26+
@test p5.metadata isa Base.Pairs
27+
@test p5.metadata[:units] == "m/s"
28+
@test p5.metadata[:description] == "velocity"
29+
30+
# Test full constructor
31+
p6 = Product([1, 2, 3], x -> x .+ 1, "full_test", Dict("key" => "value"))
32+
@test parent(p6) == [1, 2, 3]
33+
@test func(p6)([1, 2, 3]) == [2, 3, 4]
34+
@test p6.name == "full_test"
35+
@test p6.metadata["key"] == "value"
36+
end
37+
38+
@testitem "Product Methods" begin
39+
using SpaceDataModel: Product
40+
p1 = Product([1, 4, 9], x -> sqrt.(x))
41+
@test all(p1 .== [1, 4, 9])
42+
@test_nowarn sprint(show, p1)
43+
end
44+
45+
@testitem "Product Function Application and Composition" begin
46+
using SpaceDataModel: Product
47+
48+
p1 = Product([1, 2, 3], x -> x .* 2)
49+
@test p1() == [2, 4, 6]
50+
51+
# Test calling with additional arguments
52+
p2 = Product([1, 2, 3], (x, y) -> x .+ y)
53+
@test p2(10) == [11, 12, 13]
54+
55+
# Test with identity transformation
56+
p3 = Product([1, 2, 3])
57+
@test p3() == [1, 2, 3]
58+
59+
# Test composition with function on left
60+
p1 = Product([1, 2, 3], x -> x .* 2)
61+
p2 = Product([1, 2, 3], x -> x .+ 1)
62+
p3 = (x -> x .+ 1) p1
63+
@test p3() == [3, 5, 7] # (x * 2) + 1
64+
65+
# Test composition with function on right
66+
p4 = p1 (x -> x .+ 1)
67+
@test p4() == [4, 6, 8] # (x + 1) * 2
68+
69+
# Test composition between products
70+
p5 = p1 p2
71+
@test p5() == [4, 6, 8] # (x + 1) * 2
72+
end
73+
74+
@testitem "Product Setting Operations" begin
75+
using SpaceDataModel: Product, set
76+
77+
# Test immutable set operations
78+
p1 = Product([1, 2, 3]; name="original")
79+
80+
# Test setting name
81+
p2 = set(p1; name="new_name")
82+
@test p2.name == "new_name"
83+
@test p1.name == "original" # Original unchanged
84+
85+
# Test setting data
86+
data = [4, 5, 6]
87+
@test set(p1; data)() == data
88+
@test set(p1, data)() == data
89+
90+
# Test setting metadata
91+
p4 = set(p1; units="m/s", description="velocity")
92+
@test p4.metadata[:units] == "m/s"
93+
@test p4.metadata[:description] == "velocity"
94+
end

0 commit comments

Comments
 (0)