use strict;
use warnings;
-our $VERSION = '0.02';
+our $VERSION = '0.900001_04';
+$VERSION = eval $VERSION; # no errors in dev versions
## On create/insert, add new entry to AuditLog
-# sub new
-# {
-# my ($class, $attrs, @rest) = @_;
+sub _journal_schema {
+ my $self = shift;
+ $self->result_source->schema->_journal_schema;
+}
-# $class->result_source->schema->_journal_schema->current_user(delete $attrs->{user_id});
+sub insert {
+ my ($self, @args) = @_;
-# $class->next::method($attrs, @rest);
-# }
+ return if $self->in_storage;
-sub insert
-{
- my ($self) = @_;
+ my $res = $self->next::method(@args);
- return if($self->in_storage);
- ## create new transaction here?
- my $res = $self->next::method();
- if($self->in_storage)
- {
- my $s_name = $self->result_source->source_name();
- my $al = $self->result_source->schema->_journal_schema->resultset("${s_name}AuditLog");
- $al->update_or_create({
- ( map { $_ => $self->get_column($_)} $self->primary_columns ),
- created => { changeset_id => $al->result_source->schema->current_changeset },
- });
- }
+ $self->journal_log_insert;
return $res;
}
+sub journal_log_insert {
+ my ($self) = @_;
+
+ if ( $self->in_storage ) {
+ my $j = $self->_journal_schema;
+ my $change_id = $j->journal_create_change()->id;
+ $j->journal_update_or_create_log_entry( $self, create_id => $change_id );
+ $j->journal_record_in_history( $self, audit_change_id => $change_id );
+ }
+}
+
## On delete, update delete_id of AuditLog
-sub delete
-{
- my ($self, @rest) = @_;
- $self->next::method(@rest);
-
- if(!$self->in_storage)
- {
- my $s_name = $self->result_source->source_name();
- my $al = $self->result_source->schema->_journal_schema->resultset("${s_name}AuditLog");
- my $alentry = $al->find_or_create({ map { $_ => $self->get_column($_)} $self->primary_columns });
-
- ## bulk_update doesnt do "create new item on update of rel-accessor with hashref, yet
- my $change = $self->result_source->schema->_journal_schema->resultset('ChangeLog')->create({ changeset_id => $self->result_source->schema->_journal_schema->current_changeset });
- $alentry->delete_id($change->id);
- $alentry->update();
+sub delete {
+ my $self = shift;
+ $self->next::method(@_);
+ $self->journal_log_delete(@_);
+}
+
+sub journal_log_delete {
+ my ($self) = @_;
+
+ unless ($self->in_storage) {
+ my $j = $self->_journal_schema;
+ $j->journal_update_or_create_log_entry( $self, delete_id => $j->journal_create_change->id );
}
-
}
## On update, copy previous row's contents to AuditHistory
-sub update
-{
- my ($self, $upd, @rest) = @_;
+sub update {
+ my $self = shift;
+ $self->journal_log_update(@_);
+ $self->next::method(@_);
+}
- if($self->in_storage)
- {
- my $s_name = $self->result_source->source_name();
- my $ah = $self->result_source->schema->_journal_schema->resultset("${s_name}AuditHistory");
+sub journal_log_update {
+ my $self = shift;
- my $obj = $self->result_source->resultset->find( $self->ident_condition );
- $ah->create({
- $obj->get_columns
- });
- }
+ if($self->in_storage) {
+ my $j = $self->_journal_schema;
- $self->next::method($upd, @rest);
+ my $change = $j->journal_create_change;
+ my $prev = $self->result_source->resultset->find( $self->ident_condition );
+ $j->journal_record_in_history( $prev, audit_change_id => $change );
+ }
}
=head1 NAME
=head1 SYNOPSIS
- package My::Schema;
- use base 'DBIx::Class::Schema';
+ package My::Schema;
+ use base 'DBIx::Class::Schema';
- __PACKAGE__->load_components(qw/+DBIx::Class::Schema::Journal/);
+ __PACKAGE__->load_components(qw/Schema::Journal/);
- __PACKAGE__->journal_connection(['dbi:SQLite:t/var/Audit.db']);
- __PACKAGE__->journal_user(['My::Schema::User', {'foreign.userid' => 'self.user_id'}]);
+ __PACKAGE__->journal_connection(['dbi:SQLite:t/var/Audit.db']);
+ __PACKAGE__->journal_user(['My::Schema::User', {'foreign.userid' => 'self.user_id'}]);
- ########
+ #######
- $schema->changeset_user($user->id);
- my $new_artist = $schema->txn_do( sub {
- return = $schema->resultset('Artist')->create({ name => 'Fred' });
- });
+ $schema->changeset_user($user->id);
+ my $new_artist = $schema->txn_do( sub {
+ return $schema->resultset('Artist')->create({ name => 'Fred' });
+ });
=head1 DESCRIPTION
of each row is stored, as well as the previous contents of any row
that gets changed.
-All queries which want auditing should be called using
+All queries which need auditing must be called using
L<DBIx::Class::Schema/txn_do>, which is used to create changesets for
each transaction.
=head2 TABLES
-The journal schema contains a number of tables.
+The journal schema contains a number of tables.
=over
A ChangeSet has_many Changes.
-=item Change
+=item ChangeLog
Each change/operation done in the transaction is recorded as a row in
-the Change table. It contains an auto-incrementing ID, the
+the ChangeLog table. It contains an auto-incrementing ID, the
changeset_id and an order column for the ordering of each change in
the changeset.
For every table in the original database to be audited, an
AuditHistory table is created. Each row has a change_id field
-containing the ID of the Change row. The other fields correspond to
+containing the ID of the ChangeLog row. The other fields correspond to
all the fields from the original table. Each time a column value in
the original table is changed, the entire row contents before the
change are added as a new row in this table.
=over
-=item journal_connection
-
-=item Arguments: \@connect_info
-
-=back
+=item journal_connection \@connect_info
Set the connection information for the database to save your audit
-information to. Leaving this blank assumes you want to store the audit
-data into your current database.
+information to.
-=over
+Leaving this blank assumes you want to store the audit data into your current
+database. The storage object will be shared by the regular schema and the
+journalling schema.
-=item journal_sources
+=item journal_components @components
-=item Arguments: \@source_names
+If you want to add components to your journal
+(L<DBIx::Class::Schema::Versioned> for example) this would be the
-=back
+
+=item journal_sources \@source_names
Set a list of source names you would like to audit, if unset, all
sources are used.
NOTE: Currently only sources with a single-column PK are supported, so
use this method if you have sources with multi-column PKs.
-=over
-
-=item journal_storage_type
-
-=item Arguments: $storage_type
-
-=back
+=item journal_storage_type $type
Enter the special storage type of your journal schema if needed. See
L<DBIx::Class::Storage::DBI> for more information on storage types.
-=over
-
-=item journal_user
-
-=item Arguments: \@relation_args
-
-=back
+=item journal_user \@rel
The user_id column in the L</ChangeSet> will be linked to your user id
with a belongs_to relation, if this is set with the appropriate
arguments.
-=over
+=item journal_deploy_on_connect $bool
-=item changeset_user
+If set to a true value will cause C<journal_schema_deploy> to be called on
+C<connect>.
-=item Arguments: $user_id
+Not reccomended, but present for backwards compatibility.
-=back
+=item changeset_user $user_id
Set the user_id for the following changeset(s). This must be an integer.
-=over
-
-=item changeset_session
-
-=item Arguments: $user_id
-
-=back
+=item changeset_session $session_id
Set the session_id for the following changeset(s). This must be an integer.
-=over
-
-=item txn_do
-
-=iitem Arguments: $code_ref
-
-=back
+=item txn_do $code_ref, @args
Overloaded L<DBIx::Class::Schema/txn_do>, this must be used to start a
new changeset to cover a group of changes. Each subsequent change to
an audited table will use the changeset_id created in the most recent
txn_do call.
+Currently nested C<txn_do> calls cause a single ChangeSet object to be created.
+
+=back
+
=head1 SEE ALSO
L<DBIx::Class> - You'll need it to use this.