__PACKAGE__->load_components(qw/PK::Auto::Pg Core/); # for example
__PACKAGE__->table('foo');
+ # Elsewhere in your code:
my $schema1 = My::Schema->connect(
$dsn,
$user,
=head1 METHODS
-=head2 register_class <moniker> <component_class>
+=head2 register_class
+
+=head3 Arguments: <moniker> <component_class>
Registers a class which isa ResultSourceProxy; equivalent to calling
$self->register_source($moniker => $to_register->result_source_instance);
}
-=head2 register_source <moniker> <result source>
+=head2 register_source
+
+=head3 Arguments: <moniker> <result source>
Registers the result source in the schema with the given moniker
return $self->source($moniker)->resultset;
}
-=head2 load_classes [<classes>, (<class>, <class>), {<namespace> => [<classes>]}]
+=head2 load_classes
+
+=head3 Arguments: [<classes>, (<class>, <class>), {<namespace> => [<classes>]}]
Uses L<Module::Find> to find all classes under the database class' namespace,
or uses the classes you select. Then it loads the component (using L<use>),
}
}
-=head2 compose_connection <target> <@db_info>
+=head2 compose_connection
+
+=head3 Arguments: <target> <@db_info>
This is the most important method in this class. it takes a target namespace,
as well as dbh connection info, and creates a L<DBIx::Class::DB> class as
return $schema;
}
-=head2 setup_connection_class <$target> <@info>
+=head2 setup_connection_class
+
+=head3 Arguments: <$target> <@info>
Sets up a database connection class to inject between the schema
and the subclasses the schema creates.
$target->connection(@info);
}
-=head2 connection(@args)
+=head2 connection
+
+=head3 Arguments: (@args)
Instantiates a new Storage object of type storage_type and passes the
arguments to $storage->connect_info. Sets the connection in-place on
return $self;
}
-=head2 connect(@info)
+=head2 connect
+
+=head3 Arguments: (@info)
Conveneience method, equivalent to $schema->clone->connection(@info)
=cut
-sub connect { shift->clone->connection(@_) };
+sub connect { shift->clone->connection(@_) }
+
+=head2 txn_begin
+
+Begins a transaction (does nothing if AutoCommit is off).
+
+=cut
+
+sub txn_begin { shift->storage->txn_begin }
+
+=head2 txn_commit
+
+Commits the current transaction.
+
+=cut
+
+sub txn_commit { shift->storage->txn_commit }
+
+=head2 txn_rollback
+
+Rolls back the current transaction.
+
+=cut
+
+sub txn_rollback { shift->storage->txn_rollback }
+
+=head2 txn_do
+
+=head3 Arguments: <coderef>, [@coderef_args]
+
+Executes <coderef> with (optional) arguments <@coderef_args> transactionally,
+returning its result (if any). If an exception is caught, a rollback is issued
+and the exception is rethrown. If the rollback fails, (i.e. throws an
+exception) an exception is thrown that includes a "Rollback failed" message.
+
+For example,
+
+ my $foo = $schema->resultset('foo')->find(1);
+
+ my $coderef = sub {
+ my ($foo, @bars) = @_;
+
+ # If any one of these fails, the entire transaction fails
+ $foo->create_related('bars', {
+ col => $_
+ }) foreach (@bars);
+
+ return $foo->bars;
+ };
+
+ my $rs;
+ eval {
+ $rs = $schema->txn_do($coderef, $foo, qw/foo bar baz/);
+ };
+
+ if ($@) {
+ my $error = $@;
+ if ($error =~ /Rollback failed/) {
+ die "something terrible has happened!";
+ } else {
+ deal_with_failed_transaction();
+ die $error;
+ }
+ }
+
+Nested transactions should work as expected (i.e. only the outermost
+transaction will issue a txn_commit on the Schema's storage)
+
+=cut
+
+sub txn_do {
+ my ($self, $coderef, @args) = @_;
+
+ ref $self or $self->throw_exception('Cannot execute txn_do as a '.
+ 'class method');
+
+ my (@return_values, $return_value);
+
+ $self->txn_begin; # If this throws an exception, no rollback is needed
+
+ my $wantarray = wantarray; # Need to save this since it's reset in eval{}
+
+ eval {
+ # Need to differentiate between scalar/list context to allow for returning
+ # a list in scalar context to get the size of the list
+ if ($wantarray) {
+ @return_values = $coderef->(@args);
+ } else {
+ $return_value = $coderef->(@args);
+ }
+ $self->txn_commit;
+ };
+
+ if ($@) {
+ my $error = $@;
+
+ eval {
+ $self->txn_rollback;
+ };
+
+ if ($@) {
+ my $rollback_error = $@;
+ my $exception_class = "DBIx::Class::Storage::NESTED_ROLLBACK_EXCEPTION";
+ $self->throw_exception($error) # propagate nested rollback
+ if $rollback_error =~ /$exception_class/;
+
+ $self->throw_exception("Transaction aborted: $error. Rollback failed: ".
+ $rollback_error);
+ } else {
+ $self->throw_exception($error); # txn failed but rollback succeeded
+ }
+ }
+
+ return $wantarray ? @return_values : $return_value;
+}
=head2 clone
return $clone;
}
-=head2 populate($moniker, \@data);
+=head2 populate
+
+=head3 Arguments: ($moniker, \@data);
Populates the source registered with the given moniker with the supplied data.
@data should be a list of listrefs, the first containing column names, the
second matching values - i.e.
-$schema->populate('Foo', [
- [ qw/foo_id foo_string/ ],
- [ 1, 'One' ],
- [ 2, 'Two' ],
- ...
-]);
+ $schema->populate('Foo', [
+ [ qw/foo_id foo_string/ ],
+ [ 1, 'One' ],
+ [ 2, 'Two' ],
+ ...
+ ]);
=cut