Bigtop::Backend::Model::GantryDBIxClass - Bigtop backend generating DBIx::Class models


Bigtop documentation Contained in the Bigtop distribution.

Index


Code Index:

NAME

Top

Bigtop::Backend::Model::GantryDBIxClass - Bigtop backend generating DBIx::Class models

SYNOPSIS

Top

If your bigtop file looks like this:

    config {
        base_dir `/home/user`;
        ...
        Model GantryDBIxClass {}
    }
    app Name {...}

and there are tables in the app block, when you type:

    bigtop your.bigtop Model

or bigtop your.bigtop all

this module will make model modules which are subclasses of Gantry::Utils::DBIxClass (which inherits from DBIx::Class).

All modules will live in the lib subdirectory of the app's build directory. See Bigtop::Init::Std for an explanation of how base_dir and the build directory are related.

DESCRIPTION

Top

This is a Bigtop backend which generates data model modules which are subclasses of Gantry::Utils::DBIxClass.

KEYWORDS

Top

This module does not register any keywords. See Bigtop::Model for a list of keywords models understand.

The default for the model_base_class keyword is Gantry::Utils::DBIxClass.

METHODS

Top

To keep podcoverage tests happy.

backend_block_keywords

Tells tentmaker that I understand these config section backend block keywords:

    no_gen
    model_base_class
    template

what_do_you_make

Tells tentmaker what this module makes. Summary: DBIx::Class models and schema.

gen_Model

Called by Bigtop::Parser to get me to do my thing.

setup_template

Called by Bigtop::Parser so the user can substitute an alternate template for the hard coded one here.

AUTHOR

Top

Phil Crow <crow.phil@gmail.com>

COPYRIGHT and LICENSE

Top


Bigtop documentation Contained in the Bigtop distribution.

package Bigtop::Backend::Model::GantryDBIxClass;
use strict; use warnings;

use Bigtop::Backend::Model;
use File::Spec;
use Inline;
use Bigtop;

#-----------------------------------------------------------------
#   Register keywords in the grammar
#-----------------------------------------------------------------

BEGIN {
    Bigtop::Parser->add_valid_keywords(
        Bigtop::Keywords->get_docs_for(
            'field',
            'accessor',
            'add_columns',
        )
    );
}

#-----------------------------------------------------------------
#   The Default Template
#-----------------------------------------------------------------

our $template_is_setup = 0;
our $default_template_text = <<'EO_TT_blocks';
[% BLOCK stub_base_model %]
package [% app_name %]::Model;
use strict; use warnings;

use base 'DBIx::Class::Schema';

use [% app_name %]::GENModel;

sub get_db_options {
    return { AutoCommit => 1 };
}

1;

=head1 NAME

[% app_name %]::Model - schema class for [% app_name +%]

=head1 SYNOPSIS

In your base module:

    use [% app_name %]::Model;
    sub schema_base_class { return '[% app_name %]::Model'; }
    use Gantry::Plugins::DBIxClassConn qw( get_schema );

[%- FOREACH table_model IN table_models -%]
    use [% app_name %]::Model::[% table_model %] qw( $[% table_model | upper %] );

[%- END -%]

=head1 DESCRIPTION

This module was generated by Bigtop.  But, feel free to edit it.  You
might even want to update these docs.

=over 4

=item get_db_options

The generated version sets AutoCommit to 1, this assumes that you will
do all transaction work via the DBIx::Class API.

=back

=head1 DEPENDENCIES

    Gantry::Utils::DBIxClass
    [% app_name %]::GENModel

=head1 AUTHOR

[% FOREACH author IN authors %]
[% author.0 %][% IF author.1 %], E<lt>[% author.1 %]E<gt>[% END %]

[% END %]

=head1 COPYRIGHT AND LICENSE

Copyright (C) [% year %] [% copyright_holder %]


[% IF license_text %]
[% ELSE %]
This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.8.6 or,
at your option, any later version of Perl 5 you may have available.
[% END %]

=cut
[% END %]

[% BLOCK gen_base_model %]
package [% app_name %]::Model;
use strict; use warnings;

__PACKAGE__->load_classes( qw/
[% FOREACH table_model IN table_models %]
    [% table_model %]

[% END %]
/ );

