aspekt 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -1,3 +1,11 @@
1
1
  source 'http://rubygems.org'
2
2
 
3
3
  gem 'is_same'
4
+
5
+ group :development, :test do
6
+ gem 'rspec'
7
+ gem 'rspec-core'
8
+ gem 'rdoc' # yard uses rdoc
9
+ gem 'yard'
10
+ gem 'yard-rspec'
11
+ end
@@ -0,0 +1,57 @@
1
+ module Aspekt
2
+
3
+
4
+
5
+ class Advice < Aspekt::Object
6
+
7
+ # Creates new Advice Object
8
+ #
9
+ # == Example usage
10
+ #
11
+ # advice = Aspekt::Advice.new type: :before, pointcut: pointcut do |joinpoint|
12
+ # puts "before #{joinpoint}"
13
+ # end
14
+ #
15
+ # advice = Aspekt::Advice.new type: :around, pointcuts: [pointcut1, pointcut2] do |joinpoint|
16
+ # puts "around :start for #{joinpoint}"
17
+ # return_value = joinpoint.proceed
18
+ # puts "around :end for #{joinpoint}"
19
+ # end
20
+ #
21
+ # @param {:type => :before|:after|:around, :pointcut|:pointcuts => Pointcut.new(...)|[Pointcut.new(...)]}
22
+
23
+ def initialize opts, &block
24
+ raise ArgumentError, "type has to be Symbol: before, after or around, but got '#{opts[:type]}'" unless [:before, :after, :around].include?(opts[:type])
25
+ raise ArgumentError, "has to define at least one Pointcut with :pointcut or :pointcuts" unless opts.has_key?(:pointcut) or opts.has_key?(:pointcuts)
26
+
27
+ # get the module in which advice was created
28
+ begin
29
+ opts[:module] = caller.including_same(/<module:/).collect {|l| /<module:(.*)>/.match(l)[1] }.reverse.inject(Object) {|a, b| a.const_get(b) }
30
+ rescue
31
+ end
32
+
33
+ # add block info to opts
34
+ opts[:proc] = block
35
+
36
+ # register opts values
37
+ super
38
+
39
+ # weave into the code
40
+ weave &block
41
+ end
42
+
43
+
44
+ # Weaves advice into the code.
45
+ #
46
+ # @see Aspekt::Weaver
47
+
48
+ def weave &block
49
+ Aspekt::Weaver.send(self[:type], self.values_at(:pointcut, :pointcuts).flatten.compact, &block)
50
+ end
51
+ private :weave
52
+
53
+ end
54
+
55
+
56
+
57
+ end
@@ -0,0 +1,80 @@
1
+ module Aspekt
2
+ module Helpers
3
+ module ShorthandMethods
4
+
5
+ # Shorthand for before advice.
6
+ #
7
+ # === Examples
8
+ #
9
+ # before type: Test, methods: /to_/ do
10
+ # puts "Before called on a object #{self}"
11
+ # end
12
+ #
13
+ # before(
14
+ # {type: Class1, method: :method1},
15
+ # {types: [Class2, Class3], methods: [/cotaining/, :method3]},
16
+ # {instances: [Class3, Class4, some_object], method: :lal}
17
+ # ) do |joinpoint|
18
+ # puts "Before #{joinpoint[:method]} on #{self}. Other information of joinpoint: #{joinpoint}."
19
+ # end
20
+ #
21
+ # class TestMe
22
+ # before methods: [:a, /b/] do |joinpoint| # note how type or instance is not specified, it is automatically resolved to self (what is TestMe in class body)
23
+ # puts "Before #{joinpoint}!"
24
+ # end
25
+ # end
26
+
27
+ def before *pointcuts, &block
28
+ Aspekt::Advice.new type: :before, pointcuts: pointcuts, &block
29
+ end
30
+
31
+
32
+
33
+ # Shorthand for after advice.
34
+ #
35
+ # === Examples
36
+ #
37
+ # after type: Test, methods: /to_/ do
38
+ # puts "Before called on a object #{self}"
39
+ # end
40
+ #
41
+ # class TestMe
42
+ # after methods: [:a, /b/] do |joinpoint| # note how type or instance is not specified, it is automatically resolved to self (what is TestMe in class body)
43
+ # puts "After #{joinpoint}!"
44
+ # end
45
+ # end
46
+
47
+ def after *pointcuts, &block
48
+ Aspekt::Advice.new type: :after, pointcuts: pointcuts, &block
49
+ end
50
+
51
+
52
+
53
+ # Shorthand for around advice.
54
+ #
55
+ # === Example
56
+ #
57
+ # around type: Test, methods: /to_/ do |joinpoint|
58
+ # puts "Before called on a object #{self} and method #{joinpoint[:method]}"
59
+ # result = joinpoint.proceed
60
+ # puts "After called on a object #{self} and method #{joinpoint[:method]}"
61
+ # result
62
+ # end
63
+ #
64
+ # class TestMe
65
+ # around methods: [:a, /b/] do |joinpoint| # note how type or instance is not specified, it is automatically resolved to self (what is TestMe in class body)
66
+ # puts "Before around #{joinpoint}!"
67
+ # result = joinpoint.proceed
68
+ # puts "After around #{joinpoint}!"
69
+ # result
70
+ # end
71
+ # end
72
+
73
+ def around *pointcuts, &block
74
+ Aspekt::Advice.new type: :around, pointcuts: pointcuts, &block
75
+ end
76
+
77
+
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,18 @@
1
+ module Aspekt
2
+
3
+
4
+
5
+ class Joinpoint < Aspekt::Object
6
+
7
+
8
+ # Execute the orginal method.
9
+
10
+ def proceed
11
+ self[:bound_method].(*self[:args], &self[:block])
12
+ end
13
+
14
+ end
15
+
16
+
17
+
18
+ end
@@ -0,0 +1,44 @@
1
+ module Aspekt
2
+
3
+
4
+
5
+ # All Aspekt's objects are extended from Aspekt::Object.
6
+ #
7
+ # It extends Hash.
8
+ class Object < Hash
9
+
10
+
11
+ # Creates a new Aspekt::Object with base class Hash and registres self in class :@all.
12
+ #
13
+ # === Example
14
+ #
15
+ # class MyClass < Aspekt::Object; end
16
+ # obj = MyClass.new a: 'b', c: 'd'
17
+ # obj # {a: 'b', c: 'd'}
18
+
19
+ def initialize opts = {}
20
+ raise ArgumentError, "should be Hash" unless opts.is_a?(Hash)
21
+
22
+ self.merge! opts
23
+ self.class.all << self
24
+ end
25
+
26
+
27
+ # Will be probleby deparated and implemented as aspect
28
+ #
29
+ # === Example
30
+ #
31
+ # class MyClass < Aspekt::Object; end
32
+ # object1 = MyClass.new
33
+ # object2 = MyClass.new
34
+ # MyClass.all # will contain object1 and object2
35
+
36
+ def self.all
37
+ @all ||= []
38
+ end
39
+
40
+ end
41
+
42
+
43
+
44
+ end
@@ -0,0 +1,157 @@
1
+ module Aspekt
2
+
3
+
4
+ # Examples of usage can also be found in https://github.com/tione/aspekt/blob/development/spec/aspekt/pointcut_spec.rb
5
+
6
+ class Pointcut < Aspekt::Object
7
+
8
+
9
+ # Constructor takes for arguments Hash with keys class(es) or object(s) and method(s), or Array of Hash-es.
10
+ #
11
+ # === Example
12
+ # ==== Pointcut for object method
13
+ #
14
+ # pointcut = Aspekt::Pointcut.new(type: :String, method: :to_s)
15
+ # pointcut = Aspekt::Pointcut.new(type: /String/, method: /to_s/)
16
+ # pointcut = Aspekt::Pointcut.new(type: String, method: 'to_s')
17
+ #
18
+ #
19
+ # ==== Pointcut for class method (singleton method)
20
+ #
21
+ # pointcut = Aspekt::Pointcut.new(instance: String, method: :new)
22
+ # pointcut = Aspekt::Pointcut.new(type: (class String; class<<self; self; end; end, method: :new)
23
+ # pointcut = Aspekt::Pointcut.new(instance: some_object, method: /some_singleton_method/)
24
+ #
25
+ #
26
+ # ==== Pointcut with more than a one matcher
27
+ #
28
+ # pointcut = Aspekt::Pointcut.new(
29
+ # {type: String, method: :capitalize},
30
+ # {types: [String, Array], methods: [/a/, /o/]},
31
+ # {instances: [String, /Arr/], method: /new|try/}
32
+ # )
33
+ #
34
+ # ==== Reasoning behind :type and :instance
35
+ #
36
+ # - using :type or :types (see: http://ruby-doc.org/core-1.9.3/BasicObject.html#method-i-instance_exec)
37
+ # - using :instance or :instances (see: http://ruby-doc.org/core-1.9.3/Module.html#method-i-module_exec)
38
+ #
39
+ # @param Array<{:type => Class|::Object|String|Symbol|Regexp, :instance => ::Object}>
40
+
41
+ def initialize *matchers
42
+ raise ArgumentError, "must have matchers" if matchers.length == 0
43
+
44
+ matchers.each { |matcher| add_matcher matcher }
45
+ end
46
+
47
+
48
+ # Add matcher for a pointcut.
49
+ #
50
+ # Restructures matchers for faster searches.
51
+ # Structure will be in self[:matchers] {instances: {Object1: [:method1, :method2], Object2: [:method3, :method4]}, types: {Class1: [:method5], ...}}
52
+ #
53
+ # @private
54
+ # @param {:method => Symbol, :methods => Array<Symbol>, :instance|:type => Object, :instances|:types => Array<Object> }
55
+
56
+ def add_matcher matcher
57
+ raise ArgumentError, "matcher must be a Hash" unless matcher.is_a?(Hash)
58
+
59
+ # extract values
60
+ matcher_methods = matcher.values_at(:method, :methods).flatten.compact
61
+ matcher_instances = matcher.values_at(:instance, :instances).flatten.compact
62
+ matcher_types = matcher.values_at(:type, :types).flatten.compact
63
+
64
+ raise ArgumentError, "must include :method|:methods" if matcher_methods.length == 0
65
+ raise ArgumentError, "must include :type(s)|:instance(s)" if matcher_instances.length == 0 and matcher_types.length == 0
66
+
67
+ # add values to structure
68
+ matcher_instances.each do |matcher_instance|
69
+ self[:instances] ||= {}
70
+ self[:instances][matcher_instance] ||= []
71
+ self[:instances][matcher_instance].concat matcher_methods
72
+ end
73
+ matcher_types.each do |matcher_type|
74
+ self[:types] ||= {}
75
+ self[:types][matcher_type] ||= []
76
+ self[:types][matcher_type].concat matcher_methods
77
+ end
78
+
79
+ return true
80
+ end
81
+ private :add_matcher
82
+
83
+
84
+ # Argument Hash with keys :class or :object and :method for checking if Pointcut is matching with a object or a class.
85
+ #
86
+ # === Example
87
+ #
88
+ # pointcut = Aspekt::Pointcut.new(class: :SomeClass, method: :some_method).has_matchers_for?(class: SomeClass, method: :some_method)
89
+ # pointcut = Aspekt::Pointcut.new(object: :SomeClass, method: :some_method).has_matchers_for?(object: SomeClass, method: :some_method)
90
+ #
91
+ # @param Array<{:type => Class|::Object|Symbol|Regexp, :instance => ::Object, :method => Symbol}>
92
+
93
+ def has_matchers_for? opts
94
+ raise ArgumentError, "has to include keys 'instance' or 'type', may include 'method'." unless opts[:instance] or opts[:type]
95
+
96
+ # matcher_branch - from where to search matcher, thing - class or object
97
+ matcher_branch, thing = case
98
+ when opts[:instance]; [self[:instances], opts[:instance]];
99
+ when opts[:type]; [self[:types], opts[:type]];
100
+ end
101
+
102
+ matchers_for_thing = matcher_branch.keys.including_same thing
103
+
104
+ unless matchers_for_thing.length == 0
105
+ return true unless opts[:method] # we have no method matching and object is matching
106
+ matcher_objects_methods = matcher_branch.values_at(*matchers_for_thing).flatten
107
+ return true if matcher_objects_methods.include_same?(opts[:method]) # one of the matching object has matching method
108
+ end
109
+
110
+ return false # opts[:object] existed but no matcher was found
111
+ end
112
+
113
+
114
+ # Finds currently existing all Objects for this Pointcut. Returns Object and its method matchers.
115
+ #
116
+ # === Example
117
+ #
118
+ # pointcut = Pointcut.new(types: [/Strin/, /^Hash$/, :ThisClassDoesNotExist)], instance: Array, methods: [/to/, :send])
119
+ # pointcut_matching_types = pointcut.matching[:types] # will be: {String: [/to/, :send], Hash: [/to/, :send]}
120
+ # pointcut_matching_instances = pointcut.matching[:instances] # will be: {Array: [/to/, :send]}
121
+ #
122
+ # # if now ThisClassDoesNotExist will be defined and #matching is called again, then it will also contain it.
123
+
124
+ def matching
125
+ matching = {}
126
+
127
+ defined_classes = nil # this will be created for Regexp object matcher, if needed
128
+
129
+ self.each do |type, object_matchers|
130
+ matching[type] = {}
131
+
132
+ object_matchers.each do |object_matcher, methods|
133
+ case object_matcher
134
+ when Symbol
135
+ begin
136
+ matching_class = object_matcher.to_s.split(/::/).inject(Object) {|a, b| a.const_get(b) } # throws NameError if does not exist
137
+ matching[type][matching_class] = methods
138
+ rescue NameError
139
+ end
140
+ when Regexp
141
+ defined_classes ||= ObjectSpace.each_object(Class).to_a # do it when need it
142
+ defined_classes.select {|defined_class| matching[type][defined_class] = methods if object_matcher.match(defined_class.name) }
143
+ else
144
+ matching[type][object_matcher] = methods
145
+ end
146
+ end
147
+
148
+ end
149
+
150
+ return matching
151
+ end
152
+
153
+
154
+
155
+ end
156
+
157
+ end
@@ -0,0 +1,192 @@
1
+ module Aspekt
2
+
3
+
4
+ # Responsible for weaving code into methods.
5
+ #
6
+ # == You can use Weaver so:
7
+ #
8
+ # === ... in method call to Aspekt::Weaver
9
+ #
10
+ # Aspekt::Weaver.before class: ExistingClass, method: :existing_method do |joinpoint|
11
+ # puts "before context: '#{joinpoint[:context]}' and method '#{joinpoint[:method]}'"
12
+ # end
13
+ #
14
+ #
15
+ # === ... in where you want to (for example class definition body).
16
+ #
17
+ # include Aspekt::Weaver
18
+ #
19
+ # # for object derived from this class:
20
+ # before method: :my_method |joinpoint| # if :type(s)|:method(s) is missing, then automatically type will be self.
21
+ # puts "before #{joinpoint[:method]}"
22
+ # end
23
+ # before type: self, method: :my_method do |joinpoint| # remember that self in class body will be Class
24
+ # puts "before #{joinpoint[:method]}"
25
+ # end
26
+ #
27
+ # # singleton
28
+ # before instance: self, method: :my_method do |joinpoint| # remember that self in class body will be Class and its treated as instance now
29
+ # puts "before #{joinpoint[:method]}"
30
+ # end
31
+ # before type: (class<<self; self; end), method: :my_method do |joinpoint| # accessing self metaclass
32
+ # puts "before #{joinpoint[:method]}"
33
+ # end
34
+ #
35
+ #
36
+ # === More complicated examples:
37
+ #
38
+ # Aspekt::Weaver.before instances: [Object1, :ClassHereForSingletonMethods], types: [Class1, :Class2], methods: [:method1, 'method2', /d3/] do |joinpoint|
39
+ # puts "Called in the context of a object: #{joinpoint[:context]} and method is: #{joinpoint[:method]}"
40
+ # end
41
+ #
42
+ # Aspekt::Weaver.before types: //, methods: /secure/ do |joinpoint|
43
+ # throw SecurityException, "not secure enough" unless authenticated_user.is_secured?
44
+ # end
45
+ #
46
+ #
47
+ #
48
+ # More examples can be found in Rspec tests: https://github.com/tione/aspekt/blob/development/spec/aspekt/weaver_spec.rb
49
+ #
50
+ # === Weaver does not support Regexp in its object and method matching
51
+ #
52
+ # Therefore its recommended to use Aspects, Advices and Pointcuts that do support Regexp.
53
+
54
+ module Weaver
55
+ extend self
56
+
57
+
58
+ # When included to a Class or Object, there will be Weaver methods available.
59
+
60
+ def self.included base #:nodoc:
61
+ base.extend self
62
+ end
63
+
64
+
65
+ # Weaves Proc to method.
66
+ #
67
+ # If argument matcher is a Hash not a Aspekt::Pointcut, it will be converted to it.
68
+ #
69
+ # @param Array<Hash|Aspekt::Pointcut> for hash see Aspekt::Pointcut.new
70
+
71
+ def weave *pointcuts_or_matchers
72
+
73
+ # if not pointcut, convert to it. (it might be a regexp matcher?)
74
+ pointcuts = pointcuts_or_matchers.flatten.collect do |pom|
75
+ case pom
76
+ when Aspekt::Pointcut
77
+ pom
78
+ when Hash
79
+ # Add DSL support for if include Aspekt::Weaver was called in a class.
80
+ pom[:type] = self if !(pom[:type] or pom[:types] or pom[:instance] or pom[:instances]) and self != Aspekt::Weaver
81
+ Aspekt::Pointcut.new pom
82
+ else
83
+ raise ArgumentError, "should be Hash or Aspekt::Pointcut but was #{pom.class} (#{pom})"
84
+ end
85
+ end
86
+
87
+ # iterate over pointcuts
88
+ pointcuts.each do |pointcut|
89
+ # objects to be weaved into
90
+ matching = pointcut.matching
91
+ objects = matching[:types] || {}
92
+ matching[:instances].each do |instance, methods|
93
+ singleton = class<<instance; self; end
94
+ objects[singleton] ||= []
95
+ objects[singleton].concat methods
96
+ end if matching[:instances]
97
+
98
+ # iterate through objects (classes and singleton classes)
99
+ objects.each do |object, method_matchers|
100
+ object.module_exec do
101
+ weave_methods = object.instance_methods.select {|meth| method_matchers.include_same? meth }
102
+
103
+ weave_methods.each do |weave_method|
104
+ m = instance_method(weave_method)
105
+
106
+ # replace orginal method with orginal method + what we got as block to yield
107
+ define_method(weave_method) do |*args, &block|
108
+ bm = m.bind self
109
+ joinpoint = Aspekt::Joinpoint.new args: args, block: block, context: self, method: weave_method, bound_method: bm
110
+ yield(joinpoint)
111
+ end
112
+ end
113
+ end
114
+ end
115
+ end
116
+
117
+ end
118
+
119
+
120
+ # Weaves Proc before the method.
121
+ #
122
+ # == Example usage for object
123
+ #
124
+ # string = "trolololo"
125
+ #
126
+ # Aspekt::Weaver.before object: string, method: :to_s do |joinpoint|
127
+ # puts "Before self: #{self} joinpoint: #{joinpoint}"
128
+ # end
129
+ #
130
+ # new_string = string.to_s # output: Before trolololo at {:args=>[], :block=>nil, :context=>"trolololo", :method=>:to_s, :bound_method=>#<Method: String(String)#to_s>}
131
+ # puts new_string # output: "trolololo"
132
+ #
133
+ #
134
+ # == Example usage for all objects that derive from certain classes
135
+ #
136
+ # Aspekt::Weaver.before classes: [Array, String], methods: [:initialize, :to_s] do
137
+ # puts "#{self} is doing something important #{joinpoint}"
138
+ # end
139
+
140
+ def before opts, &block
141
+ self.weave opts do |joinpoint|
142
+ joinpoint[:context].instance_exec joinpoint, &block # call block with argument joinpoint
143
+ joinpoint.proceed # call orginal method
144
+ end
145
+ end
146
+
147
+
148
+ # Weaves Proc after the method.
149
+ # @see Aspekt::Weaver.before
150
+
151
+ def after opts, &block
152
+ self.weave opts do |joinpoint|
153
+ retval = joinpoint.proceed # call orginal method
154
+ joinpoint.merge! retval: retval # add orginal method retval
155
+ joinpoint[:context].instance_exec joinpoint, &block # call block with argument joinpoint
156
+ retval
157
+ end
158
+ end
159
+
160
+
161
+ # Wheaves Proc for around.
162
+ #
163
+ # == Example usage
164
+ #
165
+ # hash = {key: :value}
166
+ # puts "Hash ID is: #{hash.object_id}"
167
+ #
168
+ # Aspekt::Weaver.around object: hash, method: :has_key? do |joinpoint|
169
+ # puts "Enter: #{joinpoint}"
170
+ # puts "My self ID is: through self #{self.object_id} and through joinpoint[:context] #{joinpoint[:context].object_id}"
171
+ # result = joinpoint.proceed
172
+ # puts "Exit with result #{result}: #{joinpoint}"
173
+ # end
174
+ #
175
+ # # Calling `hash.has_key?(:key)` outputs:
176
+ # # Hash ID is: 15441680
177
+ # # Enter: {:args=>[:key], :block=>nil, :context=>{:key=>:value}, :method=>:has_key?, :bound_method=>#<Method: Hash(Hash)#has_key?>}
178
+ # # My self ID is: through self 15441680 and through joinpoint[:context] 15441680
179
+ # # Exit with result true: {:args=>[:key], :block=>nil, :context=>{:key=>:value}, :method=>:has_key?, :bound_method=>#<Method: Hash(Hash)#has_key?>}
180
+
181
+ def around opts, &block
182
+ self.weave opts do |joinpoint|
183
+ joinpoint[:context].instance_exec joinpoint, &block # call block with argument joinpoint
184
+ # calling joinpoint.proceed should be done in the block
185
+ end
186
+ end
187
+
188
+ end
189
+
190
+
191
+
192
+ end
data/lib/aspekt.rb CHANGED
@@ -1,137 +1,41 @@
1
+ #$: << '.' unless $:.include?('.')
2
+
1
3
  require 'is_same' # https://github.com/tione/is_same
