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

В 2014-м JavaScript удос­тоил­ся вни­мания серь­езных ребят из Apple. На кон­ферен­ции WWDC 2014 была анон­сирова­на новая тех­нология JavaScript Automation, поз­воля­ющая соз­давать при­ложе­ния для OS X на этом хитовом язы­ке прог­рамми­рова­ния. Поп­робу­ем поз­накомить­ся с новин­кой поб­лиже и на реаль­ных при­мерах понять: а сто­ит ли игра свеч?

 

Новое — хорошо забытое старое

В любой нор­маль­ной опе­раци­онной сис­теме есть средс­тва для авто­мати­зации. Чаще все­го они пред­став­ляют собой хар­дкор­ные инс­тру­мен­ты. Взять, к при­меру, nix’ы. Здесь гла­венс­тву­ет мощ­ный bash, спо­соб­ный решать самые изощ­ренные задачи. Проб­лема лишь в его слож­ности. Не вся­кий юзер решит­ся на твор­ческий союз с этим швей­цар­ским ножом. Получа­ется, что инс­тру­мент есть, а поль­зуют­ся им толь­ко прод­винутые еди­ницы.

Ана­логич­ная ситу­ация с Windows, у которой есть свой интер­пре­татор в виде CMD. Более прос­той, но и менее фун­кци­ональ­ный — тягать­ся с bash ему не под силу. Резуль­тат тот же — инс­тру­мент есть, а работать с ним желания нет.

Спе­циаль­но для таких поль­зовате­лей были при­дума­ны все­воз­можные прос­лой­ки и аль­тер­нативы. Нап­ример, в Windows все жела­ющие могут писать на том же JavaScript (при­чем очень дав­но) или VBS. Поз­же добавил­ся мощ­ный инс­тру­мент в виде PowerShell, дающий воз­можность более дос­тупной авто­мати­зации.

Что почитать по теме?

Обыч­но я при­вожу кучу полез­ных ссы­лок к статье, но сегод­ня набор будет более скуд­ным. JXA слиш­ком молодая тех­нология, и боль­шого объ­ема литера­туры по ней пока нет. Есть отдель­ные обзо­ры зарубеж­ных кол­лег, нем­ного воп­росов с отве­тами (при­чем воп­росов без отве­тов боль­ше) на Stack Overflow и скуд­новатая офи­циаль­ная докумен­тация. Поэто­му если ты всерь­ез решил занять­ся JXA, то при­готовь­ся к самос­тоятель­ному ресечин­гу.

  • goo.gl/M3Bqx3 — офи­циаль­ная докумен­тация в Mac Developer Library. До нор­маль­ной докумен­тации она не дотяги­вает (по прав­де говоря, это тех­ничес­кий пресс‑релиз), поэто­му осо­бо оболь­щать­ся не сто­ит. Одна­ко про­читать ее на разок нуж­но: по ней рас­кидана куча полез­ных снип­петов.
  • goo.gl/m5AO3h — Batch File Rename Script. При­мер пол­ноцен­ного скрип­та с исполь­зовани­ем JavaScript for Automation. Как вид­но из наз­вания, сце­нарий поз­воля­ет пере­име­новы­вать фай­лы, выделен­ные в Finder. Вто­рая инте­рес­ная осо­бен­ность при­мера — готов­ность к исполь­зованию с Automator.
  • goo.gl/veMVWn — репози­торий с при­мера­ми, демонс­три­рующи­ми исполь­зование мос­та к Objective-C. При­меры прос­тей­шие и приз­ваны помочь разоб­рать­ся с при­мене­нием стан­дар­тных эле­мен­тов управле­ния в JavaScript.
  • goo.gl/87SnZb — статья Building OS X Apps with JavaScript. Доб­ротный матери­ал с при­мером раз­работ­ки неболь­шого при­ложе­ния для OS X, с исполь­зовани­ем фрей­мвор­ка Foundation (не путать с одно­имен­ным CSS-про­дук­том).
  • goo.gl/2egSwH — офи­циаль­ная докумен­тация по Foundation Framework.
  • goo.gl/NrftJs — JavaScript for Automation Cookbook. Репози­торий с полез­ной информа­цией о при­мене­нии JXA. Информа­ции не так мно­го, как хотелось бы, но упус­кать из виду ее нель­зя.]

