КатегорияКодинг



Кодим в консоли. Как отказаться от IDE и стать более продуктивным и уверенным в себе


Зачем в XXI веке разработчику нужны навыки программирования в «голой» консоли? Да затем же, зачем солдату навыки рукопашного и штыкового боя. Тому, чья служба состоит из покраски травы и стен, не нужны совсем, а вот коммандос этому все еще учат.

А уходя от лукавых аналогий, можно назвать вот такие причины:

  • Удобство в работе. Да, как ни странно, работать с инструментами терминала оказывается не менее, а иногда и более удобно, чем с супер-IDE, создатели которой решили за тебя, что и как ты должен делать в своем проекте. Не веришь? В далеком 2008 году я стал участником нежданного эксперимента. Мы с моим коллегой занимались тогда портированием на Python и C кода, написанного специалистом-«предметником» в Matlab. Мой коллега писал в Eclipse, я — в Notepad++, запуская в консоли интерпретатор Python или make. Для автоматического тестирования мы оба использовали набор контрольных примеров. Квалификация, работоспособность, рвение обоих программистов были сопоставимы, прибавим к этому еще желание обогнать соперника. В итоге ни один из нас не показал превосходства в скорости разработки или качестве кода. IDE — не серебряная пуля, серебряной пули нет.
  • Мобильность при смене проекта или области деятельности. Это я опять сужу по себе. Три года назад я сделал большой проект на Qt под RHEL. Сегодня занимаюсь Embedded, а инструменты все те же: screen, vim, ctags, coreutils, make.
  • Долгосрочные перспективы. Судьба проекта не должна быть связана с судьбой IDE. Где былое доминирование Delphi или Eclipse? Многие в опенсорсе стараются не зависеть от конкретной IDE, особенно проприетарной.
  • Опенсорсные проекты. Впрочем, кажется, я уже это говорил. Включая (на минуточку!) и AOSP — Android Open Source Project. Кто владеет приемами разработки без IDE — берет и работает. Кто не владеет — строчит на форумы «Мужики, помогите, СРОЧНО ОЧЕНЬ НАДО импортировать AOSP в xyzStudio!11».
  • Работа на слабом железе. Современные IDE «как не в себя» кушают память, процессор, требуют для комфортной работы гигантских размеров монитор, а лучше — «дайте два». Это не проблема, пока ты сидишь в уютном офисе в эргономическом кресле за большим и чистым столом. Это проблема, когда ты работаешь в командировке в тьмутаракани, в кабине внедорожника, или в салоне самолета, или в вагоне электрички с 10-дюймовым субноутбуком на коленях.
  • Удаленная работа над проектом. Да, конечно, тут есть альтернативы: TeamViewer, TigerVNC. Со своими, конечно, проблемами.
  • Психологический аспект. Повышает уверенность в своих силах, «а я, оказывается, не просто мышевод-клаводав, я же настоящий хацкер, итить!».

 

Screen (или Tmux)

Первый инструмент, который мы рассмотрим, — screen. Он играет ту же роль, что оконный менеджер в мире GUI: управляет несколькими приложениями, позволяя переключаться с одного на другое. В консольной вселенной такие инструменты называются терминальными мультиплексорами. Пожалуй, из всех оконных менеджеров ближе всего к screen будет легендарный ratpoison, позволяющий обходиться одной лишь клавиатурой, без мыши.

Управляют screen с помощью хоткеев. Но раз приложений несколько, а клавиатура одна, надо «дать понять» системе, к кому именно мы обращаемся: к самому терминальному мультиплексору или к приложению. Поэтому все команды screen предваряются аккордом (клавиатурным сочетанием) Ctrl^a. Наиболее распространенные команды привязаны к горячим клавишам, для менее частых нужно перейти в командную строку screen командой : (то есть нажать Ctrl^a, а затем :).

Пример: запускаем screen, видим баннер с предложением нажать пробел или ввод для продолжения работы. Воспользуемся любезным предложением — баннер исчезает, мы снова в терминале. Открываем в Vim (про него будет дальше) файл main.cpp.