2
4
 
3
5
 
4
- # Supports only already existing objects (and classes) and methods Weaving.
5
- # Aspects defined in folder ./aspects will add runtime loading.
6
+
7
+ # *Aspekt* consists of multiple classes, but for the libary user should be important only following methods:
6
8
  #
7
- # @see Aspekt::Pointcut
8
- # @see Aspekt::Advice
9
+ # * {#before before}
10
+ # * {#after after}
11
+ # * {#around around}
12
+
9
13
  module Aspekt
14
+ end
10
15
 
11
16
 
12
17
 
13
- # All Aspekt's objects are extended from Aspekt::Object.
14
- class Object < Hash
15
-
16
- # Creates a new Aspekt::Object with base class Hash and registres self in class :@all.
17
- def initialize opts
18
- self.merge! opts
19
- self.class.all << self
20
- end
21
-
22
- # Returns all Objects created.
23
- def self.all
24
- @all ||= []
25
- end
26
-
27
- end
28
-
29
-
30
- class Pointcut < Aspekt::Object
31
-
32
- # === Example usage
33
- # class SomeClass
34
- #
35
- # def self.some_method
36
- # return "some_value_from_singleton"
37
- # end
38
- #
39
- # def some_method
40
- # return "some_value"
41
- # end
42
- #
43
- # def other_method
44
- # return "other_value"
45
- # end
46
- #
47
- # end
48
- #
49
- # some_object = SomeClass.new
50
- #
51
- # ==== Pointcuts for instance with types
52
- # pointcut = Aspekt::Pointcut.new( class: :SomeClass, method: :some_method )
53
- # pointcut = Aspekt::Pointcut.new( class: SomeClass, method: /some/ )
54
- # pointcut = Aspekt::Pointcut.new( class: /Some/, method: /some/ )
55
- #
56
- # ==== Pointcut for class method (singleton method)
57
- # pointcut = Aspekt::Pointcut.new( object: SomeClass, method: :some_method )
58
- # pointcut = Aspekt::Pointcut.new( object: /Some/, method: /some/)
59
- # pointcut = Aspekt::Pointcut.new( class: (class SomeClass; class<<self; self; end; end, method: :some_method )
60
- #
61
- # ==== Pointcut for only one object
62
- # pointcut = Aspekt::Pointcut.new( object: some_object, method: :some_method)
63
- #
64
- # === Arguments
65
- # - object: object, method: :method
66
- # - [{object: object, method: :method}, {object: object2, method: :method2
67
- # - objects: [object, object2], methods: [:method, :method2]
68
- # - class: :SomeClass, methods: [:method, :method2]
69
- # - [{objects: [Hash, Array], method: new}, {class: Array, method: :push}, {classes: [Hash, Array, SomeClass], method: /_id/}]
70
- #
71
- # ==== Limitations
72
- # Instances of Regexp, Symbol and Constant are used as finders not thought of as objects.
73
- # TODO: Fix. Possibly add a new hash key value?
74
-
75
- def initialize *matchers
76
- matchers = matchers.flatten.each do |matcher|
77
- matcher[:classes] = [matcher.delete(:class)].flatten if matcher.has_key?(:class)
78
- matcher[:objects] = [matcher.delete(:object)].flatten if matcher.has_key?(:object)
79
- matcher[:methods] = [matcher.delete(:method)].flatten if matcher.has_key?(:method)
80
- end
81
- super matchers: matchers
82
- end
83
-
84
- # === Example usage
85
- # pointcut = Aspekt::Pointcut.new( class: :SomeClass, method: :some_method ).is_matching?( object: SomeClass.new )
86
- # pointcut = Aspekt::Pointcut.new( object: :SomeClass, method: :some_method ).is_matching?( object: SomeClass )
87
- #
88
- # === Arguments
89
- # - object: object
90
- # - object: object, method: :method
91
- def is_matching? opts
92
- raise ArgumentError, "has to include keys 'object', may include 'method'." unless opts[:object]
93
-
94
- self[:matchers].each do |matcher|
95
- # object matching
96
- return true if
97
- (matcher.has_key(:objects) and matcher[:objects].select{|object| object.is_same?(opts[:object])}.length > 0) or
98
- (matcher.has_key(:classes) and matcher[:classes].select{|klass| klass.is_same?(opts[:object].class.name)}.length > 0)
99
- # method matching
100
- return true unless opts.has_key?(:methods) or
101
- (matcher.has_key(:methods) and matcher[:methods].select{|klass| klass.is_same?(opts[:methods].class.name)}.length > 0)
102
- end
103
-
104
- return false
105
- end
106
-
107
- end
108
-
109
-
110
-
111
- class Advice < Aspekt::Object
112
-
113
- # == Example usage
114
- # advice = Aspekt::Advice.new ( type: :before, pointcut: pointcut ) do |joinpoint|
115
- # puts "before #{joinpoint}"
116
- # end
117
- #
118
- # advice = Aspekt::Advice.new ( type: :around, pointcuts: [pointcut1, pointcut2]) do |joinpoint|
119
- # puts "around :start for #{joinpoint}"
120
- # return_value = joinpoint.proceed
121
- # puts "around :end for #{joinpoint}"
122
- # end
123
- #
124
- def initialize opts
125
- opts[:pointcuts] = [opts.delete(:pointcut)].flatten if opts[:pointcut]
126
- super opts
127
- raise ArgumentError, "type has to be Symbol: before, after or around" unless [:before, :after, :around].include?(self[:type])
128
- raise ArgumentError, "no pointcut defined" unless self[:pointcuts].length
129
- end
130
-
131
- end
18
+ # Core
132
19
 
