PDA

View Full Version : Hash prefs



Robert Moser
2005-06-09, 13:13
In bug 494 (http://bugs.slimdevices.com/show_bug.cgi?id=494) the
suggested patch has a scheme to "Adds support for tri-state select of
which remotes are enabled (user-enabled, user-disabled,
slimserver-default". Well, I hated that, so I came up with an
alternative - hash preferences.

Using a hash preference for irsets, we could default to either 1 for
enabled or 0 for disabled. If new Slim Devices provided files are added
later, they could be defaulted either way.

The attached patch just has the groundwork in it, the changes to
Prefs.pm to allow for hash prefs. I still need to work on Setup.pm to
allow hash prefs to be set via the web interface.

Notable features:
getInd() used for both array prefs and hash prefs.
getHash() to get a copy of the hash pref's hash.
getKeys() to get just the keys of the hash pref.

In the preference file hash prefs are written using a % to separate the
hash preference name from the keys of the hash, so using the hash pref I
put in the default prefs (testhash, with keys (item1, itemX, itemZ) I
get the following lines in the pref file:
testhash%item1 = something
testhash%itemX = something else
testhash%itemZ = something with % in it

I really wanted to use either . or - as the separator, but the
upgrade-$version-script messed up both of those for me.

Comments?

Index: C:/Documents and Settings/vhapalmoserro/My Documents/eclipse/trunk/server/Slim/Utils/Prefs.pm
================================================== =================
--- C:/eclipse/trunk/server/Slim/Utils/Prefs.pm (revision 3358)
+++ C:/eclipse/trunk/server/Slim/Utils/Prefs.pm (working copy)
@@ -128,6 +128,7 @@
'commonAlbumTitles' => ['Greatest Hits', 'Best of...', 'Live'],
'upgrade-6.0b3-script' => 1,
'rank-PLUGIN_PICKS_MODULE_NAME' => 4,
+ 'testhash' => {'item1' => 'something', 'itemX' => 'something else', 'itemZ' => 'something with % in it' },
);

# The following hash contains functions that are executed when the pref corresponding to
@@ -415,12 +416,22 @@
sub checkServerPrefs {
for my $key (keys %DEFAULT) {
if (!defined($prefs{$key})) {
- if (ref($DEFAULT{$key} eq 'ARRAY')) {
+ if (ref($DEFAULT{$key}) eq 'ARRAY') {
my @temp = @{$DEFAULT{$key}};
$prefs{$key} = \@temp;
+ } elsif (ref($DEFAULT{$key}) eq 'HASH') {
+ my %temp = %{$DEFAULT{$key}};
+ $prefs{$key} = \%temp;
} else {
$prefs{$key} = $DEFAULT{$key};
}
+ } elsif (ref($DEFAULT{$key}) eq 'HASH') {
+ # check defaults for individual hash prefs
+ for my $subkey (keys %{$DEFAULT{$key}}) {
+ if (!defined $prefs{$key}{$subkey}) {
+ $prefs{$key}{$subkey} = $DEFAULT{$key}{$subkey};
+ }
+ }
}
}

@@ -430,6 +441,9 @@
Slim::Utils::Prefs::set("upgrade-$version-script", 0);
}
}
+
+ # write it out
+ writePrefs();
}

# This makes sure all the client preferences defined in the submitted hash are in the pref file.
@@ -442,9 +456,19 @@
if (ref($defaultPrefs->{$key}) eq 'ARRAY') {
my @temp = @{$defaultPrefs->{$key}};
$prefs{$clientkey} = \@temp;
+ } elsif (ref($defaultPrefs->{$key}) eq 'HASH') {
+ my %temp = %{$defaultPrefs->{$key}};
+ $prefs{$clientkey} = \%temp;
} else {
$prefs{$clientkey} = $defaultPrefs->{$key};
}
+ } elsif (ref($defaultPrefs->{$key}) eq 'HASH') {
+ # check defaults for individual hash prefs
+ for my $subkey (keys %{$defaultPrefs->{$key}}) {
+ if (!defined $prefs{$clientkey}{$subkey}) {
+ $prefs{$clientkey}{$subkey} = $defaultPrefs->{$key}{$subkey};
+ }
+ }
}
}
}
@@ -489,7 +513,7 @@
# getArray($arrayPref)
sub getArray {
my $arrayPref = shift;
- if (defined($prefs{($arrayPref)})) {
+ if (defined($prefs{($arrayPref)}) && ref($prefs{$arrayPref}) eq 'ARRAY') {
return @{$prefs{($arrayPref)}};
} else {
return ();
@@ -503,16 +527,58 @@
assert($client);
return getArray($client->id() . "-" . $arrayPref);
}
+
# get($pref)
sub get {
return $prefs{$_[0]}
-};
+}

# getInd($pref,$index)
+# for indexed (array or hash) prefs
sub getInd {
- return $prefs{(shift)}[(shift)];
+ my ($pref,$index) = @_;
+ if (defined $prefs{$pref}) {
+ if (ref $prefs{$pref} eq 'ARRAY') {
+ return $prefs{$pref}[$index];
+ } elsif (ref $prefs{$pref} eq 'HASH') {
+ return $prefs{$pref}{$index};
+ }
+ }
+ return undef;
}

+# getKeys($pref)
+# gets the keys of a hash pref
+sub getKeys {
+ return keys %{$prefs{(shift)}};
+}
+
+# getHash($pref)
+sub getHash {
+ my $hashPref = shift;
+ if (defined($prefs{($hashPref)}) && ref($prefs{$hashPref}) eq 'HASH') {
+ return %{$prefs{($hashPref)}};
+ } else {
+ return ();
+ }
+}
+
+# clientGetKeys($client, $hashPref)
+sub clientGetKeys {
+ my $client = shift;
+ my $hashPref = shift;
+ assert($client);
+ return getKeys($client->id() . "-" . $hashPref);
+}
+
+# clientGetHash($client, $hashPref)
+sub clientGetHash {
+ my $client = shift;
+ my $hashPref = shift;
+ assert($client);
+ return getHash($client->id() . "-" . $hashPref);
+}
+
# Ugh - this should be a method on $client.
#
# clientGet($client, $pref [,$ind])
@@ -536,7 +602,13 @@
my $key = shift;
my $ind = shift;
if (defined($ind)) {
- return $DEFAULT{$key}[$ind];
+ if (defined $DEFAULT{$key}) {
+ if (ref $DEFAULT{$key} eq 'ARRAY') {
+ return $DEFAULT{$key}[$ind];
+ } elsif (ref $DEFAULT{$key} eq 'HASH') {
+ return $DEFAULT{$key}{$ind};
+ }
+ }
}
return $DEFAULT{$key};
}
@@ -548,12 +620,22 @@

