cuts 0.0.1

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/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
+