Spec-Zone.ru › Elixir 1.17

Исходный код Inspect протокол

Протокол Inspect преобразует структуру данных Elixir в алгебраический документ.

Это обычно делается, когда вы хотите настроить, как ваши собственные структуры отображаются в логах и терминале.

Эта документация посвящена реализации протокола Inspect для ваших собственных структур данных. Чтобы узнать больше об использовании inspect, см. Kernel.inspect/2 и IO.inspect/2.

Представление Inspect

Обычно существует три варианта представления inspect. Чтобы понять их, давайте представим, что у нас есть следующая User структура:

defmodule User do
  defstruct [:id, :name, :address]
end

Наши варианты:

  1. Вывести структуру, используя синтаксис структур Elixir, например: %User{address: "Earth", id: 13, name: "Jane"}. Это стандартное представление и лучший выбор, если все поля структуры являются общедоступными.

  2. Вывести, используя обозначение #User<...>, например: #User<id: 13, name: "Jane", ...>. Это обозначение не генерирует корректный код Elixir и обычно используется, когда структура имеет закрытые поля (например, вы можете скрыть поле :address, чтобы скрыть персональную информацию).

  3. Вывести структуру, используя синтаксис выражения, например: User.new(13, "Jane", "Earth"). Это предполагает наличие функции User.new/3. Этот вариант в основном используется как альтернатива варианту 2 для представления пользовательских структур данных, таких как MapSet, Date.Range и другие.

Вы можете реализовать протокол Inspect для своих структур, соблюдая вышеуказанные соглашения. Вариант 1 является стандартным представлением, и вы можете быстро получить вариант 2, унаследовав протокол Inspect. Для варианта 3 вам понадобится собственная реализация.

Наследование

Протокол Inspect может быть унаследован для настройки порядка полей (стандартное значение — алфавитный) и скрытия определённых полей из структур, чтобы они не отображались в логах, результатах inspect и т. п. Последнее особенно полезно для полей, содержащих конфиденциальную информацию.

Поддерживаемые варианты:

  • :only — включать только указанные поля при отображении.

  • :except — исключать указанные поля при отображении.

  • :optional — (с версии v1.14.0) не включать поле, если его значение совпадает со значением по умолчанию. Это можно использовать для упрощения представления структуры за счёт скрытия информации.

Когда используется :only или :except, чтобы ограничить поля, структура будет выводиться с использованием обозначения #User<...>, поскольку структура больше не может быть скопирована и вставлена как допустимый код Elixir. Давайте посмотрим пример:

defmodule User do
  @derive {Inspect, only: [:id, :name]}
  defstruct [:id, :name, :address]
end

inspect(%User{id: 1, name: "Jane", address: "Earth"})
#=> #User<id: 1, name: "Jane", ...>

Если используется только опция :optional, структура всё равно будет выводиться как %User{...}.

Кастомная реализация

Вы также можете определить собственную реализацию протокола, определив функцию inspect/2. Функция принимает проверяемый объект и опции проверки, представленные структурой Inspect.Opts. Создание алгебраического документа выполняется с помощью Inspect.Algebra.

Часто проверка структуры может быть реализована на основе уже существующих объектов. Например, вот реализация MapSet для inspect/2:

defimpl Inspect, for: MapSet do
  import Inspect.Algebra

  def inspect(map_set, opts) do
    concat(["MapSet.new(", Inspect.List.inspect(MapSet.to_list(map_set), opts), ")"])
  end
end

Функция concat/1 взята из Inspect.Algebra и объединяет алгебраические документы вместе. В примере выше она объединяет строку "MapSet.new(", документ, возвращённый Inspect.Algebra.to_doc/2, и конечную строку ")". Следовательно, MapSet с числами 1, 2 и 3 будет выводиться как:

iex> MapSet.new([1, 2, 3], fn x -> x * 2 end)
MapSet.new([2, 4, 6])

Другими словами, представление inspect MapSet возвращает выражение, которое, при вычислении, создаёт сам MapSet.

Обработка ошибок

В случае возникновения ошибки во время проверки вашей структуры Elixir поднимет ошибку ArgumentError и автоматически вернётся к исходному представлению для вывода структуры. Кроме того, необходимо быть осторожным при отладке собственной реализации Inspect, так как вызовы IO.inspect/2 или dbg/1 могут вызвать бесконечную рекурсию (поскольку для проверки/отладки структуры необходимо вызвать inspect).

Вот несколько советов:

  • Для отладки используйте IO.inspect/2 с опцией structs: false, которая отключает пользовательскую печать и предотвращает рекурсивный вызов реализации Inspect.

  • Чтобы получить доступ к основной ошибке в вашей реализации Inspect, вы можете вызвать протокол напрямую. Например, мы можем вызвать реализацию Inspect.MapSet выше как:

    Inspect.MapSet.inspect(MapSet.new(), %Inspect.Opts{})

Резюме

Типы

t()

Все типы, которые реализуют этот протокол.

Функции

inspect(term, opts)

Преобразует term в алгебраический документ.

Типы

t()Исходный код

@type t() :: term()

Все типы, которые реализуют этот протокол.

Функции

inspect(term, opts)Исходный код

@spec inspect(t(), Inspect.Opts.t()) :: Inspect.Algebra.t()

Преобразует term в алгебраический документ.

Эта функция не должна вызываться напрямую, за исключением случаев реализации кастомного inspect_fun для передачи в Inspect.Opts. Во всех остальных случаях следует отдавать предпочтение Inspect.Algebra.to_doc/2, так как она обрабатывает структуры и исключения.

Скачать версию ePub

Создано с помощью ExDoc (v0.34.1) для языка программирования Elixir

© 2012-2024 The Elixir Team
Licensed under the Apache License, Version 2.0.
https://hexdocs.pm/elixir/1.17.2/Inspect.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API