Accessors and anonymous classes

Hmmm… odd I wrote a version of this last night but it would appear
that I failed to post it to the forum, doh! so here it goes again. If
it did get out and this is a duplicate then please accept my apologies
for the waste of bandwidth.

I am writing a recursive descent parser to parse a configuration file
that consists of nested ‘Sections’ which all look like this:
[ ] {


.
.
}

where may be another section.

I wrote a clase Section to parse the basic structure. Section.initialise
calls method specificItems to parse non section items (see below). For
each type of section I have an anonymous class tied to a Class variable
– I have included one such class below. I had to make the classes
anonymous because I needed to include references to the classes in a
data structure (see subSections ) which tells the parser which section
types are valid in this section and what to do with them.

[ aside: someone asked why I wanted reference to scalar variable in my
previous post – this is why. I needed to have a refernce to a variable
in that structure into which I could store the parsed class]

Now to the problem: As you see I have defined a bunch of accessor
methods using attr_reader and attr_writer, but these don’t work ( I get
an undefined method error ). Adding the accessor methods explicitly
works fine. Odd!

I’m not sure if this is a bug, a feature or simply some lack in my
understanding of how things should work.

Any ideas?

Also this is my first real OO Ruby program and I would appreciate
comments – it is too large (currently 1000 lines) to post here but if
anyone would be willing to have a look at it I’d be happy to email it to
them. My email is [email protected]

I have run into various problems in this project and have managed to
solve all of them but I am far from confident that my solution was the
best way of doing it. The use of anonymous classes is one example. I
wrote a generic text parser for this project and because Ruby does not
support multiple inheiritance I ended up passing an instance of the
parser as a parameter to all the methods – I figured that since the
parser needed to maintain state that I could not make it a mixin…

Cheers, Russell

@@hostService = Class.new( Section ) {

  attr_reader = :services, :converted, :actions, :patterns, 

:realTime,
:periodic, :files
attr_writer = :converted

  def initialize( head, parser )
    @services = []
    @actions = []
    @patterns = []
    @realTime = []
    @periodic = []
    @files = []
    @converted = false
    super( head, parser )
  end

accessor methods — since the shorthand way did not work

  def actions
    @actions
  end

[ boring repeditive stuff snipped ]

  def converted=(value)
    @converted = value
  end

this handles the non section items in the section

  def specificItem( firstToken, parser )

    case firstToken
    when 'service'
      if token = parser.expect(/^(\w+)/, "service name") then
        @services.push( token )
      else
        @errors = true
        parser.restofLine   # ignore the rest of the line
      end
    else
      @errors = true
      parser.error( "#{firstToken} not valid in host section" )
      parser.restofLine   # ignore the rest of the line
    end
 end

defines which sections may be nested within this section

Key is the section type the vailue is [ ,

class to parse section and where to store the result ]

  def subSections( kind )
    {'actions'     => [nil, @@actionList, @actions ],
     'files'       => [nil, @@commaList, @files],
     'patterns'   => [nil, @@patternList, @patterns],
     'realtime'   => [nil, @@matchList, @realTime],
     'periodic'   => [nil, @@matchList, @periodic]
    }[kind]
  end
} # end of hostSection

Russell F. wrote:

Now to the problem: As you see I have defined a bunch of accessor
methods using attr_reader and attr_writer, but these don’t work ( I get
an undefined method error ). Adding the accessor methods explicitly
works fine. Odd!

Hmmmm… I have now done what I should have done before posting this –
written a little test program that illustrates the problem. Of course
it works as expected so something more complex is going on. I’ll go
back and double check everything yet again. If I can reporduce the
‘problem’ in a small program I’ll post again.

If anyone feels like looking at the full program email me – address in
original post.

Russell

Hi –

On Sun, 9 Jul 2006, Russell F. wrote:

