Futurescale, Inc. PureMVC Home

The PureMVC Framework Code at the Speed of Thought


Over 10 years of community discussion and knowledge are maintained here as a read-only archive.

New discussions should be taken up in issues on the appropriate projects at https://github.com/PureMVC

Show Posts

* | |

  Show Posts
Pages: [1]
1  PureMVC Manifold / Port Authority / Re: Ruby Port (Standard and MultiCore) on: November 02, 2009, 01:27:58
Mediators typically retrieve and cache references to their Proxy collaborators in the onRegister method. There usually isn't a need for a Proxy to send itself to a Mediator inside a notification.

And when would the Proxy send this notification containing itself? What would trigger it? It can't be at or immediately after construction, as the Model is generally prepared before the View. This allows the Mediators to retrieve the Proxies they want to collaborate with. This would require an extra mechanism to tell the Proxies to send themselves AFTER Model and View preparation.

Heh, this was just an example, maybe sending the notification from the facade without a proxy would have been a better way. The actual implementation is trivial. I'm aware that there is no need to do this, but if i had chosen an example which incorporated wxruby, it would have been more complex to understand.

One of the big benefits of this framework is that its best practices aren't platform specific. They dictate the collaboration patterns of the actors, who exist in an abstract realm that is portable to many languages. So one of the dangers when porting is that we break the generally applicable best practices when we alter the framework implementation in order to take advantage of some platform specific magic.

Agreed, but since ruby is all about open classes and duck-typing in which modules play an important role, i thought it would be a good idea to allow rubyists their daily dose of syntactic sugar.

I agree with adhering to platform conventions such as use of lower_case_and_underscore for method names as opposed to camelCase, if that's what developers are used to in the target platform/language. This is a trivial choice and none of the meaning is lost either way.

Alright. What about package-structure and naming?

But best practices governing things like how a Mediator works and how the PureMVC actors collaborate need to remain as similar to the reference as possible, or confusion will reign.

They work the same way in regards to interaction with others actors of the system. As mentioned, you can stick to the reference implementation if you want to. Sure, if there is an open-source project which would use the module extended mediators, you need to take a look at the rubydocs (documentation) when porting to AS3, but that (again) is the same if they had implemented the mediator themselves against the interface. (as you mentioned in one of your online talks)

There is currently only one Best Practices document, with AS3 examples, but the advice should remain valid for all ports without exception. The framework is simple enough in scope and implementation for this to be possible.

It's fully compliant to the best practices docs.

And while ostensibly all the contributors are supposed to tend their forums, the reality is I do the bulk of the support here. I'm in it for the long haul. So, when someone asks me a question about how to move data around in their application, I need to be able to dispense advice based on the core best practices without a lot of disclaimers about how the Frobnitz++ port does things differently and since I'm not a Frobnitz programmer I'm not sure I understand much about it.

I'm aware that this is quite a hassle, but you could still support people in the AS3 reference "way" and point out solutions without caring for additions.

And a developer (or team) using more than one port of the framework, should not find themselves having to apply two different approaches when they build or migrate their applications. They will find it particularly frustrating.

In short, my vote is to veto these platform-specific mediator implementation alterations for the sake of of maintaining the core roles, responsibilities and collaborations of the reference.

Okay, i could refactor this into a separated "utility" to clearly distinguish between default behavior and additional features, but this is just for clarity and a little bit overkill.
2  PureMVC Manifold / Port Authority / Re: Ruby Port (Standard and MultiCore) on: October 28, 2009, 07:08:25
Explaining the use and benefit of a module-extendable mediator

Setup

We have 1 proxy and 3 mediators. The proxy will send a notification with a reference to itself. All 3 mediators will listen to the Proxy::Reference notification and handle it by putting a message to STDOUT which includes its own object-name and a reference to the proxy from note.body. The mediators will come in different "flavors", Classic, Alternative & ModuleExtended.


An ordinary proxy which will send a notification for us, with a reference to itself.

:
class Proxy < PureMVC::MultiCore::Proxy
  Reference = :proxy_reference # notification constant

  def initialize(name, data)
    super(name, data)
  end  
 
  def distribute_reference
    facade.send_notification(Proxy::Reference, self)
  end
  
