Self as method argument revisited

Hello –

[Disclaimer: this isn’t intended as a general solution, just something
I’ve been working on.]

A while ago transami brought up the question of an object needing
access to the object that created it, and there was discussion of
techniques including sending self as an argument. I’ve been working
on a case of the underlying problem he was talking about, and I
thought I’d share my (current) solution. Comments welcome.

The big object is a (simple) database image. It creates Row objects
from arguments to an #insert method, and populates itself hash-wise
with the values.

There are also field names. A Row object needs to know its own field
names, so that one can do things like:

r = db[“Black”] # r is a Row object
r[“first_name”] # => David

Originally I had been creating an array of field names for every row,
but that seemed very clunky. Then I got an idea…

When the database object is created, it creates a new class, which
inherits from an existing Row class (DBDBD::Row). That new class has
an instance variable (i.e., its virtual class defines an accessor
method) called @row_class. The new database object assigns itself to
@db in that Row class’s virtual class:

class DBDB
def initialize(…)

@row_class =
class << self
class Row < DBDBD::Row
class << self
attr_accessor :db
end
self
end
end
@row_class.db = self
end

end

The Row objects already know that they can get their fields by doing:

type.db.fields

So everybody’s happy :slight_smile: The database object does not have to pass
itself as an argument when initializing rows, and the rows don’t have
to carry around a copy of the field names (which perhaps they were
doing only by reference, so it wasn’t a storage issue, but it was
still awkward).

I just thought I’d share this since it reminded me so exactly of the
problems transami and others had been talking about.

David

···


David Alan Black | Register for RubyConf 2002!
home: dblack@candle.superlink.net | November 1-3
work: blackdav@shu.edu | Seattle, WA, USA
Web: http://pirate.shu.edu/~blackdav | http://www.rubyconf.com

interesting david. just skimmed it over. perhaps it’s merely that i just
woke, but i suspect it’s b/c your a ruby programming god :slight_smile:
i will have to read it over again, i haven’t fully grasped your example.
what would be nice though is the contrasting “clunky” example to which
you refer, as a source of comparison.

very interested in this, thanks,
tranasmi

···

On Sat, 2002-09-21 at 21:43, dblack@candle.superlink.net wrote:

Hello –

[Disclaimer: this isn’t intended as a general solution, just something
I’ve been working on.]

A while ago transami brought up the question of an object needing
access to the object that created it, and there was discussion of
techniques including sending self as an argument. I’ve been working
on a case of the underlying problem he was talking about, and I
thought I’d share my (current) solution. Comments welcome.

The big object is a (simple) database image. It creates Row objects
from arguments to an #insert method, and populates itself hash-wise
with the values.

There are also field names. A Row object needs to know its own field
names, so that one can do things like:

r = db[“Black”] # r is a Row object
r[“first_name”] # => David

Originally I had been creating an array of field names for every row,
but that seemed very clunky. Then I got an idea…

When the database object is created, it creates a new class, which
inherits from an existing Row class (DBDBD::Row). That new class has
an instance variable (i.e., its virtual class defines an accessor
method) called @row_class. The new database object assigns itself to
@db in that Row class’s virtual class:

class DBDB
def initialize(…)

@row_class =
class << self
class Row < DBDBD::Row
class << self
attr_accessor :db
end
self
end
end
@row_class.db = self
end

end

The Row objects already know that they can get their fields by doing:

type.db.fields

So everybody’s happy :slight_smile: The database object does not have to pass
itself as an argument when initializing rows, and the rows don’t have
to carry around a copy of the field names (which perhaps they were
doing only by reference, so it wasn’t a storage issue, but it was
still awkward).

I just thought I’d share this since it reminded me so exactly of the
problems transami and others had been talking about.

David


David Alan Black | Register for RubyConf 2002!
home: dblack@candle.superlink.net | November 1-3
work: blackdav@shu.edu | Seattle, WA, USA
Web: http://pirate.shu.edu/~blackdav | http://www.rubyconf.com


tom sawyer, aka transami
transami@transami.net

dblack wrote

A while ago transami brought up the question of an object needing
access to the object that created it, and there was discussion of
techniques including sending self as an argument. I’ve been working
on a case of the underlying problem he was talking about, and I
thought I’d share my (current) solution. Comments welcome.

The big object is a (simple) database image. It creates Row objects
from arguments to an #insert method, and populates itself hash-wise
with the values.

There are also field names. A Row object needs to know its own field
names, so that one can do things like:

r = db[“Black”] # r is a Row object
r[“first_name”] # => David

Originally I had been creating an array of field names for every row,
but that seemed very clunky. Then I got an idea…

When the database object is created, it creates a new class, which
inherits from an existing Row class (DBDBD::Row). That new class has
an instance variable (i.e., its virtual class defines an accessor
method) called @row_class. The new database object assigns itself to
@db in that Row class’s virtual class:

While I am still marveling what problem your are trying to solve -
I am sort of wandering why you are creating a Row constant in
the scope of the meta (virtual) class of every DBDB instance?
I doubt hat you woould ever make use of this constant, unless
your are planning on peppering every DBDB instance with a
barrage of singleton methods.