Мир OS X стал­кивал­ся с подоб­ной ситу­ацией. Как ни кру­ти, а в осно­ве этой ОС всег­да лежала BSD. И зна­чит, средс­тва авто­мати­зации в нем исходно сле­дуют тра­дици­онно­му UNIX-way: bash или любой дру­гой язык прог­рамми­рова­ния.

Есть толь­ко одно но. В отли­чие от сво­их пред­ков, OS X ста­рает­ся по мак­симуму упро­щать поль­зовате­лям жизнь. Их сред­ний поль­зователь вов­се не хар­дкор­ный гик, ему не нуж­ны 100500 малопо­нят­ных фич и велико­леп­ные язы­ковые пас­сажи. Для него апри­ори важ­на юза­бель­ность и прос­тота — чем про­ще, тем луч­ше.

Бо­рода­тые поль­зовате­ли дол­жны пом­нить, что воз­можность писать авто­мати­зиру­ющие скрип­ты (сце­нарии) на JS появи­лась еще в далеком 2001 году. Увы, тог­да популяр­ность он не заво­евал, и нес­коль­кими годами поз­же его сме­нил язык AppleScript, надол­го став­ший стан­дартом. Это был не прос­то «еще один язык прог­рамми­рова­ния», а новый взгляд на прог­рамми­рова­ние для обыч­ных людей. Вмес­то при­выч­ных матерым раз­работ­чикам син­такси­чес­ких конс­трук­ций AppleScript общался язы­ком, похожим на челове­чес­кий.

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

По­луча­ется, что JavaScript (JS OSA) в яблочном строю был дав­но, но по воле рока и вви­ду сво­ей юнос­ти нес­пра­вед­ливо был заб­рошен на зад­ворки. И эту ситу­ацию лег­ко мож­но понять, если вспом­нить, что в начале нулевых JS ассо­цииро­вал­ся боль­ше с хулиган­ским инс­тру­мен­том для изде­ватель­ств над бра­узе­ром, нежели с уни­вер­саль­ным язы­ком прог­рамми­рова­ния…

 

JavaScript for Automation

Ес­ли прог­рамми­ровать на JavaScript для OS X теоре­тичес­ки мож­но уже боль­ше десяти лет, то в чем же тог­да фиш­ка столь обсужда­емо­го анон­са? Неуж­то Apple соз­рела для запоз­давшей мар­кетин­говой кам­пании?

В пос­ледней вер­сии OS X (Yosimite) была про­дела­на боль­шая работа, дав­шая воз­можность более тес­ного вза­имо­дей­ствия с сис­темой. Тут речь даже не о JavaScript, а о появ­лении целого ком­плек­са API, биб­лиотек, поз­воля­ющих в пер­спек­тиве при­менять для авто­мати­зации не толь­ко JavaScript, но и дру­гие язы­ки прог­рамми­рова­ния. Если не упи­рать­ся в тех­ничес­кие детали, то это блю­до мож­но упо­добить .NET (уж прос­ти за гру­бое срав­нение). То есть в нашем рас­поряже­нии ока­зыва­ется одно прог­рам­мное ядро, оди­нако­во хорошо работа­ющее с дру­гими язы­ками прог­рамми­рова­ния.

Языки программирования и автоматизация
Язы­ки прог­рамми­рова­ния и авто­мати­зация

Воз­ника­ет резон­ный воп­рос: а почему пер­вопро­ход­цем выб­рали JavaScript? Вряд ли кто отве­тит на него точ­но — офи­циаль­ная информа­ция отсутс­тву­ет. Мне кажет­ся, что не пос­ледним за в этом воп­росе ста­ла его популяр­ность. Сегод­ня этот язык пережи­вает бум, и армия его раз­работ­чиков уве­рен­но рас­тет. Грех не вос­поль­зовать­ся столь удач­ным сте­чени­ем обсто­ятель­ств.
Ну а теперь — нем­ного тех­ничес­ких деталей.

 

