[Jifty-commit] r6196 - jifty/trunk/lib/Jifty

Jifty commits jifty-commit at lists.jifty.org
Sat Dec 27 04:45:03 EST 2008


Author: ruz
Date: Sat Dec 27 04:45:02 2008
New Revision: 6196

Modified:
   jifty/trunk/lib/Jifty/Config.pm

Log:
* allow devs to sub-class Config in apps
* update docs
* move methods around

Modified: jifty/trunk/lib/Jifty/Config.pm
==============================================================================
--- jifty/trunk/lib/Jifty/Config.pm	(original)
+++ jifty/trunk/lib/Jifty/Config.pm	Sat Dec 27 04:45:02 2008
@@ -9,33 +9,75 @@
 
 =head1 SYNOPSIS
 
-  my $app_name = Jifty->config->framework('ApplicationName');
-  my $frobber  = Jifty->config->app('PreferredFrobnicator');
+    # in your application
+    my $app_name = Jifty->config->framework('ApplicationName');
+    my $frobber  = Jifty->config->app('PreferredFrobnicator');
+
+    # sub classing
+    package MyApp::Config;
+    use base 'Jifty::Config';
+
+    sub post_load {
+        my $self = shift;
+        my $stash = $self->stash; # full config in a hash
+
+        ... do something with options ...
+
+        $self->stash( $stash ); # save config
+    }
+
+    1;
 
 =head1 DESCRIPTION
 
 This class is automatically loaded during Jifty startup. It contains the configuration information loaded from the F<config.yml> file (generally stored in the F<etc> directory of your application, but see L</load> for the details). This configuration file is stored in L<YAML> format.
 
-This configuration file contains two major sections named "C<framework>" and "C<application>". The framework section contains Jifty-specific configuration options and the application section contains whatever configuration options you want to use with your application. (I.e., if there's any configuration information your application needs to know at startup, this is a good place to put it.)
+This configuration file contains two major sections named "framework" and "application". The framework section contains Jifty-specific configuration options and the application section contains whatever configuration options you want to use with your application. (I.e., if there's any configuration information your application needs to know at startup, this is a good place to put it.)
+
+Usually you don't need to know anything about this class except
+L<app|/"app VARIABLE"> and L<framework|/"framework VARIABLE"> methods and
+about various config files and order in which they are loaded what
+described in L</load>.
 
 =cut
 
 use Jifty::Util;
 use Jifty::YAML;
-use File::Spec;
-use File::Basename;
-use Log::Log4perl;
+
 use Hash::Merge;
 Hash::Merge::set_behavior('RIGHT_PRECEDENT');
 
-use File::Basename();
 use base qw/Class::Accessor::Fast/;
+__PACKAGE__->mk_accessors(qw/stash/);
 
 use vars qw/$CONFIG/;
 
-__PACKAGE__->mk_accessors(qw/stash/);
+=head1 ACCESSING CONFIG
+
+=head2 framework VARIABLE
+
+Get the framework configuration variable C<VARIABLE>.
+
+    Jifty->config->framework('ApplicationName')
+
+=cut
+
+sub framework { return shift->_get( framework => @_ ) }
+
+=head2 app VARIABLE
+
+Get the application configuration variable C<VARIABLE>.
+
+    Jifty->config->framework('MyOption');
+
+=cut
 
-=head1 METHODS
+sub app { return shift->_get( application => @_ ) }
+
+# A teeny helper for framework and app
+sub _get { return $_[0]->stash->{ $_[1] }{ $_[2] } }
+
+=head1 LOADING
 
 =head2 new PARAMHASH
 
@@ -45,8 +87,7 @@
 
 in your application.
 
-This class method instantiates a new C<Jifty::Config> object. This
-object deals with configuration files.  
+This class method instantiates a new C<Jifty::Config> object.
 
 PARAMHASH currently takes a single option
 
@@ -54,7 +95,10 @@
 
 =item load_config
 
-This boolean defaults to true. If true, L</load> will be called upon initialization.
+This boolean defaults to true. If true, L</load> will be called upon
+initialization. Using this object without loading prevents sub-classing
+and only makes sense if you want to generate default config for
+a new jifty application or something like that.
 
 =back
 
@@ -78,19 +122,33 @@
 
 =head2 load
 
+Loads all config files for your application and initializes application
+level sub-class.
+
+Called from L<new|/"new PARAMHASH">, takes no arguments,
+returns nothing interesting, but do the following:
+
+=head3 Application config
+
 Jifty first loads the main configuration file for the application, looking for
 the C<JIFTY_CONFIG> environment variable or C<etc/config.yml> under the
 application's base directory.
 
+=head3 Vendor config
+
 It uses the main configuration file to find a vendor configuration
 file -- if it doesn't find a framework variable named 'VendorConfig',
 it will use the C<JIFTY_VENDOR_CONFIG> environment variable.
 
+=head3 Site config
+
 After loading the vendor configuration file (if it exists), the
 framework will look for a site configuration file, specified in either
 the framework's C<SiteConfig> or the C<JIFTY_SITE_CONFIG> environment
 variable. (Usually in C<etc/site_config.yml>.)
 
+=head3 Test config(s)
+
 After loading the site configuration file (if it exists), the
 framework will look for a test configuration file, specified in either
 the framework's C<TestConfig> or the C<JIFTY_TEST_CONFIG> environment
@@ -99,19 +157,37 @@
 Note that the test config may be drawn from several files if you use
 L<Jifty::Test>. See the documentation of L<Jifty::Test::load_test_configs>.
 
+=head3 Options clobbering
+
 Values in the test configuration will clobber the site configuration.
 Values in the site configuration file clobber those in the vendor
 configuration file. Values in the vendor configuration file clobber
-those in the application configuration file. (See L</WHY SO MANY FILES> for a deeper search for truth on this matter.)
+those in the application configuration file.
+(See L</WHY SO MANY FILES> for a deeper search for truth on this matter.)
+
+=head3 Guess defaults
 
 Once we're all done loading from files, several defaults are
-assumed based on the name of the application -- see L</guess>. 
+assumed based on the name of the application -- see L</guess>.
+
+=head3 Reblessing into application's sub-class
 
-After we have the config file, we call the coderef in C<$Jifty::Config::postload>, if it exists. This last bit is generally used by the test harness to do a little extra work.
+Ok, config is ready. Rebless this object into C<YourApp::Config> class
+and call L</post_load> hook, so you can do some tricks detailed in
+L</SUB-CLASSING>.
 
-B<SPECIAL PER-VALUE PROCESSING:> If a value begins and ends with "%" (e.g.,
-"%bin/foo%"), converts it with C<Jifty::Util/absolute_path> to an absolute path.
-This is typically unnecessary, but helpful for configuration variables such as C<MailerArgs> that only sometimes specify files.
+=head3 Another hook
+
+After we have the config file, we call the coderef in C<$Jifty::Config::postload>,
+if it exists. This last bit is generally used by the test harness to do
+a little extra work.
+
+=head3 B<SPECIAL PER-VALUE PROCESSING>
+
+If a value begins and ends with "%" (e.g., "%bin/foo%"), converts it with
+C<Jifty::Util/absolute_path> to an absolute path. This is typically
+unnecessary, but helpful for configuration variables such as C<MailerArgs>
+that only sometimes specify files.
 
 =cut
 
@@ -182,59 +258,131 @@
     # Bring old configurations up to current expectations
     $self->stash($self->update_config($self->stash));
 
+    # check for YourApp::Config
+    my $app_class = $self->framework('ApplicationClass') . '::Config';
+    # we have no class loader at this moment :(
+    my $found = Jifty::Util->try_to_require( $app_class );
+    if ( $found && $app_class->isa('Jifty::Config') ) {
+        bless $self, $app_class;
+    } elsif ( $found ) {
+        warn "You have $app_class, however it's not an sub-class of Jifty::Config."
+            ." Read `perldoc Jifty::Config` about sub classing. Skipping.";
+    }
+
+    # post load hook for sub-classes
+    $self->post_load;
+
     # Finally, check for global postload hooks (these are used by the
     # test harness)
     $self->$Jifty::Config::postload()
       if $Jifty::Config::postload;
+
 }
 
-=head2 framework VARIABLE
+# Sets up the initial location of the site configuration file
+sub _default_config_files {
+    my $self = shift;
+    my $config  = {
+        framework => {
+            SiteConfig => (
+                $ENV{JIFTY_SITE_CONFIG} || 'etc/site_config.yml'
+            )
+        }
+    };
+    return $self->_expand_relative_paths($config);
+}
 
-Get the framework configuration variable C<VARIABLE>.  
+=head2 post_load
 
-=cut
+Helper hook for L</SUB-CLASSING> and post processing config. At this
+point does nothing by default. That may be changed so do something like:
 
-sub framework {
-    my $self = shift;
-    my $var  = shift;
+    sub post_load {
+        my $self = shift;
+        $self->post_load( @_ );
+        ...
+    }
 
-    $self->_get( 'framework', $var );
-}
+=cut
 
-=head2 app VARIABLE
+sub post_load {}
 
-Get the application configuration variable C<VARIABLE>.
+=head2 load_file PATH
+
+Loads a YAML configuration file and returns a hashref to that file's
+data.
 
 =cut
 
-sub app {
+sub load_file {
     my $self = shift;
-    my $var  = shift;
+    my $file = shift;
 
-    $self->_get( 'application', $var );
+    # only try to load files that exist
+    return {} unless ( $file && -f $file );
+    my $hashref = Jifty::YAML::LoadFile($file)
+        or die "I couldn't load config file $file: $!";
+
+    # Make sure %path% values are made absolute
+    $hashref = $self->_expand_relative_paths($hashref);
+    return $hashref;
 }
 
-# A teeny helper for framework and app
-sub _get {
-    my $self    = shift;
-    my $section = shift;
-    my $var     = shift;
+# Does a DFS, turning all leaves that look like C<%paths%> into absolute paths.
+sub _expand_relative_paths {
+    my $self  = shift;
+    my $datum = shift;
 
-    return  $self->stash->{$section}->{$var} 
+    # Recurse through each value in an array
+    if ( ref $datum eq 'ARRAY' ) {
+        return [ map { $self->_expand_relative_paths($_) } @$datum ];
+    } 
+    
+    # Recurse through each value in a hash
+    elsif ( ref $datum eq 'HASH' ) {
+        for my $key ( keys %$datum ) {
+            my $new_val = $self->_expand_relative_paths( $datum->{$key} );
+            $datum->{$key} = $new_val;
+        }
+        return $datum;
+    } 
+    
+    # Do nothing with other kinds of references
+    elsif ( ref $datum ) {
+        return $datum;
+    } 
+    
+    # Check scalars for %path% and convert the enclosed value to an abspath
+    else {
+        if ( defined $datum and $datum =~ /^%(.+)%$/ ) {
+            $datum = Jifty::Util->absolute_path($1);
+        }
+        return $datum;
+    }
 }
 
-# Sets up the initial location of the site configuration file
-sub _default_config_files {
-    my $self = shift;
-    my $config  = {
+=head1 OTHER METHODS
+
+=head2 stash
+
+It's documented only for L</SUB-CLASSING>.
+
+Returns the current config as a hash reference (see below). Plenty of code
+considers Jifty's config as a static thing, so B<don't mess> with it in
+run-time.
+
+    {
         framework => {
-            SiteConfig => (
-                $ENV{JIFTY_SITE_CONFIG} || 'etc/site_config.yml'
-            )
-        }
-    };
-    return $self->_expand_relative_paths($config);
-}
+            ...
+        },
+        application => {
+            ...
+        },
+    }
+
+This method as well can be used to set a new config:
+
+    $config->stash( $new_stash );
 
 =head2 guess
 
@@ -334,7 +482,6 @@
     return $self->_expand_relative_paths($guess);
 }
 
-
 =head2 initial_config
 
 Returns a default guessed config for a new application.
@@ -431,60 +578,56 @@
 
 }
 
