Agenda.Place (Agenda v0.3.0)

Copy Markdown View Source

A place — a named location that contains resources and other places.

Places form a tree of arbitrary depth. A campus contains buildings, a building contains floors, a floor contains rooms; the library never interprets what a level means, only how the levels nest.

The tree exists so that travel between two resources can be derived rather than configured. A flat location: :sydney attribute can answer "is this room in Sydney?", but not "can someone get from here to there in the ten-minute break?" — and only the second question decides whether a programme is workable. separation/2 is the primitive that answers it.

As an iCalendar VLOCATION

RFC 9073's VLOCATION is the nearest iCalendar has to a place, and the mapping loses the very thing the tree is for:

  • name is VLOCATION's NAME.

  • location_type is its LOCATION-TYPE, the RFC 4589 location types (:office, :hotel, :classroom, …) written as atoms. It is no part of a place's identity, which is its chain of names.

  • within has no equivalent. A VLOCATION does not nest, so an export writes a place alone, or the places on its path/1 as separate VLOCATIONs with nothing to say which holds which. contains?/2, common_ancestor/2, separation/2 and Agenda.travel_time/3, all derived from the nesting, do not survive a round trip: a place read back from iCalendar is a root.

Summary

Types

How far apart two places are: 0 when they are the same place, n when the deeper of the two is n levels below their nearest common ancestor, and :disjoint when they share no ancestor at all.

t()

A place in the containment tree.

Functions

The nearest place enclosing both a and b, or nil when they share no ancestor.

true when outer encloses inner, at any depth. A place contains itself.

Build a place, optionally inside another.

The chain of places from the root down to place, inclusive.

The outermost place enclosing place, or place itself when it is already a root.

How far apart two places are, measured in levels of the containment tree.

Types

separation()

@type separation() :: non_neg_integer() | :disjoint

How far apart two places are: 0 when they are the same place, n when the deeper of the two is n levels below their nearest common ancestor, and :disjoint when they share no ancestor at all.

t()

@type t() :: %Agenda.Place{
  location_type: [atom()],
  name: String.t(),
  within: t() | nil
}

A place in the containment tree.

Functions

common_ancestor(a, b)

@spec common_ancestor(t(), t()) :: t() | nil

The nearest place enclosing both a and b, or nil when they share no ancestor.

Arguments

  • a and b are each a t/0.

Returns

  • the innermost common t/0, or

  • nil when the two are in unrelated trees.

Examples

iex> sydney = Agenda.Place.new("Sydney Convention Centre")
iex> level_2 = Agenda.Place.new("Level 2", within: sydney)
iex> level_3 = Agenda.Place.new("Level 3", within: sydney)
iex> Agenda.Place.common_ancestor(level_2, level_3).name
"Sydney Convention Centre"

contains?(outer, inner)

@spec contains?(t(), t()) :: boolean()

true when outer encloses inner, at any depth. A place contains itself.

Arguments

  • outer is the enclosing t/0.

  • inner is the enclosed t/0.

Returns

  • true or false.

Examples

iex> sydney = Agenda.Place.new("Sydney Convention Centre")
iex> level_2 = Agenda.Place.new("Level 2", within: sydney)
iex> Agenda.Place.contains?(sydney, level_2)
true

iex> sydney = Agenda.Place.new("Sydney Convention Centre")
iex> level_2 = Agenda.Place.new("Level 2", within: sydney)
iex> Agenda.Place.contains?(level_2, sydney)
false

new(name, options \\ [])

@spec new(String.t(), keyword()) :: t()

Build a place, optionally inside another.

Arguments

  • name is the place's name.

  • options is a keyword list of options.

Options

  • :within is the enclosing t/0. The default is nil, making this place a root.

  • :location_type is what kind of place it is, RFC 9073's LOCATION-TYPE: a list of RFC 4589 location types written as atoms, such as [:office] or [:hotel, :restaurant]. One atom is a list of one. The default is [].

Returns

Examples

iex> sydney = Agenda.Place.new("Sydney Convention Centre")
iex> level_2 = Agenda.Place.new("Level 2", within: sydney)
iex> level_2.within.name
"Sydney Convention Centre"

iex> Agenda.Place.new("Head office", location_type: :office).location_type
[:office]

path(place)

@spec path(t()) :: [t()]

The chain of places from the root down to place, inclusive.

Arguments

  • place is a t/0.

Returns

  • a list of t/0 ordered outermost first.

Examples

iex> sydney = Agenda.Place.new("Sydney Convention Centre")
iex> level_2 = Agenda.Place.new("Level 2", within: sydney)
iex> Enum.map(Agenda.Place.path(level_2), & &1.name)
["Sydney Convention Centre", "Level 2"]

root(place)

@spec root(t()) :: t()

The outermost place enclosing place, or place itself when it is already a root.

Arguments

  • place is a t/0.

Returns

Examples

iex> sydney = Agenda.Place.new("Sydney Convention Centre")
iex> level_2 = Agenda.Place.new("Level 2", within: sydney)
iex> Agenda.Place.root(level_2).name
"Sydney Convention Centre"

separation(a, b)

@spec separation(t(), t()) :: separation()

How far apart two places are, measured in levels of the containment tree.

This is the primitive travel time is derived from: the further up the tree you must climb to get from one place to the other, the longer the journey.

Arguments

  • a and b are each a t/0.

Returns

  • 0 when a and b are the same place,

  • a positive integer — the number of levels from the deeper place up to the nearest common ancestor, or

  • :disjoint when the two share no ancestor.

Examples

iex> sydney = Agenda.Place.new("Sydney Convention Centre")
iex> level_2 = Agenda.Place.new("Level 2", within: sydney)
iex> level_3 = Agenda.Place.new("Level 3", within: sydney)
iex> {Agenda.Place.separation(level_2, level_2),
...>  Agenda.Place.separation(level_2, level_3)}
{0, 1}

iex> sydney = Agenda.Place.new("Sydney Convention Centre")
iex> darling = Agenda.Place.new("Darling Harbour Theatre")
iex> Agenda.Place.separation(sydney, darling)
:disjoint