package FlowDB::Task; use Carp qw/croak/; use Moose; extends 'DBIx::Class::Core'; my ( $MANDATORY, $OPTIONAL ) = map { { is_nullable => $_ } } 0, 1; __PACKAGE__->table('task'); __PACKAGE__->add_column( ROWID => { data_type => 'INTEGER' } ); __PACKAGE__->add_columns( user => $MANDATORY, # TODO: Must be set for subtasks? name => $MANDATORY, # dito: subtask steps can do without title => $MANDATORY, main_step => $OPTIONAL, # It's not really, but otherwise we would run into circular # requirement problems: # Because steps (like main_step) have a foreign key constraint # matching our autoincremented primary key, the task record # must be inserted first. # By deferring the step-side foreign key check, we would have # to guess our primary key, but doing so smells of pitfalls. # So we have to ensure main_step is properly dealt with in our # insert and update wrappers. # ... from_date, priority, and fields related to archival storage # and repetition. ); __PACKAGE__->belongs_to( user_row => 'FlowDB::User', { 'foreign.id' => 'self.user' } ); __PACKAGE__->has_many( steps => 'FlowDB::Step', { 'foreign.task' => 'self.ROWID' }, { cascade_copy => 1, cascade_delete => 1 }, ); __PACKAGE__->has_many( timestages => 'FlowDB::TimeStage', # provides track_id, until_date, ... { 'foreign.task_id' => 'self.ROWID' } ); __PACKAGE__->set_primary_key('ROWID'); { # In order to simplify the usage of a FlowDB::Task, it shall dele- # gate metadata handling to its main_step my %tmp_msr; # private cache used for rows not yet in storage my @proxy_fields = qw(description done checks expenditure_of_time_share substeps); __PACKAGE__->belongs_to( main_step_row => 'FlowDB::Step', { 'foreign.ROWID' => 'self.main_step' }, { proxy => \@proxy_fields, cascade_update => 1 }, ); # As DBIx::Class::Row constructor would wee about unmatched parameters, # we have to store and keep them in our own private cache in order to # flush them not before insertion sub _tmp_main_step { my $args = shift; my $main_step = do { if ( my $row = shift ) { if ( exists $tmp_msr{$row} ) { delete $tmp_msr{$row}; } else { $tmp_msr{$row} = {} } } else { {} } }; for (@proxy_fields) { my $val = delete $args->{$_} // next; $main_step->{$_} = $val; } return $main_step; } around new => sub { my ( $orig, $class ) = ( shift, shift ); my $args = @_ > 1 ? {@_} : shift; my $main_step = _tmp_main_step($args); my $self = $class->$orig($args); $tmp_msr{$self} = $main_step; return $self; }; # Wrap the proxy accessors to have temporary values accessed # instead, if any. # TODO: Figure out if this applies to rows in storage as well, # i.e. whether or not DBI::Class defers writing changes to # main_step_row until commit of the task row: for my $field (@proxy_fields) { around $field => sub { my ( $orig, $self ) = ( shift, shift ); # if ( $self->in_storage ) { # my $val = shift; $self->$orig( $val // () ) # // $tmp... if ( $self->in_storage ) { $self->$orig(shift); } elsif (@_) { $tmp_msr{$self}{$field} = shift; } else { return $tmp_msr{$self}{$field}; } }; } sub DEMOLISH { my $self = shift; delete $tmp_msr{$self}; } } around copy => sub { # [...] }; around insert => sub { $DB::single = 1; my ( $orig, $self ) = ( shift, shift ); my $args = @_ > 1 ? {@_} : shift; my $main_step = _tmp_main_step( $args => $self ); $main_step->{name} = q{}; $self->result_source->storage->txn_do( sub { $self->$orig($args); $main_step = $self->add_to_steps($main_step); $self->update( { main_step_row => $main_step } ); } ); return $self; }; around update => sub { $DB::single = 1; my ( $orig, $self ) = ( shift, shift ); my $args = @_ > 1 ? {@_} : shift; my $main_step = _tmp_main_step( $args => $self ); $main_step->{name} = q{}; $self->$orig($args); $self->main_step_row->$orig($main_step); return $self; }; package FlowDB::Step; use Moose; extends 'DBIx::Class::Core'; __PACKAGE__->table('step'); my @INTEGER = ( data_type => 'INTEGER' ); my @NULLABLE = ( is_nullable => 1 ); __PACKAGE__->add_columns( ROWID => {@INTEGER}, task => {}, parent => {@NULLABLE}, name => { default_value => '' }, description => {@NULLABLE}, link => {@NULLABLE}, pos => { @NULLABLE, @INTEGER }, done => { @INTEGER, default_value => 0 }, checks => { @INTEGER, default_value => 1 }, expenditure_of_time_share => { @INTEGER, default_value => 1 } ); __PACKAGE__->set_primary_key("ROWID"); __PACKAGE__->belongs_to( parent_row => 'FlowDB::Step', { 'foreign.ROWID' => 'self.parent' } ); __PACKAGE__->belongs_to( task_row => 'FlowDB::Task', { 'foreign.ROWID' => 'self.task' } ); # TODO? Assert $step->ROWID == $step->subtask_row->main_step->ROWID __PACKAGE__->belongs_to( subtask_row => 'FlowDB::Task', { 'foreign.main_step' => 'self.ROWID' }, { proxy => [qw/timestages from_date client/] , # not: priority -> s. definition below is_foreign_key_constraint => 0, } ); __PACKAGE__->belongs_to( link_row => 'FlowDB::Step', { 'foreign.ROWID' => 'self.link', } ); __PACKAGE__->has_many( substeps => 'FlowDB::Step', { 'foreign.parent' => 'self.ROWID', 'foreign.task' => 'self.task' }, { cascade_copy => 0 } ); # [...] __END__