end


The classic mediator as described in the AS3 reference implementation. I'm aware that the additional handler-method is a little bit overkill for this small example, but it makes comparison a little bit easier.

:
class ClassicMediator < PureMVC::MultiCore::Mediator

  def initialize(name, view)
    super(name, view)
  end
  
  def list_notification_interests
    [Proxy::Reference]
  end
  
  def handle_notification(note)
    case note.name
      when Proxy::Reference
        handle_proxy_reference(note)
    end
  end

  def handle_proxy_reference(note)
    p "Hello from mediator #{self} handler method, notified by proxy #{note.body}"
  end
  
end


The alternative mediator still derives from the same class, but allows a different approach for registering and handling notifications. Remember, both ways are fully interchangeable, the only decision you have to make is, to stick to one or the other way. The full potential of the alternative approach is visible in the ModuleExtendedMediator.

:
class AlternativeMediator < PureMVC::MultiCore::Mediator

  def initialize(name, view)
    super(name, view)
  end
  
  def initialize_mediator # super calls this, while initializing
    add_notification_handler(Proxy::Reference, :handle_proxy_reference)
  end
  
  # we can omit list_notification_interests and handle_notification because super-class handles them
  
  def handle_proxy_reference(note)
    p "Hello from mediator #{self} handler method, notified by proxy #{note.body}"
  end
  
end


Maybe we have a certain use-case, which is always the same for some of our mediators. We could extract these portions of code and fabricate them into a module, and with it we extend those mediators which need this behavior.

:
module ProxyAware

  def self.extended(mediator) # constructor-like method, called when this module extends an object
    mediator.add_notification_handler(Proxy::Reference, :handle_proxy_reference)
  end
  
  def handle_proxy_reference(note)
    p "Hello from mediator #{self} handler method, notified by proxy #{note.body}"
  end
  
end


The module-extended mediator just needs to extend itself with our module (ProxyAware) while initializing. We could extend our mediator with as many modules as we like, we could even have multiple handlers for the same notification, each doing something specific with it.

:
class ModuleExtendedMediator < PureMVC::MultiCore::Mediator  

  def initialize(name, view)
    super(name, view)
    extend ProxyAware # we extent our instance via module ProxyAware
  end
  
end


We tie this all together...
:
facade = PureMVC::MultiCore::Facade.new(:facade)

proxy = Proxy.new(:proxy, nil)
facade.register_proxy(proxy)

facade.register_mediator(ClassicMediator.new("classic", nil))
facade.register_mediator(AlternativeMediator.new("alternative", nil))
facade.register_mediator(ModuleExtendedMediator.new("module", nil))

proxy.distribute_reference


...and get something like this when we run it:
:
# => "Hello from mediator #<ClassicMediator:0x2e398cc> handler method, notified by proxy #<Proxy:0x2e39930>"
# => "Hello from mediator #<AlternativeMediator:0x2e397c8> handler method, notified by proxy #<Proxy:0x2e39930>"
# => "Hello from mediator #<ModuleExtendedMediator:0x2e39750> handler method, notified by proxy #<Proxy:0x2e39930>"


We are not limited to proxy-mediator relations.

Example A: Many view components wxruby provides, allow us to call #show and #hide on them, which shows or hides them respectivly. If we follow the "convention over configuration"-idiom for our application-solution, we could say that there will be always a global HidePopupFrames notification, which hides all loose(popup) frames. We build a module called Popupable which adds the notification interest for this and has a handler-methods that calls #hide on its (view)component. So all we have to do is to extend our mediators with that module, if we need this behavior.

Example B: We don't use a gui-framework, but instead build an command-line-interface which implements its own shell-like environment. Lets say certain views (yes, there might be none-monolithic view-component even in shells) needs to print out a table. we implement to two modules, one for our view-component in order to display the table, by writing data to a buffer, and one for our mediator, which adds the necessary notification-interests and handlers for the data to our mediator.

One last thing about the alternative mediator:
it's merely a byproduct we need in order to use modules. It's real power comes when you combine it with modules, if you don't except to write modules there is no real benefit of using it and the classic approach might be more suited for you.
3  PureMVC Manifold / Port Authority / Ruby Port (Standard and MultiCore) on: October 27, 2009, 09:18:07
This is a Request for Comments for the Ruby PureMVC implementation: updated (2009-10-27)