1;

=head1 NAME

[% app_name %]::GENModel - regenerating schema for [% app_name %]


=head1 SYNOPSIS

In your base schema:

    use base 'DBIx::Class::Schema';
    use [% app_name %]::GENModel;

=head1 DESCRIPTION

This module was generated by Bigtop (and IS subject to regeneration).

=head1 DEPENDENCIES

    Gantry::Utils::DBIxClass

=head1 AUTHOR

[% FOREACH author IN authors %]
[% author.0 %][% IF author.1 %], E<lt>[% author.1 %]E<gt>[% END %]

[% END %]

=head1 COPYRIGHT AND LICENSE

Copyright (C) [% year %] [% copyright_holder %]


[% IF license_text %]
[% ELSE %]
This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.8.6 or,
at your option, any later version of Perl 5 you may have available.
[% END %]

=cut
[% END %]

[% BLOCK stub_table_module %]
package [% package_name %];
use strict; use warnings;

use base '[% base_class || base_class_default %]', 'Exporter';

use [% gen_package_name %];

our $[% package_alias %] = '[% package_name %]';

our @EXPORT_OK = ( '$[% package_alias %]' );

1;

=head1 NAME

[% package_name %] - model for [% table_name %] table (stub part)

=head1 DESCRIPTION

This model inherits from its generated helper, which inherits from
[% base_class || base_class_default %].  It was generated by Bigtop, but is
NOT subject to regeneration.

=head1 METHODS (mixed in from [% gen_package_name %])

You may use all normal [% base_class || base_class_default %] methods and the
ones listed here:

=over 4

=item get_foreign_display_fields

=item get_foreign_tables

=item foreign_display

=item table_name
[% FOREACH option_field IN option_fields %]

=item [% option_field.name %]_display
[% END %]

=back

=cut
[% END %]

[% BLOCK gen_table_module %]
# NEVER EDIT this file.  It was generated and will be overwritten without
# notice upon regeneration of this application.  You have been warned.
package [% package_name %];
use strict; use warnings;

__PACKAGE__->load_components( qw/ [% load_components %] / );
__PACKAGE__->table( '[% real_table_name %]' );
__PACKAGE__->add_columns( qw/
[% FOREACH column IN regular_accessor_columns %]
    [% column +%]
[% END %]
/ );
[% IF special_accessor_columns.size > 0 %]
__PACKAGE__->add_columns(
[% FOREACH column IN special_accessor_columns %]
    [% column.name %] => { accessor => '[% column.accessor %]', },
[% END %]
);
[% END %]
[% IF add_columns.size > 0 %]
__PACKAGE__->add_columns(
[% FOREACH column IN add_columns %]
    [% column.name %] => {
[% FOREACH pair IN column.pairs %]
        [% pair.key %] => '[% pair.value %]',
[% END %]
    },
[% END %]
);
[% END %]
[% IF primary_key.0.defined %]__PACKAGE__->set_primary_key( [ qw( [% FOREACH pk IN primary_key %][% pk %][% UNLESS loop.last %] [% END %]
[% END %] ) ] );
[% ELSIF primary_key %]__PACKAGE__->set_primary_key( '[% primary_key +%]' );[% END +%]
[% FOREACH uq_cons_name IN unique_name.keys.sort %]
__PACKAGE__->add_unique_constraint(
    [% uq_cons_name %] => [ qw/[% FOREACH uq IN unique_name.${uq_cons_name} %][% uq %][% UNLESS loop.last %] [% END %][% END %]/ ]
);
[% END %]
[% FOREACH has_a IN has_a_list %]
__PACKAGE__->belongs_to( [% has_a.column %] => '[% base_package_name %]::[% has_a.table %]' );
[% END %]
__PACKAGE__->base_model( '[% app_name %]::Model' );
[% FOREACH has_many IN has_manys %]
__PACKAGE__->has_many( [% has_many.name %] => '[% app_name %]::Model::[% has_many.table %]'[% IF has_many.field.defined %], '[% has_many.field %]'[% END %] );
[% END %]
[% FOREACH has_many IN three_ways %]
__PACKAGE__->has_many(
    [% has_many.join_name %] => '[% has_many.three_way_model %]',
    '[% has_many.current_table %]'
);
__PACKAGE__->many_to_many(
    [% has_many.foreign_name %] => '[% has_many.join_name %]',
    '[% has_many.foreign_key %]'
);
[% END %]

