Forum: Ruby Creating sub-classes using Struct

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Eli B. (Guest)
on 2006-05-06 19:18
Hello,

There are two Ruby features I want to combine. One is sub-classes (or
embedded classes):

class Foo
	def initialize(x)
		puts "new Foo #{x}"
	end

	class Bar
		def initialize(y)
			puts "new Bar #{y}"
		end
	end
end


joe = Foo::Bar.new(12)    # prints "new Bar 12"


Another is dynamic creation of new classes using Struct:

Customer = Struct.new(:name, :address)     #=> Customer
eddy = Customer.new("Dave", "123 Main")

Here 'Customer' is a class and 'eddy' is an instance of the Customer
class.

Now, by combining the features, I mean creating a subclass of Foo
dynamically, using Struct. The following pseudo-code demonstates it
(pseudo because it obviously doesn't work and only shows what I'm trying
to do):

class Foo
	def initialize(x)
		puts "new Foo #{x}"
	end

	Bar = Struct.new(:name, :address)
end

eddy = Foo::Bar.new("Dave", "123 Main")


How can this be done ?

TIA
Eli
Robert K. (Guest)
on 2006-05-06 21:56
(Received via mailing list)
2006/5/6, Eli B. <removed_email_address@domain.invalid>:
>         class Bar
> Another is dynamic creation of new classes using Struct:
> to do):
>
>
> How can this be done ?

You'll have to modify Struct to be able to create sub classes of
arbitrary classes and not just Object. Btw, if feels a bit strange to
have a nested class that is a sub class of the outer class.  By that
twist it contains itself - I think this is not a good idea.

Kind regards

robert
Logan C. (Guest)
on 2006-05-06 22:09
(Received via mailing list)
On May 6, 2006, at 11:19 AM, Eli B. wrote:

> 	class Bar
> Another is dynamic creation of new classes using Struct:
> trying
> eddy = Foo::Bar.new("Dave", "123 Main")
>
>
> How can this be done ?
>

I'm confused. This works for me, the above code works fine for me.

Putting a class inside another class does not subclass it though (Nor
is it supposed to, you're just setting a constant for the enclosing
class).
Eli B. (Guest)
on 2006-05-06 22:55
Robert K. wrote:
> 2006/5/6, Eli B. <removed_email_address@domain.invalid>:
>>         class Bar
>> Another is dynamic creation of new classes using Struct:
>> to do):
>>
>>
>> How can this be done ?
>
> You'll have to modify Struct to be able to create sub classes of
> arbitrary classes and not just Object. Btw, if feels a bit strange to
> have a nested class that is a sub class of the outer class.  By that
> twist it contains itself - I think this is not a good idea.
>

Sorry, I don't understand what you mean by "have a nested class that is
a sub class of the outer class".

Perhaps it's better for me to explain the motivation for what I'm trying
to do. Simplifying:

I have a flat-file full of records. The way records are stored can be
customized via a separate configuration file. I want to have a class
Database which is a container of Record classes. The Record is best
implemented as a Struct, IMHO, and I want to create the class
dynamically after reading the configuration (which says which fields
there are in a Record). I think it would be convenient for a Record to
be a subclass of Database, since there is no point having Records
without a Database.
Bira (Guest)
on 2006-05-06 23:31
(Received via mailing list)
On 5/6/06, Eli B. <removed_email_address@domain.invalid> wrote:
>
> I have a flat-file full of records. The way records are stored can be
> customized via a separate configuration file. I want to have a class
> Database which is a container of Record classes. The Record is best
> implemented as a Struct, IMHO, and I want to create the class
> dynamically after reading the configuration (which says which fields
> there are in a Record). I think it would be convenient for a Record to
> be a subclass of Database, since there is no point having Records
> without a Database.

I think that what you mean by "subclass" is something different than
the traditional meaning of the term.

Subclassing establishes a "is a" relation between the two classes. To
do it in Ruby you'd write:

class Database
  #code goes here
end

class Record < Database
  #more code goes here
end

I don't think this is completely correct, however, since a Record is
not a Database. The relationship you want to establish is "contains":
a Database contains Records. To do this, you'd have to declare a
collection of Records as an attribute of Database, doing something
like this:

class Database
  def initialize()
    @records = []
  end

  def add_record(record)
    @records << record
  end

  #et cetera

end

