README.md
# Busted [![Build Status](https://travis-ci.org/simeonwillbanks/busted.png?branch=master)](https://travis-ci.org/simeonwillbanks/busted) [![Code Climate](https://codeclimate.com/github/simeonwillbanks/busted.png)](https://codeclimate.com/github/simeonwillbanks/busted)
### Find code that busts the Ruby 2.1+ cache.
**Busted** reports when code invalidates Ruby's internal cache.
- [Basic Usage](#basic-usage) reports cache invalidations
- [Advanced Usage](#advanced-usage) details exact invalidation locations via [`dtrace`](http://en.wikipedia.org/wiki/DTrace)
- [Coming Soon](https://github.com/simeonwillbanks/busted/issues/2) a Rails profiler for reporting cache invalidations per request
- **Busted** relies upon [RubyVM.stat](http://ruby-doc.org/core-2.1.0/RubyVM.html#method-c-stat) and the [ruby:::method-cache-clear](http://ruby-doc.org/core-2.1.0/doc/dtrace_probes_rdoc.html) `dtrace` probe
## Basic Usage
*Any Cache*
```ruby
Busted.cache? do
class Beer
end
end
#=> true
Busted.run do
class Pizza
end
end
#=> {:invalidations=>{:method=>0, :constant=>1}}
Busted.start
class Pie
end
Busted.finish
#=> {:invalidations=>{:method=>0, :constant=>1}}
```
*Method Cache*
```ruby
Busted.method_cache? do
def beer
end
end
#=> true
Busted.method_cache_invalidations do
def pizza
end
end
#=> 1
Busted.run do
def pie
end
end
#=> {:invalidations=>{:method=>1, :constant=>0}}
Busted.start
def smoothie
end
Busted.finish
#=> {:invalidations=>{:method=>1, :constant=>0}}
```
*Constant Cache*
```ruby
Busted.constant_cache? do
STOUT = "stout"
end
#=> true
Busted.constant_cache_invalidations do
CHEESE = "cheese"
end
#=> 1
Busted.run do
PIE = "pumpkin"
end
#=> {:invalidations=>{:method=>0, :constant=>1}}
Busted.start
SMOOTHIE = "blueberry"
Busted.finish
#=> {:invalidations=>{:method=>0, :constant=>1}}
```
*No Cache Busted*
```ruby
Busted.cache? do
beer = "beer"
end
#=> false
Busted.run do
pizza = "pizza"
end
#=> {:invalidations=>{:method=>0, :constant=>0}}
Busted.start
pie = "pie"
Busted.finish
#=> {:invalidations=>{:method=>0, :constant=>0}}
```
## Advanced Usage
**Busted** can report method cache invalidation locations via [`dtrace`](http://en.wikipedia.org/wiki/DTrace). The running process must have root privileges, and `dtrace` must be installed.
*trace.rb*
```ruby
require "busted"
require "pp"
report = Busted.run(trace: true) do
def cookie
end
end
pp report
```
```bash
$ whoami
simeon
$ id simeon
uid=501(simeon) gid=20(staff) groups=20(staff),80(admin)
# simeon is an admin; sudo does not require a password
$ sudo grep admin /etc/sudoers
%admin ALL=(ALL) NOPASSWD: ALL
$ sudo dtrace -V
dtrace: Sun D 1.6.2
# No need to sudo
# Busted runs dtrace in a child process with root privileges
$ ruby trace.rb
{:invalidations=>{:method=>1, :constant=>0},
:traces=>
{:method=>[{:class=>"global", :sourcefile=>"trace.rb", :lineno=>"5"}]}}
```
*start_finish_trace.rb*
```ruby
require "busted"
require "pp"
Busted.start trace: true
def ice_cream
end
pp Busted.finish
```
```bash
$ ruby start_finish_trace.rb
{:invalidations=>{:method=>1, :constant=>0},
:traces=>
{:method=>
[{:class=>"global", :sourcefile=>"start_finish_trace.rb", :lineno=>"5"}]}}
```
**Busted** includes an [example `dtrace` probe](/dtrace/probes/examples/method-cache-clear.d) for use on the command line or an application. See the [probe](/dtrace/probes/examples/method-cache-clear.d) for usage.
## Installation
***Requires MRI Ruby 2.1+***
Add this line to your application's Gemfile:
gem "busted"
And then execute:
$ bundle
Or install it yourself as:
$ gem install busted
## Contributing
Check out [this guide](/CONTRIBUTING.md) if you'd like to contribute.
## License
This project is licensed under the [MIT License](/LICENSE.txt).
## Standing On The Shoulders Of Giants
A big *thank you* to [Charlie Somerville](https://github.com/charliesome) and [Aman Gupta](https://github.com/tmm1) for helping flesh out `RubyVM.stat` and committing it to Ruby core!