Skip to content

Commit

Permalink
Declarative deps + autoregister
Browse files Browse the repository at this point in the history
  • Loading branch information
tisztamo committed Jan 27, 2021
1 parent ae552ec commit ddfa42b
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 13 deletions.
3 changes: 2 additions & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ authors = ["Krisztián Schaffer"]
version = "0.4.2"

[deps]
InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240"

[compat]
julia = "1.4"

[extras]
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[targets]
test = ["Test", "Random"]
27 changes: 22 additions & 5 deletions src/deps.jl
Original file line number Diff line number Diff line change
@@ -1,15 +1,32 @@
using InteractiveUtils

struct RegisteredPlugin
type::Type
deps::Vector{Type}
deps::Vector{<:Type}
end

const registry = IdDict{Type,RegisteredPlugin}()

function register(plugin::Type, deps::Vector{<:Type} = Type[]) # !?
registry[plugin] = RegisteredPlugin(plugin, deps)
function register(plugin::Type, dependencies = deps(plugin))
registry[plugin] = RegisteredPlugin(plugin, dependencies)
return nothing
end

deps(t) = Type[]

function autoregister(base=Plugin)
for t in subtypes(base) # TODO subtypes is extremely slow
if isconcretetype(t)
if !haskey(registry, t)
@debug "Auto-registering plugin type $t"
register(t)
end
else
autoregister(t)
end
end
end

# t1 implements t2 or
# t1 is an interface that is more specific than t2, or
# t1 and t2 are implementations and t1's direct interface
Expand Down Expand Up @@ -41,12 +58,12 @@ getplugin(p) = error("The $(typeof(p)) is instantiated. Please provide a type in

allplugins() = values(registry)
plugintypes(plugins) = map(p->p.type, plugins)
deps(plugins) = unique(Iterators.flatten(map(p->p.deps, plugins)))::Vector{Type}
plugindeps(plugins) = unique(Iterators.flatten(map(p->p.deps, plugins)))
findimplementation(req, impls) = findfirst(t -> t <: req, impls)

function missingdeps(plugins, dependencies)
deptypes = plugintypes(dependencies)
return filter(deps(plugins)) do p
return filter(plugindeps(plugins)) do p
isnothing(findimplementation(p, deptypes))
end
end
Expand Down
16 changes: 9 additions & 7 deletions test/deps.jl
Original file line number Diff line number Diff line change
Expand Up @@ -68,39 +68,41 @@ A more realistic example is a small module structure with complex dependencies.
Instantiated implementations receive instances of their dependencies as arguments
to the constructor, and may also receive options through kwargs.
"""
abstract type MI1 end
abstract type MI1 <: Plugin end
struct MImpl1 <: MI1
x1
x2
MImpl1(; x1=nothing, x2=nothing, options...) = new(x1, x2)
end

abstract type MI2 end
abstract type MI2 <: Plugin end
struct MImpl2 <: MI2
MImpl2(::MI1; options...) = new()
end
Plugins.deps(::Type{MImpl2}) = [MImpl1]

abstract type MI3 end
abstract type MI3 <: Plugin end
struct MImpl3 <: MI3
MImpl3(::MI1; options...) = new()
end

abstract type MI4 end
abstract type MI4 <: Plugin end
struct MImpl4 <: MI4
MImpl4(::MI1, ::MI2; options...) = new()
end

abstract type MI5 end
abstract type MI5 <: Plugin end
struct MImpl5 <: MI5
MImpl5(::MI4, ::MI2; options...) = new()
end
Plugins.deps(::Type{MImpl5}) = [MImpl4, MI2]

@testset "Dependency hierarchies are tracked" begin
Plugins.register(MImpl1)
Plugins.register(MImpl2, [MImpl1])
Plugins.register(MImpl2)
Plugins.register(MImpl3, [MI2])
Plugins.register(MImpl4, [MI1, MI2])
Plugins.register(MImpl5, [MImpl4, MI2])
Plugins.autoregister() # MImpl5
@test sort(Plugins.instantiation_order([MI1, MI2])) == sort([MImpl1, MImpl2])
@test sort(Plugins.instantiation_order([MI1, MI3])) == sort([MImpl1, MImpl2, MImpl3])
@test sort(Plugins.instantiation_order([MImpl3, MImpl1])) == sort([MImpl1, MImpl2, MImpl3])
Expand Down

0 comments on commit ddfa42b

Please sign in to comment.