Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calling super without arguments

Tags:

ruby

According to the documentation for modules and classes, calling super (without arguments or parentheses) calls the parent method with the same arguments:

When used without any arguments super uses the arguments given to the subclass method.

Assigning a new value to the "argument variable" seems to alter this behavior:

class MyClass
  def foo(arg)
    puts "MyClass#foo(#{arg.inspect})"
  end
end

class MySubclass < MyClass
  def foo(arg)
    puts "MySubclass#foo(#{arg.inspect})"
    super
    arg = 'new value'
    super
  end
end

MySubclass.new.foo('inital value')

Output:

MySubclass#foo("inital value")
MyClass#foo("inital value")
MyClass#foo("new value")       # <- not the argument given to MySubclass#foo

Is this expected?

Update

This seems to be the expected behavior for positional and keyword arguments, but it doesn't work for block arguments:

class MyClass
  def foo(&block)
    puts "MyClass#foo { #{block.call.inspect} }"
  end
end

class MySubclass < MyClass
  def foo(&block)
    puts "MySubclass#foo { #{block.call.inspect} }"
    super
    block = Proc.new { 'new value' }
    super
  end
end

MySubclass.new.foo { 'initial value' }

Output:

MySubclass#foo { "initial value" }
MyClass#foo { "initial value" }
MyClass#foo { "initial value" }
like image 762
Stefan Avatar asked Jan 16 '15 09:01

Stefan


2 Answers

Lets take one example from the Ruby core:

Keyword2

class Base
  def single(a) a end
  def double(a, b) [a,b] end
  def array(*a) a end
  def optional(a = 0) a end
  def keyword(**a) a end
end

class Keyword2 < Base
  def keyword(foo: "keyword2")
    foo = "changed1"
    x = super
    foo = "changed2"
    y = super
    [x, y]
  end
end

Now, see the test case :-

def test_keyword2
  assert_equal([{foo: "changed1"}, {foo: "changed2"}], Keyword2.new.keyword)
end

Above example exactly mathes the keyword documentation.

Called with no arguments and no empty argument list, super calls the appropriate method with the same arguments, and the same code block, as those used to call the current method. Called with an argument list or arguments, it calls the appropriate methods with exactly the specified arguments (including none, in the case of an empty argument list indicated by empty parentheses).

same arguments means it is saying the current values of argument variables.test_super.rb files contains all the varieties of stuffs we can do with super in Ruby.

No, it work with block too (taken from core) :

a = Class.new do
  def foo
    yield
  end
end

b = Class.new(a) do
  def foo
    super{
      "b"
    }
  end
end

b.new.foo{"c"} # => "b"

But, have no idea why the below is giving "c"? This is actually the updated question of the OP:

c = Class.new do
  def foo(&block)
    block.call
  end
end

d = Class.new(c) do
  def foo(&block)
     block = -> { "b" }
    super
  end
end

d.new.foo{"c"} # => "c"
like image 166
Arup Rakshit Avatar answered Nov 06 '22 01:11

Arup Rakshit


It seems to be the expected behavior, based on the RubySpec anyway.

module RestArgsWithSuper
  class A
    def a(*args)
      args
    end
  end

  class B < A
    def a(*args)
      args << "foo"

      super
    end
  end
end

(language/fixtures/super.rb).

It's then expected that the arguments are modified:

it "passes along modified rest args when they weren't originally empty" do
  Super::RestArgsWithSuper::B.new.a("bar").should == ["bar", "foo"]
end

(language/super_spec.rb)

like image 32
Jiří Pospíšil Avatar answered Nov 06 '22 03:11

Jiří Pospíšil