vim main.cpp 

Мы редактируем файл main.cpp. Ctrl^a, затем Ctrl^c. Мы опять в терминале, а куда пропал Vim? Ничего никуда не пропало, данная комбинация клавиш открывает новый терминал («окно» в терминологии screen). Для возвращения в предыдущий терминал, где открыт Vim, нажимаем Ctrl^a, затем еще раз Ctrl^a.

Не хотелось бы превращать статью в сборник переводов man’ов, поэтому приведу здесь только самые нужные в повседневной практике команды и аккорды (здесь и далее, говоря о screen, начальный аккорд Ctrl^a я для краткости буду опускать):

  • Ctrl^c — открыть новое окно;
  • Ctrl^a — переключиться в предыдущее окно. Аналогичную функцию в мире GUI выполняет клавиатурная комбинация Alt^Tab. Очень удобна, чтобы переключаться туда-сюда между парой окон, например редактором и командной строкой (впрочем, нормальный консольный редактор позволяет выполнять команды командной строки еще проще);
  • x — заблокировать screen (для разблокировки придется ввести пароль);
  • k — закрыть текущее окно (меня эта команда не раз выручала, когда в текущем окне зависало в томной задумчивости подключение к очередной железке по SSH или Telnet);
  • " — открыть список окон, чтобы переключиться в одно из них;
  • ' — ввести имя окна, в которое хочешь переключиться (про то, как задать имя окна, чуть ниже);
  • ? — вывести справку. В общем, мало чем отличается от горячих клавиш оконного менеджера или IDE;
  • Esc — перейти в режим копирования. Я использовал эту штуку исключительно для пролистывания содержимого окна, чтобы посмотреть, а что там было N строк назад. Выход из режима копирования — по нажатию Esc (без предшествующего Ctrl^a);
  • S — (NB! заглавная буква) разделить экран на две половины (два «региона» в терминологии screen). Текущее окно остается в верхнем регионе, в нижнем изначально ничего нет вообще, но, переключившись в этот регион по Tab, можно назначить ему одно из уже открытых окон или открыть новое;
  • Tab — переключение («по кругу») между регионами;
  • Q — закрыть все регионы, кроме текущего. При этом регионы закрываются, но сами окна остаются, и в них можно переключаться;
  • X — закрыть текущий регион.

Screen c экраном, разделенным на два региона

Регион можно еще раз разделить напополам, потом еще, и так до тех пор, пока будет хватать высоты экрана.

Высоту регионов можно регулировать, например нижний урезать до двух строк для ввода, а все остальное отдать для вывода какой-нибудь программы, работающей в режиме мониторинга. Операция эта не настолько частая, чтобы заводить для нее отдельную клавиатурную комбинацию, поэтому для нее используется команда resize, которая вводится в командной строке screen: :resize размер-в-строках.

Можно задать увеличение/уменьшение размера, для этого вводим число со знаком + или -: :resize +10 увеличит размер текущего региона на десять строк, :resize -5 уменьшит размер региона на пять строк.

Если тебе чуть-чуть повезло и у тебя достаточно свежая версия screen, то можно разделить экран по горизонтали клавишей |. На самом деле для современных растянутых по горизонтали мониторов это даже более удобно. Можно комбинировать деление экрана по горизонтали и вертикали, но это, по моему опыту, уже не очень-то нужно.

Screen с экраном, разделенным на три региона

Чуть выше я говорил, что окну можно назначить имя. Делается это командой :title имя. У меня сейчас обычно «живут» окна с именами src (тут открыт Vim с исходным кодом), uart (подключение к подопытному девайсу по последовательному порту), adb (подключение к подопытному девайсу по ADB), build (запуск сборки). При переключении по клавише ' полное имя вводить не нужно, достаточно набрать первые буквы (sr, ua, ad, bu).

