Beefy Boxes and Bandwidth Generously Provided by pair Networks
We don't bite newbies here... much
 
PerlMonks  

Re^5: Perl Modules -- abstraction and interfaces: exporter and @ISA

by Discipulus (Canon)
on Jun 28, 2017 at 21:07 UTC ( [id://1193790]=note: print w/replies, xml ) Need Help??


in reply to Re^4: Perl Modules -- abstraction and interfaces
in thread Perl Modules

hello again jamroll,

I'm for sure not the best monk in the monastery to explain this, but it seems you liked my way to express it.

> are they inherited into html (package I'd add) automagically?

No, they are not. You have two options:

The plain, easy way: is let Exporter to do his job via @EXPORT_OK as already said. Read through https://perldoc.perl.org/Exporter.html manual to know more about it.

Everything in your module Project::Display (capitalize first char as idiomatic (idiomatic IS good thing) common practice), module that use Exporter (and also Project::Display states that @ISA = qw(Exporter); but more on this later) every sub defined in your module and that also is in @EXPORT_OK can be usable from other scripts or modules that include something like use Project::Display qw(a_sub_you_put_in_EXPORTER_OK another_one etc); and this is good.

Keep the whole thing simple, well named, ordered, and with some sense and everything will run smooth.

The ObjectOriented way: in this path to know a little of terminology is another good thing to do: perldoc has perlglossary just in case, but for Objected Oriented Perl (OO for brevity) the principal source of wisdom will be object oriented fundamentals that is in perlootut

Going to this path maybe trickier, but perhaps you are more inclined to program OO than in other ways. One of basic concepts is inheritance: with the already seen @ISA you put in your Project::Display module you asserted that your module IS-A Exporter: practically if a method (new term from OO, but is simply a sub..) is not found in Project::Display will be searched into every module (well package) you put into @ISA array.

So Project::Display::Console and Project::Display::HTML will both have @ISA = qw (Project::Display); very soon stated.

You than in your consumer script that uses this modules/classes you create an object, a simple scalar emitted by a constructor defined in the module: by tradition that special constructor sub, defined in a module/class/package is named new and this sub will bless that scalar: ie it marks this scalar as belonging to a particular module/class/package.

Doing so you will be able to do my $tv = Project::Display->new ( more=> "params", can_be => "supplied"); in your script and if Project::Display defines a sub format_70_chars then your $tv object can call this method: $tv->format_70_chars( $some_text );

But now you want to use inheritance and want to sublcass Project::Display and have a handy Project::Display::Console class to be able to draw text into a boxes done with - and | signs.

You create this package/module/class stating that this package @ISA is a Project::Display object. This Project::Display::Console will NOT have his own new method: it inherits it directly form Project::Display (you'll learn how to accomplish this correctly) but this new class just define a draw_in_box method.

If everything is wrote correctly only an objected created as instance of the class Project::Display::Console can call draw_in_box while objects created as Project::Display::HTML cannot. But both ojects inherits the new method from Project::Display base class.

Take a read of perlobj and consider to read the objects part of the must have ModernPerl free book (even if it just show the Moose implementation and not the plain perl OO I showed you).

ouch! i wrote a lot! read your manual now!

L*

There are no rules, there are no thumbs..
Reinvent the wheel, then learn The Wheel; may be one day you reinvent one of THE WHEELS.

Replies are listed 'Best First'.
Re^6: Perl Modules -- abstraction and interfaces: exporter and @ISA
by jamroll (Beadle) on Jul 11, 2017 at 13:54 UTC
    thank you, sir. again, a good read. can't say i fully understood all of that, but i get the gist. See, i have much programming experience - coding in so many languages, including c/c++. I liked OO and inheritance and the "Is A" and "Has A" relationships. when i began coding my new site, i had not known perl could do OO...as I code the site, i learned so much and realize i should have gone OO with this...but, i'm several modules in (11 modules), and I have so many scripts that use the modules (some 32 scripts). like the song i'm hearing says right now - "it's too late". lol - i can't change it. so i'm stuck with the procedural method for now.

    the next question on my mind....i'll use code to describe the question, cuz it gets my point across far easier than trying to use words to describe the question

    package Blah::Blah::BlackSheep; use strict; use warnings; # etc, etc...etc... my $loggedin = cookie_get("loggedin"); # does exactly what you think i +t does... my $page = get_param(get_constant($db, "QUERY_PAGE")); # the page the +user wants to visit # cookie_get, get_param, get_constant are all subs I created to ease m +y coding a little, despite it being a tad...convoluted. it works. t +hat's what matters if (not allowed($page, $loggedin)) { print cookie_set("error", "Access Denied!"); print "location: /\n\n"; exit 1; } sub something { return "blah"; } sub something2 { return "bleet"; } # etc, etc, etc... 1;
    is this "good practice" - i mean the bit before the first sub?

    why do i ask? I don't want to have to code the above into EVERY script i write....that's prone to errors, and can be a real B when a change is needed....

    if this isn't good practice, how can i accomplish this task? I know PERL allows the above code - compiles without complaint, and it does work....just wanted to know if this is the right way to go about it...?
      is this "good practice" - i mean the bit before the first sub?

      I would answer this with "no". As you said, it might work, but there are two main reasons I would recommend against it.

      First a practical point: usually, when you write a module like that, you'd be loading it with use. However, as documented in use, that code gets run in a BEGIN block, that is, while the calling code is still being complied. For some code, it will still work, but on the other hand you may get some unexpected effects (e.g. this thread). Also, modules might not yet be loaded and/or initialized depending on the order of the use statements in the calling code! (Even if you use require to load modules at runtime instead of compile time, note that this will lead to other possibly unexpected effects, like the code in that module still being executed only once, or you having to use parentheses on all your subroutine calls imported from that module.) This is why modules that are intended to be used usually limit themselves to declarations of subs and package variables (Update: and often the exporting of those subs into the caller's namespace, e.g. Exporter), and if they do have initialization code, it is limited to things that are internal to that module without externally visible side effects. (Update 2: Note I'm ignoring more complicated things like custom pragmas here, which are also just special kinds of modules.)

      Second, something to do more with convention: When I load a module with use, I don't expect that call to have any side effects other than loading that module, but the code you showed not only prints something, it may even kill my whole program with exit!

      So what can you do instead? If you want to write a module that is intended to be loaded with use, then it's easiest when it only contains sub definitions and perhaps declarations of package variables (our). If it does execute code on loading, then it should only be for its internal initialization, i.e. it shouldn't have any side effects visible to outside code, and it needs to consider that it is being executed in a BEGIN block, as I explained above (Update: i.e., I wouldn't mess with cookies or CGI params that that point yet). If you want to include initialization code with effects visible to the outside code, then for example, put it in a sub init that the user has to explicitly call.

      Another approach, which I would consider to be less elegant, would be to use do to explicitly execute another Perl file. Unlike require, that Perl file will be executed every time you call do instead of only once, so it's more like calling a sub than loading a module. However, in that case it's not necessarily the best place to put subs etc., so you'd have to consider splitting the code you showed into two parts. (So as you can tell, it's probably easier to just put the init code in a sub init instead.)

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://1193790]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others examining the Monastery: (5)
As of 2024-03-29 12:15 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found