Тесная интеграция с системой

Речь уже не идет о баналь­ной авто­мати­зации в сти­ле «открыл прог­рамму → клик­нул кноп­ку». Прод­винутые поль­зовате­ли получа­ют воз­можность вза­имо­дей­ство­вать с натив­ными фрей­мвор­ками и биб­лиоте­ками. Рань­ше фича была дос­тупна зна­токам AppleScript, а сегод­ня ее рас­ширили и отда­ли в лапы ушлых JavaScript’еров. Бла­года­ря дос­тупу к Cocoa API раз­работ­чики могут соз­давать при­ложе­ния с натив­ным интерфей­сом пря­мо на JS. При­чем в боль­шинс­тве слу­чаев не будет никаких сущес­твен­ных про­виса­ний в ско­рос­ти по срав­нению с при­мене­нием Objective-C.

 

Простой диалог с приложениями

Вза­имо­дей­ствие с при­ложе­ниями сво­дит­ся к запол­нению свой­ств и выпол­нению методов соот­ветс­тву­ющих объ­ектов. Никаких хит­роум­ных и монс­тру­озных наз­ваний! Все сде­лано в рас­чете на, ска­жем так, прог­раммис­тов сред­ней прод­винутос­ти.

 

Дружба с Automator

Automator (визу­аль­ное средс­тво для авто­мати­зации) не оста­лось в сто­роне, и его сра­зу под­ружили с JavaScript. Теперь, помимо визу­аль­ных «кубиков» с логикой AppleScript, реаль­но исполь­зовать труш­ный код на JS.

Применение Automator вместе с JS
При­мене­ние Automator вмес­те с JS
 

Документация

Пре­зен­тация говорит о хорошей докумен­тации, но, на мой взгляд, здесь все не так иде­аль­но. Да, биб­лиоте­ка с опи­сани­ем свой­ств/методов сто­ковых при­ложе­ний сде­лана хорошо. При­веде­но опи­сание всех встро­енных при­ложе­ний, и поп­робовать себя в роли гуру OS X авто­мати­зации ста­новит­ся воз­можным минут через 15 (само собой, при наличии опы­та прог­рамми­рова­ния). А вот в воп­росах более тес­ного вза­имо­дей­ствия с сис­темой воз­ника­ют некото­рые про­белы. Впро­чем, уве­рен, что это воп­рос вре­мени.

Список приложений для автоматизации
Спи­сок при­ложе­ний для авто­мати­зации
 

Готовим инструменты

Нач­нем с редак­тора кода. В прин­ципе, код мож­но писать в чем угод­но. Для меня в пос­леднее вре­мя стал стан­дартом сво­бод­ный редак­тор Brackets. Прав­да, для пер­вого зна­комс­тва с JavaScript Automation все же луч­ше вос­поль­зовать­ся стан­дар­тным редак­тором скрип­тов. Он находит­ся в «Прог­раммы → Ути­литы».

Стандартный редактор скриптов
Стан­дар­тный редак­тор скрип­тов

На мой взгляд, стан­дар­тный редак­тор выг­лядит ущер­бно — отсутс­тву­ют при­выч­ные прог­раммер­ско­му гла­зу необ­ходимые инс­тру­мен­ты. Нет даже эле­мен­тарной нумера­ции строк. Глав­ный его плюс — прос­тота запус­ка написан­ного кода. Закоди­ли нес­коль­ко стро­чек кода и одной кноп­кой выпол­нили тес­товый запуск.

Ана­логич­ного поведе­ния мож­но добить­ся с любым дру­гим редак­тором, но тог­да при­дет­ся пот­ратить вре­мя на нас­трой­ку. Я этим воп­росом пока не замора­чивал­ся, но думаю, что осо­бых слож­ностей воз­никнуть не дол­жно. Во вся­ком слу­чае, ути­лита osascript (о ней нем­ного поз­же) пок­рыва­ет все пот­ребнос­ти по запус­ку сце­нари­ев из кон­соли.