Ну и чтобы закончить со screen, упомяну про отключение. Screen завершается, когда закрывается последнее из его окон. Кроме того, можно выйти из screen, оставив все окна работающими в фоне, командой :detach (также на эту команду по умолчанию назначена клавиша d). Позднее к этому сеансу screen можно будет подключиться снова. Обычно такая фича полезна при работе с удаленной машиной.

И совсем напоследок: случается, что мантейнеры дистрибутива или пользователь конкретной системы меняют одну или несколько клавиатурных комбинаций по умолчанию (в моем опыте была чехарда с клавишей X при переходе с RHEL6 на Debian). Смотри конфигурационные файлы screenrc и, конечно, кури доки (специально для РКН: я в переносном смысле).

Источник: xakep.ru


Стоп-спам для «Телеграма». Как написать и задеплоить бота на TypeScript за полчаса


К тебе в уютный и ламповый чат заходит непонятный и никому не известный человек. Ты приветствуешь его, но он угрюмо молчит. Только ты успеваешь мысленно оправдать его поведение, как он подло кидает рекламу очередной мошеннической криптовалютной группы. Знакомо? Сегодня мы закодим и запустим в продакшен собственное решение, которое положит конец гнусным спамерам!

Меня зовут Никита, и я программист: зарабатываю программированием на хлеб насущный и в свободное время пишу разного рода инструменты, которые упрощают жизнь пользователям интернета. Некоторые ты, возможно, используешь. Один из моих самых популярных продуктов — это @voicybot, бесплатный бот для «Телеграма», который просто переводит аудиосообщения в текст. На момент написания статьи им пользуется более полумиллиона чатов, а одних только голосовых сообщений через него проходит более 40 тысяч в сутки.

На самом деле создать чат-бота для Telegram с имеющимися инструментами стало максимально просто, что я и собираюсь продемонстрировать в этой статье. Буквально за полчаса мы с тобой напишем достаточно сложного антиспам-бота на Node.js с использованием TypeScript 3 и хранением записей в MongoDB, а после закинем его на Digital Ocean и настроим быстрый и бесплатный CI на основе простых веб-хуков GitHub. Конечно, можно было бы развернуть все и на «Докере», но, думаю, новичкам в программировании сложнее разбираться с этой магией. Сегодня только олдскул!

 

Настройка окружения

Хоть в статье я и буду использовать мой верный «Мак», на других платформах все примерно так же, за исключением некоторых настроек и процесса установки программ и утилит. Если у тебя возникнут проблемы с установкой, можешь связаться со мной в Telegram — попробую помочь.

В качестве IDE (или текстового редактора, смотря какого лагеря этого холивара придерживаться) я воспользуюсь VSCode. Хранить код буду на GitHub. Скачай и установи себе VSCode, а затем зарегистрируйся на GitHub.

Первым делом стоит установить то, на чем мы будем писать проект, — Node.js. Можно стянуть инсталлятор c официального сайта, но я крайне рекомендую использовать NVM — менеджер версий для Node.js, который позволяет не только установить разные версии, но и переключаться между ними. Установить его можно из официального репозитория следующей командой:

$ curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.11/install.sh | bash 

Советую использовать наиболее актуальную команду из репозитория Readme. Не забудь перезапустить терминал после установки NVM. После стоит установить сам Node.js. У меня одиннадцатая версия, но все последующие, в принципе, должны работать примерно так же. Установим последнюю версию «Ноды»:

$ nvm install node 

Если после выполнения $ which node терминал выдает папку, то все прошло успешно. Вместе с Node.js ставится и менеджер пакетов NPM, который мы заменим на более быстрый yarn. Пропиши следующую команду, чтобы установить его:

$ npm install -g yarn 

Без опций npm и yarn добавляют и устанавливают пакеты в открытую папку проекта. Однако если к npm добавить флаг -g, то пакет установится прямо в систему (на самом деле в пользователя — но углубляться не будем) и будет доступен как отдельное приложение. Таким образом, мы установили yarn отдельным приложением и уже будем использовать его. Время заставить yarn установить TypeScript 3 глобально! Делается это не флагом, а отдельным аргументом при вызове:

$ yarn global add typescript 

