On Wednesday 10 December 2003 05:16 am, Peter wrote:
> > QUICK SIDE NOTE: might be nice to have something for all those dang ends.
> > How about end_____end, with as many underscore (2 or more) as needed:
> >
> >   Compiler.aspect do |meth|
> >     if meth.tag==:chroot
> >       meth.wrap do
> > 	if doer.cflow_ancestors.any? {|a| a.tag==:chroot}
> >           super
> >         else
> >           chroot_evironment do
> >             super
> >   end_____end
>
> Actually counting consecutive underscores is hard. On my screen they make
> up one long line.

Mine too! But I was joking :) Well, half way. It would be nice to have a good 
way, I was just throwing an psuedo example out there.

> > cflow logic is certainly a different way of thinking, but I don't really
> > see how cflow logic would become so complex that you'd want to go back to
> > explicts. It's essentially the same logic, actually, just applied
> > differently. In fact I think one will find that the ease of managing such
> > aspects are on an order of magnitude greater than explictly doing so. I
> > don;t see cflow being the the essential part of AOP, but it is another
> > useful part of it.
>
> Agreed, cflow is not *the* essential part of AOP. But I think I should
> make my example more concrete to show the complexity of cflow logic
> (although maybe it may be a bit contrived, but I hope you get the point).
>
>   class Compiler
>     def compile:no_chroot
>       task1
>       task2
>     end
>     def task1:chroot
>       # do some stuff
>       task2
>       task3
>       # some other stuff
>     end
>     def task2:chroot
>     end
>     def task3:no_chroot
>       # do some stuff
>       task4
>       # do some other stuff
>     end
>     def task4:whatever
>       task5
>     end
>     def task5:chroot
>     end
>   end
>
> I used indicators to indicate whether a method needs chroot, or doesn't
> want it, or it doesn't matter (if the task it performs is so small that
> it is overhead to change the environment and then put it back).
>
> What your wrapper then needs to do is create a chroot env for methods with
> a 'chroot' indicator if it wasn't there yet, and destroy the chroot env
> for a method with the 'no_chroot' indicator, if the env was there. So
> basically, in terms of cflow, you need to create the chroot env when you
> are in the cflow of a method m1 with the chroot indicator, and when there
> is no other method m2 in the cflow of m1, unless there's another method m3
> in the cflow of m2 that has the chroot indicator, etc. OK, I'm
> exagerating, but you need to be able to say that there exists a method
> m1:chroot in whose cflow we are and for which no method m2:no_chroot
> exists in its cflow and in whose cflow we are too. You can't even say that
> in AspectJ BTW. But if I do it myself explicitly, I'd just add a variable
> to the Compile class that keeps track of whether we are in a chroot
> environment or not, and have my wraps use that information and change
> environment if needed and set that variable accordingly. Much easier, and
> much harder to break by extensions.

Actually I disagree. Only harder b/c one is not used to it. Once "mastered" an 
AOP programmer could have the same job done in the same or less time, and 
could certainly make adjustment's much faster. While you'd have to do through 
every method to adjust. Also, anything is breakable by extension very easily.

     def Compiler.compile:no_chroops
       ...
     end

broken.

> The alternative is to change the subdivision of the tasks so that we can
> group chrooted tasked, but since chrooting is another concern, we don't
> want that governing the way we subdivide the tasks.
>
> > Well, sort-of, in 86785 I was showing how we might mixin singletons in a
> > class definable way, which is the essence of wrapping. Its not that they
> > are outer or inner per se; I wasn't thinking about that at the time, and
> > is something additional to take into consideration (currently they would
> > indeed be outer though). We can already define mixin singletons in an
> > object definable way using #extend. You may have read about that in a
> > couple of my recent posts on the matter (87408 is good starting point in
> > the thread).
>
> I agree that there are already features present in the Ruby language that
> allow us to do wrapping-like things. But if I understand well that's
> mostly based on subclassing (defining singletons comes down to defining
> singleton classes implicitly).

But it dosen't need to be. These aren't just "wrapping-like things". It *is* 
wrapping. And with only some basic modifications, i.e. meta-singletons and 
class defineable singletons, can be the very foundation of AOP for Ruby.

> As for my mention of friend classes and namespaces... Currently in Ruby
> there is class-based access control for methods and variables. For the
> former it can be changed (public/private/protected), for the latter it is
> fixed (until @var and @_var come along that give some control). But since
> aspects are cutting across classes, we'd naturally need something more
> than class-based access control. Actually a simple example of a
> cross-cutting concern is a bidirectional link between two objects. Keeping
> those links consistent is one concern, but if the objects involved are of
> different class, this concern involves multiple classes. An aspect could
> keep track of the links and make sure they are always consistent. In
> AspectJ you could sneak in some variables to keep the links in the
> respective classes, but they are private to the aspect and can't be
> accessed from those classes. That's logical because then any operations
> involving the link are performed by methods that the aspect offers and
> consistency is guaranteed. Of course the notion of friend classes is too
> limited in this case, but it allows to change access control. Namespaces
> do that too in a sense.

