Question about inheritance in Ruby - ruby

I've been attempting to teach myself Ruby over the past while and I've been trying to get something like the following to work but I'm getting the following error...
file.rb:44:infunc': undefined local variable or method number' #<classname:0xb75d7840 #array=[]> (NameError)
The code that's giving me this error is...
class A
def func
file = File.new("file", "r")
file.each_line {|line| #numbers << line.chomp.to_i}
#number = #array[0]
end
end
class B < A
def func
super number
puts number
end
end
Could someone please tell me what I'm doing wrong?
edit// Just a clarification that I want number in class B to inherit the value of #number in class A.

Just like it's telling you, you're calling super and puts with an the argument number — but this number (whatever it's supposed to be) hasn't been defined anywhere. Define number to be something meaningful and the code will almost work. Almost.
The other mistake, which you'll discover after you've fixed that one, is that you're calling super number, which calls A's func method with this mysterious number object as the argument — but A#func doesn't take any arguments.

You forgot the '#' symbol to reference the instance level variable.
It is a really bad design anyway. What if there are no lines in 'file'? #numbers is never initialized. You also wipe out #number completely on the next line with a variable (#array) that has never been defined. Stop trying to fit everything in as few lines as possible and properly initialize your variables.
EDIT: Also, as Chuck noticed, you are passing an argument to a method that takes no arguments.

Your problem is that while you use the instance variable #number, calling super number (which isn't what you want, as this calls the superclass version of whatever method you're in, passing number as an argument) and puts number look up the method number. If you really do want to just look up the instance variable, you just need #number; if you want to define such a method, put one of the following lines in your class:
class A
attr_accessor :number # Define reader (number) and writer (number=)
attr_reader :number # Define only a reader
attr_writer :number # Define only a writer; won't be useful here
# ...
end
And as Ed Swangren said, you ought to clean up A: initialize variables in initialize, make sure you define everything before using it, etc.
Edit 1: Corrected the description of the behavior of super.

Related

how to avoid passing same parameter through multiple methods

def foo(a)
# some computation
bar(a, b)
end
def bar(a,b)
# some computation
baz(a, b, c)
end
def baz(a, b ,c)
print a
end
How to avoid passing same parameter (a) to all methods? One way is to make parameter a as an instance variable. What is the right way to do this?
There is no single solution which works in every case, and often, passing the parameters explicitly is simply the best choice, especially when it comes to debugging.
You can use instance variables, but I would not introduce a new instance variable for the sole reason not to avoid passing it down the call chain. It should have its own value in addition.
One approach which I can see often in projects, is to us a single parameter hash, where the new parameters are added:
def foo(a)
....
bar({a:a, b:b})
end
def bar(par)
....
par[:c] = c
end
def baz(par)
par[:a]+par[:b]+par[:c]
end
This is not without drawbacks either. If you, for instance, forget to "add" a necessary parameter, this will either yield wrong results or throw an exception (depending on how to used them), while forgetting them explicitly via the parameter list, would be found at compile time already.
I would make the decisiion depending on the length of the parameter lists involved. If the number of parameters to a methods is larger than 5, I would start thinking, whether a different design might be more appropriate.
Answering the question stated: yes, ruby is a stateful OO language, having mutable everything. So in ruby one might introduce instance variables:
class MyClass
def foo(a)
#a = a
# some computation
bar(b)
end
def bar(b)
#b = b
# some computation
baz(c)
end
def baz(c)
print [#a, #b, c].inspect
end
end
In the real life, the best practice would be to avoid using instance variables wherever possible, and pass everything across methods. That might save you days of debugging in the future.

override namespaced puts only works after overriding Kernel.puts?

Sorry for the vague question title, but I have no clue what causes the following:
module Capistrano
class Configuration
def puts string
::Kernel.puts 'test'
end
end
end
Now when Capistrano calls puts, I don't see "test", but I see the original output.
However, when I also add this:
module Kernel
def puts string
::Kernel.puts 'what gives?'
end
end
Now, suddenly, puts actually returns "test", not "what gives?", not the original content, but "test".
Is there a reasonable explanation why this is happening (besides my limited understanding of the inner-workings of Ruby Kernel)?
Things that look off to me (but somehow "seem to work"):
I would expect the first block to return 'test', but it didn't
I would expect the combination of the two blocks to return 'what gives?', but it returns 'test'?
The way I override the Kernel.puts seems like a never-ending loop to me?
module Capistrano
class Configuration
def puts string
::Kernel.puts 'test'
end
def an_thing
puts "foo"
end
end
end
Capistrano::Configuration.new.an_thing
gives the output:
test
The second version also gives the same output. The reason is that you're defining an instance level method rather than a class level method (this post seems to do a good job explaining the differences). A slightly different version:
module Kernel
def self.puts string
::Kernel.puts 'what gives?'
end
end
does the following. Because it is causing infinite recursion, like you expected.
/tmp/foo.rb:14:in `puts': stack level too deep (SystemStackError)
from /tmp/foo.rb:14:in `puts'
from /tmp/foo.rb:4:in `puts'
from /tmp/foo.rb:7:in `an_thing'
from /tmp/foo.rb:18
shell returned 1
I use an answer rather than a comment because of its editing capabilities. You can edit it to add more information and I may delete it later.
Now when Capistrano calls puts, I don't see "test", but I see the
original output.
It's difficult to answer your question without seeing how Capistrano calls puts and which one. I would say it's normal if puts displays its parameter, using the original Kernel#puts (it is not clear what you call original output, I must suppose you mean the string given to puts).
I would expect the first block to return 'test', but it didn't
The only way I see to call the instance method puts defined in the class Configuration in the module Capistrano is :
Capistrano::Configuration.new.puts 'xxx'
or
my_inst_var = Capistrano::Configuration.new
and somewhere else
my_inst_var.puts 'xxx'
and of course it prints test. Again, without seeing the puts statement whose result surprises you, it's impossible to tell what's going on.
I would expect the combination of the two blocks to return 'what gives?', but it returns 'test'?
The second point is mysterious and I need to see the code calling puts, as well as the console output.

Ruby: export variable into local namespace

I want to programmatically insert a new variable into the local Ruby namespace. For example, I want to be able to write
label = 'some_name'
# some code equivalent to
# some_name = 3
# but using only 'label' to get the name.
puts some_name # returns 3
What do I put in the middle here to get this done?
I've answered another SO question similar to this. The short answer is this, if you specifically want to create a local variable with the name of it based on the value of another variable, then there is no way to do it. It you just want to make seem as though you've created a local but it is really ruby magic, then something like #mikong's answer is one way to go.
Note that if you relax your contraint and are happy to create an instance variable instead, then you can do it.
label = 'some_name'
self.instance_variable_set("#{label}", 3)
puts #some_name
You can even dynamically define an accessor and then you can get rid of the unsightly #, but once again you will simply have a method masquerading as a local rather than a real local variable.
The following is not exactly code between the 2 lines that you mentioned above:
class Example
attr_accessor :label
def method_missing(name, *args, &block)
return some_processing if name == label.to_sym
end
def some_processing
3 # of course, this can be something more complicated
end
def test
#label = 'some_name'
puts some_name
end
end
Nonetheless it seems to work with what you need. The mechanism has changed from what you gave (label is now an attribute). Also, technically, it's not a variable but a method with a dynamic name that returns what you need.
Personally, I think your requirements seem a little bit dangerous in that the "variable" name changes. I would probably not use the code in my example. I guess depending on the project requirements, I'll think of a different approach.
label = 'some_name'
eval "#{label} = 3"
puts eval "#{label}"
puts local_variables
Note that you would presumably never have an opportunity to execute...
puts some_name
...because if you knew what local variables you were going to create there would be no need to name them with run-time code. And that's good, because the interpreter will not be able to puts some_name directly because it never parsed an assignment for some_name. But it is there and it is a local, as puts local_variables is able to show.

When does Ruby know that a method exists?

One question that ran through my mind was how does the Ruby interpreter know that a method exists on a object if the definition is yet to be interpreted? Like, wouldn't it matter whether you define the method first than use it, rather than use it then define it?
It doesn't know, and it doesn't care - until execution. When a method call statement is executed, the interpreter looks to see if the class (object, not code!) has the named function. If it does not, it looks up the ancestor tree. If it does not find any, it calls the method_missing method. If that is not defined, you get your error.
If your function call does not get executed, you will not get any errors.
The interpreter doesn't know about undefined methods ahead of time, for example:
o = Object.new
o.foo # => Raises NoMethodError.
class Object
def foo
puts "Foo!"
end
end
o.foo # => prints "Foo!", since the method is defined.
However, Ruby has a neat feature called method_missing which let's the receiver of a method call take the method name and arguments as separate arguments and handle accordingly as long as no defined method already handles the call.
def o.method_missing(sym, *args)
puts "OK: #{sym}(#{args.inspect})"
# Do something depending on the value of 'sym' and args...
end
o.bar(1, 2, 3) #=> OK: bar(1, 2, 3)
"Method missing" is used by things like active record find methods and other places where it could make sense to have "dynamically defined" functions.
The problem is, the interpreter tried to find it when you use it, and since it won't be there, it may fail.
In ( some ) compiled languages, it doesn't matter, because while compiling, the compiler may say "I'll look for this on a second pass" but I don't think this is the case with Ruby.

defined? in Ruby works in irb but not in my class file

I was writing a small Heap implementation and upon creating my Node class I noticed some weird behaviour. I wanted to call defined?(x) to ensure x was defined, then check if x was an Integer, before storing it in the Node's value class variable. In IRB I can call
defined?(x) and the result is nil.
However, in the class, I try this:
def change_value value
#value = value if defined?(value)
end
and the result when I call the change_value with a random letter, let's say 'e', is the standard undefined local variable or method error. Again, in IRB it seems to work fine and I am wondering if I have some kind of environment issue or if this is not the 'best' way to check if value is really there.
Thanks.
(Edit: DigitalRoss has since cleaned up the question's formatting, so the comment on formatting and the initial re-write may no longer apply. I'll leave them in until I get some feedback from the OP.)
That's a nearly unreadable way to write a method and it doesn't even parse on my machine (Ruby 1.8.7).
I'm assuming you mean the following:
def change_value(value)
#value = value if defined?(value)
end
This works fine when I call it, but it's incorrect. nil and undefined are two different beasts in Ruby; value will always be defined in that context because it's a declared method parameter. I suspect what you are really after is:
def change_value(value)
#value = value unless value.nil?
end
Note that some people would simply write this as:
def change_value(value)
#value = value if value
end
because nil is "falsy". However, this form conflates nil and false, so it's not a good habit to get into.
This isn't the best way to check if it's really there. If change_value is called without providing any parameters, you would get:
ArgumentError: wrong number of arguments (0 for 1)
Instead, you might want to check to make sure value is not nil. Of course you can do this a variety of ways:
if !value.nil?
#...
end
if value
# this will be exeuted if value is either not `nil` or not `false`
end
Best of luck!
Would it have to do with calling the method from the class instance and not the object instance?

Resources