Defining Record within Database, as in your first example, doesn't
necessarily do any of those things, and is more commonly known as
creating a "nested class".

If you want to define Record dinamically, you could maybe do something
like this:

class Record
  def initialize(*attribs)
    @internal_class = Struct.new(*attribs)
    @internal_object = @internal_class.new
  end

  def method_missing(name, *args)
    if @internal_object.respond_to? name
      @internal_object.send(name, *args)
    else super(name, *args)
    end
  end

  def inspect
    @internal_object.inspect
  end

end

This way, everytime you create a new Record object, you'll need to
define what attributes it has (and these attributes would probably
come from you configuration files). You can place the code for the
Record class inside the Database class to make it a nested class, and
you can create an attribute for Database that is an Array or Hash of
Records.
Logan C. (Guest)
on 2006-05-06 23:43
(Received via mailing list)
On May 6, 2006, at 2:55 PM, Eli B. wrote:

>> arbitrary classes and not just Object. Btw, if feels a bit strange to
> to do. Simplifying:
>
> I have a flat-file full of records. The way records are stored can be
> customized via a separate configuration file. I want to have a class
> Database which is a container of Record classes. The Record is best
> implemented as a Struct, IMHO, and I want to create the class
> dynamically after reading the configuration (which says which fields
> there are in a Record). I think it would be convenient for a Record to
> be a subclass of Database, since there is no point having Records
> without a Database.
>

Eh, I'm not sure you get the is-a relationship. Saying that a Record
is a subclass of Database implies that anywhere you would use a
Database you could use a Record instead. That doesn't really make
sense. It would also seem to imply that a Record could contain many
records. Instead of

class Database
   ...
end

class Record < Database
   ...
end


I would suggest

class Database
   def self.new(file_containing_info)
       fields = parse_field_format(file_containing_info)
       Database.const_set('Record", Struct.new(*fields))
       super
   end

   def initialize(file)
      @records = []
      read_each_record_in_file(file) do |record_data|
           record = Database::Record.new
           extract_record_data_into(record_data, record)
           @records << record
      end
  end
end
Eli B. (Guest)
on 2006-05-07 00:21
Logan C. wrote:
> On May 6, 2006, at 2:55 PM, Eli B. wrote:
>
>>> arbitrary classes and not just Object. Btw, if feels a bit strange to
>> to do. Simplifying:
>>
>> I have a flat-file full of records. The way records are stored can be
>> customized via a separate configuration file. I want to have a class
>> Database which is a container of Record classes. The Record is best
>> implemented as a Struct, IMHO, and I want to create the class
>> dynamically after reading the configuration (which says which fields
>> there are in a Record). I think it would be convenient for a Record to
>> be a subclass of Database, since there is no point having Records
>> without a Database.
>>
>
> Eh, I'm not sure you get the is-a relationship. Saying that a Record
> is a subclass of Database implies that anywhere you would use a
> Database you could use a Record instead. That doesn't really make
> sense. It would also seem to imply that a Record could contain many
> records. Instead of
>
> class Database
>    ...
> end
>
> class Record < Database
>    ...
> end

It seems that I managed to completely confuse everyone using an
inaccurate description. By sub-class I meant a nested class here, not a
child in is-a inheritence. I don't know what I was thinking :-)

>
>
> I would suggest
>
> class Database
>    def self.new(file_containing_info)
>        fields = parse_field_format(file_containing_info)
>        Database.const_set('Record", Struct.new(*fields))
>        super
>    end

Excellent ! const_set is what I was missing to get my solution working.
No more "dynamic constant assignment" errors for me :-)

>
>    def initialize(file)
>       @records = []
>       read_each_record_in_file(file) do |record_data|
>            record = Database::Record.new
>            extract_record_data_into(record_data, record)
>            @records << record
>       end
>   end
> end

This looks like what I need. Many thanks
Hal F. (Guest)
on 2006-05-07 10:01
(Received via mailing list)
Eli B. wrote:
>
> Excellent ! const_set is what I was missing to get my solution working.
> No more "dynamic constant assignment" errors for me :-)
>

FWIW, you can look at Superstruct which I wrote awhile back.
Disclaimer: I think it has bugs not caught by the unit tests.
Also the code is probably not very pretty in places.

As I recall it's at: http://sstruct.rubyforge.org


Cheers,
Hal
This topic is locked and can not be replied to.