test_super_does_not_work_cross_method

test_super_does_not_work_cross_method
Friends,

I’m working my way through the koans and I’m at test_super_does_not_work_cross_method in ruby_koans-master/koans/about_inheritance.rb

I get that super attempts to invoke a method with the same name from the most immediate ancestor in the ancestor hierarchy. Thus george.growl throws a NoMethodError exception because there is no growl method in any of the ancestors.

What I am now curious about is how to implement the intent of the example … that is I want to invoke a cross method in an ancestor. How do I do it?

Here’s my sample code:


class Level1

def a_method

puts "Level1: This is fine"

end

def b_method

puts "Level 1: This is what I want"

end

end

class Level2 < Level1

end

class Level3 < Level2

def a_method

super

puts "Level3: This is fine"

b_method  # Produces the "wrong" version of b_method

# I want to do something like the following line

# ancestor.b_method

end

def b_method

puts "Level 3: Not what I want."

end

end

l3 = Level3.new

l3.a_method

And here is the output:


irb(main):026:0* l3 = Level3.new

=> #Level3:0x00563577ff6678

irb(main):027:0> l3.a_method

Level1: This is fine

Level3: This is fine

Level 3: Not what I want.

=> nil

Ralph

Try this:

class Level3 < Level2
  alias :ancestor_b, :b_method

  def a_method
    super
    puts "Level3: This is fine"
    ancestor_b
  end

  def b_method
    puts "Level 3: Not what I want."
  end

end

Click here to view Company Information and Confidentiality Notice.<http://www.jameshall.co.uk/index.php/small-print/email-disclaimer>

Please note that we have updated our privacy policy in line with new data protection regulations. Please refer to our website to view the ways in which we handle your data.

Re: test_super_does_not_work_cross_method
Andy,

Thank you for your answer.

While this will work (I needed to remove the comma in “alias :ancestor_b, :b_method”) it does not have the same “flavor” as using super.

What does the community think of the following:


class Level1

def a_method

puts "Level1: This is fine"

end

def b_method

puts "Level 1: This is what I want"

end

end

class Level2 < Level1

end

class Level3 < Level2

def a_method

super

puts "Level3: This is fine"

b_method  # Produces the "wrong" version of b_method

# I want to do something like the following line

# ancestor.b_method



def b_method

  puts "Sorta Level 3 b_method: This could be what I want"

  super

end



b_method

end

def b_method

puts "Level 3: Not what I want."

end

end

l3 = Level3.new

l3.a_method

Which outputs


Level1: This is fine

Level3: This is fine

Level 3: Not what I want.

Sorta Level 3 b_method: This could be what I want

Level 1: This is what I want

=> nil

Ralph

Wednesday, May 30, 2018, 6:19:59 AM, you wrote:

Try this:

class Level3 < Level2

alias :ancestor_b, :b_method

def a_method

super

puts "Level3: This is fine"

ancestor_b  

end

def b_method

puts "Level 3: Not what I want."

end

end

Click here to view Company Information and Confidentiality Notice.

Please note that we have updated our privacy policy in line with new data protection regulations. Please refer to our website to view the ways in which we handle your data.

Ralph

it does not have the same "flavor" as using super.

Well, it has the distinct advantage of actually working, regardless of the flavour. :wink: The thing is, "super" calls the method of the same name in the ancestor. That's what it does. As far as I know, you can't make it do something else.

I think your new example only appears to work. You can't really define a method inside a method; you're just defining the method twice. Can you call the second b_method at all? If not, what's the point of having it?

Related: if you want the option of calling the parent version of b_method and the new version of b_method, then your code is going to be a PITA to debug. The sensible thing to do would be to redesign your classes so that things are clearer. If you were writing code for real, this might well be a sign that you were using inheritance when something else would be better - say, composition.

···

From: ruby-talk [mailto:ruby-talk-bounces@ruby-lang.org] On Behalf Of Ralph Shnelvar
Sent: 30 May 2018 16:01
To: Ruby users
Subject: Re: test_super_does_not_work_cross_method

