Skip Menu |

This queue is for tickets about the Config-General CPAN distribution.

Report information
The Basics
Id: 125912
Status: open
Priority: 0/
Queue: Config-General

People
Owner: tlinden [...] cpan.org
Requestors: himynameisnolan [...] gmail.com
Cc:
AdminCc:

Bug Information
Severity: Wishlist
Broken in: 2.63
Fixed in: (no value)



Subject: Retain association of left hand block and right hand block.
Hello, I wasn't able to find a way to retain the association of a left hand block name and right hand block parameters. For example, given the following two distinct code snippets, the resultant data structure would be the same: Snippet 1: <Foo "bar"> Blah Blah </Foo> Snippet 2: <Foo> <Bar> Blah Blah </Bar> </Foo> The returned data structure would be something like: $VAR1= { 'Foo' => { 'Bar' => { 'Blah' => 'Blah', }, }, }; From an Apache standpoint, there's a significant difference in meaning that is lost when examining the data structure. Is Bar supposed to be parameters that are associated with the Foo section? Or are Foo and Bar completely distinct sections? If we had a way to retain the association between block names, and block parameters when examining the resulting data structure, that would be very helpful. Thank you, Nolan
Hello, I made a quick fix for this, which is just to add another new() option called: -NormalizeBlockName. It behaves the same way as NormalizeBlock, but it's for BlockNames specifically. This lets me attach a subref which can prefix the blockname distinctly from blocks. Code Snippet: if($this->{NormalizeBlock}) { $block = $this->{NormalizeBlock}($block); } if($this->{NormalizeBlockName}) { if (defined $blockname) { $blockname = $this->{NormalizeBlockName}($blockname); if($blockname eq "") { # if, after normalization no blockname is left, remove it $blockname = undef; } } } Also added a default to the my $self={} declaration at the top of the file: NormalizeBlock => 0, NormalizeBlockName => 0, If something like this could be added to the official package, that'd be nice. I'm currently using my own custom fork of the package and it feels so dirty. Thanks, Nolan On Mon Jul 23 12:10:42 2018, himynameisnolan@gmail.com wrote: Show quoted text
> Hello, > > I wasn't able to find a way to retain the association of a left hand > block name and right hand block parameters. For example, given the > following two distinct code snippets, the resultant data structure > would be the same: > > Snippet 1: > <Foo "bar"> > Blah Blah > </Foo> > > Snippet 2: > <Foo> > <Bar> > Blah Blah > </Bar> > </Foo> > > The returned data structure would be something like: > $VAR1= { > 'Foo' => { > 'Bar' => { > 'Blah' => 'Blah', > }, > }, > }; > > From an Apache standpoint, there's a significant difference in meaning > that is lost when examining the data structure. Is Bar supposed to be > parameters that are associated with the Foo section? Or are Foo and > Bar completely distinct sections? > > If we had a way to retain the association between block names, and > block parameters when examining the resulting data structure, that > would be very helpful. > > Thank you, > > Nolan
Hello Nolan, On Mon Jul 23 13:31:49 2018, himynameisnolan@gmail.com wrote: Show quoted text
> I made a quick fix for this
Can you please provide a patch file, you code snippet seems to be incomplete. Thanks, Tom
Hello, Thanks so much for getting back to me Tom. I've attached a patch file. There are a few other things I included in the patch that I think would be cool to get in, if you wanted to review them and make a decision. An overview: I've attempted to provide options that allow the data structure to be more predictably organized for easier programmatic traversal: -ForceBlockName *Ensures that blocks are always stored with a left-hand and right-hand value. If there is no right-hand value, the right-hand value key will simply be undefined. Example: Markup 1: <Foo "bar"> Blah Blah </Foo> Would produce this data structure: $VAR1= { 'Foo' => { 'Bar' => { 'Blah' => 'Blah', }, }, }; Markup 2: <Foo> <Bar> Blah Blah </Bar> </Foo> Would produce this data structure: $VAR1= { 'Foo' => { undef => { 'Bar' => { undef => { 'Blah' => 'Blah', }, }, }, }, }; This way, you could create a map that leads directly the the option value using the keys of the structure which would look like this: my @map=('Foo', undef, 'Bar', undef', 'Blah'); This predictable structure allow us to maintain the association of the block and any provided block name. Provided the last key points to an option value, you can use all elements in the map (minus the last one) to create a list of key/value pairs for every layer of block context, where the key is the block, and the value is the block name: use Tie::DxHash; my $sections={}; tie %{$sections}, "Tie::DxHash"; %{$sections}=@map; You could then use each key/value pair to programmatically evaluate the meaning of every block. -ForceValueArray *Forces single VALUES to be stored in an array. I took out the syntax requirement (enclosing the option value in square brackets in the config file), because I feel the point of this option is to allow the programmer to always expect option values to be stored in an array (rather than sometimes being an array and sometimes being a hash) when programmatically traversing the data structure. I don't think arbitrary requirements should be placed on the manager of the config file (who might not be affiliated with the programmer) to facilitate this. -ForceBlockArray *Forces single BLOCKS to be stored in an array. This is the same idea as ForceValueArray, but it applies specifically to the storage of blocks. In the current release of Config::General, arrays will be generated in the hash structure if there are duplicate elements, but arrays will not be generated if there is only one element. For example, two VirtualHost blocks for two distinct users on a shared IP address: <VirtualHost 192.168.1.10:80> ServerName example-a.com DocumentRoot /home/a/public_html </VirtualHost> <VirtualHost 192.168.1.10:80> ServerName example-b.com DocumentRoot /home/b/public_html </VirtualHost> ...Create a data structure that looks like this: $VAR1 = { 'VirtualHost' => { '192.168.1.10:80' => [ #Array of Hashes { 'ServerName' => 'example-a.com', 'DocumentRoot' => '/home/a/public_html', }, { 'ServerName' => 'example-b.com', 'DocumentRoot' => '/home/b/public_html', }, ], }, }; Whereas a single VirtualHost block for a single user on a dedicated IP address...: <VirtualHost 192.168.1.120:80> ServerName example-c.com DocumentRoot /home/c/public_html </VirtualHost> ...Produces a data structure that looks like this: $VAR1 = { 'VirtualHost' => { '192.168.1.10:80' => { #No array 'ServerName' => 'example-c.com', 'DocumentRoot' => '/home/c/public_html', }, }, }; This variation in the data's internal structure makes it difficult to programmatically traverse the structure... ForceBlockArray would ensure that the programmer can expect all elements contained in a block will be found in the array that is directly following the block: The dedicated IP example: <VirtualHost 192.168.1.120:80> ServerName example-c.com DocumentRoot /home/c/public_html </VirtualHost> ...Would then produce a structure that looks like this: $VAR1 = { 'VirtualHost' => { '192.168.1.10:80' => { [ #For an array 'ServerName' => 'example-c.com', 'DocumentRoot' => '/home/c/public_html', ], }, }, }; -NormalizeBlockName *This is just another way to provide distinction between blocks and block names. Example: -NormalizeBlock => sub { my $x = shift; $x =~ s/^/BLOCK:/; $x; } -NormalizeBlockName => sub { my $x = shift; $x =~ s/^/BLOCKNAME:/; $x; } -NormalizeOption => sub { my $x = shift; $x =~ s/^/OPTION:/; $x; } -NormalizeValue => sub { my $x = shift; $x =~ s/^/BLOCKVALUE:/; $x; } I've attached the .patch file with all my changes. On Tue Jul 24 08:50:24 2018, TLINDEN wrote: Show quoted text
> Hello Nolan, > > On Mon Jul 23 13:31:49 2018, himynameisnolan@gmail.com wrote:
> > I made a quick fix for this
> > Can you please provide a patch file, you code snippet seems to be > incomplete. > > > Thanks, > Tom
Subject: General.pm.patch
--- orig/General.pm 2018-07-25 10:06:25.147883737 -0600 +++ new/General.pm 2018-07-25 10:20:43.594506937 -0600 @@ -89,10 +89,13 @@ files => {}, # which files we have read, if any UTF8 => 0, SaveSorted => 0, - ForceArray => 0, # force single value array if value enclosed in [] + ForceValueArray => 0, # force values to be stored in an array + ForceBlockArray => 0, # force block contents to be stored in an array + ForceBlockName => 0, # force undefined block name if no block name is provided. AllowSingleQuoteInterpolation => 0, NoEscape => 0, NormalizeBlock => 0, + NormalizeBlockName => 0, NormalizeOption => 0, NormalizeValue => 0, Plug => {}, @@ -900,7 +903,7 @@ $option = $this->{NormalizeOption}($option); } - if ($value && $value =~ /^"/ && $value =~ /"$/) { + if (defined $value && $value =~ /^"/ && $value =~ /"$/) { $value =~ s/^"//; # remove leading and trailing " $value =~ s/"$//; } @@ -920,8 +923,10 @@ } if($this->{NormalizeBlock}) { $block = $this->{NormalizeBlock}($block); + } + if($this->{NormalizeBlockName}) { if (defined $blockname) { - $blockname = $this->{NormalizeBlock}($blockname); + $blockname = $this->{NormalizeBlockName}($blockname); if($blockname eq "") { # if, after normalization no blockname is left, remove it $blockname = undef; @@ -988,9 +993,12 @@ } } else { - if($this->{ForceArray} && defined $value && $value =~ /^\[\s*(.+?)\s*\]$/) { + if($this->{ForceValueArray}) { + if(defined $value) { + ($value) = $value =~ /^(?>\[\s*+)?(.+?)(?>\s*+\])?$/; + } # force single value array entry - push @{$config->{$option}}, $this->_parse_value($config, $option, $1); + push @{$config->{$option}}, $this->_parse_value($config, $option, $value); } else { # standard config option, insert key/value pair into node @@ -1014,7 +1022,7 @@ push @newcontent, $_; # push onto new content stack } else { # calling myself recursively, end of $block reached, $block_level is 0 - if (defined $blockname) { + if (defined $blockname or $this->{ForceBlockName}) { # a named block, make it a hashref inside a hash within the current node if (! exists $config->{$block}) { @@ -1070,7 +1078,12 @@ $tmphash->{__stack} = $this->_copy($config->{__stack}); } - $config->{$block}->{$blockname} = $this->_parse($tmphash, \@newcontent); + if ($this->{ForceBlockArray}) { + push @{$config->{$block}->{$blockname}}, $this->_parse($tmphash, \@newcontent); + } + else { + $config->{$block}->{$blockname} = $this->_parse($tmphash, \@newcontent); + } } } else { @@ -1124,7 +1137,12 @@ $tmphash->{__stack} = $this->_copy($config->{__stack}); } - $config->{$block} = $this->_parse($tmphash, \@newcontent); + if ($this->{ForceBlockArray}) { + push @{$config->{$block}}, $this->_parse($tmphash, \@newcontent); + } + else { + $config->{$block} = $this->_parse($tmphash, \@newcontent); + } } } undef $blockname; @@ -1346,18 +1364,12 @@ } if (ref($config->{$entry}) eq 'ARRAY') { - if( $this->{ForceArray} && scalar @{$config->{$entry}} == 1 && ! ref($config->{$entry}->[0]) ) { - # a single value array forced to stay as array - $config_string .= $this->_write_scalar($level, $entry, '[' . $config->{$entry}->[0] . ']'); - } - else { - foreach my $line ( $this->{SaveSorted} ? sort @{$config->{$entry}} : @{$config->{$entry}} ) { - if (ref($line) eq 'HASH') { - $config_string .= $this->_write_hash($level, $entry, $line); - } - else { - $config_string .= $this->_write_scalar($level, $entry, $line); - } + foreach my $line ( $this->{SaveSorted} ? sort @{$config->{$entry}} : @{$config->{$entry}} ) { + if (ref($line) eq 'HASH') { + $config_string .= $this->_write_hash($level, $entry, $line); + } + else { + $config_string .= $this->_write_scalar($level, $entry, $line); } } } @@ -2103,6 +2115,10 @@ This removes trailing whitespaces of block names. +=item B<-NormalizeBlockName> + +Same as B<-NormalizeBlock> but applied on block names only. + =item B<-NormalizeOption> Same as B<-NormalizeBlock> but applied on options only. @@ -2472,13 +2488,24 @@ =head2 FORCE SINGLE VALUE ARRAYS You may also force a single config line to get parsed into an array by -turning on the option B<-ForceArray> and by surrounding the value of the -config entry by []. Example: +turning on the option B<-ForceValueArray>. Example: - hostlist = [ foo.bar ] + hostlist = foo.bar Will be a singlevalue array entry if the option is turned on. If you want -it to remain to be an array you have to turn on B<-ForceArray> during save too. +it to remain to be an array you have to turn on B<-ForceValueArray> during save too. + +=head2 FORCE SINGLE BLOCK ARRAYS + +You may also force a single config block to get parsed into an array by +turning on the option B<-ForceBlockArray>. Example: + + <VirtualHost 192.168.1.10:80> + ServerName localhost.com + DocumentRoot /usr/local/apache/htdocs +</VirtualHost> + +Will be a singleblock array entry if the option is turned on. =head1 LONG LINES
No, sorry. One patch for one issue. Also, those other changes would break existing code.