twitter-status-bot/.gems/gems/naught-1.0.0/README.markdown

437 lines
11 KiB
Markdown
Raw Normal View History

2014-09-03 08:49:59 +00:00
[![Build Status](https://travis-ci.org/avdi/naught.png?branch=master)](https://travis-ci.org/avdi/naught)
[![Code Climate](https://codeclimate.com/github/avdi/naught.png)](https://codeclimate.com/github/avdi/naught)
[![Coverage Status](https://coveralls.io/repos/avdi/naught/badge.png?branch=master)](https://coveralls.io/r/avdi/naught?branch=master)
[![Gem Version](https://badge.fury.io/rb/naught.png)](http://badge.fury.io/rb/naught)
A quick intro to Naught
-------------------------
#### What's all this now then?
Naught is a toolkit for building [Null
Objects](http://en.wikipedia.org/wiki/Null_Object_pattern) in Ruby.
#### What's that supposed to mean?
Null Objects can make your code more
[confident](http://confidentruby.com).
Here's a method that's not very sure of itself.
```ruby
class Geordi
def make_it_so(logger=nil)
logger && logger.info("Reversing the flux phase capacitance!")
logger && logger.info("Bounding a tachyon particle beam off of Data's cat!")
logger && logger.warn("Warning, bogon levels are rising!")
end
end
```
Now, observe as we give it a dash of confidence with the Null Object
pattern!
```ruby
class NullLogger
def debug(*); end
def info(*); end
def warn(*); end
def error(*); end
def fatal(*); end
end
class Geordi
def make_it_so(logger=NullLogger.new)
logger.info "Reversing the flux phase capacitance!"
logger.info "Bounding a tachyon particle beam off of Data's cat!"
logger.warn "Warning, bogon levels are rising!"
end
end
```
By providing a `NullLogger` which implements [some of] the `Logger`
interface as no-op methods, we've gotten rid of those unsightly `&&`
operators.
#### That was simple enough. Why do I need a library for it?
You don't! The Null Object pattern is a very simple one at its core.
#### And yet here we are…
Yes. While you don't *need* a Null Object library, this one offers some
conveniences you probably won't find elsewhere.
But there's an even more important reason I wrote this library. In the
immortal last words of James T. Kirk: "It was… *fun!*"
#### OK, so how do I use this thing?
Well, what would you like to do?
#### I dunno, gimme an object that responds to any message with nil
Sure thing!
```ruby
require 'naught'
NullObject = Naught.build
null = NullObject.new
null.foo # => nil
null.bar # => nil
```
#### That was… weird. What's with this "build" business?
Naught is a *toolkit* for building null object classes. It is not a
one-size-fits-all solution.
What else can I make for you?
#### How about a "black hole" null object that supports infinite chaining of methods?
OK.
```ruby
require 'naught'
BlackHole = Naught.build do |config|
config.black_hole
end
null = BlackHole.new
null.foo # => <null>
null.foo.bar.baz # => <null>
null << "hello" << "world" # => <null>
```
#### What's that "config" thing?
That's what you use to customize the generated class to your
liking. Internally, Naught uses the [Builder
Pattern](http://en.wikipedia.org/wiki/Builder_pattern) to make this work..
#### Whatever. What if I want a null object that has conversions to Integer, String, etc. using sensible conversions to "zero values"?
We can do that.
```ruby
require 'naught'
NullObject = Naught.build do |config|
config.define_explicit_conversions
end
null = NullObject.new
null.to_s # => ""
null.to_i # => 0
null.to_f # => 0.0
null.to_a # => []
null.to_h # => {}
null.to_c # => (0+0i)
null.to_r # => (0/1)
```
#### Ah, but what about implicit conversions such as `#to_str`? Like what if I want a null object that implicitly splats the same way as an empty array?
Gotcha covered.
```ruby
require 'naught'
NullObject = Naught.build do |config|
config.define_implicit_conversions
end
null = NullObject.new
null.to_str # => ""
null.to_ary # => []
a, b, c = []
a # => nil
b # => nil
c # => nil
x, y, z = null
x # => nil
y # => nil
z # => nil
```
#### How about a null object that only stubs out the methods from a specific class?
That's what `mimic` is for.
```ruby
require 'naught'
NullIO = Naught.build do |config|
config.mimic IO
end
null_io = NullIO.new
null_io << "foo" # => nil
null_io.readline # => nil
null_io.foobar # =>
# ~> -:11:in `<main>': undefined method `foobar' for
# <null:IO>:NullIO (NoMethodError)
```
There is also `impersonate` which takes `mimic` one step further. The
generated null class will be derived from the impersonated class. This
is handy when refitting legacy code that contains type checks.
```ruby
require 'naught'
NullIO = Naught.build do |config|
config.impersonate IO
end
null_io = NullIO.new
IO === null_io # => true
case null_io
when IO
puts "Yep, checks out!"
null_io << "some output"
else
raise "Hey, I expected an IO!"
end
# >> Yep, checks out!
```
#### What about predicate methods? You know, the ones that end with question marks? Shouldn't they return `false` instead of `nil`?
Sure, if you'd like.
```ruby
require 'naught'
NullObject = Naught.build do |config|
config.predicates_return false
end
null = NullObject.new
null.foo # => nil
null.bar? # => false
null.nil? # => false
```
#### Alright smartypants. What if I want to add my own methods?
Not a problem, just define them in the `.build` block.
```ruby
require 'naught'
NullObject = Naught.build do |config|
config.define_explicit_conversions
config.predicates_return false
def to_path
"/dev/null"
end
# You can override methods generated by Naught
def to_s
"NOTHING TO SEE HERE MOVE ALONG"
end
def nil?
true
end
end
null = NullObject.new
null.to_path # => "/dev/null"
null.to_s # => "NOTHING TO SEE HERE MOVE ALONG"
null.nil? # => true
```
#### Got anything else up your sleeve?
Well, we can make the null class a singleton, since null objects
generally have no state.
```ruby
require 'naught'
NullObject = Naught.build do |config|
config.singleton
end
null = NullObject.instance
null.__id__ # => 17844080
NullObject.instance.__id__ # => 17844080
NullObject.new # =>
# ~> -:11:in `<main>': private method `new' called for
# NullObject:Class (NoMethodError)
```
Speaking of null objects with state, we can also enable tracing. This is
handy for playing "where'd that null come from?!" Try doing *that* with
`nil`!
```ruby
require 'naught'
NullObject = Naught.build do |config|
config.traceable
end
null = NullObject.new # line 7
null.__file__ # => "example.rb"
null.__line__ # => 7
```
We can even conditionally enable either singleton mode (for production)
or tracing (for development). Here's an example of using the `$DEBUG`
global variable (set with the `-d` option to ruby) to choose which one.
```ruby
require 'naught'
NullObject = Naught.build do |config|
if $DEBUG
config.traceable
else
config.singleton
end
end
```
The only caveat is that when swapping between singleton and
non-singleton implementations, you should be careful to always
instantiate your null objects with `NullObject.get`, not `.new` or
`.instance`. `.get` will work whether the class is implemented as a
singleton or not.
```ruby
NullObject.get # => <null>
```
#### And if I want to know legacy code better?
Naught can make a null object behave as a pebble object.
```ruby
require 'naught'
NullObject = Naught.build do |config|
if $DEBUG
config.pebble
else
config.black_hole
end
end
```
Now you can pass the pebble object to your code and see which messages are sent to the pebble.
```ruby
null = NullObject.new
class MyConsumer < Struct.new(:producer)
def consume
producer.produce
end
end
MyConsumer.new(null).consume
# >> produce() from consume
# => <null>
```
#### Are you done yet?
Just one more thing. For maximum convenience, Naught-generated null
classes also come with a full suite of conversion functions which can be
included into your classes.
```ruby
require 'naught'
NullObject = Naught.build
include NullObject::Conversions
# Convert nil to null objects. Everything else passes through.
Maybe(42) # => 42
Maybe(nil) # => <null>
Maybe(NullObject.get) # => <null>
Maybe{ 42 } # => 42
# Insist on a non-null (or nil) value
Just(42) # => 42
Just(nil) rescue $! # => #<ArgumentError: Null value: nil>
Just(NullObject.get) rescue $! # => #<ArgumentError: Null value: <null>>
# nils and nulls become nulls. Everything else is rejected.
Null() # => <null>
Null(42) rescue $! # => #<ArgumentError: 42 is not null!>
Null(nil) # => <null>
Null(NullObject.get) # => <null>
# Convert nulls back to nils. Everything else passes through. Useful
# for preventing null objects from "leaking" into public API return
# values.
Actual(42) # => 42
Actual(nil) # => nil
Actual(NullObject.get) # => nil
Actual { 42 } # => 42
```
Installation
--------------
``` {.example}
gem install naught
```
Requirements
--------------
- Ruby 1.9
Contributing
--------------
- Fork, branch, submit PR, blah blah blah. Don't forget tests.
Who's responsible
-------------------
Naught is by [Avdi Grimm](http://devblog.avdi.org/).
Prior Art
---------
This isn't the first Ruby Null Object library. Others to check out include:
- [NullAndVoid](https://github.com/jfelchner/null_and_void)
- [BlankSlate](https://github.com/saturnflyer/blank_slate)
Further reading
-----------------
- [Null Object: Something for
Nothing](http://www.two-sdg.demon.co.uk/curbralan/papers/europlop/NullObject.pdf)
(PDF) by Kevlin Henney
- [The Null Object
Pattern](http://www.cs.oberlin.edu/~jwalker/refs/woolf.ps) (PS) by
Bobby Woolf
- [NullObject](http://www.c2.com/cgi/wiki?NullObject) on WikiWiki
- [Null Object
pattern](http://en.wikipedia.org/wiki/Null_Object_pattern) on
Wikipedia
- [Null Objects and
Falsiness](http://devblog.avdi.org/2011/05/30/null-objects-and-falsiness/),
by Avdi Grimm