Коллега спросил меня сегодня: «Не могли бы вы утверждать, что использование Groovy / Grails может сделать вас значительно более продуктивным для небольших проектов по сравнению с чем-то вроде JSF? Если да, то почему?»
В ответ я создал очень маленькое приложение. В тот момент, когда небольшое приложение было готово, я представила, что отправила его клиенту. Затем я представил, что гораздо позже кому-то еще дали мои источники с указанием добавить новую функцию. Ниже, в двух частях, приведены некоторые общие выводы, которые я сделал в результате. Не стесняйтесь спорить!
Создание начального проекта
Вот небольшое приложение, то есть он-лайн кладбище домашних животных. Все нижеприведенное было сгенерировано командами Grails после документа Введение в веб-платформу Grails , который также знакомит вас со связанными инструментами IDE NetBeans.
Есть одно дополнение к тому, что описано в приведенном выше документе, в контексте страницы, показанной ниже — я использовал команду «grails generate-all», чтобы Grails генерировал контроллер и представления для созданного мною класса домена. Затем у меня было 4 файла GSP (т.е. файлы Grails JSP), которые я немного подправил, чтобы изменить текст.
Содержимое выше — это некоторые тестовые данные, которые я включил в приложение в файле «BootStrap.groovy», который является одним из нескольких заполнителей конфигурации, которые предоставляет каждое приложение Grails:
class BootStrap {
def init = { servletContext ->
new Pet (type: "Cat", name: "Weebles", story: "Run over by tractor.").save()
new Pet (type: "Cat", name: "Humphrey", story: "Attacked by rabid hamster.").save()
new Pet (type: "Dog", name: "Mister Sox", story: "Flushed down toilet.").save()
new Pet (type: "Cat", name: "Lucky Lambkins", story: "Hit in face by newspaper.").save()
new Pet (type: "Dog", name: "Quincy", story: "Unforeseen avalanche.").save()
}
def destroy = {
}
}
Во-вторых, вот как я создал свой домен:
class Pet {
String type
String name
String story
static constraints = {
type(blank: false)
name(blank: false)
story(maxSize: 1000)
}
}
Просто определив maxSize больше 255, я получаю текстовую область для элемента «story», в то время как «type» и «name» представлены текстовыми полями:
Это также здорово, потому что это означает … Grails дает мне «разработку, управляемую доменом». Это очень мощно, так как мой домен находится там, где хранятся все знания приложения. Его структура и взаимосвязи, а также высокоуровневая инкапсуляция пользовательских требований также хранятся в домене приложения. Действительно мощная особенность Grails в том, что все в приложении генерируется из домена … вплоть до того, представлен ли элемент домена текстовым полем или текстовой областью!
Кроме того, чтобы получить раскрывающийся список с поддерживаемыми типами домашних животных, я заменил элемент input в сгенерированном классе «create.gsp» следующим элементом:
<g:select from="['Dog', 'Cat']" id="type" name="type"/>
У Grails есть ряд таких специальных тегов, которые предоставляют готовые функциональные возможности, такие как, например, «g: countrySelect», который дает вам раскрывающийся список названий стран.
Лучше всего, когда я вносил изменения в свое приложение, мне нужно было только обновить браузер (т. Е. Повторное развертывание приложения не требовалось), чтобы увидеть мои изменения.
Добавление новой функции
Затем я представил, что отправил вышеуказанное приложение своему конечному пользователю. Через некоторое время, когда было зарегистрировано много мертвых питомцев, конечный пользователь хочет добавить некоторые функции поиска. По какой-то причине кто-то другой получает задание с просьбой. Нет проблем, особенно если они уже знают Grails, так как все в моем проекте находится в том же месте, что и любой другой проект, который использует Grails (соглашение по конфигурации):
Поэтому сначала я создаю новый файл GSP с именем «search.gsp» (для которого я использую шаблон файла в IDE NetBeans). Получив новую страницу GSP, я заглянул внутрь страницы «list.gsp», чтобы найти место, где определяется ссылка на «New Dead Pet». (Помните, что страница list.gsp вместе со всем ее содержимым была сгенерирована Grails, поэтому я ничего там не делал, кроме настройки некоторых меток.) Найдя этот элемент, я просто скопировал его, а затем изменил, как следующим образом:
Теперь, когда я обновил браузер, я увидел это:
Я нажал на новую ссылку и увидел это:
Поскольку все действия определены в связанном контроллере, я добавил фиктивное поисковое действие:
def search = {}
Обновляя браузер, я увидел страницу «search.gsp», которую создал ранее. Пришло время добавить контент. Я вызвал один из предопределенных (т.е. не мной, а Grails) методов для моего объекта домена, используя завершение кода в среде IDE для поиска нужного мне метода:
Я получил $ {Pet.count ()} на своей странице GSP.
Обновляя браузер (то есть снова без повторного развертывания) я увидел результат:
Теперь пришло время серьезно!
Почти за короткое время я создал эту простую форму поиска …
… который при нажатии кнопки приводит к отображению следующей страницы:
Как это сделать?
- Создайте две новые страницы GSP, одну с именем «search.gsp», а другую — «results.gsp».
- Скопируйте заголовок из одного из созданных файлов GSP, чтобы внешний вид ваших страниц был одинаковым.
- Определите тело «search.gsp» следующим образом:
<h1>Search</h1>
<g:form action="results">
<label>Type</label>
<g:textField name="type"/>
<g:submitButton name="search" value="Search"/>
</g:form>
<p>Searching ${Pet.count()} pets...Обратите внимание на атрибут name в textField. Здесь это называется «тип». Также обратите внимание на атрибут «action» формы GSP. Здесь это называется «результаты». В результате имени «results», когда пользователь нажимает кнопку, вызывается действие «results» в контроллере, открывается страница «results.gsp», и содержимое textField передается как параметр, доступ к которому осуществляется через «params.type».
- Однако действия «результатов» не существует. Добавьте его следующим образом в контроллер, где ВСЕ действия найдены, что, конечно, очень удобно.
def results = {
def pets = Pet.findAllByType(params.type)
return [ pets: pets, term: params.type ]
}Поскольку мы никогда не создавали метод findAllByType для объекта домена Pet, нам нужно потратить некоторое время на его создание. Но, подожди минутку! Нам не нужно тратить время на его создание, потому что это один из многих блестящих «динамических искателей», предоставляемых Grails. Основываясь на определении нашего доменного объекта, Grails предоставляет набор методов, которые можно вызывать в нашей базе данных, просто передав строку поиска, полученную от пользователя! Вот список динамических искателей, доступных приложению для кладбища домашних животных, автоматически созданных Grails для объекта домена, определенного ранее:
На этом этапе действие «results» делает две части данных доступными для соответствующей страницы «results.gsp». Во-первых, поисковый запрос, а во-вторых, список домашних животных, извлеченных из базы данных. И вот определение тела страницы результатов, показанной ранее:
<h1>Results for "${term}"</h1>
<ul>
<g:each var="pet" in="${pets}">
<li><b>${pet.name}</b> (${pet.story})
</g:each>
</ul>Затем просто обновите браузер снова, и у вас будет полностью функционирующая функция поиска! Он становится намного более мощным, то есть всей этой историей о «динамическом поиске», и намного более сложным, но для этого вам нужно прочитать « Grails in Action » Глена Смита и Питера Ледбрука.
Вывод
На этом этапе я могу пойти довольно далеко, ответив на первоначальный вопрос: «Не могли бы вы утверждать, что использование Groovy / Grails может сделать вас значительно более продуктивным для небольших проектов по сравнению с чем-то вроде JSF? Если да, то почему?»
Мой ответ утвердительно основан на следующем, вытекающем из приведенных выше экспериментов:
- Grails создает структуру проекта и заставляет все быть в определенном месте, так что мне не нужно об этом беспокоиться, и чтобы тот, кто в конечном итоге расширит приложение позже, сразу же знал, где все найти.
- Разработка под управлением домена — это здорово, и замечательно, что Grails заставляет меня работать таким образом. Все правила и требования моего приложения могут быть определены, и тогда я смогу сгенерировать всю структуру приложения вокруг этого. Когда домен должен измениться, я могу заново создать основные файлы и при необходимости настроить их.
- Встроенная проверка. Поскольку поля «тип» и «имя» не могут быть пустыми, благодаря закрытию ограничений в классе домена конечный пользователь видит красные сообщения об ошибках (которые можно настроить) и не может сохранять такие записи в базе данных, если любое из этих полей не заполнено.
- Поскольку генерируется так много файлов, я могу использовать много форматирования и стилей, просто посмотрев, как Grails сделал это для меня в других файлах. Например, «search.gsp» и «results.gsp» почти не заняли времени, так как я скопировал так много из «create.gsp» и других сгенерированных файлов GSP.
- Все действия находятся в одном месте (контроллере) и они тесно связаны с конкретными файлами GSP.
- Файлы GSP имеют несколько очень мощных тегов, так что вы можете, например, включить раскрывающийся список, содержащий список стран или селектор даты / времени, связанный с вашими контроллерами и объектами домена, без особой работы.
- Доступ к базе данных обрабатывается для вас, так что, не зная ничего ни о Hibernate, ни о Spring, вы используете их под капотом. Также очень легко подключить свою собственную базу данных к приложению, заменив систему в памяти, к которой приложения Grails автоматически получают доступ.
- «Динамические искатели» великолепны. Я понятия не имею, как они работают, но мне и знать не нужно. Они просто работают, и моя IDE поддерживает меня, показывая, что доступно.
- Groovy — это глазурь на торте или, лучше сказать, цемент между трещинами, позволяющий мне кодировать гораздо быстрее и интуитивнее, чем я был бы вынужден делать в Java.
- Нет необходимости перераспределения во время разработки. Настройте файл, измените контроллер, что угодно, затем сохраните и обновите браузер, и это ваше изменение.
Во всяком случае, это то, что я скажу своему коллеге на основе моего личного опыта с Grails! Что-нибудь еще, о чем я должен рассказать ему в этом контексте? Наверное, довольно много. Например, я даже не коснулся встроенных функций тестирования! Но что конкретно для небольших проектов, что еще может быть включено в мой список?