Purpose of "self" in this Ruby code snippet - ruby

When I was working on RubyMonk's online exercise "Ruby Primer : Ascent, 3.2 Stacks and Queues" to create a Stack class, I found that I'm not quite understanding the purpose of the self in function push.
class Stack
def initialize(size)
#size = size
#stack = Array.new(#size)
#top = -1
end
def pop
if empty?
return nil
else
result = #stack[#top]
#stack[#top] = nil
#top -= 1
return result
end
end
def push(element)
if full? || element.nil?
return nil
else
#top += 1
#stack[#top] = element
self
end
end
def size
#size
end
def look
#stack[#top]
end
private
def full?
#top == #size - 1
end
def empty?
#top == -1
end
end

It returns the object of class Stack itself, so you could chain method calls like this:
my_stack.push(1).push(2).push(3).size
#=> 3
Each call of push() will result in the original my_stack, so you can keep calling push().

Related

Delegating everything but allowing the possibility to pre-extend our proxy classes

Given a set of class definitions:
class Application
def self.open_current
return Current.new()
end
end
class Current
def get_row(row)
Row.new(row)
end
end
class Row
def get_col(row)
#...
end
end
Design a Proxy class which will:
Create Proxy<<Class>> versions of each class which are extendable via
class ProxyApplication
def myMethod()
#...
end
end
#...
Wrap all return values of all methods in each class such that a proxied class is always used instead of a standard class. I.E.
app = Proxy.new(Application) #app == ProxyApplication
current = app.open_current #current == #<ProxyCurrent>
Ultimately, the definition of Proxy must be dynamic rather than static definitions.
I've been working on this problem for about 6 hours now. I've got the following code. This includes 3 sections:
Initial class setup
Proxy class definition
Testing proxy class
Currently I've got to the point where pApplication=Proxy.new(Application) returns #<Proxy> and pApplication.open_current returns #<ProxyCurrent> which seems kind of on the correct line. However currently it errors when delegate.rb tries to call test() with 2,3 arguments instead of 0...
But my question is, realistically am I going about this correctly? Is using SimpleDelegator the easiest way to do this? One current problem is I'm basically having to add new functionality to the existing SimpleDelegator. I've also looked at using Forwardable, but having to delegate methods manually is not where I want to go with this project, if possible.
Any ideas?
Due to numerous limitations with the initial idea of packing all the calls into a single class, I redesigned it a bit, but it works in exactly the same way.
def proxy__enwrap(obj)
isClass = obj.is_a?(Class)
oldClass = isClass ? obj : obj.class
sNewClass = "Proxy#{oldClass.to_s}"
code = <<-EOF
class #{sNewClass}
include InstanceProxy
def self.__cinit__(obj)
##__cobj__ = obj
end
def self.__cget__
##__cobj__
end
def self.method_missing(m,*args,&block)
if ##__cobj__.respond_to? m
retVal = ##__cobj__.public_send(m,*args,*block)
return proxy__enwrap(retVal)
else
puts "ERROR " + m.to_s + "(" + args.to_s + ") + block?"
#Throw error
end
end
end
#{sNewClass}.__cinit__(#{oldClass.to_s})
if isClass
return #{sNewClass}
else
return #{sNewClass}.new(obj)
end
EOF
::Kernel.eval(code)
end
module InstanceProxy
def method_missing(m,*args,&block)
retVal = #__obj__.__send__(m,*args,&block)
return proxy__enwrap(retVal)
end
def initialize(obj)
#__obj__ = obj
end
end
XXApplication = Application
::Object.const_set "Application", proxy__enwrap(Application)
Currently the only issue is that the object doesn't wrap correctly around yielded objects... I'm not sure if that's even possible though.
Edit:
I've improved the system to also wrap objects passed in as blocks:
def proxy__enwrap(obj)
isClass = obj.is_a?(Class)
oldClass = isClass ? obj : obj.class
sNewClass = "Proxy#{oldClass.to_s}"
code = <<-EOF
class #{sNewClass}
include InstanceProxy
def self.__cinit__(obj)
##__cobj__ = obj
end
def self.__cget__
##__cobj__
end
def self.to_ary
#fix for puts (puts calls to_ary. see: https://stackoverflow.com/questions/8960685/ruby-why-does-puts-call-to-ary)
[self.to_s]
end
def self.method_missing(m,*args,&block)
#Wrap block arguments:
newBlock = Proc.new {}
if block_given?
newBlock = Proc.new do |*args|
args = args.map {|arg| proxy__enwrap(arg)}
block.call(*args)
end
end
#Call delegated functions. Raise error if object doesn't respond to method.
#Return wrapped value
if ##__cobj__.respond_to? m
retVal = ##__cobj__.public_send(m,*args,*block)
return proxy__enwrap(retVal)
else
raise ArgumentError.new("Method '\#\{m.to_s}' doesn't exist.")
end
end
end
#{sNewClass}.__cinit__(#{oldClass.to_s})
if isClass
return #{sNewClass}
else
return #{sNewClass}.new(obj)
end
EOF
::Kernel.eval(code)
end
module InstanceProxy
def method_missing(m,*args,&block)
#Wrap block arguments:
newBlock = Proc.new {}
if block_given?
newBlock = Proc.new do |*args|
args = args.map {|arg| proxy__enwrap(arg)}
block.call(*args)
end
end
#Call delegated functions. Raise error if object doesn't respond to method.
#Return wrapped value
if #__obj__.respond_to? m
retVal = #__obj__.__send__(m,*args,&newBlock)
return proxy__enwrap(retVal)
else
raise ArgumentError.new("Method '#{m.to_s}' doesn't exist.")
end
end
def initialize(obj)
#__obj__ = obj
end
def to_ary
#fix for puts (puts calls to_ary. see: https://stackoverflow.com/questions/8960685/ruby-why-does-puts-call-to-ary)
[self.to_s]
end
end
#
XXApplication = Application
#Silence warnings of overwriting constant
original_verbosity = $VERBOSE
$VERBOSE = nil
::Object.const_set "Application", proxy__enwrap(Application)
$VERBOSE = original_verbosity

