Giter Site home page Giter Site logo

advisor's Introduction

Advisor: Solve your cross-cutting concerns without mumbo-jumbo.

https://travis-ci.org/rranelli/advisor.svg

Advisor is Ruby gem that enables you to solve cross-cutting concerns without the usual method-renaming present in most alternatives.

Advisor intercepts method calls and allows you to mix cross cutting concerns and tedious book-keeping tasks. Logging, metric reporting, auditing, timing, timeouting can be handled beautifully.

Advisor works with plain Ruby modules and do not mess your stack trace.

Also, the amount of intrusion required to set up is kept to a minimum while still keeping it discoverable. Every affected class must explicitly extend a given module and every affected method call must also be declared.

Usage

Advisor is organized between two main concepts only: Advisor modules and Advice modules. Advisor modules are extensions applied to your classes and Advice modules define the actual behavior of the intercepted method calls.

In order to understand better how Advisor works, we are going to use an example:

Example

Suppose you want to log calls to some methods but don’t want to keep repeating the message formatting or messing with the method body. Advisor provides a simple built-in module called Advisor::Loggable that solves this issue.

class Account
  extend Advisor::Loggable

  log_calls_to :deposit

  def deposit(_amount, _origin)
    #...
    :done
  end
end

In an interactive console:

$ Account.new.deposit(300, 'Jane Doe')
# => I, [2015-04-11T21:26:42.405180 #13840]  INFO -- : [Time=2015-04-11 21:26:42 -0300][Thread=70183196300040]Called: Account#deposit(300, "Jane Doe")
# => :done

As you can see, the method call is intercepted and a message is printed to stdout.

Advisor achieves this by using Ruby 2.0’s Module#prepend. If you were to check Account’s ancestors you would get:

$ Account.ancestors
# => [Advisor::Advices::CallLogger(deposit), Account, Object, Kernel, BasicObject]

As you can see, the Advisor::Advices::CallLogger(deposit) module is listed before Account itself in the ancestor chain.

In the next session we are going to explain how to write your own custom advice.

Writing an Advice

An Advice defines what to do with the advised method call.

The required interface for an advice must be like the example bellow:

class Advice
  def initialize(receiver, advised_method, call_args, **options)
    # The constructor of an advice must receive 3 arguments and extra options.
    # Those extra options are defined when applying the extension to the advised
    # class.
  end

  def self.applier_method
    # Must return the name of the method which must be called in the class body
    # to define which methods will be intercepted with the advice.

    # In the case of `Advisor::Loggable`, this method returns `:log_calls_to`
  end

  def call
    # This is the body of the advice.
    #
    # This method will always be called with the block `{ super(*call_args,
    # &blk) }` That means the method implementation can decide when to run the
    # advised method call. Check `Advisor::Advices::CallLogger` for an example.
  end
end
Creating an Advisor module

Every Advisor module must be built from the corresponding Advice by using the Advisor::Factory#build method.

Advisor::Loggable is built from the Advisor::Advices::CallLogger module.

Advisor::Loggable itself is built like this:

module Advisor
  Loggable = Factory.new(Advices::CallLogger).build
end

Hence, if your custom Advice complies to the required interface, Advisor::Factory will be able to convert it to an extension module with no problems.

Disclaimer

This version of the library is still experimental and probably not production ready. Use at your own risk.

advisor's People

Contributors

p-lambert avatar ricardotealdi avatar rranelli avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

advisor's Issues

Error when using allow_any_instance_of

Given a method that is being listened by log_calls_to, example:

class NextLogin
  extend Advisor::Loggable
  log_calls_to :execute, result: true

When using allow_any_instance_of, rspec raises a error:

allow_any_instance_of(
  NextLogin
).to receive(:execute).and_return('lala')
Failure/Error: allow_any_instance_of(
       Using `any_instance` to stub a method (execute) that has been defined on a prepended module (Advisor::Advices::CallLogger(execute)) is not supported.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.