Reference
Plugins.Configuration
— Typeabstract type Configuration <: ContextStage end
Stage that potentially changes the behavior of the program without evaluating previously unknown code.
Plugins.ContextStage
— Typeabstract type ContextStage <: Stage end
Stage technique that generates a new stage context type, which can be used for dispatching or in @generated
functions. Assembled types may be regenerated during a ContextStage
, depending on TODO.
A context stage does not create a new world but run in the same world than the previous stage.
Plugins.EvalStage
— Typeabstract type EvalStage <: Stage end
Stage technique that runs in a new world and generates a new stage context type.
Plugins.Extension
— Typeabstract type Extension <: EvalStage end
Stage that evaluates previously unknown code, e.g. loads new plugins.
Plugins.FieldSpec
— TypeFieldSpec(name, type::Type, constructor::Union{Function, DataType} = type)
Field specification for plugin-assembled types.
Note that every field of an assembled type will be constructed with the same arguments. Possibly The constructor will be called when the system
Plugins.HookList
— TypeHookList{TNext, THandler, TPlugin}
Provides fast, inlinable call to the implementations of a specific hook.
You can get a HookList by calling hooklist()
directly, or using hooks()
.
The HookList
can be called with arbitrary number of extra arguments. If any of the plugins referenced in the list fails to handle the extra arguments, the call will raise a MethodError
Plugins.ImmutableStruct
— Typestruct ImmutableStruct <: TemplateStyle end
Plugin-assembled types marked as ImmutableStruct
will be generated as a struct
.
Plugins.Initialization
— Typeabstract type Initialization <: EvalStage end
A classical Stage that runs before the normal operation of the program.
Multiple initialization stages may run, but only before the first non-Initialization stage.
Plugins.MutableStruct
— Typestruct MutableStruct <: TemplateStyle end
Plugin-assembled types marked as MutableStruct
will be generated as a mutable struct
.
Plugins.Optimization
— Typeabstract type Optimization <: ContextStage end
Stage that does not change the functional behavior of the program, only its performance characteristics.
Plugins.Plugin
— Typeabstract type Plugin
Provides default implementations of lifecycle hooks.
Plugins.PluginStack
— TypePluginStack(plugins, hookfns = [])
Manages the plugins loaded into an application.
It provides fast access to the plugins by symbol, e.g. pluginstack[:logger]
. Collection methods and iteration interface are implemented.
The pluginstack is created from a list of plugins, and optionally a list of hook functions. If hook functions are provided, the hooks()
` function can be called to
Plugins.Stage
— Typeabstract type Stage end
Base type that represents a step of iterated staging.
Iterated staging allows the program to repeatedly self-recompile its parts. The first iterations are
Stage
is the root of a layered type hierarchy:
- Direct subtypes of it (ContextStage, EvalStage) represent staging
techniques available in Julia.
- Downstream subtypes represent means of staging:
Initialization, Extension, Optimization, Configuration.
Plugins.TemplateStyle
— MethodTemplateStyle(::Type) = MutableStruct()
Trait to select the template used for plugin-assembled types
Use MutableStruct
(default), ImmutableStruct
, or subtype it when you want to create your own template.
#Examples
Assembling immutable structs:
abstract type DebugInfo end
Plugins.TemplateStyle(::Type{DebugInfo}) = Plugins.ImmutableStruct()
Defining your own template (see also typedef
):
struct CustomTemplate <: Plugins.TemplateStyle end
Plugins.TemplateStyle(::Type{State}) = CustomTemplate()
Plugins.autoregister
— Functionfunction autoregister(base=Plugin)
Find and register every concrete subtype of 'base' as a Plugin
Plugins.customfield
— Methodcustomfield(plugin::Plugin, abstract_type::Type, args...) = nothing
Provide field specifications to plugin-assembled types.
Using this lifecycle hook the system can define custom plugin-assembled types (typically structs) based on field specifications provided by plugins. E.g. an error type can be extended with debug information.
A plugin can provide zero or one field to every assembled type. To provide a field, return a FieldSpec
.
The assembled type will be a subtype of abstract_type
. To allow differently configured systems to run in the same Julia session, new types may be assembled for every instance of the system.
Although plugin-assmebled types are designed to help doing metaprogramming in a controlled fashion, it is usually better to use non-meta solutions instead. E.g. Store plugin state inside the plugin, collect data from multiple plugins using lifecycle hooks, etc.
Plugins.customtype
— Functioncustomtype(stack::PluginStack, typename::Symbol, abstract_type::Type, target_module::Module = Main; unique_name = true)
Assemble a type with fields provided by the plugins in stack
.
abstract_type
will be the supertype of the assembled type.
If unique_name
== true
, then typename
will be suffixed with a structure-dependent id. The id is generated as a hash of the evaluated expression (with the :TYPE_NAME placeholder used instead of the name), meaning that for the same id will be generated for a given type when the same plugins with the same source code are loaded.
Examples
Assembling a type AppStateImpl <: AppState
and parametrizing the app with it.
abstract type AppState end
mutable struct CustomFieldsApp{TCustomState}
state::TCustomState
function CustomFieldsApp(plugins, hookfns, stateargs...)
stack = PluginStack(plugins, hookfns)
state_type = customtype(stack, :AppStateImpl, AppState)
return new{state_type}(Base.invokelatest(state_type, stateargs...))
end
end
We need to use invokelatest
to instantiate a newly generated type. To use the generated type normally, first you have to allow control flow to go to the top-level scope after the type was generated. See also the docs
Assembling state types is an antipattern, because plugins can have their own state. (This may provide better performance in a few cases though) Assembled types can make your code less readable, use them sparingly!
Plugins.deps
— MethodPlugins.deps(::Type{T}) = Type[] # where T is your plugin type
Add a method to declare your dependencies.
The plugin type must have a constructor accepting an instance of every of their dependencies.
Examples
abstract type InterfaceLeft end struct ImplLeft <: InterfaceLeft end
abstract type InterfaceRight end struct ImplRight <: InterfaceRight end
Plugins.deps(::Type{ImplLeft}) = [ImplRight]
Plugins.enter_stage
— Methodenter_stage(plugin::Plugin, stage::Stage, args...)
Lifecycle hook marking the start of the next stage.
Types are reasssembled at this point. If stage isa EvalStage
, then the world is already updated. (execution reached toplevel)
Plugins.hook_cache
— Methodhook_cache(stack::PluginStack, hookfns)
hook_cache(plugins, hookfns)
Create a cache of HookList
s for a PluginStack or from lists of plugins and hook functions.
Returns a NamedTuple with an entry for every handler.
Examples
cache = hook_cache([Plugin1(), Plugin2()], [hook1, hook2])
cache.hook1()
Plugins.hooklist
— Methodfunction hooklist(plugins, hookfn)
Create a HookList which allows fast, inlinable call to the merged implementations of hookfn
by the given plugins.
A plugin of type TPlugin
found in plugins will be referenced in the resulting HookList if there is a method that matches the following signature: hookfn(::TPlugin, ...)
Plugins.hooks
— Functionhooks(app)
hooks(pluginstack::PluginStack)
Create or get a hook cache for stack
.
The first form can be used when pluginstack
is stored in app.plugins
(the recommended pattern).
When this function is called first time on a PluginStack
, the hooks cache will be created by calling hook_cache()
, and stored in pluginstack
for quick access later.
Plugins.leave_stage
— Methodleave_stage(plugin::Plugin, stage::Stage, nextstage::Stage, args...)
Lifecycle hook marking the end of a stage.
Plugins.prepare_stage
— Methodprepare_stage(plugin::Plugin, stage::Stage)
Lifecycle hook to prepare the plugin for the next stage. If the stage is an EvalStage
and the plugin needs to evaluate code, this is the point to do it.
Plugins.request_stage
— Methodrequest_stage(plugin::Plugin, args...)::Stage
Request a new stage iteration by returning a Stage representing it.
This lifecycle hook will be called repeatedly to ask plugins their wish to stage. If a plugin returns a Stage
instance and the request is accepted, the stage will start immediately.
If more than one plugins ask for staging, their request will be merged if possible and only one stage will run. If the stages are incompatible, meaning that different sets of plugins handle the prepeare
hook of the stages, then only a compatible subset of them will run.
Plugins should continue requesting staging until their wish gets fulfilled.
Plugins.setup!
— Methodsetup!(plugin, deps, args...)
Initialize the plugin with the given dependencies and arguments (e.g. shared state).
This lifecycle hook will be called when the application loads a plugin. Plugins.jl does not (yet) helps with this, application developers should do it manually, right after the PluginStack was created, before the hook_cache() call.
Plugins.shutdown!
— Methodshutdown!(plugin, args...)
Shut down the plugin.
This lifecycle hook will be called when the application unloads a plugin, e.g. before the application exits. Plugins.jl does not (yet) helps with this, application developers should do it manually.
Plugins.symbol
— Methodsymbol(plugin)
Return the per-PluginStack unique Symbol of this plugin if it exports a "late-bind" runtime API to other plugins.
Plugins.typedef
— Functiontypedef(templatestyle, spec::TypeSpec)::Expr
Return an expression defining a type.
Implement it for your own template styles. More info in the tests.