Spec-Zone.ru › Elixir 1.16

Исходный код Access поведение

Доступ к данным по ключам.

Модуль Access определяет поведение для динамического доступа к ключам любого типа в структуре данных с помощью синтаксиса data[key].

Access поддерживает списки ключевых слов (Keyword) и карты (Map) из коробки. Ключевые слова поддерживают только ключи-атомы, ключи для карт могут быть любого типа. Оба возвращают nil если ключ не существует:

iex> keywords = [a: 1, b: 2]
iex> keywords[:a]
1
iex> keywords[:c]
nil

iex> map = %{a: 1, b: 2}
iex> map[:a]
1

iex> star_ratings = %{1.0 => "★", 1.5 => "★☆", 2.0 => "★★"}
iex> star_ratings[1.5]
"★☆"

Этот синтаксис очень удобен, так как его можно вложенно использовать:

iex> keywords = [a: 1, b: 2]
iex> keywords[:c][:unknown]
nil

Это работает, потому что доступ к чему-либо в значении nil возвращает само значение nil.

iex> nil[:a]
nil

Синтаксис доступа также может использоваться с макросами Kernel.put_in/2, Kernel.update_in/2 и Kernel.get_and_update_in/2 для установки значений вложенных структурах данных:

iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}}
iex> put_in(users["john"][:age], 28)
%{"john" => %{age: 28}, "meg" => %{age: 23}}

Карты и структуры

Хотя синтаксис доступа разрешен в картах через map[key], если ваша карта состоит из предопределенных ключей-атомов, вы должны предпочесть использовать доступ к этим ключам с помощью map.key вместо map[key], так как map.key будет генерировать ошибку, если ключ отсутствует (чего не должно происходить, если ключи предопределены).

Аналогично, поскольку структуры являются картами и структуры имеют предопределенные ключи, они допускают только синтаксис struct.key и не допускают синтаксис доступа struct[key]. Access.key/1 также может использоваться для создания динамического доступа к структурам и картам.

Вкратце, при использовании put_in/2 и аналогичных:

put_in(struct_or_map.key, :value)
put_in(keyword_or_map[:key], :value)

При использовании put_in/3 и аналогичных:

put_in(struct_or_map, [Access.key!(:key)], :value)
put_in(keyword_or_map, [:key], :value)

Это охватывает двойственную природу карт в Elixir, так как они могут быть либо для структурированных данных, либо в качестве хранилища пар «ключ-значение». Подробнее см. модуль Map.

Вложенные структуры данных

Оба синтаксиса доступа по ключу могут использоваться с функциями и макросами вложенного обновления в Kernel, например, Kernel.get_in/2, Kernel.put_in/3, Kernel.update_in/3, Kernel.pop_in/2 и Kernel.get_and_update_in/3.

Например, чтобы обновить карту внутри другой карты:

iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}}
iex> put_in(users["john"].age, 28)
%{"john" => %{age: 28}, "meg" => %{age: 23}}

Этот модуль предоставляет удобные функции для обхода других структур, таких как кортежи и списки. Эти функции могут использоваться во всех функциях и макросах, связанных с Access, в Kernel.

Например, если у вас есть карта пользователя с ключами :name и :languages, вот как можно глубоко пройти по карте и преобразовать все имена языков в верхний регистр:

iex> languages = [
...>   %{name: "elixir", type: :functional},
...>   %{name: "c", type: :procedural}
...> ]
iex> user = %{name: "john", languages: languages}
iex> update_in(user, [:languages, Access.all(), :name], &String.upcase/1)
%{
  name: "john",
  languages: [
    %{name: "ELIXIR", type: :functional},
    %{name: "C", type: :procedural}
  ]
}

См. функции key/1, key!/1, elem/1 и all/0 для некоторых доступных вспомогательных функций.

Сводка

Типы

access_fun(data, current_value)
container()
get_and_update_fun(data, current_value)
get_fun(data)
key()
nil_container()
t()
value()

Обработчики

fetch(term, key)

Вызывается для доступа к значению, хранящемуся под key в данном элементе term.

get_and_update(data, key, function)

Вызывается для доступа к значению по key и одновременного обновления.

pop(data, key)

Вызывается для "удаления" значения по key из данной структуры данных.

