Some cleanups around loading/use of DBIx::Class::Exception (no func. changes)
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / ResultSourceHandle.pm
1 package DBIx::Class::ResultSourceHandle;
2
3 use strict;
4 use warnings;
5
6 use base qw/DBIx::Class/;
7
8 use Try::Tiny;
9 use namespace::clean;
10
11 use overload
12     q/""/ => sub { __PACKAGE__ . ":" . shift->source_moniker; },
13     fallback => 1;
14
15 __PACKAGE__->mk_group_accessors('simple' => qw/schema source_moniker _detached_source/);
16
17 # Schema to use when thawing.
18 our $thaw_schema;
19
20 =head1 NAME
21
22 DBIx::Class::ResultSourceHandle - Serializable pointers to ResultSource instances
23
24 =head1 DESCRIPTION
25
26 Currently instances of this class are used to allow proper serialization of
27 L<ResultSources|DBIx::Class::ResultSource> (which may contain unserializable
28 elements like C<CODE> references).
29
30 Originally this module was used to remove the fixed link between
31 L<Rows|DBIx::Class::Row>/L<ResultSets|DBIx::Class::ResultSet> and the actual
32 L<result source objects|DBIx::Class::ResultSource> in order to obviate the need
33 of keeping a L<schema instance|DBIx::Class::Schema> constantly in scope, while
34 at the same time avoiding leaks due to circular dependencies. This is however
35 no longer needed after introduction of a proper mutual-assured-destruction
36 contract between a C<Schema> instance and its C<ResultSource> registrants.
37
38 =head1 METHODS
39
40 =head2 new
41
42 =cut
43
44 sub new {
45   my ($class, $args) = @_;
46   my $self = bless $args, ref $class || $class;
47
48   unless( ($self->{schema} || $self->{_detached_source}) && $self->{source_moniker} ) {
49     my $err = 'Expecting a schema instance and a source moniker';
50     $self->{schema}
51       ? $self->{schema}->throw_exception($err)
52       : DBIx::Class::Exception->throw($err)
53   }
54
55   $self;
56 }
57
58 =head2 resolve
59
60 Resolve the moniker into the actual ResultSource object
61
62 =cut
63
64 sub resolve {
65   return $_[0]->{schema}->source($_[0]->source_moniker) if $_[0]->{schema};
66
67   $_[0]->_detached_source || DBIx::Class::Exception->throw( sprintf (
68     # vague error message as this is never supposed to happen
69     "Unable to resolve moniker '%s' - please contact the dev team at %s",
70     $_[0]->source_moniker,
71     'http://search.cpan.org/dist/DBIx-Class/lib/DBIx/Class.pm#GETTING_HELP/SUPPORT',
72   ), 'full_stacktrace');
73 }
74
75 =head2 STORABLE_freeze
76
77 Freezes a handle.
78
79 =cut
80
81 sub STORABLE_freeze {
82   my ($self, $cloning) = @_;
83
84   my $to_serialize = { %$self };
85
86   delete $to_serialize->{schema};
87   delete $to_serialize->{_detached_source};
88   $to_serialize->{_frozen_from_class} = $self->{schema}
89     ? $self->{schema}->class($self->source_moniker)
90     : $self->{_detached_source}->result_class
91   ;
92
93   Storable::nfreeze($to_serialize);
94 }
95
96 =head2 STORABLE_thaw
97
98 Thaws frozen handle. Resets the internal schema reference to the package
99 variable C<$thaw_schema>. The recommended way of setting this is to use
100 C<< $schema->thaw($ice) >> which handles this for you.
101
102 =cut
103
104 sub STORABLE_thaw {
105   my ($self, $cloning, $ice) = @_;
106   %$self = %{ Storable::thaw($ice) };
107
108   my $from_class = delete $self->{_frozen_from_class};
109
110   if( $thaw_schema ) {
111     $self->schema( $thaw_schema );
112   }
113   elsif( my $rs = $from_class->result_source_instance ) {
114     # in the off-chance we are using CDBI-compat and have leaked $schema already
115     if( my $s = try { $rs->schema } ) {
116       $self->schema( $s );
117     }
118     else {
119       $rs->source_name( $self->source_moniker );
120       $rs->{_detached_thaw} = 1;
121       $self->_detached_source( $rs );
122     }
123   }
124   else {
125     DBIx::Class::Exception->throw(
126       "Thaw failed - original result class '$from_class' does not exist on this system"
127     );
128   }
129 }
130
131 =head1 AUTHOR
132
133 Ash Berlin C<< <ash@cpan.org> >>
134
135 =cut
136
137 1;