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

Users are reminded of the forum rules they sign up to which prohibits any activity that violates any laws including posting material covered by copyright

[Ruby] Intercommunication

Post any examples or modules that you want to share here

[Ruby] Intercommunication

Postby tulamide » 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
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 RubyEdit


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:
Code: Select all
ObjectSpace.each_object(RubyEdit) do |obj|
   if obj.respond_to?(:refresh)
      obj.refresh
   end
end


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.
"There lies the dog buried" (German saying translated literally)
tulamide
 
Posts: 2714
Joined: Sat Jun 21, 2014 2:48 pm
Location: Germany

Re: [Ruby] Intercommunication

Postby Tronic » Wed Apr 20, 2016 7:11 am

Why not just use an Observable Pattern?
Tronic
 
Posts: 539
Joined: Wed Dec 21, 2011 12:59 pm

Re: [Ruby] Intercommunication

Postby tulamide » Wed Apr 20, 2016 8:28 am

Because there is no pattern?

I'm not sure I understand what you mean. Could you please elaborate on that?

I case you mean the "Observable" module, that one is not part of Flowstone's Ruby implementation, unfortunately.

I try to solve this with whatever is on-board, so that I don't need to bundle external files.
"There lies the dog buried" (German saying translated literally)
tulamide
 
Posts: 2714
Joined: Sat Jun 21, 2014 2:48 pm
Location: Germany

Re: [Ruby] Intercommunication

Postby Tronic » Wed Apr 20, 2016 8:49 am

this is what I use:

Code: Select all
module Observable

    def add_observer(name,id=nil)
        observer = self
        observers[observer][name] = id            
    end

    def delete_observer(name)
        observer = self
        observers[observer].delete(name)   
    end
   
    def notify_observer(name, id, *args)
        observers.each do |observer,names|   
            if observer.respond_to? name
                if names.include? name
                    if names.fetch(name) == id
                        observer.public_send(name, observer => args)
                    end
                end
            end
        end
    end
   
    def notify_observers(name, *args)
        observers.each do |observer,names|   
            if observer.respond_to? name
                if names.include? name
                    if names.fetch(name).nil?
                        observer.public_send(name, observer => args)
                    end
                end
            end
        end
    end

    def observers
        @@observers ||= Hash.new{|h,k| h[k] = {}}
    end

end

class RubyEdit
   include Observable
end


in some ruby module add the add observer
Code: Select all
add_observer(:fun,:hot)
add_observer(:fuz)

def fuz(*args)
watch 'fuz', args
end

def fun(*args)
watch 'fun', args
end


and in other ruby module use the notify observer
Code: Select all
notify_observers(:fuz,"zap")
notify_observer(:fun,:hot,"huz")
Tronic
 
Posts: 539
Joined: Wed Dec 21, 2011 12:59 pm

Re: [Ruby] Intercommunication

Postby tulamide » Wed Apr 20, 2016 9:23 am

So you created your very own Observable module!

That's pretty cool. Thank you very much for sharing. Will report back in a few days if it worked (well, it will work, but if it works better).
"There lies the dog buried" (German saying translated literally)
tulamide
 
Posts: 2714
Joined: Sat Jun 21, 2014 2:48 pm
Location: Germany

Re: [Ruby] Intercommunication

Postby Tronic » Thu Apr 21, 2016 6:13 pm

If you are interested in the subject,
I can also show an Event Emitter implementation.
Tronic
 
Posts: 539
Joined: Wed Dec 21, 2011 12:59 pm

Re: [Ruby] Intercommunication

Postby tulamide » Thu Apr 21, 2016 6:26 pm

Tronic wrote:If you are interested in the subject,
I can also show an Event Emitter implementation.

Similar to JS? Like 'addListener', 'on event', etc.?
Yes, please, that would be very helpful!
"There lies the dog buried" (German saying translated literally)
tulamide
 
Posts: 2714
Joined: Sat Jun 21, 2014 2:48 pm
Location: Germany

Re: [Ruby] Intercommunication

Postby Tronic » Fri Apr 22, 2016 12:06 am

Here as you can see there is the base of an event system in ruby
and the proper operation of how to perform callbacks in ruby.

This module gives you the ability to have a global event system,
but you can use the same code, only by changing the variable container of callbacks,
instead of using a global variable, use a local variable,
and then add the module to your classes, so it can work locally with your class.

the global event emitter module
Code: Select all
module GlobalEventEmitter
   def _callbacks
      @@_callbacks ||= Hash.new { |h, k| h[k] = {} }
   end
   
   def on(type, &block)
      listener = self
      _callbacks[type][listener] = block if block_given?
   end
   
   def emit(type, *args)
      _callbacks[type].each do |listener,block|
         return block.call(*args)
      end
   end
end

class RubyEdit
   include GlobalEventEmitter
end


add some listener
Code: Select all
on(:this_function) do |args|
   # the last parameter is always returned
   # to the emitter as callback
   # in this case it return args modified   
   args[:knob_1] *= 0.5
   args[:knob_2] += 0.1
   args
end

on(:drawLine) do |a|
   # this draw an line as callback
   p = Pen.new (Color.new(255)), 1
   a.drawLine p, [0,0],[2,2]
end

on(:message) do |a|
   # this output to the emitter module
   a.output 0, "hello"
   # here return true as callback value
   true
end


generate some emitter
Code: Select all
def draw v
   emit(:drawLine,v)
end
watch 'cbk_1', @cbk_1 = emit(:message,self)
watch 'cbk_2', @cbk_2 = emit(:this_function,:knob_1 => 0.1,:knob_2 => 0.8)


callback me? :mrgreen:
Tronic
 
Posts: 539
Joined: Wed Dec 21, 2011 12:59 pm

Re: [Ruby] Intercommunication

Postby tulamide » Fri Apr 22, 2016 1:46 pm

on(:tronic) do |thank_you|
thank_you.because = "this is so elegant yet lightweight!"
thank_you.also = "because it is so easy to understand."
thank_you.finally = "for an implementation that makes communication a lot easier!"
end

awesome = emit(:tronic, laudation)
"There lies the dog buried" (German saying translated literally)
tulamide
 
Posts: 2714
Joined: Sat Jun 21, 2014 2:48 pm
Location: Germany

Re: [Ruby] Intercommunication

Postby Tronic » Fri Apr 22, 2016 3:27 pm

I want to also share this thing (a little magic?):

if you create a primitive module and give it a name in the caption:
___SYSTEM___
it will work to the ruby interpreter level (main),
if you try to write the self function method in a ruby module,
you'll see that it will not give you a RubyEdit instance,
but it will result in the 'main instance of the ruby interpreter',

then, in this primitive module add all your RubyEdit modules, classes, and codes that you want them declared before all things,
this solves the stress of adding new things to the whole Ruby Flowstone System
without having to take account of the order of creation of RubyEdit modules,

so in short, everything that contains this primitive module will always be loaded before anything else.

try it, and then tell me.

Edit: some correction in the text
Last edited by Tronic on Tue May 24, 2016 10:46 pm, edited 1 time in total.
Tronic
 
Posts: 539
Joined: Wed Dec 21, 2011 12:59 pm

Next

Return to User Examples

Who is online

Users browsing this forum: No registered users and 5 guests