Andy,

Thank you for your answer.

While this will work (I needed to remove the comma in "alias :ancestor_b, :b_method") it does not have the same "flavor" as using super.

What does the community think of the following:
- - -
class Level1
def a_method
   puts "Level1: This is fine"
end
def b_method
   puts "Level 1: This is what I want"
end
end

class Level2 < Level1
end

class Level3 < Level2
def a_method
   super
   puts "Level3: This is fine"
   b_method # Produces the "wrong" version of b_method
   # I want to do something like the following line
   # ancestor.b_method

   def b_method
     puts "Sorta Level 3 b_method: This could be what I want"
     super
   end

   b_method
end
def b_method
   puts "Level 3: Not what I want."
end
end

l3 = Level3.new
l3.a_method

Which outputs
- - -
Level1: This is fine
Level3: This is fine
Level 3: Not what I want.
Sorta Level 3 b_method: This could be what I want
Level 1: This is what I want
=> nil

Ralph

Wednesday, May 30, 2018, 6:19:59 AM, you wrote:

Try this:

class Level3 < Level2
alias :ancestor_b, :b_method

def a_method
   super
   puts "Level3: This is fine"
   ancestor_b
end

def b_method
   puts "Level 3: Not what I want."
end

end

Click here to view Company Information and Confidentiality Notice.<http://www.jameshall.co.uk/index.php/small-print/email-disclaimer>
Please note that we have updated our privacy policy in line with new data protection regulations. Please refer to our website to view the ways in which we handle your data.

Ralph

Click here to view Company Information and Confidentiality Notice.<http://www.jameshall.co.uk/index.php/small-print/email-disclaimer>

Please note that we have updated our privacy policy in line with new data protection regulations. Please refer to our website to view the ways in which we handle your data.

Re: test_super_does_not_work_cross_method
Andy,

“I think your new example only appears to work. You can’t really define a method inside a method; you’re just defining the method twice. Can you call the second b_method at all? If not, what’s the point of having it?”

You are correct … it only appears to work. Thank you for your analysis.

Here is my revised version. I’d like your criticism.


class Level1

def a_method

puts "Level1: This is fine"

end

def b_method

puts "Level 1: b_method: This is what I want"

end

end

class Level2 < Level1

end

class Level3 < Level2

def a_method

puts "Level3: a_method"

super

b_method  # Produces the "wrong" version of b_method

# I want to do something like the following line

# ancestor.b_method

    

l3a = Level3A.new

l3a.b_method

end

def b_method

puts "Level 3: b_method"

end

class Level3A < Level2

def b_method

  puts "Level 3A b_method"

  super

end

end

end

l3 = Level3.new

l3.a_method

l3.b_method

And the output is:


Level3: a_method

Level1: This is fine

Level 3: b_method

Level 3A b_method

Level 1: b_method: This is what I want

=> nil

irb(main):039:0> l3.b_method

Level 3: b_method

=> nil

Here is my revised version. I'd like your criticism.

Okay. That works. Instead of using alias, you are instantiating an inner class that inherits from the ancestor, and calling the overridden method on the inner class.
Technically speaking, that's a solution to your problem. But you have added a lot of complexity when you could have just solved the problem in one line. If this purely a technical exercise, there is nothing wrong with that, of course.
(But if I came across this in production code I would swear at you a lot, because in production code this would likely be far more complex than in your example, and it would take me a long time to work out what the hell it was doing, let alone why. The primary purpose of code is so that other developers can read it and make sense of it...)

Click here to view Company Information and Confidentiality Notice.<http://www.jameshall.co.uk/index.php/small-print/email-disclaimer>

Please note that we have updated our privacy policy in line with new data protection regulations. Please refer to our website to view the ways in which we handle your data.

For comparison: here's your code reworked so that Level3 uses composition. See how much cleaner this is?

Sometimes inheritance is a good idea. Sometimes it's a terrible idea. It depends on what you need.

class Level1
  def a_method
    puts "Level1: This is fine"
  end

  def b_method
    puts "Level 1: b_method: This is what I want"
  end