Одна команда — и у тебя в системе теперь есть TypeScript. Вся мощь Unix на кончиках пальцев!

Последний кусочек пазла — это база данных MongoDB, которую мы и будем использовать для этого проекта. MongoDB — база данных типа NoSQL, что означает отсутствие очевидных связей между таблицами при помощи связных таблиц (или join tables), но зато дает упрощенную структуру данных в читабельном виде и простейшие миграции. Человеческим языком: эффективных связей между объектами быть не может, но разрабатывать проще.

Проще всего установить MongoDB с официального сайта. Разработчики будут всячески пытаться продать тебе собственное хранилище базы данных (БД) в облаке — не ведись на провокации. Все хранилища БД в облаках дешевые только до первых 10 тысяч пользователей, дальше тебя начнут разорять. Мы поднимем собственное облако для БД с блек-джеком и прочими атрибутами. Также есть и официальный туториал по установке MongoDB на «Мак» через терминал — именно это я и советую сделать. Заодно и homebrew себе поставь, лишним точно не будет.

На этот момент у тебя должны быть установлены:

  • Node.js;
  • Yarn;
  • TypeScript;
  • MongoDB.

Если все установлено и работает, смело продолжай читать туториал. Если что-то не получилось, дальше будет слишком тяжко, лучше поправить сейчас.

 

Тест-драйв

Типичная вещь, с которой нужно начинать писать любой проект. Давай прохеллоуворлдим наш технологический стек (или его часть) до начала реального кодинга — чтобы понять, все ли работает.

Создай в любом месте у себя на компьютере (у меня есть удобная папочка ~/code, где я держу все проекты) папку shieldy_bot и зайди в нее в терминале. В папке проекта пропиши

$ yarn init 

Эта команда задаст тебе ряд вопросов и создаст проект вместе с файлом package.json — это тот самый мастер-файл, на который будет смотреть Node.js при запуске проекта. Обязательно укажи dist/index.js в качестве entry point (точки входа) — чуть позже я расскажу зачем.

Таким образом я настроил свой проект

Создай файл index.js в папке dist в папке проекта. Внутри пропиши лишь одну строку: console.log('Hello world!'). В файл package.json добавь скрипт start вида node dist/index.js.

Примерно так должен выглядеть проект

INFO

Здесь и далее: команды Unix должны выполняться в папке проекта, если не сказано иначе.

Вперед: запусти команду $ yarn start, и ты должен увидеть Hello world! в своей консоли. Если так и произошло — успех, Node.js работает! Теперь проверим TypeScript. Но сначала установим его прямо в проект.

$ yarn add typescript 

После добавления TypeScript прямо в проект будет использоваться именно он, а не тот, что установлен в системе. TypeScript не запускается напрямую «Нодой», он сначала компилируется в JavaScript, а потом компилированный код и запускается при помощи Node.js. В нашем проекте мы будем хранить и писать наш код TypeScript в папке src, а компилироваться и запускаться JavaScript будет в папке dist. Чтобы tsc (тулза — компилятор из TypeScript в JavaScript) работала правильно, ее нужно настроить. Добавь в корень проекта файл tsconfig.json со следующим содержанием:

{ "compilerOptions": { "module": "commonjs", "target": "es2017", "lib": ["es2015"], "moduleResolution": "node", "sourceMap": true, "outDir": "dist", "baseUrl": ".", "paths": { "*": ["node_modules/*", "src/types/*"] } }, "include": ["src/**/*"] } 

Вдаваться в подробности, что это за файл и как он работает, я не буду: это задача вне текущего руководства. Стоит лишь отметить, что мы берем TypeScript-файлы из src, конвертируем в стандарт es2017 и кладем в папочку dist.

После удаляем папку dist — она теперь должна генерироваться сама. Добавляем папку src и кладем туда уже index.ts с содержанием console.log('Жизнь за Харамби') (ts — это расширение файлов TypeScript). В принципе, любой рабочий JavaScript (JS) — это еще и рабочий TypeScript (TS), так как TS — это надстройка над JS.