Функции

all()

Возвращает функцию, которая обращается ко всем элементам списка.

at(index)

Возвращает функцию, которая обращается к элементу по index (нумерация с 0) списка.

at!(index)

То же, что и at/1, но вызывает исключение Enum.OutOfBoundsError если указанный индекс вне диапазона.

elem(index)

Возвращает функцию, которая обращается к элементу по указанному индексу в кортеже.

fetch(container, key)

Возвращает значение по данному ключу в контейнере (карта, список ключевых слов или структура, реализующая поведение Access).

fetch!(container, key)

Аналогично fetch/2, но возвращает значение напрямую, или вызывает исключение KeyError если key не найден.

filter(func)

Возвращает функцию, которая обращается ко всем элементам списка, которые удовлетворяют заданному предикату.

get(container, key, default \\ nil)

Возвращает значение по данному ключу в контейнере (карта, список ключевых слов или структура, реализующая поведение Access).

get_and_update(container, key, fun)

Возвращает значение по заданному ключу и обновляет его в container (карта, список ключевых слов, структура, реализующая поведение Access).

key(key, default \\ nil)

Возвращает функцию, которая обращается к заданному ключу в карте/структуре.

key!(key)

Возвращает функцию, которая обращается к заданному ключу в карте/структуре.

pop(container, key)

Удаляет запись с заданным ключом из контейнера (карта, список ключевых слов или структура, реализующая поведение Access).

slice(range)

Возвращает функцию, которая обращается ко всем элементам списка в заданном диапазоне.

