...

пятница, 4 апреля 2014 г.

Проект Lazybones — «Лентяй», который работает за вас

Я не люблю Maven.

О моей пассионарной ненависти к этой штуке можно написать не одну статью, но сегодня я хочу поговорить об одной очень хорошей фиче Мавена — об архетипах. Что это такое можно прочитать в официальной документации, в каждом из туторилов по Мавену на Хабре(1, 2, 3), да и вообще, вы наверняка знаете и сами.

Так вот, архетипы — это круто, и было бы здорово, если бы 1) во многих проектах со стандартной структурой они были. 2) можно было бы их прикрутить к тем, у которых их нет.


Примерно так думал Питер Ледбрук, когда смотрел на полное отсутствие архетапов в Ratpack. Тогда и родился проект Lazybones — инструмент генерации проектов.


image


В этой статье я расскажу вам как 1) Пользоваться Lazybones для генерации проектов, для которых уже созданы шаблоны. 2) Создавать новые шаблоны для любых проектов.



Использование существующих шаблонов Lazybones



Tут все будет предельно коротко:


  1. Устанавливаем Lazybones с помощью GVM или скачиваем дистрибутив с Bintray

  2. Смотрим какие шаблоны существуют с помощью команды lazybones list (или изучаем репозиторий)

  3. Изучаем информацию о выбраном шаблоне с помощью команды lazybones info <имя шаблона> (или читаем readme в packag-e шаблона на Бинтрее)

  4. Создаем проект командой lazybones create <имя шаблона> <версия шаблона> <имя директории в которой создавать>




Всё, спасибо за внимание, все свободны. Хотя нет, сейчас будем делать как раз интересное.
Создание своего шаблона проекта



Поскольку вы все, скорее всего, знакомы с мавеновским архетипом #361 (maven-archetype-quickstart), мы сделаем что-то похожее (воссоздав все фичи, мы опустим некоторые повторы).

Мало того, что вы сможете сравнить количество скаченного интернета для создания обоих проектов, вы еще и сможете сравнить сложность создания самого шаблона, ибо процесс создания архетайпа Мавена прекрасно описан вот тут.
Итак, поехали.