-=head2 load_file PATH
+=head1 SUB-CLASSING
 
-Loads a YAML configuration file and returns a hashref to that file's
-data.
+Template for sub-classing you can find in L</SYNOPSIS>.
 
-=cut
+Application config may have ApplicationClass or ApplicationName options,
+so it's B<important> to understand that your class goes into game later.
+Read </load> to understand when C<YourApp::Config> class is loaded.
 
-sub load_file {
-    my $self = shift;
-    my $file = shift;
+Use L</stash> method to get and/or change config.
 
-    # only try to load files that exist
-    return {} unless ( $file && -f $file );
-    my $hashref = Jifty::YAML::LoadFile($file)
-        or die "I couldn't load config file $file: $!";
+L</post_load> hook usually is all you want to (can :) ) sub class. Other
+methods most probably called before your class can operate.
 
-    # Make sure %path% values are made absolute
-    $hashref = $self->_expand_relative_paths($hashref);
-    return $hashref;
-}
+Sub-classing may be useful for:
 
+=over 4
 
-# Does a DFS, turning all leaves that look like C<%paths%> into absolute paths.
-sub _expand_relative_paths {
-    my $self  = shift;
-    my $datum = shift;
+=item * validation
 
-    # Recurse through each value in an array
-    if ( ref $datum eq 'ARRAY' ) {
-        return [ map { $self->_expand_relative_paths($_) } @$datum ];
-    } 
-    
-    # Recurse through each value in a hash
-    elsif ( ref $datum eq 'HASH' ) {
-        for my $key ( keys %$datum ) {
-            my $new_val = $self->_expand_relative_paths( $datum->{$key} );
-            $datum->{$key} = $new_val;
-        }
-        return $datum;
-    } 
-    
-    # Do nothing with other kinds of references
-    elsif ( ref $datum ) {
-        return $datum;
-    } 
-    
-    # Check scalars for %path% and convert the enclosed value to an abspath
-    else {
-        if ( defined $datum and $datum =~ /^%(.+)%$/ ) {
-            $datum = Jifty::Util->absolute_path($1);
-        }
-        return $datum;
-    }
-}
+For example check if file or module exists.
+
+=item * canonicalization
+
+For example turn relative paths into absolute or translate all possible
+variants of an option to a canonic structure
+
+=item * generation
+
+For example generate often used constructions based on other options,
+user of your app can even don't know about them
+
+=item * config upgrades
+
+Jifty has ConfigVersion option you may want to implement something like
+that in your apps
+
+=back
+
+Sub-classing is definitely not for:
+
+=over 4
+
+=item * default values
+
+You have L<so many files|/"WHY SO MANY FILES"> to allow users of your
+app and you to override defaults.
+
+=item * anything else, but configs
+
+=back
 
 =head1 WHY SO MANY FILES
 


More information about the Jifty-commit mailing list