114 changes: 97 additions & 17 deletions Changes
Original file line number Diff line number Diff line change
@@ -1,64 +1,144 @@
Current Known Issues / Regressions
- Breaks DBIx::Class::ResultSet::WithMetaData (fix pending in RT#104602)
- Breaks DBIx::Class::Tree::NestedSet (fix pending in RT#114440)

Revision history for DBIx::Class

* Notable Changes and Deprecations
- The entire class hierarchy now explicitly sets the 'c3' mro, even
in cases where load_components was not used. Extensive testing led
the maintainer believe this is safe, but this is a very complex
area and reality may turn out to be different. If **ANYHTING** at
all seems out of place, please file a report at once
- The unique constraint info (including the primary key columns) is no
longer shared between related (class and schema-level) ResultSource
instances. If your app stops working with no obvious pointers, set
DBIC_ASSERT_NO_ERRONEOUS_METAINSTANCE_USE=1 to obtain extra info
- Neither exception_action() nor $SIG{__DIE__} handlers are invoked
on recoverable errors. This ensures that the retry logic is fully
insulated from changes in control flow, as the handlers are only
invoked when an error is leaving the DBIC internals to be handled by
the caller (n.b. https://github.com/PerlDancer/Dancer2/issues/1125)
(also fixes the previously rejected RT#63874)
- Overrides of ResultSourceProxy-provided methods are no longer skipped
silently: a one-per-callsite warning is issued any time this tricky
situation is encoutered https://is.gd/dbic_rsrcproxy_methodattr
- $result->related_resultset() no longer passes extra arguments to
an underlying search_rs(), as by design these arguments would be
used only on the first call to ->related_resultset(), and ignored
afterwards. Instead an exception (detailing the fix) is thrown.
- Change func_rs() and as_subselect_rs() to properly ignore list
context (i.e. wantarray). Both were implemented broken from day 1 :/
- Increased checking for the correctness of the is_nullable attribute
within the prefetch result parser may highlight previously unknown
mismatches between your codebase and data source
- Calling the set_* many-to-many helper with a list (instead of an
arrayref) now emits a deprecation warning
- Calling the getter $rsrc->from("argument") now throws an exception
instead of silently discarding the argument
- search() calls with an empty select list are deprecated. While DBIC
will still issue a SELECT * ..., it now warns given there is nothing
higher up in the stack prepared to interpret the result

* New Features
- When using non-scalars (e.g. arrays) as literal bind values it is no
longer necessary to explicitly specify a bindtype (this turned out
to be a mostly useless overprotection)
- DBIC now performs a range of sanity checks on the entire hierarchy
of Schema/Result/ResultSet classes loudly alerting the end user to
potential extremely hard-to-diagnose pitfalls ( RT#93976, also fully
addresses https://blog.afoolishmanifesto.com/posts/mros-and-you/ )
- A new low-level API for relationship resolution is available as an
official method ( $rsrc->resolve_relationship_condition ). This is
mainly of interest to builders of reflection tools
- InflateColumn::DateTime now accepts the ecosystem-standard option
'time_zone', in addition to the DBIC-only 'timezone' (GH#28)
- Massively optimised literal SQL snippet scanner - fixes all known
slowdowns ( in some cases 50x ) of very complex prefetch/selects
- DBIx::Class::Optional::Dependencies now properly understands
combinations of requirements and does the right thing with e.g.
->req_list_for([qw( rdbms_oracle ic_dt )]) bringing in the Oracle
specific DateTime::Format dependencies

* Fixes
- Ensure failing on_connect* / on_disconnect* are dealt with properly,
notably on_connect* failures now properly abort the entire connect
- Fix regresion (0.082800) of certain calls being presented stale
result source metadata (RT#107462)
- Fix incorrect SQL generated with invalid {rows} on complex resultset
operations, generally more robust handling of rows/offset attrs
- Fix silent failure to retrieve a primary key (RT#80283) or worse:
returning an incorrect value (RT#115381) in case a rdbms-side autoinc
column is declared as PK with the is_auto_increment attribute unset
- Fix incorrect $storage state on unexpected RDBMS disconnects and
other failure events, preventing clean reconnection (RT#110429)
- Ensure leaving an exception stack via Return::MultiLevel or something
similar produces a large warning
- Make sure exception objects stringifying to '' are properly handled
and warned about (GH#15)
- Fix incorrect data returned in a corner case of partial-select HRI
invocation (no known manifestations of this bug in the field, see
commit message for description of exact failure scenario)
- Fix corner case of stringify-only overloaded objects being used in
create()/populate()
- Fix spurious ROLLBACK statements when a TxnScopeGuard fails a commit
of a transaction with deferred FK checks: a guard is now inactivated
immediately before the commit is attempted (RT#107159)
- Work around unreliable $sth->finish() on INSERT ... RETURNING within
DBD::Firebird on some compiler/driver combinations (RT#110979)
- Fix spurious warning on MSSQL cursor invalidation retries (RT#102166)
- Fix incorrect ::Storage->_ping() behavior under Sybase (RT#114214)
- Fix some corner cases of non-fatal failures during relationship
resolution showing up as hard errors
- Fix several corner cases with Many2Many over custom relationships
- Fix the Sybase ASE storage incorrectly attempting to retrieve an
autoinc value when inserting rows containing blobs (GH#82)
- Fix intermittent failure to infer the CASCADE attributes of relations
during deployment_statements()/deploy()
- Fix corner cases of C3 composition being broken on OLD_MRO (5.8.x)
only: https://github.com/frioux/DBIx-Class-Helpers/issues/61

* Misc
- Add explicit test for pathological example of asymmetric IC::DT setup
working with copy() in t/icdt/engine_specific/sybase.t (GH#84)
- Fix t/54taint.t failing on local::lib's with upgraded Carp on 5.8.*
- Fix invalid variable names in ResultSource::View examples
- Typo fixes from downstream debian packagers (RT#112007)
- Fix missing ORDER BY leading to failures of t/prefetch/grouped.t
under upcoming libsqlite (RT#117271)
- Skip tests in a way more intelligent and speedy manner when optional
dependencies are missing
- Make the Optional::Dependencies error messages cpanm-friendly
- Incompatibly change values (not keys) of the hash returned by
Optional::Dependencies::req_group_list (no known users in the wild)
- Protect tests and codebase from incomplete caller() overrides, like
e.g. RT#32640
- Stop using bare $] throughout - protects the codebase from issues
similar (but likely not limited to) P5#72210
- Config::Any is no longer a core dep, but instead is migrated to a new
optdep group 'config_file_reader'

0.082840 2016-06-20 07:02 (UTC)
* New Features
- When using non-scalars (e.g. arrays) as literal bind values it is no
longer necessary to explicitly specify a bindtype (this turned out
to be a mostly useless overprotection)

* Fixes
- Ensure leaving an exception stack via Return::MultiLevel or something
similar produces a large warning
- Another relatively invasive set of ::FilterColumn changes, covering
potential data loss (RT#111567). Please run your regression tests!
- Ensure failing on_connect* / on_disconnect* are dealt with properly,
notably on_connect* failures now properly abort the entire connect
- Fix use of ::Schema::Versioned combined with a user-supplied
$dbh->{HandleError} (GH#101)
- Fix parsing of DSNs containing driver arguments (GH#99)
- Fix silencing of exceptions thrown by custom inflate_result() methods
- Fix complex prefetch when ordering over foreign boolean columns
( Pg can't MAX(boolcol) despite being able to ORDER BY boolcol )
- Fix infinite loop on ->svp_release("nonexistent_savepoint") (GH#97)
- Fix spurious ROLLBACK statements when a TxnScopeGuard fails a commit
of a transaction with deferred FK checks: a guard is now inactivated
immediately before the commit is attempted (RT#107159)
- Fix the Sybase ASE storage incorrectly attempting to retrieve an
autoinc value when inserting rows containing blobs (GH#82)
- Remove spurious exception warping in ::Replicated::execute_reliably
(RT#113339)
- Work around unreliable $sth->finish() on INSERT ... RETURNING within
DBD::Firebird on some compiler/driver combinations (RT#110979)
- Fix leaktest failures with upcoming version of Sub::Quote
- Really fix savepoint rollbacks on older DBD::SQLite (fix in 0.082800
was not sufficient to cover up RT#67843)

* Misc
- Test suite is now officially certified to work under very high random
parallelism: META x_parallel_test_certified set to true accordingly
- Typo fixes from downstream debian packagers (RT#112007)

0.082821 2016-02-11 17:58 (UTC)
* Fixes
- Fix t/52leaks.t failures on compilerless systems (RT#104429)
Expand Down
125 changes: 125 additions & 0 deletions GOVERNANCE
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
DBIx::Class Core Team and Voting System

Non normative section:

DBIx::Class originally operated under a BDFL system, but one where it was
expected that an informal core team would be maintained, and that where
consensus could not be pre-assumed, the core team and/or the user base
would be consulted publically such that measured decisions could be made.

This document is intended to formalise a form of this system, while still
providing room for the system to adapt later as required.

It is intended that this system provides confidence to the user base that
decisions will be made in the open and that their wishes will be taken into
account.

It is also intended that this system allows business as usual to happen
without unnecessary red tape.

It is not intended that this system becomes the primary decision making
process in and of itself; instead, it is intended that this system is used
to ratify consensus as formed by discussion, and only sparingly as a tie
breaking system when consensus cannot be reached.

Normative section:

Terms: VM - Voting Member - part of the benevolent dictatorship
LS - List Subscriber - a subscriber to the mailing list
LAV - List Aggregate Vote - the aggregate vote of the non-VM LSes

Voting Members are:

Matt S Trout (mst) cpan:MSTROUT
Dagfinn Ilmari Mansaker (ilmari) cpan:ILMARI
Frew Schmidt (frew) cpan:FREW
Jess Robinson (castaway) cpan:JROBINSON

PAUSE release perms are to be held by:

Matt S Trout (mst) cpan:MSTROUT
Dagfinn Ilmari Mansaker (ilmari) cpan:ILMARI
Frew Schmidt (frew) cpan:FREW
Jess Robinson (castaway) cpan:JROBINSON

First come permissions are to be held by FREW.

(the above two lists may or may not be identical at any given time)

A resolution must be proposed and then successfully voted upon to:

- Make a PAUSE-indexed (i.e. non-dev) release of DBIx::Class
- Make changes to the master and blead branches of the repository
- Amend this document

This document is currently in bootstrap phase, and as such no merges will be
made to master or blead until this sentence is removed.

A resolution that amends the 'PAUSE release perms' list is to be assumed to
also intend the permission within PAUSE itself to be updated accordingly.

Adding or removing entries from the list of situations requiring resolutions
is absolutely a valid topic for resolutions.

A resolution may be proposed for reasons including, but not limited to:

- Force/revert/block a branch merge
- Add/remove a commit bit
- Resolve a design discussion
- Anything you like, under the assumption frivolous proposals will be
voted down naturally anyway

Merges to topic branches and similar actions that do not have a resolution
attached may be made at the discretion of those with ability to do so, but
a developer unsure if the merge will be uncontroversial is expected to ping
the list first so a vote can be called if people believe it to be required.

Rules that restrict this "ask unless you're sure" trust-by-default position
are also absolutely a valid topic for resolutions.

Resolution proposal:

A resolution is proposed by starting a new thread entitled 'PROPOSAL: ...'

A resolution must be seconded before it is voted upon.

If a VM makes or seconds a proposal, they are required to abstain from
voting upon it.

If a non-VM LS makes or seconds a proposal, no such restriction applies.

Resolution voting:

Once a proposal is seconded, the initial proposer may start a new thread
entitled 'VOTE: ...' (voting does not automatically begin after seconding
in case other feedback leads the proposer to wish to alter and re-present
their proposal).

Each VM may cast one vote, either +1, -1 or abstain.

Each non-VM LS may post +1 or -1, and the aggregate of those form the LAV.

Voting closes after 72h from when the proposal was first posted.

A resolution passes if the VM total is at least +1 and the LAV is
non-negative, to avoid requiring the list members to expend time to vote on
business as usual while still providing them a veto.

Resolution forcing:

If a resolution gains a positive LAV, but is voted down by the VMs, a force
vote may be proposed. This requires two list members who did not propose or
second the initial resolution to propose and second the force vote.

A force vote also lasts 72h, and is LAV-only. If it receives at least 25%
more +1s than -1s, the resolution passes no matter the VM vote.

This mechanism is not intended to be needed on a regular basis, but exists
to permit the list to forcibly recall a VM if they believe it to be necessary.

Once a resolution has passed, the resolution will be carried out by those with
the power to do so. It will not be reverted without a new resolution
amending or reversing the decision of the previous once.

Passed resolutions will be recorded in a RESOLUTIONS file maintained next
to this document.
37 changes: 13 additions & 24 deletions Makefile.PL
Original file line number Diff line number Diff line change
Expand Up @@ -42,33 +42,22 @@ my $runtime_requires = {
###
'DBI' => '1.57',

# on older versions first() leaks
# for the time being make it a hard dep - when we get
# rid of Sub::Name will revisit this (possibility is
# to use Devel::HideXS to force the pure-perl version
# or something like that)
'List::Util' => '1.16',

# XS (or XS-dependent) libs
'Sub::Name' => '0.04',

# pure-perl (FatPack-able) libs
'Class::Accessor::Grouped' => '0.10012',
'Class::C3::Componentised' => '1.0009',
'Class::Inspector' => '1.24',
'Context::Preserve' => '0.01',
'Data::Dumper::Concise' => '2.020',
'Data::Page' => '2.00',
'Devel::GlobalDestruction' => '0.09',
'Hash::Merge' => '0.12',
'Moo' => '2.000',
'Moo' => '2.002002',
'MRO::Compat' => '0.12',
'Module::Find' => '0.07',
'namespace::clean' => '0.24',
'Path::Class' => '0.18',
'Scope::Guard' => '0.03',
'SQL::Abstract' => '1.81',
'Try::Tiny' => '0.07',

# Technically this is not a core dependency - it is only required
# by the MySQL codepath. However this particular version is bundled
Expand All @@ -86,9 +75,14 @@ my $test_requires = {
'Test::Warn' => '0.21',
'Test::More' => '0.94',

# this is already a dep of n::c, but just in case - used by t/55namespaces_cleaned.t
# remove and do a manual glob-collection if n::c is no longer a dep
'Package::Stash' => '0.28',
# This has a bug in the caller() override, ideally we need go get rid
# of it entirely, but that's for another maint
#
# FIXME - this does protect tests, but does *NOT* protect the rest of
# DBIC itself from a faulty caller() override. Something more substantial
# needs to be done in the guts of DBIC::Carp
#
'Sub::Uplevel' => '0.19',

# needed for testing only, not for operation
# we will move away from this dep eventually, perhaps to DBD::CSV or something
Expand All @@ -99,7 +93,7 @@ my $test_requires = {
# tests will fail
# Note - these are added as test_requires *directly*, so they get properly
# excluded on META.yml cleansing (even though no dist can be created from this)
# we force these reqs regarless of author_deps, worst case scenario they will
# we force these reqs regarless of --with-optdeps, worst case scenario they will
# be specified twice
#
# also note that we *do* set dynamic_config => 0, as these are the only things
Expand Down Expand Up @@ -205,10 +199,10 @@ sub invoke_author_mode {
config => [qw/gnu_getopt bundling_override no_ignore_case pass_through/]
);
my $args = {
skip_author_deps => undef,
with_optdeps => undef,
};
$getopt->getoptions($args, qw/
skip_author_deps|skip-author-deps
with_optdeps|with-optdeps
/);
if (@ARGV) {
warn "\nIgnoring unrecognized option(s): @ARGV\n\n";
Expand Down Expand Up @@ -236,15 +230,10 @@ sub invoke_author_mode {
"\t" . $mm_proto->oneliner( qq(\$ENV{PERLIO}='unix' and system( \$^X, qw( -MExtUtils::Command -e dos2unix -- ), $targets ) ) );
};

# we are in the process of (re)writing the makefile - some things we
# call below very well may fail
local $ENV{DBICTEST_NO_MAKEFILE_VERIFICATION} = 1;

require File::Spec;
# string-eval, not do(), because we need to provide the
# $mm_proto, $reqs and $*_requires lexicals to the included file
# (some includes *do* modify $reqs above)
for my $inc (sort glob ( File::Spec->catfile('maint', 'Makefile.PL.inc', '*') ) ) {
for my $inc (sort glob ( 'maint/Makefile.PL.inc/*' ) ) {
my $src = do { local (@ARGV, $/) = $inc; <> } or die $!;
eval "use warnings; use strict; $src" or die sprintf
"Failed execution of %s: %s\n",
Expand Down
Empty file added RESOLUTIONS
Empty file.
48 changes: 48 additions & 0 deletions examples/Benchmarks/benchmark_join_optimizer.pl
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#!/usr/bin/env perl

use strict;
use warnings;

use Time::HiRes qw(gettimeofday tv_interval);
use Digest::SHA 'sha1_hex';

use lib 't/lib';
BEGIN { $ENV{DBICTEST_ANFANG_DEFANG} = 1 };
use DBICTest;

my $schema = DBICTest->init_schema(
quote_names => 1,
cursor_class => 'DBIx::Class::Cursor::Cached'
);

use Cache::FileCache;
my $c = Cache::FileCache->new({ namespace => 'SchemaClass' });

for my $i (1..9) {

my $t0 = [gettimeofday];

# getting a fresh rs makes sure we do not cache anything
my $rs = $schema->resultset("Artist")->search({},{
cache_object => $c,
cache_for => 999999999999,
prefetch => {
cds => [
( { tracks => { cd_single => { artist => { cds => { tracks => 'cd_single' } } } } } ) x 50,
],
},
rows => 2,
});

my $q = ${$rs->as_query}->[0];

print STDERR "@{[ length $q]} byte-long query generated (via as_query() in: ".tv_interval($t0) . " seconds (take $i)\n";

# stuff below can be made even faster, but another time
next;

$t0 = [ gettimeofday ];

my $x = $rs->all_hri;
print STDERR "Got collapsed results (via HRI) in: ".tv_interval($t0) . " seconds (take $i)\n";
}
5 changes: 5 additions & 0 deletions examples/Schema/MyApp/Schema.pm
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,9 @@ use strict;
use base qw/DBIx::Class::Schema/;
__PACKAGE__->load_namespaces;

# no point taxing 5.8, but otherwise leave the default: a user may
# be interested in exploring and seeing what broke
__PACKAGE__->schema_sanity_checker('')
if DBIx::Class::_ENV_::OLD_MRO;

1;
4 changes: 2 additions & 2 deletions examples/Schema/insertdb.pl
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
use warnings;

use MyApp::Schema;
use DBIx::Class::_Util 'parent_dir';

use Path::Class 'file';
my $db_fn = file($INC{'MyApp/Schema.pm'})->dir->parent->file('db/example.db');
my $db_fn = parent_dir( $INC{'MyApp/Schema.pm'} ) . '../db/example.db';

my $schema = MyApp::Schema->connect("dbi:SQLite:$db_fn");

Expand Down
4 changes: 2 additions & 2 deletions examples/Schema/testdb.pl
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
use strict;

use MyApp::Schema;
use DBIx::Class::_Util 'parent_dir';

use Path::Class 'file';
my $db_fn = file($INC{'MyApp/Schema.pm'})->dir->parent->file('db/example.db');
my $db_fn = parent_dir( $INC{'MyApp/Schema.pm'} ) . '../db/example.db';

# for other DSNs, e.g. MySql, see the perldoc for the relevant dbd
# driver, e.g perldoc L<DBD::mysql>.
Expand Down
65 changes: 16 additions & 49 deletions lib/DBIx/Class.pm
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package DBIx::Class;

# important to load early
use DBIx::Class::_Util;

use strict;
use warnings;

Expand All @@ -11,64 +14,28 @@ our $VERSION;
# $VERSION declaration must stay up here, ahead of any other package
# declarations, as to not confuse various modules attempting to determine
# this ones version, whether that be s.c.o. or Module::Metadata, etc
$VERSION = '0.082899_15';
$VERSION = '0.082899_25';

$VERSION = eval $VERSION if $VERSION =~ /_/; # numify for warning-free dev releases

use DBIx::Class::_Util;
use mro 'c3';

use DBIx::Class::Optional::Dependencies;

use base qw/DBIx::Class::Componentised DBIx::Class::AccessorGroup/;
use DBIx::Class::StartupCheck;
use DBIx::Class::Exception;

__PACKAGE__->mk_group_accessors(inherited => '_skip_namespace_frames');
__PACKAGE__->_skip_namespace_frames('^DBIx::Class|^SQL::Abstract|^Try::Tiny|^Class::Accessor::Grouped|^Context::Preserve|^Moose::Meta::');

# FIXME - this is not really necessary, and is in
# fact going to slow things down a bit
# However it is the right thing to do in order to get
# various install bases to highlight their brokenness
# Remove at some unknown point in the future
#
# The oddball BEGIN is there for... reason unknown
# It does make non-segfaulty difference on pre-5.8.5 perls, so shrug
BEGIN {
sub DESTROY { &DBIx::Class::_Util::detected_reinvoked_destructor };
}

sub mk_classdata {
shift->mk_classaccessor(@_);
}

sub mk_classaccessor {
my $self = shift;
$self->mk_group_accessors('inherited', $_[0]);
$self->set_inherited(@_) if @_ > 1;
}
__PACKAGE__->mk_classaccessor(
_skip_namespace_frames => join( '|', map { '^' . $_ } qw(
DBIx::Class
SQL::Abstract
SQL::Translator
Try::Tiny
Class::Accessor::Grouped
Context::Preserve
Moose::Meta::
)),
);

sub component_base_class { 'DBIx::Class' }

sub MODIFY_CODE_ATTRIBUTES {
my ($class,$code,@attrs) = @_;
$class->mk_classdata('__attr_cache' => {})
unless $class->can('__attr_cache');
$class->__attr_cache->{$code} = [@attrs];
return ();
}

sub _attr_cache {
my $self = shift;
my $cache = $self->can('__attr_cache') ? $self->__attr_cache : {};

return {
%$cache,
%{ $self->maybe::next::method || {} },
};
}

# *DO NOT* change this URL nor the identically named =head1 below
# it is linked throughout the ecosystem
sub DBIx::Class::_ENV_::HELP_URL () {
Expand Down Expand Up @@ -291,7 +258,7 @@ accessible at the following locations:
=item * Travis-CI log: L<https://travis-ci.org/dbsrgits/dbix-class/builds>
=for html
&#x21AA; Stable branch CI status: <img src="https://secure.travis-ci.org/dbsrgits/dbix-class.png?branch=master"></img>
&#x21AA; Bleeding edge dev CI status: <img src="https://secure.travis-ci.org/dbsrgits/dbix-class.png?branch=master"></img>
=back
Expand Down
96 changes: 83 additions & 13 deletions lib/DBIx/Class/AccessorGroup.pm
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,103 @@ package DBIx::Class::AccessorGroup;
use strict;
use warnings;

use base qw/Class::Accessor::Grouped/;
use Scalar::Util qw/weaken blessed/;
use base qw( DBIx::Class::MethodAttributes Class::Accessor::Grouped );

use Scalar::Util 'blessed';
use DBIx::Class::_Util 'fail_on_internal_call';
use namespace::clean;

my $successfully_loaded_components;
sub mk_classdata :DBIC_method_is_indirect_sugar {
DBIx::Class::_ENV_::ASSERT_NO_INTERNAL_INDIRECT_CALLS and fail_on_internal_call;
shift->mk_classaccessor(@_);
}

sub mk_classaccessor :DBIC_method_is_indirect_sugar {
my $self = shift;
$self->mk_group_accessors('inherited', $_[0]);
(@_ > 1)
? $self->set_inherited(@_)
: ( DBIx::Class::_ENV_::ASSERT_NO_INTERNAL_INDIRECT_CALLS and fail_on_internal_call )
;
}

sub mk_group_accessors {
my $class = shift;
my $type = shift;

$class->next::method($type, @_);

# label things
if( $type =~ /^ ( inflated_ | filtered_ )? column $/x ) {

$class = ref $class
if length ref $class;

for my $acc_pair (
map
{ [ $_, "_${_}_accessor" ] }
map
{ ref $_ ? $_->[0] : $_ }
@_
) {

for my $i (0, 1) {

my $acc_name = $acc_pair->[$i];

attributes->import(
$class,
(
$class->can($acc_name)
||
Carp::confess("Accessor '$acc_name' we just created on $class can't be found...?")
),
'DBIC_method_is_generated_from_resultsource_metadata',
($i
? "DBIC_method_is_${type}_extra_accessor"
: "DBIC_method_is_${type}_accessor"
),
)
}
}
}
elsif( $type eq 'inherited_ro_instance' ) {
DBIx::Class::Exception->throw(
"The 'inherted_ro_instance' CAG group has been retired - use 'inherited' instead"
);
}
}

sub get_component_class {
my $class = $_[0]->get_inherited($_[1]);

# It's already an object, just go for it.
return $class if blessed $class;

if (defined $class and ! $successfully_loaded_components->{$class} ) {
no strict 'refs';
if (
defined $class
and
# inherited CAG can't be set to undef effectively, so people may use ''
length $class
and
# It's already an object, just go for it.
! defined blessed $class
and
! ${"${class}::__LOADED__BY__DBIC__CAG__COMPONENT_CLASS__"}
) {
$_[0]->ensure_class_loaded($class);

no strict 'refs';
$successfully_loaded_components->{$class}
= ${"${class}::__LOADED__BY__DBIC__CAG__COMPONENT_CLASS__"}
= do { \(my $anon = 'loaded') };
weaken($successfully_loaded_components->{$class});
${"${class}::__LOADED__BY__DBIC__CAG__COMPONENT_CLASS__"}
= do { \(my $anon = 'loaded') };
}

$class;
};

sub set_component_class {
shift->set_inherited(@_);
$_[0]->set_inherited($_[1], $_[2]);

# trigger a load for the case of $foo->component_accessor("bar")->new
$_[0]->get_component_class($_[1])
if defined wantarray;
}

1;
Expand Down
19 changes: 14 additions & 5 deletions lib/DBIx/Class/Admin.pm
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ sub _build_schema {
my ($self) = @_;

$self->connect_info->[3]{ignore_version} = 1;
return $self->schema_class->connect(@{$self->connect_info});
return $self->schema_class->clone->connection(@{$self->connect_info});
}

=head2 resultset
Expand Down Expand Up @@ -340,7 +340,13 @@ sub create {

my $schema = $self->schema();

$schema->create_ddl_dir( $sqlt_type, (defined $schema->schema_version ? $schema->schema_version : ""), $self->sql_dir->stringify, $preversion, $sqlt_args );
$schema->create_ddl_dir(
$sqlt_type,
(defined $schema->schema_version ? $schema->schema_version : ""),
$self->sql_dir,
$preversion,
$sqlt_args,
);
}


Expand Down Expand Up @@ -474,7 +480,8 @@ sub update {
$where ||= $self->where();
$set ||= $self->set();
my $resultset = $self->schema->resultset($rs);
$resultset = $resultset->search( ($where||{}) );
$resultset = $resultset->search_rs( $where )
if $where;

my $count = $resultset->count();
print "This action will modify $count ".ref($resultset)." records.\n" if (!$self->quiet);
Expand Down Expand Up @@ -505,7 +512,8 @@ sub delete {
$where ||= $self->where();
$attrs ||= $self->attrs();
my $resultset = $self->schema->resultset($rs);
$resultset = $resultset->search( ($where||{}), ($attrs||()) );
$resultset = $resultset->search_rs( ($where||{}), ($attrs||()) )
if $where or $attrs;

my $count = $resultset->count();
print "This action will delete $count ".ref($resultset)." records.\n" if (!$self->quiet);
Expand Down Expand Up @@ -536,7 +544,8 @@ sub select {
$where ||= $self->where();
$attrs ||= $self->attrs();
my $resultset = $self->schema->resultset($rs);
$resultset = $resultset->search( ($where||{}), ($attrs||()) );
$resultset = $resultset->search_rs( ($where||{}), ($attrs||()) )
if $where or $attrs;

my @data;
my @columns = $resultset->result_source->columns();
Expand Down
2 changes: 2 additions & 0 deletions lib/DBIx/Class/CDBICompat/AbstractSearch.pm
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ package # hide form PAUSE
use strict;
use warnings;

use base 'DBIx::Class';

=head1 NAME
DBIx::Class::CDBICompat::AbstractSearch - Emulates Class::DBI::AbstractSearch
Expand Down
2 changes: 2 additions & 0 deletions lib/DBIx/Class/CDBICompat/AccessorMapping.pm
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ package # hide from PAUSE Indexer
use strict;
use warnings;

use base 'DBIx::Class';

use Scalar::Util 'blessed';
use namespace::clean;

Expand Down
4 changes: 3 additions & 1 deletion lib/DBIx/Class/CDBICompat/AttributeAPI.pm
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@ package # hide from PAUSE
use strict;
use warnings;

use base 'DBIx::Class';

sub _attrs {
my ($self, @atts) = @_;
return @{$self->{_column_data}}{@atts};
}

*_attr = \&_attrs;
sub _attr { shift->_attrs(@_) }

sub _attribute_store {
my $self = shift;
Expand Down
4 changes: 2 additions & 2 deletions lib/DBIx/Class/CDBICompat/AutoUpdate.pm
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ package # hide from PAUSE
use strict;
use warnings;

use base qw/Class::Data::Inheritable/;
use base 'DBIx::Class';

__PACKAGE__->mk_classdata('__AutoCommit');
__PACKAGE__->mk_group_accessors( inherited => '__AutoCommit' );

sub set_column {
my $self = shift;
Expand Down
6 changes: 4 additions & 2 deletions lib/DBIx/Class/CDBICompat/ColumnCase.pm
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,16 @@ package # hide from PAUSE
use strict;
use warnings;

use base 'DBIx::Class';

sub _register_column_group {
my ($class, $group, @cols) = @_;
return $class->next::method($group => map lc, @cols);
}

sub add_columns {
sub add_columns :DBIC_method_is_bypassable_resultsource_proxy {
my ($class, @cols) = @_;
return $class->result_source_instance->add_columns(map lc, @cols);
return $class->result_source->add_columns(map lc, @cols);
}

sub has_a {
Expand Down
48 changes: 31 additions & 17 deletions lib/DBIx/Class/CDBICompat/ColumnGroups.pm
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,16 @@ package # hide from PAUSE

use strict;
use warnings;
use Sub::Name ();
use List::Util ();

use base qw/DBIx::Class::Row/;

use List::Util ();
use DBIx::Class::_Util 'set_subname';
use namespace::clean;

__PACKAGE__->mk_classdata('_column_groups' => { });

sub columns {
sub columns :DBIC_method_is_bypassable_resultsource_proxy {
my $proto = shift;
my $class = ref $proto || $proto;
my $group = shift || "All";
Expand All @@ -32,9 +34,9 @@ sub _add_column_group {
$class->_register_column_group($group => @cols);
}

sub add_columns {
sub add_columns :DBIC_method_is_bypassable_resultsource_proxy {
my ($class, @cols) = @_;
$class->result_source_instance->add_columns(@cols);
$class->result_source->add_columns(@cols);
}

sub _register_column_group {
Expand Down Expand Up @@ -83,7 +85,23 @@ sub _register_column_group {

no strict 'refs';
my $existing_accessor = *{$class .'::'. $name}{CODE};
return $existing_accessor && !$our_accessors{$existing_accessor};

return(
defined $existing_accessor
and
! $our_accessors{$existing_accessor}
and
# under 5.8 mro the CODE slot may simply be a "cached method"
! (
DBIx::Class::_ENV_::OLD_MRO
and
grep {
$_ ne $class
and
( $Class::C3::MRO{$_} || {} )->{methods}{$name}
} @{mro::get_linear_isa($class)}
)
)
}

sub _deploy_accessor {
Expand All @@ -95,7 +113,7 @@ sub _register_column_group {
no strict 'refs';
no warnings 'redefine';
my $fullname = join '::', $class, $name;
*$fullname = Sub::Name::subname $fullname, $accessor;
*$fullname = set_subname $fullname, $accessor;
}

$our_accessors{$accessor}++;
Expand All @@ -121,20 +139,16 @@ sub _mk_group_accessors {

($name, $field) = @$field if ref $field;

my $accessor = $class->$maker($group, $field);
my $alias = "_${name}_accessor";

# warn " $field $alias\n";
{
no strict 'refs';

$class->_deploy_accessor($name, $accessor);
$class->_deploy_accessor($alias, $accessor);
for( $name, "_${name}_accessor" ) {
$class->_deploy_accessor(
$_,
$class->$maker($group, $field, $_)
);
}
}
}

sub all_columns { return shift->result_source_instance->columns; }
sub all_columns { return shift->result_source->columns; }

sub primary_column {
my ($class) = @_;
Expand Down
1 change: 1 addition & 0 deletions lib/DBIx/Class/CDBICompat/ColumnsAsHash.pm
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package
use strict;
use warnings;

use base 'DBIx::Class';

=head1 NAME
Expand Down
2 changes: 2 additions & 0 deletions lib/DBIx/Class/CDBICompat/Constraints.pm
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package # hide from PAUSE
DBIx::Class::CDBICompat::Constraints;

use base 'DBIx::Class';

use strict;
use warnings;

Expand Down
1 change: 1 addition & 0 deletions lib/DBIx/Class/CDBICompat/Constructor.pm
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use base 'DBIx::Class::CDBICompat::ImaDBI';

use Carp;
use DBIx::Class::_Util qw(quote_sub perlstring);
use namespace::clean;

__PACKAGE__->set_sql(Retrieve => <<'');
SELECT __ESSENTIAL__
Expand Down
3 changes: 3 additions & 0 deletions lib/DBIx/Class/CDBICompat/Copy.pm
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ package # hide from PAUSE
use strict;
use warnings;

use base 'DBIx::Class';

use Carp;
use namespace::clean;

=head1 NAME
Expand Down
3 changes: 3 additions & 0 deletions lib/DBIx/Class/CDBICompat/DestroyWarning.pm
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ package # hide from PAUSE

use strict;
use warnings;

use base 'DBIx::Class';

use DBIx::Class::_Util 'detected_reinvoked_destructor';
use namespace::clean;

Expand Down
2 changes: 1 addition & 1 deletion lib/DBIx/Class/CDBICompat/GetSet.pm
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ package # hide from PAUSE
use strict;
use warnings;

#use base qw/Class::Accessor/;
use base 'DBIx::Class';

sub get {
my ($self, @cols) = @_;
Expand Down
13 changes: 9 additions & 4 deletions lib/DBIx/Class/CDBICompat/ImaDBI.pm
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ package # hide from PAUSE
use strict;
use warnings;
use DBIx::ContextualFetch;
use DBIx::Class::_Util qw(quote_sub perlstring);

use base qw(Class::Data::Inheritable);
use base 'DBIx::Class';

use DBIx::Class::_Util qw(quote_sub perlstring);
use namespace::clean;

__PACKAGE__->mk_classdata('sql_transformer_class' =>
'DBIx::Class::CDBICompat::SQLTransformer');
Expand Down Expand Up @@ -50,9 +52,12 @@ sub sth_to_objects {

$sth->execute(@$execute_args);

my @ret;
my (@ret, $rsrc);
while (my $row = $sth->fetchrow_hashref) {
push(@ret, $class->inflate_result($class->result_source_instance, $row));
push(@ret, $class->inflate_result(
( $rsrc ||= $class->result_source ),
$row
));
}

return @ret;
Expand Down
1 change: 1 addition & 0 deletions lib/DBIx/Class/CDBICompat/Iterator.pm
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package DBIx::Class::CDBICompat::Iterator;
use strict;
use warnings;

use base 'DBIx::Class';

=head1 NAME
Expand Down
9 changes: 5 additions & 4 deletions lib/DBIx/Class/CDBICompat/LazyLoading.pm
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ package # hide from PAUSE
use strict;
use warnings;

use base 'DBIx::Class';

sub resultset_instance {
my $self = shift;
my $rs = $self->next::method(@_);
$rs = $rs->search(undef, { columns => [ $self->columns('Essential') ] });
return $rs;
$self->next::method(@_)
->search_rs(undef, { columns => [ $self->columns('Essential') ] });
}


Expand Down Expand Up @@ -96,7 +97,7 @@ sub _flesh {
my %want;
$want{$_} = 1 for map { keys %{$self->_column_groups->{$_}} } @groups;
if (my @want = grep { !exists $self->{'_column_data'}{$_} } keys %want) {
my $cursor = $self->result_source->storage->select(
my $cursor = $self->result_source->schema->storage->select(
$self->result_source->name, \@want,
\$self->_ident_cond, { bind => [ $self->_ident_values ] });
#my $sth = $self->storage->select($self->_table_name, \@want,
Expand Down
3 changes: 2 additions & 1 deletion lib/DBIx/Class/CDBICompat/LiveObjectIndex.pm
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ use strict;
use warnings;

use Scalar::Util qw/weaken/;
use namespace::clean;

use base qw/Class::Data::Inheritable/;
use base 'DBIx::Class';

__PACKAGE__->mk_classdata('purge_object_index_every' => 1000);
__PACKAGE__->mk_classdata('live_object_index' => { });
Expand Down
2 changes: 2 additions & 0 deletions lib/DBIx/Class/CDBICompat/NoObjectIndex.pm
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ package # hide from PAUSE
use strict;
use warnings;

use base 'DBIx::Class';

=head1 NAME
DBIx::Class::CDBICompat::NoObjectIndex - Defines empty methods for object indexing. They do nothing
Expand Down
4 changes: 3 additions & 1 deletion lib/DBIx/Class/CDBICompat/Pager.pm
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ use strict;
# leaving the compat layer as-is, something may in fact depend on that
use warnings FATAL => 'all';

*pager = \&page;
use base 'DBIx::Class';

sub pager { shift->page(@_) }

sub page {
my $class = shift;
Expand Down
2 changes: 2 additions & 0 deletions lib/DBIx/Class/CDBICompat/ReadOnly.pm
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ package # hide from PAUSE
use strict;
use warnings;

use base 'DBIx::Class';

sub make_read_only {
my $proto = shift;
$proto->add_trigger("before_$_" => sub { shift->throw_exception("$proto is read only") })
Expand Down
3 changes: 3 additions & 0 deletions lib/DBIx/Class/CDBICompat/Relationship.pm
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ package
use strict;
use warnings;

use base 'DBIx::Class';

use DBIx::Class::_Util 'quote_sub';
use namespace::clean;

=head1 NAME
Expand Down
27 changes: 18 additions & 9 deletions lib/DBIx/Class/CDBICompat/Relationships.pm
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ package # hide from PAUSE

use strict;
use warnings;
use base 'Class::Data::Inheritable';
use base 'DBIx::Class';

use Clone;
use DBIx::Class::CDBICompat::Relationship;
use Scalar::Util 'blessed';
use DBIx::Class::_Util qw(quote_sub perlstring);
use namespace::clean;

__PACKAGE__->mk_classdata('__meta_info' => {});

Expand Down Expand Up @@ -65,7 +66,7 @@ sub _declare_has_a {
}
else {
$self->belongs_to($col, $f_class);
$rel_info = $self->result_source_instance->relationship_info($col);
$rel_info = $self->result_source->relationship_info($col);
}

$rel_info->{args} = \%args;
Expand Down Expand Up @@ -109,14 +110,14 @@ sub has_many {

if( !$f_key and !@f_method ) {
$class->ensure_class_loaded($f_class);
my $f_source = $f_class->result_source_instance;
my $f_source = $f_class->result_source;
($f_key) = grep { $f_source->relationship_info($_)->{class} eq $class }
$f_source->relationships;
}

$class->next::method($rel, $f_class, $f_key, $args);

my $rel_info = $class->result_source_instance->relationship_info($rel);
my $rel_info = $class->result_source->relationship_info($rel);
$args->{mapping} = \@f_method;
$args->{foreign_key} = $f_key;
$rel_info->{args} = $args;
Expand All @@ -127,8 +128,13 @@ sub has_many {
);

if (@f_method) {
quote_sub "${class}::${rel}", sprintf( <<'EOC', perlstring $rel), { '$rf' => \sub { my $o = shift; $o = $o->$_ for @f_method; $o } };
my $rs = shift->search_related( %s => @_);
my @qsub_args = (
{ '$rf' => \sub { my $o = shift; $o = $o->$_ for @f_method; $o } },
{ attributes => [ 'DBIC_method_is_generated_from_resultsource_metadata' ] },
);

quote_sub "${class}::${rel}", sprintf( <<'EOC', perlstring $rel), @qsub_args;
my $rs = shift->related_resultset(%s)->search_rs( @_);
$rs->{attrs}{record_filter} = $rf;
return (wantarray ? $rs->all : $rs);
EOC
Expand All @@ -149,7 +155,7 @@ sub might_have {
{ proxy => \@columns });
}

my $rel_info = $class->result_source_instance->relationship_info($rel);
my $rel_info = $class->result_source->relationship_info($rel);
$rel_info->{args}{import} = \@columns;

$class->_extend_meta(
Expand Down Expand Up @@ -193,7 +199,7 @@ sub meta_info {
sub search {
my $self = shift;
my $attrs = {};
if (@_ > 1 && ref $_[$#_] eq 'HASH') {
if (@_ > 1 && ref $_[-1] eq 'HASH') {
$attrs = { %{ pop(@_) } };
}
my $where = (@_ ? ((@_ == 1) ? ((ref $_[0] eq "HASH") ? { %{+shift} } : shift)
Expand All @@ -212,7 +218,10 @@ sub search {
}

sub new_related {
return shift->search_related(shift)->new_result(shift);
$_[0]->throw_exception("Calling new_related() as a class method is not supported")
unless length ref $_[0];

shift->next::method(@_);
}

=head1 FURTHER QUESTIONS?
Expand Down
2 changes: 2 additions & 0 deletions lib/DBIx/Class/CDBICompat/Retrieve.pm
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ use strict;
# leaving the compat layer as-is, something may in fact depend on that
use warnings FATAL => 'all';

use base 'DBIx::Class';

sub retrieve {
my $self = shift;
die "No args to retrieve" unless @_ > 0;
Expand Down
2 changes: 2 additions & 0 deletions lib/DBIx/Class/CDBICompat/SQLTransformer.pm
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package DBIx::Class::CDBICompat::SQLTransformer;
use strict;
use warnings;

use base 'DBIx::Class';

=head1 NAME
DBIx::Class::CDBICompat::SQLTransformer - Transform SQL
Expand Down
2 changes: 1 addition & 1 deletion lib/DBIx/Class/CDBICompat/Stringify.pm
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ package # hide from PAUSE
use strict;
use warnings;

use Scalar::Util;
use base 'DBIx::Class';

use overload
'""' => sub { return shift->stringify_self; },
Expand Down
3 changes: 2 additions & 1 deletion lib/DBIx/Class/CDBICompat/TempColumns.pm
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ package # hide from PAUSE

use strict;
use warnings;
use base qw/Class::Data::Inheritable/;
use base 'DBIx::Class';

use Carp;
use namespace::clean;

__PACKAGE__->mk_classdata('_temp_columns' => { });

Expand Down
3 changes: 3 additions & 0 deletions lib/DBIx/Class/CDBICompat/Triggers.pm
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ package # hide from PAUSE

use strict;
use warnings;

use base 'DBIx::Class';

use Class::Trigger;

sub insert {
Expand Down
57 changes: 51 additions & 6 deletions lib/DBIx/Class/Carp.pm
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,67 @@ use warnings;
use Carp ();
$Carp::Internal{ (__PACKAGE__) }++;

use Scalar::Util ();

# Because... sigh
# There are cases out there where a user provides a can() that won't actually
# work as perl intends it. Since this is a reporting library, we *have* to be
# extra paranoid ( e.g. https://rt.cpan.org/Ticket/Display.html?id=90715 )
sub __safe_can ($$) {
local $@;
local $SIG{__DIE__} if $SIG{__DIE__};

my $cref;
eval {
$cref = $_[0]->can( $_[1] );

# in case the can() isn't an actual UNIVERSAL::can()
die "Return value of $_[0]" . "->can(q($_[1])) is true yet not a code reference...\n"
if $cref and Scalar::Util::reftype($cref) ne 'CODE';

1;
} or do {
undef $cref;

# can not use DBIC::_Util::emit_loud_diag - it uses us internally
printf STDERR
"\n$0: !!! INTERNAL PANIC !!!\nClass '%s' implements or inherits a broken can() - PLEASE FIX ASAP!: %s\n\n",
( length ref $_[0] ? ref $_[0] : $_[0] ),
$@,
;
};

$cref;
}

sub __find_caller {
my ($skip_pattern, $class) = @_;

my $skip_class_data = $class->_skip_namespace_frames
if ($class and $class->can('_skip_namespace_frames'));
if ($class and __safe_can($class, '_skip_namespace_frames') );

$skip_pattern = qr/$skip_pattern|$skip_class_data/
if $skip_class_data;

my $fr_num = 1; # skip us and the calling carp*

my (@f, $origin);
my (@f, $origin, $eval_src);
while (@f = CORE::caller($fr_num++)) {

next if
( $f[3] eq '(eval)' or $f[3] =~ /::__ANON__$/ );
undef $eval_src;

next if (
$f[2] == 0
or
# there is no value reporting a sourceless eval frame
(
( $f[3] eq '(eval)' or $f[1] =~ /^\(eval \d+\)$/ )
and
not defined ( $eval_src = (CORE::caller($fr_num))[6] )
)
or
$f[3] =~ /::__ANON__$/
);

$origin ||= (
$f[3] =~ /^ (.+) :: ([^\:]+) $/x
Expand All @@ -40,7 +85,7 @@ sub __find_caller {
) ? $f[3] : undef;

if (
$f[0]->can('_skip_namespace_frames')
__safe_can( $f[0], '_skip_namespace_frames' )
and
my $extra_skip = $f[0]->_skip_namespace_frames
) {
Expand All @@ -51,7 +96,7 @@ sub __find_caller {
}

my $site = @f # if empty - nothing matched - full stack
? "at $f[1] line $f[2]"
? ( "at $f[1] line $f[2]" . ( $eval_src ? "\n === BEGIN $f[1]\n$eval_src\n === END $f[1]" : '' ) )
: Carp::longmess()
;

Expand Down
4 changes: 2 additions & 2 deletions lib/DBIx/Class/Componentised.pm
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use warnings;
use base 'Class::C3::Componentised';
use mro 'c3';

use DBIx::Class::_Util 'get_subname';
use DBIx::Class::Carp '^DBIx::Class|^Class::C3::Componentised';
use namespace::clean;

Expand Down Expand Up @@ -54,8 +55,7 @@ sub inject_base {
or next;

if ($sc ne $base_store_column) {
require B;
my $definer = B::svref_2object($sc)->STASH->NAME;
my ($definer) = get_subname($sc);
push @broken, ($definer eq $existing_comp)
? $existing_comp
: "$existing_comp (via $definer)"
Expand Down
17 changes: 8 additions & 9 deletions lib/DBIx/Class/DB.pm
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ it. See resolve_class below.
=cut

__PACKAGE__->mk_classdata('class_resolver' =>
__PACKAGE__->mk_classaccessor('class_resolver' =>
'DBIx::Class::ClassResolver::PassThrough');

=begin hidden
Expand Down Expand Up @@ -101,7 +101,7 @@ sub setup_schema_instance {
my $class = shift;
my $schema = {};
bless $schema, 'DBIx::Class::Schema';
$class->mk_classdata('schema_instance' => $schema);
$class->mk_classaccessor('schema_instance' => $schema);
}

=begin hidden
Expand Down Expand Up @@ -176,7 +176,7 @@ native L<DBIx::Class::ResultSet> system.
=cut

sub resultset_instance {
$_[0]->result_source_instance->resultset
$_[0]->result_source->resultset
}

=begin hidden
Expand All @@ -189,12 +189,12 @@ Returns an instance of the result source for this class
=cut

__PACKAGE__->mk_classdata('_result_source_instance' => []);
__PACKAGE__->mk_classaccessor('_result_source_instance' => []);

# Yep. this is horrific. Basically what's happening here is that
# (with good reason) DBIx::Class::Schema copies the result source for
# registration. Because we have a retarded setup order forced on us we need
# to actually make our ->result_source_instance -be- the source used, and we
# to actually make our ->result_source -be- the source used, and we
# need to get the source name and schema into ourselves. So this makes it
# happen.

Expand Down Expand Up @@ -222,15 +222,14 @@ sub result_source_instance {
}

my($source, $result_class) = @{$class->_result_source_instance};
return unless blessed $source;
return undef unless blessed $source;

if ($result_class ne $class) { # new class
# Give this new class its own source and register it.
$source = $source->new({
%$source,
$source = $source->clone(
source_name => $class,
result_class => $class
} );
);
$class->_result_source_instance([$source, $class]);
$class->_maybe_attach_source_to_schema($source);
}
Expand Down
41 changes: 27 additions & 14 deletions lib/DBIx/Class/FilterColumn.pm
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,11 @@ use namespace::clean;
sub filter_column {
my ($self, $col, $attrs) = @_;

my $colinfo = $self->column_info($col);
my $colinfo = $self->result_source->columns_info([$col])->{$col};

$self->throw_exception("FilterColumn can not be used on a column with a declared InflateColumn inflator")
if defined $colinfo->{_inflate_info} and $self->isa('DBIx::Class::InflateColumn');

$self->throw_exception("No such column $col to filter")
unless $self->has_column($col);

$self->throw_exception('filter_column expects a hashref of filter specifications')
unless ref $attrs eq 'HASH';

Expand All @@ -34,8 +31,7 @@ sub _column_from_storage {

return $value if is_literal_value($value);

my $info = $self->result_source->column_info($col)
or $self->throw_exception("No column info for $col");
my $info = $self->result_source->columns_info([$col])->{$col};

return $value unless exists $info->{_filter_info};

Expand All @@ -49,8 +45,7 @@ sub _column_to_storage {

return $value if is_literal_value($value);

my $info = $self->result_source->column_info($col) or
$self->throw_exception("No column info for $col");
my $info = $self->result_source->columns_info([$col])->{$col};

return $value unless exists $info->{_filter_info};

Expand All @@ -63,7 +58,7 @@ sub get_filtered_column {
my ($self, $col) = @_;

$self->throw_exception("$col is not a filtered column")
unless exists $self->result_source->column_info($col)->{_filter_info};
unless exists $self->result_source->columns_info->{$col}{_filter_info};

return $self->{_filtered_column}{$col}
if exists $self->{_filtered_column}{$col};
Expand All @@ -78,11 +73,13 @@ sub get_filtered_column {
sub get_column {
my ($self, $col) = @_;

if (exists $self->{_filtered_column}{$col}) {
return $self->{_column_data}{$col} ||= $self->_column_to_storage (
$col, $self->{_filtered_column}{$col}
);
}
! exists $self->{_column_data}{$col}
and
exists $self->{_filtered_column}{$col}
and
$self->{_column_data}{$col} = $self->_column_to_storage (
$col, $self->{_filtered_column}{$col}
);

return $self->next::method ($col);
}
Expand All @@ -101,6 +98,22 @@ sub get_columns {
$self->next::method (@_);
}

# and *another* separate codepath, argh!
sub get_dirty_columns {
my $self = shift;

$self->{_dirty_columns}{$_}
and
! exists $self->{_column_data}{$_}
and
$self->{_column_data}{$_} = $self->_column_to_storage (
$_, $self->{_filtered_column}{$_}
)
for keys %{$self->{_filtered_column}||{}};

$self->next::method(@_);
}

sub store_column {
my ($self, $col) = (shift, @_);

Expand Down
13 changes: 5 additions & 8 deletions lib/DBIx/Class/InflateColumn.pm
Original file line number Diff line number Diff line change
Expand Up @@ -87,15 +87,14 @@ L<DBIx::Class::DateTime::Epoch>
sub inflate_column {
my ($self, $col, $attrs) = @_;

my $colinfo = $self->column_info($col);
my $colinfo = $self->result_source->columns_info([$col])->{$col};

$self->throw_exception("InflateColumn can not be used on a column with a declared FilterColumn filter")
if defined $colinfo->{_filter_info} and $self->isa('DBIx::Class::FilterColumn');

$self->throw_exception("No such column $col to inflate")
unless $self->has_column($col);
$self->throw_exception("inflate_column needs attr hashref")
unless ref $attrs eq 'HASH';

$colinfo->{_inflate_info} = $attrs;
my $acc = $colinfo->{accessor};
$self->mk_group_accessors('inflated_column' => [ (defined $acc ? $acc : $col), $col]);
Expand All @@ -111,8 +110,7 @@ sub _inflated_column {
is_literal_value($value) #that would be a not-yet-reloaded literal update
);

my $info = $self->result_source->column_info($col)
or $self->throw_exception("No column info for $col");
my $info = $self->result_source->columns_info([$col])->{$col};

return $value unless exists $info->{_inflate_info};

Expand All @@ -133,8 +131,7 @@ sub _deflated_column {
is_literal_value($value)
);

my $info = $self->result_source->column_info($col) or
$self->throw_exception("No column info for $col");
my $info = $self->result_source->columns_info([$col])->{$col};

return $value unless exists $info->{_inflate_info};

Expand All @@ -160,7 +157,7 @@ sub get_inflated_column {
my ($self, $col) = @_;

$self->throw_exception("$col is not an inflated column")
unless exists $self->result_source->column_info($col)->{_inflate_info};
unless exists $self->result_source->columns_info->{$col}{_inflate_info};

# we take care of keeping things in sync
return $self->{_inflated_column}{$col}
Expand Down
58 changes: 38 additions & 20 deletions lib/DBIx/Class/InflateColumn/DateTime.pm
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ use strict;
use warnings;
use base qw/DBIx::Class/;
use DBIx::Class::Carp;
use DBIx::Class::_Util 'dbic_internal_try';
use Try::Tiny;
use DBIx::Class::_Util qw( dbic_internal_try dbic_internal_catch );
use namespace::clean;

=head1 NAME
Expand All @@ -31,12 +30,19 @@ Then you can treat the specified column as a L<DateTime> object.
print "This event starts the month of ".
$event->starts_when->month_name();
If you want to set a specific timezone and locale for that field, use:
If you want to set a specific time zone and locale for that field, use:
__PACKAGE__->add_columns(
starts_when => { data_type => 'datetime', timezone => "America/Chicago", locale => "de_DE" }
starts_when => { data_type => 'datetime', time_zone => "America/Chicago", locale => "de_DE" }
);
Note: DBIC before 0.082900 only accepted C<timezone>, and silently discarded
any C<time_zone> arguments. For backwards compatibility, C<timezone> will
continue being accepted as a synonym for C<time_zone>, and the value will
continue to be available in the
L<< C<column_info> hash|DBIx::Class::ResultSource/column_info >>
under both names.
If you want to inflate no matter what data_type your column is,
use inflate_datetime or inflate_date:
Expand Down Expand Up @@ -73,7 +79,7 @@ that this feature is new as of 0.07, so it may not be perfect yet - bug
reports to the list very much welcome).
If the data_type of a field is C<date>, C<datetime> or C<timestamp> (or
a derivative of these datatypes, e.g. C<timestamp with timezone>), this
a derivative of these datatypes, e.g. C<timestamp with time zone>), this
module will automatically call the appropriate parse/format method for
deflation/inflation as defined in the storage class. For instance, for
a C<datetime> field the methods C<parse_datetime> and C<format_datetime>
Expand Down Expand Up @@ -152,7 +158,7 @@ sub register_column {
}

if ($info->{extra}) {
for my $slot (qw/timezone locale floating_tz_ok/) {
for my $slot (qw/time_zone timezone locale floating_tz_ok/) {
if ( defined $info->{extra}{$slot} ) {
carp "Putting $slot into extra => { $slot => '...' } has been deprecated, ".
"please put it directly into the '$column' column definition.";
Expand All @@ -161,6 +167,17 @@ sub register_column {
}
}

# Store the time zone under both 'timezone' for backwards compatibility and
# 'time_zone' for DateTime ecosystem consistency
if ( defined $info->{timezone} ) {
$self->throw_exception("Conflicting 'timezone' and 'time_zone' values in '$column' column defintion.")
if defined $info->{time_zone} and $info->{time_zone} ne $info->{timezone};
$info->{time_zone} = $info->{timezone};
}
elsif ( defined $info->{time_zone} ) {
$info->{timezone} = $info->{time_zone};
}

# shallow copy to avoid unfounded(?) Devel::Cycle complaints
my $infcopy = {%$info};

Expand Down Expand Up @@ -198,12 +215,13 @@ sub _flate_or_fallback
my $preferred_method = sprintf($method_fmt, $info->{ _ic_dt_method });
my $method = $parser->can($preferred_method) || sprintf($method_fmt, 'datetime');

return dbic_internal_try {
dbic_internal_try {
$parser->$method($value);
}
catch {
dbic_internal_catch {
$self->throw_exception ("Error while inflating '$value' for $info->{__dbic_colname} on ${self}: $_")
unless $info->{datetime_undef_if_invalid};

undef; # rv
};
}
Expand All @@ -219,13 +237,13 @@ sub _deflate_from_datetime {
}

sub _datetime_parser {
shift->result_source->storage->datetime_parser (@_);
shift->result_source->schema->storage->datetime_parser (@_);
}

sub _post_inflate_datetime {
my( $self, $dt, $info ) = @_;

$dt->set_time_zone($info->{timezone}) if defined $info->{timezone};
$dt->set_time_zone($info->{time_zone}) if defined $info->{time_zone};
$dt->set_locale($info->{locale}) if defined $info->{locale};

return $dt;
Expand All @@ -234,14 +252,14 @@ sub _post_inflate_datetime {
sub _pre_deflate_datetime {
my( $self, $dt, $info ) = @_;

if (defined $info->{timezone}) {
carp "You're using a floating timezone, please see the documentation of"
if (defined $info->{time_zone}) {
carp "You're using a floating time zone, please see the documentation of"
. " DBIx::Class::InflateColumn::DateTime for an explanation"
if ref( $dt->time_zone ) eq 'DateTime::TimeZone::Floating'
and not $info->{floating_tz_ok}
and not $ENV{DBIC_FLOATING_TZ_OK};

$dt->set_time_zone($info->{timezone});
$dt->set_time_zone($info->{time_zone});
}

$dt->set_locale($info->{locale}) if defined $info->{locale};
Expand All @@ -254,13 +272,13 @@ __END__
=head1 USAGE NOTES
If you have a datetime column with an associated C<timezone>, and subsequently
If you have a datetime column with an associated C<time_zone>, and subsequently
create/update this column with a DateTime object in the L<DateTime::TimeZone::Floating>
timezone, you will get a warning (as there is a very good chance this will not have the
time zone, you will get a warning (as there is a very good chance this will not have the
result you expect). For example:
__PACKAGE__->add_columns(
starts_when => { data_type => 'datetime', timezone => "America/Chicago" }
starts_when => { data_type => 'datetime', time_zone => "America/Chicago" }
);
my $event = $schema->resultset('EventTZ')->create({
Expand All @@ -273,7 +291,7 @@ The warning can be avoided in several ways:
=item Fix your broken code
When calling C<set_time_zone> on a Floating DateTime object, the timezone is simply
When calling C<set_time_zone> on a Floating DateTime object, the time zone is simply
set to the requested value, and B<no time conversion takes place>. It is always a good idea
to be supply explicit times to the database:
Expand All @@ -284,7 +302,7 @@ to be supply explicit times to the database:
=item Suppress the check on per-column basis
__PACKAGE__->add_columns(
starts_when => { data_type => 'datetime', timezone => "America/Chicago", floating_tz_ok => 1 }
starts_when => { data_type => 'datetime', time_zone => "America/Chicago", floating_tz_ok => 1 }
);
=item Suppress the check globally
Expand All @@ -293,7 +311,7 @@ Set the environment variable DBIC_FLOATING_TZ_OK to some true value.
=back
Putting extra attributes like timezone, locale or floating_tz_ok into extra => {} has been
Putting extra attributes like time_zone, locale or floating_tz_ok into extra => {} has been
B<DEPRECATED> because this gets you into trouble using L<DBIx::Class::Schema::Versioned>.
Instead put it directly into the columns definition like in the examples above. If you still
use the old way you'll see a warning - please fix your code then!
Expand All @@ -305,7 +323,7 @@ use the old way you'll see a warning - please fix your code then!
=item More information about the add_columns method, and column metadata,
can be found in the documentation for L<DBIx::Class::ResultSource>.
=item Further discussion of problems inherent to the Floating timezone:
=item Further discussion of problems inherent to the Floating time zone:
L<Floating DateTimes|DateTime/Floating DateTimes>
and L<< $dt->set_time_zone|DateTime/"Set" Methods >>
Expand Down
18 changes: 12 additions & 6 deletions lib/DBIx/Class/InflateColumn/File.pm
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,17 @@ package DBIx::Class::InflateColumn::File;

use strict;
use warnings;

# check deps
BEGIN {
require DBIx::Class::Optional::Dependencies;
if (my $missing = DBIx::Class::Optional::Dependencies->req_missing_for ('ic_file') ) {
die "The following extra modules are required for DBIx::Class::InflateColumn::File: $missing\n";
}
}

use base 'DBIx::Class';
use File::Path;
use File::Copy;
use Path::Class;
use DBIx::Class::Carp;
use namespace::clean;

Expand All @@ -20,7 +27,6 @@ carp 'InflateColumn::File has entered a deprecation cycle. This component '
unless $ENV{DBIC_IC_FILE_NOWARN};



__PACKAGE__->load_components(qw/InflateColumn/);

sub register_column {
Expand All @@ -43,7 +49,7 @@ sub register_column {
sub _file_column_file {
my ($self, $column, $filename) = @_;

my $column_info = $self->result_source->column_info($column);
my $column_info = $self->result_source->columns_info->{$column};

return unless $column_info->{is_file_column};

Expand All @@ -68,7 +74,7 @@ sub delete {

for ( keys %$colinfos ) {
if ( $colinfos->{$_}{is_file_column} ) {
rmtree( [$self->_file_column_file($_)->dir], 0, 0 );
$self->_file_column_file($_)->dir->rmtree;
last; # if we've deleted one, we've deleted them all
}
}
Expand Down Expand Up @@ -116,7 +122,7 @@ sub _save_file_column {
return unless ref $value;

my $fs_file = $self->_file_column_file($column, $value->{filename});
mkpath [$fs_file->dir];
$fs_file->dir->mkpath;

# File::Copy doesn't like Path::Class (or any for that matter) objects,
# thus ->stringify (http://rt.perl.org/rt3/Public/Bug/Display.html?id=59650)
Expand Down
4 changes: 2 additions & 2 deletions lib/DBIx/Class/Manual/Cookbook.pod
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,9 @@ almost like you would define a regular ResultSource.
#

# do not attempt to deploy() this view
__PACKAGE__->result_source_instance->is_virtual(1);
__PACKAGE__->result_source->is_virtual(1);

__PACKAGE__->result_source_instance->view_definition(q[
__PACKAGE__->result_source->view_definition(q[
SELECT u.* FROM user u
INNER JOIN user_friends f ON u.id = f.user_id
WHERE f.friend_user_id = ?
Expand Down
444 changes: 444 additions & 0 deletions lib/DBIx/Class/MethodAttributes.pm

Large diffs are not rendered by default.

26 changes: 18 additions & 8 deletions lib/DBIx/Class/Optional/Dependencies.pm
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,16 @@ my $dbic_reqs = {
},
},

ic_file => {
req => {
'Path::Class' => '0.18',
},
pod => {
title => 'DBIx::Class::InflateColumn::File (Deprecated)',
desc => 'Modules required for the deprecated L<DBIx::Class::InflateColumn::File>',
},
},

ic_dt => {
req => {
'DateTime' => '0.55',
Expand All @@ -168,7 +178,6 @@ my $dbic_reqs = {

cdbicompat => {
req => {
'Class::Data::Inheritable' => '0',
'Class::Trigger' => '0',
'DBIx::ContextualFetch' => '0',
'Clone' => '0.32',
Expand Down Expand Up @@ -861,7 +870,9 @@ sub skip_without {
if ( my $err = $self->req_missing_for($groups) ) {
my ($fn, $ln) = (caller(0))[1,2];
$tb->skip("block in $fn around line $ln requires $err");
local $^W = 0;

BEGIN { ${^WARNING_BITS} = "" }

last SKIP;
}

Expand Down Expand Up @@ -1144,6 +1155,9 @@ sub _errorlist_for_modreqs {
my $v = $reqs->{$m};

if (! exists $req_unavailability_cache{$m}{$v} ) {
# masking this off is important, as it may very well be
# a transient error
local $SIG{__DIE__} if $SIG{__DIE__};
local $@;
eval( "require $m;" . ( $v ? "$m->VERSION(q($v))" : '' ) );
$req_unavailability_cache{$m}{$v} = $@;
Expand Down Expand Up @@ -1204,16 +1218,12 @@ sub _gen_pod {
"\n\n---------------------------------------------------------------------\n"
;

# do not ask for a recent version, use 1.x API calls
# this *may* execute on a smoker with old perl or whatnot
require File::Path;

(my $modfn = __PACKAGE__ . '.pm') =~ s|::|/|g;

(my $podfn = "$pod_dir/$modfn") =~ s/\.pm$/\.pod/;
(my $dir = $podfn) =~ s|/[^/]+$||;

File::Path::mkpath([$dir]);
require DBIx::Class::_Util;
DBIx::Class::_Util::mkdir_p( DBIx::Class::_Util::parent_dir( $podfn ) );

my $sqltver = $class->req_list_for('deploy')->{'SQL::Translator'}
or die "Hrmm? No sqlt dep?";
Expand Down
175 changes: 125 additions & 50 deletions lib/DBIx/Class/Ordered.pm
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use strict;
use warnings;
use base qw( DBIx::Class );

use List::Util 'first';
use DBIx::Class::_Util qw( bag_eq fail_on_internal_call );
use namespace::clean;

=head1 NAME
Expand Down Expand Up @@ -109,7 +109,7 @@ positional value of each record. Defaults to "position".
=cut

__PACKAGE__->mk_classdata( 'position_column' => 'position' );
__PACKAGE__->mk_classaccessor( 'position_column' => 'position' );

=head2 grouping_column
Expand All @@ -121,7 +121,7 @@ ordered lists within the same table.
=cut

__PACKAGE__->mk_classdata( 'grouping_column' );
__PACKAGE__->mk_group_accessors( inherited => 'grouping_column' );

=head2 null_position_value
Expand All @@ -136,7 +136,7 @@ indeed start from 0.
=cut

__PACKAGE__->mk_classdata( 'null_position_value' => 0 );
__PACKAGE__->mk_classaccessor( 'null_position_value' => 0 );

=head2 siblings
Expand All @@ -146,13 +146,28 @@ __PACKAGE__->mk_classdata( 'null_position_value' => 0 );
Returns an B<ordered> resultset of all other objects in the same
group excluding the one you called it on.
Underneath calls L<DBIx::Class::ResultSet/search>, and therefore returns
objects by implicitly invoking L<C<< ->all() >>|DBIx::Class::ResultSet/all>
in list context.
The ordering is a backwards-compatibility artifact - if you need
a resultset with no ordering applied use C<_siblings>
=cut

sub siblings {
my $self = shift;
return $self->_siblings->search ({}, { order_by => $self->position_column } );
#my $self = shift;

DBIx::Class::_ENV_::ASSERT_NO_INTERNAL_INDIRECT_CALLS
and
wantarray
and
! eval { fail_on_internal_call; 1 }
and
die "ILLEGAL LIST CONTEXT INVOCATION: $@";

# *MUST* be context sensitive due to legacy (DO NOT call search_rs)
$_[0]->_siblings->search ({}, { order_by => $_[0]->position_column } );
}

=head2 previous_siblings
Expand All @@ -163,15 +178,29 @@ sub siblings {
Returns a resultset of all objects in the same group
positioned before the object on which this method was called.
Underneath calls L<DBIx::Class::ResultSet/search>, and therefore returns
objects by implicitly invoking L<C<< ->all() >>|DBIx::Class::ResultSet/all>
in list context.
=cut
sub previous_siblings {
my $self = shift;
my $position_column = $self->position_column;
my $position = $self->get_column ($position_column);
return ( defined $position

DBIx::Class::_ENV_::ASSERT_NO_INTERNAL_INDIRECT_CALLS
and
wantarray
and
! eval { fail_on_internal_call; 1 }
and
die "ILLEGAL LIST CONTEXT INVOCATION: $@";

# *MUST* be context sensitive due to legacy (DO NOT call search_rs)
defined( $position )
? $self->_siblings->search ({ $position_column => { '<', $position } })
: $self->_siblings
);
;
}

=head2 next_siblings
Expand All @@ -182,15 +211,29 @@ sub previous_siblings {
Returns a resultset of all objects in the same group
positioned after the object on which this method was called.
Underneath calls L<DBIx::Class::ResultSet/search>, and therefore returns
objects by implicitly invoking L<C<< ->all() >>|DBIx::Class::ResultSet/all>
in list context.
=cut
sub next_siblings {
my $self = shift;
my $position_column = $self->position_column;
my $position = $self->get_column ($position_column);
return ( defined $position

DBIx::Class::_ENV_::ASSERT_NO_INTERNAL_INDIRECT_CALLS
and
wantarray
and
! eval { fail_on_internal_call; 1 }
and
die "ILLEGAL LIST CONTEXT INVOCATION: $@";

# *MUST* be context sensitive due to legacy (DO NOT call search_rs)
defined( $position )
? $self->_siblings->search ({ $position_column => { '>', $position } })
: $self->_siblings
);
;
}

=head2 previous_sibling
Expand All @@ -206,12 +249,12 @@ sub previous_sibling {
my $self = shift;
my $position_column = $self->position_column;

my $psib = $self->previous_siblings->search(
my $psib = $self->previous_siblings->search_rs(
{},
{ rows => 1, order_by => { '-desc' => $position_column } },
)->single;

return defined $psib ? $psib : 0;
return defined( $psib ) ? $psib : 0;
}

=head2 first_sibling
Expand All @@ -227,12 +270,12 @@ sub first_sibling {
my $self = shift;
my $position_column = $self->position_column;

my $fsib = $self->previous_siblings->search(
my $fsib = $self->previous_siblings->search_rs(
{},
{ rows => 1, order_by => { '-asc' => $position_column } },
)->single;

return defined $fsib ? $fsib : 0;
return defined( $fsib ) ? $fsib : 0;
}

=head2 next_sibling
Expand All @@ -247,12 +290,12 @@ if the current object is the last one.
sub next_sibling {
my $self = shift;
my $position_column = $self->position_column;
my $nsib = $self->next_siblings->search(
my $nsib = $self->next_siblings->search_rs(
{},
{ rows => 1, order_by => { '-asc' => $position_column } },
)->single;

return defined $nsib ? $nsib : 0;
return defined( $nsib ) ? $nsib : 0;
}

=head2 last_sibling
Expand All @@ -267,26 +310,25 @@ sibling.
sub last_sibling {
my $self = shift;
my $position_column = $self->position_column;
my $lsib = $self->next_siblings->search(
my $lsib = $self->next_siblings->search_rs(
{},
{ rows => 1, order_by => { '-desc' => $position_column } },
)->single;

return defined $lsib ? $lsib : 0;
return defined( $lsib ) ? $lsib : 0;
}

# an optimized method to get the last sibling position value without inflating a result object
sub _last_sibling_posval {
my $self = shift;
my $position_column = $self->position_column;

my $cursor = $self->next_siblings->search(
my $cursor = $self->next_siblings->search_rs(
{},
{ rows => 1, order_by => { '-desc' => $position_column }, select => $position_column },
)->cursor;

my ($pos) = $cursor->next;
return $pos;
($cursor->next)[0];
}

=head2 move_previous
Expand Down Expand Up @@ -367,8 +409,10 @@ sub move_to {

my $position_column = $self->position_column;

my $rsrc = $self->result_source;

my $is_txn;
if ($is_txn = $self->result_source->schema->storage->transaction_depth) {
if ($is_txn = $rsrc->schema->storage->transaction_depth) {
# Reload position state from storage
# The thinking here is that if we are in a transaction, it is
# *more likely* the object went out of sync due to resultset
Expand All @@ -378,9 +422,8 @@ sub move_to {

$self->store_column(
$position_column,
( $self->result_source
->resultset
->search($self->_storage_ident_condition, { rows => 1, columns => $position_column })
( $rsrc->resultset
->search_rs($self->_storage_ident_condition, { rows => 1, columns => $position_column })
->cursor
->next
)[0] || $self->throw_exception(
Expand All @@ -403,7 +446,7 @@ sub move_to {
return 0;
}

my $guard = $is_txn ? undef : $self->result_source->schema->txn_scope_guard;
my $guard = $is_txn ? undef : $rsrc->schema->txn_scope_guard;

my ($direction, @between);
if ( $from_position < $to_position ) {
Expand All @@ -418,7 +461,7 @@ sub move_to {
my $new_pos_val = $self->_position_value ($to_position); # record this before the shift

# we need to null-position the moved row if the position column is part of a constraint
if (grep { $_ eq $position_column } ( map { @$_ } (values %{{ $self->result_source->unique_constraints }} ) ) ) {
if (grep { $_ eq $position_column } ( map { @$_ } (values %{{ $rsrc->unique_constraints }} ) ) ) {
$self->_ordered_internal_update({ $position_column => $self->null_position_value });
}

Expand Down Expand Up @@ -564,7 +607,7 @@ sub update {
if (! keys %$changed_ordering_cols) {
return $self->next::method( undef, @_ );
}
elsif (defined first { exists $changed_ordering_cols->{$_} } @group_columns ) {
elsif (grep { exists $changed_ordering_cols->{$_} } @group_columns ) {
$self->move_to_group(
# since the columns are already re-set the _grouping_clause is correct
# move_to_group() knows how to get the original storage values
Expand Down Expand Up @@ -614,7 +657,11 @@ sub delete {
# add the current position/group to the things we track old values for
sub _track_storage_value {
my ($self, $col) = @_;
return $self->next::method($col) || defined first { $_ eq $col } ($self->position_column, $self->_grouping_columns);
return (
$self->next::method($col)
||
grep { $_ eq $col } ($self->position_column, $self->_grouping_columns)
);
}

=head1 METHODS FOR EXTENDING ORDERED
Expand Down Expand Up @@ -678,7 +725,7 @@ L</_next_position_value> below. Defaults to 1.
=cut

__PACKAGE__->mk_classdata( '_initial_position_value' => 1 );
__PACKAGE__->mk_classaccessor( '_initial_position_value' => 1 );

=head2 _next_position_value
Expand Down Expand Up @@ -728,7 +775,7 @@ sub _shift_siblings {
$ord = 'desc';
}

my $shift_rs = $self->_group_rs-> search ({ $position_column => { -between => \@between } });
my $shift_rs = $self->_group_rs-> search_rs ({ $position_column => { -between => \@between } });

# some databases (sqlite, pg, perhaps others) are dumb and can not do a
# blanket increment/decrement without violating a unique constraint.
Expand All @@ -740,11 +787,11 @@ sub _shift_siblings {
local $rsrc->schema->{_ORDERED_INTERNAL_UPDATE} = 1;
my @pcols = $rsrc->primary_columns;
if (
first { $_ eq $position_column } ( map { @$_ } (values %{{ $rsrc->unique_constraints }} ) )
grep { $_ eq $position_column } ( map { @$_ } (values %{{ $rsrc->unique_constraints }} ) )
) {
my $clean_rs = $rsrc->resultset;

for ( $shift_rs->search (
for ( $shift_rs->search_rs (
{}, { order_by => { "-$ord", $position_column }, select => [$position_column, @pcols] }
)->cursor->all ) {
my $pos = shift @$_;
Expand All @@ -760,8 +807,18 @@ sub _shift_siblings {
# This method returns a resultset containing all members of the row
# group (including the row itself).
sub _group_rs {
my $self = shift;
return $self->result_source->resultset->search({$self->_grouping_clause()});
#my $self = shift;

DBIx::Class::_ENV_::ASSERT_NO_INTERNAL_INDIRECT_CALLS
and
wantarray
and
! eval { fail_on_internal_call; 1 }
and
die "ILLEGAL LIST CONTEXT INVOCATION: $@";

# *MUST* be context sensitive due to legacy (DO NOT call search_rs)
$_[0]->result_source->resultset->search({ $_[0]->_grouping_clause() });
}

# Returns an unordered resultset of all objects in the same group
Expand All @@ -770,7 +827,17 @@ sub _siblings {
my $self = shift;
my $position_column = $self->position_column;
my $pos;
return defined ($pos = $self->get_column($position_column))

DBIx::Class::_ENV_::ASSERT_NO_INTERNAL_INDIRECT_CALLS
and
wantarray
and
! eval { fail_on_internal_call; 1 }
and
die "ILLEGAL LIST CONTEXT INVOCATION: $@";

# *MUST* be context sensitive due to legacy (DO NOT call search_rs)
defined( $pos = $self->get_column($position_column) )
? $self->_group_rs->search(
{ $position_column => { '!=' => $pos } },
)
Expand Down Expand Up @@ -813,17 +880,26 @@ sub _is_in_group {
my ($self, $other) = @_;
my $current = {$self->_grouping_clause};

no warnings qw/uninitialized/;

return 0 if (
join ("\x00", sort keys %$current)
ne
join ("\x00", sort keys %$other)
);
for my $key (keys %$current) {
return 0 if $current->{$key} ne $other->{$key};
}
return 1;
(
bag_eq(
[ keys %$current ],
[ keys %$other ],
)
and
! grep {
(
defined( $current->{$_} )
xor
defined( $other->{$_} )
)
or
(
defined $current->{$_}
and
$current->{$_} ne $other->{$_}
)
} keys %$other
) ? 1 : 0;
}

# This is a short-circuited method, that is used internally by this
Expand All @@ -839,9 +915,8 @@ sub _is_in_group {
# you are doing use this method which bypasses any hooks introduced by
# this module.
sub _ordered_internal_update {
my $self = shift;
local $self->result_source->schema->{_ORDERED_INTERNAL_UPDATE} = 1;
return $self->update (@_);
local $_[0]->result_source->schema->{_ORDERED_INTERNAL_UPDATE} = 1;
shift->update (@_);
}

1;
Expand Down
19 changes: 13 additions & 6 deletions lib/DBIx/Class/PK.pm
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ use warnings;

use base qw/DBIx::Class::Row/;

use DBIx::Class::_Util 'fail_on_internal_call';
use namespace::clean;

=head1 NAME
DBIx::Class::PK - Primary Key class
Expand All @@ -27,12 +30,16 @@ a class method.
=cut

sub id {
my ($self) = @_;
$self->throw_exception( "Can't call id() as a class method" )
unless ref $self;
my @id_vals = $self->_ident_values;
return (wantarray ? @id_vals : $id_vals[0]);
sub id :DBIC_method_is_indirect_sugar {
DBIx::Class::_ENV_::ASSERT_NO_INTERNAL_INDIRECT_CALLS and fail_on_internal_call;

$_[0]->throw_exception( "Can't call id() as a class method" )
unless ref $_[0];

wantarray
? $_[0]->_ident_values
: ($_[0]->_ident_values)[0] # FIXME - horrible horrible legacy crap
;
}

sub _ident_values {
Expand Down
122 changes: 100 additions & 22 deletions lib/DBIx/Class/Relationship/Accessor.pm
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,16 @@ sub add_relationship_accessor {
my ($class, $rel, $acc_type) = @_;

if ($acc_type eq 'single') {
quote_sub "${class}::${rel}" => sprintf(<<'EOC', perlstring $rel);

my @qsub_args = ( {}, {
attributes => [qw(
DBIC_method_is_single_relationship_accessor
DBIC_method_is_generated_from_resultsource_metadata
)]
});


quote_sub "${class}::${rel}" => sprintf(<<'EOC', perlstring $rel), @qsub_args;
my $self = shift;
if (@_) {
Expand All @@ -35,40 +44,48 @@ sub add_relationship_accessor {
return $self->{_relationship_data}{%1$s};
}
else {
my $relcond = $self->result_source->_resolve_relationship_condition(
rel_name => %1$s,
foreign_alias => %1$s,
self_alias => 'me',
self_result_object => $self,
);
my $rsrc = $self->result_source;
my $jfc;
return undef if (
$relcond->{join_free_condition}
and
$relcond->{join_free_condition} ne DBIx::Class::_Util::UNRESOLVABLE_CONDITION
$rsrc->relationship_info(%1$s)->{attrs}{undef_on_null_fk}
and
scalar grep { not defined $_ } values %%{ $relcond->{join_free_condition} || {} }
$jfc = ( $rsrc->resolve_relationship_condition(
rel_name => %1$s,
foreign_alias => %1$s,
self_alias => 'me',
self_result_object => $self,
)->{join_free_condition} || {} )
and
$self->result_source->relationship_info(%1$s)->{attrs}{undef_on_null_fk}
grep { not defined $_ } values %%$jfc
);
my $val = $self->search_related( %1$s )->single;
my $val = $self->related_resultset( %1$s )->single;
return $val unless $val; # $val instead of undef so that null-objects can go through
return $self->{_relationship_data}{%1$s} = $val;
}
EOC
}
elsif ($acc_type eq 'filter') {
$class->throw_exception("No such column '$rel' to filter")
unless $class->has_column($rel);

my $f_class = $class->relationship_info($rel)->{class};
my $rsrc = $class->result_source_instance;

$rsrc->throw_exception("No such column '$rel' to filter")
unless $rsrc->has_column($rel);

my $f_class = $rsrc->relationship_info($rel)->{class};

$class->inflate_column($rel, {
inflate => sub {
my ($val, $self) = @_;
return $self->find_or_new_related($rel, {}, {});
return $self->find_or_new_related($rel, {});
},
deflate => sub {
my ($val, $self) = @_;
Expand All @@ -93,15 +110,76 @@ EOC
return $pk_val;
},
});


# god this is horrible...
my $acc =
$rsrc->columns_info->{$rel}{accessor}
||
$rel
;

# because CDBI may elect to never make an accessor at all...
if( my $main_cref = $class->can($acc) ) {

attributes->import(
$class,
$main_cref,
qw(
DBIC_method_is_filter_relationship_accessor
DBIC_method_is_generated_from_resultsource_metadata
),
);

if( my $extra_cref = $class->can("_${acc}_accessor") ) {
attributes->import(
$class,
$extra_cref,
qw(
DBIC_method_is_filter_relationship_extra_accessor
DBIC_method_is_generated_from_resultsource_metadata
),
);
}
}
}
elsif ($acc_type eq 'multi') {

quote_sub "${class}::${rel}_rs", "shift->search_related_rs( $rel => \@_ )";
quote_sub "${class}::add_to_${rel}", "shift->create_related( $rel => \@_ )";
quote_sub "${class}::${rel}", sprintf( <<'EOC', perlstring $rel );
DBIx::Class::_ENV_::ASSERT_NO_INTERNAL_WANTARRAY and my $sog = DBIx::Class::_Util::fail_on_internal_wantarray;
shift->search_related( %s => @_ )

my @qsub_args = (
{},
{
attributes => [qw(
DBIC_method_is_multi_relationship_accessor
DBIC_method_is_generated_from_resultsource_metadata
DBIC_method_is_indirect_sugar
)]
},
);


quote_sub "${class}::${rel}", sprintf( <<'EOC', perlstring $rel ), @qsub_args;
DBIx::Class::_ENV_::ASSERT_NO_INTERNAL_INDIRECT_CALLS and DBIx::Class::_Util::fail_on_internal_call;
shift->related_resultset(%s)->search( @_ )
EOC


$qsub_args[1]{attributes}[0]
=~ s/^DBIC_method_is_multi_relationship_accessor$/DBIC_method_is_multi_relationship_extra_accessor/
or die "Unexpected attr '$qsub_args[1]{attributes}[0]' ...";


quote_sub "${class}::${rel}_rs", sprintf( <<'EOC', perlstring $rel ), @qsub_args;
DBIx::Class::_ENV_::ASSERT_NO_INTERNAL_INDIRECT_CALLS and DBIx::Class::_Util::fail_on_internal_call;
shift->related_resultset(%s)->search_rs( @_ )
EOC


quote_sub "${class}::add_to_${rel}", sprintf( <<'EOC', perlstring $rel ), @qsub_args;
DBIx::Class::_ENV_::ASSERT_NO_INTERNAL_INDIRECT_CALLS and DBIx::Class::_Util::fail_on_internal_call;
shift->create_related( %s => @_ );
EOC

}
else {
$class->throw_exception("No such relationship accessor type '$acc_type'");
Expand Down
353 changes: 259 additions & 94 deletions lib/DBIx/Class/Relationship/Base.pm

Large diffs are not rendered by default.

13 changes: 6 additions & 7 deletions lib/DBIx/Class/Relationship/BelongsTo.pm
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ package # hide from PAUSE

use strict;
use warnings;
use Try::Tiny;
use DBIx::Class::_Util 'dbic_internal_try';
use DBIx::Class::_Util qw( dbic_internal_try dbic_internal_catch );
use namespace::clean;

our %_pod_inherit_config =
Expand Down Expand Up @@ -39,16 +38,16 @@ sub belongs_to {

$class->throw_exception(
"No such column '$f_key' declared yet on ${class} ($guess)"
) unless $class->has_column($f_key);
) unless $class->result_source->has_column($f_key);

$class->ensure_class_loaded($f_class);
my $f_rsrc = dbic_internal_try {
$f_class->result_source_instance;
$f_class->result_source;
}
catch {
dbic_internal_catch {
$class->throw_exception(
"Foreign class '$f_class' does not seem to be a Result class "
. "(or it simply did not load entirely due to a circular relation chain)"
. "(or it simply did not load entirely due to a circular relation chain): $_"
);
};

Expand Down Expand Up @@ -81,7 +80,7 @@ sub belongs_to {
and
(keys %$cond)[0] =~ /^foreign\./
and
$class->has_column($rel)
$class->result_source->has_column($rel)
) ? 'filter' : 'single';

my $fk_columns = ($acc_type eq 'single' and ref $cond eq 'HASH')
Expand Down
2 changes: 1 addition & 1 deletion lib/DBIx/Class/Relationship/CascadeActions.pm
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ sub delete {
my $ret = $self->next::method(@rest);

foreach my $rel (@cascade) {
if( my $rel_rs = dbic_internal_try { $self->search_related($rel) } ) {
if( my $rel_rs = dbic_internal_try { $self->related_resultset($rel) } ) {
$rel_rs->delete_all;
} else {
carp "Skipping cascade delete on relationship '$rel' - related resultsource '$rels{$rel}{class}' is not registered with this schema";
Expand Down
4 changes: 2 additions & 2 deletions lib/DBIx/Class/Relationship/HasMany.pm
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ sub has_many {

unless (ref $cond) {

my $pri = $class->result_source_instance->_single_pri_col_or_die;
my $pri = $class->result_source->_single_pri_col_or_die;

my ($f_key,$guess);
if (defined $cond && length $cond) {
Expand All @@ -30,7 +30,7 @@ sub has_many {

# FIXME - this check needs to be moved to schema-composition time...
# # only perform checks if the far side appears already loaded
# if (my $f_rsrc = dbic_internal_try { $f_class->result_source_instance } ) {
# if (my $f_rsrc = dbic_internal_try { $f_class->result_source } ) {
# $class->throw_exception(
# "No such column '$f_key' on foreign class ${f_class} ($guess)"
# ) if !$f_rsrc->has_column($f_key);
Expand Down
31 changes: 18 additions & 13 deletions lib/DBIx/Class/Relationship/HasOne.pm
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ package # hide from PAUSE
use strict;
use warnings;
use DBIx::Class::Carp;
use Try::Tiny;
use DBIx::Class::_Util 'dbic_internal_try';
use DBIx::Class::_Util qw( dbic_internal_try dbic_internal_catch );
use namespace::clean;

our %_pod_inherit_config =
Expand All @@ -24,7 +23,7 @@ sub has_one {
sub _has_one {
my ($class, $join_type, $rel, $f_class, $cond, $attrs) = @_;
unless (ref $cond) {
my $pri = $class->result_source_instance->_single_pri_col_or_die;
my $pri = $class->result_source->_single_pri_col_or_die;

my ($f_key,$guess,$f_rsrc);
if (defined $cond && length $cond) {
Expand All @@ -36,12 +35,12 @@ sub _has_one {
$class->ensure_class_loaded($f_class);

$f_rsrc = dbic_internal_try {
my $r = $f_class->result_source_instance;
my $r = $f_class->result_source;
die "There got to be some columns by now... (exception caught and rewritten by catch below)"
unless $r->columns;
$r;
}
catch {
dbic_internal_catch {
$class->throw_exception(
"Foreign class '$f_class' does not seem to be a Result class "
. "(or it simply did not load entirely due to a circular relation chain)"
Expand All @@ -60,8 +59,8 @@ sub _has_one {

# FIXME - this check needs to be moved to schema-composition time...
# # only perform checks if the far side was not preloaded above *AND*
# # appears to have been loaded by something else (has a rsrc_instance)
# if (! $f_rsrc and $f_rsrc = dbic_internal_try { $f_class->result_source_instance }) {
# # appears to have been loaded by something else (has a rsrc)
# if (! $f_rsrc and $f_rsrc = dbic_internal_try { $f_class->result_source }) {
# $class->throw_exception(
# "No such column '$f_key' on foreign class ${f_class} ($guess)"
# ) if !$f_rsrc->has_column($f_key);
Expand Down Expand Up @@ -97,12 +96,18 @@ sub _validate_has_one_condition {
return unless $self_id =~ /^self\.(.*)$/;

my $key = $1;
$class->throw_exception("Defining rel on ${class} that includes '$key' but no such column defined here yet")
unless $class->has_column($key);
my $column_info = $class->column_info($key);
if ( $column_info->{is_nullable} ) {
carp(qq'"might_have/has_one" must not be on columns with is_nullable set to true ($class/$key). This might indicate an incorrect use of those relationship helpers instead of belongs_to.');
}

my $column_info = $class->result_source->columns_info->{$key}
or $class->throw_exception(
"Defining rel on ${class} that includes '$key' "
. 'but no such column defined there yet'
);

carp(
"'might_have'/'has_one' must not be used on columns with is_nullable "
. "set to true ($class/$key). This almost certainly indicates an "
. "incorrect use of these relationship helpers instead of 'belongs_to'"
) if $column_info->{is_nullable};
}
}

Expand Down
111 changes: 72 additions & 39 deletions lib/DBIx/Class/Relationship/ManyToMany.pm
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ use strict;
use warnings;

use DBIx::Class::Carp;
use DBIx::Class::_Util qw(fail_on_internal_wantarray quote_sub);
use DBIx::Class::_Util qw( quote_sub perlstring );

# FIXME - this souldn't be needed
my $cu;
BEGIN { $cu = \&carp_unique }
# FIXME - this should go away
# instead Carp::Skip should export usable keywords or something like that
my $unique_carper;
BEGIN { $unique_carper = \&carp_unique }

use namespace::clean;

Expand Down Expand Up @@ -56,38 +57,66 @@ EOW
}
}

my $qsub_attrs = {
'$rel_attrs' => \{ alias => $f_rel, %{ $rel_attrs||{} } },
'$carp_unique' => \$cu,
};

quote_sub "${class}::${rs_meth}", sprintf( <<'EOC', $rel, $f_rel ), $qsub_attrs;

# this little horror is there replicating a deprecation from
# within search_rs() itself
shift->search_related_rs( q{%1$s} )
->search_related_rs(
q{%2$s},
undef,
( @_ > 1 and ref $_[-1] eq 'HASH' )
? { %%$rel_attrs, %%{ pop @_ } }
: $rel_attrs
)->search_rs(@_)
;
my @main_meth_qsub_args = (
{},
{ attributes => [
'DBIC_method_is_indirect_sugar',
( keys( %{$rel_attrs||{}} )
? 'DBIC_method_is_m2m_sugar_with_attrs'
: 'DBIC_method_is_m2m_sugar'
),
] },
);


quote_sub "${class}::${meth}", sprintf( <<'EOC', $rs_meth ), @main_meth_qsub_args;
DBIx::Class::_ENV_::ASSERT_NO_INTERNAL_INDIRECT_CALLS and DBIx::Class::_Util::fail_on_internal_call;
shift->%s( @_ )->search;
EOC


quote_sub "${class}::${meth}", sprintf( <<'EOC', $rs_meth );
my @extra_meth_qsub_args = (
{
'$rel_attrs' => \{ alias => $f_rel, %{ $rel_attrs||{} } },
'$carp_unique' => \$unique_carper,
},
{ attributes => [
'DBIC_method_is_indirect_sugar',
( keys( %{$rel_attrs||{}} )
? 'DBIC_method_is_m2m_extra_sugar_with_attrs'
: 'DBIC_method_is_m2m_extra_sugar'
),
] },
);


DBIx::Class::_ENV_::ASSERT_NO_INTERNAL_WANTARRAY and my $sog = DBIx::Class::_Util::fail_on_internal_wantarray;
quote_sub "${class}::${rs_meth}", sprintf( <<'EOC', map { perlstring $_ } ( "${class}::${meth}", $rel, $f_rel ) ), @extra_meth_qsub_args;

my $rs = shift->%s( @_ );
DBIx::Class::_ENV_::ASSERT_NO_INTERNAL_INDIRECT_CALLS
and
# allow nested calls from our ->many_to_many, see comment below
( (CORE::caller(1))[3] ne %s )
and
DBIx::Class::_Util::fail_on_internal_call;

wantarray ? $rs->all : $rs;
# this little horror is there replicating a deprecation from
# within search_rs() itself
shift->related_resultset( %s )
->related_resultset( %s )
->search_rs (
undef,
( @_ > 1 and ref $_[-1] eq 'HASH' )
? { %%$rel_attrs, %%{ pop @_ } }
: $rel_attrs
)->search_rs(@_)
;
EOC

# the above is the only indirect method, the 3 below have too much logic
shift @{$extra_meth_qsub_args[1]{attributes}};

quote_sub "${class}::${add_meth}", sprintf( <<'EOC', $add_meth, $rel, $f_rel ), $qsub_attrs;

quote_sub "${class}::${add_meth}", sprintf( <<'EOC', $add_meth, $rel, $f_rel ), @extra_meth_qsub_args;

( @_ >= 2 and @_ <= 3 ) or $_[0]->throw_exception(
"'%1$s' expects an object or hashref to link to, and an optional hashref of link data"
Expand All @@ -101,7 +130,7 @@ EOC

my $guard;

# the API needs is always expected to return the far object, possibly
# the API is always expected to return the far object, possibly
# creating it in the process
if( not defined Scalar::Util::blessed( $far_obj ) ) {

Expand Down Expand Up @@ -131,7 +160,7 @@ EOC
EOC


quote_sub "${class}::${set_meth}", sprintf( <<'EOC', $set_meth, $add_meth, $rel, $f_rel ), $qsub_attrs;
quote_sub "${class}::${set_meth}", sprintf( <<'EOC', $set_meth, $add_meth, $rel, $f_rel ), @extra_meth_qsub_args;

my $self = shift;

Expand All @@ -153,7 +182,7 @@ EOC
) if (
@_ > 1
or
( @_ and ref $_[0] ne 'HASH' )
( defined $_[0] and ref $_[0] ne 'HASH' )
);

my $guard;
Expand All @@ -164,13 +193,13 @@ EOC

# if there is a where clause in the attributes, ensure we only delete
# rows that are within the where restriction
$self->search_related(
q{%3$s},
( $rel_attrs->{where}
? ( $rel_attrs->{where}, { join => q{%4$s} } )
: ()
)
)->delete;
$self->related_resultset( q{%3$s} )
->search_rs(
( $rel_attrs->{where}
? ( $rel_attrs->{where}, { join => q{%4$s} } )
: ()
)
)->delete;

# add in the set rel objects
$self->%2$s(
Expand All @@ -182,12 +211,16 @@ EOC
EOC


quote_sub "${class}::${remove_meth}", sprintf( <<'EOC', $remove_meth, $rel, $f_rel );
# the last method needs no captures - just kill it all with fire
$extra_meth_qsub_args[0] = {};


quote_sub "${class}::${remove_meth}", sprintf( <<'EOC', $remove_meth, $rel, $f_rel ), @extra_meth_qsub_args;

$_[0]->throw_exception("'%1$s' expects an object")
unless defined Scalar::Util::blessed( $_[1] );

$_[0]->search_related_rs( q{%2$s} )
$_[0]->related_resultset( q{%2$s} )
->search_rs( $_[1]->ident_condition( q{%3$s} ), { join => q{%3$s} } )
->delete;
EOC
Expand Down
11 changes: 9 additions & 2 deletions lib/DBIx/Class/Relationship/ProxyMethods.pm
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,18 @@ sub proxy_to_related {
my ($class, $rel, $proxy_args) = @_;
my %proxy_map = $class->_build_proxy_map_from($proxy_args);

quote_sub "${class}::$_", sprintf( <<'EOC', $rel, $proxy_map{$_} )
my @qsub_args = ( {}, {
attributes => [qw(
DBIC_method_is_proxy_to_relationship
DBIC_method_is_generated_from_resultsource_metadata
)],
} );

quote_sub "${class}::$_", sprintf( <<'EOC', $rel, $proxy_map{$_} ), @qsub_args
my $self = shift;
my $relobj = $self->%1$s;
if (@_ && !defined $relobj) {
$relobj = $self->create_related( %1$s => { %2$s => $_[0] } );
$relobj = $self->create_related( q{%1$s} => { %2$s => $_[0] } );
@_ = ();
}
$relobj ? $relobj->%2$s(@_) : undef;
Expand Down
Loading