···

class DBDB # The 1.6 object model is inferior
def initialize(…)

@row_class = Class.new (DBDBD::Row)
class << @row_class
attr_accessor :db
end
@row_class.db = self
end

end

class DBDB # 1.7 version
class Row

@row_class = self
class << self
attr_accessor :db
end
end
def initialize(…)

@row_class = Class.new (DBDBD::Row)
@row_class.db = self
end

end

/Christoph

Hi –

interesting david. just skimmed it over. perhaps it’s merely that i just
woke, but i suspect it’s b/c your a ruby programming god :slight_smile:

No, I think it’s because you just woke :slight_smile: Just wait til the
weekend’s over and the one-line drop-in replacements and
methodological critiques start showing up – you’ll see :slight_smile:

i will have to read it over again, i haven’t fully grasped your example.
what would be nice though is the contrasting “clunky” example to which
you refer, as a source of comparison.

(Code is somewhat schematic in what follows, but should give the
idea.)

The thing I was originally doing was something like this:

db is the database object; db.fields are the field names

lines.each do |line|
r = Row.new(line)
db.insert(r)
r.fields = db.fields # mirror the fields in each row
end

which I didn’t like. For one thing, the user can set the database
fields, so I had to do something like this:

def fields=(string)
@fields = string.split
each {|key,row| row.fields = @fields}
end

to keep the db fields and each row’s fields in sync.

OK, so then I thought, if the rows just know which database they
belonged to, they wouldn’t have to carry their own fields around with
them. They could get at the fields through the database object.

That led to:

class Row
def initialize(db, args)

end
end

and this:

r = Row.new(self,args)

so now a row could do:

db.fields # fields being an attr_reader of the database object

But I really didn’t like the passing self thing.

At that point, the question was: where else can the database object be
available? It occurred to me that it could be known to the class of
the rows. But that class was already written… so then it became a
matter of rewriting it dynamically.

So now, when a new database is created, a new Row class is also
created. That class lives in the virtual class of the database
object. It has a @db variable, which hold a reference to the database
object. Schematically:

  db object  <<<<<<<<<<<<<<<<<<
                               ^
  class << db object           ^
                               ^
    class Row                  ^
                               ^
>>>>> @db >>>>>>>>>>>>>>>>>>>>>

^
^ row object
^
^<<<<<<< type.db

So every Database object has a custom-made Database::Row class
available to it, and that class knows how to get at the database
object.

David

···

On Mon, 23 Sep 2002, Tom Sawyer wrote:


David Alan Black | Register for RubyConf 2002!
home: dblack@candle.superlink.net | November 1-3
work: blackdav@shu.edu | Seattle, WA, USA
Web: http://pirate.shu.edu/~blackdav | http://www.rubyconf.com

“Christoph” wrote

class DBDB # 1.7 version
class Row

@row_class = self
whops that should have been;-)

    @db =   nil
   class << self
      attr_accessor :db
   end

end
def initialize(…)

/Christoph

Hi –

dblack wrote

A while ago transami brought up the question of an object needing
access to the object that created it, and there was discussion of
techniques including sending self as an argument. I’ve been working
on a case of the underlying problem he was talking about, and I
thought I’d share my (current) solution. Comments welcome.

The big object is a (simple) database image. It creates Row objects
from arguments to an #insert method, and populates itself hash-wise
with the values.

There are also field names. A Row object needs to know its own field
names, so that one can do things like:

r = db[“Black”] # r is a Row object
r[“first_name”] # => David

Originally I had been creating an array of field names for every row,
but that seemed very clunky. Then I got an idea…

When the database object is created, it creates a new class, which
inherits from an existing Row class (DBDBD::Row). That new class has
an instance variable (i.e., its virtual class defines an accessor
method) called @row_class. The new database object assigns itself to
@db in that Row class’s virtual class:

While I am still marveling what problem your are trying to solve -
I am sort of wandering why you are creating a Row constant in
the scope of the meta (virtual) class of every DBDB instance?
I doubt hat you woould ever make use of this constant, unless
your are planning on peppering every DBDB instance with a
barrage of singleton methods.

Don’t marvel – I may well be on the wrong track :slight_smile: I’m not sure I
understand what you’re saying, though. My thinking is: each database
(DBDB instance) needs to create rows, and those rows need to know how
to send messages to the database. Since I don’t want to put that
knowledge in a static Row class (since different databases will have
different fields), I thought it would make sense to have a separate
Row class for every database.


class DBDB # The 1.6 object model is inferior
def initialize(…)

@row_class = Class.new (DBDBD::Row)
class << @row_class
attr_accessor :db
end
@row_class.db = self
end

end

class DBDB # 1.7 version
class Row

@row_class = self
class << self
attr_accessor :db
end
end
def initialize(…)

@row_class = Class.new (DBDBD::Row)
@row_class.db = self
end

end

