cuts 0.0.4 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/COPYING +162 -669
- data/MANIFEST +12 -13
- data/README +13 -15
- data/RELEASE +24 -0
- data/VERSION +1 -1
- data/lib/cuts/aop.rb +32 -31
- data/lib/cuts/cut.rb +31 -100
- data/test/test_cut.rb +15 -137
- metadata +19 -35
- data/NOTES +0 -1
- data/meta/requires +0 -1
data/MANIFEST
CHANGED
@@ -1,22 +1,21 @@
|
|
1
|
-
|
1
|
+
lib
|
2
|
+
meta
|
2
3
|
test
|
3
|
-
|
4
|
-
|
5
|
-
NOTES
|
4
|
+
Rakefile
|
5
|
+
RELEASE
|
6
6
|
README
|
7
7
|
HISTORY
|
8
|
-
|
8
|
+
VERSION
|
9
|
+
COPYING
|
10
|
+
lib/cuts.rb
|
11
|
+
lib/cuts
|
12
|
+
lib/cuts/aop.rb
|
13
|
+
lib/cuts/cut.rb
|
9
14
|
meta/created
|
10
15
|
meta/homepage
|
11
16
|
meta/summary
|
12
17
|
meta/abstract
|
13
18
|
meta/license
|
14
|
-
meta/requires
|
15
19
|
meta/contact
|
16
|
-
|
17
|
-
|
18
|
-
lib/cuts
|
19
|
-
lib/cuts/aop.rb
|
20
|
-
lib/cuts/cut.rb
|
21
|
-
VERSION
|
22
|
-
COPYING
|
20
|
+
test/test_cut.rb
|
21
|
+
test/test_aop.rb
|
data/README
CHANGED
@@ -11,13 +11,9 @@ Cuts is an expiremental implementation of cut-based AOP for Ruby.
|
|
11
11
|
|
12
12
|
== RELEASE NOTES
|
13
13
|
|
14
|
-
Please see
|
14
|
+
Please see RELEASE file.
|
15
15
|
|
16
16
|
|
17
|
-
== RECENT CHANGES
|
18
|
-
|
19
|
-
Please see CHANGES file.
|
20
|
-
|
21
17
|
|
22
18
|
== HOW TO INSTALL
|
23
19
|
|
@@ -27,25 +23,27 @@ To install with RubyGems simply open a console and type:
|
|
27
23
|
|
28
24
|
gem install cuts
|
29
25
|
|
30
|
-
To manually installation,
|
26
|
+
To manually installation, you will need the Ruby Setup package
|
27
|
+
(http://setup.rubyforge.org). Then download the tgz package
|
28
|
+
and type:
|
29
|
+
|
30
|
+
$ tar -xvzf cuts-x.y.z.tgz
|
31
|
+
$ cd cuts-x.y.z.tgz
|
32
|
+
$ sudo setup.rb
|
31
33
|
|
32
|
-
tar -xvzf cuts-x.y.z.tgz
|
33
|
-
cd cuts-x.y.z.tgz
|
34
|
-
rake config
|
35
|
-
rake setup
|
36
|
-
rake install
|
37
34
|
|
38
35
|
|
39
36
|
== USAGE
|
40
37
|
|
41
|
-
|
38
|
+
Please see RDoc API documentation.
|
39
|
+
|
42
40
|
|
43
41
|
|
44
42
|
== LICENSE
|
45
43
|
|
46
|
-
|
44
|
+
Copyright (c) 2008 TigerOps
|
47
45
|
|
48
|
-
|
46
|
+
This program is ditributed unser the terms of the LGPL license.
|
49
47
|
|
50
|
-
|
48
|
+
Please see COPYING file.
|
51
49
|
|
data/RELEASE
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
This release completel overhauls how cuts are
|
2
|
+
implemented.
|
3
|
+
|
4
|
+
Before, cuts where overriding the constant of the
|
5
|
+
original class and redirecting class changes
|
6
|
+
(define_method, alias_method, etc.) to the original
|
7
|
+
class. This works, and is technically more true to
|
8
|
+
the core idea of Cuts, but it is rather fragile --
|
9
|
+
getting a handle on the Cut vs. the Cut Class b/c
|
10
|
+
tricky.
|
11
|
+
|
12
|
+
The new implementation simply creates a module for
|
13
|
+
a cut instead, and then used #extend on objects
|
14
|
+
upon instantiation. The end result is less dynamic
|
15
|
+
(cuts need to be defined up front), but it is more
|
16
|
+
stable. As long as #new, when overriden, is done so
|
17
|
+
properly (ie. use super or copy the cut extension
|
18
|
+
logic) everthing will work as expected.
|
19
|
+
|
20
|
+
|
21
|
+
### 0.1.0 / 2008-11-24
|
22
|
+
|
23
|
+
* Overhauled underlying implementation.
|
24
|
+
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
cuts 0.0
|
1
|
+
cuts 0.1.0 beta (2008-11-24)
|
data/lib/cuts/aop.rb
CHANGED
@@ -13,8 +13,6 @@
|
|
13
13
|
#
|
14
14
|
# - Can JointPoint and Target be the same class?
|
15
15
|
|
16
|
-
#require 'facets/kernel/object'
|
17
|
-
#require 'facets/module/methods'
|
18
16
|
require 'cuts/cut'
|
19
17
|
|
20
18
|
#
|
@@ -38,7 +36,7 @@ class Aspect < Module
|
|
38
36
|
|
39
37
|
end
|
40
38
|
|
41
|
-
#
|
39
|
+
# TODO: pass actual method instead of using instace_method ?
|
42
40
|
|
43
41
|
class Joinpoint
|
44
42
|
def initialize(object, base, method, *args, &block)
|
@@ -109,49 +107,52 @@ def cross_cut(klass)
|
|
109
107
|
Cut.new(klass) do
|
110
108
|
define_method :__base__ do klass end
|
111
109
|
|
112
|
-
|
113
|
-
methods.each do |meth|
|
114
|
-
undef_method(meth) unless meth.to_s =~ /(^__|initialize$|p$|class$|inspect$)/
|
115
|
-
end
|
110
|
+
def advices; @advices ||= {}; end
|
116
111
|
|
117
|
-
|
118
|
-
|
119
|
-
# @delegate = base.__new(*args, &block)
|
120
|
-
# @advices = {}
|
121
|
-
#end
|
112
|
+
def self.extended(obj)
|
113
|
+
base = obj.class #__base__
|
122
114
|
|
123
|
-
|
124
|
-
# p "METHOD MISSING: #{sym}" #if DEBUG
|
115
|
+
methods = obj.methods + obj.private_methods - ['advices']
|
125
116
|
|
126
|
-
|
117
|
+
methods.each do |sym|
|
127
118
|
|
128
|
-
|
119
|
+
#meth = obj.method(sym)
|
129
120
|
|
130
|
-
|
121
|
+
define_method(sym) do |*args| #, &blk| # TODO imporove interface mirroring
|
122
|
+
jp = Joinpoint.new(self, base, sym, *args) #, &blk)
|
131
123
|
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
124
|
+
# calculate advices on first use.
|
125
|
+
unless advices[sym]
|
126
|
+
advices[sym] = []
|
127
|
+
base.aspects.each do |aspect|
|
128
|
+
aspect.points.each do |advice, matches|
|
129
|
+
matches.each do |match|
|
130
|
+
if jp === match
|
131
|
+
advices[sym] << [aspect, advice]
|
132
|
+
end
|
133
|
+
end
|
140
134
|
end
|
141
135
|
end
|
142
136
|
end
|
143
|
-
|
144
|
-
|
137
|
+
|
138
|
+
if advices[sym].empty?
|
139
|
+
super
|
140
|
+
else
|
141
|
+
target = jp #Target.new(self, sym, *args, &blk) # Target == JoinPoint ?
|
142
|
+
advices[sym].each do |(aspect, advice)|
|
143
|
+
target = Target.new(aspect, advice, target)
|
144
|
+
end
|
145
|
+
target.super
|
146
|
+
end
|
145
147
|
|
146
|
-
|
148
|
+
end
|
147
149
|
|
148
|
-
@advices[sym].each do |(aspect, advice)|
|
149
|
-
target = Target.new(aspect, advice, target)
|
150
150
|
end
|
151
151
|
|
152
|
-
target.super
|
153
152
|
end
|
153
|
+
|
154
154
|
end
|
155
|
+
|
155
156
|
end
|
156
157
|
|
157
158
|
|
data/lib/cuts/cut.rb
CHANGED
@@ -25,13 +25,13 @@
|
|
25
25
|
# def x; "x"; end
|
26
26
|
# end
|
27
27
|
#
|
28
|
-
# cut :
|
28
|
+
# cut :Z < X do
|
29
29
|
# def x; '{' + super + '}'; end
|
30
30
|
# end
|
31
31
|
#
|
32
32
|
# X.new.x #=> "{x}"
|
33
33
|
#
|
34
|
-
#
|
34
|
+
# One way to use this in an AOP fashion is to define an aspect as a class
|
35
35
|
# or function module, and tie it together with the Cut.
|
36
36
|
#
|
37
37
|
# module LogAspect
|
@@ -41,7 +41,7 @@
|
|
41
41
|
# end
|
42
42
|
# end
|
43
43
|
#
|
44
|
-
# cut :
|
44
|
+
# cut :Z < X do
|
45
45
|
# def x
|
46
46
|
# LogAspect.log(:x, r = super)
|
47
47
|
# return r
|
@@ -57,7 +57,8 @@
|
|
57
57
|
# Instantiating AClass effecively instantiates ACut instead,
|
58
58
|
# but that action is effectively transparent.
|
59
59
|
#
|
60
|
-
# This
|
60
|
+
# This particular implementation create a module for each cut
|
61
|
+
# and extends objects as they are created. Given the following example:
|
61
62
|
#
|
62
63
|
# class Klass
|
63
64
|
# def x; "x"; end
|
@@ -67,76 +68,50 @@
|
|
67
68
|
# def x; '{' + super + '}'; end
|
68
69
|
# end
|
69
70
|
#
|
70
|
-
#
|
71
|
+
# The effect is essentially:
|
71
72
|
#
|
72
|
-
#
|
73
|
+
# k = Klass.new
|
74
|
+
# k.extend KlassCut
|
73
75
|
#
|
74
|
-
# p
|
76
|
+
# p k.x
|
75
77
|
#
|
76
|
-
#
|
77
|
-
# So we add some redirection methods to the cut to improve the
|
78
|
-
# transparency.
|
79
|
-
#
|
80
|
-
# Due to limitation in meta-programming Ruby as this level, the
|
81
|
-
# transparency isn't perfect, but it's fairly close.
|
82
|
-
|
83
|
-
class Cut
|
84
|
-
|
85
|
-
def self.new(klass, &block)
|
86
|
-
cut = Class.new(klass, &block) # <-- This is the actual cut.
|
87
|
-
|
88
|
-
#cut.class_eval(&block)
|
78
|
+
# The downside to this approach is a limitation in dynamicism.
|
89
79
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
$VERBOSE = v
|
80
|
+
class Cut < Module
|
81
|
+
def initialize(klass, &block)
|
82
|
+
klass.cuts.unshift(self)
|
83
|
+
module_eval(&block)
|
84
|
+
end
|
85
|
+
end
|
97
86
|
|
98
|
-
|
87
|
+
class Class
|
88
|
+
def cuts
|
89
|
+
@cuts ||= []
|
99
90
|
end
|
91
|
+
end
|
100
92
|
|
101
|
-
|
102
|
-
|
93
|
+
class Object
|
94
|
+
class << self
|
95
|
+
alias_method :_new, :new
|
103
96
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
97
|
+
def new(*a, &b)
|
98
|
+
o = _new(*a, &b)
|
99
|
+
if !cuts.empty?
|
100
|
+
o.extend *cuts
|
101
|
+
end
|
102
|
+
o
|
110
103
|
end
|
111
|
-
def private_methods(all=true)
|
112
|
-
self.class.superclass.private_instance_methods(all)
|
113
|
-
end
|
114
|
-
def protected_methods(all=true)
|
115
|
-
self.class.superclass.protected_instance_methods(all)
|
116
|
-
end
|
117
|
-
end
|
118
104
|
|
119
|
-
# These methods are needed to emulate full transparancy as
|
120
|
-
# closely as possible.
|
121
|
-
|
122
|
-
module MetaTransparency
|
123
|
-
#def instance_method(name) ; p "XXXX"; superclass.instance_method(name) ; end
|
124
|
-
def define_method(*a,&b) ; superclass.define_method(*a,&b) ; end
|
125
|
-
def module_eval(*a,&b) ; superclass.module_eval(*a,&b) ; end
|
126
|
-
def class_eval(*a,&b) ; superclass.class_eval(*a,&b) ; end
|
127
105
|
end
|
128
|
-
|
129
106
|
end
|
130
107
|
|
131
|
-
|
132
108
|
class Symbol
|
133
109
|
#alias :_op_lt_without_cuts :<
|
134
110
|
|
135
111
|
# A little tick to simulate subclassing literal syntax.
|
136
|
-
|
137
112
|
def <(klass)
|
138
113
|
if Class === klass
|
139
|
-
[self,klass]
|
114
|
+
[self, klass]
|
140
115
|
else
|
141
116
|
raise NoMethodError, "undefined method `<' for :#{self}:Symbol"
|
142
117
|
#_op_lt_without_cuts(cut_class)
|
@@ -144,10 +119,8 @@ class Symbol
|
|
144
119
|
end
|
145
120
|
end
|
146
121
|
|
147
|
-
|
148
122
|
module Kernel
|
149
123
|
# Cut convienence method.
|
150
|
-
|
151
124
|
def cut(klass, &block)
|
152
125
|
case klass
|
153
126
|
when Array
|
@@ -161,52 +134,10 @@ module Kernel
|
|
161
134
|
# How to handle main, but not other instance spaces?
|
162
135
|
#klass.modspace::const_set(klass.basename, cut)
|
163
136
|
mod = (Module === self ? self : Object)
|
164
|
-
mod.const_set(
|
137
|
+
mod.const_set(name, cut) if name # <<- this is what we don't have in Cut.new
|
165
138
|
|
166
139
|
return cut
|
167
140
|
end
|
168
141
|
end
|
169
142
|
|
170
|
-
class Module
|
171
|
-
# Returns the root name of the module/class.
|
172
|
-
#
|
173
|
-
# module Example
|
174
|
-
# class Demo
|
175
|
-
# end
|
176
|
-
# end
|
177
|
-
#
|
178
|
-
# Demo.name #=> "Example::Demo"
|
179
|
-
# Demo.basename #=> "Demo"
|
180
|
-
#
|
181
|
-
# For anonymous modules this will provide a basename
|
182
|
-
# based on Module#inspect.
|
183
|
-
#
|
184
|
-
# m = Module.new
|
185
|
-
# m.inspect #=> "#<Module:0xb7bb0434>"
|
186
|
-
# m.basename #=> "Module_0xb7bb0434"
|
187
|
-
#
|
188
|
-
def basename
|
189
|
-
if name and not name.empty?
|
190
|
-
name.gsub(/^.*::/, '')
|
191
|
-
else
|
192
|
-
nil #inspect.gsub('#<','').gsub('>','').sub(':', '_')
|
193
|
-
end
|
194
|
-
end
|
195
|
-
|
196
|
-
# Returns the module's container module.
|
197
|
-
#
|
198
|
-
# module Example
|
199
|
-
# class Demo
|
200
|
-
# end
|
201
|
-
# end
|
202
|
-
#
|
203
|
-
# Example::Demo.modspace #=> Example
|
204
|
-
#
|
205
|
-
# See also Module#basename.
|
206
|
-
#
|
207
|
-
def modspace
|
208
|
-
space = name[ 0...(name.rindex( '::' ) || 0)]
|
209
|
-
space.empty? ? Object : eval(space)
|
210
|
-
end
|
211
|
-
end
|
212
143
|
|
data/test/test_cut.rb
CHANGED
@@ -18,12 +18,6 @@ class TestCut < Test::Unit::TestCase
|
|
18
18
|
|
19
19
|
end
|
20
20
|
|
21
|
-
|
22
|
-
=begin
|
23
|
-
require 'facets/cut.rb'
|
24
|
-
|
25
|
-
require 'test/unit'
|
26
|
-
|
27
21
|
class TestCut1 < Test::Unit::TestCase
|
28
22
|
|
29
23
|
class F
|
@@ -31,19 +25,18 @@ class TestCut1 < Test::Unit::TestCase
|
|
31
25
|
end
|
32
26
|
|
33
27
|
cut :G < F do
|
34
|
-
join :f => :f
|
35
|
-
def f
|
28
|
+
#join :f => :f
|
29
|
+
def f; '<'+super+'>' ; end
|
36
30
|
end
|
37
31
|
|
38
32
|
def test_1_01
|
39
33
|
f = F.new
|
40
34
|
assert_equal( "<f>", f.f )
|
41
35
|
assert_equal( F, f.class )
|
42
|
-
assert_equal( F, f.object_class )
|
43
36
|
end
|
44
37
|
|
45
38
|
def test_1_02
|
46
|
-
assert(
|
39
|
+
assert(G)
|
47
40
|
assert_equal( "TestCut1::G", G.name )
|
48
41
|
end
|
49
42
|
|
@@ -58,150 +51,35 @@ class TestCut2 < Test::Unit::TestCase
|
|
58
51
|
end
|
59
52
|
|
60
53
|
cut :G < F do
|
61
|
-
join :f => :f
|
62
|
-
def f
|
54
|
+
#join :f => :f
|
55
|
+
def f; '<'+super+'>' ; end
|
63
56
|
end
|
64
57
|
|
65
58
|
cut :Q < F do
|
66
|
-
join :f => :f
|
67
|
-
def f
|
59
|
+
#join :f => :f
|
60
|
+
def f; '['+super+']'; end
|
68
61
|
end
|
69
62
|
|
70
|
-
def test_2_01
|
71
|
-
|
72
|
-
|
73
|
-
end
|
63
|
+
#def test_2_01
|
64
|
+
# assert_equal( [Q, G], F.cuts )
|
65
|
+
# assert_equal( [Q, G], F.predecessors )
|
66
|
+
#end
|
74
67
|
|
75
68
|
def test_2_02
|
76
69
|
f = F.new
|
77
70
|
assert_equal( F, f.class )
|
78
|
-
assert_equal( F, f.object_class )
|
79
71
|
assert_equal( "[<f>]", f.f )
|
80
72
|
end
|
81
73
|
|
82
74
|
def test_2_03
|
83
|
-
assert(
|
75
|
+
assert(G)
|
84
76
|
assert_equal( "TestCut2::G", G.name )
|
85
|
-
assert( Q )
|
86
|
-
assert_equal( "TestCut2::Q", Q.name )
|
87
|
-
end
|
88
|
-
|
89
|
-
end
|
90
|
-
|
91
|
-
#
|
92
|
-
|
93
|
-
class TestCut3 < Test::Unit::TestCase
|
94
|
-
|
95
|
-
class C
|
96
|
-
def r1; "r1"; end
|
97
|
-
end
|
98
|
-
|
99
|
-
cut :A < C do
|
100
|
-
def r1
|
101
|
-
b1( target( :r1 ){ super } )
|
102
|
-
end
|
103
|
-
def b1( target )
|
104
|
-
'(' + target.super + ')'
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
def test_3_01
|
109
|
-
c = C.new
|
110
|
-
assert_equal( '(r1)', c.r1 )
|
111
77
|
end
|
112
78
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
# after the cut is defined with dynamic joining.
|
117
|
-
|
118
|
-
class TestCut4 < Test::Unit::TestCase
|
119
|
-
|
120
|
-
class C
|
121
|
-
def r1; "r1"; end
|
122
|
-
def r2; "r2"; end
|
123
|
-
def j1; "j1"; end
|
124
|
-
def j2; "j2"; end
|
125
|
-
end
|
126
|
-
|
127
|
-
cut :A < C do
|
128
|
-
|
129
|
-
join :wrappy => lambda { |jp| /^r/ =~ jp }
|
130
|
-
join :square => :j1, :flare => :j2
|
131
|
-
|
132
|
-
def wrappy( target )
|
133
|
-
'{'+target.super+'}'
|
134
|
-
end
|
135
|
-
|
136
|
-
def square(target) '['+target.super+']' end
|
137
|
-
def flare(target) '*'+target.super+'*' end
|
138
|
-
end
|
139
|
-
|
140
|
-
class C
|
141
|
-
def r3; "r3"; end
|
142
|
-
end
|
143
|
-
|
144
|
-
module M
|
145
|
-
def r4 ; "r4"; end
|
146
|
-
end
|
147
|
-
|
148
|
-
class C
|
149
|
-
include M
|
150
|
-
end
|
151
|
-
|
152
|
-
def test_4_01
|
153
|
-
c = C.new
|
154
|
-
assert_equal( '{r1}', c.r1 )
|
155
|
-
assert_equal( '{r2}', c.r2 )
|
156
|
-
assert_equal( '{r3}', c.r3 )
|
157
|
-
assert_equal( '{r4}', c.r4 )
|
158
|
-
end
|
159
|
-
|
160
|
-
def test_4_02
|
161
|
-
c = C.new
|
162
|
-
assert_equal( '[j1]', c.j1 )
|
163
|
-
assert_equal( '*j2*', c.j2 )
|
79
|
+
def test_2_04
|
80
|
+
assert(Q)
|
81
|
+
assert_equal( "TestCut2::Q", Q.name )
|
164
82
|
end
|
165
83
|
|
166
84
|
end
|
167
85
|
|
168
|
-
# Test subclassing.
|
169
|
-
|
170
|
-
class TestCut5 < Test::Unit::TestCase
|
171
|
-
|
172
|
-
class C
|
173
|
-
def r1; "r1"; end
|
174
|
-
def r2; "r2"; end
|
175
|
-
end
|
176
|
-
|
177
|
-
cut :C1 < C do
|
178
|
-
join :wrap1 => [:r1, :r2]
|
179
|
-
|
180
|
-
def wrap1( target )
|
181
|
-
'{' + target.super + '}'
|
182
|
-
end
|
183
|
-
end
|
184
|
-
|
185
|
-
cut :C2 < C do
|
186
|
-
join :wrap2 => [:r1, :r2]
|
187
|
-
|
188
|
-
def wrap2( target )
|
189
|
-
'[' + target.super + ']'
|
190
|
-
end
|
191
|
-
end
|
192
|
-
|
193
|
-
class D < C
|
194
|
-
def r1; '<' + super + '>'; end
|
195
|
-
end
|
196
|
-
|
197
|
-
def test_5_01
|
198
|
-
c = C.new
|
199
|
-
assert_equal( '[{r1}]', c.r1 )
|
200
|
-
assert_equal( '[{r2}]', c.r2 )
|
201
|
-
d = D.new
|
202
|
-
assert_equal( '<[{r1}]>', d.r1 )
|
203
|
-
assert_equal( '[{r2}]', d.r2 )
|
204
|
-
end
|
205
|
-
|
206
|
-
end
|
207
|
-
=end
|