Class-AutoGenerate
view release on metacpan or search on metacpan
lib/Class/AutoGenerate.pm view on Meta::CPAN
My::AutoGenerator::autogenerated 'Some::Module';
My::AutoGenerator->autogenerated('Some::Module');
# Where $autogenerator->isa('Class::AutoGenerate');
$autogenerator->autogenerated('Some::Module');
Returns true if the package named was autogenerated by a L<Class::AutoGenerate> class loader. Returns C<undef> in any other case.
=cut
sub autogenerated($) {
my $class = shift;
if (blessed $class or (not ref $class and $class =~ /^[:\w]+$/)) {
$class = shift if $class->isa('Class::AutoGenerate');
}
return exists $AUTOGENERATED{ $class };
}
=head2 autogenerator_of MODULE
lib/Class/AutoGenerate.pm view on Meta::CPAN
My::AutoGenerator::autogenerator_of 'Some::Module';
My::AutoGenerator->autogenerator_of('Some::Module');
# Where $autogenerator->isa('Class::AutoGenerate');
$autogenerator->autogenerator_of('Some::Module');
Returns the object that was used to autogenerate the module. This is really just a shortcut for looking up the information in C<%INC>, but saves some work of converting Perl module names into package file names and the cryptic use of the C<%INC> vari...
=cut
sub autogenerator_of($) {
my $class = shift;
if (blessed $class or (not ref $class and $class =~ /^[:\w]+$/)) {
$class = shift if $class->isa('Class::AutoGenerate');
}
# Convert the module name into a package file, Some::Thing -> Some/Thing.pm
my $package_file = $class;
$package_file =~ s{::}{/}g;
$package_file .= '.pm';
lib/Class/AutoGenerate/Declare.pm view on Meta::CPAN
=item Array of Matches
Finally, you may also place a series of matches into an array. The given generates block will be used if any of the matches match a given module name.
requiring [ 'App', 'App::**', qr/^SomeOther::(Thing|Whatsit)$/ ] => ...
=back
=cut
sub _compile_glob_pattern($) {
my $glob = shift;
# If it's a regexp, we don't want to compile it as if it's a glob!
return $glob if ref $glob and ref $glob eq 'Regexp';
# The following code was adapted from Jifty::Dispatcher of trunk r2520
# Escape and normalize
$glob = quotemeta($glob);
$glob =~ s{(?:\\:)(?:\\:)}{::}g;
lib/Class/AutoGenerate/Declare.pm view on Meta::CPAN
$glob = "($glob)";
}
# Make a regexp
return qr{^$glob$};
}
# This variable used to communicate when declare { requiring ... }
our $declare_to = undef;
sub _register_rules($$$) {
my $class = shift;
my $pattern = shift;
my $code = shift;
# If an array, push the generates code for each pattern
if (ref $pattern and reftype $pattern eq 'ARRAY') {
&_register_rules($class, $_, $code) foreach @$pattern;
}
# Otherwise, compile globs and push in the pattern => code rule thingies
else {
$pattern = _compile_glob_pattern $pattern;
push @{ $declare_to || $class->_declarations }, [ $pattern => $code ];
}
}
sub requiring($$) {
my $pattern = shift;
my $code = shift;
# Register a new rule (or rules) for the caller
my $package = caller;
_register_rules $package, $pattern, $code;
}
=head2 generates { ... }
lib/Class/AutoGenerate/Declare.pm view on Meta::CPAN
};
If we included the rule above, intantiated the class loader, and then ran:
use My::Flipper;
A class would be generated named C<My::Flipper> that uses C<My::Base::Flipper> as its only base class, imports the C<looks_like_number> function from L<Scalar::Util>, defines a scalar package variable C<$scalar> set to 14, an array package variable, ...
=cut
sub generates(&) { shift }
=head2 declare { ... };
A declare block may be used to wrap your class loader code, but is not required. The block will be passed a single argument, C<$self>, which is the initialized class loader object. It is helpful if you need a reference to your C<$self>.
For example,
package My::Classloader;
use Class::Autogenerate -base;
lib/Class/AutoGenerate/Declare.pm view on Meta::CPAN
# later...
use My::Classloader;
BEGIN { My::Classloader->new( base => 'Foo' ) };
You may have multiple C<declare> blocks in your class loader.
It is important to note that the C<declare> block modifies the semantics of how the class loader is built. Normally, the C<requiring> rules are all generated and associated with the class loader package immediately. A C<declare> block causes all rule...
=cut
sub declare(&) {
my $code = shift;
# Wrap that code in a little more code that sets things up
my $declaration = sub {
my $self = shift;
# $declare_to signals to requiring to register rules differently
local $declare_to = [];
$code->($self);
return @$declare_to;
lib/Class/AutoGenerate/Declare.pm view on Meta::CPAN
This subroutine is used with L</generates> to mark the generated class as extending the named class or classes. This pushes the named classes into the C<@ISA> array for the class when it is generated.
B<N.B.> You need to ask Perl to include this class on your own. This is not exactly equivalent to <use base qw/$class/> in this regard. If a class might not be included already, you may wish to do something like the following:
require My::Parent::Class;
extends 'My::Parent::Class';
=cut
sub extends(@) {
no strict 'refs';
push @{ $Class::AutoGenerate::package . '::ISA' }, @_;
}
=head2 uses CLASS, ARGS
This subroutine states that the generated class uses another package. The first argument is the class to use and the remaining arguments are passed to the import method of the used class (the first argument may also be a version number, see L<perlfun...
=cut
sub uses($;@) {
my $class = shift;
my $args = join ', ', map { "'".quotemeta($_)."'" } @_;
$args = " ($args)" if $args;
eval "package $Class::AutoGenerate::package; use $class$args;";
die $@ if $@;
}
=head2 requires EXPR
This is similar to L</uses>, but uses L<perlfunc/require> instead of C<use>.
=cut
sub requires($) {
my $expr = shift;
# Make a nice string unless it's barewordable... this might not always do
# the right thing...
$expr = '"' . quotemeta($expr) . '"' unless $expr =~ /^[\w:]+$/;
eval "package $Class::AutoGenerate::package; require $expr;";
die $@ if $@;
}
=head2 defines NAME => VALUE
This is the general purpose definition declaration. If the given name starts with a dollar sign ($), then a scalar value is created. If the given name starts with an at sign (@), then an array value is added to the class. If the given starts with a p...
The given value must be appropriate for the type of definition being generated.
=cut
sub defines($$) {
my $name = shift;
my $value = shift;
# It's a scalar
if ($name =~ s/^\$//) {
no strict 'refs';
${ $Class::AutoGenerate::package . '::' . $name } = $value;
}
# It's an array
lib/Class/AutoGenerate/Declare.pm view on Meta::CPAN
__PACKAGE__->mk_accessors( qw/ name title description / );
};
};
B<Caution:> If user input has any effect on the code generated, you should make certain that all input is carefully validated to prevent code injection.
=cut
sub generate_from($) {
my $source_code = shift;
eval "package $Class::AutoGenerate::package; $source_code";
die $@ if $@;
}
=head2 conclude_with SOURCE
This is a special helper used in place of L</generate_from> for code that could cause a loop during code generation. This can occur because Perl does not realize that the generated module has been loaded until I<after> the L</generates> block has bee...
lib/Class/AutoGenerate/Declare.pm view on Meta::CPAN
};
If we had used C<generate_from> rather than C<conclude_with> in the code above, a loop would have been generated upon calling C<require My::Thing::Flup>. This would have resulted in a call to C<require_helpers> in the sample, which would have resulte...
By using C<conclude_with>, the code given is not executed until Perl has already noted that the class is loaded, so the loop stops and this code should execute successfully.
B<Caution:> If user input has any effect on the code generated, you should make certain that all input is carefully validated to prevent code injection.
=cut
sub conclude_with($) {
my $code = shift;
push @{ $Class::AutoGenerate::conclude_with }, $code;
}
=head2 source_code SOURCE
This method is purely for use with making your code a little easier to read. It doesn't do anything but return the argument passed to it.
B<Caution:> If user input has any effect on the code generated, you should make certain that all input is carefully validated to prevent code injection.
=cut
sub source_code($) { shift }
=head2 source_file FILENAME
Given a file name, this evalutes the Perl in that file within the context of the package.
requiring 'Another::Class' => generates {
generate_from source_file 'code_base.pl';
};
B<Caution:> If user input has any effect on this file included, you should make certain that all input is carefully validated to prevent code injection.
=cut
sub source_file($) {
my $filename = shift;
# Open the file...
open my $fh, '<', $filename or die "failed to open $filename: $!";
# Slurp it down...
local $/;
return <$fh>;
}
=head2 next_rule
By calling the C<next_rule> statement, you will prevent the current L</generates> statement from finishing. Instead, it will quit and the next L</requirng> rule will be tried.
=cut
sub next_rule() { die "NEXT_RULE\n" }
=head2 last_rule
The C<last_rule> statement causes the class loader to stop completely and return that it found no matching Perl modules.
=cut
sub last_rule() { die "LAST_RULE\n" }
=head1 SEE ALSO
L<UNIVERSAL::require>
=head1 AUTHOR
Andrew Sterling Hanenkamp C<< <hanenkamp@cpan.org> >>
=head1 COPYRIGHT AND LICENSE
sub require_not_ok($;$) {
my ($class, $message) = @_;
eval "require $class";
ok($@, ($message||"not require $class"));
}
1;
( run in 0.306 second using v1.01-cache-2.11-cpan-1f129e94a17 )