aspekt 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.
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