1. Package naming & distribution

The gem (package) should be published under a lowercase-underscore name,
which is somewhat a ruby convention for gems (yes, not all adhere to this one, i know).
Nevertheless 'puremvc_multicore' and 'puremvc_standard' would be appropriate names.

With gemcutter.org getting more popular it might be a good platform to publish upcoming
versions, with a fallback on rubyforge.org.


2. Folder structure

As ruby's "require" is different from AS3's "import" the reversed domain notation
is unnecessary. Rubyist tend to have there namespaces cohere with their
folder-structure, at least to some degree.

:
  puremvc_multicore\        # <- gem root
    lib\
      puremvc\
        multicore\
          core\
          patterns\


3. Namespaces

As for now, the ruby standard port has NO namespaces, though this might not
be a problem at first sight, its bad style and limits usability when many moving
parts (gems) are involved. I've chosen PureMVC as main namespace in which
all PureMVC related project should be embedded.

So the MultiCore port lies in PureMVC::MultiCore, while the Standard port
will reside under PureMVC::Standard.
Other Project like Pipes will be under PureMVC::MultiCore::Utilities::Pipes.

This gives developers a cleaner way to access certain classes. For now i ignored
the subspaces ::Core and ::Pattern, but if there is demand for implementing those,
it's like five minutes of refactoring.

If you are concerned that the namespaces are to long, you could always "include"
the module (somewhat closer to AS3's "import"), thus getting rid off all the namespaces.
(e.g. import PureMVC::MultiCore, would make Facade directly available in Object)


4. Constants, Symbols and Strings

Ruby is very indulgent and sometimes this tends to result in bad code.
As for now i just enforce symbols on multiton keys. this is done because
ruby hashes allow nearly any kind of key, let's look at an example:

:
a_hash = Hash.new
a_hash[nil] = "not nil at all"
a_hash[false] = "might be true"
a_hash["key"] = "value"
a_hash[:key] = :another_value

All of the above statements are valid uses of a hash, but just because they're
valid uses doesn't mean they are always good ones. I could think of use-cases
when it might be desirable, to bind nil or false to a certain value, for example
in some kind of error handler, but as a name of a Facade or a Notification it's
inappropriate. Furthermore, People new to ruby often like to mix strings
with symbols, which leads to confusion when a_hash["key"] is not a_hash[:key].

Symbols are only instantiated once, so will only use memory once,
and their purpose is "to stand for something else", hence the name symbol.
However one might argue that assigning a string to a constant solves this problem,
which is true, but if the representation within the string is only used for
comparison, it's best practice to use a symbol.

So, i ask you if the implementation of Notification should enforce symbols too,
as you never need their actual 'name' just to compare that :a is not :b.

Remember, you can still use constants for a clean programing style, just their
assigned value needs to be a symbol.
(e.g. MyStartupNotification = :my_startup_notification)


5. Mediator implementation and meta-programing

Mediators hinder ruby's meta-programing strength. I modified the current
implementation, by changing the way notification_interests and _handling is
done, to enable the use of mixins (called modules in ruby). this allows to bind
certain methods to one or more notifications and vice versa.

The good news, its fully backward compatible with any project already written
(which to this point would only be the one i wrote, hehe) and still abides
to the Reference, because normally one would override #list_notification_interests()
and #handle_notification() anyway.

I introduced three methods:
#initialize_mediator(), #add_notification_handler(), #remove_notification_handler()
with the first one being called by #initialize() and the second one is called within
#initialite_mediator() or #initialize() to bind instance methods to Notifications.
The third one might even be used for heavy meta-programing.

The only way to break this would be to override the @notification_handlers variable
which holds the handlers and NOT to override list_notification_interests()
and #handle_notification() before the mediator gets registered with the facade.
This is very unlikely and i could not think of a mediator which does not have
any notification interests BUT overrides the @notification_handlers variable.

For more information see ./lib/puremvc/multicore/patterns/mediator/mediator.rb
Documentation still needs some tuning and example uses for most methods.
Pages: [1]