[% IF foreign_display_columns %]sub get_foreign_display_fields {
    return [ qw( [% foreign_display_columns %] ) ];
}

[% END %]
sub get_foreign_tables {
    return qw(
[% FOREACH foreign_table IN foreign_tables %]
        [% base_package_name %]::[% foreign_table +%]
[% END %]
    );
}

[% IF foreign_display_columns %]sub foreign_display {
    my $self = shift;

[% foreign_display_body %]
}

[% END %]
sub table_name {
    return '[% table_name %]';
}
[%- IF option_fields.0 +%]
[% extra_methods = [] %]

my %select_map_for = (
[% FOREACH option_field IN option_fields %]
[% meth_name = option_field.name;
   extra_methods.push( "${meth_name}_display" ) %]
    [% option_field.name %] => {
[% FOREACH option IN option_field.options %]
        [% option.db_value %] => '[% option.label %]',
[% END %]
    },
[% END %]
);
[% FOREACH option_field IN option_fields %]

sub [% option_field.name %]_display {
    my $self = shift;
    my $[% option_field.name %] = defined $self->[% option_field.name %] ? $self->[% option_field.name %] : '';
    return $select_map_for{ [% option_field.name %] }{ $[% option_field.name %] }
           || $[% option_field.name %];
}
[% END %]
[% END %]


1;

=head1 NAME

[% gen_package_name %] - model for [% table_name %] table (generated part)

=head1 DESCRIPTION

This model inherits from [% base_class || base_class_default %].
It was generated by Bigtop, and IS subject to regeneration.

=head1 METHODS

You may use all normal [% base_class || base_class_default %] methods and the
ones listed here:

=over 4

=item get_foreign_display_fields

=item get_foreign_tables

=item foreign_display

=item table_name
[% FOREACH extra_method IN extra_methods %]

=item [% extra_method +%]
[% END %]

=back

=cut
[% END %]

[% BLOCK gen_three_way_module %]
package [% stub_package %];
use strict; use warnings;

use base '[% model_base_class %]', 'Exporter';

use [% gen_package %];

our $[% package_alias %] = '[% stub_package %]';

our @EXPORT_OK = ( '$[% package_alias %]' );

1;

=head1 NAME

[% stub_package %] - model for [% table_name %] table (stub part)

=head1 DESCRIPTION

This model inherits from its generated helper, which inherits from
Gantry::Utils::DBIxClass.  It was generated by Bigtop, but is
NOT subject to regeneration.

=cut
[% END %]

[% BLOCK gen_three_way_gen_module %]
# NEVER EDIT this file.  It was generated and will be overwritten without
# notice upon regeneration of this application.  You have been warned.
package [% stub_package %];
use strict; use warnings;

__PACKAGE__->load_components( qw/ PK::Auto Core / );
__PACKAGE__->table( '[% table_name %]' );
__PACKAGE__->add_columns( qw/
    id
[% FOREACH other_table IN joined_tables %]
    [% other_table +%]
[% END %]
[% FOREACH extra IN extra_fields %]
    [% extra +%]
[% END %]
/ );
__PACKAGE__->set_primary_key( 'id' );
__PACKAGE__->base_model( '[% app_name %]::Model' );
[% FOREACH other_table IN joined_tables %]
__PACKAGE__->belongs_to( [% other_table %] => '[% app_name %]::Model::[% other_table %]' );
[% END %]

sub get_foreign_tables {
    return qw(
    );
}

sub table_name {
    return '[% table_name %]';
}
[% IF option_fields.0 +%]
[% extra_methods = [] %]
my %select_map_for = (
[% FOREACH option_field IN option_fields %]
    [% option_field.name %] => {
[% FOREACH option IN option_field.options %]
        [% option.db_value %] => '[% option.label %]',
[% END %]
    },
[% END %]
);
[% FOREACH option_field IN option_fields %]
[% meth_name = option_field.name;
   extra_methods.push( "${meth_name}_display" ) %]

