aquarium 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +4 -0
- data/EXAMPLES.rd +4 -0
- data/MIT-LICENSE +20 -0
- data/README +250 -0
- data/RELEASE-PLAN +1 -0
- data/Rakefile +236 -0
- data/UPGRADE +3 -0
- data/examples/aspect_design_example.rb +36 -0
- data/examples/design_by_contract_example.rb +88 -0
- data/examples/method_missing_example.rb +44 -0
- data/examples/method_tracing_example.rb +64 -0
- data/lib/aquarium.rb +7 -0
- data/lib/aquarium/aspects.rb +6 -0
- data/lib/aquarium/aspects/advice.rb +189 -0
- data/lib/aquarium/aspects/aspect.rb +577 -0
- data/lib/aquarium/aspects/default_object_handler.rb +27 -0
- data/lib/aquarium/aspects/dsl.rb +1 -0
- data/lib/aquarium/aspects/dsl/aspect_dsl.rb +61 -0
- data/lib/aquarium/aspects/join_point.rb +158 -0
- data/lib/aquarium/aspects/pointcut.rb +254 -0
- data/lib/aquarium/aspects/pointcut_composition.rb +36 -0
- data/lib/aquarium/extensions.rb +5 -0
- data/lib/aquarium/extensions/hash.rb +85 -0
- data/lib/aquarium/extensions/regexp.rb +20 -0
- data/lib/aquarium/extensions/set.rb +49 -0
- data/lib/aquarium/extensions/string.rb +13 -0
- data/lib/aquarium/extensions/symbol.rb +22 -0
- data/lib/aquarium/extras.rb +4 -0
- data/lib/aquarium/extras/design_by_contract.rb +64 -0
- data/lib/aquarium/finders.rb +4 -0
- data/lib/aquarium/finders/finder_result.rb +121 -0
- data/lib/aquarium/finders/method_finder.rb +228 -0
- data/lib/aquarium/finders/object_finder.rb +74 -0
- data/lib/aquarium/finders/type_finder.rb +127 -0
- data/lib/aquarium/utils.rb +9 -0
- data/lib/aquarium/utils/array_utils.rb +29 -0
- data/lib/aquarium/utils/hash_utils.rb +28 -0
- data/lib/aquarium/utils/html_escaper.rb +17 -0
- data/lib/aquarium/utils/invalid_options.rb +9 -0
- data/lib/aquarium/utils/method_utils.rb +18 -0
- data/lib/aquarium/utils/nil_object.rb +13 -0
- data/lib/aquarium/utils/set_utils.rb +32 -0
- data/lib/aquarium/version.rb +30 -0
- data/rake_tasks/examples.rake +7 -0
- data/rake_tasks/examples_specdoc.rake +8 -0
- data/rake_tasks/examples_with_rcov.rake +8 -0
- data/rake_tasks/verify_rcov.rake +7 -0
- data/spec/aquarium/aspects/advice_chain_node_spec.rb +34 -0
- data/spec/aquarium/aspects/advice_spec.rb +103 -0
- data/spec/aquarium/aspects/aspect_invocation_spec.rb +111 -0
- data/spec/aquarium/aspects/aspect_spec.rb +978 -0
- data/spec/aquarium/aspects/aspect_with_nested_types_spec.rb +129 -0
- data/spec/aquarium/aspects/concurrent_aspects_spec.rb +423 -0
- data/spec/aquarium/aspects/concurrent_aspects_with_objects_and_types_spec.rb +103 -0
- data/spec/aquarium/aspects/concurrently_accessed.rb +21 -0
- data/spec/aquarium/aspects/dsl/aspect_dsl_spec.rb +514 -0
- data/spec/aquarium/aspects/join_point_spec.rb +302 -0
- data/spec/aquarium/aspects/pointcut_and_composition_spec.rb +131 -0
- data/spec/aquarium/aspects/pointcut_or_composition_spec.rb +111 -0
- data/spec/aquarium/aspects/pointcut_spec.rb +800 -0
- data/spec/aquarium/extensions/hash_spec.rb +187 -0
- data/spec/aquarium/extensions/regex_spec.rb +40 -0
- data/spec/aquarium/extensions/set_spec.rb +105 -0
- data/spec/aquarium/extensions/string_spec.rb +25 -0
- data/spec/aquarium/extensions/symbol_spec.rb +37 -0
- data/spec/aquarium/extras/design_by_contract_spec.rb +68 -0
- data/spec/aquarium/finders/finder_result_spec.rb +359 -0
- data/spec/aquarium/finders/method_finder_spec.rb +878 -0
- data/spec/aquarium/finders/method_sorting_spec.rb +16 -0
- data/spec/aquarium/finders/object_finder_spec.rb +230 -0
- data/spec/aquarium/finders/type_finder_spec.rb +210 -0
- data/spec/aquarium/spec_example_classes.rb +117 -0
- data/spec/aquarium/spec_helper.rb +3 -0
- data/spec/aquarium/utils/array_utils_spec.rb +47 -0
- data/spec/aquarium/utils/hash_utils_spec.rb +48 -0
- data/spec/aquarium/utils/html_escaper_spec.rb +18 -0
- data/spec/aquarium/utils/method_utils_spec.rb +50 -0
- data/spec/aquarium/utils/nil_object_spec.rb +19 -0
- data/spec/aquarium/utils/set_utils_spec.rb +60 -0
- metadata +132 -0
data/CHANGES
ADDED
data/EXAMPLES.rd
ADDED
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2007 The Aquarium Development Team
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
17
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
18
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
19
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
20
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README
ADDED
@@ -0,0 +1,250 @@
|
|
1
|
+
== Aquarium
|
2
|
+
|
3
|
+
Aquarium is a toolkit for Aspect-Oriented Programming (AOP) whose goals include:
|
4
|
+
* A powerful "pointcut" language for specifying where to apply aspects, comparable to the pointcut language in AspectJ for Java.
|
5
|
+
* Management of concurrent aspects (i.e., those acting on the same "join points").
|
6
|
+
* Adding and removing aspects dynamically.
|
7
|
+
* A user-friendly DSL.
|
8
|
+
|
9
|
+
=== Why Is an AOP Framework Useful in Ruby?
|
10
|
+
|
11
|
+
Ruby's metaprogramming facilities already provide some of the capabilities for which static-language AOP toolkits like AspectJ are typically used. With Ruby, you can easily add new methods and attributes to existing classes and objects. You can alias and redefine existing methods, which provides the method interception and "wrapping" needed to extend or modify existing behavior.
|
12
|
+
|
13
|
+
However, what is missing in Ruby is an expressive language for describing systemic modifications, a so-called "pointcut language". If you have simple needs for method interception and wrapping, then Aquarium will be overkill. However, if you have system-wide concerns that cross the boundaries of many objects, then an AOP tookit like Aquarium can help you implement these concerns in a modular way.
|
14
|
+
|
15
|
+
=== Terminology
|
16
|
+
|
17
|
+
Several terms are used in the AOP community.
|
18
|
+
|
19
|
+
* Join Point - A point of execution in a program where "advice" might invoked.
|
20
|
+
* Pointcut - (yes, one word...) A set of join points, like a query over all join points in the system.
|
21
|
+
* Advice - The behavior invoked at a join point. There are several kinds of advice:
|
22
|
+
* Before advice - Advice invoked before the actual join point is invoked.
|
23
|
+
* After returning advice - Advice invoked after the join point executes successfully.
|
24
|
+
* After raising advice - Advice invoked only if the join point raises an exception.
|
25
|
+
* After advice - Advice invoked after the join point executes successfully or raises an exception.
|
26
|
+
* Around advice - Advice invoked instead of the join point. The around advice must choose whether or not to invoke the join point by calling a special "proceed" method. Otherwise, the join point is NOT executed.
|
27
|
+
|
28
|
+
Only around advice can prevent execution of the join point, except for the special case where before advice raises an exception.
|
29
|
+
|
30
|
+
=== Known Limitations
|
31
|
+
|
32
|
+
* Concurrent type- and object-based advice can't be removed cleanly.
|
33
|
+
* See also the comparison with AspectJ behavior next.
|
34
|
+
* The API and wrapper DSL will probably evolve until the 1.0.0 release. Backwards compatibility will be maintained between releases as much as possible and translation tools will be provided, when necessary.
|
35
|
+
|
36
|
+
=== Differences With Other Ruby AOP Toolkits
|
37
|
+
|
38
|
+
There are several other AOP toolkits for Ruby that precede Aquarium. The most notable are AspectR and the aspect capabilities in the Facets toolkit. There are also Ruby 2.0 proposals to add method wrappers for "before", "after" and "wrap" behavior.
|
39
|
+
|
40
|
+
The goal of Aquarium is to provide a superset of the functionality provided by these other toolkits. Aquarium is suitable for non-trivial and large-scale aspect-oriented components in systems. Aquarium will be most valuable for systems where aspects might be added and removed dynamically at runtime and systems where nontrivial pointcut descriptions are needed, requiring a full-featured pointcut language (as discussed elsewhere...). For less demanding needs, the alternatives are lighter weight and hence may be more appropriate.
|
41
|
+
|
42
|
+
=== Differences With AspectJ Behavior
|
43
|
+
|
44
|
+
Many of AspectJ's behaviors that aren't currently supported are planned for future releases.
|
45
|
+
|
46
|
+
* Attribute reading and writing join points are not supported. The :attribute(s) Aspect options are convenience wrappers for the corresponding accessor methods.
|
47
|
+
* At this time, the pointcut language supported by Aquarium is not nearly as feature-rich as AspectJ's language. For example, there are no runtime pointcut designators supported, such as "if" conditions and "cflow" (context flow). Most of AspectJ pointcut language features are planned, however.
|
48
|
+
* While AspectJ provides "intertype declaration" capabilities (i.e., adding state and behavior to existing classes), Ruby's native metaprogramming support satisfies this need. There may be convenience hooks added in a future release, however.
|
49
|
+
* User defined advice precedence is not supported. However, advice precedence is unambiguous; the last aspects created as modules are loaded at runtime have higher precedence than earlier aspects. Ensuring a particular order is not always easy, of course.
|
50
|
+
* Unlike AspectJ, Aquarium can advise individual objects, can remove advice, and it has named pointcuts that can be defined separately from aspects.
|
51
|
+
|
52
|
+
=== Examples
|
53
|
+
|
54
|
+
Several complete examples are provided in the "examples" directory.
|
55
|
+
|
56
|
+
In most cases, you can either declare the appropriate classes or use the optional DSL, which adds methods to Object.
|
57
|
+
|
58
|
+
Here is an example that traces invocations of all public instance methods of the classes or modules Foo and Bar.
|
59
|
+
|
60
|
+
require 'aquarium'
|
61
|
+
Aspect.new :around, :types => [Foo, Bar], :methods => :all do |execution_point, *args|
|
62
|
+
p "Entering: #{execution_point.type.name}##{execution_point.method_name}"
|
63
|
+
execution_point.proceed
|
64
|
+
p "Leaving: #{execution_point.type.name}##{execution_point.method_name}"
|
65
|
+
end
|
66
|
+
|
67
|
+
The advice to execute at each join point is the block. The pointcut is the set of all public instance methods in Foo and Bar. (There are additional options available for specifying class methods, protected methods, etc.) Here is the same example using the convenience DSL that adds aspectual behavior to Object.
|
68
|
+
|
69
|
+
require 'aquarium'
|
70
|
+
around :types => [Foo, Bar], :methods => :all do |execution_point, *args|
|
71
|
+
p "Entering: #{execution_point.type.name}##{execution_point.method_name}"
|
72
|
+
execution_point.proceed
|
73
|
+
p "Leaving: #{execution_point.type.name}##{execution_point.method_name}"
|
74
|
+
end
|
75
|
+
|
76
|
+
See "examples/method_tracing_example.rb" for a more detailed version of this example.
|
77
|
+
|
78
|
+
If you use the DSL inside a class and omit the :type(s) and :object(s) options, "self" is assumed.
|
79
|
+
|
80
|
+
class Foo
|
81
|
+
...
|
82
|
+
def critical_operation *args
|
83
|
+
...
|
84
|
+
end
|
85
|
+
end
|
86
|
+
...
|
87
|
+
class Foo
|
88
|
+
around :critical_operation do |execution_point, *args|
|
89
|
+
p "Entering: Foo#critical_operation"
|
90
|
+
execution_point.proceed
|
91
|
+
p "Leaving: Foo#critical_operation"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
Here are some more succinct examples, illustrating the API.
|
96
|
+
|
97
|
+
You can pass in pointcuts defined elsewhere:
|
98
|
+
|
99
|
+
my_pointcut = Pointcut.new :types => /Foo::Bar::/, :methods => /^do_/
|
100
|
+
around :pointcuts => my_pointcut do |jp, *args| ... # Pass in a pointcut
|
101
|
+
around :pointcuts => [my_pointcut, ...] do |jp, *args| ... # Pass in a pointcut array
|
102
|
+
|
103
|
+
You can specify a single type, a type name, a type regular expression, or an array of the same. Note that :type and :types are synonymous.
|
104
|
+
|
105
|
+
around :type = A, ...
|
106
|
+
around :types => [A, B, ...], ...
|
107
|
+
around :types => /A::.*Helper$/, ...
|
108
|
+
around :types => [/A::.*Helper$/, /B::Foo.*/], ...
|
109
|
+
|
110
|
+
You can specify a single object or an array of objects. Note that :object and :objects are synonymous. If no types or objects are specified, the object defaults to "self".
|
111
|
+
|
112
|
+
a1 = A.new
|
113
|
+
a2 = A.new
|
114
|
+
around :object = a1, ...
|
115
|
+
around :objects => [a1, a2], ...
|
116
|
+
|
117
|
+
You can specify a single method symbol (name), a regular expression, or an array of the same. Note that :all is a special keyword meaning all methods and :method and :methods are synonymous.
|
118
|
+
|
119
|
+
around :method = :all, ...
|
120
|
+
around :method = :foo, ...
|
121
|
+
around :methods = [:foo, :bar, :baz], ...
|
122
|
+
around :methods = /^foo/, ...
|
123
|
+
around :methods = [/^foo/, /bar$/], ...
|
124
|
+
|
125
|
+
You can specify a method options. By default, public instance methods only are matched.
|
126
|
+
|
127
|
+
around :methods = /foo/, :method_options => [:instance], ... # match instance methods (default)
|
128
|
+
around :methods = /foo/, :method_options => [:class], ... # match class methods
|
129
|
+
around :methods = /foo/, :method_options => [:public, :protected, :private], ...
|
130
|
+
# match public, protected, and private instance methods
|
131
|
+
around :methods = /foo/, :method_options => [:singleton], ... # match singleton methods
|
132
|
+
|
133
|
+
You can specify attributes, which are actually convenience methods for the attribute accessors. They work
|
134
|
+
very much like the :method options. Note that :all is NOT supported in this case and :attribute and :attributes are synonymous.
|
135
|
+
|
136
|
+
around :attribute = :foo, ... # defaults to methods #foo and #foo=
|
137
|
+
around :attribute = :foo, :attribute_options => [:readers]... # only matches #foo
|
138
|
+
around :attribute = :foo, :attribute_options => [:writers]... # only matches #foo=
|
139
|
+
around :attributes = [:foo, :bar, :baz], ...
|
140
|
+
around :attributes = /^foo/, ...
|
141
|
+
around :attributes = [/^foo/, /bar$/], ...
|
142
|
+
|
143
|
+
You can advice methods before execution:
|
144
|
+
|
145
|
+
before :types => ...
|
146
|
+
|
147
|
+
You can advice methods after returning successfully (i.e., no exceptions were raised):
|
148
|
+
|
149
|
+
after_returning :types => ...
|
150
|
+
after_returning_from :types => ...
|
151
|
+
|
152
|
+
You can advice methods after raising exceptions:
|
153
|
+
|
154
|
+
after_raising :types => ... # After any exception is thrown
|
155
|
+
after_raising_within :types => ...
|
156
|
+
after_raising => MyError, :types => ... # Only invoke advice if "MyError" is raised.
|
157
|
+
after_raising => [MyError1, MyError2], :types => ...
|
158
|
+
# Only invoke advice if "MyError1" or "MyError2" is raised.
|
159
|
+
|
160
|
+
You can advice methods after returning successfully or raising exceptions. (You can't specify
|
161
|
+
a set of exceptions in this case.):
|
162
|
+
|
163
|
+
after :types => ...
|
164
|
+
after_raising_within_or_returning_from : types =>
|
165
|
+
|
166
|
+
You can advice methods both before after. This is different from around advice, where the advice has to explicitly invoke the join point (using JoinPoint#proceed). Rather, these methods are convenience wrappers
|
167
|
+
around the creation of before advice and the corresponding after advice.
|
168
|
+
|
169
|
+
before_and_after :types =>, ...
|
170
|
+
before_and_after_returning :types =>, ...
|
171
|
+
before_and_after_returning_from :types =>, ...
|
172
|
+
before_and_after_raising :types =>, ...
|
173
|
+
before_and_after_raising_within :types =>, ...
|
174
|
+
before_and_after_raising_within_or_returning_from :types =>, ...
|
175
|
+
|
176
|
+
You can pass a block as the advice. Notice that all advice blocks and Procs (see below) are required to accept two arguments, the JoinPoint, which will contain useful context information, and "*args", which will contain the parameters used when invoking the join point (method). It is an error if no block is specified.
|
177
|
+
|
178
|
+
around :type => [...], :methods => :all do |join_point, *args|
|
179
|
+
advice_to_execute_before_the_jp
|
180
|
+
join_point.proceed # Invoke the join point, passing *args implicitly (you can override...)
|
181
|
+
advice_to_execute_after_the_jp
|
182
|
+
end
|
183
|
+
around(:type => [...], :methods => :all) {|join_point, *args| ...} # (...) necessary for precedence...
|
184
|
+
|
185
|
+
Rather than passing a block as the advice, you can pass a previously-created Proc:
|
186
|
+
|
187
|
+
around :type => [...], :methods => :all, :advice => advice
|
188
|
+
around :type => [...], :methods => :all, :advise_with => advice # synonym for advice. Note the "s"!
|
189
|
+
around :type => [...], :methods => :all, :call => advice # synonym for advice.
|
190
|
+
around :type => [...], :methods => :all, :invoke => advice # synonym for advice.
|
191
|
+
|
192
|
+
=== Packages
|
193
|
+
|
194
|
+
Aquarium::Aspects contains the Aspect class and supporting classes Pointcut, JoinPoint, etc.
|
195
|
+
|
196
|
+
Aquarium::Finders provides tools for locating types, objects, and methods in the runtime, using names, symbols, or regular expressions.
|
197
|
+
|
198
|
+
Aquarium::Extensions provides extensions to several Ruby core library routines.
|
199
|
+
|
200
|
+
Aquarium::Utils provides general-purpose utilities for manipulating Strings, Sets, Hashes, etc. as well as some generic types.
|
201
|
+
|
202
|
+
Aquarium::Extras provides add-ons for Aquarium, such as a Design by Contract implementation. These extras are NOT included when you require the general 'aquarium.rb' file. You have to explicitly include 'aquarium/extras' or one of the 'aquarium/extras/*' if you want to use them.
|
203
|
+
|
204
|
+
== Installation
|
205
|
+
|
206
|
+
The simplest approach is to install the gem:
|
207
|
+
|
208
|
+
gem install -r aquarium # sudo may be required
|
209
|
+
|
210
|
+
== Building the Aquarium gem
|
211
|
+
|
212
|
+
If you prefer to build the gem locally, check out source from svn://rubyforge.org/var/svn/aquarium/trunk. Then
|
213
|
+
do the following:
|
214
|
+
|
215
|
+
rake gem
|
216
|
+
gem install pkg/aquarium-x.y.z.gem # sudo may be required
|
217
|
+
|
218
|
+
== Running Aquarium's RSpec Specs
|
219
|
+
|
220
|
+
In order to run Aquarium's full suite of specs (rake pre_commit) you must install the following gems:
|
221
|
+
|
222
|
+
* rake # Runs the build script
|
223
|
+
* rspec # Used instead of Test::Unit for TDD
|
224
|
+
* rcov # Verifies that the code is 100% covered by specs
|
225
|
+
* webgen # Generates the static HTML website
|
226
|
+
* RedCloth # Required by webgen
|
227
|
+
* syntax # Required by RSpec's custom webgen extension to highlight ruby code
|
228
|
+
* diff-lcs # Required if you use the --diff switch
|
229
|
+
* win32console # Required by the --colour switch if you're on Windows
|
230
|
+
* meta_project # Required in order to make releases at RubyForge
|
231
|
+
* heckle # Required if you use the --heckle switch
|
232
|
+
|
233
|
+
Once those are all installed, you should be able to run the suite with the following steps:
|
234
|
+
|
235
|
+
* svn co svn://rubyforge.org/var/svn/aquarium/trunk aquarium
|
236
|
+
* cd aquarium
|
237
|
+
* rake spec
|
238
|
+
or
|
239
|
+
* rake spec_rcov # also runs rcov
|
240
|
+
|
241
|
+
Note that Aquarium itself - once built - doesn't have any dependencies outside the Ruby core and stdlib.
|
242
|
+
|
243
|
+
See http://aquarium.rubyforge.org for further documentation.
|
244
|
+
|
245
|
+
=== Acknowledgments
|
246
|
+
|
247
|
+
My colleagues in the AOSD community, in particular those who developed AspectJ, have been a big inspiration.
|
248
|
+
The RSpec team, in particular David Chelimsky, have really inspired my thinking about what's possible in Ruby, especially in the realm of DSLs. I also cribbed parts of the RSpec Rake process ;)
|
249
|
+
My colleagues at Object Mentor are an endless source of insight and inspiration.
|
250
|
+
|
data/RELEASE-PLAN
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
TODO:
|
data/Rakefile
ADDED
@@ -0,0 +1,236 @@
|
|
1
|
+
$:.unshift('lib')
|
2
|
+
require 'rubygems'
|
3
|
+
require 'rake'
|
4
|
+
require 'rake/gempackagetask'
|
5
|
+
require 'rake/contrib/rubyforgepublisher'
|
6
|
+
require 'rake/clean'
|
7
|
+
require 'rake/rdoctask'
|
8
|
+
require 'aquarium/version'
|
9
|
+
|
10
|
+
# Use RSpec's files.
|
11
|
+
require 'spec/rake/spectask'
|
12
|
+
require 'spec/rake/verify_rcov'
|
13
|
+
|
14
|
+
# Some of the tasks are in separate files since they are also part of the website documentation
|
15
|
+
load File.dirname(__FILE__) + '/rake_tasks/examples.rake'
|
16
|
+
load File.dirname(__FILE__) + '/rake_tasks/verify_rcov.rake'
|
17
|
+
|
18
|
+
PKG_NAME = "aquarium"
|
19
|
+
PKG_VERSION = Aquarium::VERSION::STRING
|
20
|
+
PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
|
21
|
+
PKG_FILES = FileList[
|
22
|
+
'[A-Z]*',
|
23
|
+
'lib/**/*.rb',
|
24
|
+
'spec/**/*.rb',
|
25
|
+
'examples/**/*.rb',
|
26
|
+
'rake_tasks/**/*.rake'
|
27
|
+
]
|
28
|
+
FileUtils.touch(File.dirname(__FILE__) + '/previous_failures.txt')
|
29
|
+
|
30
|
+
task :default => [:verify_rcov]
|
31
|
+
|
32
|
+
desc "Run all specs"
|
33
|
+
Spec::Rake::SpecTask.new do |t|
|
34
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
35
|
+
t.spec_opts = ['--options', 'spec.opts']
|
36
|
+
t.rcov = true
|
37
|
+
t.rcov_dir = '../doc/output/coverage'
|
38
|
+
t.rcov_opts = ['--exclude', 'examples']
|
39
|
+
end
|
40
|
+
|
41
|
+
desc "Run all specs and store html output in doc/output/report.html"
|
42
|
+
Spec::Rake::SpecTask.new('spec_html') do |t|
|
43
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
44
|
+
t.spec_opts = ['--format html:../doc/output/report.html','--backtrace']
|
45
|
+
end
|
46
|
+
|
47
|
+
desc 'Generate HTML documentation for website'
|
48
|
+
task :webgen do
|
49
|
+
Dir.chdir '../doc' do
|
50
|
+
output = nil
|
51
|
+
IO.popen('webgen 2>&1') do |io|
|
52
|
+
output = io.read
|
53
|
+
end
|
54
|
+
raise "ERROR while running webgen: #{output}" if output =~ /ERROR/n || $? != 0
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
desc 'Generate RDoc'
|
59
|
+
rd = Rake::RDocTask.new do |rdoc|
|
60
|
+
rdoc.rdoc_dir = '../doc/output/rdoc'
|
61
|
+
rdoc.options << '--title' << 'Aquarium' << '--line-numbers' << '--inline-source' << '--main' << 'README'
|
62
|
+
rdoc.rdoc_files.include('README', 'CHANGES', 'MIT-LICENSE', 'examples/**/*.rb', 'UPGRADE', 'lib/**/*.rb') # 'EXAMPLES.rd'
|
63
|
+
end
|
64
|
+
|
65
|
+
# desc "Generate EXAMPLES.rb"
|
66
|
+
# task :rdoc do
|
67
|
+
# core.rdoc
|
68
|
+
# end
|
69
|
+
|
70
|
+
aquarium = Gem::Specification.new do |s|
|
71
|
+
s.name = PKG_NAME
|
72
|
+
s.version = PKG_VERSION
|
73
|
+
s.summary = Aquarium::VERSION::DESCRIPTION
|
74
|
+
s.description = <<-EOF
|
75
|
+
Aquarium is a full-featured Aspect-Oriented Programming (AOP) framework for Ruby that is
|
76
|
+
designed to provide an intuitive syntax and support for large-scale, dynamic aspects.
|
77
|
+
EOF
|
78
|
+
|
79
|
+
s.files = PKG_FILES.to_a
|
80
|
+
s.require_path = 'lib'
|
81
|
+
|
82
|
+
s.has_rdoc = true
|
83
|
+
s.rdoc_options = rd.options
|
84
|
+
s.extra_rdoc_files = rd.rdoc_files.reject { |fn| fn =~ /\.rb$|^EXAMPLES.rd$/ }.to_a
|
85
|
+
|
86
|
+
s.autorequire = 'aquarium'
|
87
|
+
s.bindir = 'bin'
|
88
|
+
s.executables = []
|
89
|
+
s.default_executable = ''
|
90
|
+
s.author = ["Aquarium Development Team"]
|
91
|
+
s.email = "aquarium-devel@rubyforge.org"
|
92
|
+
s.homepage = "http://aquarium.rubyforge.org"
|
93
|
+
s.rubyforge_project = "aquarium"
|
94
|
+
end
|
95
|
+
|
96
|
+
Rake::GemPackageTask.new(aquarium) do |pkg|
|
97
|
+
pkg.need_zip = true
|
98
|
+
pkg.need_tar = true
|
99
|
+
end
|
100
|
+
|
101
|
+
def egrep(pattern)
|
102
|
+
Dir['**/*.rb'].each do |fn|
|
103
|
+
count = 0
|
104
|
+
open(fn) do |f|
|
105
|
+
while line = f.gets
|
106
|
+
count += 1
|
107
|
+
if line =~ pattern
|
108
|
+
puts "#{fn}:#{count}:#{line}"
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
desc "Look for TODO and FIXME tags in the code"
|
116
|
+
task :todo do
|
117
|
+
egrep /(FIXME|TODO|TBD)/
|
118
|
+
end
|
119
|
+
|
120
|
+
task :clobber do
|
121
|
+
rm_rf '../doc/output'
|
122
|
+
rm_rf 'translated_specs'
|
123
|
+
end
|
124
|
+
|
125
|
+
task :release => [:clobber, :verify_committed, :verify_user, :spec, :publish_packages, :tag, :publish_website, :publish_news]
|
126
|
+
|
127
|
+
desc "Verifies that there is no uncommitted code"
|
128
|
+
task :verify_committed do
|
129
|
+
IO.popen('svn stat') do |io|
|
130
|
+
io.each_line do |line|
|
131
|
+
raise "\n!!! Do a svn commit first !!!\n\n" if line =~ /^\s*M\s*/
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
desc "Creates a tag in svn"
|
137
|
+
task :tag do
|
138
|
+
from = `svn info #{File.dirname(__FILE__)}`.match(/URL: (.*)\/aquarium/n)[1]
|
139
|
+
to = from.gsub(/trunk/, "tags/#{Aquarium::VERSION::TAG}")
|
140
|
+
tag_cmd = "svn cp #{from} #{to} -m \"Tag release #{Aquarium::VERSION::FULL_VERSION}\""
|
141
|
+
raise "Can't tag to the same place: #{tag_cmd}" if to == from
|
142
|
+
puts "Creating tag in SVN"
|
143
|
+
`#{tag_cmd}`
|
144
|
+
raise "Tagging failed" unless $? == 0
|
145
|
+
end
|
146
|
+
|
147
|
+
# TODO: adapt from rspec.
|
148
|
+
# desc "Run this task before you commit. You should see 'OK TO COMMIT'"
|
149
|
+
# task :pre_commit => [
|
150
|
+
# :website,
|
151
|
+
# :examples
|
152
|
+
# ]
|
153
|
+
|
154
|
+
desc "Build the website, but do not publish it"
|
155
|
+
task :website => [:clobber, :verify_rcov, :spec_html, :webgen, :rdoc]
|
156
|
+
|
157
|
+
task :rdoc_rails do
|
158
|
+
Dir.chdir '../aquarium_on_rails' do
|
159
|
+
rake = (PLATFORM == "i386-mswin32") ? "rake.cmd" : "rake"
|
160
|
+
`#{rake} rdoc`
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
task :verify_user do
|
165
|
+
raise "RUBYFORGE_USER environment variable not set!" unless ENV['RUBYFORGE_USER']
|
166
|
+
end
|
167
|
+
|
168
|
+
desc "Upload Website to RubyForge"
|
169
|
+
task :publish_website => [:verify_user, :website] do
|
170
|
+
unless Aquarium::VERSION::RELEASE_CANDIDATE
|
171
|
+
publisher = Rake::SshDirPublisher.new(
|
172
|
+
"aquarium-website@rubyforge.org",
|
173
|
+
"/var/www/gforge-projects/#{PKG_NAME}",
|
174
|
+
"../doc/output"
|
175
|
+
)
|
176
|
+
publisher.upload
|
177
|
+
else
|
178
|
+
puts "** Not publishing packages to RubyForge - this is a prerelease"
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
desc "Publish gem+tgz+zip on RubyForge. You must make sure lib/version.rb is aligned with the CHANGELOG file"
|
183
|
+
task :publish_packages => [:verify_user, :package] do
|
184
|
+
release_files = FileList[
|
185
|
+
"pkg/#{PKG_FILE_NAME}.gem",
|
186
|
+
"pkg/#{PKG_FILE_NAME}.tgz",
|
187
|
+
"pkg/#{PKG_FILE_NAME}.zip"
|
188
|
+
]
|
189
|
+
unless Aquarium::VERSION::RELEASE_CANDIDATE
|
190
|
+
require 'meta_project'
|
191
|
+
require 'rake/contrib/xforge'
|
192
|
+
|
193
|
+
Rake::XForge::Release.new(MetaProject::Project::XForge::RubyForge.new(PKG_NAME)) do |xf|
|
194
|
+
# Never hardcode user name and password in the Rakefile!
|
195
|
+
xf.user_name = ENV['RUBYFORGE_USER']
|
196
|
+
xf.files = release_files.to_a
|
197
|
+
xf.release_name = "Aquarium #{PKG_VERSION}"
|
198
|
+
end
|
199
|
+
else
|
200
|
+
puts "SINCE THIS IS A PRERELEASE, FILES ARE UPLOADED WITH SSH, NOT TO THE RUBYFORGE FILE SECTION"
|
201
|
+
puts "YOU MUST TYPE THE PASSWORD #{release_files.length} TIMES..."
|
202
|
+
|
203
|
+
host = "aquarium-website@rubyforge.org"
|
204
|
+
remote_dir = "/var/www/gforge-projects/#{PKG_NAME}"
|
205
|
+
|
206
|
+
publisher = Rake::SshFilePublisher.new(
|
207
|
+
host,
|
208
|
+
remote_dir,
|
209
|
+
File.dirname(__FILE__),
|
210
|
+
*release_files
|
211
|
+
)
|
212
|
+
publisher.upload
|
213
|
+
|
214
|
+
puts "UPLADED THE FOLLOWING FILES:"
|
215
|
+
release_files.each do |file|
|
216
|
+
name = file.match(/pkg\/(.*)/)[1]
|
217
|
+
puts "* http://aquarium.rubyforge.org/#{name}"
|
218
|
+
end
|
219
|
+
|
220
|
+
puts "They are not linked to anywhere, so don't forget to tell people!"
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
desc "Publish news on RubyForge"
|
225
|
+
task :publish_news => [:verify_user] do
|
226
|
+
unless Aquarium::VERSION::RELEASE_CANDIDATE
|
227
|
+
require 'meta_project'
|
228
|
+
require 'rake/contrib/xforge'
|
229
|
+
Rake::XForge::NewsPublisher.new(MetaProject::Project::XForge::RubyForge.new(PKG_NAME)) do |news|
|
230
|
+
# Never hardcode user name and password in the Rakefile!
|
231
|
+
news.user_name = ENV['RUBYFORGE_USER']
|
232
|
+
end
|
233
|
+
else
|
234
|
+
puts "** Not publishing news to RubyForge - this is a prerelease"
|
235
|
+
end
|
236
|
+
end
|