20
+ require 'aspekt/object'
21
+ require 'aspekt/joinpoint'
22
+ require 'aspekt/pointcut'
23
+ require 'aspekt/advice'
24
+ require 'aspekt/weaver'
133
25
 
134
26
 
135
- end
136
27
 
28
+ # Shorthand methods
29
+
30
+ require 'aspekt/helpers/shorthand_methods.rb'
137
31
 
32
+ include Aspekt::Helpers::ShorthandMethods # introduces aspect(), advice() and pointcut()
33
+
34
+
35
+
36
+ # Load aspects
37
+
38
+ $LOAD_PATH.each do |folder|
39
+ aspects_folder = "#{folder}/aspects"
40
+ Dir.glob("#{aspects_folder}/**/*.rb") {|file| require file } if Dir.exists? aspects_folder
41
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: aspekt
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-02-13 00:00:00.000000000Z
12
+ date: 2012-02-25 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: is_same
16
- requirement: &22453880 !ruby/object:Gem::Requirement
16
+ requirement: &13075360 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '0'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *22453880
24
+ version_requirements: *13075360
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rspec
27
- requirement: &22453420 !ruby/object:Gem::Requirement
27
+ requirement: &13074900 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '0'
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *22453420
35
+ version_requirements: *13074900
36
36
  - !ruby/object:Gem::Dependency
