- PVSM.RU - https://www.pvsm.ru -

Пятничный JS: reqyire.js и очепятко-ориентированное программирование

И вновь я приветствую уважаемых читателей в своей не то чтобы постоянной, но повторяющейся рубрике. Сегодня мы с вами поговорим о том, как стать более эффективным программистом под Node.js. А также, как вы могли догадаться из названия, об опечатках и их роли в этом процессе. Немного кода для привлечения внимания

const reqyire = require("reqyire");
const http = reqyire("htpp");

const server = http.creteServer((req, res) => {
  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/plain');
  res.end('Hello Worldn');
});

server.listem(3000, "127.0.0.1");


Пожалуй, не будет преувеличением сказать, что опечатки — бич и чума программирования. Огромное количество ресурсов уходит на борьбу с ними. Шерстят код хитроумные статические анализаторы, тратя бесценные секунды рабочего времени; автодополнение IDE, нещадно сжирая такты процессора, подсказывает правильные, по его мнению, варианты. Компилятор отказывается компилировать, а интерпретатор — интерпретировать код, где пропущена или заменена какая-нибудь жалкая буква. Даже ежу понятно, что «creteServer» — это «createServer» с пропущенной буквой «a». Но движок V8 — не ёж, и на эту безобидную описку он ругнётся своим беспощадным «undefined is not a function».

И однажды я задумался: возможно, мы что-то делаем не так? Может быть, то, с чем мы боремся как с врагом, способно стать нам союзником и слугой? Результатом этих размышлений стала библиотека reqyire.js, которая, я надеюсь, навсегда изменит мир серверного JS-программирования.

Инструкция по применению

Для начала, разумеется, нужно установить библиотеку:

npm install reqyire

После этого в начале каждого файла, где вы планируете её использовать, напишите:

const reqyire = require("reqyire");

В дальнейшем используйте reqyire вместо require, когда есть опасения, что вы можете сделать опечатку в названии модуля. То есть всегда. Больше вам не придётся видеть бесконечные «Error: cannot find module» - reqyire обязательно найдёт вам модуль. Следующий код:

const reqyire = require("reqyire");
const path = reqyire("pah"),
    fs = reqyire("fds"),
    util = require("./disrt/ytil.js");

— будет работать, и работать так, как надо, а не так, как написано.

Если у вас достаточно свежая версия Node.js (или не совсем свежая, но запускаемая с флагом --harmony-proxies), то чудеса на этом не закончатся. Благодаря магии свежих стандартов JavaScript все объекты, запрошенные через reqyire, также становятся понятливыми и позволяют допускать опечатки в именах своих свойств. Эта понятливость передаётся также дочерним объектам, и даже объектам, возвращаемым функциями:

const path = reqyire("pat"),
    fs = reqyire("fd");
var folder = path.win33.dirmame(".\Works\perfectly\correct\index.js");
fs.stasSync(folder).isDyrectori(); // true

Если же какому-то объекту не выпало счастье быть вызванным через reqyire, но вы всё же хотите добиться от него понятливости, на помощь приходит require.wrap:

var obj = {prop: "The internet is for"};
var wrappedObj = reqyire.wrap(obj);
console.log(obj.porn, "porn"); 

Также сгодится reqyire.wrop, reqyire.warp, да и вообще любое имя метода, хотя бы отдалённо похожее на wrap.

Теперь, когда у вас есть reqyire.js, вы сможете тратить меньше сил на исправление опечаток. Это позволит писать больше строк кода в день и, очевидно, сделает вас более эффективным программистом.

Дисклеймер

Вы ведь заметили табличку «Сарказм» [1]? Разумеется, слова про повышение эффективности, как и сама библиотека в принципе — это не всерьёз. Каждый, кто решит использовать это в продакшоне, будет мёрзнуть в ледяном аду Хельхейма.

Как это работает?

Вряд ли этот вопрос возник у матёрых профессионалов, но менее искушённым читателям ответ может пригодиться. Что ж, я с удовольствием всё объясню, и даже не пытайтесь меня остановить.

Нечёткий поиск

Сердце всей библиотеки — это функция fuzzySearch(arrayOfStrings, string). Она находит в массиве строк ту, между которой и вторым аргументом наименьшее расстояние Левенштейна [2], т.е. для которой требуется наименьшее количество операций добавления, удаления или изменения символа, чтобы превратить одно в другое. Делается это с помощью слегка модифицированного алгоритма Вагнера-Фишера [3]. Вместо того, чтобы честно считать расстояние для каждой пары строк, мы запоминаем наименьшее из уже найденных расстояний и останавливаем алгоритм, если для новой строки расстояние оказывается заведомо больше.

