[Moscow.pm] Mouse хочу странного

Ilya Chesnokov chesnokov.ilya на gmail.com
Чт Окт 17 03:35:48 PDT 2013


17 октября 2013 г., 2:25 пользователь Ivan Petrov
<i.petro.77.00 на gmail.com>написал:

> есть допустим объект
>
>
> package User;
> use Mouse;
> has name => is => 'ro', isa => 'Str';
>
> далее роль у него составляющая некие списки юзеров
>
> with 'User::List';
>
> ...
>
> package User::List;
> use Mouse::Role;
>
> sub list {
>     ... возвратит список пользователей
> }
>
> sub list_by_some {
>     ... возвратит еще какой-то список
> }
>
> ну понятно
>
> далее у юзеров могут быть допустим  заказы. всю работу с заказами
> выносим в отдельную роль (тут в примере работа со списками заказов)
>
>
> package User::Order::List;
> use Mouse::Role;
>
> sub list_orders {
>     ... возвратит список заказов пользователя
> }
>
> или игрушки
>
> package User::Toy::List;
> use Mouse::Role;
>
> sub list_toys {
>     ... возвратит игрушки пользователя
> }
>
> Ну вот, в большом проекте накапливается у некоторых сущностей много
> разных ролей (или иерархия наследования, тут по разному можно
> смотреть, это непринципиально).
>
> и хочется писать далее не
>
> $user->list_orders;
> $user->list_toys;
>
> а организовать нечто вроде неймспейсов ролевых.
>
> то есть я хочу чтобы можно было написать
>
> ns 'User::Order::List' => as => 'orders';
> ns 'User::Toy::List' => as => 'toys';
>
> в основном классе и далее чтобы можно было обращаться к его методам
> так:
>
> $user->orders->list;
> $user->toys->list;
>
> то есть очень похоже на контейнер, но это не контейнер.
> это неймспейс.
> функция $user->orders->list в качестве self должна получать объект $user.
>
> то есть
>
> has things => is => 'ro', isa => 'User::Thing::List';
>
> тут не подходит. можно прослойку сделать на базе has конечно, но я
> боюсь сильно просесть на бенчмарках.
>
> что еще хочется:
> - has внутри namespace
> - в идеале наследования какого-то всей этой "хрени с которой
>   взлетать", но в целом наследование мы почти не применяем в Mouse,
>   поэтому не сильно актуально
>
>
> я примерно представляю как такое написать, но
>
> 1. может быть кто-то писал уже подобное? дайте ссылку
> 2. сейчас сильно занят другими проектами, но если бы кто взялся мог бы
> профинансировать появление подобного модуля на cpan :) с
> документацией, тестами и сравнительными бенчмарками
> (сравниваем с Mouse::Role). Пусть бенчмаркает вдвое хуже метода
> Mouse::Role, но не хуже чем вдвое. Я думаю можно сделать чтобы
> бенчмаркало соизмеримо.
>
> кто что думает?
>
>
Действительно странного хотите.

По-моему более очевидно для каждой сущности (игрушка, заказ, пользователь)
сделать отдельный класс (не роль), в котором прописать, в какой таблице
хранятся записи для этого класса, а в роль вынести как раз работу с БД.
Примерно так мы делаем в нашем проекте на работе. При этом никто не мешает
добавить в класс User метод orders(), который будет возвращать список
заказов данного пользователя. И, что важно, сможет сделать обратное -
например, добавить в класс Toy метод users(), который выдаст юзеров,
пользующихся данной игрушкой (а как вы это будете делать с неймспейсами -
непонятно).

Из $self, который вы хотите получать в качестве параметра, вам нужно только
значение первичного ключа, который вы добавите в поисковый запрос.

Кусок кода, который иллюстрирует вышесказанное, приведен ниже (я в нём
использую более привычный мне Moose, но код на Mouse должен выглядеть
похоже, если не идентично).

package Role::DatabaseObject;

use Moose::Role;
requires 'table';

sub list {
    my $self = shift;
    return $self->_list_table($self->table, @_);
}

sub _list_table {
    my ($self, $table, @args) = @_;

    # свой код для выборки из $table с поиском по параметрам @args
}

sub list_related {
    my ($self, $related_table, @args) = @_;

    # Это если внешний ключ связанной таблицы имеет вид $foreign_table .
'_id',
    # иначе делайте свой маппинг.
    my $key_name  = $self->table . '_id';
    my $key_value = $self->get_column_value($foreign_key_name);

    return $self->_list_table(
        $related_table,
        @args,
        $foreign_key_name => $foreign_key_value
    );
}

# Это можно добавить в BUILD, а можно добавлять в каждый класс вручную - в
зависимости
# от того, как будет удобнее и что фантазия позволит
sub add_relations {
    my ($class, %class_mapping) = @_;
    while (my ($relation, $related_class) = each %class_mapping) {
        $class->meta->add_method(
            $relation => sub {
                my ($self) = @_;

                require $related_class;
                return $self->list_related($related_class->table),;
            }
        );
    }
}

package User;
use Moose;
with 'Role::DatabaseObject';
sub table { 'user' }

__PACKAGE__->add_relations(
    orders => 'Order',
    toys   => 'Toy',
);

package Order;
use Moose;
with 'Role::DatabaseObject';
sub table { 'order' }

# здесь логика работы с заказами

1;

package Toy;
use Moose;
with 'Role::DatabaseObject';
sub table { 'toy' }

__PACKAGE__->add_relations(users => 'User');

# здесь логика работы с игрушками

1;

-- 
Best regards,
Ilya Chesnokov
----------- следущая часть -----------
Вложение в формате HTML было извлечено…
URL: <http://mail.pm.org/pipermail/moscow-pm/attachments/20131017/634c53d4/attachment-0001.html>


Подробная информация о списке рассылки Moscow-pm