package DBIx::DataModel::Compatibility::V1; use strict; use warnings; no strict 'refs'; no warnings 'once'; use DBIx::DataModel::ConnectedSource; use DBIx::DataModel::Meta; use DBIx::DataModel::Meta::Schema; use DBIx::DataModel::Meta::Source; use DBIx::DataModel::Meta::Utils; use DBIx::DataModel::Schema; use DBIx::DataModel::Source; use DBIx::DataModel::Source::Table; use DBIx::DataModel::Statement; use DBIx::DataModel::Statement::JDBC; use SQL::Abstract::More; my $tmp; # used for various renaming loops # utility fonction for replacing 'camelCase' keys in hashs by 'camel_case' sub _rename_camelCase_keys { my $hashref = shift; foreach my $key (keys %$hashref) { my $new_key = $key; $new_key =~ s/([a-z])([A-Z])/$1_\L$2\E/g and $hashref->{$new_key} = delete $hashref->{$key}; } # an exception for -postSQL $tmp = delete $hashref->{-post_sQL} and $hashref->{-post_SQL} = $tmp; } #---------------------------------------------------------------------- package DBIx::DataModel; #---------------------------------------------------------------------- use strict; use warnings; no warnings 'redefine'; my $orig_Schema = \&Schema; *Schema = sub { my ($class, $schema_class_name, %args) = @_; # convert args received as camelCase DBIx::DataModel::Compatibility::V1::_rename_camelCase_keys(\%args); # extract args that should go to DBIDM::Schema and not DBIDM::Meta::Schema my %singleton_args; foreach my $key (qw/dbh debug dbi_prepare_method sql_abstract sql_dialect/) { $tmp = delete $args{$key} and $singleton_args{$key} = $tmp; } # view_parent is now join_parent (not 100% correct, but the best we can do) if (my $vp = delete $args{view_parent}) { $args{join_parent} ||= []; $args{join_parent} = [$args{join_parent}] unless ref $args{join_parent}; push @{$args{join_parent}}, @$vp; } # create the Meta::Schema my $schema_class = $class->$orig_Schema($schema_class_name, %args); # also create a Schema singleton, if needed if (%singleton_args) { # recuperate existing SQLA instance, if any my %sqlam_args; if (my $sqla = delete $singleton_args{sql_abstract}) { # create a fake SQLA object in order to know how many builtin ops it has my $fake_sqla = SQL::Abstract->new; # surgery: remove builtin ops from our $sqla object for my $op_name (qw/special_ops unary_ops/) { my $n_builtin_ops = @{$fake_sqla->{$op_name}}; splice @{$sqla->{$op_name}}, -$n_builtin_ops; } # now inject the remaining stuff in $sqla as argument for a SQLAM object %sqlam_args = %$sqla if $sqla; } # sql_dialect, previously passed to Schema, is now passed to SQLAM if (my $dialect = delete $singleton_args{sql_dialect}) { if (ref $dialect) { DBIx::DataModel::Compatibility::V1::_rename_camelCase_keys($dialect); $sqlam_args{$_} = $dialect->{$_} foreach keys %$dialect; } else { $dialect =~ s/^MySQL/MySQL_old/; $sqlam_args{sql_dialect} = $dialect; } } # create a new SQLAM instance $singleton_args{sql_abstract} = SQL::Abstract::More->new(%sqlam_args); # create the singleton my $singleton = $schema_class->singleton(%singleton_args); } return $schema_class; }; #---------------------------------------------------------------------- package DBIx::DataModel::ConnectedSource; #---------------------------------------------------------------------- use strict; use warnings; sub ColumnType { my ($self, $typeName, @args) = @_; $self->{meta_source}->define_column_type($typeName, @args); } sub AutoExpand { my ($self, @roles) = @_; $self->{meta_source}->define_auto_expand(@roles); } #---------------------------------------------------------------------- package DBIx::DataModel::Meta::Schema; #---------------------------------------------------------------------- use strict; use warnings; no warnings 'redefine'; my $orig_Type = \&Type; *Type = *ColumnType = sub { my ($self, $type_name, %handlers) = @_; my $tmp; $tmp = delete $handlers{fromDB} and $handlers{from_DB} = $tmp; $tmp = delete $handlers{toDB} and $handlers{to_DB} = $tmp; $self->$orig_Type($type_name, %handlers); }; my $orig_new = \&new; *new = sub { my ($class, %options) = @_; $class->$orig_new(sql_no_inner_after_left_join => 1, %options); }; sub tables { # return classname instead of metadm instance my $self = shift; return map {$_->class} values %{$self->{table}}; } sub views { my $self = shift; return map {$_->class} values %{$self->{table}}; } #---------------------------------------------------------------------- package DBIx::DataModel::Schema; #---------------------------------------------------------------------- use strict; use warnings; no warnings 'redefine'; use Carp; *_createPackage = \&DBIx::DataModel::Meta::Utils::define_class; *doTransaction = \&do_transaction; sub _defineMethod { my ($class, $target, $method_name, $body, $is_silent) = @_; my %args = ( class => $target, name => $method_name, body => $body, ); $args{check_override} = 0 if $is_silent; DBIx::DataModel::Meta::Utils->define_method(%args); } sub ColumnType { my $self = shift; $self->metadm->Type(@_); } sub Autoload { # installs or desinstalls an AUTOLOAD my ($class, $toggle) = @_; DBIx::DataModel::Source::Table->Autoload($toggle); } sub autoInsertColumns { my $class = shift; return $class->metadm->auto_insert_columns; } sub autoUpdateColumns { my $class = shift; return $class->metadm->auto_update_columns; } sub noUpdateColumns { my $class = shift; my %no_update_column = $class->metadm->no_update_column; return keys %no_update_column; } sub AutoInsertColumns { my ($class, %handlers) = @_; $class->metadm->{auto_insert_columns} = \%handlers; } sub AutoUpdateColumns { my ($class, %handlers) = @_; $class->metadm->{auto_update_columns} = \%handlers; } sub NoUpdateColumns { my ($class, @columns) = @_; $class->metadm->{no_update_columns} = {map {$_ => 1} @columns}; } sub tables { my $class = shift; $class->metadm->tables; } sub selectImplicitlyFor { my $self = shift; $self->select_implicitly_for(@_); } sub classData { my $class = shift; return $class->singleton; } sub localizeState { my $class = shift; return $class->localize_state; } #---------------------------------------------------------------------- package DBIx::DataModel::Source; #---------------------------------------------------------------------- use strict; use warnings; no warnings 'redefine'; use Carp; *primKey = \&primary_key; sub MethodFromJoin { my $self = shift; $self->metadm->define_navigation_method(@_); } sub createStatement { my $class = shift; carp "->createStatement() is obsolete, use " . "->select(.., -resultAs => 'statement')"; return $class->select(@_, -resultAs => 'statement'); } sub selectImplicitlyFor { my $self = shift; carp "HACK: obsolete method \$source->selectImplicitlyFor() is delegated " . "to \$schema->select_implicitly_for(); the semantics is not exactly " . "identical"; $self->metadm->schema->class->select_implicitly_for(@_); } sub _autoloader { my $self = shift; my $class = ref($self) || $self; my $attribute = our $AUTOLOAD; $attribute =~ s/^.*:://; return if $attribute eq 'DESTROY'; # won't overload that one! return $self->{$attribute} if ref($self) and exists $self->{$attribute}; croak "no $attribute method in $class"; # otherwise } sub Autoload { # installs or desinstalls an AUTOLOAD in $package my ($class, $toggle) = @_; not ref($class) or croak "Autoload is a class method"; defined($toggle) or croak "Autoload : missing toggle value"; no strict 'refs'; if ($toggle) { *{"${class}::AUTOLOAD"} = \&_autoloader; } else { delete ${"${class}::"}{AUTOLOAD}; } } #---------------------------------------------------------------------- package DBIx::DataModel::Source::Table; #---------------------------------------------------------------------- use strict; use warnings; no warnings 'redefine'; sub DefaultColumns { my ($class, $columns) = @_; $class->metadm->default_columns($columns); } sub ColumnType { my ($class, $typeName, @args) = @_; $class->metadm->define_column_type($typeName, @args); } sub ColumnHandlers { my ($class, $columnName, %handlers) = @_; $class->metadm->define_column_handlers($columnName, %handlers); } sub AutoExpand { my ($class, @roles) = @_; $class->metadm->define_auto_expand(@roles); } sub autoInsertColumns { my $self = shift; $self->metadm->auto_insert_column; } sub autoUpdateColumns { my $self = shift; $self->metadm->auto_update_column; } sub noUpdateColumns { my $self = shift; my %no_update_columns = $self->metadm->no_update_column; return keys %no_update_columns; } sub componentRoles { my $self = shift; $self->metadm->components; } sub applyColumnHandler { my $class = shift; $class->apply_column_handler(@_); } sub AutoInsertColumns { my ($class, %handlers) = @_; $class->metadm->{auto_insert_columns} = \%handlers; } sub AutoUpdateColumns { my ($class, %handlers) = @_; $class->metadm->{auto_update_columns} = \%handlers; } sub NoUpdateColumns { my ($class, @columns) = @_; $class->metadm->{no_update_columns} = {map {$_ => 1} @columns}; } sub blessFromDB { my $class = shift; $class->bless_from_DB(@_); } sub db_table { my $class = shift; return $class->metadm->db_from; } #---------------------------------------------------------------------- package DBIx::DataModel::Statement; #---------------------------------------------------------------------- use strict; use warnings; no warnings 'redefine'; use Carp; use Scalar::Util qw/reftype/; my $orig_refine = \&refine; *refine = sub { my $self = shift; # parse named or positional arguments my %args; if ($_[0] and not ref($_[0]) and $_[0] =~ /^-/) { # called with named args %args = @_; } else { # we were called with unnamed args (all optional!), so we try # to guess which is which from their datatypes. no warnings 'uninitialized'; $args{-columns} = shift unless !@_ or reftype $_[0] eq 'HASH' ; $args{-where} = shift unless !@_ or reftype $_[0] eq 'ARRAY'; $args{-orderBy} = shift unless !@_ or reftype $_[0] eq 'HASH' ; croak "too many args for select()" if @_; } # camelCase keys DBIx::DataModel::Compatibility::V1::_rename_camelCase_keys(\%args); # -distinct => \@columns is now -columns => [-distinct => @columns] if (my $distinct = delete $args{-distinct}) { ref $distinct or $distinct = [$distinct]; unshift @$distinct, '-distinct'; $args{-columns} = $distinct; } # various old ways to require -result_as => 'statement' $args{-result_as} =~ s/^(cursor|iter(ator)?)/statement/i if $args{-result_as}; # delegate to the real refine() method $self->$orig_refine(%args); }; *{rowCount} = \&row_count; *{pageCount} = \&page_count; *{gotoPage} = \&goto_page; *{shiftPages} = \&shift_pages; *{nextPage} = \&next_page; *{pageBoundaries} = \&page_boundaries; *{pageRows} = \&page_rows; #---------------------------------------------------------------------- package DBIx::DataModel::Statement::JDBC; #---------------------------------------------------------------------- use strict; use warnings; no warnings 'redefine'; use Carp; *{rowCount} = \&row_count; # simulate previous classes, now moved into the Source:: namespace, so that # they can be inherited from #---------------------------------------------------------------------- package DBIx::DataModel::Table; #---------------------------------------------------------------------- $INC{"DBIx/DataModel/Table.pm"} = 1; our @ISA = qw/DBIx::DataModel::Source::Table/; #---------------------------------------------------------------------- package DBIx::DataModel::View; #---------------------------------------------------------------------- $INC{"DBIx/DataModel/View.pm"} = 1; our @ISA = qw/DBIx::DataModel::Source::Table/; 1; __END__ =head1 NAME DBIx::DataModel::Compatibility::V1 - compatibility with previous versions 1.* =head1 SYNOPSIS Do not C this package directly; use indirectly through use DBIx::DataModel -compatibility => 1.0; =head1 DESCRIPTION Version 2 of C was a major refactoring from versions 1.*, with a number of incompatible changes in the API (classes renamed, arguments renamed or reorganized, etc..). The present package injects a compatibility layer between your application and C : that layer intercepts the calls and modifies the arguments and/or return values so that the API is compatible with prior versions C<1.*>. For the moment, this compatibility layer is automatically activated, in order to automatically preserve backwards compatibility. Some time in the future, the compatibility layer will be deprecated, and will need to be explicitly required, as shown above in the synopsis. If you want to explicitly disable the compatibility layer, write use DBIx::DataModel -compatibility => undef; =head1 SEE ALSO L =cut