Re: Indentation (was Re: invalid installdirs values)

[email protected] ("David E. Wheeler")
Newsgroups perl.module.build
Message-ID <[email protected]>
On Sep 27, 2008, at 14:11, Michael G Schwern wrote:

> Try using diff -b.  It filters out whitespace changes making it  
> easier to see
> the significant changes.

I used `svn diff -x '-ub'. Attached.

> At the risk of starting a flamewar, can we reindent the whole thing  
> at 4
> characters?  The 2 char indent means we're constantly going to have to
> reindent submissions.

+1 YES, PLEASE! This is the indentation I always use. Also, no tabs  
(there are lots in the source).

Thanks,

David
add_property.patch (application/octet-stream, 8 KB)
Index: lib/Module/Build/Base.pm
===================================================================
--- lib/Module/Build/Base.pm	(revision 11885)
+++ lib/Module/Build/Base.pm	(working copy)
@@ -665,7 +665,9 @@
     sub valid_properties_defaults {
       my %out;
       for (reverse shift->_mb_classes) {
-	@out{ keys %{ $valid_properties{$_} } } = values %{ $valid_properties{$_} };
+	@out{ keys %{ $valid_properties{$_} } } = map {
+            $_->()
+        } values %{ $valid_properties{$_} };
       }
       return \%out;
     }
@@ -685,55 +687,67 @@
     }
 
     sub add_property {
-      my ($class, $property, $default) = @_;
-      die "Property '$property' already exists" if $class->valid_property($property);
+        my ($class, $property) = (shift, shift);
+        die "Property '$property' already exists"
+            if $class->valid_property($property);
+        my %p = @_ == 1 ? ( default => shift ) : @_;
 
-      $valid_properties{$class}{$property} = $default;
+        my $type = ref $p{default};
+        $valid_properties{$class}{$property} = $type eq 'CODE'
+            ? $p{default}
+            : sub { $p{default} };
 
-      my $type = ref $default;
-      if ($type) {
-	push @{$additive_properties{$class}->{$type}}, $property;
-      }
+        push @{$additive_properties{$class}->{$type}}, $property
+            if $type;
 
-      unless ($class->can($property)) {
-        no strict 'refs';
-	if ( $type eq 'HASH' ) {
-          *{"$class\::$property"} = sub {
-            # XXX this needs 'use strict' again
+        return $class if $class->can($property);
+
+        my $check = $p{check} || sub { 1 };
+        my $sub = $type eq 'HASH' ? sub {
 	    my $self = shift;
-	    my $x = $self->{properties};
+            # Allow to work in class or object context.
+            my $x = do { no strict 'refs'; $self->{properties} };
 	    return $x->{$property} unless @_;
 
-	    if ( defined($_[0]) && !ref($_[0]) ) {
+            my $prop = $x->{$property};
+            if ( defined $_[0] && !ref $_[0] ) {
 	      if ( @_ == 1 ) {
-		return exists( $x->{$property}{$_[0]} ) ?
-		         $x->{$property}{$_[0]} : undef;
+                    return exists $prop->{$_[0]} ? $prop->{$_[0]} : undef;
               } elsif ( @_ % 2 == 0 ) {
-	        my %args = @_;
-	        while ( my($k, $v) = each %args ) {
-	          $x->{$property}{$k} = $v;
-	        }
+                    my %new = (%{ $prop }, @_);
+                    local $_ = \%new;
+                    $x->{$property} = \%new if $check->($self);
+                    return $x->{$property};
 	      } else {
 		die "Unexpected arguments for property '$property'\n";
 	      }
 	    } else {
-	      $x->{$property} = $_[0];
+                die "Unexpected arguments for property '$property'\n"
+                    if defined $_[0] && ref $_[0] ne 'HASH';
+                local $_ = $_[0];
+                $x->{$property} = shift if $check->($self);
 	    }
+        } : sub {
+            my $self = shift;
+            # Allow to work in class or object context.
+            my $x = do { no strict 'refs'; $self->{properties} };
+            return $x->{$property} unless @_;
+            local $_ = $_[0];
+            $x->{$property} = shift if $check->($self);
+            return $x->{$property};
 	  };
 
-        } else {
-          *{"$class\::$property"} = sub {
-            # XXX this needs 'use strict' again
-	    my $self = shift;
-	    $self->{properties}{$property} = shift if @_;
-	    return $self->{properties}{$property};
-	  }
-        }
+        no strict 'refs';
+        *{"$class\::$property"} = $sub;
 
-      }
       return $class;
     }
 
+    sub property_error {
+        my $self = shift;
+        die 'ERROR: ', @_;
+    }
+
     sub _set_defaults {
       my $self = shift;
 
@@ -772,7 +786,6 @@
 __PACKAGE__->add_property(build_bat => 0);
 __PACKAGE__->add_property(config_dir => '_build');
 __PACKAGE__->add_property(include_dirs => []);
-__PACKAGE__->add_property(installdirs => 'site');
 __PACKAGE__->add_property(metafile => 'META.yml');
 __PACKAGE__->add_property(recurse_into => []);
 __PACKAGE__->add_property(use_rcfile => 1);
@@ -782,6 +795,20 @@
 __PACKAGE__->add_property(test_file_exts => ['.t']);
 __PACKAGE__->add_property(use_tap_harness => 0);
 __PACKAGE__->add_property(tap_harness_args => {});
+__PACKAGE__->add_property(
+    'installdirs',
+    default => 'site',
+    check   => sub {
+        return 1 if /^(core|site|vendor)$/;
+        return shift->property_error(
+            $_ eq 'perl'
+            ? 'Perhaps you meant installdirs to be "core" rather than "perl"?'
+            : 'installdirs must be one of "core", "site", or "vendor"'
+        );
+        return shift->property_error("Perhaps you meant 'core'?") if $_ eq 'perl';
+        return 0;
+    },
+);
 
 {
   my $Is_ActivePerl = eval {require ActivePerl::DocTools};
Index: lib/Module/Build/API.pod
===================================================================
--- lib/Module/Build/API.pod	(revision 11885)
+++ lib/Module/Build/API.pod	(working copy)
@@ -791,9 +791,89 @@
 defaults to C<MyModuleBuilder>.  The C<code> parameter specifies Perl
 code to use as the body of the subclass.
 
+=item add_property
+
+[version 0.31]
+
+  package 'My::Build';
+  use base 'Module::Build';
+  __PACKAGE__->add_property( 'pedantic' );
+  __PACKAGE__->add_property( answer => 42 );
+  __PACKAGE__->add_property(
+      'epoch',
+      default => sub { time },
+      check   => sub {
+          return 1 if /^\d+$/;
+          shift->property_error( "'$_' is not an epoch time" );
+          return 0;
+      },
+  );
+
+Adds a property to a Module::Build class. Properties are those attributes of a
+Module::Build object which can be passed to the constructorand which have
+accessors to get and set them. All of the core properties, such as
+C<module_name> and C<license>, are defined using this class method.
+
+The first argument to C<add_property()> is always the name of the property.
+The second argument can be either a default value for the property, or a list
+of key/value pairs. The supported keys are:
+
+=over
+
+=item C<default>
+
+The default value. May optionally be specified as a code reference, in which
+case the return value from the execution of the code reference will be used.
+If you need the default to be a code reference, just use a code reference to
+return it, e.g.:
+
+      default => sub { sub { ... } },
+
+=item C<check>
+
+A code reference that checks that a value specified for the property is valid.
+During the execution of the code reference, the new value will be included in
+the C<$_> variable. If the value is correct, the C<check> code reference
+should return true. If the value is not correct, it send an error message to
+C<property_error()> and return false.
+
 =back
 
+When this method is called, a new property will be installed in the
+Module::Build class, and an accessor will be built to allow the property to be
+get or set on the build object.
 
+  print $build->pedantic, $/;
+  $build->pedantic(0);
+
+If the default value is a hash reference, this generetes a special-case
+accessor method, wherein individual key/value pairs may be set or fetched:
+
+  print "stuff{foo} is: ", $build->stuff( 'foo' ), $/;
+  $build->stuff( foo => 'bar' );
+  print $build->stuff( 'foo' ), $/; # Outputs "bar"
+
+Of course, you can still set the entire hash reference at once, as well:
+
+  $build->stuff( { foo => 'bar', baz => 'yo' } );
+
+In either case, if a C<check> has been specified for the property, it will be
+applied to the entire hash. So the check code reference should look something
+like:
+
+      check => sub {
+            return 1 if defined $_ && exists $_->{foo};
+            shift->property_error(qq{Property "stuff" needs "foo"});
+            return 0;
+      },
+
+=item property_error
+
+[version 0.31]
+
+=back
+
+
 =head2 METHODS
 
 =over 4
@@ -883,7 +963,6 @@
 compiling & linking C code.  If no such object is available (e.g. if
 the system has no compiler installed) an exception will be thrown.
 
-
 =item check_installed_status($module, $version)
 
 [version 0.11]
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.