Примеры использования File Templates для типовых сценариев, где требуется шаблонный код

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Всем привет! На связи Дима Котиков, и это вторая статья из цикла про File Templates. В предыдущей части мы описали проблему с необходимостью написания шаблонного кода и вариантами решения, познакомились с инструментом шаблонов File Templates и разобрали синтаксис написания шаблонов.

В этой статье перейдем к практике — напишем шаблоны для кода типового экрана SomeFeature мобильного приложения.

Подготовка условия для создания шаблона

Структура файлов и папок, которые мы хотим видеть:

Структура файлов и папок. Желтыми блоками обозначены папки (пакеты), зеленым — файлы 
Структура файлов и папок. Желтыми блоками обозначены папки (пакеты), зеленым — файлы 

Определяемся с тем, что нам нужно:

1. Возможность задать имя root folder.

2. Возможность задать название фичи, которое будет являться префиксом в именах файлов — будет задаваться переменной по умолчанию $NAME.

3. После ввода имени рутовой папки и названия фичи должна быть сгенерирована структура файлов и папок, в файлах — сгенерирован шаблонный код со всеми корректными импортами между файлами.


А еще нам нужно определить файл, который будет являться точкой входа для нашего шаблона и относительно которого мы будем формировать дочерние файлы и папки в нашей структуре. Для этого выбираем самый верхнеуровневый по вложенности файл, например SomeFeatureViewModel.kt.

Возможность задать root folder

Для указания имени root folder идем в меню создания шаблонов, жмем на вкладке Files на значок + и указываем в форме информацию:

Создание входного файла для генерации шаблонов и помещение в ROOT_PACKAGE
Создание входного файла для генерации шаблонов и помещение в ROOT_PACKAGE

В меню создания шаблонов мы указали:

  • название нашего набора шаблонов как Compose feature;

  • расширение файла kt, так как нам нужен kotlin-файл;

  • в поле File name новую переменную ${ROOT_PACKAGE} и через слеш название файла, состоящее из ${NAME} и суффикса ViewModel. Содержимое файла заполним позже.

Нажимаем кнопку Apply для сохранения изменений или кнопку OK для сохранения изменений и выхода из меню.

Пробуем наш шаблон в деле. Для этого в папке в дереве проекта среды разработки жмем правую кнопку мыши и в контекстном меню выбираем New → Compose feature:

Выбор шаблона из контекстного меню
Выбор шаблона из контекстного меню

Видим диалоговое окно, в котором отображается поле File name, за которое отвечает переменная $NAME, а еще поле ROOT PACKAGE для нашей кастомной переменной $ROOT_PACKAGE:

Диалоговое окно создания шаблона
Диалоговое окно создания шаблона

Заполняем поля, нажимаем на OK и видим, что структура из файла и папки создалась:

Сгенерированный файл и папка
Сгенерированный файл и папка

Первый, а заодно и второй пункты нашего плана выполнены: корневая папка создается с именем, которое мы задали, префикс названия фичи для файлов также можно задать и файл генерируется с корректным именем.

Генерация структуры файлов и папок

После ввода имени рутовой папки и названия фичи должна быть сгенерирована структура файлов и папок в соответствии с нужной нам структурой и в файлах должен быть сгенерирован шаблонный код со всеми корректными импортами между файлами.

Для начала заполним шаблонным кодом наш уже созданный шаблон для ViewModel-и.

Шаблон для ViewModel
#if ($PACKAGE_NAME && $PACKAGE_NAME != "" && $ROOT_PACKAGE && $ROOT_PACKAGE != "")
    #set($OUT_PACKAGE = "$PACKAGE_NAME.$ROOT_PACKAGE")
#elseif ($PACKAGE_NAME && $PACKAGE_NAME != "")
    #set($OUT_PACKAGE = $PACKAGE_NAME)
#elseif ($ROOT_PACKAGE && $ROOT_PACKAGE != "")
    #set($OUT_PACKAGE = $ROOT_PACKAGE)
#else
    #set($OUT_PACKAGE = "")
#end
#if ($OUT_PACKAGE != "")package ${OUT_PACKAGE}#end
 
