Skip to content

Commit

Permalink
Use a macro to declare required functions
Browse files Browse the repository at this point in the history
  • Loading branch information
oxinabox committed Oct 8, 2024
1 parent d0022b0 commit f516db2
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 15 deletions.
2 changes: 2 additions & 0 deletions docs/src/utilities.jl
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ A.accessors()

@test length(A.accessors()) == 35 #src

@test length(A.required_functions()) == 11

# You do not need to overload all of them (e.g., if you model does not have any
# genes you can completely omit all gene-related functions). The main required
# ones (esp. the reaction- and metabolite-related ones) will throw an error if
Expand Down
2 changes: 1 addition & 1 deletion src/AbstractFBCModels.jl
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ module AbstractFBCModels
using DocStringExtensions

include("types.jl")
include("utils.jl")
include("accessors.jl")
include("io.jl")
include("utils.jl")
include("testing.jl")
include("canonical.jl")

Expand Down
22 changes: 10 additions & 12 deletions src/accessors.jl
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ modeling, such as metabolite exchanges, separated forward and reverse reaction
directions, supplies of enzymatic and genetic material and virtual cell volume,
etc.
"""
reactions(a::AbstractFBCModel)::Vector{String} = unimplemented(typeof(a), :reactions)
@required reactions(a::AbstractFBCModel)::Vector{String}

"""
$(TYPEDSIGNATURES)
Expand All @@ -19,7 +19,7 @@ The number of reactions in the given model. Must be equal to the length of the
vector returned by [`reactions`](@ref), and may be more efficient for just
determining the size.
"""
n_reactions(a::AbstractFBCModel)::Int = unimplemented(typeof(a), :n_reactions)
@required n_reactions(a::AbstractFBCModel)::Int

"""
$(TYPEDSIGNATURES)
Expand All @@ -29,7 +29,7 @@ Return a vector of metabolite identifiers in a model.
As with [`reactions`](@ref), some metabolites in models may be virtual,
representing purely technical equality constraints.
"""
metabolites(a::AbstractFBCModel)::Vector{String} = unimplemented(typeof(a), :metabolites)
@required metabolites(a::AbstractFBCModel)::Vector{String}

"""
$(TYPEDSIGNATURES)
Expand All @@ -39,7 +39,7 @@ the vector returned by [`metabolites`](@ref), and may be more efficient for
just determining the size.
"""
function n_metabolites end
n_metabolites(a::AbstractFBCModel)::Int = unimplemented(typeof(a), :n_metabolites)
@required n_metabolites(a::AbstractFBCModel)::Int

"""
$(TYPEDSIGNATURES)
Expand All @@ -49,7 +49,7 @@ Return identifiers of all genes contained in the model. Empty if none.
Genes are also sometimes called "gene products" but we write genes for
simplicity.
"""
genes(a::AbstractFBCModel)::Vector{String} = unimplemented(typeof(a), :genes)
@required genes(a::AbstractFBCModel)::Vector{String}

"""
$(TYPEDSIGNATURES)
Expand All @@ -60,7 +60,7 @@ by [`genes`](@ref)).
This may be more efficient than calling [`genes`](@ref) and measuring the
array.
"""
n_genes(a::AbstractFBCModel)::Int = unimplemented(typeof(a), :n_genes)
@required n_genes(a::AbstractFBCModel)::Int

"""
$(TYPEDSIGNATURES)
Expand Down Expand Up @@ -94,7 +94,7 @@ The sparse stoichiometric matrix of a given model.
This usually corresponds to all the equality constraints in the model. The
matrix must be of size [`n_metabolites`](@ref) by [`n_reactions`](@ref).
"""
stoichiometry(a::AbstractFBCModel)::SparseMat = unimplemented(typeof(a), :stoichiometry)
@required stoichiometry(a::AbstractFBCModel)::SparseMat

"""
$(TYPEDSIGNATURES)
Expand All @@ -112,8 +112,7 @@ $(TYPEDSIGNATURES)
Lower and upper bounds of all reactions in the model.
"""
bounds(a::AbstractFBCModel)::Tuple{Vector{Float64},Vector{Float64}} =
unimplemented(typeof(a), :bounds)
@required bounds(a::AbstractFBCModel)::Tuple{Vector{Float64},Vector{Float64}}

"""
$(TYPEDSIGNATURES)
Expand All @@ -138,7 +137,7 @@ $(TYPEDSIGNATURES)
The objective vector of the model.
"""
objective(a::AbstractFBCModel)::SparseVec = unimplemented(typeof(a), :objective)
@required objective(a::AbstractFBCModel)::SparseVec

"""
$(TYPEDSIGNATURES)
Expand Down Expand Up @@ -189,8 +188,7 @@ maps the metabolite IDs to their stoichiometric coefficients.
Using this function may be more efficient in cases than loading the whole
[`stoichiometry`](@ref).
"""
reaction_stoichiometry(a::AbstractFBCModel, reaction_id::String)::Dict{String,Float64} =
unimplemented(typeof(a), :reaction_stoichiometry)
@required reaction_stoichiometry(a::AbstractFBCModel, reaction_id::String)::Dict{String,Float64}

"""
$(TYPEDSIGNATURES)
Expand Down
2 changes: 1 addition & 1 deletion src/io.jl
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ $(TYPEDSIGNATURES)
Save a model to the given path.
"""
save(a::AbstractFBCModel, path::String)::Nothing = unimplemented(typeof(a), :save)
@required save(a::AbstractFBCModel, path::String)

"""
$(TYPEDSIGNATURES)
Expand Down
48 changes: 47 additions & 1 deletion src/utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,40 @@ import Downloads: download
import SHA: sha256
import InteractiveUtils: methodswith


