use strict; use warnings; use Test::More 0.88; use lib 't/lib'; use Fey::ORM::Test qw( schema ); use Fey::Placeholder; use List::Util qw( first ); my $Schema = schema(); { package Schema; use Fey::ORM::Schema; has_schema $Schema; } { package Message; use Fey::ORM::Table; has_table $Schema->table('Message'); has_one $Schema->table('User'); } { can_ok( 'Message', 'user' ); my $attr = Message->meta()->get_attribute('user'); ok( $attr, 'found attribute for user' ); is( ref $attr->default(), 'CODE', 'user attribute default is a coderef' ); is( $attr->type_constraint()->name(), 'Fey::Object::Table', 'user attribute type constraint is Fey::Object::Table' ); my @ones = Message->meta()->has_ones(); is( scalar @ones, 1, 'one has_one for Message class' ); my $ho = $ones[0]; isa_ok( $ho, 'Fey::Meta::HasOne::ViaFK' ); is( $ho->associated_class(), Message->meta(), 'associated_class is Message->meta()' ); is( $ho->name(), 'user', 'name is user' ); is( $ho->table(), $Schema->table('Message'), 'table is Message table object' ); is( $ho->foreign_table(), $Schema->table('User'), 'foreign_table is User table object' ); ok( $ho->is_cached(), 'is_cached is true' ); ok( !$ho->allows_undef(), 'allows_undef is false' ); is( $ho->fk()->source_table(), $Schema->table('Message'), 'fk source table is Message' ); is( $ho->fk()->target_table(), $Schema->table('User'), 'fk target table is User' ); is( $ho->associated_method(), undef, 'associated_method is undef' ); my $assoc_attr = $ho->associated_attribute(); is( $assoc_attr, $attr, 'associated attribute is same as the one in the metaclass' ); } { package Message; use Fey::ORM::Table; __PACKAGE__->meta()->remove_has_one('user'); ::is( scalar __PACKAGE__->meta()->has_ones(), 0, 'no has_ones after calling remove_has_one' ); ::ok( !__PACKAGE__->meta()->has_attribute('user'), 'does not have a user attribute after calling remove_has_one' ); has_one 'user' => ( table => $Schema->table('User'), undef => 1, ); } { my $attr = Message->meta()->get_attribute('user'); is( $attr->type_constraint()->name(), 'Maybe[Fey::Object::Table]', 'user attribute type constraint is Maybe[Fey::Object::Table]' ); my @ones = Message->meta()->has_ones(); is( scalar @ones, 1, 'one has_one for Message class' ); ok( $ones[0]->allows_undef, 'allows_undef is true' ); } { package Message; __PACKAGE__->meta()->remove_has_one('user'); has_one 'my_user' => ( table => $Schema->table('User'), cache => 1, ); } { can_ok( 'Message', 'my_user' ); can_ok( 'Message', '_clear_my_user' ); my $attr = Message->meta()->get_attribute('my_user'); ok( $attr, 'found attribute for my_user' ); is( ref $attr->default(), 'CODE', 'my_user attribute default is a coderef' ); is( $attr->type_constraint()->name(), 'Fey::Object::Table', 'my_user attribute type constraint is Fey::Object::Table' ); } { package Message; __PACKAGE__->meta()->remove_has_one('my_user'); has_one 'user' => ( table => $Schema->table('User'), cache => 0, ); } { can_ok( 'Message', 'user' ); ok( !Message->can('_clear_user'), 'no clearer for non-cached has_one' ); ok( !Message->meta()->has_attribute('user'), 'Message does not have an attribute for user (but does have a user() method)' ); my @ones = Message->meta()->has_ones(); is( scalar @ones, 1, 'one has_one for Message class' ); is( $ones[0]->associated_method(), Message->meta()->get_method('user'), 'associated_method matches user method in Message class' ); } { package Message; use Fey::ORM::Table; __PACKAGE__->meta()->remove_has_one('user'); has_one 'user' => ( table => $Schema->table('User'), handles => [qw( username email )], ); } { can_ok( 'Message', 'username' ); can_ok( 'Message', 'email' ); } { package Message; eval { has_one $Schema->table('Group') }; ::like( $@, qr/\QThere are no foreign keys between the table for this class, Message and the table you passed to has_one(), Group/, 'Cannot declare a has_one relationship to a table with which we have no FK' ); eval { has_one $Schema->table('Message') }; ::is( $@, '', 'no exception declaring a self-referential has_one' ); my $table = Fey::Table->new( name => 'NewTable' ); eval { has_one $table }; ::like( $@, qr/\QA table used for has-one or -many relationships must have a schema/, 'table without a schema passed to has_one()' ); } { my $editor_user_id = Fey::Column->new( name => 'editor_user_id', type => 'integer', ); $Schema->table('Message')->add_column($editor_user_id); my $fk = Fey::FK->new( source_columns => [ $Schema->table('Message')->column('editor_user_id') ], target_columns => [ $Schema->table('User')->column('user_id') ], ); $Schema->add_foreign_key($fk); } { package Message; __PACKAGE__->meta()->remove_has_one('user'); eval { has_one 'editor' => ( table => $Schema->table('User') ) }; ::like( $@, qr/\QThere is more than one foreign key between the table for this class, Message and the table you passed to has_one(), User. You must specify one explicitly/i, 'exception is thrown if trying to make a has_one() when there is >1 fk between the two tables' ); my ($fk) = grep { $_->source_columns()->[0]->name() eq 'editor_user_id' } $Schema->foreign_keys_between_tables( 'Message', 'User' ); eval { has_one 'editor' => ( table => $Schema->table('User'), fk => $fk, ); }; ::is( $@, '', 'no error when specifying passing a disambiguating fk to has_one' ); my @ones = Message->meta()->has_ones(); ::is( $ones[0]->fk(), $fk, 'fk matches the one passed to has_one' ); } { package Message; __PACKAGE__->meta()->remove_has_one( $_->name() ) for __PACKAGE__->meta()->has_ones(); my $select = Fey::SQL->new_select()->select( $Schema->table('Message') )->where( $Schema->table('Message')->column('parent_message_id'), '=', Fey::Placeholder->new() )->order_by( $Schema->table('Message')->column('message_id'), 'DESC' ) ->limit(1); has_one 'most_recent_child' => ( table => $Schema->table('Message'), select => $select, bind_params => sub { $_[0]->message_id() }, ); } { can_ok( 'Message', 'most_recent_child' ); my $attr = Message->meta()->get_attribute('most_recent_child'); ok( $attr, 'found attribute for most_recent_child' ); is( ref $attr->default(), 'CODE', 'most_recent_child attribute default is a coderef' ); is( $attr->type_constraint()->name(), 'Maybe[Fey::Object::Table]', 'most_recent_child attribute type constraint is Maybe[Fey::Object::Table]' ); my @ones = Message->meta()->has_ones(); is( scalar @ones, 1, 'one has_one for Message class' ); my $ho = $ones[0]; isa_ok( $ho, 'Fey::Meta::HasOne::ViaSelect' ); is( $ho->name(), 'most_recent_child', 'name is most_recent_child' ); ok( $ho->allows_undef(), 'allows_undef is true' ); } { package Message; __PACKAGE__->meta()->remove_has_one('most_recent_child'); my $select = Fey::SQL->new_select()->select( $Schema->table('Message') ) ->from( $Schema->table('Message') )->where( $Schema->table('Message')->column('parent_message_id'), '=', Fey::Placeholder->new() )->order_by( $Schema->table('Message')->column('message_id'), 'DESC' ) ->limit(1); has_one 'most_recent_child' => ( table => $Schema->table('Message'), select => $select, bind_params => sub { $_[0]->message_id() }, cache => 0, ); } { can_ok( 'Message', 'most_recent_child' ); ok( !Message->meta()->get_attribute('most_recent_child'), 'Message does not have a most_recent_child attribute, but does have a method for it' ); } done_testing();