Support

If you have a problem or need to report a bug please email : support@dsprobotics.com

There are 3 sections to this support area:

DOWNLOADS: access to product manuals, support files and drivers

HELP & INFORMATION: tutorials and example files for learning or finding pre-made modules for your projects

USER FORUMS: meet with other users and exchange ideas, you can also get help and assistance here

NEW REGISTRATIONS - please contact us if you wish to register on the forum

[Ruby] How to call back from superclass to subclass?

For general discussion related FlowStone

[Ruby] How to call back from superclass to subclass?

Postby tulamide » Wed Mar 14, 2018 7:09 am

Ok, it isn't obvious what I'm looking for. So I try to use an example that is as close to a real world issue, as I can think of, while keeping it simple.

You have a class for centered coordinates. It defines a setter method.
Code: Select all
class A
  def initialize
    @x = 0.0
  end
  def x=(value)
    @x = value
  end
end


A second class inherits A and uses x to calculate the top-left position.
Code: Select all
class B < A
  def initialize
    @l = 0.0
  end
  def update
    @l = @x - 2
  end
end


Now, to actually do the update, I have to wait for x to change. If that happens, update is called afterwards. And here's where it gets ugly. Implementation:
Code: Select all
class B < A
  def initialize
    @l = 0.0
  end
  def update
    @l = @x - 2
  end
  def x=(value)
    super(value)
    update
  end
end


I don't like that for two reasons:
1) defining x=(value) two times (I think it's called monkey patching) shouldn't be necessary in such an elegant language.
2) The update method in reality is a general updater, that not only updates @l, but some other variables as well. The superclass does not just host @x, but a lot of other variables. In effect, I need to define the setter two times for each of them, always with a call of update()

What I'm looking for, is a way to signalize the subclass, that a superclass method has been called, so that I can run update()

PSEUDOCODE!
Code: Select all
class B < A
  def initialize
    @l = 0.0
  end
  def update
    @l = @x - 2
  end
  def message_from_super
    update()
  end
end


Any ideas?
"There lies the dog buried" (German saying translated literally)
tulamide
 
Posts: 2686
Joined: Sat Jun 21, 2014 2:48 pm
Location: Germany

Re: [Ruby] How to call back from superclass to subclass?

Postby KG_is_back » Wed Mar 14, 2018 3:28 pm

Define a dummy update method in the parent and include it in each setter. Then you only need to modify the update method in the subclasses:
Code: Select all

class A

def update;end

def x=(v)
@x=v
update()
return v
end

end

class B < A

def update
@l=@x-2
end

end


That is the simplest most elegant solution.

Alternatively, you may automate the monkey-patching by iterrating trough the parent's methods and declaring new method with the same name for each setter (name should match /^\w+=$/ or in english a nonzero-length word character (numbers/letters/_) string ending with "="). This has the added benefit, that it will also work with setter methods declared with attr_writer and attr_accessor methods. In the previous solution this would not be the case.

Note, you will have to update the specific ruby component for changes to apply, if you create new setters elsewhere.
Code: Select all
class B < A
A.instance_methods.select{
   |name| name.to_s=~/^\w+=$/  #matches setter method names
   }.each{|name|
   #defines new method in B with identical name
   define_method(name){|v| super(v);update();v}
   }
end


Or you may list specific method names to for which this applies, if you're uncomfortable with relying on automatically detecting relevant method names.
Code: Select all
class B < A
[:x=,:y=,:w=,:h=].each{|name|
   #defines new method in B with identical name
   define_method(name){|v| super(v);update();v}
   }
end


EDIT: setters should always return the modified value. If they don't you can quickly run into problems.
KG_is_back
 
Posts: 1196
Joined: Tue Oct 22, 2013 5:43 pm
Location: Slovakia

Re: [Ruby] How to call back from superclass to subclass?

Postby tulamide » Wed Mar 14, 2018 11:04 pm

