Cincom Visualworks Smalltalk - Class Methods Initialization - smalltalk

Apologies for the newbie question, but I failed to figure this despite of long try.
I created a matrix class using NewClass feature in Cincom Visualworks.
Smalltalk.Core defineClass: #Matrix
superclass: #{Core.Object}
indexedType: #none
private: false
instanceVariableNames: 'rowCount columnCount cellValues '
classInstanceVariableNames: ''
imports: ''
category: ''
Added the following class method:
withRowCount: rowCount withColumnCount: columnCount withCellValues: cellValues
^self new rowCount: rowCount columnCount: columnCount cellValues: cellValues.
Added the following accessor methods:
cellValues
^cellValues
cellValues: anObject
cellValues := anObject
columnCount
^columnCount
columnCount: anObject
columnCount := anObject
rowCount
^rowCount
rowCount: anObject
rowCount := anObject
I have this code in workspace:
|myMatrix|
myMatrix := Matrix rowCount: 5 columnCount: 5 cellValues: 5.
Transcript show: (myMatrix rowCount).
But compiler says that message undefined.
I guess my class method is not working as expected.
Can someone please point out where I am going wrong?

First: Matrix doesn't have a rowCount:columnCount:cellValues: method. You probably meant Matrix withRowCount: 5 withColumnCount: 5 withCellValues: 5.
Second, I'm thinking methods return the value of the last expression. So chaining methods doesn't work quite like that. (And even if it did, that still looks like one message.)
Your class method should probably read like
withRowCount: rowCount withColumnCount: columnCount withCellValues: cellValues
| newMatrix |
newMatrix := self new.
newMatrix rowCount: rowCount;
columnCount: columnCount;
cellValues: cellValues.
^newMatrix
The ; breaks up the messages and tells Smalltalk to send all three to newMatrix.
Then you can use it like
|myMatrix|
myMatrix := Matrix withRowCount: 5 withColumnCount: 5 withCellValues: 5.
Transcript show: (myMatrix rowCount).

Related

Generate test method with their body in pharo