Свободный редактор Brackets
Сво­бод­ный редак­тор Brackets

Во вре­мя написа­ния кода будет край­не полезен встро­енный в редак­тор скрип­тов жур­нал событий (Окно → Жур­нал событий). Из него JXA-девело­пер чер­пает информа­цию, необ­ходимую для отладки. На пер­вых порах туда заг­лядывать при­дет­ся час­то, так как даже наличие опы­та в раз­работ­ке на JavaScript не спа­сет от некото­рых неожи­дан­ностей, при­сущих JXA.

Сра­зу взгля­нем на еще один инс­тру­мент, без которо­го вряд ли удас­тся написать серь­езный сце­нарий, — «Биб­лиоте­ку». Биб­лиоте­ка хра­нит информа­цию о методах и свой­ствах стан­дар­тных при­ложе­ний. Как задумал что‑то авто­мати­зиро­вать — сра­зу заг­ляни в нее (Окно → Биб­лиоте­ка).

Подробное описание объектов
Под­робное опи­сание объ­ектов

Те­перь поп­робу­ем про­верить все это на прак­тике и сот­ворить прос­тей­ший скрипт. Пусть это будет тра­дици­онный Hello, World, но толь­ко свое при­ветс­твие миру мы ска­жем голосом. Сна­чала в окне редак­тора скрип­тов сме­ним язык прог­рамми­рова­ния на JavaScript. Затем наберем три строч­ки и запус­тим сце­нарий на выпол­нение:

App = Application.currentApplication();
App.includeStandardAdditions = true;
App.say("Hello, World!");

Ес­ли все вве­дено без оши­бок, то при­ятный жен­ский голос (все зависит от сис­темных нас­тро­ек) про­изне­сет замылен­ную в прог­раммер­ских кру­гах фра­зу.

 

Рулим браузером

В моей любимой интернет‑бро­дил­ке пос­тоян­но висят десят­ки откры­тых вкла­док. Уви­дишь что‑нибудь инте­рес­ное, а читать вре­мени нет. Оставля­ешь вклад­ку откры­той и даешь себе обе­щание: «Чуть поз­же про­читаю». Вот толь­ко это «поз­же» не нас­тупа­ет никог­да, и вклад­ки хаотич­но накап­лива­ются. Энтро­пия нарас­тает и в ито­ге сжи­рает всю дос­тупную память. Потом раз­бирать­ся в этом хла­ме уже не хочет­ся, и все откры­тые вклад­ки разом зак­рыва­ются.

С опи­сан­ной проб­лемой я начал бороть­ся дав­но, путем проб и оши­бок при­дя наконец к исполь­зованию рас­ширения OneTab. Сей­час я покажу, как при­мер­но то же самое пов­торить средс­тва­ми JXA. Заод­но на реаль­ном при­мере мы уви­дим нюан­сы вза­имо­дей­ствия с популяр­ными при­ложе­ниями — Goolge Chrome и TextEdit. Сде­лаем новый скрипт и напишем в него код из лис­тинга 1 (эх, пом­ню, как Никиту Кис­лицына дико бесили эти Иго­ревы «лис­тинги», он даже отдель­но зап­рещал исполь­зование сло­ва «лис­тинг» в жур­нале :). — Прим. ред.).

Листинг 1. Грабим ссылки из вкладок

var googleChrome = Application("Google Chrome");
var textEdit = Application("TextEdit");
var newDoc = textEdit.Document().make();
var content = "";
newDoc.name = "pagesFromBrowser.txt";
for (j = 0; j <= googleChrome.windows.length-1; j++) {
var window = googleChrome.windows[j];
for (var i = 0; i <= window.tabs.length-1; i++) {
content = content + window.tabs[i].url() + " " + window.tabs[i].name() + "\n";
}
textEdit.documents["pagesFromBrowser.txt"].text = content;
}

