DCATR.Manifest.Type behaviour (DCAT-R.ex v0.1.0)

Copy Markdown View Source

Behaviour for defining custom manifest types with extensible logic.

This module enables applications to create specialized manifest types by extending DCATR.Manifest with custom properties, loading logic, and resource extraction. The behaviour provides a hierarchical callback system with low-level building blocks and high-level orchestration points.

Callback Hierarchy

Low-level callbacks (per-file/resource operations):

  • init_dataset/1 - Initialize empty dataset with optional seed data
  • graph_name_for_file/2 - Classify files into named graphs during loading
  • load_file/2 - Load and preprocess individual RDF files

High-level callbacks (pipeline orchestration):

  • load_dataset/1 - Complete dataset loading pipeline (calls low-level callbacks)
  • load_manifest/2 - Extract service and additional resources from loaded dataset

Usage

Custom manifest types should use DCATR.Manifest.Type and define a Grax schema extending DCATR.Manifest:

defmodule MyApp.Manifest do
  use DCATR.Manifest.Type
  use Grax.Schema

  schema MyApp.NS.ManifestType < DCATR.Manifest do
    property custom_config: MyApp.NS.customConfig(), type: :string
    link agent: MyApp.NS.agent(), type: MyApp.Agent
  end

  @impl true
  def load_manifest(manifest, opts) do
    with {:ok, manifest} <- super(manifest, opts),
         {:ok, agent} <- load_custom_agent(manifest) do
      Grax.put(manifest, :agent, agent)
    end
  end
end

The module automatically provides convenience functions for accessing the manifest hierarchy: manifest/1, service/1, repository/1, dataset/1 (with ! variants).

Summary

Callbacks

Generates manifest files from templates for a project.

Returns the path to the template directory used for manifest generation.

Low-level callback for determining the graph name for a file during loading.

Low-level callback for initializing the manifest dataset before loading files.

High-level callback for orchestrating the complete dataset loading process.

Low-level callback for loading and preprocessing a single RDF file.

High-level callback for extracting and loading resources from the dataset.

Functions

Returns the service type module for a given manifest type.

Types

schema()

@type schema() :: Grax.Schema.t()

t()

@type t() :: module()

Callbacks

generate(project_dir, opts)

@callback generate(project_dir :: Path.t(), opts :: keyword()) :: :ok | {:error, any()}

Generates manifest files from templates for a project.

See DCATR.Manifest.Generator for details on the generation process which the default implementation uses.

generator_template()

@callback generator_template() :: String.t()

Returns the path to the template directory used for manifest generation.

The default implementation points to DCATR.Manifest.Generator.default_template_dir/0. Override this to provide custom manifest templates for your manifest type.

graph_name_for_file(file, opts)

@callback graph_name_for_file(file :: String.t(), opts :: keyword()) ::
  RDF.Statement.graph_name() | nil

Low-level callback for determining the graph name for a file during loading.

Called by load_file/2 to classify loaded graphs. Override this to add custom file classification patterns (e.g., based on filename patterns or content inspection).

The default implementation calls DCATR.Manifest.Loader.graph_name_for_file/2.

Example Override

@impl true
def graph_name_for_file(file, opts) do
  super(file, opts) ||
    cond do
      file =~ ~r/store\.(test|dev|prod)\.(ttl|nt)$/ -> DCATR.Manifest.Loader.service_manifest_graph_name()
      file =~ ~r/history\./ -> RDF.bnode("history")
      true -> nil
    end
end

init_dataset(opts)

@callback init_dataset(opts :: keyword()) :: {:ok, RDF.Dataset.t()} | {:error, any()}

Low-level callback for initializing the manifest dataset before loading files.

Called by load_dataset/1 at the start of the loading process, before any files are read. Override this to inject seed data or perform custom initialization.

The default implementation DCATR.Manifest.Loader.init_dataset/1 returns an empty dataset.

Example Override

@impl true
def init_dataset(opts) do
  # Add seed triples before file loading
  {:ok, RDF.Dataset.new(...)}
end

load_dataset(opts)

@callback load_dataset(opts :: keyword()) :: {:ok, RDF.Dataset.t()} | {:error, any()}

High-level callback for orchestrating the complete dataset loading process.

Called by DCATR.Manifest.Loader.load/2 to load all manifest files and merge them into a dataset. Override this to customize the loading pipeline or add dataset-level preprocessing after all files are loaded.

The default implementation calls DCATR.Manifest.Loader.load_dataset/1.

Example Override

@impl true
def load_dataset(opts) do
  with {:ok, dataset} <- super(opts) do
    preprocess(dataset)
  end
end

load_file(file, opts)

@callback load_file(file :: String.t(), opts :: keyword()) ::
  {:ok, RDF.Dataset.t() | RDF.Graph.t() | nil} | {:error, any()}

Low-level callback for loading and preprocessing a single RDF file.

Called by load_dataset/1 for each file during the loading process. Override this to add custom preprocessing (e.g., template expansion, validation) before the file data is merged into the dataset.

Return {:ok, nil} to skip a file (e.g., based on custom filtering logic).

The default implementation calls DCATR.Manifest.Loader.load_file/2.

Example Override

@impl true
def load_file(file, opts) do
  cond do
    file =~ ~r/\.ignore.nt\./ -> {:ok, nil}  # Skip files matching pattern
    true -> super(file, opts)
  end
end

load_manifest(schema, opts)

@callback load_manifest(schema(), opts :: keyword()) :: {:ok, schema()} | {:error, any()}

High-level callback for extracting and loading resources from the dataset.

Called by DCATR.Manifest.Loader.load/2 after the dataset is loaded. Override this to load additional resources (e.g., agents, policies) alongside the service.

The default implementation calls DCATR.Manifest.Loader.load_manifest/2.

Options

  • :service_id - Explicit service ID (skips auto-detection)

Example Override

@impl true
def load_manifest(manifest, opts) do
  with {:ok, manifest} <- super(manifest, opts),
       {:ok, agent} <- Agent.load(manifest.dataset, agent_id(), ...) do
    Grax.put(manifest, :agent, agent)
  end
end

Functions

service_type(manifest_type)

@spec service_type(module()) :: module()

Returns the service type module for a given manifest type.

Extracts the DCATR.Service.Type from the manifest's :service property schema definition.

Examples

iex> DCATR.Manifest.Type.service_type(DCATR.Manifest)
DCATR.Service