cuts 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/NOTES ADDED
@@ -0,0 +1,6 @@
1
+
2
+ # This is a place to put release notes.
3
+ # Like CHANGES this is also uploaded in the release process.
4
+ # This is good place to mention major changes and things
5
+ # users might need to do to take them into account.
6
+
data/README ADDED
@@ -0,0 +1,51 @@
1
+ = Cuts
2
+
3
+ http://cuts.rubyforge.org
4
+
5
+
6
+ == INTRODUCTION
7
+
8
+ Cuts is an expiremental implementation of cut-based AOP for Ruby.
9
+
10
+
11
+
12
+ == RELEASE NOTES
13
+
14
+ Please see NOTES file.
15
+
16
+
17
+ == RECENT CHANGES
18
+
19
+ Please see CHANGES file.
20
+
21
+
22
+ == HOW TO INSTALL
23
+
24
+ Describe your installation procedure here.
25
+
26
+ To install with RubyGems simply open a console and type:
27
+
28
+ gem install cuts
29
+
30
+ To manually installation, download the tgz package and type:
31
+
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
+
38
+
39
+ == USAGE
40
+
41
+ Describe how to use your library or application here.
42
+
43
+
44
+ == LICENSE
45
+
46
+ Copyright (c) 2008
47
+
48
+ This program is ditributed unser the terms of the MIT license.
49
+
50
+ Please see COPYING file.
51
+
@@ -0,0 +1,2 @@
1
+ require File.join(File.dirname(__FILE__), "setup.rb")
2
+
@@ -0,0 +1,2 @@
1
+ require 'cuts/cut.rb'
2
+ require 'cuts/aop.rb'
@@ -0,0 +1,205 @@
1
+ # TITLE:
2
+ #
3
+ # Aspect Oriented Programming for Ruby
4
+ #
5
+ # SUMMARY:
6
+ #
7
+ #
8
+ # AUTHORS:
9
+ #
10
+ # - Thomas Sawyer
11
+ #
12
+ # NOTES:
13
+ #
14
+ # - Can JointPoint and Target be the same class?
15
+
16
+ require 'facets/kernel/object'
17
+ require 'facets/module/methods'
18
+ require 'facets/cut'
19
+
20
+ #
21
+
22
+ class Aspect < Module
23
+
24
+ def initialize(&block)
25
+ instance_eval(&block)
26
+ extend self
27
+ end
28
+
29
+ def points
30
+ @points ||= {}
31
+ end
32
+
33
+ # TODO Should this accept pattern matches as an alternative to the block too?
34
+ # Eg. join(name, pattern=nil, &block)
35
+ def join(name, &block)
36
+ (points[name] ||= []) << block
37
+ end
38
+
39
+ end
40
+
41
+ #
42
+
43
+ class Joinpoint
44
+ def initialize(object, base, method, *args, &block)
45
+ @object = object
46
+ @base = base
47
+ @method = method
48
+ @args = args
49
+ @block = block
50
+ end
51
+
52
+ def ===(match)
53
+ case match
54
+ when Proc
55
+ match.call(self)
56
+ else # Pattern matches (not supported presently)
57
+ match.to_sym == @method.to_sym
58
+ end
59
+ end
60
+
61
+ def ==(sym)
62
+ sym.to_sym == @method.to_sym
63
+ end
64
+
65
+ #
66
+
67
+ def super
68
+ anc = @object.class.ancestors.find{ |anc| anc.method_defined?(@method) }
69
+ anc.instance_method(@method).bind(@object).call(*@args, &@block)
70
+ end
71
+ end
72
+
73
+
74
+ # module LogAspect
75
+ # extend self
76
+ #
77
+ # join :log do |jp|
78
+ # jp.name == :x
79
+ # end
80
+ #
81
+ # def log(target)
82
+ # r = target.super
83
+ # ...
84
+ # return r
85
+ # end
86
+ # end
87
+ #
88
+ # class X
89
+
90
+
91
+ class Target
92
+
93
+ def initialize(aspect, advice, *target, &block)
94
+ @aspect = aspect
95
+ @advice = advice
96
+ @target = target
97
+ @block = block
98
+ end
99
+
100
+ def super
101
+ @aspect.send(@advice, *@target, &@block)
102
+ end
103
+
104
+ alias_method :call, :super
105
+ end
106
+
107
+
108
+ def cross_cut(klass)
109
+ Cut.new(klass) do
110
+ define_method :__base__ do klass end
111
+
112
+ all_instance_methods.each do |meth|
113
+ undef_method(meth) unless meth.to_s =~ /(^__|initialize$|p$|class$|inspect$)/
114
+ end
115
+
116
+ #def initialize(base, *args, &block)
117
+ # @base = base
118
+ # @delegate = base.__new(*args, &block)
119
+ # @advices = {}
120
+ #end
121
+
122
+ def method_missing(sym, *args, &blk)
123
+ # p "METHOD MISSING: #{sym}" #if DEBUG
124
+
125
+ @advices ||= {}
126
+
127
+ base = __base__
128
+
129
+ jp = Joinpoint.new(self, base, sym, *args, &blk)
130
+
131
+ # calculate advices on first use.
132
+ unless @advices[sym]
133
+ @advices[sym] = []
134
+ base.aspects.each do |aspect|
135
+ aspect.points.each do |advice, matches|
136
+ matches.each do |match|
137
+ if jp === match
138
+ @advices[sym] << [aspect, advice]
139
+ end
140
+ end
141
+ end
142
+ end
143
+ end
144
+
145
+ target = jp #Target.new(self, sym, *args, &blk) # Target == JoinPoint ?
146
+
147
+ @advices[sym].each do |(aspect, advice)|
148
+ target = Target.new(aspect, advice, target)
149
+ end
150
+
151
+ target.super
152
+ end
153
+ end
154
+ end
155
+
156
+
157
+ #
158
+
159
+ class Class
160
+ #def cut; @cut; end
161
+ def aspects; @aspects ||= []; end
162
+
163
+ def apply(aspect)
164
+ if aspects.empty?
165
+ cross_cut(self)
166
+ #(class << self;self;end).class_eval do
167
+ # alias_method :__new, :new
168
+ # def new(*args, &block)
169
+ # CrossConcerns.new(self,*args, &block)
170
+ # end
171
+ #end
172
+ end
173
+ aspects.unshift(aspect)
174
+ end
175
+
176
+ end
177
+
178
+
179
+ =begin demo
180
+
181
+ class X
182
+ def x; "x"; end
183
+ def y; "y"; end
184
+ def q; "<" + x + ">"; end
185
+ end
186
+
187
+ Xa = Aspect.new do
188
+ join :x do |jp|
189
+ jp == :x
190
+ end
191
+
192
+ def x(target); '{' + target.super + '}'; end
193
+ end
194
+
195
+ X.apply(Xa)
196
+
197
+ x1 = X.new
198
+ #print 'X == ' ; p x1
199
+ print 'X == ' ; p x1.class
200
+ print '["q", "y", "x"] == ' ; p x1.public_methods(false)
201
+ print '"{x}" == ' ; p x1.x
202
+ print '"y" == ' ; p x1.y
203
+ print '"<{x}>" == ' ; p x1.q
204
+
205
+ =end
@@ -0,0 +1,190 @@
1
+ # TITLE:
2
+ #
3
+ # Cut
4
+ #
5
+ # SUMMARY:
6
+ #
7
+ # By definition, a Cut is a *transparent* subclass.
8
+ # Thay serve as the basis of Cut-based AOP, by providing
9
+ # a clean wrapping mechinism.
10
+ #
11
+ # COPYRIGHT:
12
+ #
13
+ # Copyright (c) 2005 Thomas Sawyer
14
+ #
15
+ # LICENSE:
16
+ #
17
+ # Ruby License
18
+ #
19
+ # This module is free software. You may use, modify, and/or redistribute this
20
+ # software under the same terms as Ruby.
21
+ #
22
+ # This program is distributed in the hope that it will be useful, but WITHOUT
23
+ # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
24
+ # FOR A PARTICULAR PURPOSE.
25
+ #
26
+ # AUTHORS:
27
+ #
28
+ # - Thomas Sawyer
29
+
30
+ require 'facets/module/name'
31
+ require 'facets/kernel/silence'
32
+
33
+ # = Cut
34
+ #
35
+ # Cuts are transparent subclasses. Thay are the basis of
36
+ # Cut-based AOP. The general idea of Cut-based AOP is that
37
+ # the Cut can serve a clean container for customized advice on
38
+ # top of which more sophisticated AOP systems can be built.
39
+ #
40
+ # == Examples
41
+ #
42
+ # The basic usage is:
43
+ #
44
+ # class X
45
+ # def x; "x"; end
46
+ # end
47
+ #
48
+ # cut :C < X do
49
+ # def x; '{' + super + '}'; end
50
+ # end
51
+ #
52
+ # X.new.x #=> "{x}"
53
+ #
54
+ # To use this in an AOP fashion you can define an Aspect, as a class
55
+ # or function module, and tie it together with the Cut.
56
+ #
57
+ # module LogAspect
58
+ # extend self
59
+ # def log(meth, result)
60
+ # ...
61
+ # end
62
+ # end
63
+ #
64
+ # cut :C < X do
65
+ # def x
66
+ # LogAspect.log(:x, r = super)
67
+ # return r
68
+ # end
69
+ # end
70
+ #
71
+ # == Implementation
72
+ #
73
+ # Cuts act as a "pre-class". Which depictively is:
74
+ #
75
+ # ACut < AClass < ASuperClass
76
+ #
77
+ # Instantiating AClass effecively instantiates ACut instead,
78
+ # but that action is effectively transparent.
79
+ #
80
+ # This is the basic model of this particluar implementation:
81
+ #
82
+ # class Klass
83
+ # def x; "x"; end
84
+ # end
85
+ #
86
+ # cut KlassCut < Klass
87
+ # def x; '{' + super + '}'; end
88
+ # end
89
+ #
90
+ # We cut it like so:
91
+ #
92
+ # Klass = KlassCut
93
+ #
94
+ # p Klass.new.x
95
+ #
96
+ # This is simple and relatvely robust, but not 100% transparent.
97
+ # So we add some redirection methods to the cut to improve the
98
+ # transparency.
99
+ #
100
+ # Due to limitation in meta-programming Ruby as this level, the
101
+ # transparency isn't perfect, but it's fairly close, and we continue
102
+ # to improve it.
103
+
104
+ class Cut
105
+
106
+ def self.new(klass, &block)
107
+ cut = Class.new(klass, &block) # <-- This is the actual cut.
108
+
109
+ #cut.class_eval(&block)
110
+
111
+ cut.send(:include, Transparency)
112
+ cut.extend MetaTransparency
113
+
114
+ # TODO Shutdown warning
115
+ silence_warnings do
116
+ klass.modspace::const_set(klass.basename, cut)
117
+ end
118
+
119
+ return cut
120
+ end
121
+
122
+ # These methods are needed to emulate full transparancy as
123
+ # closely as possible.
124
+
125
+ module Transparency
126
+ def methods(all=true)
127
+ self.class.superclass.instance_methods(all)
128
+ end
129
+ def public_methods(all=true)
130
+ self.class.superclass.public_instance_methods(all)
131
+ end
132
+ def private_methods(all=true)
133
+ self.class.superclass.private_instance_methods(all)
134
+ end
135
+ def protected_methods(all=true)
136
+ self.class.superclass.protected_instance_methods(all)
137
+ end
138
+ end
139
+
140
+ # These methods are needed to emulate full transparancy as
141
+ # closely as possible.
142
+
143
+ module MetaTransparency
144
+ #def instance_method(name) ; p "XXXX"; superclass.instance_method(name) ; end
145
+ def define_method(*a,&b) ; superclass.define_method(*a,&b) ; end
146
+ def module_eval(*a,&b) ; superclass.module_eval(*a,&b) ; end
147
+ def class_eval(*a,&b) ; superclass.class_eval(*a,&b) ; end
148
+ end
149
+
150
+ end
151
+
152
+
153
+ class Symbol
154
+ #alias :_op_lt_without_cuts :<
155
+
156
+ # A little tick to simulate subclassing literal syntax.
157
+
158
+ def <(klass)
159
+ if Class === klass
160
+ [self,klass]
161
+ else
162
+ raise NoMethodError, "undefined method `<' for :#{self}:Symbol"
163
+ #_op_lt_without_cuts(cut_class)
164
+ end
165
+ end
166
+ end
167
+
168
+
169
+ module Kernel
170
+ # Cut convienence method.
171
+
172
+ def cut(klass, &block)
173
+ case klass
174
+ when Array
175
+ name, klass = *klass
176
+ else
177
+ name = nil
178
+ end
179
+
180
+ cut = Cut.new(klass, &block)
181
+
182
+ # How to handle main, but not other instance spaces?
183
+ #klass.modspace::const_set(klass.basename, cut)
184
+ mod = (Module === self ? self : Object)
185
+ mod.const_set(cutname, cut) # <<- this is what we don't have in Cut.new
186
+
187
+ return cut
188
+ end
189
+ end
190
+