Этот сце­нарий сох­ранит ссыл­ки на вклад­ки во всех откры­тых окнах бра­узе­ра. Для наг­ляднос­ти адрес сай­та отде­ляет­ся от заголов­ка стра­ницы сим­волом табуля­ции. Теперь нем­ного заос­трим вни­мание на коде.

Пер­вым делом необ­ходимо уста­новить связь с жела­емым при­ложе­нием. В моем слу­чае это Google Chrome и TextEdit. Нам тре­бует­ся соз­дать экзем­пляр объ­екта для даль­нейше­го вза­имо­дей­ствия. Для это­го мы берем и выпол­няем гло­баль­ный метод Application. В качес­тве парамет­ра ему необ­ходимо передать имя при­ложе­ния (ID про­цес­са, путь к при­ложе­нию). Если все прош­ло нор­маль­но, мож­но прис­тупать к работе.

Пос­ле получе­ния экзем­пля­ра объ­екта сле­дует сра­зу открыть биб­лиоте­ку и пос­мотреть дос­тупные свой­ства/методы у выб­ранно­го при­ложе­ния. Я спе­циаль­но выб­рал Google Chrome, так как его опи­сание в биб­лиоте­ке отсутс­тву­ет. Как же быть? Офи­циаль­ные ком­мента­рии мне не попались, поэто­му я рис­кнул и спи­сал наз­вание методов из раз­дела про Safari. Код прек­расно зарабо­тал.

С TextEdit ситу­ация ана­логич­ная: уста­нав­лива­ем связь и соз­даем новый документ. Опи­сание всех методов и свой­ств берем из докумен­тации.

Пос­коль­ку у бра­узе­ра может быть откры­то нес­коль­ко окон и в них зак­ладки, необ­ходимо прой­тись по каж­дому. Для это­го переби­раем кол­лекцию windows, а затем у оче­ред­ного окна про­бега­емся по вклад­кам (tabs). Даль­ше идут стан­дар­тные воз­можнос­ти JS, которые в допол­нитель­ных ком­мента­риях не нуж­дают­ся.

При­веден­ную идею лег­ко раз­вить и дописать код откры­тия ссы­лок из фай­ла. А что, получит­ся очень даже недур­но! Подоб­ная фун­кция ког­да‑то даже была реали­зова­на в пав­шем смертью храб­рых (я про его ори­гиналь­ный дви­жок) бра­узе­ре Opera. Ну и само собой, сде­лать под­дер­жку раз­ных бра­узе­ров. Сра­зу рас­смот­рим при­мер откры­тия новой вклад­ки в Google Chrome:

