Here is a description of my problem. I have a Person class which has three attributes: lastname, name and birthDate.
Object subclass: #Person
instanceVariableNames: 'name lastName birthDate'
classVariableNames: ''
package: 'Moi'
i have the setters:
name: aString
name := aString
and similarly for birthDate
and lastName
.
For this class, i have a constructor:
Person class >> withName: aName birthDate: aDate lastName: aLastName
| person |
person := self new.
person
name: aName;
birthDate: aDate;
lastName: aLastName.
^person
To create an instance of a class i send new
to the class:
person := Person new
Then, i provide values to its ivars:
person name: 'toto'; birthDate: '13 Sep 2022'; lastName: 'tata'
Now, I'm not going to have the user enter the method values himself
Person>>#withName:andBirthDate:andLastName:
For this I wrote a method generateData which takes between a method and generates the values that the method receives as arguments. i call it like this in my playground:
generateData:Person>>#withName:andBirthDate:andLastName:
once inside the method, I start by retrieving the instance variables of the class via:
iVars := aMethod variableWriteNodes.
(iVars collect: [ :i | myAllInstVars add:i name ]).
at the end, myAllInstVars has all the instance variables of the class where the method has been implemented. now i am generating random value for each variable based on its type. to do it, i do this:
resultTypeVariables collect: [ :i |
(i isFloat ) ifTrue: [ items add: ((1 to: 1000) atRandom asFloat) ].
(i = SmallInteger) ifTrue: [ items add:(1 to: 256) atRandom ].
(i isInteger) ifTrue: [ items add:(1 to: 256) atRandom ].
(i isNumber) ifTrue: [ items add:(1 to: 256) atRandom ].
(i isString ) ifTrue: [ items add:UUID new asString36].
(i == ByteString ) ifTrue: [ items add:UUID new asString36].
(i == Date) ifTrue: [ items add:(Date fromDays: (1 to: 36000)atRandom) ].
].
items contains the generated values. This is where my problem begins. I would like to rebuild the Person>>#withName:andBirthDate:andLastName: method by adding in its signature the values contained in items.
here is the idea i implemented. I retrieve the setters in the setter protocol like this:
setter := classMethod allSelectorsInProtocol: #'setter'.
( setter ) do: [:i|
instObject := setter,':',items.
].
but when i return instObject i get this as result:
I don't know what to do right now.
I think that the part you are missing here is the #perform:
family of messages. They transform selectors into messages as follows
person := Person new.
person perform: #name: withArgument: 'toto'
where #perform:with:
builds the message with selector #name:
and argument 'toto'
. While there are variants for any number of arguments, what you need is the one I just described.
Thus, if you have say ivars := #('toto' '12 Sep 2022' 'tata')
you will be done with
setters with: ivars do: [:setter :ivar | person perform: setter with: ivar]
where setters := #(#name: #birthDate: #lastName:)
.
Given that in your case #allSelectorsInProtocol:
collects the selectors in a Set
, you might want to put them in an Array
instead and sort them alphabetically for indentification:
(class allSelectorsInProtocol: #setters) asArray sorted
which will produce #(#birthDate: #lastName: #name:)
. Note also that this will require collecting your data in the same order so to match the arguments.