Статья посвящена Java девелоперам, которых жизнь заставила или пока только заставляет двигаться вперед, к светлому agile будущему. Препологается, что читатель знаком с Java, Javascript и слышал про JSR 223.
Многим очень часто приходилось спорить на тему «что лучше, X или Y». Раньше это была пара Java/C++, сейчас акцент больше сместился в сторону скриптовых языков.
Буквально на днях обуждали с коллегами стоит ли учить все эти новшества тем, кто с любимым языком (будь то Java или С++) прошел огонь, воду и медные трубы и кажется теперь умеет все. Думаю что стоит.
Есть очень много примеров изящного кода написаного на любом языке, но ничто так не запоминается как хороший код написаный на функиональном языке. Тех кто пользуется Javascript-ом каждый день будет трудно чем-то удивить, но для приверженцев чистой Java, без «вредных» скриптовых примесей кое-что может показаться открытием. Потверждением тому служит простой пример с которым мне пришлось столкнуться.
Небольшой Java проект над которым я работаю состоит в написании простенького Swing интерфейса, распространяться он должен через Web Start. И вот тут оказывается что человек привыкший к изобилию дополнительных библиотек на server side (apache commons, spring, freemarker) оставшись один на один с Java должен для себя сделать трудный выбор: добавлять сторонние библиотеки в проект просто потому что там есть «вкусненький» класс или писать что-то самому. Опыт вроде позволяет выбрать второй путь, но при этом сложность проекта возрастает в разы.
Например пришлось столкнуться с задачей сгенерировать довольно сложный HTML. Данные берутся из модели, казалось вот оно, тут можно применить Velocity или Freemarker, но мысль о том что я добавлю в проект сотни классов просто чтобы красиво нарисовать страничку не давала мне покоя. С другой стороны я понимаю что написать самому парсер это не полчаса времени, плюс тестирование… хотя стоп! а что если написать его на Javascript-е? К счастью Javascript достаточно легко исполнить в JRE, более того в Java7 влючает в себя Javascript Engine который знает и умеет Javascript 1.8, а это означает много вкусных «плюшечек». И я решил попробовать.
Синтаксис выбрал на подобие JSP: <% %> это набор statement-ов, а <%= %> — соответсвенно expression. Для тестирования использвал скриптовую консоль jrunscript. Идея очень простая и полностью повторяет то что делает JSP: из темплейта сгенерировать некий код или функцию, выполнение которой вернет готовую страничку. Простейший пример темплейта для тестирования:
js> var string = "Variable x is <% if ( 'undefined' == typeof x ) { %> undefined <%} else { %> <%= x %><%}%>"
Для того чтобы обработать такой темплейт достаточно его разбить с помощью функии split:
js> string.split(/<%(.*?)%>/).forEach(function(x,i)print((i+1)+': '+x))
1: Variable x is
2: if ( 'undefined' == typeof x ) {
3: undefined
4: } else {
5:
6: = x
7:
8: }
9:
Вызов forEach нужен был просто чтобы красиво распечатать массив. Как видно нечетными идут строки, а четными — выражения между ними. А другого нам и не надо, теперь дело техники из этого массива сформировать тело функции.
js> string.split(/<%(.*?)%>/).map(function(s,i) (i % 2
> ? (s[0] == '=' ? 'this.push(' + s.slice(1) + ');' : s)
> : (s && 'this.push(' + uneval(s) + ');')
> )).join('n')
this.push("Variable x is ");
if ( 'undefined' == typeof x ) {
this.push(" undefined ");
} else {
this.push(" ");
this.push( x );
}
И вот тот момент ради которого я все это затеял. Надо написать генератор функций, по сути он будет компилировать наш темплейт:
js> function compile(template) new Function(template.split(/<%(.*?)%>/).map(function(s,i) (i % 2
> ? (s[0] == '=' ? ['this.push(', ');'].join(s.slice(1)) : s)
> : (s && ['this.push(', ');'].join(uneval(s)))
> )).join('n'))
js> compiled = compile(string)
function anonymous() {
this.push("Variable x is ");
if ("undefined" == typeof x) {
this.push(" undefined ");
} else {
this.push(" ");
this.push(x);
}
}
Наша история подходит к концу, опытный читатель уже наверное догадался что полученные в строки мы будем складывать в массив с помощью функции push.
Осталось протестировать полученый результат:
js> var array = []; compiled.call(array); array.join('')
Variable x is undefined
js> x = 5
js> compiled.call(array = []); array.join('')
Variable x is 5
Осталось украсить код чтобы его было удобно использовать и обработчик темплейтов готов.
Вывод: мне понадобиось около получаса времени чтобы написать на Javascript достаточно мощный обработчик темплейтов, в тоже время используя Java я даже затрудняюсь сказать точно заняло бы это день или неделю. Насчет недели это конечно же шутка, но она недалека от истины.
Автор: acebanenco