const REQUIRED_FUNCTIONS = Function[]
macro required(sig)
call_ex = sig.head == :(::) ? sig.args[1] : sig
call_ex.head == :call || error("malformed signature definition")
name = call_ex.args[1]
name isa Symbol || error("malformed signature definition")

model_arg_ex = call_ex.args[2]
model_arg_ex.head == :(::) || error("malformed signature definition")
model_arg = model_arg_ex.args[1]
model_arg isa Symbol || error("malformed signature definition")

return esc(quote
Base.@__doc__ $call_ex = $unimplemented(typeof($model_arg), $(Meta.quot(name)))
push!(REQUIRED_FUNCTIONS, $name)
$name
end)
end

unimplemented(t::Type, x::Symbol) =
error("AbstractFBCModels interface method $x is not implemented for type $t")

"""
$(TYPEDSIGNATURES)
Provide a `methodswith`-style listing of accessors that the model implementors
should implement.
may implement.
For typesystem reasons, the list **will not contain** methods for
[`save`](@ref) and [`filename_extensions`](@ref) that dispatch on type objects.
You should implement these as well.
See also [`required_functions`](@ref) for the minimal list that must be implemented.
"""
function accessors()
ms = Method[]
Expand All @@ -27,6 +49,30 @@ function accessors()
return ms
end

"""
$(TYPEDSIGNATURES)
Provide a `methodswith`-style listing of functions that the model implementors
must implement to have a functional `AbstractFBCModel`.
constrast this to the longer list of items that are returned by [`accessors`](@ref).
The extra elements have sensible defaults based on these required functions.
Where-as not defining these methods will result in errors.
Though depending on your models capability relying on those defaults may mean some
functionality is hidden.
(e.g. default [`coupling`](@ref) if you don't implement that is to assume none)
"""
function required_functions()
ms = Method[]
for f = REQUIRED_FUNCTIONS
methodswith(AbstractFBCModels.AbstractFBCModel, f, ms)
end
return ms
end




"""
$(TYPEDSIGNATURES)
Expand Down

0 comments on commit f516db2

Please sign in to comment.