end

class Level2 < Level1
end

class Level3

  def initialize
    @level2 = Level2.new
  end

  def a_method
    puts "Level3: a_method"
    @level2.a_method

    b_method
    @level2.b_method
  end

  def b_method
    puts "Level 3: b_method"
  end

end

l3 = Level3.new
l3.a_method
l3.b_method

Click here to view Company Information and Confidentiality Notice.<http://www.jameshall.co.uk/index.php/small-print/email-disclaimer>

Please note that we have updated our privacy policy in line with new data protection regulations. Please refer to our website to view the ways in which we handle your data.

Re: test_super_does_not_work_cross_method
Andy,

I thank you.

My objective was trying to develop some coding pattern that would allow someone to use super on a cross method … something super does not allow out of the box.

I come from a C++ background and what comes to mind is the envelope/letter idiom. That is, if C++ does not have a (much needed) facility, then one creates it. The envelop/letter idiom is complex to implement … but it is a well-established idiom and easy-to-use once one gets all the details right.

Is a cross-method super idiom needed in Ruby? From what I infer from your analysis, the answer is no. That is - if I am inferring correctly - if one needs a cross-method super then that indicates there is something wrong with the design.

I don’t know Ruby well enough to know if there are times when a cross-method super is a good decision. Nonetheless, I was trying to come up with a pattern that would allow the use of a cross-method super.

Thoughts on this subject would be appreciated.

Ralph

Thursday, May 31, 2018, 1:50:00 AM, you wrote:

For comparison: here’s your code reworked so that Level3 uses composition. See how much cleaner this is?

Sometimes inheritance is a good idea. Sometimes it’s a terrible idea. It depends on what you need.

class Level1

def a_method

puts "Level1: This is fine"

end

def b_method

puts "Level 1: b_method: This is what I want"

end

end

class Level2 < Level1

end

class Level3

def initialize

@level2 = Level2.new

end

def a_method

puts "Level3: a_method"

@level2.a_method

b_method

@level2.b_method

end

def b_method

puts "Level 3: b_method"

end

end

l3 = Level3.new

l3.a_method

l3.b_method

Click here to view Company Information and Confidentiality Notice.

Please note that we have updated our privacy policy in line with new data protection regulations. Please refer to our website to view the ways in which we handle your data.

Ralph

I'm not a C++ programmer, but for what it's worth:

The envelop/letter idiom is complex to implement ... but it is a well-established idiom and easy-to-use once one gets all the details right.
Ruby is a much, much terser, more flexible language than C++. That is, you can get much more done in fewer lines of code. In my experience, if you are using an idiom that is complex to implement in Ruby, then the chances are very high that there is an easier way to do it. (For example, many of the gang-of-four Java patterns are redundant in Ruby.) And the easier way will be better: more readable, more elegant, whatever.

Because Ruby is so flexible, there is probably _always_ at least one other way to do it. And because it can be so terse, it's worth finding the clearest, simplest, most readable way (that is still performant). That usually means going with the flow of the language - "the Ruby way" -- although of course we have our 'gotchas' just like all the other languages.

To give you a kind of idea of Ruby terseness, Sandi Metz once half-jokingly proposed her own rules for Ruby coding:

* Classes can be no longer than one hundred lines of code.
* Methods can be no longer than five lines of code.
* Pass no more than four parameters into a method.
* Controllers (model-view-controller) can instantiate only one object.

These are a target to hit, rather than an achievable goal! But in my experience _most_ of your code can manage two or three of these, and should.

That is - if I am inferring correctly - if one needs a cross-method super then that indicates there is something wrong with the design.
Put another way - absolutely no offense meant here - you probably don't _need_ a cross-method super because there are simpler ways to achieve the same thing - ways that other people (and you, six months down the line) will find easier to read, and that will take up less code lines.

Click here to view Company Information and Confidentiality Notice.<http://www.jameshall.co.uk/index.php/small-print/email-disclaimer>

Please note that we have updated our privacy policy in line with new data protection regulations. Please refer to our website to view the ways in which we handle your data.