END_OF_DOCUMENT_MARKER ```

Типы

access_fun(data, current_value)Source

@type access_fun(data, current_value) ::
  get_fun(data) | get_and_update_fun(data, current_value)

container()Source

@type container() :: keyword() | struct() | map()

get_and_update_fun(data, current_value)Source

@type get_and_update_fun(data, current_value) ::
  (:get_and_update, data, (term() -> term()) ->
     {current_value, new_data :: container()} | :pop)

get_fun(data)Source

@type get_fun(data) :: (:get, data, (term() -> term()) -> new_data :: container())

key()Source

@type key() :: any()

nil_container()Source

@type nil_container() :: nil

t()Source

@type t() :: container() | nil_container() | any()

value()Source

@type value() :: any()

Обратные вызовы

fetch(term, key)Source

@callback fetch(term :: t(), key()) :: {:ok, value()} | :error

Вызывается для доступа к значению, хранящемуся по ключу key в заданном объекте term.

Эта функция должна возвращать {:ok, value}, где value — значение по ключу key, если ключ существует в объекте, или :error, если ключ не существует.

Многие функции, определённые в модуле Access, вызывают эту функцию внутри себя. Эта функция также используется при использовании синтаксиса доступа с квадратными скобками (structure[key]). Обратный вызов fetch/2, реализованный модулем, определяющим структуру structure, вызывается, и если он возвращает {:ok, value}, то возвращается value, а если :error, то nil.

См. реализации Map.fetch/2 и Keyword.fetch/2 для примеров реализации этого обратного вызова.

get_and_update(data, key, function)Source

@callback get_and_update(
  data,
  key(),
  (value() | nil -> {current_value, new_value :: value()} | :pop)
) ::
  {current_value, new_data :: data}
when current_value: value(), data: container()

Вызывается для доступа к значению по ключу key и одновременного обновления.

Реализация этого обратного вызова должна вызвать fun со значением по ключу key в переданной структуре data, или с nil, если key отсутствует. Эта функция должна вернуть либо {current_value, new_value}, либо :pop.

Если переданная функция возвращает {current_value, new_value}, возвращаемое значение этого обратного вызова должно быть {current_value, new_data}, где:

  • current_value — извлечённое значение (которое может быть обработано перед возвратом)

  • new_value — новое значение для хранения по ключу key

  • new_data — data после обновления значения по ключу key на new_value.

Если переданная функция возвращает :pop, возвращаемое значение этого обратного вызова должно быть {value, new_data}, где value — значение по ключу key (или nil, если ключ отсутствует), а new_data — data без key.

См. реализации Map.get_and_update/3 или Keyword.get_and_update/3 для примеров.

pop(data, key)Source

@callback pop(data, key()) :: {value(), data} when data: container()

Вызывается для удаления значения по ключу key из заданной структуры данных.

Если key существует в структуре data, реализация должна вернуть кортеж {value, new_data}, где value — значение, которое было по ключу key, а new_data — term без key.

Если key отсутствует в структуре, должен быть возвращён кортеж {value, data}, где value — значение, определённое реализацией.

См. реализации Map.pop/3 или Keyword.pop/3 для примеров.

END_OF_DOCUMENT_MARKER

Функции

all()Source

@spec all() :: access_fun(data :: list(), current_value :: list())

Возвращает функцию, которая получает доступ ко всем элементам в списке.

Возвращаемая функция обычно передаётся в качестве аксессора для Kernel.get_in/2, Kernel.get_and_update_in/3 и аналогичных функций.

Примеры

iex> list = [%{name: "john"}, %{name: "mary"}]
iex> get_in(list, [Access.all(), :name])
["john", "mary"]
iex> get_and_update_in(list, [Access.all(), :name], fn prev ->
...>   {prev, String.upcase(prev)}
...> end)
{["john", "mary"], [%{name: "JOHN"}, %{name: "MARY"}]}
iex> pop_in(list, [Access.all(), :name])
{["john", "mary"], [%{}, %{}]}

Вот пример, который проходит по списку, удаляя чётные числа и умножая нечётные числа на 2:

iex> require Integer
iex> get_and_update_in([1, 2, 3, 4, 5], [Access.all()], fn num ->
...>   if Integer.is_even(num), do: :pop, else: {num, num * 2}
...> end)
{[1, 2, 3, 4, 5], [2, 6, 10]}

Если доступная структура не является списком, генерируется ошибка:

iex> get_in(%{}, [Access.all()])
** (RuntimeError) Access.all/0 expected a list, got: %{}

at(index)Source

@spec at(integer()) :: access_fun(data :: list(), current_value :: term())

Возвращает функцию, которая получает доступ к элементу по index (нумерация с нуля) в списке.

Обратите внимание, что поиск по индексу в списках занимает линейное время: чем больше список, тем дольше потребуется для доступа к его индексу. Поэтому операции, основанные на индексе, обычно избегаются в пользу других функций в модуле Enum.

Возвращаемая функция обычно передаётся в качестве аксессора для Kernel.get_in/2, Kernel.get_and_update_in/3 и аналогичных функций.

Примеры

iex> list = [%{name: "john"}, %{name: "mary"}]
iex> get_in(list, [Access.at(1), :name])
"mary"
iex> get_in(list, [Access.at(-1), :name])
"mary"
iex> get_and_update_in(list, [Access.at(0), :name], fn prev ->
...>   {prev, String.upcase(prev)}
...> end)
{"john", [%{name: "JOHN"}, %{name: "mary"}]}
iex> get_and_update_in(list, [Access.at(-1), :name], fn prev ->
...>   {prev, String.upcase(prev)}
...> end)
{"mary", [%{name: "john"}, %{name: "MARY"}]}

at/1 также может использоваться для извлечения элементов из списка или ключей внутри списка:

iex> list = [%{name: "john"}, %{name: "mary"}]
iex> pop_in(list, [Access.at(0)])
{%{name: "john"}, [%{name: "mary"}]}
iex> pop_in(list, [Access.at(0), :name])
{"john", [%{}, %{name: "mary"}]}

Если индекс находится за пределами границ, возвращается nil и функция обновления не вызывается:

iex> list = [%{name: "john"}, %{name: "mary"}]
iex> get_in(list, [Access.at(10), :name])
nil
iex> get_and_update_in(list, [Access.at(10), :name], fn prev ->
...>   {prev, String.upcase(prev)}
...> end)
{nil, [%{name: "john"}, %{name: "mary"}]}

Если доступная структура не является списком, генерируется ошибка:

iex> get_in(%{}, [Access.at(1)])
** (RuntimeError) Access.at/1 expected a list, got: %{}

at!(index)Source

@spec at!(integer()) :: access_fun(data :: list(), current_value :: term())

То же, что и at/1, за исключением того, что генерирует исключение Enum.OutOfBoundsError, если заданный индекс находится за пределами границ.

Примеры

iex> get_in([:a, :b, :c], [Access.at!(2)])
:c
iex> get_in([:a, :b, :c], [Access.at!(3)])
** (Enum.OutOfBoundsError) out of bounds error

elem(index)Source

@spec elem(non_neg_integer()) :: access_fun(data :: tuple(), current_value :: term())

Возвращает функцию, которая получает доступ к элементу по заданному индексу в кортеже.

Возвращаемая функция обычно передаётся в качестве аксессора для Kernel.get_in/2, Kernel.get_and_update_in/3 и аналогичных функций.

Возвращаемая функция генерирует исключение, если index находится за пределами границ.

Обратите внимание, что извлечение элементов из кортежей невозможно и генерирует ошибку.

Примеры

iex> map = %{user: {"john", 27}}
iex> get_in(map, [:user, Access.elem(0)])
"john"
iex> get_and_update_in(map, [:user, Access.elem(0)], fn prev ->
...>   {prev, String.upcase(prev)}
...> end)
{"john", %{user: {"JOHN", 27}}}
iex> pop_in(map, [:user, Access.elem(0)])
** (RuntimeError) cannot pop data from a tuple

Если доступная структура не является кортежем, генерируется ошибка:

iex> get_in(%{}, [Access.elem(0)])
** (RuntimeError) Access.elem/1 expected a tuple, got: %{}

fetch(container, key)Source

@spec fetch(container(), term()) :: {:ok, term()} | :error
@spec fetch(nil_container(), any()) :: :error

Получает значение для заданного ключа в контейнере (словарь, ключевой список или структура, реализующая поведение Access).

Возвращает {:ok, value}, где value — значение под key, если такой ключ существует, или :error, если key не найден.

Примеры

iex> Access.fetch(%{name: "meg", age: 26}, :name)
{:ok, "meg"}

iex> Access.fetch([ordered: true, on_timeout: :exit], :timeout)
:error

fetch!(container, key)Source

@spec fetch!(container(), term()) :: term()

То же, что и fetch/2, но возвращает значение напрямую или генерирует исключение KeyError, если key не найден.

Примеры

iex> Access.fetch!(%{name: "meg", age: 26}, :name)
"meg"

filter(func)Source

@spec filter((term() -> boolean())) ::
  access_fun(data :: list(), current_value :: list())

Возвращает функцию, которая получает доступ ко всем элементам списка, соответствующим заданному предикату.

Возвращаемая функция обычно передаётся в качестве аксессора для Kernel.get_in/2, Kernel.get_and_update_in/3 и аналогичных функций.

Примеры

iex> list = [%{name: "john", salary: 10}, %{name: "francine", salary: 30}]
iex> get_in(list, [Access.filter(&(&1.salary > 20)), :name])
["francine"]
iex> get_and_update_in(list, [Access.filter(&(&1.salary <= 20)), :name], fn prev ->
...>   {prev, String.upcase(prev)}
...> end)
{["john"], [%{name: "JOHN", salary: 10}, %{name: "francine", salary: 30}]}

filter/1 также можно использовать для извлечения элементов из списка или ключей внутри списка:

iex> list = [%{name: "john", salary: 10}, %{name: "francine", salary: 30}]
iex> pop_in(list, [Access.filter(&(&1.salary >= 20))])
{[%{name: "francine", salary: 30}], [%{name: "john", salary: 10}]}
iex> pop_in(list, [Access.filter(&(&1.salary >= 20)), :name])
{["francine"], [%{name: "john", salary: 10}, %{salary: 30}]}

Если совпадений не найдено, возвращается пустой список, и функция обновления не вызывается

iex> list = [%{name: "john", salary: 10}, %{name: "francine", salary: 30}]
iex> get_in(list, [Access.filter(&(&1.salary >= 50)), :name])
[]
iex> get_and_update_in(list, [Access.filter(&(&1.salary >= 50)), :name], fn prev ->
...>   {prev, String.upcase(prev)}
...> end)
{[], [%{name: "john", salary: 10}, %{name: "francine", salary: 30}]}

Если предикат не является функцией или имеет неправильную арность, генерируется ошибка:

iex> get_in([], [Access.filter(5)])
** (FunctionClauseError) no function clause matching in Access.filter/1

Если доступная структура не является списком, генерируется ошибка:

iex> get_in(%{}, [Access.filter(fn a -> a == 10 end)])
** (RuntimeError) Access.filter/1 expected a list, got: %{}

get(container, key, default \\ nil)Source

@spec get(container(), term(), term()) :: term()
@spec get(nil_container(), any(), default) :: default when default: var

Получает значение для заданного ключа в контейнере (словарь, ключевой список или структура, реализующая поведение Access).

Возвращает значение под key, если такой ключ существует, или default, если key не найден.

Примеры

iex> Access.get(%{name: "john"}, :name, "default name")
"john"
iex> Access.get(%{name: "john"}, :age, 25)
25

iex> Access.get([ordered: true], :timeout)
nil

get_and_update(container, key, fun)Source

@spec get_and_update(
  data,
  key(),
  (value() | nil -> {current_value, new_value :: value()} | :pop)
) ::
  {current_value, new_data :: data}
when data: container(), current_value: var

Получает и обновляет указанный ключ в container (словарь, ключевой список, структура, реализующая поведение Access).

Аргумент fun получает значение key (или nil, если key отсутствует в container). Он должен возвращать двухэлементный кортеж {current_value, new_value}: значение "get" current_value (полученное значение, над которым можно выполнить операции перед возвратом) и новое значение для хранения под key (new_value). fun также может вернуть :pop, что означает, что текущее значение должно быть удалено из контейнера и возвращено.

Возвращаемое значение — двухэлементный кортеж со значением "get", возвращённым fun, и новым контейнером с обновлённым значением под key.

Примеры

iex> Access.get_and_update([a: 1], :a, fn current_value ->
...>   {current_value, current_value + 1}
...> end)
{1, [a: 2]}

key(key, default \\ nil)Source

@spec key(key(), term()) ::
  access_fun(data :: struct() | map(), current_value :: term())

Возвращает функцию, которая получает доступ к заданному ключу в словаре/структуре.

Возвращаемая функция обычно передаётся в качестве аксессора для Kernel.get_in/2, Kernel.get_and_update_in/3 и аналогичных функций.

Возвращаемая функция использует значение по умолчанию, если ключ не существует. Это позволяет задавать значения по умолчанию и безопасно обрабатывать отсутствующие ключи:

iex> get_in(%{}, [Access.key(:user, %{}), Access.key(:name, "meg")])
"meg"

Это также полезно при использовании функций обновления, позволяя нам вводить значения при обходе структуры данных для обновлений:

iex> put_in(%{}, [Access.key(:user, %{}), Access.key(:name)], "Mary")
%{user: %{name: "Mary"}}

Примеры

iex> map = %{user: %{name: "john"}}
iex> get_in(map, [Access.key(:unknown, %{}), Access.key(:name, "john")])
"john"
iex> get_and_update_in(map, [Access.key(:user), Access.key(:name)], fn prev ->
...>   {prev, String.upcase(prev)}
...> end)
{"john", %{user: %{name: "JOHN"}}}
iex> pop_in(map, [Access.key(:user), Access.key(:name)])
{"john", %{user: %{}}}

Если доступная структура не является словарем или структурой, генерируется ошибка:

iex> get_in([], [Access.key(:foo)])
** (BadMapError) expected a map, got: []

key!(key)Source

@spec key!(key()) :: access_fun(data :: struct() | map(), current_value :: term())

Возвращает функцию, которая получает доступ к заданному ключу в словаре/структуре.

Возвращаемая функция обычно передаётся в качестве аксессора для Kernel.get_in/2, Kernel.get_and_update_in/3 и аналогичных функций.

Аналогично key/2, но возвращаемая функция генерирует исключение, если ключ не существует.

Примеры

iex> map = %{user: %{name: "john"}}
iex> get_in(map, [Access.key!(:user), Access.key!(:name)])
"john"
iex> get_and_update_in(map, [Access.key!(:user), Access.key!(:name)], fn prev ->
...>   {prev, String.upcase(prev)}
...> end)
{"john", %{user: %{name: "JOHN"}}}
iex> pop_in(map, [Access.key!(:user), Access.key!(:name)])
{"john", %{user: %{}}}
iex> get_in(map, [Access.key!(:user), Access.key!(:unknown)])
** (KeyError) key :unknown not found in: %{name: "john"}

Приведённые выше примеры можно частично переписать следующим образом:

iex> map = %{user: %{name: "john"}}
iex> map.user.name
"john"
iex> get_and_update_in(map.user.name, fn prev ->
...>   {prev, String.upcase(prev)}
...> end)
{"john", %{user: %{name: "JOHN"}}}

Однако невозможно удалить поля с помощью точечной нотации, так как предполагается, что эти поля также должны присутствовать. В любом случае, Access.key!/1 полезна, когда ключ неизвестен заранее и должен быть получен динамически.

Если доступная структура не является словарем/структурой, генерируется ошибка:

iex> get_in([], [Access.key!(:foo)])
** (RuntimeError) Access.key!/1 expected a map/struct, got: []

pop(container, key)Source

@spec pop(data, key()) :: {value(), data} when data: container()

Удаляет запись с заданным ключом из контейнера (словарь, ключевой список или структура, реализующая поведение Access).

Возвращает кортеж, содержащий значение, связанное с ключом, и обновлённый контейнер. nil возвращается для значения, если ключ не находится в контейнере.

Примеры

Со словарем:

iex> Access.pop(%{name: "Elixir", creator: "Valim"}, :name)
{"Elixir", %{creator: "Valim"}}

Ключевой список:

iex> Access.pop([name: "Elixir", creator: "Valim"], :name)
{"Elixir", [creator: "Valim"]}

Неизвестный ключ:

iex> Access.pop(%{name: "Elixir", creator: "Valim"}, :year)
{nil, %{creator: "Valim", name: "Elixir"}}

slice(range)Source

@spec slice(Range.t()) :: access_fun(data :: list(), current_value :: list())

Возвращает функцию, которая получает все элементы списка, находящиеся в указанном диапазоне.

Диапазон будет нормализован по тем же правилам, что и в Enum.slice/2.

Возвращаемая функция обычно передается в качестве аксессора к Kernel.get_in/2, Kernel.get_and_update_in/3 и аналогичным функциям.

Примеры

iex> list = [%{name: "john", salary: 10}, %{name: "francine", salary: 30}, %{name: "vitor", salary: 25}]
iex> get_in(list, [Access.slice(1..2), :name])
["francine", "vitor"]
iex> get_and_update_in(list, [Access.slice(1..3//2), :name], fn prev ->
...>   {prev, String.upcase(prev)}
...> end)
{["francine"], [%{name: "john", salary: 10}, %{name: "FRANCINE", salary: 30}, %{name: "vitor", salary: 25}]}

slice/1 также может использоваться для извлечения элементов из списка или ключей внутри списка:

iex> list = [%{name: "john", salary: 10}, %{name: "francine", salary: 30}, %{name: "vitor", salary: 25}]
iex> pop_in(list, [Access.slice(-2..-1)])
{[%{name: "francine", salary: 30}, %{name: "vitor", salary: 25}], [%{name: "john", salary: 10}]}
iex> pop_in(list, [Access.slice(-2..-1), :name])
{["francine", "vitor"], [%{name: "john", salary: 10}, %{salary: 30}, %{salary: 25}]}

Если совпадение не найдено, возвращается пустой список, и функция обновления не вызывается

iex> list = [%{name: "john", salary: 10}, %{name: "francine", salary: 30}, %{name: "vitor", salary: 25}]
iex> get_in(list, [Access.slice(5..10//2), :name])
[]
iex> get_and_update_in(list, [Access.slice(5..10//2), :name], fn prev ->
...>   {prev, String.upcase(prev)}
...> end)
{[], [%{name: "john", salary: 10}, %{name: "francine", salary: 30}, %{name: "vitor", salary: 25}]}

Возникает ошибка, если доступная структура не является списком:

iex> get_in(%{}, [Access.slice(2..10//3)])
** (ArgumentError) Access.slice/1 expected a list, got: %{}

Возникает ошибка, если шаг диапазона отрицательный:

iex> get_in([], [Access.slice(2..10//-1)])
** (ArgumentError) Access.slice/1 does not accept ranges with negative steps, got: 2..10//-1

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

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

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

Spec-Zone.ru

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