Beefy Boxes and Bandwidth Generously Provided by pair Networks
Do you know where your variables are?
 
PerlMonks  

Formats and Variable Scope

by Tanalis (Curate)
on Nov 19, 2002 at 10:26 UTC ( [id://214068]=perlquestion: print w/replies, xml ) Need Help??

Tanalis has asked for the wisdom of the Perl Monks concerning the following question:

Heys,

I'm currently working on a large project, part of which involves converting a large number of reports to use database data and formats.

I'm getting the data from the DB happily enough, and have defined a set of formats which I've put into a module of their own to try and keep the code readable and maintainable (the current reporting scripts are neither).

The script compiles happily with the format module included, but when it comes to outputting the data I simply get the format itself and pages full of blank lines, as if the write is happening but there's no data in the variables.

I've consulted the Camel (2nd Ed.), which says that variables must be in scope when the format is defined in order to be used in that format. I'm ensuring this is happening (I think) by having each set of formats in a subroutine (in the module), which I call just prior to the first write to carry out the format definition.

I'm guessing that this has something to do with variable scope, but I can't seem to find a way to solve the problem or work round it.

If anyone has any comments or ideas, they'd be welcomed. Example code is below.

# module Formats.pm use strict; package Formats; sub admin_usage_reports { format admin_usage_top = A D M I N U S A G E Date: @<<<<<<<<<< $report_date Username Login Logout Activity ---------------------------------------------------------- . format admin_usage_data = @<<<<<<< @<<<<<<< @<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<< $usr, $login, $logout, $activity . } # main script Formats::admin_usage_reports; $^ = "Formats::admin_usage_top"; $~ = "Formats::admin_usage_data"; write;

--Foxcub

Replies are listed 'Best First'.
Re: Formats and Variable Scope
by jreades (Friar) on Nov 19, 2002 at 12:07 UTC

    I may be misreading the code, but it looks like you're trying to access the formats as if they were variables declared in your imported package, when they're actually being defined within a sub.

    You have two choices, depending on how you want to define your formats:

    1. Define the format(s) outside of the sub
    2. Have the sub return the two formats

    I'd probably go with '1' since there's nothing really happening that seems to require a sub.

    package Formatting::Admin::Usage; format top = ... format data = ... package main; $^ = Formatting::Admin::Usage::top; $~ = Formatting::Admin::Usage::data;

    I haven't tried compiling this and it's been a long time since I've worked with formats, so you might need to do some tweaks (I notice that in your code the last two lines use double-quotes to make the assignment, for instance), but this gives you a fairly flexible structure... have more Admin reports? Define a new package Formatting::Admin::Other::top. Want to have user reports? Formatting::User::Usage::top. And so on.

    HTH

      The sub was there simply because I understood that the variables had to be in scope when the format was defined - calling the sub allowed me to choose when to define it.

      I do like the idea for the structure, though .. that works very well in the environment here.

      Still, the variable issue is still there, with or without the sub, and regardless of the structure of the modules.

      Thanks, though, you've given me some ideas to experiment with.

      --Foxcub

Re: Formats and Variable Scope
by jmcnamara (Monsignor) on Nov 19, 2002 at 14:16 UTC

    A better approach might be to use formline and the accumulator to create formatting subs. For example:
    #!/usr/bin/perl -w use strict; sub print_admin_usage_top { my $format = "A D M I N U S A G E\n\n" . "Date: @<<<<<<<<<<\n\n" . "Username Login Logout Activity\n" . "--------------------------------------\n"; $^A = ''; # Clear the accmulator formline($format, @_); print $^A; } sub print_admin_usage_data { my $format = "@<<<<<<< @<<<<<<< @<<<<<<< " . "@<<<<<<<<<<<<<<<<<<<<<<<<<<\n"; $^A = ''; formline($format, @_); print $^A; } print_admin_usage_top('19 Nov 2002'); print_admin_usage_data('J Smith', '9:00', '12:00', 'Low'); print_admin_usage_data('J Jones', '9:00', '12:00', 'Low'); __END__ Prints: A D M I N U S A G E Date: 19 Nov 2002 Username Login Logout Activity -------------------------------------- J Smith 9:00 12:00 Low J Jones 9:00 12:00 Low

    It might also be worthwhile considering one of the Perl templating systems such as Text::Template.

    --
    John.

Re: Formats and Variable Scope
by TheHobbit (Pilgrim) on Nov 19, 2002 at 18:31 UTC

    Hi,
    IIRC, formats are 'global': hyding them in a sub (more generaly in a block) does not do any good, just like subs' declarations, they are visible through the program.

    As sugested, you could use formline, but this has the inconvenience that you have to do your own page breaking.

    What I usually do, is defining the format using $_[0], $_[1],..., $_[n] as variables (as @_ is globaly defined, they are always in scope), then define a sub wich just calls write (the sub is there to put the data into @_).

    format admin_usage_data = @<<<<<<< @<<<<<<< @<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<< $_[0], $_[1], $_[2], $_[3] sub write_admin_usage_data { write(); } write_admin_usage_data($username,$login,$logout,$activity);

    Then you could put all the formats relating to a given report into a module definition together with a sub activating the formats (which will take a filehandle) and a writeline sub to write.

    You could even make it object oriented and a subclass of IO::File so that using of formats is disjoint from the logic of data acquisition.

    I apologize if this is confused, but here I have no Perl, so I can not try something and post the code...

    Hoping this is usefull

    Cheers


    Leo TheHobbit

    update (broquaint): added <code> tags to escape square brackets

      That's pretty clever, I like it! Go one step further and define named constants, and it's even readable:

      use constant USERNAME => 0; use constant LOGIN => 1; use constant LOGOUT => 2; use constant ACTIVITY => 3; format admin_usage_data = @<<<<<<< @<<<<<<< @<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<< $_[USERNAME], $_[LOGIN], $_[LOGOUT], $_[ACTIVITY]

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others making s'mores by the fire in the courtyard of the Monastery: (4)
As of 2024-03-29 02:11 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found