KG_is_back wrote:Define a dummy update method in the parent and include it in each setter. Then you only need to modify the update method in the subclasses
Reversing the monkey patching. Such a brilliant, yet simple solution. I wonder why this next logical step didn't occur to me. It is not a callback, but it makes the most sense (when reading the code sometime in the future, it is still obvious)


KG_is_back wrote:Or you may list specific method names to for which this applies, if you're uncomfortable with relying on automatically detecting relevant method names.
Code: Select all
class B < A
[:x=,:y=,:w=,:h=].each{|name|
   #defines new method in B with identical name
   define_method(name){|v| super(v);update();v}
   }
end

I like this second most. Using a block, it even comes close to callbacks. However, since it is runtime code, it may confuse me, if I go back to the code in the future.

KG_is_back wrote:EDIT: setters should always return the modified value. If they don't you can quickly run into problems.
What makes you think so? Or do you mean, when using iterating to create the methods? I don't use the return value in the original code example, but access @x directly from the subclass. That's what inheritance is all about (having access to all variables/methods of the superclass). So I hope there isn't a bug hidden somewhere?
"There lies the dog buried" (German saying translated literally)
tulamide
 
Posts: 2686
Joined: Sat Jun 21, 2014 2:48 pm
Location: Germany

Re: [Ruby] How to call back from superclass to subclass?

Postby tulamide » Thu Mar 15, 2018 12:40 am

I just stumbled upon this on stackoverflow. It is a very similar question, and the answer is not only preferring your solution 1 (so we all three agree), it is also stating why that is the ONLY solution, one should think of!

https://stackoverflow.com/a/4846249
"There lies the dog buried" (German saying translated literally)
tulamide
 
Posts: 2686
Joined: Sat Jun 21, 2014 2:48 pm
Location: Germany

Re: [Ruby] How to call back from superclass to subclass?

Postby KG_is_back » Thu Mar 15, 2018 12:52 am

tulamide wrote:What makes you think so? Or do you mean, when using iterating to create the methods? I don't use the return value in the original code example, but access @x directly from the subclass. That's what inheritance is all about (having access to all variables/methods of the superclass). So I hope there isn't a bug hidden somewhere?


It makes a difference when you stack assignment or use it inside other statements. Here is a simple scenario which would yield incorrect behavior:
Code: Select all
#stacked assignment
var=obj.x=5 #you would expect var to receive same value as x. In reality it receives the return value of .x= method, which may be different if you implement .x= method incorrectly.



tulamide wrote:KG_is_back wrote:
Or you may list specific method names to for which this applies, if you're uncomfortable with relying on automatically detecting relevant method names.
CODE: SELECT ALL
class B < A
[:x=,:y=,:w=,:h=].each{|name|
   #defines new method in B with identical name
   define_method(name){|v| super(v);update();v}
   }
end

I like this second most. Using a block, it even comes close to callbacks. However, since it is runtime code, it may confuse me, if I go back to the code in the future.

It will confuse the hell out of you. Especially if you are not careful with the order in which ruby components are loaded. The runtime code is only executed once exactly when the component loads. Shenanigans may occur, especially with the first version, when setters of the parent class get defined after this runtime auto-definer for the child class runs. You may easily end up with code working, saving the schematic and be broken or spammed with exceptions when loading the schematic later...
KG_is_back
 
Posts: 1196
Joined: Tue Oct 22, 2013 5:43 pm
Location: Slovakia

Re: [Ruby] How to call back from superclass to subclass?

Postby tulamide » Thu Mar 15, 2018 6:49 pm

I see what you mean regarding return value! In my case it doesn't have any impact, but if you create code that might be extended by others, it makes sense to keep that in mind.

For me the lesson was, that I should never try to let the superclass know of functionality of the subclass.
"There lies the dog buried" (German saying translated literally)
tulamide
 
Posts: 2686
Joined: Sat Jun 21, 2014 2:48 pm
Location: Germany


Return to General

Who is online

Users browsing this forum: No registered users and 43 guests

cron