attr_accessor - Accessing an objects attributes from another class

I want to access the ogre's object's swings attribute from the Human's class. However, all I am getting is:
NameError: undefined local variable or method ogre for
**<Human:0x007fdb452fb4f8 #encounters=3, #saw_ogre=true>
Most likely a simple solution, and my brain is just not operating this morning. I am running tests with minitest. The test and classes are below:
ogre_test.rb
def test_it_swings_the_club_when_the_human_notices_it
ogre = Ogre.new('Brak')
human = Human.new
ogre.encounter(human)
assert_equal 0, ogre.swings
refute human.notices_ogre?
ogre.encounter(human)
ogre.encounter(human)
assert_equal 1, ogre.swings
assert human.notices_ogre?
end
ogre.rb
class Ogre
attr_accessor :swings
def initialize(name, home='Swamp')
#name = name
#home = home
#encounters = 0
#swings = 0
end
def name
#name
end
def home
#home
end
def encounter(human)
human.encounters
end
def encounter_counter
#encounters
end
def swing_at(human)
#swings += 1
end
def swings
#swings
end
end
class Human
def initialize(encounters=0)
#encounters = encounters
#saw_ogre = false
end
def name
"Jane"
end
def encounters
#encounters += 1
if #encounters % 3 == 0 and #encounters != 0
#saw_ogre = true
else
#saw_ogre = false
end
if #saw_ogre == true
ogre.swings += 1 # <----issue
end
end
def encounter_counter
#encounters
end
def notices_ogre?
#saw_ogre
end
end
The easy fix would be to pass the ogre object as an argument to encounters - assuming encounters isn't used anywhere else without the argument.
class Ogre
...
def encounter(human)
human.encounters(self)
end
...
end
class Human
...
def encounters(ogre)
#encounters += 1
if #encounters % 3 == 0 and #encounters != 0
#saw_ogre = true
else
#saw_ogre = false
end
if #saw_ogre == true
ogre.swings += 1 # <----issue
end
end
...
end

Linked List ruby, the Node and LInkedlist class doesnt work properly