Чего мы хотим добиться:


  • Создать базовый pom.xml с выбраными через интерактивную командную строку groupId, artifactId и версией

  • Создать директории src/main/java, src/main/resources (тоже самое для тестов, но мы не будем, для простоты примера)

  • Создать класс для примера, прописать в нем выбранный через интерактивную командную строку package и положить его в походящую под package директорию (тоже самое для теста, но мы не будем, для простоты примера)

  • В классе сгенерить main, который будет при запуске фазы теста печатать сообщение, выбранное через интерактивную командную строку

  • Собрать шаблон

  • Создать проект по нашему шаблону

  • Запустить mvn test

  • Профит



  1. Для сборки шаблона нам понадобится скрипт Gradle и директория с шаблонами. Поскольку нам лень, мы запустим lazybones:

    >lazybones create lazybones-project lzb-templates

    В результате у нас есть следующее:

    │ build.gradle //скрипт сборки шалбонов
    │ gradlew //файл запуска скрипта для никсов
    │ gradlew.bat //файл запуска скрипта для винды
    │ README.md //файл описывающий этот проект

    ├───gradle //вспомогательная директория для скрипта

    └───templates //пустая директория для наших шаблонов



  2. Заходим в директорию templates, создаем в ней под-директорию нашего шаблона, и начинаем ваять. Создаем файл версии. Он называется VERSION и содержит только версию, например 0.1

    >mkdir maven-simple
    >cd maven simple
    >echo 0.1 > VERSION



  3. Кроме того, нужно создать readme.md, который будет показан после создания проекта.

  4. Создаем директории src/main/java, src/main/resources. В java и resources из них кладем по пустому файлу .retain

    ├───maven-simple
    │ │ README.md
    │ │ VERSION
    │ │
    │ └───src
    │ └───main
    │ ├───java
    │ │ .retain
    │ │
    │ └───resources
    │ .retain



  5. Теперь займемся шаблонами. Начнем с pom.xml:

    <project>
    <modelVersion>4.0.0</modelVersion>
    <groupId>${groupId}</groupId>
    <artifactId>${artifactId}</artifactId>
    <version>${version}</version>
    <build>
    <plugins>
    <plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>exec-maven-plugin</artifactId>
    <version>1.2.1</version>
    <executions>
    <execution>
    <phase>test</phase>
    <goals>
    <goal>java</goal>
    </goals>
    <configuration>
    <mainClass>${pkg}.App</mainClass>
    <arguments>
    <argument>${message}</argument>
    </arguments>
    </configuration>
    </execution>
    </executions>
    </plugin>
    </plugins>
    </build>
    </project>





    ААААА!!!!!!!



    Так, взяли себя в руки, смотрим. Обратите внимание на всякие ${...}. Это то, что мы будем менять на значения, которые нам задаст пользователь во время запуска create. По сути, это просто маркеры Groovy Templates. Если вы знакомы с Velocity, Freemarker или любым другим обработчиком шаблонов, вам всё будет знакомо. Но об этом позже.

    Адовый ад в <build></build> — это всего лишь запуск main-а класса App. Обратите внимание, что нам пока нам неизвестны package этого класса и параметр, который мы передаем в main.

  6. Теперь смотрим на файл App.java:

    package ${pkg};

    public class App {
    public static void main(String[] args) {
    System.out.println(args[0]);
    }
    }




    Тут у нас всего одна переменная — опять-же, package. Заодно мы видим, что main печатает аргумент. Значит, что во время запуска мавена, в фазе теста, мы ожидаем увидеть сообщение, которое пользователь выберет, опять-же, во время create.

    Итак, теперь мы имеем все директории и шаблоны:

    │ App.java
    │ lazybones.groovy
    │ pom.xml
    │ README.md
    │ VERSION

    └───src
    └───main
    ├───java
    │ .retain

    └───resources
    .retain



  7. А вот теперь начинается самое интересное. Нам осталось написать пост-процессор, который будет бежать после распаковывания директорий. Задачи: 1) Узнать всё, что нужно у пользователя, 2) перенести java файл в директорию, соответствующую package, 3) обработать шаблоны.

    Поможет нам в этом, конечно, элегантный Груви скрипт:

    import static org.apache.commons.io.FileUtils.moveFileToDirectory

    Map<String,String> props = [:]

    //метод ask принимает 2 параметра - сообщение, и значение по умолчанию.
    //он показывает сообщение и ждет ввода. Ввод (или значение по умолчанию, если ввод пустой) возвращается.
    props.groupId = ask('Выберите groupId [org.example]: ', 'org.example')
    props.artifactId = ask('Выберите artifactId [maven-simple]: ', 'maven-simple')
    props.version = ask('Выберите версию [1.0-SNAPSHOT]: ', '1.0-SNAPSHOT')
    props.pkg = ask("Выберите package для класса [$props.groupId]:", props.groupId)
    props.message = ask('Чего печатать в тесте? ', 'Привет, лентяй!')

    //метод processTemplates обрабатывает шаблоны, заменяя меаркеры значениями из мапы.
    processTemplates 'pom.xml', props

    //заменяем точки на слэши
    String packageDir = props.pkg.replaceAll(/\./, '/')
    //переносим исходник в нужную директорию
    moveFileToDirectory(new File(targetDir, 'App.java'), new File(targetDir, "src/main/java/$packageDir"), true)
    //обрабатываем шаблон
    processTemplates 'src/main/java/**/App.java', props




    Надеюсь, комментарии достаточно понятно объясняют, что происходит. Единственное, наверное, что нужно добавить, это то, что методы ask() и processTemplates() и поле targetDir попадают в скрипт из класса uk.co.cacoethes.lazybones.LazybonesScript, который является кастомным супер-классом этого скрипта.

  8. Пора собирать. У Lazybones есть свой плагин для Грейдла, который уже сконфигурирован в скрипте сборки, который мы сгенерировали в пункте 1. Этот плагин определяет task rules для сборки, установки в локальном кэше и деплоймента шаблонов на Бинтрей. Поскольку шаблон у нас не серъезный, на Бинтрей мы его класть не будем, а вот установить в кэш, чтобы попробовать запустить — обязательно. Запускаем сборку:

    >gradlew installTemplateMavenSimple

    :packageTemplateMavenSimple

    :installTemplateMavenSimple

    BUILD SUCCESSFUL



  9. Тестируем! Создаем новую директорию и в ней создаем проект из шаблона (как мы уже видели):

    >lazybones create maven-simple 0.1 maven-simple

    Creating project from template maven-simple 0.1 in 'maven-simple'

    Выберите groupId [org.example]: com.demo

    Выберите artifactId [maven-simple]:

    Выберите версию [1.0-SNAPSHOT]: 0.1

    Выберите package для класса [org.example]:org.simple

    Чего печатать в тесте? Привет, Хабр!


    Шаблон а-ля архетайп

    ---------------------------------

    Ты создал Мавеновский проект. Может хватит? Грейлд ждет тебя.


    Project created in maven-simple!


    Сообщение в конце, приходит, конечно из readme.md. Обратите внимание, я не указал artifactId, ожидаю maven-simple по умолчанию.

    Заходим в директорию maven-simple, и любуемся:



    │ pom.xml
    │ README.md

    └───src
    └───main
    ├───java
    │ └───org
    │ └───simple
    │ App.java

    └───resources



    Открываем pom.xml:

    <project>
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.demo</groupId>
    <artifactId>maven-simple</artifactId>
    <version>0.1</version>
    <build>
    <plugins>
    <plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>exec-maven-plugin</artifactId>
    <version>1.2.1</version>
    <executions>
    <execution>
    <phase>test</phase>
    <goals>
    <goal>java</goal>
    </goals>
    <configuration>
    <mainClass>org.simple.App</mainClass>
    <arguments>
    <argument>Привет, Хабр!</argument>
    </arguments>
    </configuration>
    </execution>
    </executions>
    </plugin>
    </plugins>
    </build>
    </project>




    Всё как надо. Открываем App.java:

    package org.simple;

    public class App {
    public static void main(String[] args) {
    System.out.println(args[0]);
    }
    }




    Тоже порядок. Запускаем Мавен:

    >mvn test
    [INFO] Scanning for projects...
    [INFO]
    [INFO] ------------------------------------------------------------------------
    [INFO] Building maven-simple 0.1
    [INFO] ------------------------------------------------------------------------
    [INFO]
    [INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ maven-simple ---
    [INFO] Copying 0 resource
    [INFO]
    [INFO] --- maven-compiler-plugin:2.5.1:compile (default-compile) @ maven-simple ---
    [INFO] Nothing to compile - all classes are up to date
    [INFO]
    [INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ maven-simple ---
    [INFO]
    [INFO] --- maven-compiler-plugin:2.5.1:testCompile (default-testCompile) @ maven-simple ---
    [INFO] No sources to compile
    [INFO]
    [INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ maven-simple ---
    [INFO] No tests to run.
    [INFO]
    [INFO] >>> exec-maven-plugin:1.2.1:java (default) @ maven-simple >>>
    [INFO]
    [INFO] <<< exec-maven-plugin:1.2.1:java (default) @ maven-simple <<<
    [INFO]
    [INFO] --- exec-maven-plugin:1.2.1:java (default) @ maven-simple ---
    Привет, Хабр!
    [INFO] ------------------------------------------------------------------------
    [INFO] BUILD SUCCESS
    [INFO] ------------------------------------------------------------------------
    [INFO] Total time: 0.768s
    [INFO] Finished at: Fri Apr 04 02:54:57 IDT 2014
    [INFO] Final Memory: 7M/304M
    [INFO] ------------------------------------------------------------------------






Вот и всё. Я надеюсь, вы прониклись простотой и изяществом как создания проектов из шаблонов так и созданием самих шаблонов в Lazybones. Мне кажется, за эти простоту и изящество во многом надо благодaрить Груви.
Бесстыдный пиар, можно не читать
Если вам понравилось и вы со мной согласны на счет изящества и простоты, и если вы хотите и сами подучить немножко Груви, то приходите ко мне на тренинг 15-го апреля в Казани, или 17-го апреля в Москве. Обещаю научить Грувийным плюшкам.


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Комментариев нет:

Отправить комментарий