Please remove me from your mailing list. Thanks
Valerie

···

31. May 2018 02:41 by Andy.Jones@jameshall.co.uk <mailto:Andy.Jones@jameshall.co.uk>:

Here is my revised version. I'd like your criticism.

Okay. That works. Instead of using alias, you are instantiating an inner class that inherits from the ancestor, and calling the overridden method on the inner class.

Technically speaking, that’s a solution to your problem. But you have added a lot of complexity when you could have just solved the problem in one line. If this purely a technical exercise, there is nothing wrong with that, of course.

(But if I came across this in production code I would swear at you a lot, because in production code this would likely be far more complex than in your example, and it would take me a long time to work out what the hell it was doing, let alone why. The primary purpose of code is so that other developers can read it and make sense of it…)

Click here to view Company Information and Confidentiality Notice. <http://www.jameshall.co.uk/index.php/small-print/email-disclaimer&gt;

Please note that we have updated our privacy policy in line with new data protection regulations. Please refer to our website to view the ways in which we handle your data.

Please remove me from the mailing list as well.
Thank you.

Jessica

···

On Fri, Jun 8, 2018 at 10:45 AM, <valerie.t@tutanota.com> wrote:

Please remove me from your mailing list. Thanks

Valerie

31. May 2018 02:41 by Andy.Jones@jameshall.co.uk:

Here is my revised version. I'd like your criticism.

Okay. That works. Instead of using alias, you are instantiating an inner
class that inherits from the ancestor, and calling the overridden method on
the inner class.

Technically speaking, that’s a solution to your problem. But you have
added a lot of complexity when you could have just solved the problem in
one line. If this purely a technical exercise, there is nothing wrong with
that, of course.

(But if I came across this in production code I would swear at you a lot,
because in production code this would likely be far more complex than in
your example, and it would take me a long time to work out what the hell it
was doing, let alone why. The primary purpose of code is so that other
developers can read it and make sense of it…)

Click here to view Company Information and Confidentiality Notice.
<http://www.jameshall.co.uk/index.php/small-print/email-disclaimer&gt;

Please note that we have updated our privacy policy in line with new data
protection regulations. Please refer to our website to view the ways in
which we handle your data.

Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk&gt;

If you would like to be removed from the list, please look at the
bottom of each e-mail. It shows how to "Unsubscribe".

Leam

···

On Fri, Jun 8, 2018 at 12:05 PM, Jess R <jwrisavi@gmail.com> wrote:

Please remove me from the mailing list as well.
Thank you.

Jessica

On Fri, Jun 8, 2018 at 10:45 AM, <valerie.t@tutanota.com> wrote:

Please remove me from your mailing list. Thanks

Valerie

31. May 2018 02:41 by Andy.Jones@jameshall.co.uk:

Here is my revised version. I'd like your criticism.

Okay. That works. Instead of using alias, you are instantiating an inner
class that inherits from the ancestor, and calling the overridden method on
the inner class.

Technically speaking, that’s a solution to your problem. But you have
added a lot of complexity when you could have just solved the problem in one
line. If this purely a technical exercise, there is nothing wrong with
that, of course.

(But if I came across this in production code I would swear at you a lot,
because in production code this would likely be far more complex than in
your example, and it would take me a long time to work out what the hell it
was doing, let alone why. The primary purpose of code is so that other
developers can read it and make sense of it…)

Click here to view Company Information and Confidentiality Notice.

Please note that we have updated our privacy policy in line with new data
protection regulations. Please refer to our website to view the ways in
which we handle your data.

Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk&gt;

Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk&gt;

I do not remember my login/password that i signed up with. How can do that?

···

On Fri, Jun 8, 2018, 11:06 AM leam hall <leamhall@gmail.com> wrote:

If you would like to be removed from the list, please look at the
bottom of each e-mail. It shows how to "Unsubscribe".

Leam