Также стоит отметить, что TS — это типизированный язык, в отличие от JS. Но так как множество пакетов было изначально написано на JS, сообщество начало дополнять уже существующие проекты информацией о типах в репозитории Definitely Typed, откуда все разработчики заимствуют типы. Так и мы сделаем для «Ноды» — выполним команду $ yarn add @types/node. Если все было правильно, твой проект должен походить на следующий скрин.

Добавили шаг компиляции TS

Теперь перед каждым запуском $ yarn start тебе нужно запускать команду $ tsc, чтобы компилировать код TS в JS. К слову, можно еще и использовать $ tsc -w. Флаг -w заставит tsc перекомпилировать файлы, которые изменяются, при их сохранении. Удобно! Но еще удобнее будет поменять команду start из package.json на tsc && node dist/index.js — тогда каждый раз при запуске $ yarn start будет запускаться и tsc.

Запусти $ yarn start, предварительно добавив или запустив $ tsc вручную. Ты должен увидеть выплюнутую консолью строку «Жизнь за Харамби».

Если все получилось, то поздравляю: ты настроил окружение для разработки и теперь знаешь, что TypeScript работает. Теперь настроим контроль версий!

Источник: xakep.ru


Идеальная форма. Обрабатываем сложные формы на Python с помощью WTForms


Обработка HTML-форм в веб-приложениях — несложная задача. Казалось бы, о чем говорить: набросал форму в шаблоне, создал обработчики на сервере, и готово. Проблемы начинаются, когда форма разрастается: нужно следить за полями, их ID, атрибутами name, корректно маппить атрибуты на бэкенде при генерации и процессинге данных. А если часть формы нужно еще и переиспользовать, то разработка превращается в постоянную рутину: приходится бесконечно копировать атрибуты тегов с клиента на сервер и копипастить однотипный код. Однако есть способы сделать работу с формами удобной.
 

Зачем это нужно?

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

<form action=""> <!-- personal info --> <input type="text" id="f_name" name="f_name" placeholder="John" /> <input type="text" id="l_name" name="l_name" placeholder="Dow" /> <!-- account info --> <input type="email" id="email" name="email" placeholder="john@example.com" /> <input type="password" id="password" name="password" placeholder="**********" /> <!-- meta info --> <select name="gender" id="gender"> <option value="0">Male</option> <option value="1" selected>Female</option> </select> <input type="city" id="city" name="city" placeholder="Saint-Petersburg" /> <textarea name="signature" id="signature" cols="30" rows="10"></textarea> <input type="submit" value="Create user!" /> </form> 

