Веб разработчики
знают, что такое CoffeeScript, а другим
достаточно такого объяснения: это
JavaScript с изрядным количеством синтаксического
сахара. Многие яваскриптовые конструкции
пишутся и, что важнее, читаются легче
на кофескрипт. Кто хочет знать больше
— велкам http://jashkenas.github.io/coffee-script/
Я же хочу
поделиться практическим опытом применения
CoffeeScript, на примере переписывания одного
из модулей одного из проектов. Проект
завязан на dojo, поэтому модуль — суть
модуль dojo, что доставляет (одни только
скобочки типа «)}))» после блока в сотню
строк стоят отдельной песни).
Начну с начала.
Основное рабочее место — MS Windows 2003 (да,
у меня есть планы переползти на один из
юниксов, но пока руки не доходят, не все
сразу). Поэтому мне нужен Node.js такой:
http://nodejs.org/dist/v0.10.24/node-v0.10.24-x86.msi
Собственно,
его и предлагает по умолчанию страница
http://nodejs.org/
Качаю, ставлю
— easy-peasy. При установке выбрал папку
d:\node
Внутри этой
папки запускаю cmd.exe, получаю типа консоль.
В консоли
запускаю установку кофескрипта
npm install -g coffee-script
Тоже не рокет сайенс.
Внутри рабочего
проекта создаю файл build.coffee.cmd с содержимым:
set wd=%~dp0 pushd "%wd%" coffee -w -c -o c:\mapedit\javascript\vs\obj\ c:\mapedit\javascript\vs\
Его задача — запустить вечный цикл, в
котором проверяется состояние файлов
*.coffee в папке c:\mapedit\javascript\vs\ и как только
любой из них изменился, компилять его,
создавая одноименный файл (но с расширением
js) в папке c:\mapedit\javascript\vs\obj\
В качестве
бонуса компиляции получаю проверку
кода на предмет ошибок. Удобно —
сохраняешь файл и тут же в окошке рядом
видно — ОК или облом.
Остается
немного, заменить в коде (другие файлы
проекта) подключение модуля с
require("vs/meapp"], function(meapp) { try {vsapp.log('try vsapp');} catch(ex) {vsapp = new meapp();} vsapp.log(" vsapp ready");
на
require(["vs/obj/meapp"], function(meapp) { try {vsapp.log('try vsapp');} catch(ex) {vsapp = new meapp();} vsapp.log(" vsapp ready");
и переписать файл vs\meapp.js на аналогичный
vs\meapp.coffee
Сейчас вы
увидите, чем конкретно coffeescript код лучше
кода javascript.
Было (vs\meapp.js):
Стало
(vs\meapp.coffee):
А это код JS на
выходе компилятора coffee:
Ниже тройной гист с кодом:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// deprecated. using meapp.coffee now | |
console.log('meapp enter'); | |
var vappFactory = null; | |
define( | |
["dojo/_base/declare", "dojo/dom", 'dojo/has', | |
"vs/mpvlib"], | |
function(declare, dom, has, mpvlib) { | |
console.log('meapp dojo/define. define app classes'); | |
try {vslib.log('try vslib');} catch(ex) {vslib = new mpvlib();} | |
vslib.log('vs.mpvlib declare vappFactory'); | |
vappFactory = { | |
constructor: function(args) { | |
vslib.log('vs.meapp constructor'); | |
declare.safeMixin(this, args); | |
}, | |
//Create menu of editor options | |
createEditorMenu : function createEditorMenu() { | |
require( | |
["dijit/Menu", | |
"dijit/MenuItem", | |
"dijit/CheckedMenuItem", | |
"dijit/MenuSeparator", | |
"dijit/PopupMenuItem", | |
"dojo/domReady!"], | |
dojo.hitch(this, function(Menu, MenuItem, CheckedMenuItem, MenuSeparator, PopupMenuItem){ | |
this.log("createEditorMenu"); | |
// editor menu | |
var menu = new dijit.DropDownMenu({ | |
style : "display: none;" | |
}); | |
var menuItem = new dijit.MenuItem({ | |
label : "Шаблоны", | |
iconClass : "dijitIconSearch", | |
onClick : function() { | |
console.log("editor menu, templates menuitem onclick. ", arguments); | |
navigateStack('editPanel'); | |
} | |
}); | |
menu.addChild(menuItem); | |
// tables submenu | |
var tabsmenu = new Menu(); | |
tabsmenu.addChild(new MenuItem({ | |
label: "Отключающие устройства", | |
iconClass : "dijitFolderOpened", | |
onClick : dojo.hitch(this, function() { | |
console.log("editor menu, table onclick. ", arguments); | |
this.loadFCTable("http://cgis.allgis.org/arcgis/rest/services/edit_Газовая_сеть/FeatureServer/0"); | |
}) | |
})); | |
tabsmenu.addChild(new MenuItem({ | |
label: "СКЗ", | |
iconClass : "dijitFolderOpened", | |
onClick : dojo.hitch(this, function() { | |
console.log("editor menu, table onclick. ", arguments); | |
this.loadFCTable("http://cgis.allgis.org/arcgis/rest/services/edit_Газовая_сеть/FeatureServer/1"); | |
}) | |
})); | |
tabsmenu.addChild(new MenuItem({ | |
label: "Газопроводы", | |
iconClass : "dijitFolderOpened", | |
onClick : dojo.hitch(this, function() { | |
console.log("editor menu, table onclick. ", arguments); | |
this.loadFCTable("http://cgis.allgis.org/arcgis/rest/services/edit_Газовая_сеть/FeatureServer/2"); | |
}) })); | |
tabsmenu.addChild(new MenuItem({ | |
label: "ГРП", | |
iconClass : "dijitFolderOpened", | |
onClick : dojo.hitch(this, function() { | |
console.log("editor menu, table onclick. ", arguments); | |
this.loadFCTable("http://cgis.allgis.org/arcgis/rest/services/edit_Газовая_сеть/FeatureServer/3"); | |
}) })); | |
menu.addChild(new PopupMenuItem({ | |
label: "Таблицы", | |
popup: tabsmenu, | |
iconClass : "dijitFolderOpened" | |
})); | |
// create dropdown button to display menu | |
var menuButton = new dijit.form.DropDownButton({ | |
label : "Редактирование", | |
id : 'editorDropDownButton', | |
title : "Шаблоны, таблицы", | |
dropDown : menu, | |
iconClass : "dijitFolderOpened" | |
}); | |
menuButton.startup(); | |
dojo.byId('webmap-toolbar-left').appendChild(menuButton.domNode); | |
})); | |
}, // createEditorMenu | |
//this.loadFCTable("http://cgis.allgis.org/arcgis/rest/services/edit_Газовая_сеть/FeatureServer/0"); | |
loadFCTable: function(fcUrl) { | |
this.log("loadFCTable"); | |
console.debug("loadFCTable. fcUrl: ", arguments); | |
}, // loadFCTable | |
log: function(str, doalert) { // write app messages to log | |
vslib.log("vs.meapp." + str, doalert); | |
} // log | |
}; // vlibFactory | |
vslib.log('meapp dojo/declare vs.meapp'); | |
return declare("vs.meapp", null, vappFactory); | |
} | |
); // define | |
console.log('meapp leave'); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
### | |
Mapedit app classes. | |
Author: vasnake@gmail.com | |
### | |
console.log "meapp enter" | |
if vappFactory? | |
alert "vappFactory defined already!" | |
else | |
vappFactory = {} | |
define(["dojo/_base/declare", "dojo/dom", 'dojo/has', "vs/mpvlib"], | |
(declare, dom, has, mpvlib) -> | |
console.log 'meapp dojo/define. define app classes' | |
try | |
vslib.log('try vslib') | |
catch | |
window.vslib = new mpvlib() | |
vslib.log('vs.mpvlib declare vappFactory') | |
vappFactory = { # meapp object factory | |
# used as: require(["vs/obj/meapp"], function(meapp) { vsapp = new meapp(); ... | |
constructor: (args) -> | |
vslib.log('vs.meapp constructor') | |
declare.safeMixin(this, args) | |
log: (str, doalert) -> # write app messages to log | |
vslib.log("vs.meapp.#{str}", doalert); | |
# this.loadFCTable("http://cgis.allgis.org/arcgis/rest/services/edit_Газовая_сеть/FeatureServer/0"); | |
loadFCTable: (fcUrl, fcName = '') -> | |
this.log("loadFCTable") | |
console.debug("loadFCTable. fcUrl: ", arguments) | |
# Create editor menu | |
createEditorMenu : () -> | |
require( | |
["dijit/Menu", | |
"dijit/MenuItem", | |
"dijit/CheckedMenuItem", | |
"dijit/MenuSeparator", | |
"dijit/PopupMenuItem", | |
"dojo/domReady!"], | |
dojo.hitch(this, | |
(Menu, MenuItem, CheckedMenuItem, MenuSeparator, PopupMenuItem) -> | |
this.log("createEditorMenu") | |
# tables submenu | |
subdata = [ | |
{label: "Отключающие устройства", url: "http://cgis.allgis.org/arcgis/rest/services/edit_Газовая_сеть/FeatureServer/0"}, | |
{label: "СКЗ", url: "http://cgis.allgis.org/arcgis/rest/services/edit_Газовая_сеть/FeatureServer/1"}, | |
{label: "Газопроводы", url: "http://cgis.allgis.org/arcgis/rest/services/edit_Газовая_сеть/FeatureServer/2"}, | |
{label: "ГРП", url: "http://cgis.allgis.org/arcgis/rest/services/edit_Газовая_сеть/FeatureServer/3"} | |
] | |
self = this | |
tabsmenu = new Menu | |
tabsmenu.addChild new MenuItem { | |
label: sd.label, | |
iconClass : "dijitFolderOpened", | |
fClassUrl: sd.url, | |
onClick : () -> | |
console.log("editor menu, table menuitem onclick. ", arguments) | |
self.loadFCTable(this.fClassUrl, this.label) | |
} for sd, idx in subdata | |
# editor menu | |
menu = new dijit.DropDownMenu { style : "display: none;" } | |
menu.addChild MenuItem { | |
label : "Шаблоны", | |
iconClass : "dijitIconSearch", | |
onClick : () -> | |
console.log("editor menu, templates menuitem onclick. ", arguments) | |
navigateStack('editPanel') | |
} | |
menu.addChild new PopupMenuItem { | |
label: "Таблицы", | |
popup: tabsmenu, | |
iconClass : "dijitFolderOpened" | |
} | |
# create dropdown button to display menu | |
menuButton = new dijit.form.DropDownButton { | |
label : "Редактирование", | |
id : 'editorDropDownButton', | |
title : "Шаблоны, таблицы", | |
dropDown : menu, | |
iconClass : "dijitFolderOpened" | |
} | |
menuButton.startup | |
dojo.byId('webmap-toolbar-left').appendChild(menuButton.domNode) | |
) # dojo.hitch | |
) # require | |
# createEditorMenu | |
} # vappFactory | |
vslib.log('meapp dojo/declare vs.meapp') | |
# return from define | |
declare("vs.meapp", null, vappFactory) | |
# define inner function | |
) # dojo/define | |
console.log "meapp leave" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Generated by CoffeeScript 1.6.3 | |
/* | |
Mapedit app classes. | |
Author: vasnake@gmail.com | |
*/ | |
(function() { | |
var vappFactory; | |
console.log("meapp enter"); | |
if (typeof vappFactory !== "undefined" && vappFactory !== null) { | |
alert("vappFactory defined already!"); | |
} else { | |
vappFactory = {}; | |
} | |
define(["dojo/_base/declare", "dojo/dom", 'dojo/has', "vs/mpvlib"], function(declare, dom, has, mpvlib) { | |
console.log('meapp dojo/define. define app classes'); | |
try { | |
vslib.log('try vslib'); | |
} catch (_error) { | |
window.vslib = new mpvlib(); | |
} | |
vslib.log('vs.mpvlib declare vappFactory'); | |
vappFactory = { | |
constructor: function(args) { | |
vslib.log('vs.meapp constructor'); | |
return declare.safeMixin(this, args); | |
}, | |
log: function(str, doalert) { | |
return vslib.log("vs.meapp." + str, doalert); | |
}, | |
loadFCTable: function(fcUrl, fcName) { | |
if (fcName == null) { | |
fcName = ''; | |
} | |
this.log("loadFCTable"); | |
return console.debug("loadFCTable. fcUrl: ", arguments); | |
}, | |
createEditorMenu: function() { | |
return require(["dijit/Menu", "dijit/MenuItem", "dijit/CheckedMenuItem", "dijit/MenuSeparator", "dijit/PopupMenuItem", "dojo/domReady!"], dojo.hitch(this, function(Menu, MenuItem, CheckedMenuItem, MenuSeparator, PopupMenuItem) { | |
var idx, menu, menuButton, sd, self, subdata, tabsmenu, _i, _len; | |
this.log("createEditorMenu"); | |
subdata = [ | |
{ | |
label: "Отключающие устройства", | |
url: "http://cgis.allgis.org/arcgis/rest/services/edit_Газовая_сеть/FeatureServer/0" | |
}, { | |
label: "СКЗ", | |
url: "http://cgis.allgis.org/arcgis/rest/services/edit_Газовая_сеть/FeatureServer/1" | |
}, { | |
label: "Газопроводы", | |
url: "http://cgis.allgis.org/arcgis/rest/services/edit_Газовая_сеть/FeatureServer/2" | |
}, { | |
label: "ГРП", | |
url: "http://cgis.allgis.org/arcgis/rest/services/edit_Газовая_сеть/FeatureServer/3" | |
} | |
]; | |
self = this; | |
tabsmenu = new Menu; | |
for (idx = _i = 0, _len = subdata.length; _i < _len; idx = ++_i) { | |
sd = subdata[idx]; | |
tabsmenu.addChild(new MenuItem({ | |
label: sd.label, | |
iconClass: "dijitFolderOpened", | |
fClassUrl: sd.url, | |
onClick: function() { | |
console.log("editor menu, table menuitem onclick. ", arguments); | |
return self.loadFCTable(this.fClassUrl, this.label); | |
} | |
})); | |
} | |
menu = new dijit.DropDownMenu({ | |
style: "display: none;" | |
}); | |
menu.addChild(MenuItem({ | |
label: "Шаблоны", | |
iconClass: "dijitIconSearch", | |
onClick: function() { | |
console.log("editor menu, templates menuitem onclick. ", arguments); | |
return navigateStack('editPanel'); | |
} | |
})); | |
menu.addChild(new PopupMenuItem({ | |
label: "Таблицы", | |
popup: tabsmenu, | |
iconClass: "dijitFolderOpened" | |
})); | |
menuButton = new dijit.form.DropDownButton({ | |
label: "Редактирование", | |
id: 'editorDropDownButton', | |
title: "Шаблоны, таблицы", | |
dropDown: menu, | |
iconClass: "dijitFolderOpened" | |
}); | |
menuButton.startup; | |
return dojo.byId('webmap-toolbar-left').appendChild(menuButton.domNode); | |
})); | |
} | |
}; | |
vslib.log('meapp dojo/declare vs.meapp'); | |
return declare("vs.meapp", null, vappFactory); | |
}); | |
console.log("meapp leave"); | |
}).call(this); |
Как видно,
кроме небольшой экономии на количестве
строк, изрядно улучшается читаемость
кода за счет применения Python-подобных
(Python — the best!) методов — использование
отступов вместо скобок (хотя можно
продолжать использовать скобки —
либерализм), расширенного набора
операторов для циклов и условий, и …
читайте http://jashkenas.github.io/coffee-script/
там все очень подробно и понятно
нарисовано.
А я свою задачу
выполнил — показал на примере, как можно
переписать dojo-завязанный код на
CoffeScript.
Мне понравилось.
Осталось
упомянуть про удобство редактирования
кода. Тут все не так радужно.
К сожалению,
плагин для Eclipse, которому положено
ублажать лень разработчика, не рабочий.
От слова «совсем». Может быть, когда-нибудь
он заработает. Для SciTE поддержки
CoffeeScript тоже пока не наблюдается. На
сегодня самый удобный способ (под MS
Windows) редактирования кофескриптов —
это использование Notepad++ с подключенным
https://github.com/blakmatrix/CoffeeScript_notepad_UDL
Подключается
он проще пареной свеклы: меню Language —
Define your language — Import -
https://github.com/blakmatrix/CoffeeScript_notepad_UDL/raw/master/CoffeeScript.xml
закрыть окно,
перезапустить Нотепад++.
Дополнительно:
Комментариев нет:
Отправить комментарий