Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl: the Markov chain saw
 
PerlMonks  

Re: Proper way to create packages and re-usable code?

by Discipulus (Abbot)
on Feb 11, 2016 at 09:23 UTC ( #1154929=note: print w/replies, xml ) Need Help??


in reply to Proper way to create packages and re-usable code?

Hello i'm less or more in the same situation. The moment when you pass from standalone scripts to modules is crucial (from my point of view) moment that tells the scriptor from the progammer.

You abstracts functionalities (possibly grouping them on affinities) from your main script that become a bare bone sketch of your desired behaviour of the application.

So if your application is ZXappz and you need to log and also use a database connection to read data you'll probably need a ZXappz::Logger and a ZXappz::Database modules. maybe the main ZXappz.pm just loads these two modules. Is ZXappz::Logger that uses log4perl and ZXappz::Database will load DBI

This in the simplest case ie when you just group functionalities into modules for a logical separation. The application can be one or more scripts that use such functionalities (subroutines exported by your modules) to do the work.

In this perpective you gained in readability and maintenence. But there is a better gain: you can document and test your software in a just one place: the module file. Better: you can embrass the "test driven" develop method that will save your time in the long run. You imagine a functionality let's say you want to write to alocal file whe the database is offline. Ok, first you choose a meaningful name like db_fallback_write and you imediatly spot ZXappz::Database as the right place to put it. Then, before coding the subroutine, you wite the POD documentation in the module for the functionality. you describe it as you wont it. Then you write an initial test (a .t file in t/ dir of the ZXappz::Database distribution) for db_fallback_write let'say return the name of the file written on success and 0 on failure. only now you start to code the sub db_fallback_write in the ZXappz/Database.pm file. You are defining an interface for your software: the db_fallback_write functionality want some parameters and returns something. it does it's work as described in the POD.

db_fallback_write may needs to chose a filename based on the day of the week. This is something private to the sub implementation: the final user of db_fallback_write (you or your script) does not need to know how the filename is composed. So in the ZXappz/Database.pm you can have a choose_filename sub (or as frequently happen a name starting with underscore _choose_filename ). choose_filename can have it's own tests but not POD documentation because it is not part of the interface but only of the implementation.

i hope i gave you the idea. wiser monks can add and specify about the matter. In my homenode you can search for the string About new modules to see a bounch of links on the subject.

HtH

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^2: Proper way to create packages and re-usable code?
by Anonymous Monk on Feb 11, 2016 at 09:38 UTC

    A little related commentary

    Is ZXappz::Logger that uses log4perl and ZXappz::Database will load DBI

    :) That is one of those weird things that don't quite make sense

    log4perl and DBI both provide good APIs that they don't need generic wrappers

    Now if you're talking a static "Config" module or a "Model" module, that makes sense, but not a generic logger/database, log4per/DBI are already that generic module, just configure them and you're done

    Yeah, sometimes it makes sense to have a generic logger/database module as part of your API, but I'd say most of the time it doesn't :) its weird :D

      Ok, stated my limited experience, i just only to say that probably you need ZXappz::Database anyway because it can contains for example store_new_user and store_action_for_user and so on. So in the main code you can just:
      my $appdb = ZXappz::Database->new(); $appdb->store_new_user($previously_generated_name,time); # uses DBI to + insert into a table .. $appdb->close # call the DBI's close but maybe also log somewhere the +succesfull operation

      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.

        ZXappz::Database anyway because it can contains for example store_new_user and store_action_for_user and so on...

        :) That is good thinking, separate the stuff, groupt the database stuff together

        but the name should be different from "database" because that is kind of stuff that goes in your "model",

        so you might have a ZXappz::UserGroup (or ZXappz::Model::UserGroup) with a add_user and ...

        yes it goes in a database, maybe one created from ZXappz::Schema::UserGroup ...

        but you call it something important to your app, symbolic not generic

        Its like when you first learn programming, no subroutines, just long stretches of code, you have to start somewhere

        then the next evolution, you start using subroutines, but its still one file, and they're very long and do too much, and the names are focused on the implementation details

        then you start making packages, which is great, but you're organizing principle is not the intent of the code but implementation detail;

        Is this terrible? No. Its common, its workable, its maintainable, esp for short programs, but could be improved; and should be improved , esp if you want to reuse the code, and for others to use your code , and enjoy it (collaboration )

        There is no reason to take the long way around, jump straight to intent :) organize stuff with big picture in mind, like a house, you have to frame it, but you don't design around the frame, around stud, you design around rooms with purpose, by walking around the house in your head, wake up in the morning, get ready for work, host a party...

        start with a story board, how should the user use the application, what should it do

        then start with use cases, how should the code be used by the programmer, write that code, and your api will emerge

