Complicated class initialization/instantiation problem.

Discussion in 'Ruby' started by Michael T. Richter, Jul 16, 2008.

  1. [Note: parts of this message were removed to make it a legal post.]

    I've been trying to crack this nut on my own for the past week or so,
    but can't get a solution that doesn't suck. I'd appreciate any
    assistance that could be tossed my way by some of the more expert Ruby
    users.

    The situation is this: I have a stream of bytes in a particular format
    (I have no control over this format). The stream corresponds to types
    in another programming language. The structure of the stream is
    basically a boilerplate wrapper around a variety of different types
    internally (some simple types, some complex types which can contain
    other types both simple and complex).

    I'm treating the external wrapper as one type which:

    1. sanity checks (makes sure it has the right wrapper tag, makes
    sure the internal object's tag is a real one, etc.);
    2. decompresses the stream if necessary since the data internal to
    it can often be very large so is frequently compressed;
    3. instantiates the actual objects based on the internal object
    tags.


    Now the internal objects themselves have tags which reference them but
    which must go with the objects because once extracted they need to be
    manipulated, moved around, possibly embedded in other wrapped types,
    etc. So the external wrapper, when parsing, needs to read the tag and
    dispatch object creation to the appropriate instance.

    I've got all this working nicely, I should mention. It's all functional
    and glorious and such. It's just fugly. REALLY fugly. It breaks DRY
    in so many ways it's frightening. Here's an example of what I mean:


    class Bar
    @@registry = {}
    def External.register tag, klass
    @@registry[tag] = klass unless @@registry.has_key? tag
    end
    # other stuff
    end

    class Foo1
    [TAG1 = 1, TAG2 = 2, TAG3 = 3].each { |tag| Bar.register(tag, Foo1) }
    # other stuff
    end

    class Foo2
    [TAG4 = 4, TAG5 = 5, TAG6 = 6].each { |tag| Bar.register(tag, Foo1) }
    # other stuff
    end


    (Note that this is a simplified representation of what I'm doing. I
    have much better error checking, etc. in the registry in real life.)

    What I like about this setup is that classes are registered
    automagically upon the file being loaded. There's no chance for error
    if someone forgets to call the registration function. Further, the
    External class is instantly usable because by the time you can use it it
    already has every tag and associated class registered. That being said,
    I've shut down one avenue of bugs by introducing a whole new set of
    them. Take a close look at class Foo2 to see if you can spot the bug,
    for example.

    Basically I'm relying on several non-DRY chunks of code, each of which
    can cause me problems in the future. First, in each of the Foo* classes
    I'm repeating ".each { |tag| Bar.register(tag, Foo1) }". This means
    that if I change the way I choose to register classes for parsing, I
    have to change each and every client class because I RYed. Given that
    there's currently about two dozen such classes to implement, you can see
    that this would make refactoring a real bitch. The second, more subtle,
    problem is exemplified in Foo2. Because the code is mostly boilerplate
    -- I'm changing the tags and the instantiating class only -- it's very
    easy to make a cut-and-paste bug like telling the registry to
    instantiate Foo1 instead of Foo2 for tags 4, 5 and 6. (How do I know
    it's easy? I did it to myself three times THIS MORNING.) Now catching
    this is easy because I have each instantiating class sanity-check to
    make sure the right tag is being passed in, but I'd prefer a more robust
    solution.

    Were the registration to take place in an instance method, this wouldn't
    be a problem. I'd pass self.class in and likely make the method that
    does the registration something in the parent class so that all the
    child classes would have to do is call super in initialize to get the
    glorious benefits of knowing its own class. Unfortunately, because of
    the requirement that the registration happen out of any actual instance
    executing, I have no self to get the class from.

    The only way I've seen so far to get around this problem is fuglier than
    the problem: have boilerplate code evaled to create a class instance.
    I'm hoping there's something better out there that I've overlooked.

    Any suggestions?

    --
    Michael T. Richter <> (GoogleTalk:
    )
    If there's one thing that computers do well, it's to make the same
    mistake uncountable times at inhuman speed. (Peter Coffee)
     
    Michael T. Richter, Jul 16, 2008
    #1
    1. Advertising

  2. Michael T. Richter

    Pit Capitain Guest

    2008/7/16 Michael T. Richter <>:
    > Basically I'm relying on several non-DRY chunks of code, each of which
    > can cause me problems in the future. First, in each of the Foo* classes
    > I'm repeating ".each { |tag| Bar.register(tag, Foo1) }".
    > (...)
    > The second, more subtle,
    > problem is exemplified in Foo2. Because the code is mostly boilerplate
    > -- I'm changing the tags and the instantiating class only -- it's very
    > easy to make a cut-and-paste bug like telling the registry to
    > instantiate Foo1 instead of Foo2 for tags 4, 5 and 6.


    Hello Michael, to remove the each loop you could change Bar.register
    to accept multiple tags, and to avoid copy and paste errors you could
    just use "self" instead of the classes:

    class Bar
    @registry = {}
    def self.register klass, *tags
    tags.each do |tag|
    @registry[tag] ||= klass
    end
    end
    # other stuff
    end

    class Foo1
    Bar.register self, TAG1 = 1, TAG2 = 2, TAG3 = 3
    # other stuff
    end

    class Foo2
    Bar.register self, TAG4 = 4, TAG5 = 5, TAG6 = 6
    # other stuff
    end

    Regards,
    Pit
     
    Pit Capitain, Jul 16, 2008
    #2
    1. Advertising

  3. Michael T. Richter

    Robert Dober Guest

    On Wed, Jul 16, 2008 at 9:13 AM, Pit Capitain <> wrote:
    > 2008/7/16 Michael T. Richter <>:


    > @registry[tag] ||= klass

    Although your simplification of
    h[k] = value unless h.has_key? k
    to
    h[k] || = value
    is *often* what the user wants, but it is *not* equivalent code of course.
    Just wanted to point that out to be on the save side.
    Cheers
    Robert

    --
    http://ruby-smalltalk.blogspot.com/

    ---
    AALST (n.) One who changes his name to be further to the front
    D.Adams; The Meaning of LIFF
     
    Robert Dober, Jul 16, 2008
    #3
  4. Michael T. Richter

    Pit Capitain Guest

    2008/7/16 Robert Dober <>:
    > On Wed, Jul 16, 2008 at 9:13 AM, Pit Capitain <> wrote:
    >
    >> @registry[tag] ||= klass

    > Although your simplification of
    > h[k] = value unless h.has_key? k
    > to
    > h[k] || = value
    > is *often* what the user wants, but it is *not* equivalent code of course.
    > Just wanted to point that out to be on the save side.


    Servus Robert, thanks for the reminder. Because Michael wants to store
    classes as hash values, I assumed that nil or false wouldn't be valid
    values and therefore the simplification was OK.

    Regards,
    Pit
     
    Pit Capitain, Jul 16, 2008
    #4
  5. [Note: parts of this message were removed to make it a legal post.]

    On Wed, 2008-07-16 at 16:13 +0900, Pit Capitain wrote:

    > Hello Michael, to remove the each loop you could change Bar.register
    > to accept multiple tags,



    Yeah, I'm ashamed I didn't spot that until after I asked for help. :D


    > and to avoid copy and paste errors you could
    > just use "self" instead of the classes:



    This however I thought of already and tried before posing the question:


    $ irb
    irb(main):001:0> class Foo ; p self.class ; end
    Class
    => nil
    irb(main):002:0>


    See the problem?

    --
    Michael T. Richter <> (GoogleTalk:
    )
    The most exciting phrase to hear in science - the one that heralds new
    discoveries - is not "Eureka!" but "That's funny..." (Isaac Asimov)
     
    Michael T. Richter, Jul 16, 2008
    #5
  6. Michael T. Richter

    Pit Capitain Guest

    2008/7/16 Michael T. Richter <>:
    > This however I thought of already and tried before posing the question:
    >
    > $ irb
    > irb(main):001:0> class Foo ; p self.class ; end
    > Class
    > => nil
    > irb(main):002:0>
    >
    > See the problem?


    Yes: you are using "self.class" instead of "self".

    Regards,
    Pit
     
    Pit Capitain, Jul 16, 2008
    #6
  7. [Note: parts of this message were removed to make it a legal post.]

    On Wed, 2008-07-16 at 19:57 +0900, Pit Capitain wrote:

    > > $ irb
    > > irb(main):001:0> class Foo ; p self.class ; end
    > > Class
    > > => nil
    > > irb(main):002:0>




    > > See the problem?




    > Yes: you are using "self.class" instead of "self".



    Doh! Thanks a lot.

    --
    Michael T. Richter <> (GoogleTalk:
    )
    Our outrage at China notwithstanding, we should remember that before
    1891 the copyrights of foreigners were not protected in the United
    States. (Lawrence Lessig)
     
    Michael T. Richter, Jul 16, 2008
    #7
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. JKop
    Replies:
    10
    Views:
    987
  2. Replies:
    0
    Views:
    402
  3. Erik Leunissen

    Complicated linking problem

    Erik Leunissen, Dec 9, 2005, in forum: C Programming
    Replies:
    4
    Views:
    317
    Mark McIntyre
    Dec 10, 2005
  4. Freshman_in_C_Programming

    Basic but complicated problem....Help me Plz!!!

    Freshman_in_C_Programming, Feb 1, 2006, in forum: C Programming
    Replies:
    24
    Views:
    789
    Ed Prochak
    Feb 6, 2006
  5. Replies:
    1
    Views:
    616
    Salt_Peter
    Dec 25, 2006
Loading...

Share This Page