Use weak refs for schema object attributes
[dbsrgits/SQL-Translator.git] / lib / SQL / Translator / Schema / View.pm
1 package SQL::Translator::Schema::View;
2
3 =pod
4
5 =head1 NAME
6
7 SQL::Translator::Schema::View - SQL::Translator view object
8
9 =head1 SYNOPSIS
10
11   use SQL::Translator::Schema::View;
12   my $view   = SQL::Translator::Schema::View->new(
13       name   => 'foo',                      # name, required
14       sql    => 'select id, name from foo', # SQL for view
15       fields => 'id, name',                 # field names in view
16   );
17
18 =head1 DESCRIPTION
19
20 C<SQL::Translator::Schema::View> is the view object.
21
22 =head1 METHODS
23
24 =cut
25
26 use Moo;
27 use SQL::Translator::Utils qw(parse_list_arg ex2err);
28 use SQL::Translator::Types qw(schema_obj);
29 use List::MoreUtils qw(uniq);
30
31 with qw(
32   SQL::Translator::Schema::Role::BuildArgs
33   SQL::Translator::Schema::Role::Extra
34   SQL::Translator::Schema::Role::Error
35   SQL::Translator::Schema::Role::Compare
36 );
37
38 our $VERSION = '1.59';
39
40 =head2 new
41
42 Object constructor.
43
44   my $view = SQL::Translator::Schema::View->new;
45
46 =head2 fields
47
48 Gets and set the fields the constraint is on.  Accepts a string, list or
49 arrayref; returns an array or array reference.  Will unique the field
50 names and keep them in order by the first occurrence of a field name.
51
52   $view->fields('id');
53   $view->fields('id', 'name');
54   $view->fields( 'id, name' );
55   $view->fields( [ 'id', 'name' ] );
56   $view->fields( qw[ id name ] );
57
58   my @fields = $view->fields;
59
60 =cut
61
62 has fields => (
63     is => 'rw',
64     default => sub { [] },
65     coerce => sub { [uniq @{parse_list_arg($_[0])}] },
66 );
67
68 around fields => sub {
69     my $orig   = shift;
70     my $self   = shift;
71     my $fields = parse_list_arg( @_ );
72     $self->$orig($fields) if @$fields;
73
74     return wantarray ? @{ $self->$orig } : $self->$orig;
75 };
76
77 =head2 tables
78
79 Gets and set the tables the SELECT mentions.  Accepts a string, list or
80 arrayref; returns an array or array reference.  Will unique the table
81 names and keep them in order by the first occurrence of a field name.
82
83   $view->tables('foo');
84   $view->tables('foo', 'bar');
85   $view->tables( 'foo, bar' );
86   $view->tables( [ 'foo', 'bar' ] );
87   $view->tables( qw[ foo bar ] );
88
89   my @tables = $view->tables;
90
91 =cut
92
93 has tables => (
94     is => 'rw',
95     default => sub { [] },
96     coerce => sub { [uniq @{parse_list_arg($_[0])}] },
97 );
98
99 around tables => sub {
100     my $orig   = shift;
101     my $self   = shift;
102     my $fields = parse_list_arg( @_ );
103     $self->$orig($fields) if @$fields;
104
105     return wantarray ? @{ $self->$orig } : $self->$orig;
106 };
107
108 =head2 options
109
110 Gets and sets a list of options on the view.
111
112   $view->options('ALGORITHM=UNDEFINED');
113
114   my @options = $view->options;
115
116 =cut
117
118 has options => (
119     is => 'rw',
120     default => sub { [] },
121     coerce => sub { [uniq @{parse_list_arg($_[0])}] },
122 );
123
124 around options => sub {
125     my $orig    = shift;
126     my $self    = shift;
127     my $options = parse_list_arg( @_ );
128
129     if ( @$options ) {
130         $self->$orig([ @{$self->$orig}, @$options ])
131     }
132
133     return wantarray ? @{ $self->$orig } : $self->$orig;
134 };
135
136 sub is_valid {
137
138 =pod
139
140 =head2 is_valid
141
142 Determine whether the view is valid or not.
143
144   my $ok = $view->is_valid;
145
146 =cut
147
148     my $self = shift;
149
150     return $self->error('No name') unless $self->name;
151     return $self->error('No sql')  unless $self->sql;
152
153     return 1;
154 }
155
156 =head2 name
157
158 Get or set the view's name.
159
160   my $name = $view->name('foo');
161
162 =cut
163
164 has name => ( is => 'rw', default => sub { '' } );
165
166 =head2 order
167
168 Get or set the view's order.
169
170   my $order = $view->order(3);
171
172 =cut
173
174 has order => ( is => 'rw', default => sub { 0 } );
175
176 around order => sub {
177     my ( $orig, $self, $arg ) = @_;
178
179     if ( defined $arg && $arg =~ /^\d+$/ ) {
180         return $self->$orig($arg);
181     }
182
183     return $self->$orig;
184 };
185
186 =head2 sql
187
188 Get or set the view's SQL.
189
190   my $sql = $view->sql('select * from foo');
191
192 =cut
193
194 has sql => ( is => 'rw', default => sub { '' } );
195
196 =head2 schema
197
198 Get or set the view's schema object.
199
200   $view->schema( $schema );
201   my $schema = $view->schema;
202
203 =cut
204
205 has schema => ( is => 'rw', isa => schema_obj('Schema'), weak_ref => 1 );
206
207 around schema => \&ex2err;
208
209 =head2 equals
210
211 Determines if this view is the same as another
212
213   my $isIdentical = $view1->equals( $view2 );
214
215 =cut
216
217 around equals => sub {
218     my $orig = shift;
219     my $self = shift;
220     my $other = shift;
221     my $case_insensitive = shift;
222     my $ignore_sql = shift;
223
224     return 0 unless $self->$orig($other);
225     return 0 unless $case_insensitive ? uc($self->name) eq uc($other->name) : $self->name eq $other->name;
226     #return 0 unless $self->is_valid eq $other->is_valid;
227
228     unless ($ignore_sql) {
229         my $selfSql = $self->sql;
230         my $otherSql = $other->sql;
231         # Remove comments
232         $selfSql =~ s/--.*$//mg;
233         $otherSql =~ s/--.*$//mg;
234         # Collapse whitespace to space to avoid whitespace comparison issues
235         $selfSql =~ s/\s+/ /sg;
236         $otherSql =~ s/\s+/ /sg;
237         return 0 unless $selfSql eq $otherSql;
238     }
239
240     my $selfFields = join(":", $self->fields);
241     my $otherFields = join(":", $other->fields);
242     return 0 unless $case_insensitive ? uc($selfFields) eq uc($otherFields) : $selfFields eq $otherFields;
243     return 0 unless $self->_compare_objects(scalar $self->extra, scalar $other->extra);
244     return 1;
245 };
246
247 # Must come after all 'has' declarations
248 around new => \&ex2err;
249
250 1;
251
252 =pod
253
254 =head1 AUTHOR
255
256 Ken Youens-Clark E<lt>kclark@cpan.orgE<gt>.
257
258 =cut