produce_diff_sql(): list context
[dbsrgits/SQL-Translator.git] / lib / SQL / Translator / Diff.pm
index 99de6b7..c291ce4 100644 (file)
@@ -6,16 +6,61 @@ use strict;
 use warnings;
 
 use Data::Dumper;
+use Carp::Clan qw/^SQL::Translator/;
 use SQL::Translator::Schema::Constants;
-
-use base 'Class::Accessor::Fast';
-
-# Input/option accessors
-__PACKAGE__->mk_accessors(qw/
-  ignore_index_names ignore_constraint_names ignore_view_sql
-  ignore_proc_sql output_db source_schema target_schema 
-  case_insensitive no_batch_alters ignore_missing_methods producer_options
-/);
+use Sub::Quote qw(quote_sub);
+use Moo;
+
+has ignore_index_names => (
+  is => 'rw',
+);
+has ignore_constraint_names => (
+  is => 'rw',
+);
+has ignore_view_sql => (
+  is => 'rw',
+);
+has ignore_proc_sql => (
+  is => 'rw',
+);
+has output_db => (
+  is => 'rw',
+);
+has source_schema => (
+  is => 'rw',
+);
+has target_schema => (
+  is => 'rw',
+);
+has case_insensitive => (
+  is => 'rw',
+);
+has no_batch_alters => (
+  is => 'rw',
+);
+has ignore_missing_methods => (
+  is => 'rw',
+);
+has producer_args => (
+  is => 'rw',
+  lazy => 1,
+  default => quote_sub '{}',
+);
+has tables_to_drop => (
+  is => 'rw',
+  lazy => 1,
+  default => quote_sub '[]',
+);
+has tables_to_create => (
+  is => 'rw',
+  lazy => 1,
+  default => quote_sub '[]',
+);
+has table_diff_hash => (
+  is => 'rw',
+  lazy => 1,
+  default => quote_sub '{}',
+);
 
 my @diff_arrays = qw/
   tables_to_drop
@@ -35,8 +80,6 @@ my @diff_hash_keys = qw/
   table_renamed_from
 /;
 
