cuts 0.0.4 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (11) hide show
  1. data/COPYING +162 -669
  2. data/MANIFEST +12 -13
  3. data/README +13 -15
  4. data/RELEASE +24 -0
  5. data/VERSION +1 -1
  6. data/lib/cuts/aop.rb +32 -31
  7. data/lib/cuts/cut.rb +31 -100
  8. data/test/test_cut.rb +15 -137
  9. metadata +19 -35
  10. data/NOTES +0 -1
  11. data/meta/requires +0 -1
data/MANIFEST CHANGED
@@ -1,22 +1,21 @@
1
- Rakefile
1
+ lib
2
+ meta
2
3
  test
3
- test/test_cut.rb
4
- test/test_aop.rb
5
- NOTES
4
+ Rakefile
5
+ RELEASE
6
6
  README
7
7
  HISTORY
8
- meta
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
- lib
17
- lib/cuts.rb
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 NOTES file.
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, download the tgz package and type:
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
- Describe how to use your library or application here.
38
+ Please see RDoc API documentation.
39
+
42
40
 
43
41
 
44
42
  == LICENSE
45
43
 
46
- Copyright (c) 2008
44
+ Copyright (c) 2008 TigerOps
47
45
 
48
- This program is ditributed unser the terms of the MIT license.
46
+ This program is ditributed unser the terms of the LGPL license.
49
47
 
50
- Please see COPYING file.
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.4 alpha (2008-09-18)
1
+ cuts 0.1.0 beta (2008-11-24)
@@ -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
- methods = public_instance_methods + private_instance_methods + protected_instance_methods
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
- #def initialize(base, *args, &block)
118
- # @base = base
119
- # @delegate = base.__new(*args, &block)
120
- # @advices = {}
121
- #end
112
+ def self.extended(obj)
113
+ base = obj.class #__base__
122
114
 
123
- def method_missing(sym, *args, &blk)
124
- # p "METHOD MISSING: #{sym}" #if DEBUG
115
+ methods = obj.methods + obj.private_methods - ['advices']
125
116
 
126
- @advices ||= {}
117
+ methods.each do |sym|
127
118
 
128
- base = __base__
119
+ #meth = obj.method(sym)
129
120
 
130
- jp = Joinpoint.new(self, base, sym, *args, &blk)
121
+ define_method(sym) do |*args| #, &blk| # TODO imporove interface mirroring
122
+ jp = Joinpoint.new(self, base, sym, *args) #, &blk)
131
123
 
132
- # calculate advices on first use.
133
- unless @advices[sym]
134
- @advices[sym] = []
135
- base.aspects.each do |aspect|
136
- aspect.points.each do |advice, matches|
137
- matches.each do |match|
138
- if jp === match
139
- @advices[sym] << [aspect, advice]
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
- end
144
- end
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
- target = jp #Target.new(self, sym, *args, &blk) # Target == JoinPoint ?
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
 
@@ -25,13 +25,13 @@
25
25
  # def x; "x"; end
26
26
  # end
27
27
  #
28
- # cut :C < X do
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
- # To use this in an AOP fashion you can define an Aspect, as a class
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 :C < X do
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 is the basic model of this particluar implementation:
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
- # We cut it like so:
71
+ # The effect is essentially:
71
72
  #
72
- # Klass = KlassCut
73
+ # k = Klass.new
74
+ # k.extend KlassCut
73
75
  #
74
- # p Klass.new.x
76
+ # p k.x
75
77
  #
76
- # This is simple and relatvely robust, but not 100% transparent.
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
- cut.send(:include, Transparency)
91
- cut.extend MetaTransparency
92
-
93
- v = $VERBOSE
94
- $VERBOSE = false
95
- klass.modspace::const_set(klass.basename, cut)
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
- return cut
87
+ class Class
88
+ def cuts
89
+ @cuts ||= []
99
90
  end
91
+ end
100
92
 
101
- # These methods are needed to emulate full transparancy as
102
- # closely as possible.
93
+ class Object
94
+ class << self
95
+ alias_method :_new, :new
103
96
 
104
- module Transparency
105
- def methods(all=true)
106
- self.class.superclass.instance_methods(all)
107
- end
108
- def public_methods(all=true)
109
- self.class.superclass.public_instance_methods(all)
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(cutname, cut) # <<- this is what we don't have in Cut.new
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
 
@@ -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(target); '<'+target.super+'>' ; end
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( G )
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(target); '<'+target.super+'>' ; end
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(target); '['+target.super+']'; end
59
+ #join :f => :f
60
+ def f; '['+super+']'; end
68
61
  end
69
62
 
70
- def test_2_01
71
- assert_equal( [Q, G], F.cuts )
72
- assert_equal( [Q, G], F.predecessors )
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( G )
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
- end
114
-
115
- # Test the addition of new methods and module inclusions
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