if (defined $ind) {

- if (defined($prefs{$key}[$ind]) && defined($value) && $value eq $prefs{$key}[$ind]) {
- return;
+ if (defined $prefs{$key}) {
+ if (ref $prefs{$key} eq 'ARRAY') {
+ if (defined($prefs{$key}[$ind]) && defined($value) && $value eq $prefs{$key}[$ind]) {
+ return;
+ }
+
+ $prefs{$key}[$ind] = $value;
+ } elsif (ref $prefs{$key} eq 'HASH') {
+ if (defined($prefs{$key}{$ind}) && defined($value) && $value eq $prefs{$key}{$ind}) {
+ return;
+ }
+
+ $prefs{$key}{$ind} = $value;
+ }
}

- $prefs{$key}[$ind] = $value;
-
} elsif ($key =~ /(.+?)(\d+)$/) {

# trying to set a member of an array pref directly
@@ -643,7 +725,11 @@
return;
}
if (defined($ind)) {
- splice(@{$prefs{$key}},$ind,1);
+ if (ref($prefs{$key}) eq 'ARRAY') {
+ splice(@{$prefs{$key}},$ind,1);
+ } elsif (ref($prefs{$key}) eq 'HASH') {
+ CORE::delete $prefs{$key}{$ind};
+ }
} elsif ($key =~ /(.+?)(\d+)$/) {
#trying to delete a member of an array pref directly
#re-call function the correct way
@@ -669,7 +755,13 @@
my $key = shift;
my $ind = shift;
if (defined($ind)) {
- return defined $prefs{$key}[$ind];
+ if (defined $prefs{$key}) {
+ if (ref $prefs{$key} eq 'ARRAY') {
+ return defined $prefs{$key}[$ind];
+ } elsif (ref $prefs{$key} eq 'HASH') {
+ return defined $prefs{$key}{$ind};
+ }
+ }
}
return defined $prefs{$key};
}
@@ -694,6 +786,10 @@
}
}

+sub writePending {
+ return $writePending;
+}
+
sub writePrefs {

return unless $canWrite;
@@ -723,6 +819,13 @@
print NUPREFS ($k . $i++ . " = " . $val . "\n");
}

