in reply to Re: Help to override Spreadsheet/ParseXLSX.pm module
in thread Help to override Spreadsheet/ParseXLSX.pm module

Thanks Rolf!
So I think I subclassed it with a patch module :)

For my reference:
I copied the ParseXLSX.pm module to CE_ParseXLSX_patch.pm then I removed all the subs which I did not modify, so I was left with new and _parseworkbook

I located CE_ParseXLSX_patch.pm to a new directory in my repo: COMMON/Spreadsheet/ COMMON is in @INC
Then I added a use Spreadsheet::CE_ParseXLSX_patch in the original module that had use Spreadsheet::Read
When I run the code, I get the warning that 2 subs are overridden,
And my modifications work!

Subroutine new redefined at ../Spreadsheet/CE_ParseXLSX_patch.pm line +19. at ../Spreadsheet/CE_ParseXLSX_patch.pm line 19. require Spreadsheet/CE_ParseXLSX_patch.pm called at ../portable_sp +readsheet_subs.pm line 4 main::BEGIN() called at ../Spreadsheet/CE_ParseXLSX_patch.pm line +19 eval {...} called at ../Spreadsheet/CE_ParseXLSX_patch.pm line 19 require portable_spreadsheet_subs.pm called at ../write_csv_ss.pm +line 3 require write_csv_ss.pm called at ../renameRules.pm line 5 require renameRules.pm called at ../simpleBga.pm line 3 require simpleBga.pm called at ../pin_report_modules.pm line 2 require pin_report_modules.pm called at ../read_pin_report.pm line + 3 require read_pin_report.pm called at d:/GitHub/CE_TOOLS/COMMON/TES +TS/test_acust_daisy_chain.pl line 35 Subroutine _parse_workbook redefined at ../Spreadsheet/CE_ParseXLSX_pa +tch.pm line 37. at ../Spreadsheet/CE_ParseXLSX_patch.pm line 37. require Spreadsheet/CE_ParseXLSX_patch.pm called at ../portable_sp +readsheet_subs.pm line 4 main::BEGIN() called at ../Spreadsheet/CE_ParseXLSX_patch.pm line +37 eval {...} called at ../Spreadsheet/CE_ParseXLSX_patch.pm line 37 require portable_spreadsheet_subs.pm called at ../write_csv_ss.pm +line 3 require write_csv_ss.pm called at ../renameRules.pm line 5 require renameRules.pm called at ../simpleBga.pm line 3 require simpleBga.pm called at ../pin_report_modules.pm line 2 require pin_report_modules.pm called at ../read_pin_report.pm line + 3 require read_pin_report.pm called at d:/GitHub/CE_TOOLS/COMMON/TES +TS/test_acust_daisy_chain.pl line 35

Replies are listed 'Best First'.
Re^3: Help to override Spreadsheet/ParseXLSX.pm module (monkey-patching)
by LanX (Saint) on Apr 03, 2021 at 12:42 UTC
    It's not clear to me how you did the "sub-classing", by inheriting methods or by importing or a do or ...?

    Anyway, as I already said, I prefer monkey-patching over subclassing and you won't need to create another module/namespace for it.

    Sub-classing requires that the author played by "the" book, and the OOP book is indeed a TIMTOWTDI library in Perl.

    Monkey-patching OTOH is well defined and has no dependency to any OOP model.

    So in our own interest consider

    sub call_patched { local *Spreadsheet::ParseXLSX::new = \&patched_new; local *Spreadsheet::ParseXLSX::_parse_workbook = \&patched_parse_w +orkbook; # do whatever with Spreadsheet::ParseXLSX }

    And all patches will disappear outside this function.

    For completeness: Both ways may fail if there are any lexical variables closed over the patched subs, but even this can be solved with PadWalker, iff this really happens.

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    Wikisyntax for the Monastery

      Thanks Rolf! This is helpful and I will try it out...

Re^3: Help to override Spreadsheet/ParseXLSX.pm module
by bliako (Abbot) on Apr 03, 2021 at 19:43 UTC

    I guess this is the hybrid "monkeyclassing"! Was package ParseXLSX; at the 1st line of both original and patched files? If yes then you define a class (package) and then you redefine that class in the 2nd CE_ file. I have no idea what's meant to happen in this case. Obviously it did work for you and in your circumstances but I do not know how robust this is. For example, reversing the order of use statements would have different effect.

    Here is a tiny example of subclassing in Perl. You subclass class P1 by creating a new class in a new file P2.pm . Consider this:

    # file P1.pm package P1; sub new { my $class = shift; return bless {} => $class } sub A1 { print "i am A1 in P1\n"; } sub A2 { print "i am A2 in P1\n"; } 1;
    # file P2.pm package P2; use parent 'P1'; sub new { my $class = shift; return bless {} => $class } sub A1 { print "i am A1 in >>>P2<<< (i have overwritten previous A1)\n +"; } # A2 and everything else are inherited from class P1 1;
    # file test.pl # run: perl -I. test.pl use P2; # and not P1 my $P2obj = P2->new(); $P2obj->A1(); # this is overwritten $P2obj->A2(); # this is inherited verbatim from parent P1

    The above demonstrates how P2 inherits all methods from P1 and then overwrites A1() but all other still hold as they were defined in P1.

    How does that translate to your case? P1 is the original class/package. And P2.pm is the new file and package you created essentially overwriting 2 methods. The caveat here is that ALL your scripts which mention use P1; now should be modified to use P2;. Well OK, unless you don't have this power.

    bw, bliako

      Yes, the first line in both files was package ParseXLSX
      I can see how Rolf's monkey-patching looks a bit safer than the way I did it so I will give that a go

      Thanks for taking the time to write up this quick tutorial! It was pretty helpful to me
      Now I think I can also see why sub-classing will not work in my application because I don't get to create the $P2obj on my own...
      it gets created in the Spreadsheet::ReadData method.