sub [% option_field.name %]_display {
    my $self = shift;
    return $select_map_for{ [% option_field.name %] }{ $self->[% option_field.name %] }
           || $self->[% option_field.name %];
}
[% END %]
[% END %]

1;

=head1 NAME

[% gen_package %] - model for [% table_name %] table (generated part)

=head1 DESCRIPTION

This model inherits from Gantry::Utils::DBIxClass.
It was generated by Bigtop, and IS subject to regeneration.

=head1 METHODS

You may use all normal Gantry::Utils::DBIxClass methods and the
ones listed here:

=over 4

=item get_foreign_display_fields

=item get_foreign_tables

=item foreign_display

=item table_name
[% FOREACH extra_method IN extra_methods %]

=item [% extra_method +%]
[% END %]

=back

=cut
[% END %]
EO_TT_blocks

#-----------------------------------------------------------------
#   Methods in the Bigtop::Model::GantryDBIxClass package
#-----------------------------------------------------------------

sub what_do_you_make {
    return [
        [ 'lib/AppName/Model/*.pm'     =>
            'DBIx::Class style model stubs [safe to change]'                 ],
        [ 'lib/AppName/Model/GEN/*.pm' =>
            'DBIx::Class style model specifications [please, do not change]' ],
        [ 'note' =>
            'This backend requires "For use with DBIx::Class" to be checked '
            .   'for the Control Gantry backend.' ],
        [ 'note' =>
            'This backend is incompatible with other Model backends.' ],
    ];
}

sub backend_block_keywords {
    return [
        { keyword => 'no_gen',
          label   => 'No Gen',
          descr   => 'Skip everything for this backend',
          type    => 'boolean' },

        { keyword => 'model_base_class',
          label   => 'Models Inherit From',
          descr   => 'Defaults to Gantry::Utils::DBIxClass',
          type    => 'text' },

        { keyword => 'template',
          label   => 'Alternate Template',
          descr   => 'A custom TT template.',
          type    => 'text' },

        { keyword => 'extra_components',
          label   => 'Extras for load_components',
          descr   => 'Things other than PK::Auto and Core to load. '
                     . 'Separate multiples with spaces.',
          type    => 'text' },
    ];
}

sub setup_template {
    my $class         = shift;
    my $template_text = shift || $default_template_text;

    return if ( $template_is_setup );

    Inline->bind(
        TT                  => $template_text,
        POST_CHOMP          => 1,
        TRIM_LEADING_SPACE  => 0,
        TRIM_TRAILING_SPACE => 0,
    );

    $template_is_setup = 1;
}

sub gen_Model {
    my $class       = shift;
    my $build_dir   = shift;
    my $bigtop_tree = shift;

    # make sure the directories are ready for us
    my $app_base_name = $bigtop_tree->get_appname();
    my $model_name    = $app_base_name . '::Model';

    my ( $module_dir, @sub_dirs )
                      = Bigtop::make_module_path( $build_dir, $model_name );

    my $gen_dir       = File::Spec->catdir( $module_dir, 'GEN' );

    mkdir $gen_dir;

    # see if there is an alternate default base module
    my $config_block  = $bigtop_tree->get_config()->{Model};

    # build the individual model packages
    my $child_models = $bigtop_tree->walk_postorder(
        'output_dbix_model',
        {
            app_name         => $app_base_name,
            module_dir       => $module_dir,
            model_name       => $model_name,
            lookup           => $bigtop_tree->{application}{lookup},
            model_base_class => $config_block->{model_base_class}
                                    || 'Gantry::Utils::DBIxClass',
            extra_components => $config_block->{extra_components},
        },
    );

    $bigtop_tree->walk_postorder(
        'output_join_modules_dbix',
        {
            app_name         => $app_base_name,
            module_dir       => $module_dir,
            model_name       => $model_name,
            lookup           => $bigtop_tree->{application}{lookup},
            model_base_class => $config_block->{model_base_class}
                                    || 'Gantry::Utils::DBIxClass',
        }
    );

    my $year = ( localtime )[5] + 1900;

    my $gen_base_model =
        Bigtop::Backend::Model::GantryDBIxClass::gen_base_model(
            {
                app_name     => $app_base_name,
                table_models => $child_models,
                authors      => $bigtop_tree->get_authors(),

                year             => $year,
                copyright_holder => $bigtop_tree->get_copyright_holder(),
                license_text     => $bigtop_tree->get_license_text(),
            }
        );

    my $base_model = Bigtop::Backend::Model::GantryDBIxClass::stub_base_model(
        {
            app_name     => $app_base_name,
            table_models => $child_models,
            authors      => $bigtop_tree->get_authors(),

            year             => $year,
            copyright_holder => $bigtop_tree->get_copyright_holder(),
            license_text     => $bigtop_tree->get_license_text(),
        }
    );

    my ( $base_dir ) = Bigtop::make_module_path( $build_dir, $app_base_name );
    my $base_file    = File::Spec->catfile( $base_dir, 'Model.pm' );
    my $gen_file     = File::Spec->catfile( $base_dir, 'GENModel.pm' );

    Bigtop::write_file( $gen_file, $gen_base_model );

    no warnings qw( Bigtop );
    Bigtop::write_file( $base_file, $base_model, 'no overwrite' );
}

