[Ruby] Intercommunication
Posted: Wed Apr 20, 2016 4:47 am
Sometimes I am excited like a little kid. I just feel like discovering a new world, while I'm probably the billionth to discover this:
One way to establish a communication in other languages are so called "callbacks". Ruby does not supply exactly those types of "callbacks", and so some time ago we had a thread in the user example section, covering different approaches to mimic such a callback.
Now I found an area of Ruby that I never spend much time in. What you can do with it is more than the mechanic of a callback, so I made this new topic.
There's a kernel module called ObjectSpace. It is defined as "containing a number of routines that interact with the garbage collection facility and allow you to traverse all living objects with an iterator."
Now this sounds like I would never need to use it. So I thought. Then I ran into an issue with my ACM.
It is a server/client design of some kind, which means, there's the global mixer module, where you manage all of the colors of your project, and local receiver modules that each provide one of the many colors for use in drawing. The system works wireless, and it needs to work as such, because you may have hundreds of colors throughout your project. As you know, wireless works downwards only. And here's the catch: When you drag a receiver from the toolbox to your schematic, the receiver immediatly needs to know which color groups of what colors currently exist (so that it can display them for you to choose a color)
But the colors are all manged in the mixer module, way up in the schematic and only connected wireless. But inputs of Ruby don't send a back-trigger, they provide you with the values that were last active (in this case when the receiver was copied first into the toolbox).
So, what I need is a way to recognize when a receiver is dragged from the toolbox, then asking the mixer to update its outputs. Houston, we have a problem.
First I found out that dragging from the toolbox is considered duplicating and so an AfterDup prim will trigger as soon as the module containg it "arrives" in the schematic. Phew, issue one solved!
The following may sound like nothing spectacular, but I worked on and off for almost a year on it. I discovered the same problem when really duplicating a receiver via copy/paste. So I concentrated on this first. After desperate trial and error sessions, one day I remembered ObjectSpace and began to read more closely. I found a method called "_id2ref(object_id)". What it does is creating a reference to the object from its id.
Cool, that would be a way to access the RubyEdit that makes up the ACM mixer from the ACM receiver. In the RubyEdit of the mixer I used the init method to output self.object_id to a wireless transmitter. Also I created a method called "refresh" that sends all colorgroups over the wireless link.
In the receiver I created a section in the event method to react on a trigger from the AfterDup prim and doing
I then copied receivers like hell, and it worked! It's alivvvvveee! Yeah! Heureka!
Then I draged the receiver into my toolbox, dragged it back from the toolbox and ... it didn't work. After a few weeks I realized it is because object_id is stored when copied to the toolbox. But it is not static. The next time you use the mixer, object_id will be different. And the mixer's RubyEdit only sends its object_id once, at init.
I needed a way to tell the mixer that things have changed and a receiver needs an update of all the data. Almost giving up, while working on a project for someone, I turned back to ObjectSpace a few hours ago. To my surprise I found the solution. It was there all the time, I just didn't get it. It isn't straight-forward, so maybe this is an excuse.
respond_to?(symbol) is a method that returns true or false. It checks if an object does respond to a call of the method passed as symbol.
ObjectSpace.each_object(module) loops through all active objects (if module is omitted) or all objects of that type of class or module (if module is passed).
From there it was a small step. I replaced above mentioned code with this one:
This goes through all RubyEdit classes currently alive and checks if any of them know the method .refresh, in which case the method is called (which then sends all the information the receiver needs).
So far so good. There is still an issue. Flowstone crashes when I search for the receiver in the toolbox. Maybe it triggers AfterDup also when an object is dragged into the toolbox? That would produces severe issues with ObjectSpace. I will investigate further.
But for now I just enjoy having solved this pesky issue of upward communication between classes.
One way to establish a communication in other languages are so called "callbacks". Ruby does not supply exactly those types of "callbacks", and so some time ago we had a thread in the user example section, covering different approaches to mimic such a callback.
Now I found an area of Ruby that I never spend much time in. What you can do with it is more than the mechanic of a callback, so I made this new topic.
There's a kernel module called ObjectSpace. It is defined as "containing a number of routines that interact with the garbage collection facility and allow you to traverse all living objects with an iterator."
Now this sounds like I would never need to use it. So I thought. Then I ran into an issue with my ACM.
It is a server/client design of some kind, which means, there's the global mixer module, where you manage all of the colors of your project, and local receiver modules that each provide one of the many colors for use in drawing. The system works wireless, and it needs to work as such, because you may have hundreds of colors throughout your project. As you know, wireless works downwards only. And here's the catch: When you drag a receiver from the toolbox to your schematic, the receiver immediatly needs to know which color groups of what colors currently exist (so that it can display them for you to choose a color)
But the colors are all manged in the mixer module, way up in the schematic and only connected wireless. But inputs of Ruby don't send a back-trigger, they provide you with the values that were last active (in this case when the receiver was copied first into the toolbox).
So, what I need is a way to recognize when a receiver is dragged from the toolbox, then asking the mixer to update its outputs. Houston, we have a problem.
First I found out that dragging from the toolbox is considered duplicating and so an AfterDup prim will trigger as soon as the module containg it "arrives" in the schematic. Phew, issue one solved!
The following may sound like nothing spectacular, but I worked on and off for almost a year on it. I discovered the same problem when really duplicating a receiver via copy/paste. So I concentrated on this first. After desperate trial and error sessions, one day I remembered ObjectSpace and began to read more closely. I found a method called "_id2ref(object_id)". What it does is creating a reference to the object from its id.
Cool, that would be a way to access the RubyEdit that makes up the ACM mixer from the ACM receiver. In the RubyEdit of the mixer I used the init method to output self.object_id to a wireless transmitter. Also I created a method called "refresh" that sends all colorgroups over the wireless link.
In the receiver I created a section in the event method to react on a trigger from the AfterDup prim and doing
Code: Select all
mixer = ObjectSpace._id2ref(@mixer_id) #@mixer_id is the input from the wireless that receives self.object_id
mixer.refresh # calling the method refresh of the mixer's RubyEdit from within the receiver's RubyEditI then copied receivers like hell, and it worked! It's alivvvvveee! Yeah! Heureka!
Then I draged the receiver into my toolbox, dragged it back from the toolbox and ... it didn't work. After a few weeks I realized it is because object_id is stored when copied to the toolbox. But it is not static. The next time you use the mixer, object_id will be different. And the mixer's RubyEdit only sends its object_id once, at init.
I needed a way to tell the mixer that things have changed and a receiver needs an update of all the data. Almost giving up, while working on a project for someone, I turned back to ObjectSpace a few hours ago. To my surprise I found the solution. It was there all the time, I just didn't get it. It isn't straight-forward, so maybe this is an excuse.
respond_to?(symbol) is a method that returns true or false. It checks if an object does respond to a call of the method passed as symbol.
ObjectSpace.each_object(module) loops through all active objects (if module is omitted) or all objects of that type of class or module (if module is passed).
From there it was a small step. I replaced above mentioned code with this one:
Code: Select all
ObjectSpace.each_object(RubyEdit) do |obj|
if obj.respond_to?(:refresh)
obj.refresh
end
endThis goes through all RubyEdit classes currently alive and checks if any of them know the method .refresh, in which case the method is called (which then sends all the information the receiver needs).
So far so good. There is still an issue. Flowstone crashes when I search for the receiver in the toolbox. Maybe it triggers AfterDup also when an object is dragged into the toolbox? That would produces severe issues with ObjectSpace. I will investigate further.
But for now I just enjoy having solved this pesky issue of upward communication between classes.