teejayvanslyke-gazer 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/History.txt ADDED
@@ -0,0 +1,4 @@
1
+ == 0.0.1 2009-02-14
2
+
3
+ * 1 major enhancement:
4
+ * Initial release
data/Manifest.txt ADDED
@@ -0,0 +1,13 @@
1
+ History.txt
2
+ Manifest.txt
3
+ PostInstall.txt
4
+ README.rdoc
5
+ Rakefile
6
+ lib/gazer.rb
7
+ script/console
8
+ script/destroy
9
+ script/generate
10
+ spec/gazer_spec.rb
11
+ spec/spec.opts
12
+ spec/spec_helper.rb
13
+ tasks/rspec.rake
data/PostInstall.txt ADDED
@@ -0,0 +1,7 @@
1
+
2
+ For more information on gazer, see http://gazer.rubyforge.org
3
+
4
+ NOTE: Change this information in PostInstall.txt
5
+ You can also delete it if you don't want it.
6
+
7
+
data/README.rdoc ADDED
@@ -0,0 +1,79 @@
1
+ = gazer
2
+
3
+ * http://github.com/teejayvanslyke/gazer
4
+
5
+ == DESCRIPTION:
6
+
7
+ Poor man's aspect-oriented programming for Ruby.
8
+
9
+ == FEATURES/PROBLEMS:
10
+
11
+ * Elegant DSL
12
+ * before & after pointcuts.
13
+ * Rails integration.
14
+
15
+ == ROADMAP:
16
+
17
+ * Locking down Object monkeypatching scheme.
18
+ * More selectors and pointcuts.
19
+ * Regular-expression-based pointcuts.
20
+ * <tt>:only</tt> and <tt>except</tt> options for Rails helper.
21
+
22
+ == SYNOPSIS:
23
+
24
+ Gazer's DSL is accessible to subclasses of Gazer::Aspect::Base.
25
+
26
+ class LoggingAspect < Gazer::Aspect::Base
27
+
28
+ before instances_of(Article) => :create do |point|
29
+ puts "Creating article"
30
+ end
31
+
32
+ after instances_of([Article, Comment]) => :save do |point|
33
+ puts "Saved #{point.object}."
34
+ end
35
+
36
+ end
37
+
38
+ I have also provided a Rails generator for generating aspects for
39
+ your project. Simply execute the following:
40
+
41
+ $ script/generate aspect logging
42
+
43
+ A template will be created at <tt>app/aspects/logging_aspect.rb</tt>
44
+ for your aspect-oriented pleasure.
45
+
46
+ To include an aspect for a given controller, use the aspect class method:
47
+
48
+ class MyController < ApplicationController
49
+ aspect :logging
50
+ end
51
+
52
+ == INSTALL:
53
+
54
+ $ sudo gem install teejayvanslyke-gazer
55
+
56
+ == LICENSE:
57
+
58
+ (The MIT License)
59
+
60
+ Copyright (c) 2009 T.J. VanSlyke
61
+
62
+ Permission is hereby granted, free of charge, to any person obtaining
63
+ a copy of this software and associated documentation files (the
64
+ 'Software'), to deal in the Software without restriction, including
65
+ without limitation the rights to use, copy, modify, merge, publish,
66
+ distribute, sublicense, and/or sell copies of the Software, and to
67
+ permit persons to whom the Software is furnished to do so, subject to
68
+ the following conditions:
69
+
70
+ The above copyright notice and this permission notice shall be
71
+ included in all copies or substantial portions of the Software.
72
+
73
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
74
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
75
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
76
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
77
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
78
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
79
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/VERSION.yml ADDED
@@ -0,0 +1,4 @@
1
+ ---
2
+ :patch: 1
3
+ :major: 0
4
+ :minor: 0
data/lib/gazer.rb ADDED
@@ -0,0 +1,12 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+
5
+ module Gazer
6
+ VERSION = '0.0.1'
7
+ end
8
+
9
+ require 'gazer/object_extensions'
10
+ require 'gazer/aspect'
11
+ require 'gazer/rails' if defined?(RAILS_ENV)
12
+
@@ -0,0 +1,9 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ require 'aspect/join_point'
5
+ require 'aspect/pointcut'
6
+ require 'aspect/base'
7
+ require 'aspect/filter'
8
+ require 'aspect/instance_of'
9
+
@@ -0,0 +1,38 @@
1
+
2
+ module Gazer
3
+ module Aspect
4
+ class Base
5
+
6
+ class << self
7
+
8
+ def add_pointcut(pointcut)
9
+ @pointcuts ||= []
10
+ @pointcuts << pointcut
11
+ end
12
+
13
+ attr_accessor :pointcuts
14
+
15
+ def instances_of(klass)
16
+ InstancesOf.new(klass)
17
+ end
18
+
19
+ def apply!
20
+ pointcuts.each do |pc| pc.apply! end
21
+ end
22
+
23
+ def before(selector, &block)
24
+ add_pointcut BeforePointcut.new(selector, &block)
25
+ end
26
+
27
+ def after(selector, &block)
28
+ add_pointcut AfterPointcut.new(selector, &block)
29
+ end
30
+
31
+ def around(selector, &block)
32
+ add_pointcut AroundPointcut.new(selector, &block)
33
+ end
34
+ end
35
+
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,38 @@
1
+ module Gazer
2
+ module Aspect
3
+ class Filter
4
+ def initialize(expr)
5
+ if expr.is_a?(Array)
6
+ @types = expr
7
+ else
8
+ @types = [ expr ]
9
+ end
10
+ end
11
+
12
+ def advise_before(sym, &block)
13
+ @types.each {|t| t.advise_before(sym, &block)}
14
+ end
15
+
16
+ def advise_around(sym, &block)
17
+ @types.each {|t| t.advise_around(sym, &block)}
18
+ end
19
+
20
+ def advise_after(sym, &block)
21
+ @types.each {|t| t.advise_after(sym, &block)}
22
+ end
23
+
24
+ def advise_instances_before(sym, &block)
25
+ @types.each {|t| t.advise_instances_before(sym, &block)}
26
+ end
27
+
28
+ def advise_instances_around(sym, &block)
29
+ @types.each {|t| t.advise_instances_around(sym, &block)}
30
+ end
31
+
32
+ def advise_instances_after(sym, &block)
33
+ @types.each {|t| t.advise_instances_after(sym, &block)}
34
+ end
35
+ end
36
+ end
37
+ end
38
+
@@ -0,0 +1,21 @@
1
+ module Gazer
2
+ module Aspect
3
+ class InstancesOf
4
+ def initialize(filter)
5
+ @filter = Filter.new(filter)
6
+ end
7
+
8
+ def advise_before(sym, &block)
9
+ @filter.advise_instances_before(sym, &block)
10
+ end
11
+
12
+ def advise_around(sym, &block)
13
+ @filter.advise_instances_around(sym, &block)
14
+ end
15
+
16
+ def advise_after(sym, &block)
17
+ @filter.advise_instances_after(sym, &block)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,8 @@
1
+ require 'ostruct'
2
+
3
+ module Gazer
4
+ module Aspect
5
+ class JoinPoint < OpenStruct; end
6
+ end
7
+ end
8
+
@@ -0,0 +1,34 @@
1
+ module Gazer
2
+ module Aspect
3
+ class Pointcut
4
+ def initialize(selector, &block)
5
+ @selector = selector
6
+ @block = block
7
+ end
8
+ end
9
+
10
+ class BeforePointcut < Pointcut
11
+ def apply!
12
+ @selector.keys.each do |key|
13
+ Filter.new(key).advise_before(@selector[key], &@block)
14
+ end
15
+ end
16
+ end
17
+
18
+ class AfterPointcut < Pointcut
19
+ def apply!
20
+ @selector.keys.each do |key|
21
+ Filter.new(key).advise_after(@selector[key], &@block)
22
+ end
23
+ end
24
+ end
25
+
26
+ class AroundPointcut < Pointcut
27
+ def apply!
28
+ @selector.keys.each do |key|
29
+ Filter.new(key).advise_around(@selector[key], &@block)
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,141 @@
1
+ module Gazer
2
+
3
+ module ObjectExtensions
4
+
5
+ module ClassMethods
6
+ def advise(sym, block)
7
+ @advice ||= {}
8
+ @advice[sym] ||= []
9
+ @advice[sym] << block
10
+ end
11
+
12
+ def advice
13
+ @advice ||= {}
14
+ end
15
+
16
+ def advice_for(sym)
17
+ @advice[sym]
18
+ end
19
+
20
+ def backup_method(sym)
21
+ id = "__#{sym}_#{backup_methods_for(sym).size}__"
22
+ alias_method id, sym # Backup original method
23
+ private id # Make backup private
24
+ @backup_methods_for ||= {}
25
+ @backup_methods_for[sym] ||= []
26
+ @backup_methods_for[sym] << id
27
+ return id
28
+ end
29
+
30
+ def backup_methods_for(sym)
31
+ @backup_methods_for ||= {}
32
+ @backup_methods_for[sym] ||= []
33
+ end
34
+
35
+ def unadvise_all
36
+ advice.each do |sym, arr|
37
+ code = <<-CODE
38
+ class << self
39
+ alias_method #{sym.inspect}, '__#{sym}_0__'
40
+ public #{sym.inspect}
41
+ end
42
+ CODE
43
+ instance_eval(code)
44
+ end
45
+ end
46
+
47
+ def advise_before(sym, &block)
48
+ return unless respond_to?(sym)
49
+ advise(sym, block)
50
+ code = <<-CODE
51
+ class << self
52
+ hook = backup_method(#{sym.inspect})
53
+ define_method #{sym.inspect} do |*args|
54
+ self.advice_for(#{sym.inspect}).last.call(
55
+ Gazer::Aspect::JoinPoint.new(:object => self,
56
+ :method => #{sym.inspect},
57
+ :args => args))
58
+ __send__ hook, *args # Invoke backup
59
+ end
60
+ end
61
+ CODE
62
+ instance_eval(code)
63
+ end
64
+
65
+ def advise_around(sym, &block)
66
+ return unless respond_to?(sym)
67
+ advise(sym, block)
68
+ code = <<-CODE
69
+ class << self
70
+ hook = backup_method(#{sym.inspect})
71
+ define_method #{sym.inspect} do |*args|
72
+ self.advice_for(#{sym.inspect}).last.call(
73
+ Gazer::Aspect::JoinPoint.new(:object => self,
74
+ :method => #{sym.inspect},
75
+ :args => args))
76
+ __send__ hook, *args # Invoke backup
77
+ end
78
+ end
79
+ CODE
80
+ instance_eval(code)
81
+ end
82
+
83
+ def advise_after(sym, &block)
84
+ return unless respond_to?(sym)
85
+ advise(sym, block)
86
+ code = <<-CODE
87
+ class << self
88
+ hook = backup_method(#{sym.inspect})
89
+ define_method #{sym.inspect} do |*args|
90
+ rval = __send__ hook, *args # Invoke backup
91
+ self.advice_for(#{sym.inspect}).last.call(
92
+ Gazer::Aspect::JoinPoint.new(:object => self,
93
+ :method => #{sym.inspect},
94
+ :args => args))
95
+ return rval
96
+ end
97
+ end
98
+ CODE
99
+ instance_eval(code)
100
+ end
101
+
102
+ def advise_instances_before(sym, &block)
103
+ hook = backup_method(sym)
104
+ define_method sym do |*args|
105
+ block.call(
106
+ Gazer::Aspect::JoinPoint.new(:object => self,
107
+ :method => sym,
108
+ :args => args))
109
+ __send__ hook, *args # Invoke backup
110
+ end
111
+ end
112
+
113
+ def advise_instances_around(sym, &block)
114
+ hook = backup_method(sym)
115
+ define_method sym do |*args|
116
+ block.call(
117
+ Gazer::Aspect::JoinPoint.new(:object => self,
118
+ :method => sym,
119
+ :args => args))
120
+ __send__ hook, *args
121
+ end
122
+ end
123
+
124
+ def advise_instances_after(sym, &block)
125
+ hook = backup_method(sym)
126
+ define_method sym do |*args| # Replace method
127
+ rval = __send__ hook, *args # Invoke backup
128
+ block.call(
129
+ Gazer::Aspect::JoinPoint.new(:object => self,
130
+ :method => sym,
131
+ :args => args))
132
+ return rval
133
+ end
134
+ end
135
+ end
136
+
137
+ end
138
+
139
+ end
140
+
141
+ Object.extend(Gazer::ObjectExtensions::ClassMethods)
@@ -0,0 +1,4 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ require 'rails/action_controller'
@@ -0,0 +1,19 @@
1
+ module Gazer
2
+ module Rails
3
+ module ActionController
4
+ module ClassMethods
5
+ def aspect(*args)
6
+ args.each do |aspect|
7
+ define_method "__apply_#{aspect}_aspect__" do
8
+ eval("#{aspect.to_s.classify}Aspect.apply!")
9
+ end
10
+
11
+ before_filter "__apply_#{aspect}_aspect__"
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+
19
+ ActionController::Base.extend(Gazer::Rails::ActionController::ClassMethods)
@@ -0,0 +1,181 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+
4
+ def message(text) end
5
+ describe Gazer do
6
+
7
+ include Gazer
8
+
9
+ class Dog
10
+
11
+ def self.create
12
+ new
13
+ end
14
+
15
+ def bark!
16
+ "woof!"
17
+ end
18
+
19
+ end
20
+
21
+ class Cat
22
+ def hiss!
23
+ end
24
+
25
+ def run_away!
26
+ end
27
+
28
+ def self.get_scared
29
+ end
30
+ end
31
+
32
+ describe "advising instance methods" do
33
+
34
+ describe "before the method call" do
35
+
36
+ it "executes the advice provided before the method is executed" do
37
+
38
+ @cat = Cat.new
39
+
40
+ Dog.advise_instances_before(:bark!) do |point|
41
+ @cat.hiss!
42
+ end
43
+
44
+ @dog = Dog.new
45
+ @cat.should_receive(:hiss!)
46
+ @dog.bark!
47
+
48
+ end
49
+
50
+ end
51
+
52
+ describe "after the method call" do
53
+
54
+ it "executes the advice provided after the method is executed" do
55
+
56
+ @cat = Cat.new
57
+
58
+ Dog.advise_instances_after(:bark!) do |point|
59
+ @cat.run_away!
60
+ end
61
+
62
+ @dog = Dog.new
63
+ @cat.should_receive(:run_away!)
64
+ @dog.bark!
65
+ end
66
+
67
+ end
68
+
69
+ end
70
+
71
+ describe "advising class methods" do
72
+
73
+ describe "before the method call" do
74
+
75
+ it "executes the advice provided before the method is executed" do
76
+
77
+ Dog.unadvise_all
78
+ Cat.unadvise_all
79
+
80
+ Dog.advise_before(:create) do |point|
81
+ Cat.get_scared
82
+ end
83
+
84
+ Cat.should_receive(:get_scared)
85
+ @dog = Dog.create
86
+
87
+ end
88
+
89
+ end
90
+
91
+ describe "after the method call" do
92
+
93
+ it "executes the advice provided after the method is executed" do
94
+
95
+ Dog.unadvise_all
96
+
97
+ Dog.advise_after(:create) do |point|
98
+ Cat.get_scared
99
+ end
100
+
101
+ Cat.should_receive(:get_scared)
102
+ @dog = Dog.create
103
+
104
+ end
105
+
106
+ end
107
+
108
+ describe "after instantiating an instance already" do
109
+
110
+ before :each do
111
+ Dog.unadvise_all
112
+ Cat.unadvise_all
113
+
114
+ @dog = Dog.new
115
+ @cat = Cat.new
116
+ end
117
+
118
+ it "does not execute the advice if none has been defined" do
119
+ @cat.should_not_receive(:hiss!)
120
+ @dog.bark!
121
+ end
122
+
123
+ it "executes the advice provided on the already-instantiated instance" do
124
+ Dog.advise_instances_after(:bark!) do |point|
125
+ @cat.hiss!
126
+ end
127
+
128
+ @cat.should_receive(:hiss!)
129
+ @dog.bark!
130
+ end
131
+
132
+ end
133
+
134
+ end
135
+
136
+ describe "aspect DSL" do
137
+ class TestAspect < Gazer::Aspect::Base
138
+ before instances_of(Dog) => :bark! do |point|
139
+ Cat.get_scared
140
+ end
141
+
142
+ after instances_of(Cat) => :hiss! do |point|
143
+ Dog.new
144
+ end
145
+
146
+ before Dog => :create do |point|
147
+ message("creating #{point.object.name}")
148
+ end
149
+
150
+ def self.message(text)
151
+ end
152
+ end
153
+
154
+ before :all do
155
+ TestAspect.apply!
156
+ end
157
+
158
+ before :each do
159
+ @dog = Dog.new
160
+ @cat = Cat.new
161
+
162
+ end
163
+
164
+ it "makes the cat run away before the dog barks" do
165
+ Cat.should_receive(:get_scared)
166
+ @dog.bark!
167
+ end
168
+
169
+ it "creates a new dog after the cat hisses" do
170
+ Dog.should_receive(:new)
171
+ @cat.hiss!
172
+ end
173
+
174
+ it "messages that we are creating a new Dog" do
175
+ TestAspect.should_receive(:message).with("creating Dog")
176
+ Dog.create
177
+ end
178
+
179
+ end
180
+
181
+ end
data/spec/spec.opts ADDED
@@ -0,0 +1 @@
1
+ --colour
@@ -0,0 +1,10 @@
1
+ begin
2
+ require 'spec'
3
+ rescue LoadError
4
+ require 'rubygems'
5
+ gem 'rspec'
6
+ require 'spec'
7
+ end
8
+
9
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
10
+ require 'gazer'
@@ -0,0 +1,46 @@
1
+ require File.join(File.dirname(__FILE__), "test_generator_helper.rb")
2
+
3
+ require 'rails_generator'
4
+
5
+ class TestAspectGenerator < Test::Unit::TestCase
6
+ include RubiGen::GeneratorTestHelper
7
+
8
+ def setup
9
+ bare_setup
10
+ end
11
+
12
+ def teardown
13
+ bare_teardown
14
+ end
15
+
16
+ # Some generator-related assertions:
17
+ # assert_generated_file(name, &block) # block passed the file contents
18
+ # assert_directory_exists(name)
19
+ # assert_generated_class(name, &block)
20
+ # assert_generated_module(name, &block)
21
+ # assert_generated_test_for(name, &block)
22
+ # The assert_generated_(class|module|test_for) &block is passed the body of the class/module within the file
23
+ # assert_has_method(body, *methods) # check that the body has a list of methods (methods with parentheses not supported yet)
24
+ #
25
+ # Other helper methods are:
26
+ # app_root_files - put this in teardown to show files generated by the test method (e.g. p app_root_files)
27
+ # bare_setup - place this in setup method to create the APP_ROOT folder for each test
28
+ # bare_teardown - place this in teardown method to destroy the TMP_ROOT or APP_ROOT folder after each test
29
+
30
+ def test_generator_without_options
31
+ name = "myapp"
32
+ run_generator('aspect', [name], sources)
33
+ assert_directory_exists "some/directory"
34
+ assert_generated_file "some_file"
35
+ end
36
+
37
+ private
38
+ def sources
39
+ [RubiGen::PathSource.new(:test, File.join(File.dirname(__FILE__),"..", generator_path))
40
+ ]
41
+ end
42
+
43
+ def generator_path
44
+ "rails_generators"
45
+ end
46
+ end
@@ -0,0 +1,29 @@
1
+ begin
2
+ require File.dirname(__FILE__) + '/test_helper'
3
+ rescue LoadError
4
+ require 'test/unit'
5
+ end
6
+ require 'fileutils'
7
+
8
+ # Must set before requiring generator libs.
9
+ TMP_ROOT = File.dirname(__FILE__) + "/tmp" unless defined?(TMP_ROOT)
10
+ PROJECT_NAME = "myproject" unless defined?(PROJECT_NAME)
11
+ app_root = File.join(TMP_ROOT, PROJECT_NAME)
12
+ if defined?(APP_ROOT)
13
+ APP_ROOT.replace(app_root)
14
+ else
15
+ APP_ROOT = app_root
16
+ end
17
+ if defined?(RAILS_ROOT)
18
+ RAILS_ROOT.replace(app_root)
19
+ else
20
+ RAILS_ROOT = app_root
21
+ end
22
+
23
+ begin
24
+ require 'rubigen'
25
+ rescue LoadError
26
+ require 'rubygems'
27
+ require 'rubigen'
28
+ end
29
+ require 'rubigen/helpers/generator_test_helper'
metadata ADDED
@@ -0,0 +1,76 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: teejayvanslyke-gazer
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - T.J. VanSlyke
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-02-17 00:00:00 -08:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: poor man's aspect-oriented programming for Ruby
17
+ email: T.J. VanSlyke
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files: []
23
+
24
+ files:
25
+ - History.txt
26
+ - Manifest.txt
27
+ - PostInstall.txt
28
+ - README.rdoc
29
+ - VERSION.yml
30
+ - lib/gazer
31
+ - lib/gazer/aspect
32
+ - lib/gazer/aspect/base.rb
33
+ - lib/gazer/aspect/filter.rb
34
+ - lib/gazer/aspect/instance_of.rb
35
+ - lib/gazer/aspect/join_point.rb
36
+ - lib/gazer/aspect/pointcut.rb
37
+ - lib/gazer/aspect.rb
38
+ - lib/gazer/object_extensions.rb
39
+ - lib/gazer/rails
40
+ - lib/gazer/rails/action_controller.rb
41
+ - lib/gazer/rails.rb
42
+ - lib/gazer.rb
43
+ - test/test_aspect_generator.rb
44
+ - test/test_generator_helper.rb
45
+ - spec/gazer_spec.rb
46
+ - spec/spec.opts
47
+ - spec/spec_helper.rb
48
+ has_rdoc: true
49
+ homepage: http://github.com/teejayvanslyke/gazer
50
+ post_install_message:
51
+ rdoc_options:
52
+ - --inline-source
53
+ - --charset=UTF-8
54
+ require_paths:
55
+ - lib
56
+ required_ruby_version: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: "0"
61
+ version:
62
+ required_rubygems_version: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: "0"
67
+ version:
68
+ requirements: []
69
+
70
+ rubyforge_project:
71
+ rubygems_version: 1.2.0
72
+ signing_key:
73
+ specification_version: 2
74
+ summary: poor man's aspect-oriented programming for Ruby
75
+ test_files: []
76
+