Changing module dynamically

Ask a Question related to PERL Miscellaneous, Design and Development.

  1. #1

    Default Re: Changing module dynamically

    Thomas M. Widmann sikyal:
    > So, to sum up: I'd like a function, let's call it changelang, which
    > will do the following:
    >
    > - Take one argument, the language to change to ($lang).
    >
    > - If it hasn't been loaded, load Bibulus::Lang::$lang (or whatever
    > -- I don't care much about the name, so long as each language is
    > defined in its own file).
    >
    > - Change the symbol table look-up path, so that Bibulus::Lang::$lang
    > is searched first.
    >
    > - This should not affect the whole package, just the instance (as if
    > there was something called @{$self->ISA}).
    I'm not sure how much control you have over the architecture of the whole
    thing, but this is how I would implement such a system:

    First, have all of your text methods be defined in the base class
    Bibulus::Lang, but don't actually define any text there. Rather, define a
    global hash %text that has key => value pairs for the text, and simply
    import that hash from a module. Thus, your base class has this:

    our %text = (); # whatever . . .

    sub new {
    # blah blah blah . . .
    $self->{text} = \%text;
    bless $self, $class;
    }

    # Text functions could easily be generalized w/ AUTOLOAD
    sub edition {
    return $self->{text}->{edition};
    }

    sub changelang {
    my $lang = pop;
    require "Bibulus::Lang::$lang";
    $self->{text} = \%Bibulus::Lang::$lang::text;
    }

    All of the Bibulus::Lang::lang subclasses then have the responsibility of
    populating %text with appropriate values. The package variable %text will
    have the "standard" values for each lang, while $self->{text} will have
    instance-specific values.

    Jesse S. Bangs [email]jaspax@u.washington.edu[/email]
    [url]http://students.washington.edu/jaspax/[/url]
    [url]http://students.washington.edu/jaspax/blog[/url]
    JS Bangs Guest

  2. Similar Questions and Discussions

    1. Dynamically changing chart labels
      HI I'm after an how too really, point me in the right direction is possible. I have a new dashboard app, monitoring a server, last part is load...
    2. URL Atribute Dynamically Changing Query
      :confused; This is a very real life program, but for simplicity terms, I'm going to use the ubiquitous Art Gallery example. I know a little...
    3. Changing Text Dynamically
      Hello, I would like to know if the text displayed on a webpage changes dynamically.I have 2 drop down menus and based on the options chosen I...
    4. Changing UserControls Dynamically
      I have 2 user controls and I would like to display 1 of them depending on a session variable. I have trie a place holder, but it doesnt seem to...
    5. changing default runlevel dynamically
      How can I choose the runlevel dynamically at startup through lilo menus or otherwise? I want to make a custom runlevel for powersave and such. ...
  3. #2

    Default Re: Changing module dynamically

    Also sprach Thomas M. Widmann:
    > I have a slightly odd problem. I know I'm abusing OO programming a
    > bit, but I cannot think of any better way.
    >
    > Simply stated, my problem is as follows: I'm writing a bibliography
    > package with multilingual features, so all texts are encapsulated in
    > functions.
    >
    > E.g., $bib->edition(5) might return 'fifth edition' or '5th ed.' if
    > the language is English, and 'femte udgave' og '5. udg' if it's
    > Danish.
    >
    > Now, if one only ever needed one language in each bibliography, one
    > could simply have put something like
    > use Bibulus::Lang::da;
    > in the beginning of the script using the bibliography package, and
    > Bibulus::Lang::da would then have defined the relevant language
    > functions to return Danish texts and it would have inherited the rest
    > from the parent.
    >
    > However, the language can change for each entry. I could of course
    > write functions like the following:
    > sub edition {
    > my $self = shift;
    > my $ed = shift;
    > if ($self->LANG eq 'da') {
    > return "$ed. udgave";
    > } elsif ($self->LANG eq 'en') {
    > ...
    > -- but this would quickly become unwieldy, and it would make it
    > difficult for users to define their own languages easily.
    >
    > If only @ISA had not been package-scoped, one could have done
    > something along the following lines:
    > sub changelang {
    > my $self = shift;
    > my $lang = shift;
    > @ISA = ("Bibulus::Lang::$lang");
    > }
    > -- but it is, so one cannot. :-( (Well, if I have to, I can go down
    > this route -- since one would only write one entry at a time, one can
    > of course just always switch to correct language at the beginning of
    > each entry.)
    I don't understand this paragraph. @ISA is indeed package-scoped, but
    why does that prevent you from changing it?
    > Instead, I've been playing around with autoloading:
    > sub AUTOLOAD {
    > my $self = shift;
    > my $f = $AUTOLOAD;
    > $f =~ s/(.*::)/$1$self->{LANG}\:\:/;
    > &$f(@_);
    > }
    > but it most certainly isn't pretty, and it seems to break down if one
    > tries to use inheritance. It also seems to be necessary to require
    > all the language modules initially, which again makes it difficult for
    > users to define their own languages.
    No, it does not. Just extract the package from $AUTOLOAD and
    eval-require it:

    sub AUTOLOAD {
    my ($pkg, $meth) = $AUTOLOAD =~ /(.*)::(.*)/;
    eval "require $pkg";
    goto &$AUTOLOAD;
    }
    > So, to sum up: I'd like a function, let's call it changelang, which
    > will do the following:
    >
    > - Take one argument, the language to change to ($lang).
    >
    > - If it hasn't been loaded, load Bibulus::Lang::$lang (or whatever
    > -- I don't care much about the name, so long as each language is
    > defined in its own file).
    >
    > - Change the symbol table look-up path, so that Bibulus::Lang::$lang
    > is searched first.
    >
    > - This should not affect the whole package, just the instance (as if
    > there was something called @{$self->ISA}).
    Ah, now I see what you meant. You want an @ISA per object. That arguably
    does not exist.

    I'd go for an AUTOLOAD approach thusly:

    Make a convention that you sufffix the edition() method with the
    language. Extract this suffix, pull in the appropriate module and call
    the edition() method from this namespace:

    sub AUTOLOAD {
    my ($meth, $lang) = $AUTOLOAD =~ /(edition)_(.*)/
    or return;
    eval "require Bibulus::Lang::$lang";

    # for $self->edition_da(@args) this will translate to
    # $self->Bibulus::Lang::da::edition(@args)
    goto &{"Bibulus::Lang::${lang}::edition"};
    }

    Tassilo
    --
    $_=q#",}])!JAPH!qq(tsuJ[{@"tnirp}3..0}_$;//::niam/s~=)]3[))_$-3(rellac(=_$({
    pam{rekcahbus})(rekcah{lrePbus})(lreP{rehtonabus}) !JAPH!qq(rehtona{tsuJbus#;
    $_=reverse,s+(?<=sub).+q#q!'"qq.\t$&."'!#+sexisexi ixesixeseg;y~\n~~dddd;eval
    Tassilo v. Parseval Guest

  4. #3

    Default Re: Changing module dynamically

    Thomas M. Widmann <thomas@widmann.uklinux.net> wrote:
    > I have a slightly odd problem. I know I'm abusing OO programming a
    > bit, but I cannot think of any better way.
    [snip]
    > So, to sum up: I'd like a function, let's call it changelang, which
    > will do the following:
    >
    > - Take one argument, the language to change to ($lang).
    >
    > - If it hasn't been loaded, load Bibulus::Lang::$lang (or whatever
    > -- I don't care much about the name, so long as each language is
    > defined in its own file).
    >
    > - Change the symbol table look-up path, so that Bibulus::Lang::$lang
    > is searched first.
    >
    > - This should not affect the whole package, just the instance (as if
    > there was something called @{$self->ISA}).
    What you're describing (a class for every object) is possible --

    sub changelang {
    my ($self, $lang) = @_;
    my $pack = "Bibulus::Lang::$lang";
    my $addr = sprintf '0x%x', $self;
    eval qq{
    require ${pack};
    package ${pack}_${addr};
    sub DESTROY { undef %${pack}_${addr}:: }
    }.q{
    our @ISA = ($pack, ref $self);
    bless $self;
    }
    }

    But delegating/dispatching the language methods by hand would be
    cleaner:

    sub changelang {
    my ($this, $lang) = @_;
    my $package = "Bibulus::Lang::$lang";
    eval "require $package" or die $@;
    $this->{lang} = $package;
    }

    sub edition {
    my $this = shift;
    ($this->{lang} || $default)->edition(@_);
    }

    And as you might have suggested in the big snip, any number of
    those language-aware proxy methods could be generated on the fly
    by AUTOLOAD().

    --
    Steve
    Steve Grazzini Guest

  5. #4

    Default Re: Changing module dynamically

    "Tassilo v. Parseval" <tassilo.parseval@rwth-aachen.de> writes:
    > Also sprach Thomas M. Widmann:
    >
    > [...]
    > > Instead, I've been playing around with autoloading:
    > > sub AUTOLOAD {
    > > my $self = shift;
    > > my $f = $AUTOLOAD;
    > > $f =~ s/(.*::)/$1$self->{LANG}\:\:/;
    > > &$f(@_);
    > > }
    > > but it most certainly isn't pretty, and it seems to break down if one
    > > tries to use inheritance. It also seems to be necessary to require
    > > all the language modules initially, which again makes it difficult for
    > > users to define their own languages.
    >
    > No, it does not. Just extract the package from $AUTOLOAD and
    > eval-require it:
    >
    > sub AUTOLOAD {
    > my ($pkg, $meth) = $AUTOLOAD =~ /(.*)::(.*)/;
    > eval "require $pkg";
    > goto &$AUTOLOAD;
    > }
    >
    Ah, I hadn't thought of that -- thanks a lot!
    > [...]
    > I'd go for an AUTOLOAD approach thusly:
    >
    > Make a convention that you sufffix the edition() method with the
    > language. Extract this suffix, pull in the appropriate module and call
    > the edition() method from this namespace:
    >
    > sub AUTOLOAD {
    > my ($meth, $lang) = $AUTOLOAD =~ /(edition)_(.*)/
    > or return;
    > eval "require Bibulus::Lang::$lang";
    >
    > # for $self->edition_da(@args) this will translate to
    > # $self->Bibulus::Lang::da::edition(@args)
    > goto &{"Bibulus::Lang::${lang}::edition"};
    > }
    No, that wouldn't work, since the edition function will be called in a
    place where the language is not known in advance.

    However, with help from your eval-require, I've come up with some code
    that seems to work. At least, the following program:

    use A;
    my $l1 = new A;
    $l1->lang('da');
    print $l1->test, "\n";
    $l1->lang('en');
    print $l1->test, "\n";
    my $l2 = new A;
    $l2->lang('da');
    print $l2->test, "\n";
    print $l1->test, "\n";

    will produce the required output:
    da
    en
    da
    en

    given the following three files A.pm, da.pm and en.pm:

    ##### file A.pm #####
    package A;

    sub new {
    my $class = shift;
    my $self = {};
    bless($self, $class);
    return $self;
    }

    sub lang {
    my $self = shift;
    my $lang = shift;
    $self->{LANG} = $lang;
    }

    sub AUTOLOAD {
    my $self = shift;
    my ($pkg, $meth) = $AUTOLOAD =~ /(.*)::(.*)/;
    eval "require '$self->{LANG}.pm'";
    $meth = "$self->{LANG}::$meth";
    goto &$meth;
    }
    1;

    ##### file da.pm #####
    package da;

    sub test {
    return 'da';
    }
    1;

    ##### file en.pm #####
    package en;

    sub test {
    return 'en';
    }
    1;


    Thanks a lot for your help!

    /Thomas
    --
    Thomas Widmann Bye-bye to BibTeX: join the Bibulus project now!
    [email]thomas@widmann.uklinux.net[/email] <http://www.nongnu.org/bibulus/>
    Glasgow, Scotland, EU <http://savannah.nongnu.org/projects/bibulus/>
    Thomas M. Widmann Guest

  6. #5

    Default Re: Changing module dynamically

    JS Bangs <jaspax@u.washington.edu> writes:
    > Thomas M. Widmann sikyal:
    >
    > > So, to sum up: I'd like a function, let's call it changelang,
    > > which will do the following:
    > >
    > > - Take one argument, the language to change to ($lang).
    > >
    > > - If it hasn't been loaded, load Bibulus::Lang::$lang (or
    > > whatever -- I don't care much about the name, so long as each
    > > language is defined in its own file).
    > >
    > > - Change the symbol table look-up path, so that
    > > Bibulus::Lang::$lang is searched first.
    > >
    > > - This should not affect the whole package, just the instance
    > > (as if there was something called @{$self->ISA}).
    >
    > I'm not sure how much control you have over the architecture of the
    > whole thing, but this is how I would implement such a system:
    >
    > First, have all of your text methods be defined in the base class
    > Bibulus::Lang, but don't actually define any text there. Rather,
    > define a global hash %text that has key => value pairs for the text,
    > and simply import that hash from a module. Thus, your base class has
    > this:
    >
    > [...]
    >
    > All of the Bibulus::Lang::lang subclasses then have the
    > responsibility of populating %text with appropriate values. The
    > package variable %text will have the "standard" values for each
    > lang, while $self->{text} will have instance-specific values.
    I considered something like that originally, but I gave it up when I
    realised that the order of elements can be language-dependent. (E.g.,
    "5th" and "edition" might come out in the opposite order in some
    languages.)

    One could of course populate the hash with closures, but I fear it
    would get totally out of hand.

    But thanks anyway for the proposal!

    /Thomas
    --
    Thomas Widmann, MA member of the steering group for europa.*
    Mavisbank Gardens, Glasgow, Scotland, EU
    [email]thomas@widmann.uklinux.net[/email] [url]http://www.widmann.uklinux.net[/url]
    Thomas M. Widmann Guest

Posting Permissions

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

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139