#-----------------------------------------------------------------
#   Packages named in the grammar
#-----------------------------------------------------------------

package # table_block
    table_block;
use strict; use warnings;

sub output_dbix_model {
    my $self         = shift;
    my $child_output = shift;
    my $data         = shift;

    my @option_fields;
    while ( @{ $child_output } ) {
        my $field_name = shift @{ $child_output };
        my $options    = shift @{ $child_output };

        push @option_fields, {
            name    => $field_name,
            options => $options,
        };
    }

    # Skip sequences, etc.
    return unless ( $self->{__TYPE__} =~ /tables/ );

    my $table_lookup = $data->{lookup}{tables}{ $self->{__NAME__} };
    my $grand_parent = $self->{__PARENT__}{__PARENT__};

    if ( $table_lookup->{not_for} ) {
        foreach my $skipped_type ( @{ $table_lookup->{not_for}{__ARGS__} } ) {
            return if ( $skipped_type eq 'Model' );
        }
    }

    # get columns sets
    my $lookup       = $table_lookup->{fields};

    my $regular_accessor_columns = $self->walk_postorder(
            'output_regular_accessors_dbix', $lookup
    );
    my $special_accessor_columns = $self->walk_postorder(
            'output_special_accessors_dbix', $lookup
    );
    my $add_columns = $self->walk_postorder(
            'output_add_columns_dbix', $lookup
    );
    my $essentials = $self->walk_postorder(
            'output_essential_fields_dbix', $lookup
    );

    # deal with foreign keys
    my $foreign_tables = $self->walk_postorder(
            'output_foreign_tables_dbix',       $lookup
    );

    # deal with foreign keys pointing toward this table
    my $has_manys = $self->walk_postorder(
            'output_has_manys', $data->{lookup}->{tables}
    );

    my @foreign_table_names;
    my @has_a_list;

    foreach my $entry ( @{ $foreign_tables } ) {
        my $entry_hash = { @{ $entry } };
        push @foreign_table_names, $entry_hash->{table};
        push @has_a_list,          $entry_hash;
    }

    # deal with 3 way joins
    my $three_ways = $grand_parent->walk_postorder(
            'output_has_manys_dbix',
            {
                lookup => $data->{lookup}{join_tables},
                model  => $data->{model_name},
                table  => $self->{__NAME__},
            }
    );

    # Gone Fishing.
    my $table           = $self->{__NAME__};
    $table              =~ s/\./_/;
    my $module_name     = $data->{model_name} . '::' . $table;
    my $gen_pack_name   = $data->{model_name} . '::GEN::' . $table;
    my $alias           = uc $table;
    my $sequence        = $table_lookup->{sequence};
    my $foreign_display = $table_lookup->{foreign_display};

    my $sequence_name;

    if ( $sequence ) {
        $sequence_name = $sequence->{__ARGS__}[0];
    }

    my $primary_key = $self->find_primary_key(
            $self->{__NAME__},
            $data->{ lookup },
    );

    my $unique_name = $self->find_unique_name(
            $self->{__NAME__},
            $data->{ lookup },
    );

    my $foreign_display_columns;
    my $foreign_display_body;

    if ( $foreign_display ) {
        my $foreign_display_cols = $foreign_display->{__ARGS__}[0];

        my @field_names          = ( $foreign_display_cols =~ /%([\w\d_]*)/g );
        $foreign_display_columns = "@field_names";

        $foreign_display_body  = _build_foreign_display_body(
            $foreign_display_cols, @field_names
        );
    }

    my $base_class;
    
    if ( defined $table_lookup->{model_base_class} ) {
        $base_class = $table_lookup->{model_base_class}{__ARGS__}[0];
    }

    # generate output
    my $stub_content =
        Bigtop::Backend::Model::GantryDBIxClass::stub_table_module(
        {
            base_class              => $base_class,
            base_class_default      => $data->{model_base_class},
            base_package_name       => $data->{model_name},
            gen_package_name        => $gen_pack_name,
            package_name            => $module_name,
            package_alias           => $alias,
            table_name              => $table,
            option_fields           => \@option_fields,
        }
    );

    my $pk_auto = 1;
    if ( not defined $primary_key or ref( $primary_key ) eq 'ARRAY' ) {
        $pk_auto = 0;
    }

    my @load_components = ( 'Core' );
    if ( defined $data->{ extra_components }
            and
        $data->{ extra_components } =~ /InflateColumn::DateTime/
    ) {
        unshift @load_components, $data->{ extra_components };
    }
    elsif ( $pk_auto ) {
        unshift @load_components, 'PK::Auto';
    }
    my $load_components = join ' ', @load_components;

    my $gen_content =
        Bigtop::Backend::Model::GantryDBIxClass::gen_table_module(
        {
            base_class_default      => $data->{model_base_class},
            base_package_name       => $data->{model_name},
            package_name            => $module_name,
            gen_package_name        => $gen_pack_name,
            package_alias           => $alias,
            table_name              => $table,
            sequence_name           => $sequence_name,
            primary_key             => $primary_key,
            unique_name             => $unique_name,
            load_components         => $load_components,
            foreign_display_columns => $foreign_display_columns,
            foreign_display_body    => $foreign_display_body,
            regular_accessor_columns=> $regular_accessor_columns,
            special_accessor_columns=> $special_accessor_columns,
            add_columns             => $add_columns,
            essential_columns       => $essentials,
            has_a_list              => \@has_a_list,
            has_manys               => $has_manys,
            three_ways              => $three_ways,
            foreign_tables          => \@foreign_table_names,
            app_name                => $data->{ app_name },
            real_table_name         => $self->{__NAME__},
            option_fields           => \@option_fields,
        }
    );

    # store it
    my $module_file = File::Spec->catfile( $data->{module_dir}, "$table.pm" );
    my $gen_dir     = File::Spec->catdir ( $data->{module_dir}, 'GEN' );
    my $gen_file    = File::Spec->catfile( $gen_dir, "$table.pm" );

    eval {
        no warnings qw( Bigtop );
        Bigtop::write_file( $module_file, $stub_content, 'no overwrite' );
    };
    warn $@ if $@;

    eval {
        Bigtop::write_file( $gen_file, $gen_content );
    };
    warn $@ if $@;

    return [ $table ];
}

