aspectual 0.0.1 → 0.0.2
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.
- checksums.yaml +4 -4
- data/Gemfile +4 -0
- data/Gemfile.lock +27 -0
- data/README.md +47 -0
- data/aspectual-0.0.1.gem +0 -0
- data/aspectual.gemspec +26 -0
- data/lib/aspectual/version.rb +3 -0
- data/lib/aspectual.rb +82 -0
- data/spec/lib/aspectual_spec.rb +173 -0
- metadata +13 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 791be340bcb932e339d64f0735635b806b0ed860
|
4
|
+
data.tar.gz: e85f88a62334e97d685f2d626d7d9e7b8a45c8ff
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d20e5e8d7ec23d34f6664626248f49e29276f2cb963383f19579bc6300e8182ce791eccc639d8d8bda12e3929f3aa3210d807504a3c96e3170001da86ccf07dc
|
7
|
+
data.tar.gz: c5dd639e0b3fa4f1e187fc445ba6682aef248a100bc99453e6e6d61a657c34284e6cf2edd2f9723e9390798ea21bcfd81f329f2a8c37f1f6d34fb729f64da7ef
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
aspectual (0.0.2)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
diff-lcs (1.2.5)
|
10
|
+
rake (10.3.1)
|
11
|
+
rspec (2.14.1)
|
12
|
+
rspec-core (~> 2.14.0)
|
13
|
+
rspec-expectations (~> 2.14.0)
|
14
|
+
rspec-mocks (~> 2.14.0)
|
15
|
+
rspec-core (2.14.8)
|
16
|
+
rspec-expectations (2.14.5)
|
17
|
+
diff-lcs (>= 1.1.3, < 2.0)
|
18
|
+
rspec-mocks (2.14.6)
|
19
|
+
|
20
|
+
PLATFORMS
|
21
|
+
ruby
|
22
|
+
|
23
|
+
DEPENDENCIES
|
24
|
+
aspectual!
|
25
|
+
bundler (~> 1.3)
|
26
|
+
rake
|
27
|
+
rspec
|
data/README.md
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
[](http://badge.fury.io/rb/aspectual)
|
2
|
+
|
3
|
+
# Aspectual
|
4
|
+
|
5
|
+
A simple gem to support minimal Aspect Oriented Programming in ruby.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
gem 'aspectual'
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install aspectual
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
Extend the module
|
24
|
+
|
25
|
+
extend Aspectual
|
26
|
+
|
27
|
+
Create any methods you want as aspects (Aspectual will not add methods you don't have as aspects)
|
28
|
+
|
29
|
+
Then declare your aspects
|
30
|
+
|
31
|
+
aspects before: :logging, after: :more_logging
|
32
|
+
def foo
|
33
|
+
"foo"
|
34
|
+
end
|
35
|
+
|
36
|
+
aspects before: [:notify_user, :notify_admin], after: [:remove_temp_files, :play_sound]
|
37
|
+
def bar
|
38
|
+
"bar"
|
39
|
+
end
|
40
|
+
|
41
|
+
## Contributing
|
42
|
+
|
43
|
+
1. Fork it
|
44
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
45
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
46
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
47
|
+
5. Create new Pull Request
|
data/aspectual-0.0.1.gem
ADDED
Binary file
|
data/aspectual.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'aspectual/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "aspectual"
|
8
|
+
spec.version = Aspectual::VERSION
|
9
|
+
spec.authors = ["Alex Sunderland"]
|
10
|
+
spec.email = ["agentantelope+aspectual@gmail.com"]
|
11
|
+
spec.description = %q{
|
12
|
+
A simple gem to support minimal Aspect Oriented Programming in ruby.
|
13
|
+
}
|
14
|
+
spec.summary = %q{A gem to help with AOP.}
|
15
|
+
spec.homepage = "https://github.com/AgentAntelope/aspectual"
|
16
|
+
spec.license = "MIT"
|
17
|
+
|
18
|
+
spec.files = `git ls-files`.split($/)
|
19
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
20
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
21
|
+
spec.require_paths = ["lib"]
|
22
|
+
|
23
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
24
|
+
spec.add_development_dependency "rake"
|
25
|
+
spec.add_development_dependency "rspec"
|
26
|
+
end
|
data/lib/aspectual.rb
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
module Aspectual
|
4
|
+
def aspects(aspects)
|
5
|
+
# The before aspect have to be reversed so that when multiple before
|
6
|
+
# aspects are added to one method, the first declared method will be
|
7
|
+
# added last.
|
8
|
+
@_before_aspects = Array(aspects[:before]).reverse
|
9
|
+
@_after_aspects = Array(aspects[:after])
|
10
|
+
end
|
11
|
+
|
12
|
+
def method_added(method_symbol)
|
13
|
+
return unless has_aspects_declared?
|
14
|
+
return if @_defining_method
|
15
|
+
if method_defined?(method_symbol)
|
16
|
+
|
17
|
+
%w{before after}.each do |position|
|
18
|
+
current_aspects = instance_variable_get("@_#{position}_aspects")
|
19
|
+
if current_aspects
|
20
|
+
current_aspects.each do |aspect|
|
21
|
+
define_aspect_method(method_symbol, position, aspect)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def has_aspects_declared?
|
31
|
+
@_before_aspects || @_after_aspects
|
32
|
+
end
|
33
|
+
|
34
|
+
def define_aspect_method(method_symbol, position, aspect)
|
35
|
+
# This is to prevent us from looping because we're about to define some methods.
|
36
|
+
@_defining_method = true
|
37
|
+
|
38
|
+
with_aspect_method_name = method_symbol.to_s + "_with_#{position}_" + aspect.to_s
|
39
|
+
without_aspect_method_name = method_symbol.to_s + "_without_#{position}_" + aspect.to_s
|
40
|
+
|
41
|
+
case position
|
42
|
+
when "after"
|
43
|
+
aspect_proc = lambda {|*args| send(without_aspect_method_name, *args); send(aspect, *args)}
|
44
|
+
when "before"
|
45
|
+
aspect_proc = lambda {|*args| send(aspect, *args); send(without_aspect_method_name, *args)}
|
46
|
+
end
|
47
|
+
|
48
|
+
define_method(with_aspect_method_name, aspect_proc)
|
49
|
+
|
50
|
+
scope_method_to_parent(method_symbol, with_aspect_method_name)
|
51
|
+
|
52
|
+
alias_method_chain(method_symbol, aspect.to_sym, position)
|
53
|
+
|
54
|
+
@_defining_method = false
|
55
|
+
end
|
56
|
+
|
57
|
+
def scope_method_to_parent(without_method, target)
|
58
|
+
case
|
59
|
+
when public_method_defined?(without_method)
|
60
|
+
public target
|
61
|
+
when protected_method_defined?(without_method)
|
62
|
+
protected target
|
63
|
+
when private_method_defined?(without_method)
|
64
|
+
private target
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# adapted from active support
|
69
|
+
def alias_method_chain(target, feature, position)
|
70
|
+
# Strip out punctuation on predicates or bang methods since
|
71
|
+
# e.g. target?_without_position_feature is not a valid method name.
|
72
|
+
aliased_target, punctuation = target.to_s.sub(/([?!=])$/, ''), $1
|
73
|
+
|
74
|
+
with_method = "#{aliased_target}_with_#{position}_#{feature}#{punctuation}"
|
75
|
+
without_method = "#{aliased_target}_without_#{position}_#{feature}#{punctuation}"
|
76
|
+
|
77
|
+
alias_method without_method, target
|
78
|
+
alias_method target, with_method
|
79
|
+
|
80
|
+
scope_method_to_parent(without_method, target)
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,173 @@
|
|
1
|
+
require_relative '../../lib/aspectual'
|
2
|
+
|
3
|
+
describe Aspectual do
|
4
|
+
class TestClass
|
5
|
+
extend Aspectual
|
6
|
+
|
7
|
+
attr_reader :methods_called
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
# This is to ensure that all methods are properly called within the
|
11
|
+
# context of the current instance of this object.
|
12
|
+
@methods_called = []
|
13
|
+
end
|
14
|
+
|
15
|
+
# These definitions could be looped over to make this class shorter, but the
|
16
|
+
# decision has been made to go for clarity over brevity.
|
17
|
+
def single_test_method
|
18
|
+
methods_called << "single_test_method"
|
19
|
+
self
|
20
|
+
end
|
21
|
+
|
22
|
+
def array_test_method_0
|
23
|
+
methods_called << "array_test_method_0"
|
24
|
+
self
|
25
|
+
end
|
26
|
+
|
27
|
+
def array_test_method_1
|
28
|
+
methods_called << "array_test_method_1"
|
29
|
+
self
|
30
|
+
end
|
31
|
+
|
32
|
+
def before_test_method_0
|
33
|
+
methods_called << "before_test_method_0"
|
34
|
+
self
|
35
|
+
end
|
36
|
+
|
37
|
+
def before_test_method_1
|
38
|
+
methods_called << "before_test_method_1"
|
39
|
+
self
|
40
|
+
end
|
41
|
+
|
42
|
+
def after_test_method_0
|
43
|
+
methods_called << "after_test_method_0"
|
44
|
+
self
|
45
|
+
end
|
46
|
+
|
47
|
+
def after_test_method_1
|
48
|
+
methods_called << "after_test_method_1"
|
49
|
+
self
|
50
|
+
end
|
51
|
+
|
52
|
+
aspects before: :single_test_method
|
53
|
+
def single_before_test_method
|
54
|
+
methods_called << "single_before_test_method"
|
55
|
+
self
|
56
|
+
end
|
57
|
+
|
58
|
+
aspects after: :single_test_method
|
59
|
+
def single_after_test_method
|
60
|
+
methods_called << "single_after_test_method"
|
61
|
+
self
|
62
|
+
end
|
63
|
+
|
64
|
+
aspects before: [:array_test_method_0, :array_test_method_1]
|
65
|
+
def array_before_test_method
|
66
|
+
methods_called << "array_before_test_method"
|
67
|
+
self
|
68
|
+
end
|
69
|
+
|
70
|
+
aspects after: [:array_test_method_0, :array_test_method_1]
|
71
|
+
def array_after_test_method
|
72
|
+
methods_called << "array_after_test_method"
|
73
|
+
self
|
74
|
+
end
|
75
|
+
|
76
|
+
aspects before: [:before_test_method_0, :before_test_method_1], after: [:after_test_method_0, :after_test_method_1]
|
77
|
+
def before_and_after_aspects_test_method
|
78
|
+
methods_called << "before_and_after_aspects_test_method"
|
79
|
+
self
|
80
|
+
end
|
81
|
+
|
82
|
+
def public_aspect_method
|
83
|
+
methods_called << "public_aspect_method"
|
84
|
+
self
|
85
|
+
end
|
86
|
+
|
87
|
+
protected
|
88
|
+
aspects before: :public_aspect_method
|
89
|
+
def protected_method
|
90
|
+
methods_called << "protected_method"
|
91
|
+
self
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
aspects before: :public_aspect_method
|
96
|
+
def private_method
|
97
|
+
methods_called << "private_method"
|
98
|
+
self
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
describe "before aspects" do
|
103
|
+
it "calls before aspect methods before the called method" do
|
104
|
+
test_instance = TestClass.new.single_before_test_method
|
105
|
+
expect(test_instance.methods_called).to match_array(%w{
|
106
|
+
single_test_method
|
107
|
+
single_before_test_method
|
108
|
+
})
|
109
|
+
end
|
110
|
+
|
111
|
+
it "allows multiple aspects to be declared" do
|
112
|
+
test_instance = TestClass.new.array_before_test_method
|
113
|
+
expect(test_instance.methods_called).to match_array(%w{
|
114
|
+
array_test_method_0
|
115
|
+
array_test_method_1
|
116
|
+
array_before_test_method
|
117
|
+
})
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
describe "after aspects" do
|
122
|
+
it "calls before aspect methods before the called method" do
|
123
|
+
test_instance = TestClass.new.single_after_test_method
|
124
|
+
expect(test_instance.methods_called).to match_array(%w{
|
125
|
+
single_test_method
|
126
|
+
single_after_test_method
|
127
|
+
})
|
128
|
+
end
|
129
|
+
|
130
|
+
it "allows multiple aspects to be declared" do
|
131
|
+
test_instance = TestClass.new.array_after_test_method
|
132
|
+
expect(test_instance.methods_called).to match_array(%w{
|
133
|
+
array_after_test_method
|
134
|
+
array_test_method_0
|
135
|
+
array_test_method_1
|
136
|
+
})
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
describe "mixed aspects" do
|
141
|
+
it "calls all the aspect declarations in the correct order" do
|
142
|
+
test_instance = TestClass.new.before_and_after_aspects_test_method
|
143
|
+
expect(test_instance.methods_called).to match_array(%w{
|
144
|
+
before_test_method_0
|
145
|
+
before_test_method_1
|
146
|
+
before_and_after_aspects_test_method
|
147
|
+
after_test_method_0
|
148
|
+
after_test_method_1
|
149
|
+
})
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
describe "public methods" do
|
154
|
+
it "stay public" do
|
155
|
+
test_instance = TestClass.new.single_before_test_method
|
156
|
+
expect(test_instance.public_methods).to include(:single_before_test_method)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
describe "protected methods" do
|
161
|
+
it "stay protected" do
|
162
|
+
test_instance = TestClass.new
|
163
|
+
expect(test_instance.protected_methods).to include(:protected_method)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
describe "private methods" do
|
168
|
+
it "stay private" do
|
169
|
+
test_instance = TestClass.new
|
170
|
+
expect(test_instance.private_methods).to include(:private_method)
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: aspectual
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alex Sunderland
|
@@ -59,8 +59,16 @@ email:
|
|
59
59
|
executables: []
|
60
60
|
extensions: []
|
61
61
|
extra_rdoc_files: []
|
62
|
-
files:
|
63
|
-
|
62
|
+
files:
|
63
|
+
- Gemfile
|
64
|
+
- Gemfile.lock
|
65
|
+
- README.md
|
66
|
+
- aspectual-0.0.1.gem
|
67
|
+
- aspectual.gemspec
|
68
|
+
- lib/aspectual.rb
|
69
|
+
- lib/aspectual/version.rb
|
70
|
+
- spec/lib/aspectual_spec.rb
|
71
|
+
homepage: https://github.com/AgentAntelope/aspectual
|
64
72
|
licenses:
|
65
73
|
- MIT
|
66
74
|
metadata: {}
|
@@ -84,4 +92,5 @@ rubygems_version: 2.0.6
|
|
84
92
|
signing_key:
|
85
93
|
specification_version: 4
|
86
94
|
summary: A gem to help with AOP.
|
87
|
-
test_files:
|
95
|
+
test_files:
|
96
|
+
- spec/lib/aspectual_spec.rb
|