37
- name: rdoc
38
- requirement: &22453000 !ruby/object:Gem::Requirement
37
+ name: rspec-core
38
+ requirement: &13074480 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,16 +43,43 @@ dependencies:
43
43
  version: '0'
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *22453000
46
+ version_requirements: *13074480
47
+ - !ruby/object:Gem::Dependency
48
+ name: yard
49
+ requirement: &13074060 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *13074060
58
+ - !ruby/object:Gem::Dependency
59
+ name: yard-rdoc
60
+ requirement: &13073640 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ type: :development
67
+ prerelease: false
68
+ version_requirements: *13073640
47
69
  description: Before, after and around method calls. Supports Regexp matching.
48
70
  email: margus@tione.eu
49
71
  executables: []
50
72
  extensions: []
51
73
  extra_rdoc_files: []
52
74
  files:
53
- - README.rdoc
54
75
  - Gemfile
55
76
  - lib/aspekt.rb
77
+ - lib/aspekt/object.rb
78
+ - lib/aspekt/helpers/shorthand_methods.rb
79
+ - lib/aspekt/joinpoint.rb
80
+ - lib/aspekt/pointcut.rb
81
+ - lib/aspekt/weaver.rb
82
+ - lib/aspekt/advice.rb
56
83
  homepage: https://github.com/tione/aspekt
57
84
  licenses: []
58
85
  post_install_message:
@@ -76,5 +103,6 @@ rubyforge_project:
76
103
  rubygems_version: 1.8.15
77
104
  signing_key:
78
105
  specification_version: 3
79
- summary: IN THE MAKING, NOT USABLE.
106
+ summary: AOP libary with simple and capable before(), after() and around().
80
107
  test_files: []
108
+ has_rdoc:
data/README.rdoc DELETED
@@ -1,19 +0,0 @@
1
- = IN THE MAKING, NOT USABLE
2
-
3
- == Installation
4
-
5
- Add to your Gemfile:
6
- gem 'aspekt'
7
-
8
- And run:
9
- bundle install
10
-
11
- Or run:
12
- gem install aspekt
13
-
14
-
15
- == Usage examples
16
-
17
- === Including before, after and around methods support into Object or Class
18
-
19
- === Creating aspects