I'm trying to create an object that will fetch a resource from the web, and needs to remember both where the resource was found eventually, as well as what the original URL was that we gave it.
I don't want to have to specify the URL twice, nor do I want to have loads of conditionals every time I want to use the URL to figure out whether I should use the "url" attribute or the "updated URL" attribute or some such, so I thought I'd create a second attribute with default property set to initialize from the original URL:
package foo;
use Moose;
has 'url' => (
is => 'rw',
isa => 'Str',
required => 1,
);
has 'original_url' => (
is => 'ro',
isa => 'Str',
default => sub { shift->url },
);
If this would have worked (see below), then I would have been able to change the url at will, and just use the url attribute whenever I need to access the "current" URL; and then later on, if I need to see if the url was ever changed, I can just compare the url attribute against the original_url attribute, and go from there.
The problem with this, however, is that the order of initialization of these attributes seems to be not well defined; sometimes the default sub for the original_url attribute is called before the url property has a value, which means it will return undef, which obviously causes an error.
I thought of making original_url a lazy attribute, add a predicate and then add a trigger on url to update original_url to the old value if its predicate says it hasn't been set yet, but it turns out that triggers are called after the value has been changed, so I don't think I can do that.
Is there a method to accomplish what I'm trying to do that won't cause errors?
There's no need to use
BUILDorBUILDARGS.Just use
init_argto cause both attributes to be initialized from the same argument.This approach makes it trivial and efficient to "backup" multiple attributes.
Whichever technique you use, you probably want to avoid
isaon the attribute for the original. There's no point in validating the input twice, and I suspect it would lead to confusing messages if you did.