package # table_element_block
    table_element_block;
use strict; use warnings;

sub output_regular_accessors_dbix {
    my $self         = shift;
    shift;
    my $data         = shift;

    return unless ( ref( $self->{__BODY__} ) );

    my $field  = $data->{ $self->{__NAME__} };

    return if ( _not_for_model( $field ) );

    return if $field->{ pseudo_value };

    return if ( defined $field->{ accessor }
                    or
                defined $field->{ add_columns } );

    return [ $self->{__NAME__} ];
}

sub output_special_accessors_dbix {
    my $self         = shift;
    shift;
    my $data         = shift;

    return unless ( ref( $self->{__BODY__} ) );

    my $field  = $data->{ $self->{__NAME__} };

    return unless ( defined $field->{ accessor } );

    my $special_accessor_name = $field->{ accessor }{ args }->get_first_arg();

    return [
        {
            name     => $self->{__NAME__},
            accessor => $special_accessor_name,
        }
    ];

}

sub output_add_columns_dbix {
    my $self         = shift;
    shift;
    my $data         = shift;

    return unless ( ref( $self->{__BODY__} ) );

    my $field  = $data->{ $self->{__NAME__} };

    return unless ( defined $field->{ add_columns } );

    my $args = $field->{ add_columns }{ args };

    my @pairs;
    foreach my $col ( @{ $args } ) {
        my ( $key, $value ) = %{ $col };
        push @pairs, { key => $key, value => $value };
    }

    return [
        {
            name  => $self->{__NAME__},
            pairs => \@pairs,
        }
    ];
}

