On 28.12.2011 05:46, Ben Coman wrote:
greetings all,
I am using ConfigurationOfODBC in Pharo 1.3. I have had success with
getting an ODBCResultTable(OrderedCollection) containing ODBCRow
containing about 30 fields. By default the fields are accessed by
"row at: #fieldName" but I wanted to add some convenience methods to
instead go "row fieldName." My searching and experimentation led me
to subclassing ODBCRow as EAPPackage and then using "adoptInstance:"
on each ODBCRow to convert it to the subclass. This was to avoid
having to having to understand and change any of the
ConfigurationOfODBC code. I had tried copyFrom: but some of the
instance variables inherited from ODBCRow were not filled in,
generating exceptions in some of the inherited methods. I had also
considered "become:" but that didn't seem appropriate.
Using "adoptInstance:" worked fine until I added a new instance
variable to EAPPackage. Subsequently for "<primitive: 160 error:
ec>", ec = #'bad receiver'. The "adoptInstance:" method comment says
it follows the same rules as "primitiveChangeClassTo:" (yet note that
"primitiveChangeClassTo:" never worked)
The "primitiveChangeClassTo:" method comment says "The primitive will
fail in most cases that you think might work [...] The facility is
really provided for certain, very specific applications (mostly
related to classes changing shape) and not for casual use" so I
wondered if there was another way to achieve this.
adoptInstance: only works when the object shapes are the same. (It only
switches the class pointer in the object's header, and does not touch
any contents) This is no longer true when the class you switch to have
additional variables.
The full code is hopefully not too long to include for reference
below. The use of "adoptInstance:" in the last section listed.
------
ODBCRow subclass: #EAPPackage
instanceVariableNames: 'parent' "note adoptInstance works without
this instance variable, and not with"
classVariableNames: ''
poolDictionaries: ''
category: 'BTC-EA-Explore'
------
EAPPackage >> name
^ self at: #Name
------
ODBCResultTable subclass: #EAPPackages
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'BTC-EA-Explore'
------
EAPPackages class >> newFromConnection: connection
| packages parent |
packages := (connection query: 'select * from t_package;') execute
asTable.
self adoptInstance: packages.
packages do: [ :row | EAPPackage adoptInstance: row. ].
^packages.
------
In a Workspace I successfully evaluate the following three lines
without :
connection := ODBCConnection dsn: 'CIM' user: '' password: ''.
packages := EAPPackages newFromConnection: connection.
connection close.
------
I found the ODBCRow instance creation to ODBCResultSet>>fetchRow. One
alternative I've considered is to somehow pass through a custom
SomeClass in a variable to replace the hard reference to ODBCRow - but
I don't want to chase back all the cascading call references.
Is there another way to change an existing object to another class,
such that it can be specialised with both additional methods and
instance variables?
I would appreciate any pointers.
regards, Ben
You could act preemtively, and create an extension in your package:
ODBCRow >> #basicNew
^EAPPackages basicNew
I'm somewhat puzzled that the copyFrom:/become: strategy (while an ugly
approach imho) would fail though, do you think you could reduce it to a
simple test using classes in the base image?
Cheers,
Henry