Module: Aspekt::Weaver

Extended by:
Weaver
Included in:
Weaver
Defined in:
lib/aspekt/weaver.rb

Overview

Responsible for weaving code into methods.

You can use Weaver so:

… in method call to Aspekt::Weaver

Aspekt::Weaver.before class: ExistingClass, method: :existing_method do |joinpoint|
  puts "before context: '#{joinpoint[:context]}' and method '#{joinpoint[:method]}'"  
end

… in where you want to (for example class definition body).

include Aspekt::Weaver

# for object derived from this class:
before method: :my_method |joinpoint|                  # if :type(s)|:method(s) is missing, then automatically type will be self.
  puts "before #{joinpoint[:method]}"
end
before type: self, method: :my_method do |joinpoint|   # remember that self in class body will be Class
  puts "before #{joinpoint[:method]}"
end

# singleton
before instance: self, method: :my_method do |joinpoint|   # remember that self in class body will be Class and its treated as instance now
  puts "before #{joinpoint[:method]}"
end
before type: (class<<self; self; end), method: :my_method do |joinpoint|   # accessing self metaclass
  puts "before #{joinpoint[:method]}"
end

More complicated examples:

Aspekt::Weaver.before instances: [Object1, :ClassHereForSingletonMethods], types: [Class1, :Class2], methods: [:method1, 'method2', /d3/] do |joinpoint|
  puts "Called in the context of a object: #{joinpoint[:context]} and method is: #{joinpoint[:method]}"
end

Aspekt::Weaver.before types: //, methods: /secure/ do |joinpoint|
  throw SecurityException, "not secure enough" unless authenticated_user.is_secured?
end

More examples can be found in Rspec tests: github.com/tione/aspekt/blob/development/spec/aspekt/weaver_spec.rb

Weaver does not support Regexp in its object and method matching

Therefore its recommended to use Aspects, Advices and Pointcuts that do support Regexp.

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.included(base) ⇒ Object

When included to a Class or Object, there will be Weaver methods available.



60
61
62
# File 'lib/aspekt/weaver.rb', line 60

def self.included base #:nodoc:
  base.extend self
end

Instance Method Details

#after(opts, &block) ⇒ Object

Weaves Proc after the method.

See Also:

  • before


151
152
153
154
155
156
157
158
# File 'lib/aspekt/weaver.rb', line 151

def after opts, &block
  self.weave opts do |joinpoint|
    retval = joinpoint.proceed                                                    # call orginal method
    joinpoint.merge! retval: retval                                               # add orginal method retval 
    joinpoint[:context].instance_exec joinpoint, &block                           # call block with argument joinpoint
    retval
  end
end

#around(opts, &block) ⇒ Object

Wheaves Proc for around.

Example usage

hash = {key: :value}
puts "Hash ID is: #{hash.object_id}"

Aspekt::Weaver.around object: hash, method: :has_key? do |joinpoint|
  puts "Enter: #{joinpoint}"
  puts "My self ID is: through self #{self.object_id} and through joinpoint[:context] #{joinpoint[:context].object_id}"
  result = joinpoint.proceed
  puts "Exit with result #{result}: #{joinpoint}"
end

# Calling `hash.has_key?(:key)` outputs:   
# Hash ID is: 15441680
# Enter: {:args=>[:key], :block=>nil, :context=>{:key=>:value}, :method=>:has_key?, :bound_method=>#<Method: Hash(Hash)#has_key?>}
# My self ID is: through self 15441680 and through joinpoint[:context] 15441680
# Exit with result true: {:args=>[:key], :block=>nil, :context=>{:key=>:value}, :method=>:has_key?, :bound_method=>#<Method: Hash(Hash)#has_key?>}


181
182
183
184
185
186
# File 'lib/aspekt/weaver.rb', line 181

def around opts, &block
  self.weave opts do |joinpoint|
    joinpoint[:context].instance_exec joinpoint, &block                           # call block with argument joinpoint
    # calling joinpoint.proceed should be done in the block
  end
end

#before(opts, &block) ⇒ Object

Weaves Proc before the method.

Example usage for object

string = "trolololo"  

Aspekt::Weaver.before object: string, method: :to_s do |joinpoint|
  puts "Before self: #{self} joinpoint: #{joinpoint}"
end

new_string = string.to_s  # output: Before trolololo at {:args=>[], :block=>nil, :context=>"trolololo", :method=>:to_s, :bound_method=>#<Method: String(String)#to_s>}
puts new_string           # output: "trolololo"

Example usage for all objects that derive from certain classes

Aspekt::Weaver.before classes: [Array, String], methods: [:initialize, :to_s] do
  puts "#{self} is doing something important #{joinpoint}"
end


140
141
142
143
144
145
# File 'lib/aspekt/weaver.rb', line 140

def before opts, &block
  self.weave opts do |joinpoint|
    joinpoint[:context].instance_exec joinpoint, &block                           # call block with argument joinpoint
    joinpoint.proceed                                                             # call orginal method
  end
end

#weave(*pointcuts_or_matchers) ⇒ Object

Weaves Proc to method.

If argument matcher is a Hash not a Aspekt::Pointcut, it will be converted to it.

Parameters:



71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/aspekt/weaver.rb', line 71

def weave *pointcuts_or_matchers

  # if not pointcut, convert to it. (it might be a regexp matcher?)
  pointcuts = pointcuts_or_matchers.flatten.collect do |pom|
    case pom
    when Aspekt::Pointcut
      pom
    when Hash
      # Add DSL support for if include Aspekt::Weaver was called in a class.
      pom[:type] = self if !(pom[:type] or pom[:types] or pom[:instance] or pom[:instances]) and self != Aspekt::Weaver 
      Aspekt::Pointcut.new pom
    else
      raise ArgumentError, "should be Hash or Aspekt::Pointcut but was #{pom.class} (#{pom})"
    end
  end

  # iterate over pointcuts
  pointcuts.each do |pointcut| 
    # objects to be weaved into
    matching = pointcut.matching 
    objects = matching[:types] || {}
    matching[:instances].each do |instance, methods| 
      singleton = class<<instance; self; end
      objects[singleton] ||= []
      objects[singleton].concat methods
    end if matching[:instances]

    # iterate through objects (classes and singleton classes)
    objects.each do |object, method_matchers|
      object.module_exec do
        weave_methods = object.instance_methods.select {|meth| method_matchers.include_same? meth }
        
        weave_methods.each do |weave_method|
          m = instance_method(weave_method)

          # replace orginal method with orginal method + what we got as block to yield
          define_method(weave_method) do |*args, &block|
            bm = m.bind self
            joinpoint = Aspekt::Joinpoint.new args: args, block: block, context: self, method: weave_method, bound_method: bm
            yield(joinpoint)
          end 
        end
      end
    end
  end

end