Hmm, I think I may understand what your refering to now. I don't think of it 
that way. For me all varaibles are of the same scope as they are now 
--aspects don't have some special cross-cutting scope. For that you'd use a 
class/module variable as we do now. Certainly one could invision such a 
cross-cutting scope, but such a scope is not neccessary; and whether it 
should be able to interact with the regular scope is again the difference 
between passive and active wraps...

> But to get back to my previous mail; I think the point I was trying to
> convey is as follows. When we look at outer wraps, we see that they are
> really just hooks. You can hook em, unhook em, and nobody will ever notice
> a thing except you yourself. That's clean separation of concerns, and
> that's what AspectJ does best. Examples are security, profiling, tracing,
> logging, GUI observers, pre/postcondition checking, keeping dirty bits
> when objects on screen are moved, error logging, invariant checking...
> They are best done by using hooks (or outer wraps).
>
> But then inner wraps. As you've pointed out, a primary method and its
> wraps are really one whole. If a wrap is removed, then the method will
> change functionality. Adding an inner wrap or removing one is much more
> noticeable. Actually they kind of behave like a method and the helper
> methods (or what I called subroutines by lack of a better term) it calls,
> or like a method calling the same method in a super class. They add layers
> of extra functionality. To me it feels like that is what class hierarchies
> were invented for, or the hierarchy in a method call graph.
>
> Maybe what I need is a simple example of inner wraps (that I could in no
> way classify as an outer wrap, because I think our notions of that are
> slightly different) and that clearly demonstrates its importance in
> separating concerns in the sense that it does it better than current Ruby.
> Then we could discuss something more concrete. Or maybe I should wait
> until your RCR has been updated. I know I am overly sceptical now, but
> maybe that's because some things are getting clearer, and other things
> getting fuzzier.

Active wraps need not alter functionality. Certainly they can, and even here 
they have their advantages: easily removed and so serve as testing code 
variations, they can be reused like mixins in appropriate contexts, etc. But 
more significantly they can also be used to interact in a way consistant with 
a class, injecting and extracting information without "augmenting" behavior. 
I think this is the option you are not witnessing.

Your response has got me digging up my old GUI code. Its been a while, and I'd 
be hard pressed to tell you how everything works off the top of my head, but 
I thought you might like to look at some of its heart. What better example 
than a real one. If only I had a real AOP way to do, rather then the mind 
boggling terseness of what follows. Have fun ;)

module Controller

  module Listener

    def ___listen___(meth=nil, *args, &block)
      meth = meth.to_s
      if @___events___
        if @___events___.has_key?(meth)
          @___events___[meth].each { |p|
            p.call(*args, &block)
          }
        end
      end
      if @___procs___ and @___state___
        if self != @___state___
          @___state___ = self.clone
          @___procs___.each { |p|
            p.call(self)
          }
        end
      end
    end

    def ___automate___
      $___trigger___ = [] unless $___trigger___
      self.methods.each { |meth|
        if not Object.instance_methods(true).include?(meth)
          if not ['when', 'event', 'bind', 'change', '___listen___', 
'___automate___', '___action___'].include?(meth)
            ___action___(meth)
          end
        end
      }
    end

    def ___action___(meth)
      ali = "___#{meth.hash.to_s.gsub('-', '_')}___"
      if not self.respond_to? ali
        self.instance_eval <<-EOS
          class << self
            alias_method :#{ali}, :#{meth}
            def #{meth}(*args, &block)
              return if $___trigger___.include?([self, :#{meth}])
              $___trigger___ << [self, :#{meth}]
              r = send(:#{ali}, *args, &block)
              ___listen___(:#{meth}, *args, &block)
              $___trigger___.pop
              return r
            end
          end
        EOS
      end
    end

  end

  
  module Methods
  
    include Listener
  
    def when(meth, prc=nil, &block)
      meth = meth.to_s
      ___automate___ unless @___events___
      @___events___ = {} unless @___events___
      prc = block unless prc
      @___events___[meth] = [] unless @___events___[meth]
      @___events___[meth] << prc
    end

  end

  
  module Variables

     include Listener
 
    def bind(prc=nil, &block)
      ___automate___ unless @___procs___ and @___state___
      @___state___ = self.clone unless @___state___
      @___procs___ = [] unless @___procs___
      prc = block unless prc
      @___procs___ << prc
    end    

  end

end

class Object
  include Controller::Methods
end

class Numeric
  include Controller::Variables
end

class String
  include Controller::Variables
end

class Array
  include Controller::Variables
end

class Hash
  include Controller::Variables
end