cuts 0.0.4 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|