In your next post you change that to @row_class.db = nil. But isn’t
self correct? The idea is for the Row class’s db attribute to contain
a reference to this data base object.

David

···

On Mon, 23 Sep 2002, Christoph wrote:


David Alan Black | Register for RubyConf 2002!
home: dblack@candle.superlink.net | November 1-3
work: blackdav@shu.edu | Seattle, WA, USA
Web: http://pirate.shu.edu/~blackdav | http://www.rubyconf.com

Hi –

···

On Mon, 23 Sep 2002, Christoph wrote:

“Christoph” wrote

class DBDB # 1.7 version
class Row

@row_class = self
whops that should have been;-)

    @db =   nil

Sorry, I misquoted this post in my last answer. But why do you need
to set this to nil?

David


David Alan Black | Register for RubyConf 2002!
home: dblack@candle.superlink.net | November 1-3
work: blackdav@shu.edu | Seattle, WA, USA
Web: http://pirate.shu.edu/~blackdav | http://www.rubyconf.com

dblack@candle.superlink.net wrote in message
news:Pine.LNX.4.44.0209230931170.7789-100000@candle.superlink.net

Hi –

dblack wrote

A while ago transami brought up the question of an object needing
access to the object that created it, and there was discussion of
techniques including sending self as an argument. I’ve been working
on a case of the underlying problem he was talking about, and I
thought I’d share my (current) solution. Comments welcome.

The big object is a (simple) database image. It creates Row objects
from arguments to an #insert method, and populates itself hash-wise
with the values.

There are also field names. A Row object needs to know its own field
names, so that one can do things like:

r = db[“Black”] # r is a Row object
r[“first_name”] # => David

Originally I had been creating an array of field names for every row,
but that seemed very clunky. Then I got an idea…

When the database object is created, it creates a new class, which
inherits from an existing Row class (DBDBD::Row). That new class has
an instance variable (i.e., its virtual class defines an accessor
method) called @row_class. The new database object assigns itself to
@db in that Row class’s virtual class:

While I am still marveling what problem your are trying to solve -
I am sort of wandering why you are creating a Row constant in
the scope of the meta (virtual) class of every DBDB instance?
I doubt hat you woould ever make use of this constant, unless
your are planning on peppering every DBDB instance with a
barrage of singleton methods.

Don’t marvel – I may well be on the wrong track :slight_smile: I’m not sure I
understand what you’re saying, though. My thinking is: each database
(DBDB instance) needs to create rows, and those rows need to know how
to send messages to the database. Since I don’t want to put that
knowledge in a static Row class (since different databases will have
different fields), I thought it would make sense to have a separate
Row class for every database.


class DBDB # The 1.6 object model is inferior
def initialize(…)

@row_class = Class.new (DBDBD::Row)
class << @row_class
attr_accessor :db
end
@row_class.db = self
end

end

class DBDB # 1.7 version
class Row

@row_class = self
class << self
attr_accessor :db
end
end
def initialize(…)

@row_class = Class.new (DBDBD::Row)
@row_class.db = self
end

end

In your next post you change that to @row_class.db = nil. But isn’t
self correct? The idea is for the Row class’s db attribute to contain
a reference to this data base object.

Probably the correct line would (could?) have been
`` @db = nil’'.

The ``problem’’ with our version is it forces the creation
(instantiation) of the (virtual) meta class for every DBDB
object without making any apparent use of this fact.

Here the a more complete version (it should work 1.6 - i.e.
I got my self tangled up in the usual self, meta, whatever
class loop;-) .

···

On Mon, 23 Sep 2002, Christoph wrote:


class DBDB
class Row
# …
@db = nil
class << self
attr_accessor :db
def inspect
unless equal? Row
“#<Class: DBDB::Row(#{db})>”
else
super
end
end
alias to_s inspect
end
end

def initialize #(…)
# …
@row_class = Class.new Row
@row_class.db = self
end
end

p DBDB.new
#<DBDB:0x1010a7d8 @row_class=#<Class: DBDB::Row(#DBDB:0x1010a7d8)>>

/Christoph

/Ps The news gate was clocked up yesterday so I only got to read
your answer to Tom’s email today . The general problem I see with
this approach is that one also needs to tune many standard methods
like clone,dup, … and possibly class methods like inherited, _load
etc.

dblack@candle.superlink.net wrote in message
news:Pine.LNX.4.44.0209230935140.7789-100000@candle.superlink.net

Hi –

“Christoph” wrote

class DBDB # 1.7 version
class Row

@row_class = self
whops that should have been;-)

    @db =   nil

Sorry, I misquoted this post in my last answer. But why do you need
to set this to nil?

I guess pure esthetics - the RC (ruby correct) solution
might be

class DBDB
class Row
classs << self
def db
if equal? Row
raise ArgumentError, "Abstract Row class … "
else
@db
end
end
attr_writer :db

end

Anyway I think it is a very interesting approach … I guess
it should be possible to pack the clone,dup, inherited whatever
headache into an extension …

/Christoph

···

On Mon, 23 Sep 2002, Christoph wrote: