Thursday, February 5, 2015

Повышаем интуицию с Python

Наткнулся в сети на интересную задачку:
Предположим, вы являетесь участником "Поля чудес". Выпал приз, Якубович предлагает выбрать один из трех ящиков, в одном из которых деньги. Предположим, вы выбрали первый ящик. Ведущий открывает один из ящиков, пусть третий — он пустой. Ведущий предлагает передумать и выбрать второй ящик. Измените ли вы свое решение?
Предлагаю поразмыслить над ответом, а потом будет немного кода на Python.

Saturday, May 24, 2014

Раскрашиваем bash prompt

Существует множество вариантов кастомизации приглашения bash for fun and profit. Сегодня поговорим об использовании различных цветов в PS[1234].

Friday, July 5, 2013

Perl всё

Ура, я наконец-то перешел на Python в качестве основного языка разработки. Довольно долгое время моим основным орудием был Perl, но в последнее время я все чаще стал посматривать на альтернативы (e.g. Python, Ruby, NodeJS). Немного о том, что мне не нравится в Perl, в произвольном порядке.

  • Perl, пожалуй, худший выбор в качестве первого языка программирования. Этот язык упорот настолько, что поюзав его несколько лет переключиться на что-то вменяемое становится сложно. Чего стоит, например, передача аргументов в функцию в виде массива @_, реализация областей видимости переменных, etc.

    Примерно в этом же ключе отзывался Дейкстра по отношению к Basic:
    "It is practically impossible to teach good programming to students that have had a prior exposure to BASIC: as potential programmers they are mentally mutilated beyond hope of regeneration."
    Справедливости ради стоит отметить, что комьюнити у перла весьма дружелюбное и открытое.

  • Стремление к сходству с естественным языком (на Perl можно даже писать стихи). Звучит прикольно, но на практике лишь раздувает синтаксис и делает код менее читабельным/поддерживаемым.

  • Мантры TMTOWTDI (There’s More Than One Way To Do It) и DWIM (Do What I Mean). Чем больше существует способов "сделать это", тем сложнее язык в изучении и понимании. Попытка заставить интерпретатор строить догадки также ни к чему хорошему не приводит.

  • Отсутствие вменяемого ООП. А без ООП нынче никуда.

  • Отсутствие спроса. Тренды последних лет указывают на то, что новых проектов на Perl5 с каждым годом все меньше. Типичная вакансия Perl-разработчика чуть реже чем всегда подразумевает поддержку древней базы говнокода, переписывать которую на более современных языках слишком долго и дорого.

  • Очень узкая ниша. На мой взгляд – это относительно небольшие (одноразовые) скрипты, позволяющие быстро выполнить задачу. Причем "быстро" как по времени написания кода, так и его выполнения.

  • Perl – единственный язык, в котором имеется goatse operator =)

На этом унылом фоне всё лучше смотрится Python. Он на порядок читабельней, более строгий и полностью объектно ориентированный. Наконец, его гораздо легче набивать вслепую из-за минимума пунктуации ) И еще, я уже давно собирался на него перейти...

Friday, April 5, 2013

Фиксированные имена сетевых интерфейсов в Archlinux

Как известно, в Linux имена сетевых интерфейсов обычно имеют вид "eth0", "eth1" и назначаются ядром при загрузке системы. Поскольку модули соответствующих драйверов загружаются асинхронно, порядок обнаружения интерфейсов не является постоянным. Поэтому, если в системе имеется несколько сетевых интерфейсов, их имена могут периодически меняться при перезагрузке. Это может доставлять проблемы для приложений, использующих имена интерфейсов (e.g. фаерволы, сетевые мониторы).

Начиная с версии v197 systemd/udev автоматически назначает постоянные фиксированные имена для всех Ethernet, WLAN и WWAN интерфейсов. Для имен используются префиксы en, wl и ww соответственно и автоматически сгенерированные идентификаторы. Пример для ethernet-интерфейса: enp1s0.

При обновлении с более ранних версий systemd в Archlinux эта фича остается отключенной, благодаря наличию файла /etc/udev/rules.d/80-net-name-slot.rules:


$ cat /etc/udev/rules.d/80-net-name-slot.rules
# This file masks persistent renaming rules for network devices. If you
# delete this file, /usr/lib/udev/rules.d/80-net-name-slot.rules may
# rename network devices according to ID_NET_NAME_{ONBOARD,SLOT,PATH}
# properties of your network devices, with priority in that order. See
# the output of 'udevadm test-builtin net_id /sys/class/net/$interface'
# for details on what that new name might be.
#
# http://www.freedesktop.org/wiki/Software/systemd/PredictableNetworkInterfaceNames


Чтобы для всех интерфейсов использовалось постоянное сгенерированное имя, достаточно удалить файл /etc/udev/rules.d/80-net-name-slot.rules. Посмотреть, какое имя будет назначено интерфейсу можно командой


udevadm test-builtin net_id /sys/class/net/eth0

Thursday, March 21, 2013

SSH-соединения с использованием промежуточных хостов

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

Предположим нам надо зайти на хост B (недоступный с текущего места) и мы можем зайти на хост A (с которого доступен хост B). В настройках ssh (e.g. ~/.ssh/config) указываем, что соединение с хостом B должно проксироваться через хост A, используя опцию -W:

Host B
HostName myhost
ProxyCommand ssh -q -W %h:%p A


После этого просто используем команду ssh B. Имя myhost должно резолвиться хостом A и указывать на хост B. По аналогии можно добавить произвольное число промежуточных хостов.

Надо заметить, что опция -W появилась в OpenSSH 5.4 (март 2010г). Для более ранних версий можно использовать netcat:

ProxyCommand ssh A nc %h %p


Данная простая настройка делает возможным установку соединения с хостом одной командой, а также открывает доступ к нему приложениям rsync, git, etc., использующим SSH в качестве транспорта.

Saturday, December 22, 2012

OAuth 2.0: введение

В октябре текущего года стандарт OAuth 2.0 наконец-то вышел из статуса "черновика" и превратился в RFC. Все больше сервис-провайдеров начинают поддерживать OAuth в своих API. Что же такое OAuth и зачем он нужен?

Sunday, September 9, 2012

6 полезных приемов для работы с DBIx::Class

DBIx::Class - это мощный ORM, написанный на Perl. Его функцией является прозрачное преобразование результатов запросов к реляционной БД в перловые объекты. Разработчик может использовать эти объекты для взаимодействия с БД, не прибегая к написанию SQL. Это значительно ускоряет разработку и делает код более простым для понимания. К достоинствам DBIx::Class также относятся потрясающая гибкость и интуитивность использования (DWIM). В этой заметке я приведу несколько приемов, которые помогут вам использовать DBIx::Class более эффективно.


  1. Автоматическая генерация классов с помощью DBIx::Class::Schema::Loader.


    Как известно, прежде чем начать работу с DBIx::Class, необходимо предварительно создать несколько классов: класс схемы и Result-классы для нужных таблиц. Этот шаг можно автоматизировать, и поможет в этом утилита dbicdump, которая устанавливается вместе с вышеуказанным модулем. Она берет информацию о таблицах из БД (столбцы, ключи, связи, etc.) и создает нужные классы для всех таблиц.
    dbicdump -o dump_directory=lib 
    MyApp::Schema dbi:Pg:dbname=myapp user pass

    Теперь можно использовать сгенерированные модули в приложении.
    При внесении каких-либо изменений в схему БД, классы необходимо пересоздать аналогичным образом. Для удобства можно создать shell-скрипт или alias, вызывающий dbicdump с нужными параметрами.


  2. Объединение нескольких таблиц в одном запросе с помощью join и prefetch.


    Если в схеме БД созданы вторичные ключи, и вы воспользовались приемом №1, можно использовать атрибуты join и prefetch во многих методах DBIx::Class для объединения нескольких таблиц в одном запросе. join используется когда необходимо выполнить поиск либо сортировку результатов по одному или более столбцу связанной таблицы, e.g.
    my $rs = $schema->resultset('CD')->search(
    { 'artist.name' => 'Rainbow' },
    { join => 'artist' }
    );

    Хотя join и объединяет несколько таблиц, в полученных объектах содержатся данные лишь из одной таблицы - cds. Т.е. если вызвать $cd->artist, будет сделан дополнительный запрос к БД. Для того, чтобы получить все данные из связанных таблиц одним запросом, вместо join следует использовать prefetch. И join, и prefetch могут иметь произвольный уровень вложенности.


  3. Добавление методов в Result/ResultSet-классы.


    Мы уже знаем, что в DBIx::Class для каждой таблицы должен быть задан Result-класс, который представляет строку из данной таблицы. В этот класс можно добавлять дополнительные методы для работы со строками. Например, если в таблице есть столбец email, можно добавить метод gravatar_url:
    sub gravatar_url {
    my ($self) = @_;
    return "http://www.gravatar.com/avatar/".md5_hex(lc $self->email);
    }

    В ResultSet-классы в свою очередь добавляются методы для работы с наборами строк: поиск с заданными критериями, сортировкой, etc. Это позволяет инкапсулировать часть логики в модели.


  4. Использование компонентов для добавления функционала в базовые классы DBIx::Class.


    Функционал любого класса в DBIx::Class (e.g. Schema, Result) можно расширить с помощью компонента:
    __PACKAGE__->load_components(qw(MyComponent));

    При этом MyComponent добавляется в цепочку наследования класса и может добавлять новые методы в класс, либо переопределять уже имеющиеся. Например, компонент DBIx::Class::Validation переопределяет методы insert и update в Result-классе и добавляет в них валидацию значений.


  5. Использование чистого SQL с параметрами.


    В некоторых случаях нужный SQL-запрос в DBIx::CLass можно сгенерировать лишь с использованием кастомного SQL. Данный подход используется в крайнем случае, и, как правило, можно обойтись и без этого. Чтобы включить строку SQL в сгенерированный запрос, она передается как ссылка на скаляр (подробный синтаксис можно посмотреть в perldoc SQL::Abstract). Несколько примеров:
    #  WHERE YEAR(birthday) = ?
    $rs->search(['YEAR(birthday) = ?', [dummy => $year]]);

    # ORDER BY myfunction(mycol, ?, ?) DESC
    $rs->search(undef, {
    order_by => [
    'myfunction(mycol, ?, ?) DESC', map [dummy => $_], $v1, $v2
    ]
    });

    # SET price = price + ?
    $rs->update({
    price => ['price + ?', [dummy => $v]],
    });



  6. Использование транзакций.


    Транзакции позволяют выполнять несколько операций над данными как одно действие. В случае какого-либо сбоя, вся транзакция целиком откатывается. Таким образом обеспечивается целостность данных.

    DBIx::class позволяет выполнить любой код в транзакции с помощью метода txn_do класса схемы:
    $schema->txn_do(sub {
    my $user = $schema->resultset('User')->create({
    username => $username,
    first_name => $first_name,
    last_name => $last_name,
    });
    $schema->resultset('Token')->validate($token_id) or die "Bad token: $token_id";
    $user->add_to_tokens({ token_id => $token_id });
    });