Class: CheapAdvice::Advised
- Inherits:
-
Object
- Object
- CheapAdvice::Advised
- Includes:
- Options
- Defined in:
- lib/cheap_advice.rb
Overview
Represents the application/binding of advice to a class and method.
Constant Summary collapse
- INSTANCE_SEP =
'#'.freeze
- MODULE_SEP =
'.'.freeze
- @@mutex =
Mutex.new
Instance Attribute Summary collapse
-
#advice ⇒ Object
readonly
The Advice being applied to the Module and method.
-
#advised_id ⇒ Object
readonly
The unique Advised id used to generate unique method names.
-
#after_meth ⇒ Object
readonly
The name of the before, after and around methods.
-
#around_meth ⇒ Object
readonly
The name of the before, after and around methods.
-
#before_meth ⇒ Object
readonly
The name of the before, after and around methods.
-
#enabled ⇒ Object
readonly
True if the Advised methods are currently installed.
-
#kind ⇒ Object
readonly
The Module, method and kind (instance, class or module method).
-
#meth ⇒ Object
readonly
The Module, method and kind (instance, class or module method).
-
#mod ⇒ Object
(also: #cls)
readonly
The Module, method and kind (instance, class or module method).
-
#new_meth ⇒ Object
readonly
The name of the old and new method being patched in.
-
#old_meth ⇒ Object
readonly
The name of the old and new method being patched in.
Attributes included from Options
Instance Method Summary collapse
-
#==(x) ⇒ Object
True if the advice, mod, method and kind are equal.
-
#define_new_method! ⇒ Object
Defines the new advised method in the target Module.
-
#disable! ⇒ Object
(also: #unadvise!)
Disables the advice on this method.
-
#disabled! ⇒ Object
Called when Advised is enabled.
-
#enable! ⇒ Object
(also: #advise!)
Enables the advice on this method.
-
#enabled! ⇒ Object
Called when Advised is enabled.
-
#hash ⇒ Object
Support for Hash.
-
#initialize(*args) ⇒ Advised
constructor
A new instance of Advised.
-
#meth_to_s ⇒ Object
The string name for the method.
-
#mod_resolve ⇒ Object
Resolves mod Strings to the actual target Module.
-
#mod_target ⇒ Object
Returns the target Module for the kind of method.
-
#register_advice_methods! ⇒ Object
Registers the before, after and around advice methods.
- #scope ⇒ Object
- #set_options!(options) ⇒ Object
Methods included from Options
Constructor Details
#initialize(*args) ⇒ Advised
Returns a new instance of Advised.
215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 |
# File 'lib/cheap_advice.rb', line 215 def initialize *args @mutex = Mutex.new @advice, @mod, @meth, @kind, @options = *args case @kind when :instance, :class, :module else raise ArgumentError, "invalid kind #{kind.inspect}" end @options ||= { } @@mutex.synchronize do @advised_id = @@advised_id += 1 end @old_meth = :"__advice_old_#{@@advised_id}_#{@meth}" @new_meth = :"__advice_new_#{@@advised_id}_#{@meth}" @before_meth = :"__advice_before_#{@@advised_id}_#{@meth}" @after_meth = :"__advice_after_#{@@advised_id}_#{@meth}" @around_meth = :"__advice_around_#{@@advised_id}_#{@meth}" @enabled = @advice_methods_applied = false end |
Instance Attribute Details
#advice ⇒ Object (readonly)
The Advice being applied to the Module and method.
198 199 200 |
# File 'lib/cheap_advice.rb', line 198 def advice @advice end |
#advised_id ⇒ Object (readonly)
The unique Advised id used to generate unique method names.
204 205 206 |
# File 'lib/cheap_advice.rb', line 204 def advised_id @advised_id end |
#after_meth ⇒ Object (readonly)
The name of the before, after and around methods.
210 211 212 |
# File 'lib/cheap_advice.rb', line 210 def after_meth @after_meth end |
#around_meth ⇒ Object (readonly)
The name of the before, after and around methods.
210 211 212 |
# File 'lib/cheap_advice.rb', line 210 def around_meth @around_meth end |
#before_meth ⇒ Object (readonly)
The name of the before, after and around methods.
210 211 212 |
# File 'lib/cheap_advice.rb', line 210 def before_meth @before_meth end |
#enabled ⇒ Object (readonly)
True if the Advised methods are currently installed.
213 214 215 |
# File 'lib/cheap_advice.rb', line 213 def enabled @enabled end |
#kind ⇒ Object (readonly)
The Module, method and kind (instance, class or module method)
200 201 202 |
# File 'lib/cheap_advice.rb', line 200 def kind @kind end |
#meth ⇒ Object (readonly)
The Module, method and kind (instance, class or module method)
200 201 202 |
# File 'lib/cheap_advice.rb', line 200 def meth @meth end |
#mod ⇒ Object (readonly) Also known as: cls
The Module, method and kind (instance, class or module method)
200 201 202 |
# File 'lib/cheap_advice.rb', line 200 def mod @mod end |
#new_meth ⇒ Object (readonly)
The name of the old and new method being patched in.
207 208 209 |
# File 'lib/cheap_advice.rb', line 207 def new_meth @new_meth end |
#old_meth ⇒ Object (readonly)
The name of the old and new method being patched in.
207 208 209 |
# File 'lib/cheap_advice.rb', line 207 def old_meth @old_meth end |
Instance Method Details
#==(x) ⇒ Object
True if the advice, mod, method and kind are equal.
259 260 261 262 |
# File 'lib/cheap_advice.rb', line 259 def == x return false unless self.class === x @advice == x.advice && @mod == x.mod && @meth == x.meth && @kind == x.kind end |
#define_new_method! ⇒ Object
Defines the new advised method in the target Module.
315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 |
# File 'lib/cheap_advice.rb', line 315 def define_new_method! advised = self advised.mod_target.instance_eval do define_method advised.new_meth do | *args, &block | ar = ActivationRecord.new(advised, self, args, block) # Proc to invoke the old method with :before and :after advise hooks. body = Proc.new do self.__send__(advised.before_meth, ar) begin ar.result = self.__send__(advised.old_meth, *ar.args, &ar.block) rescue ::Object => err ar.error = err ensure self.__send__(advised.after_meth, ar) end ar.result end # Invoke the :around advice with the body Proc. self.__send__(advised.around_meth, ar, body) # Reraise Exception, if occured. raise ar.error if ar.error # Return the message result to caller. ar.result end # define_method end # instance_eval self end |
#disable! ⇒ Object Also known as: unadvise!
Disables the advice on this method.
417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 |
# File 'lib/cheap_advice.rb', line 417 def disable! @mutex.synchronize do return self if ! @enabled this = self mod_target.instance_eval do if method_defined? this.old_meth alias_method this.meth, this.old_meth case this.scope when :private private this.meth when :protected protected this.meth end end end disabled! @enabled = false end self end |
#disabled! ⇒ Object
Called when Advised is enabled. Instances can override this method.
451 452 453 |
# File 'lib/cheap_advice.rb', line 451 def disabled! self end |
#enable! ⇒ Object Also known as: advise!
Enables the advice on this method.
384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 |
# File 'lib/cheap_advice.rb', line 384 def enable! @mutex.synchronize do return self if @enabled this = self mod_target.instance_eval do case this.scope when :public else public this.meth end alias_method this.old_meth, this.meth if ! method_defined? this.old_meth alias_method this.meth, this.new_meth case this.scope when :private private this.meth when :protected protected this.meth end end enabled! @enabled = true end self end |
#enabled! ⇒ Object
Called when Advised is enabled. Instances can override this method.
445 446 447 |
# File 'lib/cheap_advice.rb', line 445 def enabled! self end |
#hash ⇒ Object
Support for Hash.
265 266 267 |
# File 'lib/cheap_advice.rb', line 265 def hash @advice.hash ^ @mod.hash ^ @meth.hash ^ @kind.hash end |
#meth_to_s ⇒ Object
The string name for the method. Returns “Foo#bar” for an instance method named :bar on class Foo. Returns “Foo.bar” for a class or module method named .bar on class Foo.
253 254 255 256 |
# File 'lib/cheap_advice.rb', line 253 def meth_to_s @meth_to_s ||= "#{@mod}#{@kind == :instance ? INSTANCE_SEP : MODULE_SEP}#{@meth}".freeze end |
#mod_resolve ⇒ Object
Resolves mod Strings to the actual target Module.
282 283 284 285 286 287 288 289 290 291 292 293 |
# File 'lib/cheap_advice.rb', line 282 def mod_resolve case @mod when Module @mod when String, Symbol @mod.to_s.split('::'). reject { | name | name.empty?}. inject(Object) { | namespace, name | namespace.const_get(name) } else raise TypeError, "mod_resolve: expected Module, String, Symbol, given #{@mod.class}" end end |
#mod_target ⇒ Object
Returns the target Module for the kind of method.
270 271 272 273 274 275 276 277 278 279 |
# File 'lib/cheap_advice.rb', line 270 def mod_target case @kind when :instance mod_resolve when :class, :module (class << mod_resolve; self; end) else raise ArgumentError, "mod_target: invalid kind #{kind.inspect}" end end |
#register_advice_methods! ⇒ Object
Registers the before, after and around advice methods.
296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 |
# File 'lib/cheap_advice.rb', line 296 def register_advice_methods! scope # force calculation of scope before aliasing methods. @mutex.synchronize do return self if @advice_methods_registered this = self mod_target.instance_eval do define_method(this.before_meth, &this.advice.before) define_method(this.after_meth, &this.advice.after) define_method(this.around_meth, &this.advice.around) end @advice_methods_registered = true end self end |
#scope ⇒ Object
350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 |
# File 'lib/cheap_advice.rb', line 350 def scope @scope ||= @mutex.synchronize do this = self mod_target.instance_eval do case when private_instance_methods(false).include?(this.meth.to_s) :private when protected_instance_methods(false).include?(this.meth.to_s) :protected else :public end end end end |
#set_options!(options) ⇒ Object
242 243 244 245 |
# File 'lib/cheap_advice.rb', line 242 def @options = || { } self end |