I advanced a little in my code but I find myself facing another problem for two days. I would like to generate a test method using only the source code. But I have no idea how to do it.
I have a method that allows me to build the name of a test method but I can't write in it.
buildSelectorFor: aMethod
^ String streamContents: [:i || capitalize |
capitalize := true.
i << 'test'.
aMethod selector do: [:charactar |
charactar= $:
ifTrue: [ capitalize := true ]
ifFalse: [ capitalize
ifTrue: [
capitalize := false.
i << charactar asUppercase. ]
ifFalse:[ i << charactar ]]]]
so if I execute this method with this for example:
buildSelectorFor:Car>>#speed:mark:
I get this:
testSpeedMark
my goal is to get something like
testSpeedMark
self assert:....equals:...
I added a method writeTestMethod.
writeTestMethod: aMethod with: anObject
^(self buildTestSelectorFor: aMethod),'
|classMethod setter instObject method|
classMethod := aMethod methodClass.
setter := (classMethod allSelectorsInProtocol: #setter) asArray.
instObject := classMethod new.
(setter with: anObject do: [:set :ivar | instObject perform: set with: ivar]).
self assert: instObject class equals: (classMethod new) class.'
So here is what I get:
I don't know how to integrate the parameters of writetestMethod in the code I want to generate

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

Adding Text to a Smalltalk Cell

I'm doing a project in which I am adapting the Lights Out program in Pharo to a Minesweeper program, but I can't figure out how to add text to the cells so it shows up on-click like the color change does when it's "turned on". I've looked everywhere for a method for it with no dice.
Initialization method:
initialize
super initialize.
self label: ''.
self borderWidth: 4.
mineState := false.
cellValue := 0.
bounds := 0#0 corner: 32#32.
offColor := Color paleYellow.
onColor := Color paleBlue darker.
self useSquareCorners.
self turnOff
New Cell code:
newCellAt: i at: j
"Create a cell for position (i,j) and add it to my on-screen
representation at the appropriate screen position. Answer the new cell"
| c origin |
c := MFCell new.
"self labelString: 'hidden'."
origin := self innerBounds origin.
self addMorph: c.
c position: ((i - 1) * c width) # ((j - 1) * c height) + origin.
c mouseAction: [self checkMineAt: i at: j].
^ c
Maybe you are approaching your issue in too complex manner. You need to overload two selectors and you are done (not with the minesweeper, but the string on Cell).
I have taken the original code and just applied the label.
You have to just redefine turnOn and turnOff, which are in SimpleSwitchMorph which you are using, and apply a selector label: which can be found in super class SimpleButtonMorph. You can even have the same logic as the in the superclass.
In LOCell you can do (create a new protocol switching for them):
turnOn
super turnOn.
self label: 'X'
turnOff
super turnOff.
self label: ''
When running the example now, the X String will be added to the cells that are turned on.
It will look like this:
The protocol:
The morph now:

initialization of local block variable only at the first time the user call the block

Is there a way to write a block ( which does not get any parameters) which does something only at the first call.
I want to initialize a local block variable only at the first time and then change its value each time the user call that block by : Block value.
My block will be defined in a method A inside a class B.
and the method returns the block.
so each time I call method A it should do the initialization. but every time I call the block it should continue from the same point.
for example: I want i to be initialized to 0.
A
^[i:=i+1]
ob1 = B new.
res= obj1 A.
Transcript show: res value. // should print 1
Transcript show: res value. // should print 2
res2= obj1 A.
Transcript show: res2 value. // should print 1
Here's your modified code snippet.
A
| first |
first := true.
^[first ifTrue: [i := 0. first := false].
i := i+1]
or more simply:
A
| i |
i := 0.
^[i := i+1]
Technically the second example initializes the variable before the block is even executed. If that's ok, then this works. If you really want the variable initialized on first call, use the first example.
You can use the outer context of the block to do that:
MyClass>>myBlock
| init |
init := false.
^ [init
ifTrue: [1]
ifFalse: [
init := true.
2]].
This gives different results for the first and the second time the block is accessed:
| block |
block := MyClass new myBlock
{ block value . block value } "=> #( 2 1 )"

How to make instances of a class in Smalltalk?

I'm new in Smalltalk (VisualAge environment) and I try to make a class that counts number of her instances. Unfortunately something dosen't work when I override the 'new' method. This is my class code:
Object subclass: #TestClassB
instanceVariableNames: 'niceVariable '
classVariableNames: 'InstanceCounter '
poolDictionaries: ''!
!TestClassB class publicMethods !
initWithNiceParameter: parameter
|testClassBInstance|
testClassBInstance:= self new.
^(testClassBInstance niceVariable: parameter)!
new
super new.
InstanceCounter isNil
ifTrue: [InstanceCounter := 0]
ifFalse: [InstanceCounter := InstanceCounter + 1].
^self
! !
!TestClassB publicMethods !
niceVariable: anObject
"Save the value of niceVariable."
niceVariable := anObject.
! !
I'd like to create new object with 'initWithNiceParameter' message:
TestClassB initWithNiceParameter: 'my super string'
But all I get is error:
TestClassB does not understand niceVariable:
It's because 'TestClassB' is also an object and seems it has no 'niceVariable' setter.
Do you have any idea how to create objects, when 'new' method is overrided?
The implementation of your method new returns self. The value of self there is the class TestClassB because new is a class method and self in a class method is the class itself.
You should return the object that got created by sending super new:
new
|instance|
instance := super new.
InstanceCounter isNil
ifTrue: [InstanceCounter := 0]
ifFalse: [InstanceCounter := InstanceCounter + 1].
^instance
or shorter:
new
InstanceCounter isNil
ifTrue: [InstanceCounter := 0]
ifFalse: [InstanceCounter := InstanceCounter + 1].
^super new
I was confused because I didn't know if #initialize method is called automatically. I use VisualAge 7.5 and I noticed, that if you create a new class using GUI (right-click, "new" -> "part...") and then save it, #initialize isn't called automatically! Even if you create a instance of your class in workspace. But if you export your class and then load it again, #initialize is called. To be more specific, class definition looks like this:
Object subclass: #InitTest
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''!
!InitTest class publicMethods !
initialize
Transcript show: 'initialize method'; cr.!
new
Transcript show: 'new method'; cr.
^super new.! !
InitTest initialize! "<- it's created automatically"
InitTest initializeAfterLoad!
I think it's very tricky. Do you know how to (re)load a class definition in VisualAge workspace, to be sure that #initialize is called (without writing InitTest initialize)?
Slightly OT, but the #ifTrue:ifFalse is unnecessarily complex. The Smalltalk way to initialize class-level variables is in a class-side #initialize* like so:
TestClassB class>>#initialize
InstanceCounter := 0
Now, when you load TestClassB into the system, InstanceCounter will be initialized and you can simplify from Johan's short version to:
TestClassB class>>#new
InstanceCounter := InstanceCounter + 1.
^super new
or lazily