Creating a method that functions as both a class and instance method - ruby

Let's say I had a class and I wanted to be able to call the same method on the class itself and an instance of that class:
class Foo
def self.bar
puts 'hey this worked'
end
end
This lets me do the following:
Foo.bar #=> hey this worked
But I also want to be able to do:
Foo.new.bar #=> NoMethodError: undefined method `bar' for #<Foo:0x007fca00945120>
So now I modify my class to have an instance method for bar:
class Foo
def bar
puts 'hey this worked'
end
end
And now I can call bar on both the class and an instance of the class:
Foo.bar #=> hey this worked
Foo.new.bar #=> hey this worked
Now my class Foo is 'wet':
class Foo
def self.bar
puts 'hey this worked'
end
def bar
puts 'hey this worked'
end
end
Is there a way to avoid this redundancy?

Have one method call the other. Otherwise, no, there is no way to avoid this "redundancy", because there is no redundancy. There are two separate methods that happen to have the same name.
class Foo
def self.bar
puts 'hey this worked'
end
def bar
Foo.bar
end
end

Related

Is there any difference between Ruby class method calling with in class method with and without self?

I am little bit curious about to know that, is there any difference between below two approaches?
Calling class method with in class method using self
class Test
def self.foo
puts 'Welcome to ruby'
end
def self.bar
self.foo
end
end
Test.bar # Welcome to ruby
Calling class method with in class method without self
class Test
def self.foo
puts 'Welcome to ruby'
end
def self.bar
foo
end
end
Test.bar # Welcome to ruby
Yes, there is a difference. But not in your example. But if foo was a private class method, then your first version would raise an exception, because you call foo with an explicit receiver:
class Test
def self.foo
puts 'Welcome to ruby'
end
private_class_method :foo
def self.bar
self.foo
end
end
Test.bar
#=> NoMethodError: private method `foo' called for Test:Class
But the second version would still work:
class Test
def self.foo
puts 'Welcome to ruby'
end
private_class_method :foo
def self.bar
foo
end
end
Test.bar
#=> "Welcome to ruby"

How do I hide a method from a superclass?

I have a method in a superclass from a third-party gem which I want to hide. I would prefer if it is impossible to call this method at all, so not just override it and leave the body empty.
I believe this may be what you're looking for:
undef_method :foo
This will prevent any calls to the method foo.
In contrast, this will not achieve the same effect:
remove_method :foo
That will remove the method from the child, but will still pass through up the inheritance chain.
Docs: undef_method and remove_method
Use the undef keyword.
class A
def foo
5
end
end
class B < A
undef foo
end
A.new.foo #=> 5
B.new.foo #=> NameError: undefined local variable or method `foo'
That is wrong OOP that you're trying to do there. I suggest you use composition instead of inheritance.
require 'forwardable'
class SomeBaseClass
def foo
puts 'foo'
end
def bar
puts 'bar'
end
def quux
puts 'quux'
end
end
class MyClass
def initialize
#base = SomeBaseClass.new
end
extend Forwardable
def_delegators :#base, :foo, :bar # but not quux
end
mc = MyClass.new
mc.foo
mc.bar
mc.quux
# >> foo
# >> bar
# ~> -:32:in `<main>': undefined method `quux' for #<MyClass:0x007febcc155210> (NoMethodError)

How to define instance method in ruby dynamically?

I want to dynamically create instance method of child class through class method of parent class.
class Foo
def self.add_fizz_method &body
# ??? (This is line 3)
end
end
class Bar < Foo
end
Bar.new.fizz #=> nil
class Bar
add_fizz_method do
p "i like turtles"
end
end
Bar.new.fizz #=> "i like turtles"
What to write on line #3?
use define_method like this:
class Foo
def self.add_fizz_method &block
define_method 'fizz', &block
end
end
class Bar < Foo; end
begin
Bar.new.fizz
rescue NoMethodError
puts 'method undefined'
end
Bar.add_fizz_method do
p 'i like turtles'
end
Bar.new.fizz
output:
method undefined
"i like turtles"
define_method 'fizz' do
puts 'fizz'
end
...or accepting a block
define_method 'fizz', &block

Calling Super Methods in Ruby

I am trying to define some classes in Ruby that have an inheritance hierarchy, but I want to use one of the methods in the base class in the derived class. The twist is that I don't want to call the exact method I'm in, I want to call a different one. The following doesn't work, but it's what I want to do (basically).
class A
def foo
puts 'A::foo'
end
end
class B < A
def foo
puts 'B::foo'
end
def bar
super.foo
end
end
Probably, this is what you want?
class A
def foo
puts 'A::foo'
end
end
class B < A
alias bar :foo
def foo
puts 'B::foo'
end
end
B.new.foo # => B::foo
B.new.bar # => A::foo
A more general solution.
class A
def foo
puts "A::foo"
end
end
class B < A
def foo
puts "B::foo"
end
def bar
# slightly oddly ancestors includes the class itself
puts self.class.ancestors[1].instance_method(:foo).bind(self).call
end
end
B.new.foo # => B::foo
B.new.bar # => A::foo

Ruby Module Inclusion in Methods

In class Foo I'd like to include method Bar under certain conditions:
module Bar
def some_method
"orly"
end
end
class Foo
def initialize(some_condition)
if !some_condition
"bar"
else
class << self; include Bar; end
end
end
end
Is there any cleaner (and clearer) way to achieve the include in the method without having to do it inside the singleton class?
extend is the equivalent of include in a singleton class:
module Bar
def some_method
puts "orly"
end
end
class Foo
def initialize(some_condition)
extend(Bar) if some_condition
end
end
Foo.new(true).some_method # => "orly"
Foo.new(false).some_method # raises NoMethodError

Resources