Приветик 👋
Надеюсь, у вас отличный день! Думаю, что да, так как сегодня я объявляю о проекте, над которым работал последние недели.
Я рад представить вам Eskema, инструмент, который поможет вам проверить динамические данные в Dart & Flutter.
Проблема в том, что я не уверен, полезна ли Eskema или нет, поэтому здесь мне нужна ваша помощь. Дайте мне знать, считаете ли вы, что этот пакет может быть полезен и для каких целей.
Прежде чем продолжить, позвольте мне показать вам небольшой фрагмент, демонстрирующий, как выглядит Eskema (я объясню это позже):
final validateMap = eskema({
'name': isType<String>(),
'address': nullable(
eskema({
'city': isType<String>(),
'street': isType<String>(),
'number': all([
isType<int>(),
isMin(0),
]),
'additional': nullable(
eskema({
'doorbel_number': isType<int>(),
})
),
})
)
});
Описание
Eskema — это набор инструментов, помогающих вам проверять динамические данные. Он позволяет определять «схемы» данных и «валидаторы» для проверки значения, карты или списка, для которых у нас нет элемента управления для правильного ввода. Вы можете думать об этом как о действительно легковесной схеме типа json.
Например, допустим, вы хотите создать библиотеку, приложение или инструмент, где вы ожидаете, что пользователь передаст некоторые данные, но не хотите или не можете использовать классы или известные структуры данных. Это был бы идеальный сценарий для использования Eskema.
Мотивация
Основной мотивацией для создания этого пакета было то, что я не нашел решения этой проблемы, которое бы мне понравилось, и решил создать его сам. На момент создания пакета у меня не было реального сценария его использования, но я думаю, что есть несколько сценариев, в которых Eskema может быть очень полезна.
Некоторые решения требовали генерировать код из аннотаций, прежде чем можно было проверить данные, например, используя json_serializable.
Если вы уже используете пакет json_serializable, вы можете использовать его для валидации данных.
Я хотел попробовать создать инструмент, который не требует генерации кода, все обрабатывается во время выполнения.
Дизайн
Я с самого начала позаботился о том, чтобы пакет был гибким, расширяемым и компонуемым. Я также позаботился о надлежащем документировании почти всего пакета. Я хотел, чтобы пакет был как можно лучше протестирован, на данный момент он имеет 100% покрытие и почти полностью протестирован.
Пакет должен предлагать базовый API и инструменты для проверки данных любого типа (например, Map, List, bool, int и т.д.).
Я также позаботился о том, чтобы было очень просто создавать новые типы валидаторов, чтобы не ограничивать вас!
Как это работает?
Пакет очень прост, все в нем — это Validator
s, которые являются просто функциями, которые получают значение и возвращают IResult
.
Отходя от этой предпосылки, пакет добавляет несколько полезных Validators
для проверки одиночных полей, полей карты (eskema) и полей списка (listEskema, listEach). Все они ведут себя одинаково, что делает композицию действительно простой и мощной.
final validateMap = eskema({
'name': isType<String>(),
'address': nullable(
eskema({
'city': isType<String>(),
'street': isType<String>(),
'number': all([
isType<int>(),
isMin(0),
]),
'additional': nullable(
eskema({
'doorbel_number': isType<int>(),
})
),
})
)
});
Приведенный выше пример проверяет карту на соответствие определенной eskema.
Использование
Обратите внимание: если вы хотите проверить только одно значение, то вам, вероятно, не нужна Eskema.
В противном случае давайте проверим, как проверить одно значение. Вы можете использовать валидаторы по отдельности:
final isString = isType<String>();
const result1 = isString('valid string');
const result2 = isString(123);
result1.isValid; // true
result2.isValid; // false
result2.expected; // String
Или вы можете комбинировать валидаторы:
all([isType<String>(), isDate()]); // all validators must be valid
or(isType<String>(), isType<int>()); // either validator must be valid
and(isType<String>(), isType<int>()); // both validator must be valid
// This validator checks that, the value is a list of strings, with length 2, and contains item "test"
all([
isOfLength(2), // checks that the list is of length 2
listEach(isTypeOrNull<String>()), // checks that each item is either string or null
listContains('test'), // list must contain value "test"
]);
// This validator checks a map against a eskema. Map must contain property 'books',
// which is a list of maps that matches a sub-eskema.
final matchesEskema = eskema({
'books': listEach(
eskema({
'name': isType<String>(),
}),
),
});
matchesEskema({'books': [{'name': 'book name'}]});
Валидаторы
isType
Этот валидатор проверяет принадлежность значения к определенному типу
isType<String>();
isType<int>();
isType<double>();
isType<List>();
isType<Map>();
isTypeOrNull
Этот валидатор проверяет, что значение имеет определенный тип или является null
isTypeOrNull<String>();
isTypeOrNull<int>();
nullable
Этот валидатор позволяет сделать так, чтобы валидаторы допускали нулевые значения
nullable(eskema({...}));
- Валидатор, приведенный выше, разрешает карту или нулевое значение
eskema
Наиболее распространенным вариантом использования, вероятно, будет валидация JSON или динамических карт. Для этого вы можете использовать валидатор eskema
.
В этом примере мы проверяем карту с необязательными полями и с вложенными полями.
final validateMap = eskema({
'name': isType<String>(),
'address': nullable(
eskema({
'city': isType<String>(),
'street': isType<String>(),
'number': all([
isType<int>(),
isMin(0),
]),
'additional': nullable(
eskema({
'doorbel_number': Field([isType<int>()])
})
),
})
)
});
final invalidResult = validateMap({});
invalidResult.isValid; // false
invalidResult.isNotValid; // true
invalidResult.expected; // name -> String
invalidResult.message; // Expected name -> String
final validResult = validateMap({ 'name': 'bobby' });
validResult.isValid; // true
validResult.isNotValid; // false
validResult.expected; // Valid
listEach
Другим распространенным случаем использования является проверка динамических списков. Для этого вы можете использовать класс listEach
.
В данном примере проверяется, что предоставленное значение является списком длины 2, и каждый элемент должен быть типа int:
final isValidList = all([
listOfLength(2),
listEach(isType<int>()),
]);
isValidList(null).isValid; // true
isValidList([]).isValid; // true
isValidList([1, 2]).isValid; // true
isValidList([1, "2"]).isValid; // false
isValidList([1, "2"]).expected; // [1] -> int
Дополнительные валидаторы
Для получения полного списка валидаторов ознакомьтесь с документацией
Пользовательские валидаторы
Эскема предлагает набор общих валидаторов, расположенных в lib/src/validators.dart
. Вы не ограничены использованием только этих валидаторов, пользовательские валидаторы могут быть созданы очень легко.
Давайте посмотрим, как создать валидатор для проверки соответствия строки шаблону:
Validator validateRegexp(RegExp regexp) {
return (value) {
return Result(
isValid: regexp.hasMatch(value),
expected: 'match pattern $regexp', // the message explaining what this validator expected
);
};
}
Резюме
В заключение я хочу поблагодарить вас за то, что вы нашли время прочитать все это. Если вам понравился проект, пожалуйста, не стесняйтесь поделиться им и/или отметить его на GitHub, чтобы он мог попасть к большему количеству людей, которые могут найти его полезным.
Не забудьте сообщить мне, если вы считаете, что этот пакет полезен для вас и почему и где бы вы его использовали!
Помощь приветствуется!
Если вы хотите помочь, у вас есть идеи или вы просто хотите поддержать этот проект, пожалуйста, не стесняйтесь!!!
Дополнительная информация
- Репозиторий здесь
- Для получения дополнительной информации ознакомьтесь с docsout.
- Если вы обнаружили ошибку, пожалуйста, оформите проблему или отправьте PR в мою сторону.
- Вклад приветствуется, не стесняйтесь присылать исправления, новые возможности, пользовательские валидаторы и т.д….