@@hostService = Class.new( Section ) {

 attr_reader = :services, :converted, :actions, :patterns,

:realTime,
:periodic, :files
attr_writer = :converted

Just looking at this quickly: you’re assigning to local variables
called attr_reader and attr_writer, when what you want to do is call
the methods of those names:

attr_reader :services, :converted, :actions, …

David

unknown wrote:

Hi –

On Sun, 9 Jul 2006, Russell F. wrote:

@@hostService = Class.new( Section ) {

 attr_reader = :services, :converted, :actions, :patterns,

:realTime,
:periodic, :files
attr_writer = :converted

Just looking at this quickly: you’re assigning to local variables
called attr_reader and attr_writer, when what you want to do is call
the methods of those names:

attr_reader :services, :converted, :actions, …

Thanks David! Doh! it had to be something simple and obvious.

And that’s why my orginal test program worked fine - it was correct.

I see ruby suffers from the same problem as perl – just about anything
is valid syntax, so it compiles but the semantics are not quite what you
intended :wink:

In a past life I spent half each day for several years as a programming
consultant in a large university (in the days of mainframes) I lost
count of the number of times that I had people come in with a listing of
their program, lay it out in front of me and start explaining the latest
incomprehensible behaviour then suddenly stop, go bright red and quickly
gather everything up muttering that they now knew what the problem was.
I know how they feel :slight_smile:

Russell (who has just spent an hour or so carefully paring down his
program to 10 lines that illustrates the problem :slight_smile:

2006/7/9, Russell F. [email protected]:

I see ruby suffers from the same problem as perl – just about anything
is valid syntax, so it compiles but the semantics are not quite what you
intended :wink:

Although Ruby’s syntax is indeed quite flexible I’m rarely bitten by
this. I definitively struggled more with Perl in the old days I used
it rather frequently. So, the good news is: although you stepped into
this once you likely won’t too often in the future. Ah, and btw, the
flag “-w” is also a good way to learn about some of these errors.

Kind regards

robert

Russell, some of your questions haven’t been answered yet:

(…)
I had to make the classes
anonymous because I needed to include references to the classes in a
data structure (see subSections ) which tells the parser which section
types are valid in this section and what to do with them.

I think you can use named classes just as well as anonymous classes:

class HostService < Section

def sub_sections( kind )
{‘actions’ => [nil, ActionList, @actions ],
‘files’ => [nil, CommaList, @files],

end
end

(Note that I renamed the method. See
http://wiki.rubygarden.org/Ruby/page/show/RubyStyleGuide)

If you need to reference the classes before they are created, there are
other possibilities besides class variables: you could use the names of
the classes in the references and then call Module#const_get to get to
the class object:

def sub_sections( kind )
{‘actions’ => [nil, “ActionList”, @actions ],
‘files’ => [nil, “CommaList”, @files],

end

def class_for_name( name )
Object.const_get( name )
end

Or you could pre-create the classes and re-open them later:

class HostService < Section; end
class ActionList < Section; end
class CommaList < Section; end

class HostService
def sub_sections( kind )
{‘actions’ => [nil, ActionList, @actions ],
‘files’ => [nil, CommaList, @files],

end
end

You could also add a convenience method to Section:

class Section

 def self.define_sub_sections( *names )
   names.each do |name|
     const_set( name, Class.new( Section ) )
   end
 end

 define_sub_sections "HostService", "ActionList", "CommaList"

 class HostService
   ...
 end

end

[ aside: someone asked why I wanted reference to scalar variable in my
previous post – this is why. I needed to have a refernce to a variable
in that structure into which I could store the parsed class]

I still don’t understand the need to reference a variable. It seems all
you want is to store a value in a variable, which you did with the class
variables.

(…)
I have run into various problems in this project and have managed to
solve all of them but I am far from confident that my solution was the
best way of doing it. The use of anonymous classes is one example.

See above.

I
wrote a generic text parser for this project and because Ruby does not
support multiple inheiritance I ended up passing an instance of the
parser as a parameter to all the methods – I figured that since the
parser needed to maintain state that I could not make it a mixin…

Modules in Ruby can maintain state. The only caveat is initializing the
state. There are different ways to do it, and it has been discussed
often on ruby-talk, but I don’t have a reference.

Regards,
Pit

Pit, thanks very my for your comments and insights!

Pit C. wrote:

I think you can use named classes just as well as anonymous classes:

class HostService < Section

def sub_sections( kind )
{‘actions’ => [nil, ActionList, @actions ],
‘files’ => [nil, CommaList, @files],

end
end

Yup, that works. I tried this initially but had problems (clearly
something else was wrong). That’s a useful simplication.

(Note that I renamed the method. See
http://wiki.rubygarden.org/Ruby/page/show/RubyStyleGuide)

Ah, right… time for some global sustitutions :slight_smile:

[good stuff snipped for brevity ]

wrote a generic text parser for this project and because Ruby does not
support multiple inheiritance I ended up passing an instance of the
parser as a parameter to all the methods – I figured that since the
parser needed to maintain state that I could not make it a mixin…

Modules in Ruby can maintain state.

Yep, I worked that out eventually and the parser now works much better
as a mixin. I’ve renamed initialize to setup and call it explictly…

Seems to work fine. :slight_smile:

I have redesigned things so I am not having to resort to reference to
get values out of sub classes. So everything is looking good – thanks
to the good folk on this list/forum!

Russell

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs