Why does this Ruby method need to be a class level method? - ruby

Here's a classic fizzbuzz in Ruby:
class PrimeChecker
def print_em
1.upto 100 do |fizzbuzz|
if (fizzbuzz % 2) == 0 && (fizzbuzz % 5) == 0
puts "fizzbuzz: " + fizzbuzz.to_s
elsif (fizzbuzz % 5) == 0
puts "fizz: "+fizzbuzz.to_s
elsif (fizzbuzz % 2) == 0
puts 'buzz: ' + fizzbuzz.to_s
else
puts "-" + fizzbuzz.to_s
end
end
end
end
PrimeChecker.print_em
When I execute this, I get this error:
undefined method 'print_em'.
I change the method to self.print_em and it works. Does this mean it's a class method (I think so)? Was the method "not found" before because I can only call such methods in a class on actual instances of the object? If I wanted it to be a instance method what is the syntax for that? I'm trying to understand Ruby, classes and methods better.

Class methods are just that: called on the class. Whereas instance methods are called on an instance of that class. An example is more useful:
class Foo
def self.bar
"This is a class method!"
end
def bar
"This is an instance method!"
end
end
Foo.bar # => "This is a class method!"
foo = Foo.new # This creates "foo" to be a new instance of Foo
foo.bar # => "This is an instance method!"
Note that "class methods" in Ruby are actually methods on the class object's singleton. This is a rather difficult concept to explain, and you can read more about it if you'd like.

It's not a class method as written; you need to run it with an instance of PrimeChecker:
pc = PrimeChecker.new
pc.print_em
Using self. turns it into a class method, runnable with the syntax you show.
It doesn't need to be a class method, it's just that that's how you're trying to execute it.

Q: When I run ruby.rb I get undefined method 'print_em'. I change the method to self.print_em and it works. Does this mean it's a class method (I think so).
A: Yes. class Bar; ... def self.foo defines a class method foo for class Bar.
Q: Was the method "not found" before because I can only call such methods in a class on actual instances of the object?
A: You were first defining it as an instance method. In that case, it is only available to instances of the class.
Q: If I wanted it to be a instance method what is the syntax for that?
A: The way you had it originally: class Bar; def foo defines instance method foo for class Bar

Yes, you are completely correct. Currently, the way you define it, you can evaluate the method with:
PrimeChecker.new.print_em
The reason def self.my_awesome_method defines it on the class side is because the stuff inside
class MyAwesomeClass
end
is being executed in the context of MyAwesomeClass. It's all Ruby code, as you can see! This enables you to do things like this:
class MyAwesomeClass
puts "Hello from innards of #{self}!" #=> Hello from the innards of MyAwesomeClass!
end
Method definitions will also only work if you call them after the definition location, for example:
class MyAwesomeClass
my_awesome_method # produces a nasty error
def self.my_awesome_method
puts "Hello world"
end
my_awesome_method # executes just fine
end
Hope this clears some things up.

Related

Call a function and method with the same name in Ruby

Forgive me for my english.
I am a php programmer and now i want to learn Ruby.
In php if you want to call function "foo" within a class, you simply call foo(), and if you want to call method "foo" you call this->foo().
The question is, is it possible to call function and method with the same name in Ruby?
For example:
def foo
puts "In foo function"
end
class A
def call_foo
foo
#How can i call foo function, not a method?
end
def foo
puts "In foo method"
end
end
a = A.new
a.call_foo #Prints "In foo method"
There is no such thing as a function in Ruby, only methods.
If you define a method at the top-level it is an instance method of Object.
If you define a class without a superclass, it's superclass is Object.
So, your A#foo simply overrides Object#foo. And if it overrides Object#foo, it should respect its contract. You should never need to call Object#foo on an A, if A#foo implements Object#foo's contract correctly (and it should, otherwise it would be a violation of the Liskov Substitution Principle). If you want to reuse Object#foo's implementation within A#foo, you can defer to the superclass implementation using super.
Note: what you want is possible using reflection, but the correct solution would be to fix your design:
def foo
puts "In foo function"
end
class A
def call_foo
self.class.superclass.public_instance_method(:foo).bind(self).()
end
def foo
puts "In foo method"
end
end
a = A.new
a.call_foo #Prints "In foo function"
The foo method outside of your class definition is bound to Object, which is an instance of Class. So to call your method you can do:
> Object.foo­
=> "In foo function"
But as it was pointed out before, you should rather declare this method in an appropiate class.
You can also declare this method as class method with:
class A
def self.foo
puts "In foo class method"
end
end
Now you can call it without creating an A instance:
> A.foo
=> puts "In foo class method"

Ruby: relationship between two methods with same name, one is 'super' to the other

In the the following tea timer code, there's a 'start' method inside SleepTimer, which calls 'notify.'
def start
sleep minutes * 60
notifier.notify("Tea is ready!")
end
If you look at the code below, you'll see that there's a notify method in the class StdioUi as well as a notify method in module UiWithBeep. The start method shown above calls the notify method in module UiWithBeep, which then, via 'super,' calls the notify method in class StdioUi. (The effect is that "BEEP!" is heard before "Tea is ready".) However, I don't understand why notifier.notify calls the notify method in module UiWithBeep rather than in class StdioUi.
First Question: how does it know to go to the one 'notify' over the other.
SecondQuestion And, although I understand super, what establishes the relationship so that notify in class StdioUi is 'super' to the other notify. Can you please explain
Tea Timer
class TeaClock
attr_accessor :timer
attr_accessor :ui
def initialize(minutes)
self.ui = StdioUi.new
self.timer = SleepTimer.new(minutes, ui)
init_plugins
end
def init_plugins
puts "init plugins"
#plugins = []
::Plugins.constants.each do |name|
#plugins << ::Plugins.const_get(name).new(self)
end
end
def start
timer.start
end
end
class StdioUi
def notify(text)
puts text
end
end
SleepTimer = Struct.new(:minutes, :notifier) do
def start
sleep minutes * 60
notifier.notify("Tea is ready!")
end
end
module Plugins
class Beep
def initialize(tea_clock)
tea_clock.ui.extend(UiWithBeep)
end
module UiWithBeep
def notify(*) #gets called by notifier.notify("Tea is ready")
puts "BEEP!"
super #calls notify in class StdioUi
end
end
end
end
t = TeaClock.new(0.01).start
The Book: I keep recommending this excellent book, Metaprogramming Ruby. I was consulting it while composing this answer.
So, here you extend an object with a module. In Ruby it's called Object Extension. In simple cases it all works as expected, like this one:
module Foo
def hello
puts "foo"
end
end
class Bar
end
bar = Bar.new
bar.extend Foo
bar.hello
# >> foo
Things get complicated when there are class' own methods involved. Here's a simplified version of your snippet that exhibits the same behaviour.
module Foo
def hello
puts "foo"
super
end
end
class Bar
def hello
puts 'bar'
end
end
bar = Bar.new
bar.extend Foo
bar.hello
# >> foo
# >> bar
When you call a method in ruby, the interpreter has to first find a method to call. This is called Method Lookup. Now, when you define an instance method, in reality it's a method on class object, not that instance. So, method lookup goes like this for first snippet:
1) bar instance => method hello not found here
2) Bar class => method hello found
When you extend an object, however, methods are injected into instance's eigenclass. It's a special "hidden" class, unique for each instance. And in reality method lookup goes through it first. First snippet again:
1) bar instance => method hello not found here
2) bar's eigenclass => method hello not found here
3) Bar class => method hello found
Now it should be clear why Foo.hello is called instead of Bar.hello: because it appears earlier in the method lookup process!
1) bar instance => method hello not found here
2) bar's eigenclass => method hello found
I may have made a few mistakes but this is roughly what happens.
each class has a property called ancestors that represents the inheritance chain. ruby walks through the list of inherited behavior and looks for matching methods. if it finds one, it calls it with the given parameters. if you call super it looks for the next match.
1.9.3-p194 :003 > String.class.ancestors
=> [Class, Mocha::ClassMethods, Module, NewRelic::Agent::MethodTracer::InstanceMethods, NewRelic::Agent::MethodTracer::InstanceMethods::TraceExecutionScoped, NewRelic::Agent::MethodTracer::ClassMethods, NewRelic::Agent::MethodTracer::ClassMethods::AddMethodTracer, Mocha::ModuleMethods, ActiveSupport::Dependencies::ModuleConstMissing, Object, FactoryGirl::Syntax::Vintage, Metaclass::ObjectMethods, Mocha::ObjectMethods, PP::ObjectMixin, JSON::Ext::Generator::GeneratorMethods::Object, ActiveSupport::Dependencies::Loadable, FriendlyId::ObjectUtils, Kernel, BasicObject]

# used inside a self method is doing what exactly?

I saw this in some code today.
class Foo
def self.bar
#myvar = 'x'
end
end
What exactly is this accessing? As far as I can tell, this isn't accessible form instance methods. What's this called (something google-able) as I cant seem to find examples of this anywhere else.
The #myvar syntax identifies myvar as an instance variable so the real question is this:
What is self inside a class method?
And the answer is "self is the class object". So, #myvar is an instance variable of the Foo class object. If you add another class method:
class Foo
def self.pancakes_house
#myvar
end
end
And then do this:
Foo.bar
puts Foo.pancakes_house
You'll see x on the standard output.
A class is also an object, so you are setting a instance variable in class Foo.
>> Foo.bar
>> Foo.instance_variable_get("#myvar")
=> 'x'
Now, for a typical use, add class << self; attr_accessor :myvar; end to your class and you can write:
>> Foo.bar
>> Foo.myvar
=> 'x'
For googling, this is sometimes called a "class instance variable". That is, an instance variable of an object that just happens to be a class.

How do I access the string name of the parent class from a class method in a extended class or included module

My problem basically looks like this:
module Foo
class Bar
def self.who
self.class.to_s
end
end
end
class World < Foo::Bar
end
When I call World.who I don't get "World" as a result, I get "Class". Some quick Googling didn't yield anything useful, so hence I'm here hoping someone will know how to get the correct class name :)
If you're calling foo.bar then inside the bar method the value of self will be foo. So when you call World.who the value of self inside who is World. Since World is a class, World.class will return Class, so that's what you get.
To get back "World" just call self.to_s or self.name (or just to_s or name).
You get that because World is a Class. In ruby, AClass.class != AClass. So, you could use this:
module Foo
class Bar
def self.who
to_s
end
end
end
class World < Foo::Bar
end

How to make a base class method non-overridable in ruby?

I have some base class A with a method that is not to be overridden.
class A
def dont_override_me
puts 'class A saying, "Thank you for not overriding me!"'
end
end
And another class B that extends A and tries to override the dont_override_me method.
class B < A
def dont_override_me
puts 'class B saying, "This is my implementation!"'
end
end
If I instantiate B and call dont_override_me, class B's instance method will be called.
b = B.new
b.dont_override_me # => class B saying, "This is my implementation!"
This is because of ruby's properties. Understandable.
However, how do I force the base class method dont_override_me to be non-overridable by it's derived classes? I could not find a keyword like final in java for ruby. In C++, the base class methods can be made non-virtual so that they become non-overridable by the derived classes. How do I achieve this in ruby?
You can do it, by hooking the change event and changing it back, but it seems a bit smelly to me:
http://scie.nti.st/2008/9/17/making-methods-immutable-in-ruby
It's one of those things that sort of defines Ruby, so fighting against it seems a little pointless imo. If someone redefines something so it breaks horribly.. that's their problem ;-)
Here's a way to do it:
http://www.thesorensens.org/2006/10/06/final-methods-in-ruby-prevent-method-override/
This has also been packaged into a gem called "finalizer" (gem install finalizer)
This makes use of the method_added callback and compares the new method name with a list of methods that you wish to make final.
I recommend:
class A #This is just as you've already defined it.
def dont_override_me
puts 'class A saying, "Thank you for not overriding me!"'
end
end
module BehaviorForB
def dont_override_me
puts 'class B saying, "This is my implementation!"'
end
def greet
"Hello, Friend."
end
end
class B < A
include BehaviorForB
end
b = B.new
b.dont_override_me #=> class A saying, "Thank you for not overriding me!"
b.greet #=> Hello, Friend.
By keeping B's methods tucked away in an mixin, you get exactly what you desire. Any method of B's methods that are not already in A will be available. Methods that are already in A will not be overridden.
One way to prevent a method from being overridden by a subclass (but not recommend) :
class Class
def frozen_method(method)
if class_variable_defined?(:##__frozen_methods__)
add= class_variable_get(:##__frozen_methods__) | [method]
class_variable_set(:##__frozen_methods__,add)
else
class_variable_set(:##__frozen_methods__,[method])
end
class << self
def inherited(child)
def method_added(method)
if class_variable_get(:##__frozen_methods__).include? method
send(:remove_method, method)
error="Cannot change method #{method} because it's not overridde"
raise TypeError, error
end
end
end
end
end
end
class Foo
def hello
'hello'
end
def foo
'foo'
end
frozen_method :foo
end
class Bar < Foo
def foo
'new foo'
end
end
#=> TypeError: Cannot change method foo because it's not overridde
Bar.new.foo #=> 'foo'
Warning: this example is not complete. If you add frozen_method for a previously defined method in the subclass, when this method will be modified in the subclass, it will lose its implementation.

Resources