cuts 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +16 -0
- data/COPYING +674 -0
- data/NOTES +6 -0
- data/README +51 -0
- data/Rakefile +2 -0
- data/lib/cuts.rb +2 -0
- data/lib/cuts/aop.rb +205 -0
- data/lib/cuts/cut.rb +190 -0
- data/meta/project.yaml +23 -0
- data/meta/unixname +1 -0
- data/meta/version +1 -0
- data/setup.rb +1467 -0
- data/test/template.rb +16 -0
- metadata +72 -0
data/NOTES
ADDED
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
|
+
|
data/Rakefile
ADDED
data/lib/cuts.rb
ADDED
data/lib/cuts/aop.rb
ADDED
@@ -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
|
data/lib/cuts/cut.rb
ADDED
@@ -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
|
+
|