On Fri, Jun 8, 2018 at 12:05 PM, Jess R <jwrisavi@gmail.com> wrote:
> Please remove me from the mailing list as well.
> Thank you.
>
> Jessica
>
> On Fri, Jun 8, 2018 at 10:45 AM, <valerie.t@tutanota.com> wrote:
>>
>> Please remove me from your mailing list. Thanks
>>
>> Valerie
>>
>>
>>
>> 31. May 2018 02:41 by Andy.Jones@jameshall.co.uk:
>>
>> Here is my revised version. I'd like your criticism.
>>
>> Okay. That works. Instead of using alias, you are instantiating an
inner
>> class that inherits from the ancestor, and calling the overridden
method on
>> the inner class.
>>
>> Technically speaking, that’s a solution to your problem. But you have
>> added a lot of complexity when you could have just solved the problem
in one
>> line. If this purely a technical exercise, there is nothing wrong with
>> that, of course.
>>
>> (But if I came across this in production code I would swear at you a
lot,
>> because in production code this would likely be far more complex than in
>> your example, and it would take me a long time to work out what the
hell it
>> was doing, let alone why. The primary purpose of code is so that other
>> developers can read it and make sense of it…)
>>
>>
>>
>> Click here to view Company Information and Confidentiality Notice.
>>
>> Please note that we have updated our privacy policy in line with new
data
>> protection regulations. Please refer to our website to view the ways in
>> which we handle your data.
>>
>>
>>
>> Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org
?subject=unsubscribe>
>> <http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk&gt;
>>
>
>
>
> Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org
?subject=unsubscribe>
> <http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk&gt;
>

Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk&gt;

Hey Jess, normally you send the note from the e-mail address you're
trying to unsubscribe with. It will reply and ask if you are sure;
reply to that and you should be good. Or you can go to the URL
http://lists.ruby-lang.org (etc) and click "Unsubscribe". It should
send you an e-mail to respond to.

Hope that helps!

Leam

···

On Fri, Jun 8, 2018 at 12:30 PM, Jess R <jwrisavi@gmail.com> wrote:

I do not remember my login/password that i signed up with. How can do that?

On Fri, Jun 8, 2018, 11:06 AM leam hall <leamhall@gmail.com> wrote:

If you would like to be removed from the list, please look at the
bottom of each e-mail. It shows how to "Unsubscribe".

Leam

On Fri, Jun 8, 2018 at 12:05 PM, Jess R <jwrisavi@gmail.com> wrote:
> Please remove me from the mailing list as well.
> Thank you.
>
> Jessica
>
> On Fri, Jun 8, 2018 at 10:45 AM, <valerie.t@tutanota.com> wrote:
>>
>> Please remove me from your mailing list. Thanks
>>
>> Valerie
>>
>>
>>
>> 31. May 2018 02:41 by Andy.Jones@jameshall.co.uk:
>>
>> Here is my revised version. I'd like your criticism.
>>
>> Okay. That works. Instead of using alias, you are instantiating an
>> inner
>> class that inherits from the ancestor, and calling the overridden
>> method on
>> the inner class.
>>
>> Technically speaking, that’s a solution to your problem. But you have
>> added a lot of complexity when you could have just solved the problem
>> in one
>> line. If this purely a technical exercise, there is nothing wrong with
>> that, of course.
>>
>> (But if I came across this in production code I would swear at you a
>> lot,
>> because in production code this would likely be far more complex than
>> in
>> your example, and it would take me a long time to work out what the
>> hell it
>> was doing, let alone why. The primary purpose of code is so that other
>> developers can read it and make sense of it…)
>>
>>
>>
>> Click here to view Company Information and Confidentiality Notice.
>>
>> Please note that we have updated our privacy policy in line with new
>> data
>> protection regulations. Please refer to our website to view the ways in
>> which we handle your data.
>>
>>
>>
>> Unsubscribe:
>> <mailto:ruby-talk-request@ruby-lang.org?subject=unsubscribe>
>> <http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk&gt;
>>
>
>
>
> Unsubscribe:
> <mailto:ruby-talk-request@ruby-lang.org?subject=unsubscribe>
> <http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk&gt;
>

Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk&gt;

Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk&gt;