How to reference an instance within class initialization method

In trying to learn Ruby, I started a program to create squares.

In defining class Square, I want each new instance to have all the
properties of a square, including its grid-like rows and columns. I
wanted the initialize method to make each new instance an array of
arrays from which I can form rows, columns, sides and diagonals. The
grid could then be used for various applications including magic
squares. However, I don’t know how to reference the instance within the
initialize method so that the instance will be an array of arrays by
virtue of being an instance of class Square. Instead, I tried to create
instance methods. The methods work except mksq, which is supposed to
create the array of arrays. Result = undefined method ‘mksq’ for
[]:square.

My code is in the attached files, with excerpts below. I would
appreciate any insight as to why the mksq method doesn’t work.

I would also appreciate any insight as to whether there is a way to
reference the instance within the class methods.

excerpt from ‘ClassSquare.rb’

def mksq
@square = []
0.upto(@ord-1) do |j|
@square[j].push(Array.new)
0.upto(@ord-1) do |i|
@square[j][i].push(0)
end
end
@square
end

‘MakeSq.rb’

require_relative ‘ClassSquare.rb’

sq = Square.new
puts “Class = #{sq.class}”
puts “Ord = #{sq.ord}; magic constant = #{sq.m}”
puts “Area = #{sq.area} square units”
print “\nValues = #{sq.values}\n”
sq.push(sq.mksq)
0.upto(sq.ord-1) do |i|
puts sq[i]
end

For a start I wouldn’t inherit Array. I made that mistake when trying to
build a spreadsheet 2D array class, and ended up starting over again
with the Array hidden inside the class, not as a superclass.

Don’t use “gets” in your initialize code. Make that part of the user
interface in the rest of your program, but not integral to the class.
What if you wanted to create a Square without interacting with the user?

Keep it simple. Start with the value which is your matrix (2d array),
and then let the rest of your methods and attributes look to that core.

class Square
attr_accessor :value
def initialize( width )
self.value = Array.new(width) { Array.new(width) }
end
end

Things like “area” shouldn’t be an attribute, they should be a
calculation done on the value. This makes it possible to change the
outcome of all the calculations when you change the dimensions of your
square, without having to update each instance variable.

class Square
def area
value.length**2
end
end

I hope this helps.

You can have a look around my source code if you want. It might give you
some ideas on creating a structure of your own:

Unless there’s some code you’re not sharing, you never defined mksq
inside the class Square, so “sq.mksq” wouldn’t exist as a method. What
error / unexpected result do you get?

Joel:
Thanks. I tried using self, but couldn’t get it to work. I’ll play with
your code and see if I can figure out where the value method is defined
and how it works, and how self works. I think your approach works
because self would be the class, whereas I was trying to use it as a
reference to the instance. If I understand how it works, it incorporates
the array of arrays property into the class, eliminating the need to
refer to the instance.

I see your point about the potential need to have a square defined for a
specific purpose within an application without user involvement. A chess
board would be an example. It would need to be an 8x8 grid and therefore
would not require user input. I guess what you are saying is that I
should pass order as a parameter to the initialize method and have the
user input code in the body of the program. I’ll work on that. Thanks
again.

BTW, any thoughts as to why method mksq didn’t work whereas my other
instance methods performed as expected?

Roger Atkins wrote in post #1153215:

I’ll play with
your code and see if I can figure out where the value method is defined
and how it works, and how self works.

“self.value” is similar to “@value”, except that it uses the method
created by the line “attr_accessor :value”

When I write “attr_accessor :value”, Ruby defines these methods behind
the scenes:

class Square
def value
@value
end
def value=(obj)
@value=obj
end
end

There’s a reason I don’t just use the instance variable. Say later on
you decide to do a bit of preprocessing before returning the value, such
as ensuring that the array is a square before returning it (padding out
empty parts, maybe).
If you use @value, you have to change every place that it is used. If
you use the method Square#value, then you only have to redefine that one
method, and the rest of your code doesn’t need to be altered.

“self” is a very useful word in Ruby once you start using Classes to
their full potential. Here’s an example:

class Square
def do_something
puts 1
end
def do_something_else
puts 2
end
end

sq.do_something
sq.do_something_else

If you tried to chain those two methods together into:
sq.do_something.do_something_else
You’d get an error, because “puts” returns nil, and
nil.do_something_else isn’t defined.

However, try this:

class Square
def do_something
puts 1
self
end
def do_something_else
puts 2
self
end
end

You can now chain the methods together, because what sq.do_something
returns when it’s finished is sq. Each Instance of Square will return
itself when calling that method; not the Class Square, but that specific
Instance of that Class. What’s what “self” refers to when you’re in an
Instance method.

Holding your 2D Array in an instance variable is very useful because you
don’t need to hold all the straights and diagonals as well, all they do
is point to parts of @value. Rather than hold each of them in a
variable, just make them into methods that return parts of @value, or do
some calculation on it.

Joel:

After analyzing your code some more, I realized that self.value doesn’t
call a method. The concept of calling a variable is new to me. I had
tried things like self.push (back when Array was a super class) but
couldn’t get it to work.

I re-read the Pickaxe material regarding class Array and regarding self
in light of your code. Now I have a better understanding of what the
Pickaxe tech-speak was trying to tell me. I was able to code a solution
that accomplishes my objective. Thanks again.

Joel P. wrote in post #1153356:

Unless there’s some code you’re not sharing, you never defined mksq
inside the class Square, so “sq.mksq” wouldn’t exist as a method. What
error / unexpected result do you get?

I apologize if my code files were not attached. They were listed as
attachments when I submitted the original post. I have since modified
the files substantially based on your advice, so I can’t attach the same
material I thought I attached the first time. If the files were attached
and you looked in ‘ClassSquare.rb’ and there was no mksq method, I must
not have saved the file properly. As indicated in the original post,
mksq was as follows:

excerpt from ‘ClassSquare.rb’

def mksq
@square = []
0.upto(@ord-1) do |j|
@square[j].push(Array.new)
0.upto(@ord-1) do |i|
@square[j][i].push(0)
end
end
@square
end

It may be moot now that I have achieved the intended result using your
initialization code.