I have some issues with this code, so, I'm trying to make a linked list but with the first variable I get the next issue:
nodo.rb:34 in 'initialize': wrong number of arguments(1 for 0)
So, the Node class have the actual node and the link, and LinkedList the size and the header.
The problem comes when I try to add a new value but I receive the issue. So I dont know how to fix this problem. I will receive any help you could give me.
class Node
def intialize(data,ref = nil)
#data = data
#refe = refe
end
def get_data
return #data
end
def set_data(newdata)
#dato = newdata
end
def get_ref
return #ref
end
def set_ref(newref)
#ref = newref
end
end
class Linkedlist
def initialize
#size = 0
#header = nil
end
def add_var(value)
#aize = #size + 1
if #header == nil
#header = Node.new(value) #the issue comes here, in the moment when I try to make a new class of Node
else
nodeActual = #header
while nodeActual.get_ref != nil
nodeActual = nodeActual.get_ref
end
nodeActual.set_ref(Node.new(value))
end
end
#def print_list
#end
def get_size
return #size
end
end
list = Linkedlist.new
stop = nil
while stop != -1
a = gets.chomp
if a.to_i == -1
stop = -1
else
list.add_var(a)
end
end
#list.print_list
you have a typo in Node class, rename intialize to initialize (missing i)

Singly linked list in Ruby using part of code

I got a question for implementation methods like get_item(index), or find(value) with used classes.
class SinglyLinkedListItem
attr_accessor :object, :next
attr_reader :list
def initialize(object, list)
#object = object
#list = list
end
end
class SinglyLinkedList
attr_reader :length
def initialize
#length = 0
#head = nil
#tail = nil
end
got some methods like
def each
if #length > 0
item = #head
begin
yield item.object
item = item.next
end until item.nil?
end
end
I tried with many ways, but im still getting this error:
NoMethodError: undefined method `object' for nil:NilClass
You have to define method #object on nil:
def nil.object; self end
The error will not appear that way.
I had this problem before with attr_accessor.
Try replacing the
##
with
self.#
And you might want to check the item class as a precaution.
yield item.object unless item.nil?
For Ex:
def each
if self.length > 0
item = self.head
begin
yield item.object unless item.nil? #Check if item turned nil or not
item = item.next
end until item.nil?
end
end
And there might be some more handling that you need to do, but your given data doesn't provide me enough information.

Matching end tags in Ruby

To learn Ruby, I'm implementing different data structures starting with nodes and a simple stack. If I matching each def with a corresponding end, there are lots of error about expecting $end (EOF) but getting end. So I could fix it by stacking some ends at the end of the class, but obviously I don't know why that works.
require "Node"
class Stack
attr_accessor :top
def size
#size
end
def push(node)
if node && node.next
node.next = top
top = node
end
size++
def pop()
if top != nil
top = top.next
end
size--
def to_s
if top != nil
temp = top
while temp != nil
puts temp.value
temp = temp.next
end
else
puts "The stack is empty"
end
end
end
end
end
The node class is very simple and shouldn't cause any problems:
class Node
attr_accessor :next
def initialize(value)
#value = value
end
end
Everything works fine on that Frankenstein Stack, except pushing a node results in NoMethodError: undefined method +#' for nil:NilClass. Not sure if that is related, but I'm mostly concerned with the syntax of method/class declaration and using end
You get an error because ruby does not have ++ and -- operators.
Ruby understand the following constructs
size++
def pop()
# and
size--
def to_s()
like
size + +def pop()
# and
size - -def to_s()
Ruby syntax is expression-oriented and method definition is expression in Ruby. Method definition expressions (def pop() and def to_s()) are evaluated to nil (in your code you actually define method pop inside push method body and to_s inside pop method body). And this is why you get NoMethodError: undefined method +#' for nil:NilClass error - it evaluates expression size + +nil and nil does not define unary plus operator. In this expression first + is an Fixnum addition operator (size is Fixnum), and second + is unary plus operator of nil (result of def pop() expression).
Use += 1 and -= 1 instead of ++ and --. Your code should look like this:
class Stack
attr_accessor :top
def size
#size
end
def push(node)
if node && node.next
node.next = top
top = node
end
#size += 1 # #size, not `size` because you have `size` getter and you cannot modify size with getter method
end
def pop()
if top != nil
top = top.next
end
#size -= 1
end
def to_s
if top != nil
temp = top
while temp != nil
puts temp.value
temp = temp.next
end
else
puts "The stack is empty"
end
end
end
Your defs don’t have a matching end. Also, Ruby does not have a ++ operator; you’ll have to use += 1 instead.

Resources