Re^2: Proper way to create packages and re-usable code?
by bt101 (Acolyte) on Feb 12, 2016 at 00:01 UTC
    WOW, thanks everyone for the great responses. I understand what you are saying... Packages should have a narrow purpose and be self contained. I think I see the error of my ways as the code that I want to re-use does not really fit that mold. Basically I have a bunch of routines that do low level serial communication. All of the routines form a whole program that does the communication, error handling, logging etc. At the top level, there is one routine to call to send/receive a message. I then want to use this code for (let's say) 10 different programs where each program only differs by the main loop which loops and calls the re-usable routines for communication. All I "really" want is to house the re-usable code in a separate "file" so there is only one set of re-usable code to maintain. From what I read on the internets, I gathered that the way to split perl into different files was with a package. However my code doesn't really fit the mold of having singular purpose. I'll do some research on the links provided. I understand that if the whole program was destined to be a 1 million line ERP system, the it would be worth re-organizing and rewriting everything to fit into modules. However I'm aiming at just splitting things into "two" files, and I'm 1 yard from the finish line. So cut/paste into 10 scripts may be the way to go.

      The following is not the proper way to modularize your code, but because you are just 'one yard before the finish line' you could use a script or Makefile to concatenate multiple files to a single program. That is easier and less error prone then the temporary "manually cut and paste" solution, that you are considering.

      Assuming you have the following files:

      -rw-r--r-- 1 hadri hadri 51 Feb 12 04:24 main_barack -rw-r--r-- 1 hadri hadri 63 Feb 12 04:15 my-serial-library.src -rw-r--r-- 1 hadri hadri 68 Feb 12 04:27 postlude_general.src

      Your general libary routines are in my-serial-library.src and postlude_general.src. The 'main' program is the only customized part and is named after your client or location: main_barack.

      With a simple Makefile you can generate your program serial_com_barak.pl:

      $ make cat my-serial-library.src main_barack postlude_general.src > serial_co +mm_barack.pl

      For another customer you create a new customized main and run 'make' again:

      $ echo "This is for Bernie" > main_bernie $ make CLIENT=bernie cat my-serial-library.src main_bernie postlude_general.src > serial_co +mm_bernie.pl

      The Makefile:

      # -- sample Makefile CLIENT = barack NAME = serial_comm PROG_NAME = ${NAME}_${CLIENT}.pl PRELUDE = my-serial-library.src MAIN = main_${CLIENT} POSTLUDE = postlude_general.src TAR_NAME = serial-comm_${CLIENT}.tgz TAR_FILES = Makefile TAR_FILES += ${PRELUDE} ${MAIN} ${POSTLUDE} ${PROG_NAME}: ${PRELUDE} ${MAIN} ${POSTLUDE} cat ${PRELUDE} ${MAIN} ${POSTLUDE} > ${PROG_NAME} clean: rm -rf ${PROG_NAME} archive: tar cvzf ${TAR_NAME} ${TAR_FILES} # --- end of Makefile

      Note: Make sure that you have a tab character (\x09, CNTRL-I or ^I) before the cat and other commands in the Makefile.Verify with 'cat -t Makefile':

      ${PROG_NAME}: ${PRELUDE} ${MAIN} ${POSTLUDE} ^Icat ${PRELUDE} ${MAIN} ${POSTLUDE} > ${PROG_NAME}
        Ah yes thanks. That will get me going for now. I'll spend some time studying the info and links provided in the replies. Thanks everyone for the help.

      I understand that if the whole program was destined to be a 1 million line ERP system, the it would be worth re-organizing and rewriting everything to fit into modules. However I'm aiming at just splitting things into "two" files, and I'm 1 yard from the finish line. So cut/paste into 10 scripts may be the way to go.

      No, copy/paste isn't the answer, thats like first week of intro to programming :)

      modules are the answer, modules that are accompanied with tests, your code should live in modules :)

      Maybe

      Something like that, draw a few outlines like that on a piece of paper, putting different stuff in different places, see what makes more sense

      You can see all kinds of examples on cpan, see App::

      https://metacpan.org/release/Module-Starter

      tracker/App::TimeTracker

      booklist/App::Booklist

Log In?
Username:
Password:

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

How do I use this? | Other CB clients
Other Users?
Others rifling through the Monastery: (4)
As of 2022-05-25 06:55 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    Do you prefer to work remotely?



    Results (84 votes). Check out past polls.

    Notices?