smalltalkpharo

initialize with pharo smalltalk


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: enter image description here

I don't know what to do right now.


Solution

  • 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.