Вообще говоря, расстояние Левенштейна — не самая подходящая метрика. По уму, стоило бы использовать расстояние Дамерау-Левенштейна [4], которое лучше соответствует характеру опечаток, совершаемых человеком. Но, как писал в своей курсовой герой анекдота:"… А электроды я взял деревянные, потому что всё равно никто это читать не будет".

Поиск модулей

Для того, чтобы использовать нечёткий поиск, нужно сначала откуда-то достать массив вариантов. Первым делом мы смотрим на имя запрашиваемого модуля. Согласно спецификации [5] «родной» функции require, если имя начинается с "/", "./" или "../", то это путь к файлу, иначе — имя установленного модуля. Начнём со второго случая, он проще.

Установленные модули бывают трёх типов: локальные, глобальные и встроенные. Список первых и вторых можно получить с помощью консольных команд:

npm ls -json
npm ls -g -json

Для третьей категории, к моему удобству, нашлась подходящая библиотека [6]. Объединив эти списки, кэшируем результат (первые две команды работают достаточно долго) и проходимся по нему нечётким поиском.

Поиск файлов

В случае пути к файлу есть две проблемы, очевидная и менее очевидная. Очевидная проблема: в системе может быть (и, скорее всего, будет) астрономическое количество файлов. Собирать массив расположений их всех, а затем делать по нему нечёткий поиск — очень затратная операция. Пораскинув мозгами, я пришёл к следующему решению: разобьём путь на элементарные фрагменты (последовательность имён директорий и имя файла в конце) и будем нечётко искать по каждому из этих фрагментов, постепенно формируя итоговый путь. Таким образом, reqyire("./js/sctipr.js") найдёт файл "./js/script.js", но не файл "./jssctipr.js".

Менее очевидная (возможно, только для меня) проблема заключалась в относительных путях. Чтобы резольвить абсолютный путь так, как это сделал бы require, reqyire.js должен знать абсолютный путь к скрипту, из которого вызывали reqyire. Штатными средствами JS его узнать невозможно. Я уже собирался сдаться, но, к счастью, нашёлся грязный хак [7], связанный с особенностями движка V8 и позволяющий вытащить путь к вызывавшему скрипту из стектрейса.

Лечебные обёртывания

Как нетрудно догадаться, «понятливость» достигается с помощью ES6 Proxy [8]. Обернув объект в Proxy, можно перехватывать обращения к его свойствам, его вызов в качестве функции, а также ещё много других удивительных вещей, которые, однако, нас сейчас меньше интересуют. Обёртка, написанная мной, производит нечёткий поиск по свойствам объекта, если свойства с точным именем не существует. Если возвращаемый результат является объектом, он упаковывается в ту же обёртку. Так же упаковывается результат вызова функции.

Заключение

image

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

Автор: Вадим Шевяков

Источник [9]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/javascript/263115

Ссылки в тексте:

[1] табличку «Сарказм»: #sarcasm-sign

[2] расстояние Левенштейна: https://ru.wikipedia.org/wiki/%D0%A0%D0%B0%D1%81%D1%81%D1%82%D0%BE%D1%8F%D0%BD%D0%B8%D0%B5_%D0%9B%D0%B5%D0%B2%D0%B5%D0%BD%D1%88%D1%82%D0%B5%D0%B9%D0%BD%D0%B0

[3] алгоритма Вагнера-Фишера: https://ru.wikipedia.org/wiki/%D0%A0%D0%B0%D1%81%D1%81%D1%82%D0%BE%D1%8F%D0%BD%D0%B8%D0%B5_%D0%9B%D0%B5%D0%B2%D0%B5%D0%BD%D1%88%D1%82%D0%B5%D0%B9%D0%BD%D0%B0#.D0.90.D0.BB.D0.B3.D0.BE.D1.80.D0.B8.D1.82.D0.BC_.D0.92.D0.B0.D0.B3.D0.BD.D0.B5.D1.80.D0.B0.C2.A0.E2.80.94_.D0.A4.D0.B8.D1.88.D0.B5.D1.80.D0.B0

[4] расстояние Дамерау-Левенштейна: https://ru.wikipedia.org/wiki/%D0%A0%D0%B0%D1%81%D1%81%D1%82%D0%BE%D1%8F%D0%BD%D0%B8%D0%B5_%D0%94%D0%B0%D0%BC%D0%B5%D1%80%D0%B0%D1%83_%E2%80%94_%D0%9B%D0%B5%D0%B2%D0%B5%D0%BD%D1%88%D1%82%D0%B5%D0%B9%D0%BD%D0%B0

[5] спецификации: https://nodejs.org/api/modules.html#modules_all_together

[6] библиотека: https://github.com/sindresorhus/builtin-modules

[7] грязный хак: https://github.com/felixge/node-stack-trace

[8] ES6 Proxy: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy

[9] Источник: https://habrahabr.ru/post/336906/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best