-__PACKAGE__->mk_accessors(@diff_arrays, 'table_diff_hash');
-
 sub schema_diff {
     #  use Data::Dumper;
     ## we are getting instructions on how to turn the source into the target
@@ -58,14 +101,19 @@ sub schema_diff {
     $obj->compute_differences->produce_diff_sql;
 }
 
-sub new {
-  my ($class, $values) = @_;
-  $values->{$_} ||= [] foreach @diff_arrays;
-  $values->{table_diff_hash} = {};
+sub BUILD {
+  my ($self, $args) = @_;
+  if ($args->{producer_options}) {
+    carp 'producer_options is deprecated. Please use producer_args';
+    $self->producer_args({
+      %{$args->{producer_options}},
+      %{$self->producer_args}
+    });
+  }
 
-  $values->{producer_options} ||= {};
-  $values->{output_db} ||= $values->{source_db};
-  return $class->SUPER::new($values);
+  if (! $self->output_db) {
+    $self->output_db($args->{source_db})
+  }
 }
 
 sub compute_differences {
@@ -101,7 +149,7 @@ sub compute_differences {
           $self->table_diff_hash->{$tar_table_name}{table_renamed_from} = [ [$src_table, $tar_table] ];
         } else {
           delete $tar_table->extra->{renamed_from};
-          warn qq#Renamed table can't find old table "$old_name" for renamed table\n#;
+          carp qq#Renamed table can't find old table "$old_name" for renamed table\n#;
         }
       } else {
         $src_table = $source_schema->get_table( $tar_table_name, $self->case_insensitive );
@@ -109,7 +157,7 @@ sub compute_differences {
 
       unless ( $src_table ) {
         ## table is new
-        ## add table(s) later. 
+        ## add table(s) later.
         push @{$self->tables_to_create}, $tar_table;
         next;
       }
@@ -167,9 +215,9 @@ sub produce_diff_sql {
       table_renamed_from    => 'rename_table',
     );
     my @diffs;
-  
-    if (!$self->no_batch_alters && 
-        (my $batch_alter = $producer_class->can('batch_alter_table')) ) 
+
+    if (!$self->no_batch_alters &&
+        (my $batch_alter = $producer_class->can('batch_alter_table')) )
     {
       # Good - Producer supports batch altering of tables.
       foreach my $table ( sort keys %{$self->table_diff_hash} ) {
@@ -179,9 +227,9 @@ sub produce_diff_sql {
         push @diffs, $batch_alter->($tar_table,
           { map {
               $func_map{$_} => $self->table_diff_hash->{$table}{$_}
-            } keys %func_map 
-          }, 
-          $self->producer_options
+            } keys %func_map
+          },
+          $self->producer_args
         );
       }
     } else {
@@ -198,10 +246,9 @@ sub produce_diff_sql {
       push @diffs, map( {
           if (@{ $flattened_diffs{$_} || [] }) {
             my $meth = $producer_class->can($_);
-            
-            $meth ? map { 
-                    my $sql = $meth->( (ref $_ eq 'ARRAY' ? @$_ : $_), $self->producer_options );
-                    $sql ?  ("$sql") : (); 
+
+            $meth ? map {
+                    map { $_ ? "$_" : () } $meth->( (ref $_ eq 'ARRAY' ? @$_ : $_), $self->producer_args );
                   } @{ $flattened_diffs{$_} }
                   : $self->ignore_missing_methods
                   ? "-- $producer_class cant $_"
@@ -221,26 +268,27 @@ sub produce_diff_sql {
     }
 
     if (my @tables = @{ $self->tables_to_create } ) {
-      my $translator = new SQL::Translator(
+      my $translator = SQL::Translator->new(
         producer_type => $self->output_db,
         add_drop_table => 0,
         no_comments => 1,
         # TODO: sort out options
-        %{ $self->producer_options }
+        %{ $self->producer_args }
       );
+      $translator->producer_args->{no_transaction} = 1;
       my $schema = $translator->schema;
 
       $schema->add_table($_) for @tables;
 
-      unshift @diffs, 
+      unshift @diffs,
         # Remove begin/commit here, since we wrap everything in one.
         grep { $_ !~ /^(?:COMMIT|START(?: TRANSACTION)?|BEGIN(?: TRANSACTION)?)/ } $producer_class->can('produce')->($translator);
     }
 
     if (my @tables_to_drop = @{ $self->{tables_to_drop} || []} ) {
       my $meth = $producer_class->can('drop_table');
-      
-      push @diffs, $meth ? ( map { $meth->($_, $self->producer_options) } @tables_to_drop)
+
+      push @diffs, $meth ? ( map { $meth->($_, $self->producer_args) } @tables_to_drop)
                          : $self->ignore_missing_methods
                          ? "-- $producer_class cant drop_table"
                          : die "$producer_class cant drop_table";
@@ -257,8 +305,12 @@ sub produce_diff_sql {
       if ( $self->output_db !~ /^(?:MySQL|SQLite|PostgreSQL)$/ ) {
         unshift(@diffs, "-- Output database @{[$self->output_db]} is untested/unsupported!!!");
       }
-      return join '', map { $_ ? ( $_ =~ /;$/xms ? $_ : "$_;\n\n" ) : "\n" }
-      ("-- Convert schema '$src_name' to '$tar_name':", @diffs);
+
+      my @return =
+        map { $_ ? ( $_ =~ /;\s*\z/xms ? $_ : "$_;\n\n" ) : "\n" }
+        ("-- Convert schema '$src_name' to '$tar_name':", @diffs);
+
+      return wantarray ? @return : join('', @return);
     }
     return undef;
 
@@ -338,7 +390,7 @@ sub diff_table_fields {
     if (my $old_name = $tar_table_field->extra->{renamed_from}) {
       my $src_table_field = $src_table->get_field( $old_name, $self->case_insensitive );
       unless ($src_table_field) {
-        warn qq#Renamed column can't find old column "@{[$src_table->name]}.$old_name" for renamed column\n#;
+        carp qq#Renamed column can't find old column "@{[$src_table->name]}.$old_name" for renamed column\n#;
         delete $tar_table_field->extra->{renamed_from};
       } else {
         push @{$self->table_diff_hash->{$tar_table}{fields_to_rename} }, [ $src_table_field, $tar_table_field ];
@@ -354,12 +406,12 @@ sub diff_table_fields {
       next;
     }
 
-    # field exists, something changed. This is a bit complex. Parsers can 
+    # field exists, something changed. This is a bit complex. Parsers can
     # normalize types, but only some of them do, so compare the normalized and
     # parsed types for each field to each other
     if ( !$tar_table_field->equals($src_table_field, $self->case_insensitive) &&
-         !$tar_table_field->equals($src_table_field->parsed_field, $self->case_insensitive) && 
-         !$tar_table_field->parsed_field->equals($src_table_field, $self->case_insensitive) && 
+         !$tar_table_field->equals($src_table_field->parsed_field, $self->case_insensitive) &&
+         !$tar_table_field->parsed_field->equals($src_table_field, $self->case_insensitive) &&
          !$tar_table_field->parsed_field->equals($src_table_field->parsed_field, $self->case_insensitive) ) {
 
       # Some producers might need src field to diff against
@@ -401,17 +453,24 @@ sub diff_table_options {
     unless $src_table->_compare_objects( \@src_opts, \@tar_opts );
 }
 
+# support producer_options as an alias for producer_args for legacy code.
+sub producer_options {
+  my $self = shift;
+
+  return $self->producer_args( @_ );
+}
+
 1;
 
 __END__
 
 =head1 NAME
 
-SQL::Translator::Diff
+SQL::Translator::Diff - determine differences between two schemas
 
 =head1 DESCRIPTION
 
-Takes two input SQL::Translator::Schemas (or SQL files) and produces ALTER 
+Takes two input SQL::Translator::Schemas (or SQL files) and produces ALTER
 statments to make them the same
 
 =head1 SNYOPSIS
@@ -466,7 +525,7 @@ comment showing the method is missing, rather than dieing with an error
 =head1 PRODUCER FUNCTIONS
 
 The following producer functions should be implemented for completeness. If
-any of them are needed for a given diff, but not found, an error will be 
+any of them are needed for a given diff, but not found, an error will be
 thrown.
 
 =over
@@ -495,9 +554,9 @@ thrown.
 
 =item * C<batch_alter_table($table, $hash)> (optional)
 
-If the producer supports C<batch_alter_table>, it will be called with the 
+If the producer supports C<batch_alter_table>, it will be called with the
 table to alter and a hash, the keys of which will be the method names listed
-above; values will be arrays of fields or constraints to operate on. In the 
+above; values will be arrays of fields or constraints to operate on. In the
 case of the field functions that take two arguments this will appear as a hash.
 
 I.e. the hash might look something like the following:
@@ -519,10 +578,10 @@ Basicaly any changes that need to be made to produce the SQL file for the
 schema should be done here, so that a diff between a parsed SQL file and (say)
 a parsed DBIx::Class::Schema object will be sane.
 
-(As an aside, DBIx::Class, for instance, uses the presence of a 
+(As an aside, DBIx::Class, for instance, uses the presence of a
 C<preprocess_schema> function on the producer to know that it can diff between
 the previous SQL file and its own internal representation. Without this method
-on th producer it will diff the two SQL files which is slower, but known to 
+on th producer it will diff the two SQL files which is slower, but known to
 work better on old-style producers.)
 
 =back