Class: CheapAdvice

Inherits:
Object
  • Object
show all
Includes:
Options
Defined in:
lib/cheap_advice.rb,
lib/cheap_advice/trace.rb,
lib/cheap_advice/configuration.rb

Overview

Provides cheap advice mechanism for Ruby. See: github.com/kstephens/cheap_advice

Defined Under Namespace

Modules: Options, Trace Classes: ActivationRecord, Advised, Configuration, Error

Constant Summary collapse

EMPTY_Hash =
{ }.freeze
EMPTY_Array =
[ ].freeze
EMPTY_String =
''.freeze
NULL_PROC =
lambda { | ar | }
NULL_AROUND_PROC =
lambda { | ar, body | body.call }

Instance Attribute Summary collapse

Attributes included from Options

#options

Instance Method Summary collapse

Methods included from Options

#[], #[]=

Constructor Details

#initialize(*opts, &blk) ⇒ CheapAdvice

options:

:before
:after
:around


51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/cheap_advice.rb', line 51

def initialize *opts, &blk
  super()

  @advised = [ ]
  @advised_for = { }

  opts_hash = Hash === opts[-1] ? opts.pop : { }
  opts_key = opts.shift
  @options = opts_hash

  @before = (opts_key == :before ? blk : opts_hash[:before]) ||
    NULL_PROC
  @after  = (opts_key == :after  ? blk : opts_hash[:after])  ||
    NULL_PROC
  @around = (opts_key == :around ? blk : opts_hash[:around]) ||
    NULL_AROUND_PROC

  @blk = blk
end

Instance Attribute Details

#advisedObject

Collection of Advised method bindings.



39
40
41
# File 'lib/cheap_advice.rb', line 39

def advised
  @advised
end

#advised_extendObject

Module or Array of Modules to extend new Advised objects with.



42
43
44
# File 'lib/cheap_advice.rb', line 42

def advised_extend
  @advised_extend
end

#afterObject

Procs called before, after, and around the original method.



36
37
38
# File 'lib/cheap_advice.rb', line 36

def after
  @after
end

#aroundObject

Procs called before, after, and around the original method.



36
37
38
# File 'lib/cheap_advice.rb', line 36

def around
  @around
end

#beforeObject

Procs called before, after, and around the original method.



36
37
38
# File 'lib/cheap_advice.rb', line 36

def before
  @before
end

Instance Method Details

#advise!(mod, method, *opts) ⇒ Object

Apply advice a method on a Module (or Class).

The advised method is enabled immediately (this may change in a future release).

Returns an Advised object that describes what method was advised.

mod can be a String, a Module or an Array of either. method can be a String, a Symbol or an Array of either. if either are Arrays the result will be an Array of Advised objects.

The type of method scope can be specified by:

  • :instance (default)

  • :class

  • :module

Any additional Hash options are propagated to the Advised binding object, which can be accessed from the ActivationRecord passed to the Advice block(s).

Examples:

advice = CheapAdvice(:around) { | ar, body | ...; body.call; ... }
advice.advise! MyClass, :instance_method, options_hash
advice.advise! MyClass, :class_method, :class

Each Advised object is extended with #advised_extend. The #advised Array lists all Advised object.



98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/cheap_advice.rb', line 98

def advise! mod, method, *opts
  return mod.map { | x | advise! x, method, *opts } if
    Array === mod
  return method.map { | x | advise! mod, x, *opts } if
    Array === method

  opts_hash = Hash === opts[-1] ? opts.pop : { }
  kind = opts.shift
  kind ||= :instance

  method = method.to_sym

  @mutex.synchronize do
    unless @enabled_once
      self.enabled!
      @enabled_once = true
    end

    advised = advised_for mod, method, kind, opts_hash

    advised.enable! # Should this really be automatically enabled??

    advised
  end
end

#advised_for(mod, meth, kind, opts) ⇒ Object

Returns the existing Advised binding or creates a new one.



139
140
141
142
# File 'lib/cheap_advice.rb', line 139

def advised_for mod, meth, kind, opts
  (@advised_for[[ mod, meth, kind ]] ||=
    construct_advised_for(mod, meth, kind)).set_options!(opts)
end

#advised_select(mod, meth, kind) ⇒ Object



130
131
132
133
134
135
136
# File 'lib/cheap_advice.rb', line 130

def advised_select mod, meth, kind
  @advised.select do | ad |
    (mod ? mod == ad.mod : true) &&
      (meth ? meth == ad.meth : true) &&
      (kind ? kind == ad.kind : true)
  end
end

#construct_advised_for(mod, meth, kind) ⇒ Object

Constructs an Advised binding from this Advice.



145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
# File 'lib/cheap_advice.rb', line 145

def construct_advised_for mod, meth, kind
  advice = self

  advised = Advised.new(advice, mod, meth, kind)

  case @advised_extend
  when nil
  when Module
    advised.extend(@advised_extend)
  when Array
    @advised_extend.each { | m | advised.extend(m) }
  else
    raise TypeError, "advised_extend: expected nil, Module or Array of Modules, given #{@advised_extend.class}"
  end

  advised.register_advice_methods!

  advised.define_new_method!

  @advised << advised

  advised
end

#disable!Object Also known as: unadvise!

Disables all currently Advised methods.



171
172
173
174
175
176
# File 'lib/cheap_advice.rb', line 171

def disable!
  @mutex.synchronize do
    @advised.each { | x | x.disable! }
  end
  self
end

#enable!Object Also known as: readvise!

Enables all currently Advised methods.



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

def enable!
  @mutex.synchronize do
    @advised.each { | x | x.enable! }
  end
  self
end

#enabled!Object

Called once the first time this advice is enabled. Instances can override this method.



126
127
128
# File 'lib/cheap_advice.rb', line 126

def enabled!
  self
end