aspector 0.6.0 → 0.7.0

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/README.rdoc CHANGED
@@ -15,8 +15,6 @@ gem install aspector
15
15
 
16
16
  == Examples
17
17
 
18
- require 'aspector'
19
-
20
18
  class A
21
19
  def test
22
20
  puts 'test'
@@ -25,6 +23,8 @@ gem install aspector
25
23
 
26
24
  ##############################
27
25
 
26
+ require 'aspector'
27
+
28
28
  class TestAspect < Aspector::Base
29
29
  target do
30
30
  def do_this
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.6.0
1
+ 0.7.0
data/aspector.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{aspector}
8
- s.version = "0.6.0"
8
+ s.version = "0.7.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Guoliang Cao"]
12
- s.date = %q{2011-11-18}
12
+ s.date = %q{2011-12-09}
13
13
  s.description = %q{}
14
14
  s.email = %q{gcao99@gmail.com}
15
15
  s.extra_rdoc_files = [
@@ -33,11 +33,14 @@ Gem::Specification.new do |s|
33
33
  "examples/around_example.rb",
34
34
  "examples/aspector_apply_example.rb",
35
35
  "examples/aspector_example.rb",
36
+ "examples/cache_aspect.rb",
36
37
  "lib/aspector.rb",
37
38
  "lib/aspector/advice.rb",
38
39
  "lib/aspector/advice_metadata.rb",
39
- "lib/aspector/aspects.rb",
40
+ "lib/aspector/aspect_instances.rb",
40
41
  "lib/aspector/base.rb",
42
+ "lib/aspector/base_class_methods.rb",
43
+ "lib/aspector/context.rb",
41
44
  "lib/aspector/deferred_logic.rb",
42
45
  "lib/aspector/deferred_option.rb",
43
46
  "lib/aspector/method_matcher.rb",
@@ -0,0 +1,79 @@
1
+ class A
2
+
3
+ def test
4
+ puts 'test'
5
+ 1
6
+ end
7
+
8
+ def test2
9
+ puts 'test2'
10
+ 2
11
+ end
12
+
13
+ end
14
+
15
+ ##############################
16
+
17
+ class SimpleCache
18
+
19
+ @data = {}
20
+
21
+ def self.cache key, ttl
22
+ found = @data[key] # found is like [time, value]
23
+
24
+ if found
25
+ puts "Found in cache: #{key}"
26
+ insertion_time, value = *found
27
+
28
+ return value if Time.now < insertion_time + ttl
29
+
30
+ puts "Expired: #{key}"
31
+ end
32
+
33
+ value = yield
34
+ @data[key] = [Time.now, value]
35
+ value
36
+ end
37
+
38
+ end
39
+
40
+ ##############################
41
+
42
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
43
+
44
+ require 'aspector'
45
+
46
+ class CacheAspect < Aspector::Base
47
+ default :ttl => 60
48
+
49
+ around options[:method], :context_arg => true do |context, &block|
50
+ key = context.method_name
51
+ ttl = context.options[:ttl]
52
+
53
+ SimpleCache.cache key, ttl do
54
+ block.call
55
+ end
56
+ end
57
+
58
+ end
59
+
60
+ CacheAspect.apply A, :method => :test, :ttl => 2 # 2 seconds
61
+ CacheAspect.apply A, :method => :test2
62
+
63
+ ##############################
64
+
65
+ a = A.new
66
+
67
+ # Will store value in cache
68
+ a.test
69
+ a.test2
70
+
71
+ # Will get value from cache
72
+ a.test
73
+ a.test2
74
+
75
+ sleep 3
76
+
77
+ a.test # Cache expired
78
+ a.test2 # Cache is still valid
79
+
File without changes
data/lib/aspector/base.rb CHANGED
@@ -3,64 +3,22 @@ require 'erb'
3
3
  module Aspector
4
4
  class Base
5
5
 
6
- METHOD_TEMPLATE = ERB.new <<-CODE
7
- wrapped_method = instance_method(:<%= method %>)
8
-
9
- define_method :<%= method %> do |*args, &block|
10
- result = nil
11
-
12
- # Before advices
13
- <% before_advices.each do |definition| %>
14
- <% if definition.options[:method_name_arg] %>
15
- result = <%= definition.with_method %> '<%= method %>', *args
16
- <% else %>
17
- result = <%= definition.with_method %> *args
18
- <% end %>
6
+ attr :options
19
7
 
20
- return result.value if result.is_a? ::Aspector::ReturnThis
21
- <% if definition.options[:skip_if_false] %>
22
- return unless result
23
- <% end %>
24
- <% end %>
8
+ def initialize target, options = {}
9
+ @target = target
25
10
 
26
- <% if around_advice %>
27
- # around advice
28
- <% if around_advice.options[:method_name_arg] %>
29
- result = <%= around_advice.with_method %> '<%= method %>', *args do |*args|
30
- wrapped_method.bind(self).call *args, &block
31
- end
32
- <% else %>
33
- result = <%= around_advice.with_method %> *args do |*args|
34
- wrapped_method.bind(self).call *args, &block
11
+ default_options = self.class.default_options
12
+ if default_options and not default_options.empty?
13
+ @options = default_options.merge(options)
14
+ else
15
+ @options = options
35
16
  end
36
- <% end %>
37
- <% else %>
38
- # Invoke wrapped method
39
- result = wrapped_method.bind(self).call *args, &block
40
- <% end %>
41
17
 
42
- # After advices
43
- <% after_advices.each do |definition| %>
44
- <% if definition.options[:method_name_arg] and definition.options[:result_arg] %>
45
- result = <%= definition.with_method %> '<%= method %>', result, *args
46
- <% elsif definition.options[:method_name_arg] %>
47
- <%= definition.with_method %> '<%= method %>', *args
48
- <% elsif definition.options[:result_arg] %>
49
- result = <%= definition.with_method %> result, *args
50
- <% else %>
51
- <%= definition.with_method %> *args
52
- <% end %>
53
- <% end %>
54
- result
55
- end
56
- CODE
57
-
58
- attr :options
18
+ # @context is where advices will be applied (i.e. where methods are modified), can be different from target
19
+ @context = get_context
59
20
 
60
- def initialize target, options = {}
61
- @target = target
62
- @options = options.merge(self.class.options)
63
- @context = get_context # Context is where advices will be applied (i.e. where methods are modified)
21
+ after_initialization
64
22
  end
65
23
 
66
24
  def apply
@@ -69,6 +27,7 @@ module Aspector
69
27
  add_to_instances
70
28
  apply_to_methods
71
29
  add_method_hooks
30
+ after_application
72
31
  end
73
32
 
74
33
  def deferred_logic_results logic
@@ -76,31 +35,45 @@ module Aspector
76
35
  end
77
36
 
78
37
  def apply_to_methods
79
- @context.instance_methods.each do |method|
80
- apply_to_method(method)
38
+ @context.public_instance_methods.each do |method|
39
+ apply_to_method(method, :public)
40
+ end
41
+
42
+ @context.protected_instance_methods.each do |method|
43
+ apply_to_method(method, :protected)
81
44
  end
82
45
 
83
46
  if @options[:private_methods]
84
47
  @context.private_instance_methods.each do |method|
85
- apply_to_method(method, true)
48
+ apply_to_method(method, :private)
86
49
  end
87
50
  end
88
51
  end
89
52
 
90
- def apply_to_method method, is_private = false
53
+ def apply_to_method method, scope = nil
91
54
  advices = advices_for_method method
92
55
  return if advices.empty?
93
56
 
94
- recreate_method method, advices, is_private
57
+ scope ||=
58
+ if @context.private_instance_methods.include?(method.to_s)
59
+ :private
60
+ elsif @context.protected_instance_methods.include?(method.to_s)
61
+ :protected
62
+ else
63
+ :public
64
+ end
65
+
66
+ recreate_method method, advices, scope
95
67
  end
96
68
 
97
- def to_hash
98
- {
99
- "type" => self.class.name,
100
- "context" => @context.name,
101
- "options" => @options,
102
- "aspect" => self.class.to_hash
103
- }
69
+ protected
70
+
71
+ # Hook method that runs after an aspect is instantiated
72
+ def after_initialization
73
+ end
74
+
75
+ # Hook method that runs after an aspect is applied
76
+ def after_application
104
77
  end
105
78
 
106
79
  private
@@ -126,6 +99,7 @@ module Aspector
126
99
  self.class.advices.each do |advice|
127
100
  next unless advice.advice_block
128
101
  @context.send :define_method, advice.with_method, advice.advice_block
102
+ @context.send :private, advice.with_method
129
103
  end
130
104
  end
131
105
 
@@ -173,7 +147,7 @@ module Aspector
173
147
  end
174
148
  end
175
149
 
176
- def recreate_method method, advices, is_private
150
+ def recreate_method method, advices, scope
177
151
  @context.instance_variable_set(:@aspector_creating_method, true)
178
152
 
179
153
  before_advices = advices.select {|advice| advice.before? or advice.before_filter? }
@@ -189,7 +163,7 @@ module Aspector
189
163
 
190
164
  recreate_method_with_advices method, before_advices, after_advices, around_advices.first
191
165
 
192
- @context.send :private, method if is_private
166
+ @context.send scope, method if scope != :public
193
167
  ensure
194
168
  @context.instance_variable_set(:@aspector_creating_method, nil)
195
169
  end
@@ -197,99 +171,68 @@ module Aspector
197
171
  def recreate_method_with_advices method, before_advices, after_advices, around_advice
198
172
  code = METHOD_TEMPLATE.result(binding)
199
173
  #puts code
200
- # line no is the actual line no of METHOD_TEMPLATE + 5
201
- @context.class_eval code, __FILE__, 7
174
+ @context.class_eval code, __FILE__, __LINE__ + 4
202
175
  end
203
176
 
204
- class << self
205
-
206
- def advices
207
- @advices ||= []
208
- end
209
-
210
- def options
211
- @options ||= {}
212
- end
213
-
214
- def deferred_logics
215
- @deferred_logics ||= []
216
- end
217
-
218
- def apply target, options = {}
219
- instances = target.instance_variable_get(:@aspector_instances)
220
- return if instances and instances.detect {|instance| instance.is_a?(self) }
221
-
222
- aspect_instance = new(target, options)
223
- aspect_instance.apply
224
- end
225
-
226
- def default options
227
- if @options
228
- @options.merge! options
229
- else
230
- @options = options
231
- end
232
- end
233
-
234
- def before *methods, &block
235
- advices << create_advice(Aspector::AdviceMetadata::BEFORE, self, methods, &block)
236
- end
237
-
238
- def before_filter *methods, &block
239
- advices << create_advice(Aspector::AdviceMetadata::BEFORE_FILTER, self, methods, &block)
240
- end
241
-
242
- def after *methods, &block
243
- advices << create_advice(Aspector::AdviceMetadata::AFTER, self, methods, &block)
244
- end
177
+ METHOD_TEMPLATE = ERB.new <<-CODE
178
+ target = self
179
+ wrapped_method = instance_method(:<%= method %>)
245
180
 
246
- def around *methods, &block
247
- advices << create_advice(Aspector::AdviceMetadata::AROUND, self, methods, &block)
248
- end
181
+ define_method :<%= method %> do |*args, &block|
182
+ result = nil
249
183
 
250
- def target code = nil, &block
251
- logic = DeferredLogic.new(code || block)
252
- deferred_logics << logic
253
- logic
254
- end
184
+ # Before advices
185
+ <% before_advices.each do |advice| %>
186
+ <% if advice.options[:context_arg] %>
187
+ context = Aspector::Context.new(target, <%= self.hash %>, <%= advice.hash %>)
188
+ context.method_name = '<%= method %>'
189
+ result = <%= advice.with_method %> context, *args
190
+ <% else %>
191
+ result = <%= advice.with_method %> *args
192
+ <% end %>
255
193
 
256
- def deferred_option key
257
- DeferredOption.new(key)
258
- end
194
+ return result.value if result.is_a? ::Aspector::ReturnThis
195
+ <% if advice.options[:skip_if_false] %>
196
+ return unless result
197
+ <% end %>
198
+ <% end %>
259
199
 
260
- def to_hash
261
- {
262
- "type" => self.name,
263
- "options" => options,
264
- "advices" => advices.map {|advice| advice.to_s }
265
- }
200
+ <% if around_advice %>
201
+ # around advice
202
+ <% if around_advice.options[:context_arg] %>
203
+ context = Aspector::Context.new(target, <%= self.hash %>, <%= around_advice.hash %>)
204
+ context.method_name = '<%= method %>'
205
+ result = <%= around_advice.with_method %> context, *args do |*args|
206
+ wrapped_method.bind(self).call *args, &block
266
207
  end
267
-
268
- private
269
-
270
- def create_advice meta_data, klass_or_module, *methods, &block
271
- methods.flatten!
272
-
273
- options = meta_data.default_options.clone
274
- options.merge!(methods.pop) if methods.last.is_a? Hash
275
- options.merge!(meta_data.mandatory_options)
276
-
277
- # Convert symbols to strings to avoid inconsistencies
278
- methods.size.times do |i|
279
- methods[i] = methods[i].to_s if methods[i].is_a? Symbol
280
- end
281
-
282
- with_method = methods.pop unless block_given?
283
-
284
- Aspector::Advice.new(self,
285
- meta_data.advice_type,
286
- Aspector::MethodMatcher.new(*methods),
287
- with_method,
288
- options,
289
- &block)
208
+ <% else %>
209
+ result = <%= around_advice.with_method %> *args do |*args|
210
+ wrapped_method.bind(self).call *args, &block
290
211
  end
212
+ <% end %>
213
+ <% else %>
214
+ # Invoke wrapped method
215
+ result = wrapped_method.bind(self).call *args, &block
216
+ <% end %>
291
217
 
218
+ # After advices
219
+ <% after_advices.each do |advice| %>
220
+ <% if advice.options[:context_arg] and advice.options[:result_arg] %>
221
+ context = Aspector::Context.new(target, <%= self.hash %>, <%= advice.hash %>)
222
+ context.method_name = '<%= method %>'
223
+ result = <%= advice.with_method %> context, result, *args
224
+ <% elsif advice.options[:context_arg] %>
225
+ <%= advice.with_method %> context, *args
226
+ <% elsif advice.options[:result_arg] %>
227
+ result = <%= advice.with_method %> result, *args
228
+ <% else %>
229
+ <%= advice.with_method %> *args
230
+ <% end %>
231
+ <% end %>
232
+ result
292
233
  end
234
+ CODE
235
+
293
236
  end
294
237
  end
295
238
 
@@ -0,0 +1,85 @@
1
+ module Aspector
2
+ class Base
3
+ module ClassMethods
4
+ ::Aspector::Base.extend(self)
5
+
6
+ def advices
7
+ @advices ||= []
8
+ end
9
+
10
+ def default_options
11
+ @default_options ||= {}
12
+ end
13
+
14
+ def deferred_logics
15
+ @deferred_logics ||= []
16
+ end
17
+
18
+ def apply target, options = {}
19
+ aspect_instance = new(target, options)
20
+ aspect_instance.apply
21
+ aspect_instance
22
+ end
23
+
24
+ def default options
25
+ if @default_options
26
+ @default_options.merge! options
27
+ else
28
+ @default_options = options
29
+ end
30
+ end
31
+
32
+ def before *methods, &block
33
+ advices << create_advice(Aspector::AdviceMetadata::BEFORE, self, methods, &block)
34
+ end
35
+
36
+ def before_filter *methods, &block
37
+ advices << create_advice(Aspector::AdviceMetadata::BEFORE_FILTER, self, methods, &block)
38
+ end
39
+
40
+ def after *methods, &block
41
+ advices << create_advice(Aspector::AdviceMetadata::AFTER, self, methods, &block)
42
+ end
43
+
44
+ def around *methods, &block
45
+ advices << create_advice(Aspector::AdviceMetadata::AROUND, self, methods, &block)
46
+ end
47
+
48
+ def target code = nil, &block
49
+ logic = DeferredLogic.new(code || block)
50
+ deferred_logics << logic
51
+ logic
52
+ end
53
+
54
+ def options
55
+ DeferredOption.new
56
+ end
57
+
58
+ private
59
+
60
+ def create_advice meta_data, klass_or_module, *methods, &block
61
+ methods.flatten!
62
+
63
+ options = meta_data.default_options.clone
64
+ options.merge!(methods.pop) if methods.last.is_a? Hash
65
+ options.merge!(meta_data.mandatory_options)
66
+
67
+ # Convert symbols to strings to avoid inconsistencies
68
+ methods.size.times do |i|
69
+ methods[i] = methods[i].to_s if methods[i].is_a? Symbol
70
+ end
71
+
72
+ with_method = methods.pop unless block_given?
73
+
74
+ Aspector::Advice.new(self,
75
+ meta_data.advice_type,
76
+ Aspector::MethodMatcher.new(*methods),
77
+ with_method,
78
+ options,
79
+ &block)
80
+ end
81
+
82
+ end
83
+ end
84
+ end
85
+
@@ -0,0 +1,28 @@
1
+ module Aspector
2
+ class Context
3
+
4
+ attr_accessor :method_name
5
+
6
+ def initialize(target, aspect_hash, advice_hash)
7
+ @target = target
8
+ @aspect_hash = aspect_hash
9
+ @advice_hash = advice_hash
10
+ end
11
+
12
+ def aspect
13
+ @aspect ||= @target.instance_variable_get(:@aspector_instances).detect do |aspect|
14
+ aspect.hash == @aspect_hash
15
+ end
16
+ end
17
+
18
+ def advice
19
+ @advice ||= aspect.class.advices.detect { |advice| advice.hash == @advice_hash }
20
+ end
21
+
22
+ def options
23
+ aspect.options
24
+ end
25
+
26
+ end
27
+ end
28
+
@@ -3,8 +3,9 @@ module Aspector
3
3
 
4
4
  attr_reader :key
5
5
 
6
- def initialize key
6
+ def [] key
7
7
  @key = key
8
+ self
8
9
  end
9
10
 
10
11
  end
@@ -2,6 +2,8 @@ module Aspector
2
2
  module ModuleExtension
3
3
  Module.send :include, self
4
4
 
5
+ private
6
+
5
7
  def method_added_aspector method
6
8
  return (block_given? and yield) if
7
9
  @aspector_creating_method or
data/lib/aspector.rb CHANGED
@@ -2,12 +2,14 @@ require 'aspector/object_extension'
2
2
  require 'aspector/module_extension'
3
3
 
4
4
  require 'aspector/base'
5
+ require 'aspector/base_class_methods'
5
6
  require 'aspector/advice'
6
7
  require 'aspector/advice_metadata'
7
8
  require 'aspector/method_matcher'
8
9
  require 'aspector/deferred_logic'
9
10
  require 'aspector/deferred_option'
11
+ require 'aspector/context'
10
12
 
11
- require 'aspector/aspects'
13
+ require 'aspector/aspect_instances'
12
14
 
13
15
  require 'aspector/return_this'
@@ -26,6 +26,56 @@ describe "After advices" do
26
26
  obj.value.should == %w"test do_this"
27
27
  end
28
28
 
29
+ it "context_arg" do
30
+ klass = Class.new do
31
+ def value
32
+ @value ||= []
33
+ end
34
+
35
+ def test
36
+ value << "test"
37
+ end
38
+
39
+ def do_this context, result
40
+ value << "do_this"
41
+ result
42
+ end
43
+ end
44
+
45
+ aspector(klass) do
46
+ after :test, :do_this, :context_arg => true
47
+ end
48
+
49
+ obj = klass.new
50
+ obj.test
51
+ obj.value.should == %w"test do_this"
52
+ end
53
+
54
+ it "result_arg set to false" do
55
+ klass = Class.new do
56
+ def value
57
+ @value ||= []
58
+ end
59
+
60
+ def test
61
+ value << "test"
62
+ 'test'
63
+ end
64
+
65
+ def do_this
66
+ value << "do_this"
67
+ end
68
+ end
69
+
70
+ aspector(klass) do
71
+ after :test, :do_this, :result_arg => false
72
+ end
73
+
74
+ obj = klass.new
75
+ obj.test.should == 'test'
76
+ obj.value.should == %w"test do_this"
77
+ end
78
+
29
79
  it "logic in block" do
30
80
  klass = Class.new do
31
81
  def value
@@ -1,6 +1,6 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
2
 
3
- describe "Aspector::Aspect" do
3
+ describe "Miscellaneous" do
4
4
  it "#apply" do
5
5
  klass = Class.new do
6
6
  def value
@@ -6,7 +6,7 @@ describe "Aspector::Base" do
6
6
  default :test => 'value'
7
7
  end
8
8
 
9
- aspect.options[:test].should == 'value'
9
+ aspect.default_options[:test].should == 'value'
10
10
  end
11
11
 
12
12
  it "deferred_option" do
@@ -21,7 +21,7 @@ describe "Aspector::Base" do
21
21
  end
22
22
 
23
23
  aspect = Aspector do
24
- before deferred_option(:methods) do
24
+ before options[:methods] do
25
25
  value << "do_this"
26
26
  end
27
27
  end
@@ -85,7 +85,7 @@ describe "Aspector" do
85
85
  obj.value.should == %w"before_test test"
86
86
  end
87
87
 
88
- it "should apply only once if called multiple times" do
88
+ it "applied multiple times" do
89
89
  klass = Class.new do
90
90
  def value
91
91
  @value ||= []
@@ -105,7 +105,7 @@ describe "Aspector" do
105
105
 
106
106
  obj = klass.new
107
107
  obj.test
108
- obj.value.should == %w"before_test test"
108
+ obj.value.should == %w"before_test before_test test"
109
109
  end
110
110
 
111
111
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: aspector
3
3
  version: !ruby/object:Gem::Version
4
- hash: 7
4
+ hash: 3
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
- - 6
8
+ - 7
9
9
  - 0
10
- version: 0.6.0
10
+ version: 0.7.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Guoliang Cao
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-11-18 00:00:00 -05:00
18
+ date: 2011-12-09 00:00:00 -05:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -265,11 +265,14 @@ files:
265
265
  - examples/around_example.rb
266
266
  - examples/aspector_apply_example.rb
267
267
  - examples/aspector_example.rb
268
+ - examples/cache_aspect.rb
268
269
  - lib/aspector.rb
269
270
  - lib/aspector/advice.rb
270
271
  - lib/aspector/advice_metadata.rb
271
- - lib/aspector/aspects.rb
272
+ - lib/aspector/aspect_instances.rb
272
273
  - lib/aspector/base.rb
274
+ - lib/aspector/base_class_methods.rb
275
+ - lib/aspector/context.rb
273
276
  - lib/aspector/deferred_logic.rb
274
277
  - lib/aspector/deferred_option.rb
275
278
  - lib/aspector/method_matcher.rb