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 });
    });



Saturday, July 14, 2012

Вождение МКПП: double clutching

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

Tuesday, June 19, 2012

Бэкап FreeBSD-слайса на удаленную машину

Понадобилось слить данные с ноутбука с установленной FreeBSD: два слайса, примонтированные в /var и /usr. Рабочее решение:


  1. Загружаемся с линуксового Live-CD. В моем случае на ноутбуке уже был установлен Linux в одном из разделов, поэтому я загрузил его.

  2. Находим файлы устройств для нужных слайсов, например пробным монтированием. В моем случае это были /dev/sda9 и /dev/sda11.

  3. Дампим слайс с помощью dd и копируем на удаленную машину через ssh:

    dd if=/dev/sda9 |
    ssh -c blowfish user@host "dd of=var.img"

  4. В линуксе примонтировать полученный файл можно следующей командой:


    mount -r -t ufs -o loop,ufstype=ufs2 var.img /mnt

Sunday, June 10, 2012

svn: как удалить N последних ревизий из репозитория

Иногда возникает необходимость полностью выпилить 1 или более последних ревизий из svn-репозитория. Если вы админ svn-сервера, это делается очень просто:

# mv repo repo.bak
# svnadmin create repo
# svnadmin dump -r 0:9999 repo.bak | svnadmin load repo


Клиентам, которые уже скачали себе эти ревизии, необходимо заново сделать checkout.

Если же доступа непосредственно к файлам репозитория нет, единственный вариант - это откатить сделанные изменения, создав еще один коммит. Это можно сделать с помощью команды svn merge с указанием ревизий в обратном порядке, e.g.

$ svn merge --revision 303:302 ^/calc/trunk
$ svn commit -m "Undoing change committed in r303."

Wednesday, February 8, 2012

Perl: как из обычного скрипта сделать модуль

Если вы используете в процессе разработки TDD, то наверняка знаете, как непросто бывает тестировать standalone-скрипты. В общем случае, скрипту можно передать какие то параметры и сравнить вывод с ожидаемым.

Гораздо легче тестировать модули. Код разбивается на небольшие функции, каждая из которых выполняет одну задачу. Если функции не имеют побочных эффектов, можно передать им некие аргументы и проверить возвращаемые значения.

Допустим наш скрипт содержит какие-то функции, которые необходимо протестировать. Как сделать так, чтобы скрипт можно было подключить в виде модуля, не исполняя?

Для этого достаточно обернуть основной код скрипта в функцию, и не вызывать эту функцию в том случае, если в стеке вызовов что-либо есть - это означает, что скрипт подгружен с помощью require:
main() unless caller();

sub main {
sub1();
sub2();
...
}


Функциональность скрипта абсолютно не пострадала, зато теперь его можно подключить с помощью require и полноценно протестировать.

Кстати, подобный подход встречается и в Python:
def main():
# the main code goes here

if __name__ == "__main__":
main()