sub output_essential_fields_dbix {
    my $self         = shift;
    shift;
    my $data         = shift;

    return unless ( ref( $self->{__BODY__} ) );

    my $field  = $data->{ $self->{__NAME__} };

    if ( $field->{non_essential} ) {
        my $non_essential_value = $field->{non_essential}{args}[0];

        return if ( $non_essential_value );
    }

    return if ( _not_for_model( $field ) );

    return [ $self->{__NAME__} ];
}

sub output_foreign_tables_dbix {
    my $self         = shift;
    shift;
    my $data         = shift;

    return unless ( ref( $self->{__BODY__} ) );

    my $field  = $data->{ $self->{__NAME__} };

    if ( $field->{refers_to} ) {
        my $foreign_table_name = $field->{refers_to}{args}[0];
        if ( ref( $foreign_table_name ) eq 'HASH' ) {
            ( $foreign_table_name ) = %{ $foreign_table_name };
        }
        $foreign_table_name    =~ s/\./_/;

        return [
            [ column => $self->{__NAME__}, table => $foreign_table_name ]
        ];
    }
    return;
}

sub output_has_manys {
    my $self    = shift;
    shift;
    my $data    = shift;

    return unless ( $self->{__TYPE__} eq 'refered_to_by' );

    my @retval;
    foreach my $arg ( @{ $self->{__ARGS__} } ) {
        my ( $refering_table, $has_many_name, $field_name );

        if ( ref( $arg ) eq 'HASH' ) {
            ( $refering_table, $has_many_name ) = %{ $arg };
        }
        else {
            ( $refering_table, $has_many_name ) = ( $arg, $arg . 's' );
        }

        # Get the name of the field in the table that is refering to this one.
        FIELD_SEARCH:
        foreach my $field ( %{ $data->{$refering_table}->{fields} } ) {
            if ( $data->{$refering_table}->{fields}->{$field}->{refers_to} ) {
                foreach my $refers_to_arg ( @{ $data->{$refering_table}->{fields}->{$field}->{refers_to}->{args} } ) {
                    my $refered_to_table;

                    if ( ref( $refers_to_arg ) eq 'HASH' ) {
                        ( $refered_to_table, undef ) = %{ $refers_to_arg };
                    }
                    else {
                        $refered_to_table = $refers_to_arg;
                    }

                    if ( $refered_to_table eq $self->{__PARENT__}->{__NAME__}) {
                        $field_name = $field;
                        last FIELD_SEARCH;
                    }
                }
            }
        }

        push @retval, { name => $has_many_name, table => $refering_table, field => $field_name };
    }

    return \@retval;
}

sub output_join_modules_dbix {
    my $self         = shift;
    my $child_output = shift;

    push @{ $child_output }, { plain_field => $self->get_name };

    return $child_output;
}

package # join_table
    join_table;
use strict; use warnings;

sub output_dbix_model {
    my $self         = shift;
    my $child_output = shift;
    my $data         = shift;

    return [ $self->{__NAME__} ];
}

#    warn "im a join table ($self->{__NAME__}) and i veto\n";
#    use Data::Dumper; warn Dumper( $child_output );