Эта форма выглядит просто. Однако использование в реальном приложении добавит ряд задач.

  • У каждого поля (или в одном блоке) нужно вывести информацию об ошибках, которые могут появиться при валидации формы.
  • Скорее всего, для некоторых полей мы захотим иметь подсказки.
  • Наверняка нам нужно будет повесить по одному или несколько CSS-классов на каждое поле или даже делать это динамически.
  • Часть полей должна содержать предзаполненные данные с бэкенда — предыдущие попытки сабмита формы или данные для выпадающих списков. Частный случай с полем gender прост, однако опции для селекта могут формироваться запросами к БД.
  • И так далее. Все эти доделки раздуют нашу форму как минимум вдвое.

    А теперь посмотрим на то, как мы будем обрабатывать эту форму на сервере. Для каждого поля мы должны сделать следующее.

  • Корректно смаппить его по name.
  • Проверить диапазон допустимых значений — валидировать форму.
  • Если были ошибки, сохранить их, вернув форму для редактирования назад на клиентскую часть.
  • Если все ОK, то смаппить их на объект БД или аналогичную по свойствам структуру для дальнейшего процессинга.
  • Вдобавок при создании пользователя тебе как админу нужно заполнять только часть данных (email и password), остальное пользователь заполнит сам в профиле. В этом случае тебе, скорее всего, придется скопировать шаблон, удалив часть полей, создать идентичный обработчик формы на сервере или вставлять проверки в текущий для различных вариантов формы. Логику валидации полей придется или копировать, или выносить в отдельную функцию. При этом нужно не запутаться в названиях полей, приходящих с клиента, иначе данные просто потеряются.

    Но пользователей нужно не только создавать, но и редактировать, используя ту же самую форму! Причем у админа и юзера эти формы будут разные, с частично пересекающимся набором полей.

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

    Было бы удобнее описать форму в каком-то декларативном формате, например в виде Python-класса, одноразово описав все параметры, классы, валидаторы, обработчики, а заодно предусмотрев возможности ее наследования и расширения. Вот тут-то нам и поможет библиотека WTForms.

    INFO

    Если ты использовал крупные фреймворки типа Django или Rails, ты уже сталкивался со схожей функциональностью в том или ином виде. Однако не для каждой задачи требуется огромный Django. Применять WTForms удобно в паре с легковесными микрофреймворками или в узкоспециализированных приложениях с необходимостью обрабатывать веб-формы, где использование Django неоправданно.

     

    Установка

    Для начала установим саму библиотеку. Я буду показывать примеры на Python 3. Там, где нужен контекст, код исполняется в обработчике фреймворка aiohttp. Сути это не меняет — примеры будут работать с Flask, Sanic или любым другим модулем. В качестве шаблонизатора используется Jinja2. Устанавливаем через pip:

    pip install wtforms 

    Проверяем версию.

    import wtforms wtforms.__version__ # '2.2.1' 

    Попробуем переписать форму выше на WTForms и обработать ее.

     

    Создание формы

    В WTForms есть ряд встроенных классов для описания форм и их полей. Определение формы — это класс, наследуемый от встроенного в библиотеку класса Form. Поля формы описываются атрибутами класса, каждому из которых при создании присваивается инстанс класса поля типа, соответствующего типу поля формы. Звучит сложно, на деле проще.

    from wtforms import Form, StringField, TextAreaField, SelectField, validators class UserForm(Form): first_name = StringField('First name', [validators.Length(min=5, max=30)]) last_name = StringField('Last name', [validators.Length(min=5, max=30)]) email = StringField('Email', [validators.Email()]) password = StringField('Password') # meta gender = SelectField('Gender', coerce=int, choices=[ # cast val as int (0, 'Male'), (1, 'Female'), ]) city = StringField('City') signature = TextAreaField('Your signature', [validators.Length(min=10, max=4096)]) 

    Вот что мы сделали:

    • создали класс UserForm для нашей формы. Он наследован от встроенного FormBaseForm);
    • каждое из полей формы описали атрибутом класса, присвоив объект встроенного в либу класса типа Field.

    В большинстве полей формы мы использовали импортированный класс StringField. Как нетрудно догадаться, поле gender требует ввода другого типа — ограниченного набора значений (м/ж), поэтому мы использовали SelectField. Подпись пользователя тоже лучше принимать не в обычном input, а в textarea, поэтому мы использовали TextAreaField, чье HTML-представление (виджет) — тег <textarea>. Если бы нам нужно было обрабатывать числовое значение, мы бы импортировали встроенный класс IntegerField и описали бы поле им.

    WWW

    В WTForms множество встроенных классов для описания полей, посмотреть все можно здесь. Также можно создать поле кастомного класса.

    О полях нужно знать следующее.

  • Каждое поле может принимать набор аргументов, общий для всех типов полей.
  • Почти каждое поле имеет HTML-представление, так называемый виджет.
  • Для каждого поля можно указать набор валидаторов.
  • Некоторые поля могут принимать дополнительные аргументы. Например, для SelectField можно указать набор возможных значений.
  • Поля можно добавлять к уже существующим формам. И можно модифицировать, изменять значения на лету. Это особенно полезно, когда нужно чуть изменить поведение формы для одного конкретного случая, при этом не создавать новый класс формы.
  • Поля могут провоцировать ошибки валидации по заданным правилам, они будут храниться в form.field.errors.
  • Источник: xakep.ru