Wrapper Based an Unobtrusive DCI Ruby library

52 views
Skip to first unread message

Ritchie Paul Buitre

unread,
Oct 18, 2017, 12:45:58 AM10/18/17
to object-composition
WrapperBased is an alternative approach to ActiveSupport::Concern where instead of including mixins in models, any class including controllers can serve as context where objects and behaviors are combined.

What's different from other Ruby implementations?
  • Instead of Object#extend it uses wrappers.
  • Roles can be assigned to either Class or Module.
  • Is only required inside the context, no other source code are touched.
  • Uses an approachable API instead of technical idioms and DSL.
  • Real world production code and examples.

James O Coplien

unread,
Oct 18, 2017, 12:59:45 AM10/18/17
to object-co...@googlegroups.com

Den 18. okt. 2017 kl. 13.45 skrev Ritchie Paul Buitre <rit...@richorelse.com>:

  • Instead of Object#extend it uses wrappers.
Have you tried to implement the Dijkstra algorithm example? It usually fails with wrapper-based implementations.

Objects have state, behavior, and identity. Using wrappers to implement DCI violates identity. The same problem plagued ObjectTeams, which also gives the wrong result for the Dijkstra algorithm.

Ritchie Paul Buitre

unread,
Oct 18, 2017, 8:44:07 AM10/18/17
to object-composition
Jim,

Here's my attempt at Dijkstra algorithm. 

I've also adapted the test.

In my implementation I combined Module with Delegator using Ruby Refinements, forwarding messages to the delegated object. 
While in the context roles with the new behaviors can be accessed using the methods named after the role. The original value without the behavior can still accessed using instance variables named after the role.

With a role named 'user' the instance variable is '@user'.

One advantage I've found using wrappers over Object#extend is that I don't have to worry about name collisions. I could define different behaviors with the same method for the same object on different roles.

Matthew Browne

unread,
Oct 18, 2017, 9:50:27 AM10/18/17
to object-co...@googlegroups.com

--
You received this message because you are subscribed to the Google Groups "object-composition" group.
To unsubscribe from this group and stop receiving emails from it, send an email to object-composition+unsub...@googlegroups.com.
To post to this group, send email to object-composition@googlegroups.com.
Visit this group at https://groups.google.com/group/object-composition.
For more options, visit https://groups.google.com/d/optout.

Matthew Browne

unread,
Oct 18, 2017, 9:52:09 AM10/18/17
to object-co...@googlegroups.com
BTW have you seen this?
https://github.com/runefs/maroon

It's not native Ruby but it avoids both the issues that come with wrappers and with method injection.

On Wed, Oct 18, 2017 at 9:50 AM, Matthew Browne <mbro...@gmail.com> wrote:
On Wed, Oct 18, 2017 at 8:44 AM, Ritchie Paul Buitre <rit...@richorelse.com> wrote:
Jim,

Here's my attempt at Dijkstra algorithm. 

I've also adapted the test.

In my implementation I combined Module with Delegator using Ruby Refinements, forwarding messages to the delegated object. 
While in the context roles with the new behaviors can be accessed using the methods named after the role. The original value without the behavior can still accessed using instance variables named after the role.

With a role named 'user' the instance variable is '@user'.

One advantage I've found using wrappers over Object#extend is that I don't have to worry about name collisions. I could define different behaviors with the same method for the same object on different roles.

--
You received this message because you are subscribed to the Google Groups "object-composition" group.
To unsubscribe from this group and stop receiving emails from it, send an email to object-composition+unsubscribe@googlegroups.com.

Ritchie Paul Buitre

unread,
Oct 18, 2017, 10:16:45 AM10/18/17
to object-composition
Matthew,

Equality and identity comparison works fine so long as I pass the original value instead of the wrapping object.
I have seen Maroon, since it's not native Ruby I couldn't reason about it the same way I think in Ruby.
My goal with this library is not to be a true DCI implementation, but close enough to benefit from the DCI architecture 
without sacrificing writing native Ruby.

Rune Funch Søltoft

unread,
Oct 18, 2017, 11:30:15 AM10/18/17
to object-co...@googlegroups.com


> Den 18. okt. 2017 kl. 16.16 skrev Ritchie Paul Buitre <rit...@richorelse.com>:
>
> Equality and identity comparison works fine so long as I pass the original value instead of the wrapping object

Which is the same as saying that it doesn’t work. The Dijkstra Manhattan implementation that Jim challenged you with will illicit that issue

Ritchie Paul Buitre

unread,
Oct 18, 2017, 11:24:50 PM10/18/17
to object-composition
Rune,