sub output_join_modules_dbix {
    my $self         = shift;
    my $child_output = shift;
    my $data         = shift;
    my $table        = $self->{__NAME__};

    my @foreign_keys;
    my @option_fields;
    my @extra_fields;

    foreach my $tidbit ( @{ $child_output } ) {
        if ( ref $tidbit eq 'ARRAY' ) {
            while ( @{ $tidbit->[0] } ) {
                my $field_name = shift @{ $tidbit->[0] };
                my $options    = shift @{ $tidbit->[0] };

                push @option_fields, {
                    name    => $field_name,
                    options => $options,
                };
            }
        }
        elsif ( ref $tidbit eq 'HASH' ) {
            my ( undef, $plain_field ) = %{ $tidbit };
            push @extra_fields, $plain_field;
        }
        else {
            push @foreign_keys, $tidbit;
        }
    }

    my $package      = join '::', $data->{model_name}, $self->{__NAME__};
    my $gen_package  = join '::',
                            $data->{model_name}, 'GEN', $self->{__NAME__};

    my $module_file  = File::Spec->catfile( $data->{module_dir}, "$table.pm" );
    my $gen_file     = File::Spec->catfile(
            $data->{module_dir}, 'GEN', "$table.pm"
    );

    my $stub_content = 
        Bigtop::Backend::Model::GantryDBIxClass::gen_three_way_module(
            {
                stub_package     => $package,
                gen_package      => $gen_package,
                table_name       => $table,
                model_base_class => $data->{model_base_class},
                package_alias    => uc $table,
            }
        );

    eval {
        no warnings qw( Bigtop );
        Bigtop::write_file( $module_file, $stub_content, 'no overwrite' );
    };
    warn $@ if $@;

    my $gen_content =
        Bigtop::Backend::Model::GantryDBIxClass::gen_three_way_gen_module(
            {
                app_name         => $data->{app_name},
                stub_package     => $package,
                gen_package      => $gen_package,
                table_name       => $table,
                model_base_class => $data->{model_base_class},
                package_alias    => uc $table,
                joined_tables    => \@foreign_keys,
                option_fields    => \@option_fields,
                extra_fields     => \@extra_fields,
            }
        );

    eval {
        Bigtop::write_file( $gen_file, $gen_content );
    };
    warn $@ if $@;

    return [ 1 ];
}

package # join_table_statement
    join_table_statement;
use strict; use warnings;

sub output_has_manys_dbix {
    my $self         = shift;
    my $child_output = shift;
    my $data         = shift;

    return unless $self->{__KEYWORD__} eq 'joins';

    my $join_table_name = $self->get_join_table_name();

    my @tables = %{ $self->{__DEF__}->get_first_arg() };

    unless ( $tables[0] eq $data->{table}
                or
             $tables[1] eq $data->{table}
    ) {
        return;
    }

    my $lookup = $data->{ lookup }{ $data->{ table } };

    CANDIDATE:
    foreach my $candidate ( @{ $lookup } ) {
        if ( defined $candidate->{ joins } ) {
            my ( $foreign_key, $third_table ) = %{ $candidate->{ joins } };
            next CANDIDATE unless ( $third_table eq $join_table_name );

            my $foreign_name;
            if ( defined $candidate->{ name } ) {
                $foreign_name = $candidate->{ name };
            }
            else {
                $foreign_name = $foreign_key . 's';
            }
            return [ {
                join_name       => $third_table . 's',
                three_way_model => "$data->{ model }::$third_table",
                current_table   => $data->{ table },

                foreign_name    => $foreign_name,
                foreign_key     => $foreign_key,
            } ];
        }
    }
    return;
}

sub output_join_modules_dbix {
    my $self         = shift;
    my $child_output = shift;
    my $data         = shift;

    return unless $self->{__KEYWORD__} eq 'joins';

    my @tables       = %{ $self->{__DEF__}->get_first_arg() };

    return \@tables;
}

package # field_statement
    field_statement;
use strict; use warnings;

sub output_dbix_model {
    my $self         = shift;

    return unless $self->{__KEYWORD__} eq 'html_form_options';

    my $name = $self->get_field_name;
    my @tt_options;

    foreach my $option ( @{ $self->{__DEF__}{__ARGS__} } ) {
        my %tt_option;
        ( $tt_option{ label }, $tt_option{ db_value } ) = %{ $option };

        push @tt_options, \%tt_option;
    }

    return [ $name, \@tt_options ];
}

sub output_join_modules_dbix {
    my $self = shift;

    my $option_output = $self->output_dbix_model( );

    return unless $option_output;

    return [ [ $option_output ] ];
}

1;

__END__