window = googleChrome.windows[0];
newTab = googleChrome.Tab ({url: http://iantonov.me”})
window.tabs.push(newTab);
 

Пишем письма под копирку

Те­перь взгля­нем на встро­енный поч­товый кли­ент (mail) с точ­ки зре­ния JXA. Поп­робу­ем под­клю­чить­ся к это­му при­ложе­нию и сфор­мировать новое пись­мо. Этот при­мер любят при­водить все бло­геры, но они огра­ничи­вают­ся соз­дани­ем нового пись­ма с запол­ненной темой и тек­стом. Вро­де бы нич­то не меша­ет рас­ширить при­мер, но тут обя­затель­но (Stack Overflow тому под­твержде­ние) воз­ника­ют труд­ности. Во вто­ром лис­тинге я при­вел пол­ноцен­ный код скрип­та, поз­воля­ющий соз­дать новое пись­мо, добавить нес­коль­ко получа­телей и при­атта­чить про­изволь­ный файл.

Листинг 2. Работаем с mail

myMailApp = Application("Mail");
bodyTxt = "Привет, мен! Это пример отправки письма с помощью JS из OS X.\n\n"
+ "Все предельно просто и понятно.";
newMessage = myMailApp.OutgoingMessage().make();
newMessage.visible = true;
newMessage.content = bodyTxt;
newMessage.subject = "Письмо счастья";
newMessage.visible = true;
newMessage.toRecipients.push(myMailApp.Recipient({address: "a@iantonov.me", name: "Igor Antonov"}));
newMessage.toRecipients.push(myMailApp.Recipient({address: "info@iantonov.me", name: "bot"}));
newMessage.attachments.push(myMailApp.Attachment({ fileName: "/Users/spider_net/Downloads/\[rutracker.org\].t4878348.torrent"}));
myMailApp.outgoingMessages.push(newMessage);
myMailApp.activate();

Здесь мы идем по уже зна­комой тро­пин­ке — уста­нав­лива­ем связь с при­ложе­нием и начина­ем запол­нять его свой­ства. Наз­вания исполь­зуемых свой­ств и методов опи­саны в докумен­тации. Сто­ит лишь обра­тить вни­мание на стиль запол­нения объ­екта с пись­мом.

Результат формирования письма
Ре­зуль­тат фор­мирова­ния пись­ма

По идее, ничего необыч­ного: ини­циали­зиру­ем соот­ветс­тву­ющий объ­ект и запол­няем свой­ства. Одна­ко есть один нюанс, с которым я стол­кнул­ся при пер­вом зна­комс­тве с JXA. Смот­ри — по идее, мы мог­ли бы записать весь код в тра­дици­онном для JS сти­ле:

myMessage = Mail.OutgoingMessage({
subject: subject,
content: “”,
visible: true,
toRecipients: [
myMailApp.Recipient({
address: "a@iantonov.me", name: "Igor Antonov"
}),
]
...
});

Код выг­лядит эле­ган­тнее, син­таксис абсо­лют­но кор­рек­тный… но при­мер пра­виль­но не отра­бота­ет. Новое пись­мо будет соз­дано, но стро­ка с получа­теля­ми и спи­сок атта­чей будут пус­ты. На это сто­ит сра­зу обра­тить вни­мание, потому что такая ситу­ация будет тебя под­жидать еще в нес­коль­ких ситу­ациях.

В при­веден­ном при­мере получа­тель захар­дко­жен, а в реале его дан­ные навер­няка име­ются в адресной кни­ге. Научить код работать с при­ложе­нием «Кон­такты» — дело нес­коль­ких минут:

var contactsApps = Application("Contacts");
var recipientFromContacts = contactsApps.people[Igor Antonov];
var name = recipientFromContacts.name();
var email = recipientFromContacts.emails[0].value();

Код опять же доволь­но логич­ный, толь­ко сле­дует обра­тить вни­мание на получе­ние име­ни и email. Пом­ни: name() — это метод, а не свой­ство. Сле­дова­тель­но, не забыва­ем про скоб­ки, ина­че при­дет­ся дол­го ломать голову над вывали­вающи­мися ошиб­ками.

 

Командуем интерактивно

Воз­можнос­ти авто­мати­зации не огра­ничи­вают­ся написа­нием скрип­тов в тра­дици­онном сти­ле. JXA поз­воля­ет так­же выпол­нять код инте­рак­тивно и сра­зу же видеть резуль­тат дей­ствия каж­дой стро­ки. Про­демонс­три­ровать это поможет ути­лита osascript. Откро­ем тер­минал и запус­тим ее:

$osascript -l JavaScript -i

Пер­вым клю­чом мы выб­рали исполь­зуемый язык прог­рамми­рова­ния (не забыва­ем, все то же самое мож­но сде­лать с помощью AppleScript). Вто­рой ключ ука­зыва­ет на желание работать в инте­рак­тивном режиме.

Вы­пол­нив эту коман­ду, мы получим приг­лашение для вво­да (>>) JavaScript-кода. Поп­робу­ем для при­мера запус­тить бра­узер Google Chrome и открыть в нем нес­коль­ко вкла­док. Вво­дим стро­ку и отправ­ляем ее на выпол­нение нажати­ем кла­виши Enter.

$ osascript -l JavaScript -i
>> Chrome = Application("Google Chrome")
=> Application("Google Chrome")
>> window = Chrome.windows[0]
=> Application("Google Chrome").windows.at(0)
>> newTab = Chrome.Tab({url:"http://xakep.ru"})
=> app.Tab({"url":"http://xakep.ru", "make":[function anonymous]})
>> window.tabs.push(newTab)
=> 28
>> newTab = Chrome.Tab({url:"http://iantonov.me"})
=> app.Tab({"url":"http://iantonov.me", "make":[function anonymous]})
>> window.tabs.push(newTab)
 

Меняем bash на JS

С помощью все той же ути­литы osascript мож­но писать тра­дици­онные кон­соль­ные скрип­ты в сти­ле bash. А что дела­ет типич­ный кон­соль­ный скрипт? Как пра­вило, выпол­няет какой‑то дол­гий про­цесс (типа резер­вно­го копиро­вания). Для любого серь­езно­го скрип­та пот­ребу­ется работа с парамет­рами от поль­зовате­ля. Подоб­ное впол­не реаль­но реали­зовать на JXA. При­мер прос­тей­шей бол­ванки:

function run(argv) {
console.log(JSON.stringify(argv))
}

Для тес­та запус­каем этот сце­нарий из кон­соли и переда­ем ему нес­коль­ко парамет­ров:

> $ osascript cli.js -firstArgument -twoArgument
>> ["-firstArgument","-twoArgument"]

Про­фит оче­виден — добав­ляем обра­бот­ку необ­ходимо­го количес­тва парамет­ров и дела­ем сце­нарий мак­сималь­но гиб­ким.

Кста­ти, если тре­бует­ся выпол­нить неболь­шой код на JavaScript в кон­соли немед­ленно, то необ­ходимос­ти в соз­дании отдель­ного сце­нария нет никакой:

> osascript -l JavaScript -e 'Application("Safari").windows[0].name()'
>> JavaScript для OS X - Google Документы
 

JavaScript vs Objective-C

Бо­гатая при­мера­ми пер­вая часть статьи может соз­дать впе­чат­ление о нес­конча­емой кру­тос­ти JavaScript. Отчасти это дей­стви­тель­но так — работа со сто­ковы­ми при­ложе­ниями прос­та, но ведь на этом JXA не закан­чива­ется.

Пом­нишь, я говорил о воз­можнос­ти исполь­зования натив­ных фрей­мвор­ков? Так вот, это поис­тине мощ­ная фича! «Теперь‑то мож­но не забивать голову неподат­ливым Objective-C и писать пол­ноцен­ные при­ложе­ния на любимом язы­ке» — вот мысль истинно­го фана JS... Стоп, я тоже фанат, но ты не оболь­щайся :). Воз­можность соз­давать при­ложе­ния на JS с исполь­зовани­ем натив­ных биб­лиотек — фича, а не пол­ноцен­ная замена ObjC. Чем глуб­же ты будешь пог­ружать­ся в эту тему, тем боль­ше заметишь огра­ниче­ний.

В поисках alert’a

Пер­вое, что бро­сает­ся в гла­за начина­ющим JXA раз­работ­чикам, — отсутс­твие стан­дар­тных фун­кций вро­де Alert или Promt. В этом нет никакой ошиб­ки, так как все перечис­ленные фун­кции име­ются толь­ко в бра­узе­рах. Для отоб­ражения диало­говых окон в JXA пра­виль­нее поль­зовать­ся пла­гином (includeStandartAdditions) или натив­ными API из биб­лиоте­ки Cocoa. Вот два полез­ных снип­пета (с исполь­зовани­ем пла­гина и Cocoa соот­ветс­твен­но):

// Alert средствами плагина
function alertPlugin(text) {
App = Application.currentApplication();
App.includeStandardAdditions = true;
App.displayAlert(text);
}
// Cocoa alert
ObjC.import('Cocoa')
function alertCocoa(text) {
var alert = $.NSAlert.alloc.init
var window = alert.window
window.level = $.NSStatusWindowLevel
alert.messageText = text
var result = alert.runModal
}

Не сто­ит так­же забывать, что Apple сов­сем недав­но пред­ста­вила новый язык прог­рамми­рова­ния Swift. В бли­жай­шие годы он будет идти по пятам Objective-C и, если экспе­римент удас­тся, под­винет или вов­се вытес­нит его. На фоне это­го пер­спек­тивы JavaScript выг­лядят туман­но.

Все вышес­казан­ное — не офи­циаль­ная информа­ция, а сугубо лич­ные рас­сужде­ния. За годы прак­тики в качес­тве раз­работ­чика мне уда­лось усво­ить одно прос­тое пра­вило: даже самые кру­тые и впе­чат­ляющие надс­трой­ки не смо­гут пол­ноцен­но кон­куриро­вать с натив­ными средс­тва­ми раз­работ­ки.

Мы зна­ем, что есть кру­той HTML5 и орда фрей­мвор­ков/тех­нологий, поз­воля­ющих упа­ковать web-тех­нологию в мобиль­ное при­ложе­ние, но по воз­можнос­тям они всег­да будут усту­пать натив­ным инс­тру­мен­там. Имен­но поэто­му (любая ста­тис­тика под­твер­дит) 99% хитовых при­ложе­ний были соз­даны на Objective-C, а не с помощью вол­шебных надс­тро­ек.

К фиче раз­работ­ки под OS X на JavaScript, на мой взгляд, сто­ит отно­сить­ся точ­но так же. Это прек­расный повод упростить свою работу, не замора­чива­ясь с изу­чени­ем экзо­тич­ного AppleScript, но ни в коем слу­чае не сереб­ряная пуля, избавля­ющая от исполь­зования дру­гих тех­нологий.

Нес­мотря на все мое вор­чание, Objective-C Bridge сущес­тву­ет, а зна­чит, грех им не вос­поль­зовать­ся. Код пол­ноцен­ного при­ложе­ния при­вес­ти не смо­гу — я не эксперт в раз­работ­ке под OS X, поэто­му огра­ничусь соз­дани­ем типич­ного окна с натив­ными эле­мен­тами управле­ния (см. лис­тинг 3).

Листинг 3. По мосту к Objective-C

ObjC.import("Cocoa");
var styleMask = $.NSTitledWindowMask | $.NSClosableWindowMask | $.NSMiniaturizableWindowMask;
var windowHeight = 85;
var windowWidth = 400;
var window = $.NSWindow.alloc.initWithContentRectStyleMaskBackingDefer( $.NSMakeRect(0, 0, windowWidth, windowHeight), styleMask, $.NSBackingStoreBuffered, false );
var newLabel = $.NSTextField.alloc.initWithFrame($.NSMakeRect(25, (windowHeight - 40), 200, 24));
newLabel.stringValue = "Label";
newLabel.drawsBackground = false;
newLabel.editable = false;
newLabel.bezeled = false;
newLabel.selectable = true;
var newEdit = $.NSTextField.alloc.initWithFrame($.NSMakeRect(25, (windowHeight - 60), 205, 24));
newEdit.editable = false;
var button = $.NSButton.alloc.initWithFrame($.NSMakeRect(230, (windowHeight - 62), 150, 25));
button.title = "Пимпа";
button.bezelStyle = $.NSRoundedBezelStyle;
button.buttonType = $.NSMomentaryLightButton;
window.contentView.addSubview(newLabel);
window.contentView.addSubview(newEdit);
window.contentView.addSubview(button); window.title = "Заголовок окна";
window.center;
window.makeKeyAndOrderFront(window);
 

Не все так просто

JXA, безус­ловно, инте­рес­ное решение, но поль­зовать­ся им сто­ит осто­рож­но. С точ­ки зре­ния воз­можнос­ти баналь­ной авто­мати­зации (вза­имо­дей­ствие со сто­ковы­ми при­ложе­ниями) все прос­то шикар­но. JavaScript-раз­работ­чикам раз­вязали руки, и теперь они могут тво­рить мел­кие полез­няшки в сво­ем любимом фор­мате. Что каса­ется прес­ловуто­го мос­та к натив­ным биб­лиоте­кам, то нет проб­лем, поль­зуйся, но не забывай об опи­сан­ных мною чуть выше огра­ниче­ниях. Пла­ниру­ешь серь­езный про­ект — делай его с помощью натив­ного инс­тру­мен­тария.

Оставить мнение