+ } elsif (ref($prefs{$k}) eq 'HASH') {
+ for my $sk (sort keys (%{$prefs{$k}})) {
+ # for now only allow scalar prefs at this level
+ unless (ref($prefs{$k}{$sk})) {
+ print NUPREFS ($k . "%" . $sk . " = " . $prefs{$k}{$sk} . "\n");
+ }
+ }
} else {
print NUPREFS ($k . " = " . $prefs{$k} . "\n");
}
@@ -821,9 +924,14 @@
s/^\s+//; # no leading white
next unless length; #anything left?
my ($var, $value) = split(/\s=\s/, $_, 2);
- if ($var =~ /(.+?)(\d+|#)$/) {
+ if ($var =~ /^(.+?)\%(.+)$/) {
+ #part of hash
+ $prefs{$1}{$2} = $value;
+ } elsif ($var =~ /(.+?)(\d+|#)$/) {
#part of array
- unless ($2 eq '#') {
+ if ($2 eq '#') {
+ $prefs{$1} = [] if $value == -1;
+ } else {
$prefs{$1}[$2] = $value;
}
} else {
@@ -836,9 +944,6 @@
# see if we can write out the real prefs file
$canWrite = (-e prefsFile() && -w prefsFile()) || (-w preferencesPath());

- # write it out no matter what.
- writePrefs();
-
if (!$canWrite && !$nosetup) {
msg("Cannot write to preferences file $prefsFile, any changes made will not be preserved for the next startup of the server\n");
}

Dan Sully
2005-06-09, 14:36
* Robert Moser shaped the electrons to say...

>In bug 494 (http://bugs.slimdevices.com/show_bug.cgi?id=494) the
>suggested patch has a scheme to "Adds support for tri-state select of
>which remotes are enabled (user-enabled, user-disabled,
>slimserver-default". Well, I hated that, so I came up with an
>alternative - hash preferences.

I would prefer to move to using YAML as the format for the prefs file - it's
basically a human readable perl-serializer, so we get hash's, etc for free.

The other alternative I know everyone loves is XML. Round one, fight!

-D
--
"My pockets hurt." - Homer Simpson

Robert Moser
2005-06-09, 15:04
Dan Sully wrote:
> I would prefer to move to using YAML as the format for the prefs file -
> it's
> basically a human readable perl-serializer, so we get hash's, etc for free.
>
> The other alternative I know everyone loves is XML. Round one, fight!
>
> -D

YAML would be fine with me, I was just hesitant to break backwards
compatibility of the prefs file.

I'll look into reading/writing YAML from perl, and put it into patch form.

Robert Moser
2005-06-09, 16:27
Robert Moser wrote:
> Dan Sully wrote:
>
>> I would prefer to move to using YAML as the format for the prefs file
>> - it's
>> basically a human readable perl-serializer, so we get hash's, etc for
>> free.
>>
>> The other alternative I know everyone loves is XML. Round one, fight!
>>
>> -D
>
>
> YAML would be fine with me, I was just hesitant to break backwards
> compatibility of the prefs file.
>
> I'll look into reading/writing YAML from perl, and put it into patch form.

YAML is pretty easy to use, unfortunately, we break the CPAN version of
YAML.pm with our default timeFormat preference. I'll need to fix that
before putting together a patch. I've already got something that can
deal with either the current style of preferences or a hand-corrected
YAML version.

Dan Sully
2005-06-09, 18:08
* Robert Moser shaped the electrons to say...

>YAML is pretty easy to use, unfortunately, we break the CPAN version of
>YAML.pm with our default timeFormat preference. I'll need to fix that
>before putting together a patch. I've already got something that can
>deal with either the current style of preferences or a hand-corrected
>YAML version.

Does it just need quoting?

-D
--
<phone> i am a sausage fan

Robert Moser
2005-06-09, 19:28
Dan Sully blurted out:
> * Robert Moser shaped the electrons to say...
>
>> YAML is pretty easy to use, unfortunately, we break the CPAN version
>> of YAML.pm with our default timeFormat preference. I'll need to fix
>> that before putting together a patch. I've already got something that
>> can deal with either the current style of preferences or a
>> hand-corrected YAML version.
>
>
> Does it just need quoting?
>
> -D

Yes, but the problem is that YAML.pm should be quoting it when it writes
it out to the file, but it doesn't. I found where I think the problem
is and have sent a patch to the maintainer.

Attached is a new patch which incorporates YAML as the pref file format,
and allows for hash preferences.

Also in there are the YAML 0.39 files from CPAN, with the change I made
to avoid the bug.

The change was to line 592 of YAML.pm and consisted of adding a | to a
character class used to determine if quoting was necessary (inside the
is_valid_plain() function).

Before:
return 0 if $_[0] =~ /^[\s\{\[\~\`\'\"\!\@\#\%\&\*\^]/;
After:
return 0 if $_[0] =~ /^[\|\s\{\[\~\`\'\"\!\@\#\%\&\*\^]/;

Robert Moser
2005-06-09, 19:28
Helps if I actually attach the file