package CatalystX::Imports::Context; =head1 NAME CatalystX::Imports::Context - Exports Context Helper Functions =cut use warnings; use strict; =head1 BASE CLASSES L =cut use base 'CatalystX::Imports'; use vars qw( $EXPORT_MAP_NAME $DEFAULT_LIBRARY ); use List::MoreUtils qw( part apply uniq ); use Scalar::Util qw( set_prototype ); use Carp::Clan qw{ ^CatalystX::Imports(?:::|$) }; use Filter::EOF; $EXPORT_MAP_NAME = 'CATALYSTX_IMPORTS_EXPORT_MAP'; $DEFAULT_LIBRARY = __PACKAGE__ . '::Default'; =head1 SYNOPSIS package MyApp::Controller::Foo; use base 'Catalyst::Controller'; # Export everything minus the 'captures' function. Also load # the additional 'Foo' library and a config value use CatalystX::Imports Context => { Default => [qw( :all -captures +Foo )], Config => [qw( model_name )], }; sub list: Local { stash( rs => model(model_name)->search_rs ); } sub edit: Local { stash( foo => model(model_name)->find(args[0]), list_uri => uri_for(action('list')), ); } 1; =head1 DESCRIPTION This package represents the base class and export manager for all libraries. The default library can be found under the package name L. The exports will be removed after compiletime. By then, the calls to them in your controller will already be bound to the right code slots by perl. This keeps these functions from being available as methods on your controller object. =head1 IMPORT SYNTAX You can specify what library parts you want to import into your controller on the C line to L: use CatalystX::Imports Context => [qw(:all -captures +Foo)]; This would import all functions from the default library L, except the C function. See L for all available tags in the default library. Additionally, it will search and load the C library, which would be C. This notation doesn't accept any arguments, so the library specific default symbols will be exported. If you just want some specific functions imported, you can also specify them explicitly: use CatalystX::Imports Context => [qw(action uri_for model config stash)]; At last, to be specific about more than one library, you can pass a hash reference: use CatalystX::Imports Context => { Default => ':all', Config => [qw(model_name)] }; See the libraries documentation for further syntax information. =head1 ALIASES If documented, you can also import a function with one of it's aliases. If you import a function via a tag, it will only be exported under its real name, not its aliased names. Therefor, to use an aliase you have to specify aliases explicitly at any time to use them: # load aliases for short forms of 'request' and 'response' use CatalystX::Imports Context => [qw( req res )]; =head1 INCLUDED LIBRARIES =over =item L Contains default shortcuts and inline accessors. =item L Allows you to import local controller (instance) configuration accessors as inline functions into your namespace. =back =cut =head1 METHODS =cut =head2 register_export This method registers a new export in the library it's called upon. You will mostly only need this function for creating your own libraries: package CatalystX::Imports::Context::MyOwnLibrary; use base 'CatalystX::Imports::Context'; __PACKAGE__->register_export( name => 'double', alias => 'times_two', prototype => '$', tags => [qw( math )], code => sub { my ($library, $ctrl, $ctx, $action_args, @args) = @_; return $args[0] * 2; }, ); The C and C parameters are mandatory. If you specify an alias, it can be imported explicitly, but will not be included in the C<:all> tag. The prototype is the usual prototpe you could stuff on perl subroutines. If you specify tags as an array reference, the export will be included in those tag sets by it's name and aliases. It will be included in the C<:all> tag in any case, but only under it's name, not it's aliases. The specified code reference will get the library class name, the controller and context objects (like a L action), and an array reference of the arguments passed to the last action and then it's own actual arguments passed in. You could call the above with double(23); # 46 =cut sub register_export { my ($class, @args) = @_; # we expect pairs of option keys and values as arguments croak 'register_export expects key/value pairs as arguments' if @args % 2; my %options = @args; # check if every required option is there for my $required (qw( code name )) { croak "register_export: Missing required parameter: '$required'" unless exists $options{ $required } and defined $options{ $required }; } # optionals my @tags = @{ $options{tags} || [] }; my @aliases = @{ $options{alias} || [] }; # get the export map, we'll need it my $export_map = $class->_export_map; # walk the names we want to register this under for my $name ($options{name}, @aliases) { # register in tags, only name goes into :all by default for my $t (uniq @tags, ($options{name} eq $name ? 'all' : ())) { push @{ $export_map->{tag}{ $t } ||= [] }, $name; } # save export information $export_map->{export}{ $name } = { name => $name, code => $options{code}, ( exists $options{prototype} ? ( prototype => $options{prototype} ) : () ), }; } return 1; } =head2 _export_map Returns the libraries export map as a hash reference. This will be stored in your library class (if you build your own, otherwise you don't have to care) in the C<%CATALYSTX_IMPORTS_EXPORT_MAP> package variable. =cut sub _export_map { my ($class) = @_; my $map_name = "${class}::${EXPORT_MAP_NAME}"; { no strict 'refs'; no warnings 'once'; return \%{ $map_name }; } } =head2 get_export Expects the name of an export in the library and will return the information it stored with it. An export will be stored under its actual name as well as its aliases. =cut sub get_export { $_[0]->_export_map->{export}{ $_[1] } } =head2 export_into Called by L' C method. Takes a target and a set of commands specified in L. This will forward the commands to the actual libraries and the L method in them. =cut sub export_into { my ($class, $target, @args) = @_; my %args; # we accept lists and array refs for default, and explicit # hash refs for more control if (@args == 1 and ref $args[0] eq 'ARRAY') { %args = (Default => $args[0]); } elsif (@args == 1 and ref $args[0] eq 'HASH') { %args = %{ $args[0] }; ref($args{ $_ }) eq 'ARRAY' or $args{ $_ } = [ $args{ $_ } ] for keys %args; } else { %args = (Default => \@args); } # filter out additional libraries in Default arguments my @default_args = @{ delete($args{Default}) || [] }; my %load_default; for my $arg (@default_args) { if ($arg =~ /^[+](.+)$/) { next unless exists $args{ $1 }; $args{ $1 } ||= []; $load_default{ $1 } = 1; next; } push @{ $args{Default} ||= [] }, $arg; } # load libraries and export symbols for my $lib (keys %args) { my $lib_class = __PACKAGE__ . '::' . $lib; $class->_ensure_class_loaded($lib_class); my @symbols = @{ $args{ $lib } }; push @symbols, $lib_class->default_exports if $load_default{ $lib }; $lib_class->context_export_into($target, @{ $args{ $lib } }); } return 1; } =head2 context_export_into Takes a target and an actual command set for a library (no C<+Foo> stuff) and cleans that (flattens out tags, removes C<-substractions>). It will utilise L to actually export the final set of functions. =cut sub context_export_into { my ($class, $target, @exports) = @_; # part and clean different type of import arguments my ($export_list, $tags, $substract) = map { [] } 1..3; for my $export (@exports) { push @$substract, $export and next if $export =~ s/^-//; push @$tags, $export and next if $export =~ s/^://; push @$export_list, $export; } # fetch the export map, we're going to use it a bit my $export_map = $class->_export_map; # resolve tags for my $tag (@$tags) { my $tag_exports = $export_map->{tag}{ $tag } or croak "Unknown Context tag: ':$tag'"; push @$export_list, @$tag_exports; } # remove doubles and substractions my %substract_map = map { ($_ => 1) } @{ $substract || [] }; @$export_list = grep { not exists $substract_map{ $_ } } uniq @$export_list; # install the exports for my $export (@$export_list) { $class->context_install_export_into($target, $export); } # register the exports to be removed after compile time Filter::EOF->on_eof_call(sub { for my $export (@$export_list) { no strict 'refs'; delete ${ $target . '::' }{ $export }; } }); return 1; } =head2 context_install_export_into Takes a target class and the name of an export to install the function in the specified class. =cut sub context_install_export_into { my ($class, $target, $export) = @_; # find the export information my $export_info = $class->get_export($export) or croak "Unknown Context export: '$export'"; my ($code, $prototype) = @{ $export_info }{qw( code prototype )}; # the wrapper fetches the current objects my $export_code = sub { my ($controller, $context, $arguments) = do { no strict 'refs'; map { ${ "${target}::" . ${ "CatalystX::Imports::$_" } } } qw( STORE_CONTROLLER STORE_CONTEXT STORE_ARGUMENTS ); }; return $class->$code($controller, $context, $arguments, @_); }; # install the export, include prototype if specified { no strict 'refs'; my $name = $export_info->{name}; *{ "${target}::${name}" } = defined $prototype ? set_prototype sub { $export_code->(@_) }, $prototype : $export_code; } return 1; } =head2 default_exports Should be overridden by subclasses if they want to export something by default. This will be used if the library is specified without any arguments at all. E.g. this: use CatalystX::Imports Context => [qw( +Foo )]; will export C's defaults, but use CatalystX::Imports Context => { Foo => [] }; will not. Without an overriding method, the default is set to export nothing at all. =cut sub default_exports { } =head1 DIAGNOSTICS =head2 register_export expects key/value pairs as arguments You passed an odd number of values into the C method call, but it expects key and value pairs of named options. See L>/register_export> for available options and calling syntax. =head2 register_export: Missing required parameter: 'foo' The L method expects a few parameters that can't be omitted, including C. Pass in the parameter as specified in the section about the L method. =head2 Unknown Context tag: ':foo' You specified to import the functions in the tag C<:foo> on your C line, but no tag with the name C<:foo> was registered in the library. =head2 Unknown Context export: 'foo' You asked for export of the function C, but no function under this name was registered in the library. Please consult your library documentation for a list of available exports. The default library can be found under L. =head1 SEE ALSO L, L, L, L, L, L =head1 AUTHOR AND COPYRIGHT Robert 'phaylon' Sedlacek Crs@474.atE> =head1 LICENSE This program is free software; you can redistribute it and/or modify it under the same terms as perl itself. =cut 1;