Sometimes refactoring will take you down some strange paths. I have a subroutine that needs to be exported to other test classes. This subroutine mocks up certain portions of the SOAP::Lite interface so I can pass in the return XML and also see what the request XML looks like. I've implemented an entire class within a subroutine. I don't think this is really a good piece of code, but it was interesting to write.
sub _mock_soap_interface { package MOCK::SOAP::INTERFACE; require Sub::Override; require SOAP::Lite; my $request = ''; my $xml = shift; my $token = Sub::Override->new( 'SOAP::Lite::call', sub { my $self = shift; my $serializer = $self->serializer; $request = $serializer->envelope(method => shift(@_), @_); return $xml; } ); *MOCK::SOAP::INTERFACE::xml = sub { ${shift->{_request}} }; bless { _request => \$request, _token => $token, } => __PACKAGE__; }
And using it:
my $request = _mock_soap_interface($response_xml); my $soap = My::SOAP::Lite::Subclass->new; my $results = $soap->get_some_service; # test results my $request_xml = $request->xml; # test that request XML is properly formatted
Re:Huh?
Ovid on 2004-10-18T18:12:49
Well, package declarations are lexically scoped. It would have been simpler for me to have simply declared another package in that file and have the subroutine return a new object created from that package. Instead, just because of the way the refactoring worked, I had simply wrapped the package in the sub and now that I look at it, I can't see why this would be a bad thing.
There's no real benefit that I can tell, but there's no real harm I can identify, either.
Re:Huh?
drhyde on 2004-10-19T08:55:55
Quite simply, it means that when you enter the sub you're in one package, and then at some point during the sub the current package changes. I recently did something like that, and it was also because of some refactoring.I have a bunch of classes, all subclasses of a single class, and all pretty similar. I broke a ton of code out into a "helper" module which they all use. However, part of that is to call a method in the class's superclass, like this:
package Foo;
@ISA(qw(Bar));
use HelperMethods;
sub blah {
# massage data
return HelperMethods::blah(...);
}
########
package HelperMethods;
sub blah {...
$class->SUPER::blah(...) # call calling class's superclass's blah()
}
Trouble is, although in HelperMethods::blah() the $class variable is correctly set to Foo (indicating that this was called from Foo::blah), but it doesn't work. This is because SUPER is evaluated in the context of the current package (HelperMethods) and not really in the context of $class.
The solution was:
eval "
package $class;
\$class->SUPER::blah(...);
";
The extra step of the eval is needed because packge $class is illegal - package names must be constant.