#if ($OUT_PACKAGE != "")
    #set($IMPORT_PREFIX = "import ${OUT_PACKAGE}.")
#else
    #set($IMPORT_PREFIX = "import ")
#end
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.flow.flowOn
${IMPORT_PREFIX}model.${NAME}State
${IMPORT_PREFIX}model.${NAME}SideEffect
${IMPORT_PREFIX}model.${NAME}UiEvent
 
class ${NAME}ViewModel : ViewModel() {
 
    private val _stateFlow = MutableStateFlow<${NAME}State>(${NAME}State())
    val stateFlow: StateFlow<${NAME}State>
        get() = _stateFlow.asStateFlow()
 
    private val sideEffectChannel = Channel<${NAME}SideEffect>()
    val sideEffectFlow: Flow<${NAME}SideEffect>
        get() = sideEffectChannel.receiveAsFlow().flowOn(Dispatchers.Main.immediate)
 
    fun dispatch(uiEvent: ${NAME}UiEvent) {
        // some feature-specific logic
    }
 
    private fun reduceState(reducer: (${NAME}State) -> ${NAME}State) {
        _stateFlow.update(reducer)
    }
 
    private fun sendSideEffect(sideEffect: ${NAME}SideEffect) {
        viewModelScope.launch {
            sideEffectChannel.send(sideEffect)
        }
    }
    
}

В блоке if-else-ов мы проверяем, заданы ли переменные $PACKAGE_NAME и $ROOT_PACKAGE и не пустые ли они. В зависимости от фактических значений переменных у нас конфигурируется итоговая строка, которую мы присваиваем переменной $OUT_PACKAGE.

#if ($PACKAGE_NAME && $PACKAGE_NAME != "" && $ROOT_PACKAGE && $ROOT_PACKAGE != "")
    #set($OUT_PACKAGE = "$PACKAGE_NAME.$ROOT_PACKAGE")
#elseif ($PACKAGE_NAME && $PACKAGE_NAME != "")
    #set($OUT_PACKAGE = $PACKAGE_NAME)
#elseif ($ROOT_PACKAGE && $ROOT_PACKAGE != "")
    #set($OUT_PACKAGE = $ROOT_PACKAGE)
#else
    #set($OUT_PACKAGE = "")
#end

Проверяем переменную $OUT_PACKAGE, которую создавали выше, и, если она не пустая, указываем ее вместе с ключевым kotlin-словом package.

#if ($OUT_PACKAGE != "")package ${OUT_PACKAGE}#end

Конфигурируем строку для последующего применения в импортах сущностей State, SideEffect и UiEvent (они будут созданы чуть ниже).

Далее по коду мы применяем переменную $IMPORT_PREFIX, а также задаем префикс ${NAME} для названий сущностей.

#if ($OUT_PACKAGE != "")
    #set($IMPORT_PREFIX = "import ${OUT_PACKAGE}.")
#else
    #set($IMPORT_PREFIX = "import ")
#end

Шаблон готов — выглядит легче, чем нарисовать сову

Источник: https://habr.com/ru/companies/tbank/articles/873234/


Интересные статьи

Интересные статьи

В этой статье я расскажу об интеграции с Keycloak — популярным опенсорсным продуктом для управления доступом (IAM). Многие компании используют его для организации единого входа (Single Sign-On, SSO) в...
Разобравшись в архитектуре сервера, реализовав механизм простого расширения его функционала путем добавления пользовательского кода через WEB админ панель и игровых локаций захотелось наконец посмотре...
Привет! Меня зовут Саша Шутай, я тимлид в AGIMA. В прошлой статье я рассказывал, что делать, если на проекте Bitrix сожительствует с Vue.js и поисковые боты не видят контента сайта. А в этой помогу ра...
Прогуливаясь по просторам Интернета, я случайно наткнулся на довольно интересную разработку. Называется "Russian Radio Player". Пользователь выбирает станцию для прослуши...
В этой статье мы рассмотрим, как система управления 1С-Битрикс справляется с большими нагрузками. Данный вопрос особенно актуален сегодня, когда электронная торговля начинает конкурировать по обороту ...