Home of the Squeezebox™ & Transporter® network music players.
Results 1 to 10 of 10
  1. #1
    Senior Member
    Join Date
    Apr 2005
    Location
    UK/London
    Posts
    4,959

    How to determine if menu contains code reference

    I am caching menus in a plugin ... but sometimes the menu contains a code reference in style:
    Code:
    $menu->{type} = 'link';
    $menu->{url} = \&getMoreData;
    
    
    sub getMoreData{
    
    etc etc
    
    }
    If I try to cache this then it fails because of the code reference - with an error something like
    Code:
    Can't store CODE items at /usr/local/lib/perl5/5.32.0/arm-linux-gnueabihf-thread-multi-64int/Storable.pm line 370, at /usr/local/slimserver/Slim/Utils/DbCache.pm line 69.
    I understand the reason, but how can I test for the presence of that reference in style "\&getMoreData;" before I try to cache it so that I don't try to cache it.
    Of course, I could set a flag to say that there is a code reference when I put it in there and then check for it but am after a more generic solution.
    However, the menu structure code be deep and the problem could be some levels down and would be easy to make a mistake.

    From the code in Storable.pm it looks like I have to do an "eval" and trap it - but it would help if someone could explain it a bit.
    Code:
     # Call C routine mstore or net_mstore, depending on network order
        eval { $ret = &$xsptr($self) };
        if ($@) {
            $@ =~ s/\.?\n$/,/ unless ref $@;
            logcroak $@;
        }
        $@ = $da;
    Paul Webster
    Author of "Now Playing" plugins covering Radio France (FIP etc), PlanetRadio (Bauer - Kiss, Absolute, Scala, JazzFM etc), KCRW, ABC Australia and CBC/Radio-Canada
    and, via the extra "Radio Now Playing" plugin lots more - see https://forums.slimdevices.com/showt...Playing-plugin

  2. #2
    Babelfish's Best Boy mherger's Avatar
    Join Date
    Apr 2005
    Location
    Switzerland
    Posts
    20,629

    How to determine if menu contains code reference

    > I understand the reason, but how can I test for the presence of that
    > reference in style "\&getMoreData;" before I try to cache it so that I
    > don't try to cache it.


    if ((ref $menu->{url} || '') ne 'CODE') {}

    Please note that "ref $var" is not always defined, eg. on simple scalar
    values. Therefore I'd either check ref was defined, or use the result of
    ref or an empty string (as above).

    I usually do cache at the data level. Eg. if I need to download
    something I'd cache the download. Or if I had to process the downloaded
    data then I'd cache the processed data. I don't cache full menu
    structures, because there all too often are these code references. I'd
    rather have that code reference take care of caching and using cached data.
    Last edited by mherger; 2021-04-13 at 02:28.

  3. #3
    Senior Member
    Join Date
    Oct 2005
    Location
    Ireland
    Posts
    20,871
    The ref function return "CODE" if the reference is to CODE - otherwise it will be something like ARRAY, HASH etc.

    edit:

    I'd use eval if you are trying to execute the reference as "eval" will trap any error and prevent processing being halted.
    Last edited by bpa; 2021-04-13 at 02:26.

  4. #4
    jvromans@squirrel.nl
    Guest

    How to determine if menu contains code reference

    On Tue, 13 Apr 2021 11:22:39 +0200, Michael Herger <slim (AT) herger (DOT) net> wrote:

    > Please note that "ref $var" is not always defined, eg. on simple scalar
    > values.


    AFAIK, "ref $var" is always defined and returns an empty string on simple
    scalar values and undefined.


  5. #5
    Senior Member
    Join Date
    Apr 2005
    Location
    UK/London
    Posts
    4,959
    Menu could be nested though ... with the "problem" deeper down so I don't think I can use
    ref $menu->{url}
    because it might be in a lower level items[] ... and I wanted to avoid writing more code to navigate down through the list.
    Paul Webster
    Author of "Now Playing" plugins covering Radio France (FIP etc), PlanetRadio (Bauer - Kiss, Absolute, Scala, JazzFM etc), KCRW, ABC Australia and CBC/Radio-Canada
    and, via the extra "Radio Now Playing" plugin lots more - see https://forums.slimdevices.com/showt...Playing-plugin

  6. #6
    Babelfish's Best Boy mherger's Avatar
    Join Date
    Apr 2005
    Location
    Switzerland
    Posts
    20,629

    How to determine if menu contains code reference

    > Menu could be nested though ... with the "problem" deeper down so I
    > don't think I can use
    > ref $menu->{url}
    > because it might be in a lower level items[] ... and I wanted to avoid
    > writing more code to navigate down through the list.


    That's why I would not try to cache the menu structure, but only the
    data used by it. It might cost you a few CPU cycles, but not enough to
    be worried about.

  7. #7
    Senior Member
    Join Date
    Apr 2005
    Location
    UK/London
    Posts
    4,959
    Inefficient but I think this works without groak messages that come out of Storable.

    Code:
    use strict;
    use warnings;
    use Storable     qw( freeze thaw );
    use Data::Dumper;
    my $dumped;
    
    sub subfunction {
        return shift;    
    }
    
    my $menu = { 'hello' => 'world',
              #'sub' => \&subfunction
              'sub' => 'text'
            };
    
    sub isStorable {
        my $data = shift;
        my $ret = 1;
        eval { my $serial = Storable::mstore(\$data) };
        
        if ($@) { 
            #print("Error: $@\n");
            $ret = 0;
        }
    
        if ($ret) {
            print("storable\n")
        } else { print("not storable\n") };
    
        return $ret
    }
    
    $dumped = Dumper \$menu;
    print("menu: $dumped is ... ");
    isStorable($menu);
    
    
    $menu->{sub} = \&subfunction;
    $dumped = Dumper \$menu;
    print("menu: $dumped is ... ");
    isStorable($menu);
    output:
    Code:
    menu: $VAR1 = \{
                'hello' => 'world',
                'sub' => 'text'
              };
     is ... storable
    menu: $VAR1 = \{
                'hello' => 'world',
                'sub' => sub { "DUMMY" }
              };
     is ... not storable
    I think I'll go with something like this for now while I work out how easy it would be to switch to caching the fetched data (some of which can be a few tens of kb) rather than caching the dynamic menus.
    Paul Webster
    Author of "Now Playing" plugins covering Radio France (FIP etc), PlanetRadio (Bauer - Kiss, Absolute, Scala, JazzFM etc), KCRW, ABC Australia and CBC/Radio-Canada
    and, via the extra "Radio Now Playing" plugin lots more - see https://forums.slimdevices.com/showt...Playing-plugin

  8. #8
    Senior Member
    Join Date
    Aug 2014
    Location
    UK
    Posts
    561
    Quote Originally Posted by Paul Webster View Post
    I am caching menus in a plugin ... but sometimes the menu contains a code reference in style:
    Code:
    $menu->{type} = 'link';
    $menu->{url} = \&getMoreData;
    
    
    sub getMoreData{
    
    etc etc
    
    }
    If I try to cache this then it fails because of the code reference - with an error something like
    Code:
    Can't store CODE items at /usr/local/lib/perl5/5.32.0/arm-linux-gnueabihf-thread-multi-64int/Storable.pm line 370, at /usr/local/slimserver/Slim/Utils/DbCache.pm line 69.
    I understand the reason, but how can I test for the presence of that reference in style "\&getMoreData;" before I try to cache it so that I don't try to cache it.
    Of course, I could set a flag to say that there is a code reference when I put it in there and then check for it but am after a more generic solution.
    However, the menu structure code be deep and the problem could be some levels down and would be easy to make a mistake.

    From the code in Storable.pm it looks like I have to do an "eval" and trap it - but it would help if someone could explain it a bit.
    Code:
     # Call C routine mstore or net_mstore, depending on network order
        eval { $ret = &$xsptr($self) };
        if ($@) {
            $@ =~ s/\.?\n$/,/ unless ref $@;
            logcroak $@;
        }
        $@ = $da;

    The method I use is to always set the url as an empty string (if I know it is a menu that can be cached) and put the code ref item as a string in the passthrough (as a place holder). Then I always call a method to populate the code reference before passing a menu back for a callback (whether it was retrieved from cache or not). This method is recursive to go down an items tree. It really takes much less cpu time than you would expect and I've recently amended it to make it slightly more efficient in the coderef populating routine by using a hash rather than a series of if/elsifs.

    You can see the code in the BBC sounds plugin :
    Here is the method that populates the coderefs : https://github.com/expectingtofly/LM...eeder.pm#L1548

    Here is an example of where I construct the menu (and cache it): https://github.com/expectingtofly/LM...sFeeder.pm#L88
    Last edited by expectingtofly; 2021-04-13 at 10:34.

  9. #9
    Senior Member
    Join Date
    Apr 2005
    Location
    UK/London
    Posts
    4,959
    It was your code that I used for the caching routines in the first place ... so hopefully it should be easy enough to adapt/update.
    Paul Webster
    Author of "Now Playing" plugins covering Radio France (FIP etc), PlanetRadio (Bauer - Kiss, Absolute, Scala, JazzFM etc), KCRW, ABC Australia and CBC/Radio-Canada
    and, via the extra "Radio Now Playing" plugin lots more - see https://forums.slimdevices.com/showt...Playing-plugin

  10. #10
    Senior Member
    Join Date
    Aug 2014
    Location
    UK
    Posts
    561
    Quote Originally Posted by Paul Webster View Post
    It was your code that I used for the caching routines in the first place ... so hopefully it should be easy enough to adapt/update.
    cool, let me know if you need any further explanation/info.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •