[ANN] [RCR] Cut-based AOP

T

Trans

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!
 
D

dave.burt

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
 
C

Christophe Grandsire

Selon Trans said:
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 brok=
e OOP
rather than enhanced it. This proposal, on the other hand, is strongly ba=
sed 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 do=
esn't
have to.

I just have a few questions, which I hope aren't too dumb ( ;) ). I could=
n't
find anything in the RCR about those questions (although I may have misse=
d 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 i=
s 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 inher=
it
cuts, this could lead to surprises, with a not overriden method behaving
suddenly differently in the superclass and in the subclass. And what shou=
ld 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 goo=
d work!
--
Christophe Grandsire.

http://rainbow.conlang.free.fr

It takes a straight mind to create a twisted conlang.
 
P

Peter Vanbroekhoven

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
 
P

Peter Vanbroekhoven

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 ;-)
I just have a few questions, which I hope aren't too dumb ( ;) ). 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
 
C

Christophe Grandsire

Selon Peter Vanbroekhoven said:
Great! One down, many more to go ;-)

LOL! Seriously, I really hope this RCR gets accepted. It is nicely
non-intrusive, and brings functionality that I'm sure many people would u=
se if
it was there.
uts?

Cuts are not supposed to be subclassed (they can't be instantiated, so
what is the point?)

I agree, which is why I corrected myself and said "subcut" afterwards :) =
 
D

Dave Burt

Peter Vanbroekhoven explained:
On Wed, 19 Oct 2005 (e-mail address removed) wrote:

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

Foo = Bar

Quite. Let's imagine that's what I said.
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.

OK, great. I'm starting to see the usefulness of this. It occurs to me that
there is some similarity between this and selector namespaces (targeted for
Ruby 2) -- a lot of what I imagined namespaces being useful for (i.e. making
changes to a core class that only affects code within a particular limited
context) is accomplished easily using cuts.

I still don't know anything, really, about Ruby namespaces. Something else
to look forward too, and something complementary to this, I guess.

Last thing, a criticism: shouldn't cut be a module, but not a class, because
classes are instantiable?

Cheers,
Dave
 
P

Peter Vanbroekhoven

Peter Vanbroekhoven explained:

Quite. Let's imagine that's what I said.

Done :)
OK, great. I'm starting to see the usefulness of this. It occurs to me that
there is some similarity between this and selector namespaces (targeted for
Ruby 2) -- a lot of what I imagined namespaces being useful for (i.e. making
changes to a core class that only affects code within a particular limited
context) is accomplished easily using cuts.

There are definitely parallels, but cuts are at this moment not
context-aware by themselves. Something like namespaces can be done as
follows:

cut FileLogging < File
def initialize(*args, &blk)
do_some_logging if Thread.current[:LOGGING_ENABLED]
super
do_some_more_logging if Thread.current[:LOGGING_ENABLED]
end
end

def with_logging
old_logging = Thread.current[:LOGGING_ENABLED]
Thread.current[:LOGGING_ENABLED] = true
yield
Thread.current[:LOGGING_ENABLED] = old_logging
end

f1 = File.new('some_file') # => no logging
with_logging do
f2 = File.new('some_other_file') # => with logging
end

I think though that this is different from selector namespaces, as my
example provides different behavior in a dynamic scope, and selector
namespaces are about static scope. That's provided I understand about
selector namespaces myself.
I still don't know anything, really, about Ruby namespaces. Something else
to look forward too, and something complementary to this, I guess.

Last thing, a criticism: shouldn't cut be a module, but not a class, because
classes are instantiable?

First things first: when you talk about cut, is that Cut, or an instance
of Cut? If it's the latter, then the answer is that we had long
discussions about whether Cut should be a subclass of Class or of Module.
There are some features that make it class, and some features that make it
a module:

* a module because:
- Not instantiable (though singleton classes aren't instatiable either)
* a class because
- Not includable (though neither is Class which is a subclass of Module
too)
- It has a superclass, namely the class it cuts

So you see, there are arguments for both, and in the end we settled on it
being a class.

Peter
 
E

Eric Mahurin

What's the reason for naming the cut? In all of the code I've
seen so far, the actual cut is never used. So instead of:

cut C < A ... end

why not:

cut < A ... end

or:

cut A ... end

The only thing I can think of off hand is that you might want
to remove the cut down the road.

Conceptually, I think it is easier to think of this cut
operating on the parent class (make it look like you're just
reopening the class). You could think of this "cut" as
something just like "class" except that the superclass is
treated as the previous definition of "class". This also would
extend well to making a cut on the meta class of an object:

cut <<obj ... end

From the syntax you proposed, it would be something like this I
believe:

cut A < <<obj ... end

I didn't see anything in the proposal about cutting the
meta-class of an object, but the above seems like what the
syntax would be. Naming the cut just seems unnecessarily
verbose and complicates the concept.

Here are 2 other ideas that accomplish the majority of what you
are talking about without being so grandiose:

- have another type of def/#define_method (maybe
redef/#redefine_method) that allows redefinition of a method
such that "super" is treated as the previous definition.

- have another keyword like "super" that means previous
definition.

Right now you can actually manually do something like the
above:

class C
def foo;"foo";end
end

c =3D C.new

c.foo # =3D> "foo"

class C
foo1 =3D instance_method:)foo)
define_method:)foo) { "{"+foo1.bind(self).call+"}" }
end

c.foo # =3D> "{foo}"

class C
foo1 =3D instance_method:)foo)
define_method:)foo) { "["+foo1.bind(self).call+"]" }
end

c.foo # =3D> "[{foo}]"

Pretty sweet, huh? I wasn't sure if foo1 kept the previous
definition until I tried it, but apparently it does.

Maybe just an easier way to do the above is all that is needed.
I think the above covers what is really needed for AOP - and
we already have it. Just simpler syntax is needed IMO. No
extra baggage needs to be added to the class data structures.

I'm not sure why you guys made such a lengthy RCR. I think I
have the concept now, but with all of the verbosity, it makes
it difficult to figure out what you are really proposing. I
think a much more concise RCR would have been better.




=09
__________________________________=20
Yahoo! Music Unlimited=20
Access over 1 million songs. Try it free.
http://music.yahoo.com/unlimited/
 
D

Dave Burt

Peter Vanbroekhoven:
First things first: when you talk about cut, is that Cut, or an instance
of Cut?

Yes. We're talking about the Cut in the inheritance heirarchy.

(I don't see the ambiguity; I'd read "a cut is a module" as "an instance of
Cut is also an instance of Module", which implies "Cut < Module".)
If it's the latter, then the answer is that we had long discussions about
whether Cut should be a subclass of Class or of Module. There are some
features that make it class, and some features that make it a module:

* a module because:
- Not instantiable (though singleton classes aren't instatiable either)
* a class because
- Not includable (though neither is Class which is a subclass of Module
too)
- It has a superclass, namely the class it cuts

So you see, there are arguments for both, and in the end we settled on it
being a class.

I don't see the balance falling the same way -- I see Cut much happier as a
peer to Class than a subclass of it.
- My main reason is that a subclass of Class should be instantiable; that's
the primary difference between Class and Module.
- That singleton classes aren't instantiable is an interesting point, but
those are kind of obscure, whereas a cut is not, with an added keyword and
all.
- Cut#superclass and Class#superclass are different, as it's a different
kind of relationship.
- include and extend only accept objects whose class is Module, so this is a
non-issue

Can you un-settle on it being a class?

Cheers,
Dave
 
J

Jeff Wood

Although I see the power of the examples and the ideas, it seems like
it's a bit watered down compared to AspectJ or other Aspect
frameworks.

Usually, the definition of cuts allow you to wrap many things at once...

AspectJ ( for instance ) allows you to use wildcards in both the
object type and the method names that are getting wrapped. It allows
you to add general functionality ( like debugging logs or security
control ) very easily to chunks ( of any size, even all ) of your
application.

I'd like to see something like that for Ruby, but I do think that your
idea is a good start.

maybe something that allows cut to take some wildcards

Cut.new( "FileLogging" ) do

wrap_method( File, :any ) do |method, *args|
# do logging stuff
method.call( *args )
# do more logging stuff
end

end

or instead of File ...simply

wrap_method( IO, :any ) ...
...
end

which would turn around and add this cut to any object of that type (
or a derivative ) of IO

Anyways, just additional thoughts. Shouldn't be too hard to implement
as objects now within Ruby as it exists...

The only trick is catching overrides and new types that are defined
AFTER the cut has been added.

... just my $0.02US

j.


Peter Vanbroekhoven explained:

Quite. Let's imagine that's what I said.

Done :)
OK, great. I'm starting to see the usefulness of this. It occurs to me = that
there is some similarity between this and selector namespaces (targeted= for
Ruby 2) -- a lot of what I imagined namespaces being useful for (i.e. m= aking
changes to a core class that only affects code within a particular limi= ted
context) is accomplished easily using cuts.

There are definitely parallels, but cuts are at this moment not
context-aware by themselves. Something like namespaces can be done as
follows:

cut FileLogging < File
def initialize(*args, &blk)
do_some_logging if Thread.current[:LOGGING_ENABLED]
super
do_some_more_logging if Thread.current[:LOGGING_ENABLED]
end
end

def with_logging
old_logging =3D Thread.current[:LOGGING_ENABLED]
Thread.current[:LOGGING_ENABLED] =3D true
yield
Thread.current[:LOGGING_ENABLED] =3D old_logging
end

f1 =3D File.new('some_file') # =3D> no logging
with_logging do
f2 =3D File.new('some_other_file') # =3D> with logging
end

I think though that this is different from selector namespaces, as my
example provides different behavior in a dynamic scope, and selector
namespaces are about static scope. That's provided I understand about
selector namespaces myself.
I still don't know anything, really, about Ruby namespaces. Something e= lse
to look forward too, and something complementary to this, I guess.

Last thing, a criticism: shouldn't cut be a module, but not a class, be= cause
classes are instantiable?

First things first: when you talk about cut, is that Cut, or an instance
of Cut? If it's the latter, then the answer is that we had long
discussions about whether Cut should be a subclass of Class or of Module.
There are some features that make it class, and some features that make i= t
a module:

* a module because:
- Not instantiable (though singleton classes aren't instatiable either= )
* a class because
- Not includable (though neither is Class which is a subclass of Modul= e
too)
- It has a superclass, namely the class it cuts

So you see, there are arguments for both, and in the end we settled on it
being a class.

Peter
 
P

Peter Vanbroekhoven

What's the reason for naming the cut? In all of the code I've
seen so far, the actual cut is never used. So instead of:

cut C < A ... end

why not:

cut < A ... end

or:

cut A ... end

The only thing I can think of off hand is that you might want
to remove the cut down the road.

Well, naming the cut is interesting if you want to reopen the cut and
change some methods. And in general for managing the cuts, like removing.

Just like with classes, if you don't want to store the cut or don't want
to waste a constant on it, use the constructor:

Cut.new(A) do ... end
Conceptually, I think it is easier to think of this cut
operating on the parent class (make it look like you're just
reopening the class). You could think of this "cut" as
something just like "class" except that the superclass is
treated as the previous definition of "class". This also would
extend well to making a cut on the meta class of an object:

cut <<obj ... end

Sure, and this is exactly how you can do it.
believe:

cut A < <<obj ... end

What makes you say so? Given that subclassing is done like "class A < B"
it does not mean that opening the singleton class of B look like "class A
< << B". Some here. If you want to give the cut a name, use "cut A < B",
if you want to cut the metaclass, use "cut << B", and if you want an
anonymous cut, use "Cut.new(A)".
I didn't see anything in the proposal about cutting the
meta-class of an object, but the above seems like what the
syntax would be. Naming the cut just seems unnecessarily
verbose and complicates the concept.

Then it must be missing from the proposal, it was certainly intended to be
possible. As for the unnecessary complexity, I don't agree. Naming the cut
allows you to access it again, and change methods ro remove them, etc. It
gives you a handle on them.
Here are 2 other ideas that accomplish the majority of what you
are talking about without being so grandiose:

- have another type of def/#define_method (maybe
redef/#redefine_method) that allows redefinition of a method
such that "super" is treated as the previous definition.

In a way this is what Matz wanted to do with "def method:wrap", it's just
a different syntax. A big problem with this is how to manage these
redefinitions. How do you propose we redefine or remove a specific
rededinition if there are many for the same method?
- have another keyword like "super" that means previous
definition.

We've been over that in our discussions before. One problem is that that
keyword can be hidden inside an eval and all, which makes it impossible to
determine whether the previous definition is actually called. This causes
a problem with garbage collecting previous method definitions, as it is
impossible to determine the dead ones automatically in all possible cases.

Also, this does still not allow the redefinitions to be managed in any
sensible way.
Right now you can actually manually do something like the
above:

class C
def foo;"foo";end
end

c = C.new

c.foo # => "foo"

class C
foo1 = instance_method:)foo)
define_method:)foo) { "{"+foo1.bind(self).call+"}" }
end

c.foo # => "{foo}"

class C
foo1 = instance_method:)foo)
define_method:)foo) { "["+foo1.bind(self).call+"]" }
end

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

Pretty sweet, huh? I wasn't sure if foo1 kept the previous
definition until I tried it, but apparently it does.

Maybe just an easier way to do the above is all that is needed.
I think the above covers what is really needed for AOP - and
we already have it. Just simpler syntax is needed IMO. No
extra baggage needs to be added to the class data structures.

I'm not sure why you guys made such a lengthy RCR. I think I
have the concept now, but with all of the verbosity, it makes
it difficult to figure out what you are really proposing. I
think a much more concise RCR would have been better.

The reason for cuts, for naming them, and the rest of the RCR, is simply
because any other way proposed (using closures as above, and already shown
in the past by Ara I think, or using method aliasing, and so on) makes it
very hard to manage the stack of methods. Cuts give you the handle to do
so. If you want the set of methods you've added, you can just grab the cut
and you've got them.

We've been over all these things before in our discussions, including what
you mention above, and none of it works well with Ruby's dynamic nature.
Because cuts are based in inheritance, it does work with Ruby's dynamic
nature.

Peter
 
C

Christophe Grandsire

Selon Peter Vanbroekhoven said:
Then it must be missing from the proposal, it was certainly intended to= be
possible.

It isn't. Both "Cut.new(A) {...}" and "cut <<obj ... end" are shown in th=
e text
of the RCR. You didn't forget them. Eric just overlooked them.

As for the unnecessary complexity, I don't agree. Naming the cut
allows you to access it again, and change methods ro remove them, etc. = It
gives you a handle on them.

It also allows you to define more than one cut on one class, allowing you=
to
organise the concerns by domain and meaning, instead of having one big me=
ss of
concerns in a single place (or worse: in the original class).

As for the RCR being not concise enough, I found it to be just right. Asp=
ects
are usually described with undefined buzzwords and a lot of propaganda. I=
t was
nice to see something more palatable, which took the time to explain clea=
rly
and in a neutral tone the ins and outs of cuts and AOP.
The reason for cuts, for naming them, and the rest of the RCR, is simpl= y
because any other way proposed (using closures as above, and already sh= own
in the past by Ara I think, or using method aliasing, and so on) makes = it
very hard to manage the stack of methods. Cuts give you the handle to d= o
so. If you want the set of methods you've added, you can just grab the = cut
and you've got them.

And if you want only the methods related to one particular concern, just =
grab
the cut you specifically defined for it, and don't touch the others. I al=
so
think it would make AOP easier to document in the source code.
--
Christophe Grandsire.

http://rainbow.conlang.free.fr

It takes a straight mind to create a twisted conlang.
 
P

Peter Vanbroekhoven

Although I see the power of the examples and the ideas, it seems like
it's a bit watered down compared to AspectJ or other Aspect
frameworks.

Usually, the definition of cuts allow you to wrap many things at once...

AspectJ ( for instance ) allows you to use wildcards in both the
object type and the method names that are getting wrapped. It allows
you to add general functionality ( like debugging logs or security
control ) very easily to chunks ( of any size, even all ) of your
application.

I'd like to see something like that for Ruby, but I do think that your
idea is a good start.

maybe something that allows cut to take some wildcards

Cut.new( "FileLogging" ) do

wrap_method( File, :any ) do |method, *args|
# do logging stuff
method.call( *args )
# do more logging stuff
end

end

or instead of File ...simply

wrap_method( IO, :any ) ...
...
end

which would turn around and add this cut to any object of that type (
or a derivative ) of IO

Anyways, just additional thoughts. Shouldn't be too hard to implement
as objects now within Ruby as it exists...

The only trick is catching overrides and new types that are defined
AFTER the cut has been added.

... just my $0.02US

Thanks for contribution ;-)

Well, I think we are aware of this. Cuts provide the basis of AOP, namely
the basic wrapping functionality, and providing a convenient handle to the
wrapping methods. These things can then be used in a full-fledged AOP
library that provides wildcards and all. But I think the important point
is that we think that cuts are a good basis for AOP. Anything on top of
that is sugar. And because cuts fit well within Ruby's OO system, it will
be good sugar!

Peter
 
T

Trans

Jeff said:
Although I see the power of the examples and the ideas, it seems like
it's a bit watered down compared to AspectJ or other Aspect
frameworks.

As the RCR points out, this is purposeful and with good reason. We've
designed a foundation. Ruby has plenty of capabilites for doing these
other "watered up" things. The problem with these other frameworks is
that you are always forced to do the "watered up" thing, but more often
teh usecases don't warrant it.
Usually, the definition of cuts allow you to wrap many things at once...

You mean *cross-cut*. We draw a distinction. In fact, I'm not sure if I
have ever seen anyone else use the term "cut" withuout "cross". It is a
distinction that may be original.
AspectJ ( for instance ) allows you to use wildcards in both the
object type and the method names that are getting wrapped. It allows
you to add general functionality ( like debugging logs or security
control ) very easily to chunks ( of any size, even all ) of your
application.

We've explored this extensively. Cutting based on method names is very
*bad*. And while wildcard cutting seems great, you'll find that adding
aspects to *all* or even *most* of your app is rearely desirable.
Typically, it is very specific classes and specific methods within them
that need advising. The exceptions are for meta-programming needs like
unit-testing and debugging. But as I say, these are exceptional. (It is
rather ironic perhaps, but many people do not even think of AOP as
being applicable to anything but these "meta" usecases b/c of the way
current AOP frameworks work!)
I'd like to see something like that for Ruby, but I do think that your
idea is a good start.

maybe something that allows cut to take some wildcards

Cut.new( "FileLogging" ) do

wrap_method( File, :any ) do |method, *args|
# do logging stuff
method.call( *args )
# do more logging stuff
end

end

This can be done with the RCR as proposed, along the lines of:

cut FileLogging < File

redirect_advice instance_methods => :log

def log( target )
# do logging stuff
target.super
# do more logging stuff
end

end
or instead of File ...simply

wrap_method( IO, :any ) ...
...
end

which would turn around and add this cut to any object of that type (
or a derivative ) of IO

Your example would have to be expanded on, it's not explicit enough.
Nonetheleass you can do such things in similar fashion to the above
example.
Anyways, just additional thoughts. Shouldn't be too hard to implement
as objects now within Ruby as it exists...
Right!

The only trick is catching overrides and new types that are defined
AFTER the cut has been added.

Indeed, that requires some extra code in a #method_added hook. And
certainly it would be possible to write some methods that did that
automatically too. Cuts can advise #method_added hook too ;)

I think the important thing to understand is how Cut is a foundation
for AOP. Going the direction of Aspect/J and the like, IMO, are like
building a house from the roof down.

T.
 
P

Peter Vanbroekhoven

I find it a great idea to use modules for cuts to. A genial way to minimise the
amount of new features to implement and learn!

Thank you for the kind words!
So I don't have to worry that subclasses would suddenly behave differently from
their parents without explicit overriding. Cool.

That said, I'm suddenly thinking that sometimes one would rather want to
subclass the class as it stands without its aspects. If I'm thinking for
instance of the issue of logging. The way it stands now, if the parent class
gets a logger cut, the subclass suddenly gets logging functionality *in the
middle of its overridden methods*, when they use super, whereas one would
rather expect the logging aspect to be *external* to the implementation of the
subclass (if it is to be there at all). I hope I'm making myself clear. I have
difficulties to express my thoughts in words today...

I think I know what you mean. I think this can be done with a bit of
library support. The following should do what you want it to do, and it
can surely be done automatically by a library:

class A
def do_transaction ; ... ; end
end

class B < A
def do_transaction
do_some_stuff
super
end
end

module CutMixin
def do_transaction
do_logging if Thread.current['InsideLogger']
old = Thread.current['ACut::InsideLogger']
Thread.current['ACut::InsideLogger'] = true
super
Thread.current['ACut::InsideLogger'] = old
do_logging if Thread.current['InsideLogger']
end
end

cut ACut < A
include ACutMixin
end

cut BCut< B
include ACutMixin
end
What do you think? I like the way cuts act in a really transparent way, and that
when subclassing one needn't know about the existence of cuts, but I'm now
wondering whether it could be a problem in some cases when the aspect should
really stay external to the implementation of classes... Do you have a way to
solve that problem already?


I hope this small problem won't prevent the RCR to be accepted.
--
Christophe Grandsire.

http://rainbow.conlang.free.fr

It takes a straight mind to create a twisted conlang.

Peter
 
J

Jeff Wood

Thank you for the clarification.

j.

As the RCR points out, this is purposeful and with good reason. We've
designed a foundation. Ruby has plenty of capabilites for doing these
other "watered up" things. The problem with these other frameworks is
that you are always forced to do the "watered up" thing, but more often
teh usecases don't warrant it.
 
P

Peter Vanbroekhoven

Peter Vanbroekhoven:

Yes. We're talking about the Cut in the inheritance heirarchy.

(I don't see the ambiguity; I'd read "a cut is a module" as "an instance of
Cut is also an instance of Module", which implies "Cut < Module".)

Ah, I see. But you asked "shouldn't cut be a module". There's no "a"
before the "cut", which causes the ambiguity. I just asked to make sure I
wasn't misinterpreting. I do that when there are these tiny ambiguities in
a sentence.
I don't see the balance falling the same way -- I see Cut much happier as a
peer to Class than a subclass of it.
- My main reason is that a subclass of Class should be instantiable; that's
the primary difference between Class and Module.
- That singleton classes aren't instantiable is an interesting point, but
those are kind of obscure, whereas a cut is not, with an added keyword and
all.
- Cut#superclass and Class#superclass are different, as it's a different
kind of relationship.
- include and extend only accept objects whose class is Module, so this is a
non-issue

Can you un-settle on it being a class?

Sure. We've never managed a final decision on this, and because
implementation-wise both are possible, we settled on one of them to
prevent us from getting stuck arguing over a detail that is easy enough to
change. Problem is that most arguments for or against making cuts modules
are not conclusive or at least partly subjective. You make a pretty good
case though.

Peter
 
T

Trans

Eric said:
Right now you can actually manually do something like the
above:

class C
def foo;"foo";end
end

c = C.new

c.foo # => "foo"

class C
foo1 = instance_method:)foo)
define_method:)foo) { "{"+foo1.bind(self).call+"}" }
end

c.foo # => "{foo}"

class C
foo1 = instance_method:)foo)
define_method:)foo) { "["+foo1.bind(self).call+"]" }
end

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

Pretty sweet, huh? I wasn't sure if foo1 kept the previous
definition until I tried it, but apparently it does.

Maybe just an easier way to do the above is all that is needed.
I think the above covers what is really needed for AOP - and
we already have it. Just simpler syntax is needed IMO. No
extra baggage needs to be added to the class data structures.

Peter covers this well in his repsonse. I just want to point out one
additional thing about using a Cut rather using simple method hooks:
*introduction*. Using Cuts will allow you to conveniently store
information (i.e. state) pertaining to that cut/aspect independent of
the class being cut --an important criterea for good AOP. Presently
class instance vars are what you'd use for this, but as Ruby comes to
support local instance varaibles and/or methods introduction will
become even better.
I'm not sure why you guys made such a lengthy RCR. I think I
have the concept now, but with all of the verbosity, it makes
it difficult to figure out what you are really proposing. I
think a much more concise RCR would have been better.

Granted there are some spots where we could have been a little more
concise. But we wanted to be thurough, much like a white paper, rather
then something just thought up of a few days ago and thrown up. We also
hope the brief overwiew of AOP would be of benefit to those not so
familar with AOP.

Thanks,
T.
 
D

Dave Burt

Peter Vanbroekhoven:
Ah, I see. But you asked "shouldn't cut be a module". There's no "a"
before the "cut", which causes the ambiguity. I just asked to make sure I
wasn't misinterpreting. I do that when there are these tiny ambiguities in
a sentence.

Ah, yes. I'm not doing too well with accuracy in this thread, am I?
Sure. We've never managed a final decision on this, and because
implementation-wise both are possible, we settled on one of them to
prevent us from getting stuck arguing over a detail that is easy enough to
change. Problem is that most arguments for or against making cuts modules
are not conclusive or at least partly subjective. You make a pretty good
case though.

Thanks. I do feel pretty strongly about my first point (classes are
instantiable) and I think it really comes down to that on the Module side
versus cuts being part of the class inheritance heirarchy on the other. And
the nature of cut inheritance does separate it from the class heirarchy.

Cheers,
Dave
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads

easy AOP right now using evil.rb 2
RCR 13 0
RCR for Range.rand 0
AOP bigger picture 3
The likes of AOP (1 of 2) 1
ideas for an RCR: variable locality 16
aop in ruby 2
No Thing Here vs Uninitialized and RCR 303 21

Members online

Forum statistics

Threads
473,982
Messages
2,570,189
Members
46,735
Latest member
HikmatRamazanov

Latest Threads

Top