It works well enough for all use cases including the Dijkstra algorithm.

But if you want I'll rewrite it to fit the roles given here: 
http://fulloo.info/Examples/RubyExamples/Dijkstra/

James O Coplien

unread,
Oct 20, 2017, 7:09:32 AM10/20/17
to object-co...@googlegroups.com

Den 19. okt. 2017 kl. 12.24 skrev Ritchie Paul Buitre <rit...@richorelse.com>:

It works well enough for all use cases including the Dijkstra algorithm.

No: if you manually have to decide whether to use the wrapped object or the Role, you destroy readability. You make it necessary to distinguish between Role identifiers and other identifiers.

The goal of DCI is readability. You appear to not maintain that.

It doesn’t work.

James O Coplien

unread,
Oct 20, 2017, 7:10:09 AM10/20/17
to object-co...@googlegroups.com
Den 19. okt. 2017 kl. 12.24 skrev Ritchie Paul Buitre <rit...@richorelse.com>:

But if you want I'll rewrite it to fit the roles given here: 

Well, yes, that was the challenge I gave you. Let’s start there.

Ritchie Paul Buitre

unread,
Oct 22, 2017, 12:47:50 AM10/22/17
to object-composition
I've already written a pure DCI implementation 4 months ago, I haven't had a chance to share here coz I it was only recently I was allowed to post.


It has one advantage over Object#extend, roles are not retained outside of the context therefore no need to unextend. Unfortunately it also suffers from similar issues like creating too many one-off meta classes. At this point of the language I don't see it being used in production. That's what led me to write WrapperBased even though it is not pure DCI but I am confident about it's adoption among the Ruby community and revive interest.

James O Coplien

unread,
Oct 22, 2017, 2:12:04 AM10/22/17
to object-co...@googlegroups.com

Den 22. okt. 2017 kl. 06.47 skrev Ritchie Paul Buitre <rit...@richorelse.com>:

That's what led me to write WrapperBased even though it is not pure DCI but I am confident about it's adoption among the Ruby community and revive interest.

I just met with many of them in Japan last week, and have talked in-depth with Matz about this issue. Don’t get your hopes up.

It has all the problems described in the FAQ. Please do not tarnish the name of DCI with software that has these limitations.

Matthew Browne

unread,
Oct 22, 2017, 7:53:43 AM10/22/17
to object-co...@googlegroups.com

Hi Ritchie,
Object.extends is probably still be the best option for production usage in native Ruby. However, there's one other possibility you could look into: you could try wrapping the role-playing object rather than the role. In that case the entity of the original object is preserved. This might work pretty well you can use Object.extends to inject the "RolePlayer" behavior (I haven't looked into it myself so I don't know how well it would work in practice).

I used this approach successfully in PHP; here's a simplified code example:
https://gist.github.com/mbrowne/37d4c8e7dbda031ba3b86fcfde04f5d8

Full library here:
https://github.com/mbrowne/dci-php

But unless you are having a lot of issues with naming conflicts due to the inability to unextend in Ruby, like I said you might be better off with Object.extends.

I have a few other comments which I'll share in a subsequent post (probably will have time for that later today).

Cheers,
Matt

--
You received this message because you are subscribed to the Google Groups "object-composition" group.
To unsubscribe from this group and stop receiving emails from it, send an email to object-composit...@googlegroups.com.
To post to this group, send email to object-co...@googlegroups.com.

Ritchie Paul Buitre

unread,
Oct 23, 2017, 10:36:56 AM10/23/17
to object-composition
Thanks Matt,

I like your suggestion, wrapping role player object is less messy than wrapping the wrapper. I already have an implementation in mind with a list of improvements inspired by this challenge. I'll be back with my solution.

Cheers,
Ritchie

Matthew Browne

unread,
Oct 23, 2017, 12:50:51 PM10/23/17
to object-co...@googlegroups.com
On Mon, Oct 23, 2017 at 10:36 AM, Ritchie Paul Buitre <rit...@richorelse.com> wrote:
Thanks Matt,

I like your suggestion, wrapping role player object is less messy than wrapping the wrapper. I already have an implementation in mind with a list of improvements inspired by this challenge. I'll be back with my solution.

Nice. I was going to say that your previous wrapper implementation could still be useful for practical purposes (assuming that most of your code doesn't rely on role players in hashmaps and similar situations that rely on object identity). But as Cope pointed out, it's not DCI - the distinction is important because if the role and role player ever seem like two separate objects, you're breaking the conceptual model of DCI. So I'm glad that you have taken up the challenge :)

Reply all
Reply to author
Forward
0 new messages