[ANN] [RCR] Cut-based AOP

This is to "officially" announce an RCR that I posted to RCR archive
two days ago. I realize the RCR itself is a bit dense and techincial,
so (with thanks to ES) I thought it might be a good idea to provide a
little summary and some examples of what it's all about and why it's a
such a good approach to AOP for Ruby.

You can read the RCR #321 <a
href="http://www.rcrchive.net/rcr/show/321">here</a>. To touch on it's
history: The RCR was developed by Peter Vanbroekhoven and myself over
the course of the last two years[1]. In that time we covered a lot of
territory with regards to AOP, and the RCR itself has undergone a great
deal of scrunity, revision and refinement.

To summaraize the RCR's senitment: The transparent subclass, witch we
have dubbed the Cut, is the best basis for adding AOP to Ruby because
it is fully harmonious with OOP. This is unlike other solutions which
are add-on abstractions tossed on top of the underlying OOP framework.
With cuts one has a building block to construct all sorts of AOP
systems, from simple method hooks to full blown context-oriented
programs. Put simply, the cut is a foundation for AOP, just as the
class is a foundation for OOP.

To demonstrate what you can do with cuts, I present a few simple
examples. I'll start with a very basci one: how to take a prexiting
class and wrap a user interface around it. Nothing fancy. I'll just use
a very small class and the console.

  class Race

    attr_accessor :distance, :speed, :track

    def initialize( distance, speed )
      @distance = distance
      @speed = speed
      @track = 0
    end

    def reset
      @track = 0
    end

    def run
      while track < distance do
        self.track += 1;
        sleep( 1.0/speed )
      end
      track
    end

  end

Simple enough. We can run a race:

  Race.new( 10, 2 ).run

Though it goes about it's racey busniess just fine, we have no
indication of any progress of the race. But that's okay actually; it
keeps the Race class clean and focused on its core functionality.
Instead we can use a cut to watch the race.

  cut Race::ConsoleViewer < Race
    def track=(x)
      r = super(x)
      print "\e[0E"
      print "+" * r
      $stdout.flush
      r
    end
  end

  Race.new( 10, 2 ).run

This outputs an exra '+' at a time, finishing with 10:

···

++++++++++

So we see the progress of the race "live" without having to change the
Race class itself in any way. And we can just as easily throw any other
type of interface (gtk, qt, fox, wx, html, and so) around the Race
class in the same manner.

Now lets try a slightly more advance example; one made by Peter quite
some time ago, which demostrates the basis of how cuts could be used
for creating logging aspects. Lets say we have an ftp server class:

  class FTPServer

    def login(username, passwd)
      # code for logging in, return ID for the connection
      return connID
    end

    def logout(connID)
      # code for logging out
    end

    def upload(connID, filename, data)
      # code for writing a file
    end

    def download(connID, finename)
      # code for reading a file, returns the data read
    end

  end

We can create an aspect for it:

  module FTPLogging

    def login(username, *args)
      connID = super
      log.print("#{connID}: #{username} logging in on #{Time.new}\n")
      connID
    end

    def logout(connID)
      result = super # In case logout returns some result
      log.print("#{connID}: logging out on #{Time.new}\n")
      result
    end

    def upload(connID, filename, *args)
      result = super # In case logout returns some result
      log.print("#{connID}: uploading #{filename} on #{Time.new}\n")
      result
    end

    def download(connID, filename)
      data = super
      log.print("#{connID}: downloading #{filename} on #{Time.new}\n")
      data
    end

  end

  if logging_enabled
    cut FTPLoggingCut < FTPServer
      include FTPLogging
    end
  end

Notice the use of a separate module to house the aspect. That way it is
reusable. In this case the aspect's design was made to match the
duck-type of the target class, FTPServer, so we don't need to redirect
any advice in the cut. Though if we wanted to use the aspect on a class
not having the same interface we could easily redirect the advice on a
case by case basis. But even better, using an annotations system we
could create a routine to cut any class so annotated.

Finally, here's a example of how one might use cuts for dynamic
context-oriented programming.

  class Account
    attr_reader :id, :current
    def initialize( id, current )
      @id
      @current = current
    end
    def debit( amount )
      @current -= amount
    end
    def credit( amount )
      @current -= amount
    end
  end

  module Activiation
    def active? ; @active ; end
    def activate ; @active = true ; end
    def deactivate ; @active = false ; end
  end

  cut AccountLogging < Account
    extend Activation
    def debit( amount )
      r = super
      if AccountLogging.active?
        log "Debited account #{id} #{amount} with result #{r}"
      end
      r
    end
  end

  cut AccountDatabase < Account
    extend Activation
    def debit( amount )
      super
      if AccountDatabase.active?
        DB.transaction {
          record_transaction( -amount )
          record_total
        }
      end
    end
    def credit( amount )
      super
      if AccountDatabase.active?
        DB.transaction {
          record_transaction( amount )
          record_total
        }
      end
    end
    def record_transaction( amount )
      type = amount > 0 ? 'credit' : 'debit'
      DB.exec "INSERT INTO transactions (account,#{type}) VALUES
(#{id},#{amount.abs});"
    end
    def record_total
      DB.exec "UPDATE accounts SET total=#{current} WHERE id=#{id};"
    end
  end

Notice how we can activate and deactivate the aspects on the fly with
the activation module. If we wished we could go even further and create
a completely general purpose (and relatively efficient) context
switching mechinisms.

Oh, one more quick example taken from the RCR itself for those
wondering where the pointcut is, and how one might cross-cut a whole
slew of classes:

  ObjectSpace.each_object(Class) { |c|
    if c.instance_methods(false).include?(:to_s)
      Cut.new(c) do
        def :to_s
          super.upcase + "!"
        end
      end
    end
  end
  "a lot of shouting for joy".to_s #=> "A LOT OF SHOUTING FOR JOY!"

Okay, I think that should give a pretty good taste of what "AOP can do
for you" and how Cuts provide a solid, yet easy to use, foundation for
employing AOP on a wide scale. To understand more about why Cuts are
right for the task, covering all the criteria of AOP including
*introduction* and *inspection*, please give the RCR a read.

We hope you find our proposal rewarding and will show your supprt on
RCRchive.

Thanks,
T.

[1] I want to also thank Jamis Buck and everyone else who has spent
time exploring AOP for Ruby with us. Thank You!

What is a cut? Is it a subclass that hides its parent's class? Are the
following two snippets the same?

  cut Bar < Foo; baz() end

  class Bar < Foo; baz() end
  Bar = Foo # (except for this warning)

If not, what is the difference?

Cheers,
Dave

Selon Trans <transfire@gmail.com>:

This is to "officially" announce an RCR that I posted to RCR archive
two days ago. I realize the RCR itself is a bit dense and techincial,
so (with thanks to ES) I thought it might be a good idea to provide a
little summary and some examples of what it's all about and why it's a
such a good approach to AOP for Ruby.

I've just voted "strongly advocate" for it! After reading about AspectJ, I had
found that although aspects where interesting, they looked like they broke OOP
rather than enhanced it. This proposal, on the other hand, is strongly based on
OOP and really enhances it, and also makes AOP much easier to understand than
the traditional approach and its swarm of buzzwords. And it is nicely
unintrusive, ensuring that if one doesn't want to bother about AOP one doesn't
have to.

I just have a few questions, which I hope aren't too dumb ( :wink: ). I couldn't
find anything in the RCR about those questions (although I may have missed it):
- will one be able to subclass (are rather "subcut", one would say) cuts?
- will cuts be as open as classes? (since they *are* classes, I'd expect so, but
I just want confirmation)
- how do cuts work with each other when you add more than one cut to the same
class?
- will the subclass of a class also inherit its superclass's cuts? This is a
tricky point I think, as one might expect that the functionality provided by
cuts should be available to subclasses, but the implementation (which is
basically like transparent subclasses) may forbid it. If they don't inherit
cuts, this could lead to surprises, with a not overriden method behaving
suddenly differently in the superclass and in the subclass. And what should one
expect of the behaviour of "super"?

All in all, I find it one of the best RCR I've ever read! Keep up the good work!

···

--
Christophe Grandsire.

http://rainbow.conlang.free.fr

It takes a straight mind to create a twisted conlang.

Trans wrote:

This is to "officially" announce an RCR that I posted to RCR archive
two days ago. I realize the RCR itself is a bit dense and techincial,
so (with thanks to ES) I thought it might be a good idea to provide a
little summary and some examples of what it's all about and why it's a
such a good approach to AOP for Ruby.

You can read the RCR #321 <a
href="http://www.rcrchive.net/rcr/show/321&quot;&gt;here&lt;/a&gt;\.

+1.

I have a few questions I'll need to frame properly, but wanted to say:
  Nice work + nice description!

Hi,

···

In message "Re: [ANN] [RCR] Cut-based AOP" on Wed, 19 Oct 2005 08:56:59 +0900, "Trans" <transfire@gmail.com> writes:

This is to "officially" announce an RCR that I posted to RCR archive
two days ago. I realize the RCR itself is a bit dense and techincial,
so (with thanks to ES) I thought it might be a good idea to provide a
little summary and some examples of what it's all about and why it's a
such a good approach to AOP for Ruby.

I understand the basic idea. How about introducing a new method
e.g. "preclude" that works to intercept the methods in the target
class/module, instead of cut. It would be a counterpart of "include"
for mix-in.

              matz.

Hi!

I would like to comment on this proposal. I can agree right from the beginning that the points here will be presented from a not-so-rubiesc perspective.

the proposal looks like an method-interception framework, rather than a full blown aop solution.

The proposal is mixin together theoretical aspects of aop with Ruby specific implementation problems. While this cannot be considered a real problem, what I have noticed is that one of the main concepts of the AOP pointcuts are completely ignored.

The main idea behind AOP is to be able to define crosscutting concerns. This looks pieces of functionality that can be used disregarding the OO nature of the project.

While at the first glance the method interception mechanism is one of most important aspects of AOP, there are a few distinction that must be considered: call vs execution, before, around, after pieces of advice.

While, maybe leaving alone all these aspects, may be a Ruby implementation constraint, they are valid AOP principles that will miss from the implementation.

cheers,

./alex

···

--
.w( the_mindstorm )p.

#: Trans changed the world a bit at a time by saying on 10/19/2005 1:56 AM :#

This is to "officially" announce an RCR that I posted to RCR archive
two days ago. I realize the RCR itself is a bit dense and techincial,
so (with thanks to ES) I thought it might be a good idea to provide a
little summary and some examples of what it's all about and why it's a
such a good approach to AOP for Ruby.

You can read the RCR #321 <a
href="http://www.rcrchive.net/rcr/show/321&quot;&gt;here&lt;/a&gt;\. To touch on it's
history: The RCR was developed by Peter Vanbroekhoven and myself over
the course of the last two years[1]. In that time we covered a lot of
territory with regards to AOP, and the RCR itself has undergone a great
deal of scrunity, revision and refinement.

To summaraize the RCR's senitment: The transparent subclass, witch we
have dubbed the Cut, is the best basis for adding AOP to Ruby because
it is fully harmonious with OOP. This is unlike other solutions which
are add-on abstractions tossed on top of the underlying OOP framework.
With cuts one has a building block to construct all sorts of AOP
systems, from simple method hooks to full blown context-oriented
programs. Put simply, the cut is a foundation for AOP, just as the
class is a foundation for OOP.

To demonstrate what you can do with cuts, I present a few simple
examples. I'll start with a very basci one: how to take a prexiting
class and wrap a user interface around it. Nothing fancy. I'll just use
a very small class and the console.

  class Race

    attr_accessor :distance, :speed, :track

    def initialize( distance, speed )
      @distance = distance
      @speed = speed
      @track = 0
    end

    def reset
      @track = 0
    end

    def run
      while track < distance do
        self.track += 1;
        sleep( 1.0/speed )
      end
      track
    end

  end

Simple enough. We can run a race:

  Race.new( 10, 2 ).run

Though it goes about it's racey busniess just fine, we have no
indication of any progress of the race. But that's okay actually; it
keeps the Race class clean and focused on its core functionality.
Instead we can use a cut to watch the race.

  cut Race::ConsoleViewer < Race
    def track=(x)
      r = super(x)
      print "\e[0E"
      print "+" * r
      $stdout.flush
      r
    end
  end

  Race.new( 10, 2 ).run

This outputs an exra '+' at a time, finishing with 10:

  ++++++++++

So we see the progress of the race "live" without having to change the
Race class itself in any way. And we can just as easily throw any other
type of interface (gtk, qt, fox, wx, html, and so) around the Race
class in the same manner.

Now lets try a slightly more advance example; one made by Peter quite
some time ago, which demostrates the basis of how cuts could be used
for creating logging aspects. Lets say we have an ftp server class:

  class FTPServer

    def login(username, passwd)
      # code for logging in, return ID for the connection
      return connID
    end

    def logout(connID)
      # code for logging out
    end

    def upload(connID, filename, data)
      # code for writing a file
    end

    def download(connID, finename)
      # code for reading a file, returns the data read
    end

  end

We can create an aspect for it:

  module FTPLogging

    def login(username, *args)
      connID = super
      log.print("#{connID}: #{username} logging in on #{Time.new}\n")
      connID
    end

    def logout(connID)
      result = super # In case logout returns some result
      log.print("#{connID}: logging out on #{Time.new}\n")
      result
    end

    def upload(connID, filename, *args)
      result = super # In case logout returns some result
      log.print("#{connID}: uploading #{filename} on #{Time.new}\n")
      result
    end

    def download(connID, filename)
      data = super
      log.print("#{connID}: downloading #{filename} on #{Time.new}\n")
      data
    end

  end

  if logging_enabled
    cut FTPLoggingCut < FTPServer
      include FTPLogging
    end
  end

Notice the use of a separate module to house the aspect. That way it is
reusable. In this case the aspect's design was made to match the
duck-type of the target class, FTPServer, so we don't need to redirect
any advice in the cut. Though if we wanted to use the aspect on a class
not having the same interface we could easily redirect the advice on a
case by case basis. But even better, using an annotations system we
could create a routine to cut any class so annotated.

Finally, here's a example of how one might use cuts for dynamic
context-oriented programming.

  class Account
    attr_reader :id, :current
    def initialize( id, current )
      @id
      @current = current
    end
    def debit( amount )
      @current -= amount
    end
    def credit( amount )
      @current -= amount
    end
  end

  module Activiation
    def active? ; @active ; end
    def activate ; @active = true ; end
    def deactivate ; @active = false ; end
  end

  cut AccountLogging < Account
    extend Activation
    def debit( amount )
      r = super
      if AccountLogging.active?
        log "Debited account #{id} #{amount} with result #{r}"
      end
      r
    end
  end

  cut AccountDatabase < Account
    extend Activation
    def debit( amount )
      super
      if AccountDatabase.active?
        DB.transaction {
          record_transaction( -amount )
          record_total
        }
      end
    end
    def credit( amount )
      super
      if AccountDatabase.active?
        DB.transaction {
          record_transaction( amount )
          record_total
        }
      end
    end
    def record_transaction( amount )
      type = amount > 0 ? 'credit' : 'debit'
      DB.exec "INSERT INTO transactions (account,#{type}) VALUES
(#{id},#{amount.abs});"
    end
    def record_total
      DB.exec "UPDATE accounts SET total=#{current} WHERE id=#{id};"
    end
  end

Notice how we can activate and deactivate the aspects on the fly with
the activation module. If we wished we could go even further and create
a completely general purpose (and relatively efficient) context
switching mechinisms.

Oh, one more quick example taken from the RCR itself for those
wondering where the pointcut is, and how one might cross-cut a whole
slew of classes:

  ObjectSpace.each_object(Class) { |c|
    if c.instance_methods(false).include?(:to_s)
      Cut.new(c) do
        def :to_s
          super.upcase + "!"
        end
      end
    end
  end
  "a lot of shouting for joy".to_s #=> "A LOT OF SHOUTING FOR JOY!"

Okay, I think that should give a pretty good taste of what "AOP can do
for you" and how Cuts provide a solid, yet easy to use, foundation for
employing AOP on a wide scale. To understand more about why Cuts are
right for the task, covering all the criteria of AOP including
*introduction* and *inspection*, please give the RCR a read.

We hope you find our proposal rewarding and will show your supprt on
RCRchive.

Thanks,
T.

[1] I want to also thank Jamis Buck and everyone else who has spent
time exploring AOP for Ruby with us. Thank You!

+1

I only have one concern: I think the syntax is kinda weird. It resembles the class definition syntax, but I can't see why it should. When you write "class A < B" you're saying "class A inherits from class B" or "class A is affected by class B". If we apply the same to "cut A < B", it would be "cut A is affected by class B", which isn't the case. Why not use "cut A > B", "cut A affects class C". The singleton syntax could be like this "cut >> obj".

Just my .16 Yen
Daniel

Does this also provide a quite general form of dependency injection,
very different in nature and weight compared to something like Needle?

Trans wrote:

···

This is to "officially" announce an RCR that I posted to RCR archive
two days ago. I realize the RCR itself is a bit dense and techincial,
so (with thanks to ES) I thought it might be a good idea to provide a
little summary and some examples of what it's all about and why it's a
such a good approach to AOP for Ruby.

You can read the RCR #321 <a
href="http://www.rcrchive.net/rcr/show/321&quot;&gt;here&lt;/a&gt;\.

The concept seems clear to me. But I have an aditional question. Will
cuts be allowed on root classes that are themselves extruded from a
'C' extension? E.g. I don't really know if class IO is mostly C
extension stuff, but could a cut be applied to these class types?
Kernel? Object?

cut AspectIO < IO
   def puts(*args)
      # do something with the args
      super
   end
end

If I am understanding the syntax correctly. I don't know how the
internal hooking mechanism works here. But even if IO#puts is a
low-level C funtion, then super could just map to it, instead of the
normal ruby implementation of the method.

If so, then this could be a very powerful mechanism in Unit Testing:
(Again, forgive me if the syntax is wrong, here.)

class IOTest < Test::Unit::TestCase
   @output = ''
   cut AspectIO < IO
      def puts(*args)
         @output = args.to_s
         super
      end
   end

   def setup
      @oldio = $stdio
      $stdio = IO.new
   end

   def test_hello
      puts "Hello world" # having the advantage of making @output
avail to assertions
                                    # and spewing contents in the console.
      assert_equals "Hello world", @output
   end

   def teardown
      $stdio = @oldio
   end
end

Is this acceptable in the RCR design? Was it already implied?

Thanks
Ed

···

On 10/18/05, Trans <transfire@gmail.com> wrote:

This is to "officially" announce an RCR that I posted to RCR archive
two days ago. I realize the RCR itself is a bit dense and techincial,
so (with thanks to ES) I thought it might be a good idea to provide a
little summary and some examples of what it's all about and why it's a
such a good approach to AOP for Ruby.

What is a cut? Is it a subclass that hides its parent's class? Are the
following two snippets the same?

cut Bar < Foo; baz() end

class Bar < Foo; baz() end
Bar = Foo # (except for this warning)

Did you mean the following? (Otherwise the class definition of Bar is useless.)

    Foo = Bar

If not, what is the difference?

There are a few subtle differences:
* In the second snippet, Foo and Bar are the same. This means that code
   that changes Foo changes Bar as well. In the first snippet, Bar is
   invisible to someone using Foo. Changing Foo only changes Foo and leaves
   Bar alone.
* In the second snippet, existing instances of class Foo are not affected,
   while they are in the first case.

So the basic idea is the same, but a cut is designed to keep existing code from breaking. It's supposed to be non-intrusive. That's what a cut does and your second snippet does not.

Actually, in the beginning we considered something like your second code
snippet, but we decided it was not sufficient for our purposes as we wanted to allow sneaking in a cut into an existing class hierarchy without breaking the code that uses and changes these classes.

Peter

···

On Wed, 19 Oct 2005 dave.burt@gmail.com wrote:

I've just voted "strongly advocate" for it! After reading about AspectJ, I had
found that although aspects where interesting, they looked like they broke OOP
rather than enhanced it. This proposal, on the other hand, is strongly based on
OOP and really enhances it, and also makes AOP much easier to understand than
the traditional approach and its swarm of buzzwords. And it is nicely
unintrusive, ensuring that if one doesn't want to bother about AOP one doesn't
have to.

Great! One down, many more to go :wink:

I just have a few questions, which I hope aren't too dumb ( :wink: ). I couldn't
find anything in the RCR about those questions (although I may have missed it):
- will one be able to subclass (are rather "subcut", one would say) cuts?

Cuts are not supposed to be subclassed (they can't be instantiated, so what is the point?) We're not sure if we want to allow adding cuts to cuts. I think the current implementation allows that, but I'm not sure, it's been a while.

If you want to reuse the functionality of a cut somewhere else, you should put it in a module and reuse it like that. In a way, allowing a cut to apply to multiple classes or modules is akin to multiple-inheritance, and we want to avoid that.

- will cuts be as open as classes? (since they *are* classes, I'd expect so, but
I just want confirmation)

Yes, they will be open wide.

- how do cuts work with each other when you add more than one cut to the same
class?

They stack on top of each other, the last one added being on top:

   class A
     def m ; '1' ; end
   end

   cut B < A
     def m ; '[' + super + ']' ; end
   end

   cut C < A
     def m ; '{' + super + '}' ; end
   end

   p A.new.m # => "{[1]}"

- will the subclass of a class also inherit its superclass's cuts? This is a
tricky point I think, as one might expect that the functionality provided by
cuts should be available to subclasses, but the implementation (which is
basically like transparent subclasses) may forbid it. If they don't inherit
cuts, this could lead to surprises, with a not overriden method behaving
suddenly differently in the superclass and in the subclass. And what should one
expect of the behaviour of "super"?

Yes, subclasses inherit the cuts. This means that in the class hierarchy, the cuts are in between the class and the superclass. To show this, some example code:

   class A
     def m ; '1' ; end
   end

   class B < A
     def m ; '{' + super + '}' ; end
   end

   cut C < A
     def m ; '[' + super + ']' ; end
   end

   p A.new.m # => "[1]"
   p B.new.m # => "{[1]}"

So the methods of a class are seen the same from the outside and from the subclasses.

Note: if you use the patch, the keyword 'cut' is named '__cut__'. This is because the Tk library uses 'cut' as a method name and it fails the tests otherwise.

All in all, I find it one of the best RCR I've ever read! Keep up the good work!

Thanks!

Peter

···

On Wed, 19 Oct 2005, Christophe Grandsire wrote:

I like the idea but may I point out that "preclude" has a meaning very
close to "exclude". How about "intercept"?

Regards,

Sean

···

On 10/20/05, Yukihiro Matsumoto <matz@ruby-lang.org> wrote:

How about introducing a new method
e.g. "preclude" that works to intercept the methods in the target
class/module, instead of cut. It would be a counterpart of "include"
for mix-in.

Hi Matz,

This is indeed an option we have considered, and not completely dismissed either. Trans just asked me yesterday whether I could add this on top of cuts. I think part of the reason that we came up with cuts was because it is more like subclassing. One argument that I remember is that a class creates a wrapper around its superclass, and can actually adapt to its superclass. However, a mix-in cannot adapt to the class it is included in, because it can be included in many classes and it can't cater to all their
needs. Likewise a superclass cannot adapt to its subclasses. Because the idea of a wrapper adapting itself to the class it wraps is appealing, but can only be done if it can wrap only a single class. Hence the idea of a cut, and not just module 'preclusion'. I don't know what your thoughts are on this.

Does this mean you like the general idea, of just slipping a module or class into the class hierarchy and put the wrapper methods there? This could possibly be used with the :before and :after syntax you've proposed, with a little extension that allows naming the layer it should go to:

   class Foo
     def foo
       p :foo
     end
   end

   class Bar < Foo
     def foo:wrap:Layer
       p [:foot, :before]
       super
       p [:foot, :after]
     end
   end

All these methods could end up in an (autovivified?) 'precluded' module, overwrite only methods in that module, and this module can be retrieved for more fun.

This is just an idea that was floating around in my mind. The syntax (in case you don't like it) is not the point though, the idea is.

Peter

···

On Thu, 20 Oct 2005, Yukihiro Matsumoto wrote:

Hi,

In message "Re: [ANN] [RCR] Cut-based AOP" > on Wed, 19 Oct 2005 08:56:59 +0900, "Trans" <transfire@gmail.com> writes:

>This is to "officially" announce an RCR that I posted to RCR archive
>two days ago. I realize the RCR itself is a bit dense and techincial,
>so (with thanks to ES) I thought it might be a good idea to provide a
>little summary and some examples of what it's all about and why it's a
>such a good approach to AOP for Ruby.

I understand the basic idea. How about introducing a new method
e.g. "preclude" that works to intercept the methods in the target
class/module, instead of cut. It would be a counterpart of "include"
for mix-in.

Selon Alexandru Popescu <the.mindstorm.mailinglist@gmail.com>:

While, maybe leaving alone all these aspects, may be a Ruby implementation
constraint, they are
valid AOP principles that will miss from the implementation.

From the rest of the discussion here about this RCR, I have understood (and
Trans or others can confirm it I think) that cuts are supposed to be a *basis*,
on which fully-fledged AOP can be built, in a way that fits with Ruby, since the
base itself fits with Ruby. It's a bottom-up approach, instead of the top-down
approach that for instance AspectJ took, and which makes it look very alien to
the rest of Java (in my opinion). I personally believe it's the best approach,
which is why I voted for this RCR: first built the tools you need, then use
those tools to create a framework. This is in my opinion the only way to get
something eventually stable and useful, without looking too much like an add-on
but really like part of the language.

In other words, the RCR isn't here to propose full AOP in one go. It is there to
provide what the authors think (and so far I agree) is the only construct
missing to built fully-fledged AOP, in a way that doesn't break with the rest
of Ruby, and is easy enough to implement. I guess (but never having used it I
might be wrong) that full AOP could be built then as a standard library, built
on cuts and ObjectSpace, and/or Ruby's already existing reflection capacities.

I am always in favour op bottom-up approaches. They may sometimes seem slower,
but on the long term they result in better code, especially when each tool and
addition is built as independently as possible. In a top-down approach, people
seem often far too tempted to look at their project as a single whole, and thus
to build it in such a way, resulting in code which after some time gets
unmanageable.

I am really in favour of cuts (or maybe some other way to implement the same
concept, using modules like matz suggested), because they provide a strong,
stable foundation, on which not only AOP can be built, but who knows what more?
Cuts extend the notion of modularity in a way different from simple modules, and
this could very well lead to plenty of other things besides AOP which is the
main issue they were built for. In other words, don't get too blinded by the
issue of fully-fledged AOP when you review this RCR. See it as foundation for
it, and maybe quite a few other things.

···

--
Christophe Grandsire.

http://rainbow.conlang.free.fr

It takes a straight mind to create a twisted conlang.

+1, if it is what I think you mean. Is this what you mean?

class C
  def foo
    "bar"
  end
end

c = C.new
c.foo # => "bar"

module Braces
  def foo
    "{"+super+"}"
  end
end

class C
  preclude Braces
end

c.foo # => "{bar}"

module Brackets
  def foo
    "["+super+"]"
  end
end

class C
  preclude Brackets
end

c.foo # => "[{bar}]"

module Braces
  remove_method :foo
end

c.foo # => "[bar]"

I really like this. I don't see anything you couldn't do with
this that you can with the cut-based-AOP. And it seems so much
simpler.

···

--- Yukihiro Matsumoto <matz@ruby-lang.org> wrote:

Hi,

In message "Re: [ANN] [RCR] Cut-based AOP" > on Wed, 19 Oct 2005 08:56:59 +0900, "Trans" > <transfire@gmail.com> writes:

>This is to "officially" announce an RCR that I posted to RCR
archive
>two days ago. I realize the RCR itself is a bit dense and
techincial,
>so (with thanks to ES) I thought it might be a good idea to
provide a
>little summary and some examples of what it's all about and
why it's a
>such a good approach to AOP for Ruby.

I understand the basic idea. How about introducing a new
method
e.g. "preclude" that works to intercept the methods in the
target
class/module, instead of cut. It would be a counterpart of
"include"
for mix-in.

              matz.

__________________________________
Yahoo! Mail - PC Magazine Editors' Choice 2005

Hi Matz,

Yukihiro Matsumoto wrote:

I understand the basic idea. How about introducing a new method
e.g. "preclude" that works to intercept the methods in the target
class/module, instead of cut. It would be a counterpart of "include"
for mix-in.

Yes, this is in the RCR:

Additionally, Cuts exist in proxy form to allow modules to be
"premixed". This is analogous to proxy classes which allow modules to
mixin to the class hierarchy. So too does a proxy-cut include a module,
albeit preclusive rather the inclusive in its effect. We offer the
module command #preclude to serve as designator of this purpose.

  module A
    def m ; "<#{super}>" ; end
  end
  Class T
    preclude A
    def m ; "okay" ; end
  end
  T.new.m #=> "<okay>"

I am amazed how quickly you've latched onto the most probable usage. I
have known for sometime that "preclude" would be the most utilized part
of this. But I have avoided stressing it becuase it is important that
it too be supported by good foundation, which is the Cut. Just as there
is a *proxy-class* to *mixin* modules, there'd be a *proxy-cut* to
*premix* them. Although precluding modules will certainly be utilized
most, I do not see any benefit in hiding the underlying class which
facilitates them from the programmer.

OTOH, perhaps you have a different idea for the implementation of
precluded modules; one that avoids any sort of cut/proxy-cut class
altogether. If so, I must tell you, I am cautious of such an approach
because it implies "adding-on" rather than "integrating". IMHO,
integrating this "atomic-aspect" formally into the OO class heirarchy
would be of greater benefit to Ruby, and potentially influential on OOP
in general. From a comparitive perspective, it is the difference
between Ruby simply having an extra feature and Ruby having a new and
original object-oriented feature.

T.

Hi--

Alexandru Popescu wrote:

Hi!

I would like to comment on this proposal. I can agree right from the beginning
that the points here will be presented from a not-so-rubiesc perspective.
the proposal looks like an method-interception framework, rather than a
full blown aop solution.

Yes, in a way you are correct. Ruby provides enough reflective
functionality that it is not neccessary. In other words there is no
need for what you call "full-blown" aop b/c the system already supports
all that is neccessary to achieve full blown aop. This is not to say
there are no areas in which it can be improved, indeed there are. Cuts
is one of those. Peter and I already have developed much of the rest
too: ways to strengthen the hooks and callbacks as well as better EAOP
(event-based AOP) than provided by set_trace_func. But these are for
another day, and more importantly, they do not cover the the majority
of AOP usecase, which Cuts squarely target.

The proposal is mixin together theoretical aspects of aop with Ruby specific
implementation problems.

I would disagree completely. The proposal narrows down the core of AOP
(namely the 2nd form of AOP implemention) to a formal OOP construct.
The Cut can be implemented in any OOP system, from Simula 67 to
Smalltalk. In fact this has been all along and still is Very Important
to me. I did not want a construct suitable only to Ruby, but something
generally applicable.

While this cannot be considered a real problem, what I have noticed is that one of > the main concepts of the AOP pointcuts are completely ignored.

Pointcuts are mentioned, and why there is no need to have a special
"pointcut thing". Pointcut is a concept of selecting joinpoints
--another abstract term, meaning the place in code to intercept. Well,
we have exactly that. You can specify on small scale of one class and
it's methods, or large scale using ObjectSpace. It is not neccessary to
make a pointcut thing when we already have those. But if you want you
could write your own extermely easily, probably in one line of code.

The main idea behind AOP is to be able to define crosscutting concerns. This looks > pieces of functionality that can be used disregarding the OO nature of the > project.

Sorry, I do not full understanding. We do address crosscutting, and OO
nature.

While at the first glance the method interception mechanism is one of most
important aspects of AOP, there are a few distinction that must be considered:
call vs execution, before, around, after pieces of advice.

before and after advice are subset of around advice. We decided it was
just easer to have the one than worry about three since before and
after are merely sugar of around. You are right about call vs.
execution though. In this case it a matter of the 80/20 rule. We make
80% of the need easy (though it's probably more like 99/1 in this
case). If you _must_ have call interception then there are more advance
techinuques to use, such as set_trace_func. We've also discussed
method_dispatching hook. But understand these have much greater impact
on performance than method interception. This is important reason call
interception is not specifically part of this RCR. (But who knows?
Maybe someone will come up with an easy idea for this too).

While, maybe leaving alone all these aspects, may be a Ruby implementation
constraint, they are valid AOP principles that will miss from the implementation.

There are some. Because Ruby is not 100% OOP, though it is very close,
you can not cut conditionals for example. You've pointed out call
interception already and there are other marginal areas. Ruby itself
has some limitations that effect this. Combined with Ruby's ability to
reflect on itself, Cut-based AOP covers the vast majoirty of AOP needs.
And as Ruby improves so does the reach of cuts.

T.

Daniel Schierbeck wrote:

+1

I only have one concern: I think the syntax is kinda weird. It resembles
the class definition syntax, but I can't see why it should. When you
write "class A < B" you're saying "class A inherits from class B" or
"class A is affected by class B". If we apply the same to "cut A < B",
it would be "cut A is affected by class B", which isn't the case. Why
not use "cut A > B", "cut A affects class C". The singleton syntax could
be like this "cut >> obj".

Your .16 Yen is appreciated :slight_smile: In fact we have discussed similar
notation:

  class A > Ac
    # advice
  end

We decided against, mainly because it looks too much like normal
subclassing. though it is certaintly a possibility.

How I say 'cut Ac < A' is "cut Ac cuts A.", though I normally drop the
first 'cut', "Ac cuts A".

T.

itsme213@hotmail.com wrote:

Does this also provide a quite general form of dependency injection,
very different in nature and weight compared to something like Needle?

Had to give this some thought. Mu immediate conjecture is that it must
since DI and AOP are closely related. But I'm no DI expert like Jim
Weirich. But in giving it some conosideration, I imagine there's not
much more to it than:

  class App

    def logger
      nil # no logger available
    end

  end

  cut AppContainer < App

    def logger
      Logger.new
    end

  end

T.

Will cuts be allowed on root classes that are themselves extruded from a
'C' extension? E.g. I don't really know if class IO is mostly C
extension stuff, but could a cut be applied to these class types?
Kernel? Object?

Yes. It is applicable to any class (and any module via proxy-cut).

Is this acceptable in the RCR design?

Ed, you're right on the money, Right On The Money.

Was it already implied?

I believe we make a passing mention of AOP in general having applicable
to testing. But we offer no details in how cuts may be used for this.
Now you've lighted the way --and an interesing way at that. Very cool.

Thanks for sharing this.

T.