Wednesday, December 1, 2010

Bundler: How You Can Start Using It Today

I took some time before I could understand and use Bundler in my Ruby projects. There was something about it that just didn't click with me.

In hindsight, I should have learned Bundler earlier. Once you "get" the workflow, Bundler is very simple and very powerful at the same time. It's one of those well-made tools that makes it dead easy to do simple stuff, and entirely possible to do complex stuff when the time comes.

I wrote a Bundler primer for the latest PragPub, the Pragmatic Programmer's monthly magazine. If you're not using Bundler yet, I hope that this article will turn you into a Bundler user before dinner.

(Even if you don't care about Bundler, the rest of the magazine is likely to have something for you. PragPub is my favourite programming mag ever, and I'm proud to write for them when it happens.)

Thursday, October 7, 2010

The method_missing() Chainsaw

I have a guest post about method_missing() on RubyLearning. It's intended for Ruby beginners, but it's attracting interesting discussions in the comments and on Hacker News - mainly from people dissing Dynamic Methods in favor of Ghost Methods.

Sunday, August 29, 2010

The Ruby Metaprogramming Spell Book

Here are all the spells from Metaprogramming Ruby, as link-friendly Gists.

March 2014 update: I updated this list to match the second edition of the book. I added three spells that are new to Ruby 2.x: Refinement, Refinement Wrapper and Prepended Wrapper. I also dropped a few spells that don't seem to be as relevant today as they used to be, at least in the context of this book: Argument Array, Named Arguments, Pattern Dispatch and Class Extension Mixin.

# ===================
# Spell: Around Alias
# ===================
# Call the previous, aliased version of a method from a redefined method.
class String
alias :old_reverse :reverse
def reverse
"x#{old_reverse}x"
end
end
"abc".reverse # => "xcbax"
# For more information: http://www.pragprog.com/titles/ppmetr/metaprogramming-ruby
view raw around_alias.rb hosted with ❤ by GitHub

# ===================
# Spell: Blank Slate
# ===================
# Remove methods from an object to turn them into Ghost Methods (http://gist.github.com/534776).
class C
def method_missing(name, *args)
"a Ghost Method"
end
end
obj = C.new
obj.to_s # => "#<C:0x007fbb2a10d2f8>"
class D < BasicObject
def method_missing(name, *args)
"a Ghost Method"
end
end
blank_slate = D.new
blank_slate.to_s # => "a Ghost Method"
# For more information: http://www.pragprog.com/titles/ppmetr/metaprogramming-ruby
view raw blank_slate.rb hosted with ❤ by GitHub

# ======================
# Spell: Class Extension
# ======================
# Define class methods by mixing a module into a class’s singleton class
# (a special case of Object Extension - http://gist.github.com/534667).
class C; end
module M
def my_method
'a class method'
end
end
class << C
include M
end
C.my_method # => "a class method"
# For more information: http://www.pragprog.com/titles/ppmetr/metaprogramming-ruby

# ==============================
# Spell: Class Instance Variable
# ==============================
# Store class-level state in an instance variable of the Class object.
class C
@my_class_instance_variable = "some value"
def self.class_attribute
@my_class_instance_variable
end
end
C.class_attribute # => "some value"
# For more information: http://www.pragprog.com/titles/ppmetr/metaprogramming-ruby

# ==================
# Spell: Class Macro
# ==================
# Use a class method in a class definition.
class C; end
class << C
def my_macro(arg)
"my_macro(#{arg}) called"
end
end
class C
my_macro :x # => "my_macro(x) called"
end
# For more information: http://www.pragprog.com/titles/ppmetr/metaprogramming-ruby
view raw class_macro.rb hosted with ❤ by GitHub

# =================
# Spell: Clean Room
# =================
# Use an object as an environment in which to evaluate a block.
class CleanRoom
def a_useful_method(x); x * 2; end
end
CleanRoom.new.instance_eval { a_useful_method(3) } # => 6
# For more information: http://www.pragprog.com/titles/ppmetr/metaprogramming-ruby
view raw clean_room.rb hosted with ❤ by GitHub

# =====================
# Spell: Code Processor
# =====================
# Process Strings of Code (http://gist.github.com/535047) from an external source.
File.readlines("file_containing_lines_of_ruby.txt").each do |line|
puts "#{line.chomp} ==> #{eval(line)}"
end
# >> 1 + 1 ==> 2
# >> 3 * 2 ==> 6
# >> Math.log10(100) ==> 2.0
# For more information: http://www.pragprog.com/titles/ppmetr/metaprogramming-ruby
1 + 1
3 * 2
Math.log10(100)

# ====================
# Spell: Context Probe
# ====================
# Execute a block to access information in an object’s context.
class C
def initialize
@x = "a private instance variable"
end
end
obj = C.new
obj.instance_eval { @x } # => "a private instance variable"
# For more information: http://www.pragprog.com/titles/ppmetr/metaprogramming-ruby

# ==========================
# Spell: Deferred Evaluation
# ==========================
# Store a piece of code and its context in a proc or lambda for evaluation later.
class C
def store(&block)
@my_code_capsule = block
end
def execute
@my_code_capsule.call
end
end
obj = C.new
obj.store { $X = 1 }
$X = 0
obj.execute
$X # => 1
# For more information: http://www.pragprog.com/titles/ppmetr/metaprogramming-ruby

# =======================
# Spell: Dynamic Dispatch
# =======================
# Decide which method to call at runtime.
method_to_call = :reverse
obj = "abc"
obj.send(method_to_call) # => "cba"
# For more information: http://www.pragprog.com/titles/ppmetr/metaprogramming-ruby

# =====================
# Spell: Dynamic Method
# =====================
# Decide how to define a method at runtime.
class C
end
C.class_eval do
define_method :my_method do
"a dynamic method"
end
end
obj = C.new
obj.my_method # => "a dynamic method"
# For more information: http://www.pragprog.com/titles/ppmetr/metaprogramming-ruby

# ====================
# Spell: Dynamic Proxy
# ====================
# Forward to another object any messages that don’t match a method.
class MyDynamicProxy
def initialize(target)
@target = target
end
def method_missing(name, *args, &block)
"result: #{@target.send(name, *args, &block)}"
end
end
obj = MyDynamicProxy.new("a string")
obj.reverse # => "result: gnirts a"
# For more information: http://www.pragprog.com/titles/ppmetr/metaprogramming-ruby

# =================
# Spell: Flat Scope
# =================
# Use a closure to share variables between two scopes.
class C
def an_attribute
@attr
end
end
obj = C.new
a_variable = 100
# flat scope:
obj.instance_eval do
@attr = a_variable
end
obj.an_attribute # => 100
# For more information: http://www.pragprog.com/titles/ppmetr/metaprogramming-ruby
view raw flat_scope.rb hosted with ❤ by GitHub

# ===================
# Spell: Ghost Method
# ===================
# Respond to a message that doesn’t have an associated method.
class C
def method_missing(name, *args)
name.to_s.reverse
end
end
obj = C.new
obj.my_ghost_method # => "dohtem_tsohg_ym"
# For more information: http://www.pragprog.com/titles/ppmetr/metaprogramming-ruby
view raw ghost_method.rb hosted with ❤ by GitHub

# ==================
# Spell: Hook Method
# ==================
# Override a method to intercept object model events.
$INHERITORS = []
class C
def self.inherited(subclass)
$INHERITORS << subclass
end
end
class D < C
end
class E < C
end
class F < E
end
$INHERITORS # => [D, E, F]
# For more information: http://www.pragprog.com/titles/ppmetr/metaprogramming-ruby
view raw hook_method.rb hosted with ❤ by GitHub

# ====================
# Spell: Kernel Method
# ====================
# Define a method in module Kernel to make the method available to all objects.
module Kernel
def a_method
"a kernel method"
end
end
a_method # => "a kernel method"
# For more information: http://www.pragprog.com/titles/ppmetr/metaprogramming-ruby

# =============================
# Spell: Lazy Instance Variable
# =============================
# Wait until the first access to initialize an instance variable.
class C
def attribute
@attribute = @attribute || "some value"
end
end
obj = C.new
obj.attribute # => "some value"
# For more information: http://www.pragprog.com/titles/ppmetr/metaprogramming-ruby

# ===================
# Spell: Mimic Method
# ===================
# Disguise a method as another language construct.
def BaseClass(name)
name == "string" ? String : Object
end
class C < BaseClass "string" # a method that looks like a class
attr_accessor :an_attribute # a method that looks like a keyword
end
obj = C.new
obj.an_attribute = 1 # a method that looks like an attribute
# For more information: http://www.pragprog.com/titles/ppmetr/metaprogramming-ruby
view raw mimic_method.rb hosted with ❤ by GitHub

# ==================
# Spell: Monkeypatch
# ==================
# Change the features of an existing class.
"abc".reverse # => "cba"
class String
def reverse
"override"
end
end
"abc".reverse # => "override"
# For more information: http://www.pragprog.com/titles/ppmetr/metaprogramming-ruby
view raw monkeypatch.rb hosted with ❤ by GitHub

# ================
# Spell: Namespace
# ================
# Define constants within a module to avoid name clashes.
module MyNamespace
class Array
def to_s
"my class"
end
end
end
Array.new # => []
MyNamespace::Array.new # => my class
# For more information: http://www.pragprog.com/titles/ppmetr/metaprogramming-ruby
view raw namespace.rb hosted with ❤ by GitHub

# ================
# Spell: Nil Guard
# ================
# Override a reference to nil with an “or.”
x = nil
y=x || "avalue" # =>"avalue"
# For more information: http://www.pragprog.com/titles/ppmetr/metaprogramming-ruby
view raw nil_guard.rb hosted with ❤ by GitHub

# =======================
# Spell: Object Extension
# =======================
# Define Singleton Methods by mixing a module into an object’s singleton class.
obj = Object.new
module M
def my_method
'a singleton method'
end
end
class << obj
include M
end
obj.my_method # => "a singleton method"
# For more information: http://www.pragprog.com/titles/ppmetr/metaprogramming-ruby

# =================
# Spell: Open Class
# =================
# Modify an existing class.
class String
def my_string_method
"my method"
end
end
"abc".my_string_method # => "my method"
# For more information: http://www.pragprog.com/titles/ppmetr/metaprogramming-ruby
view raw open_class.rb hosted with ❤ by GitHub

# ========================
# Spell: Prepended Wrapper
# ========================
# Call a method from its prepended override.
module M
def reverse
"x#{super}x"
end
end
String.class_eval do
prepend M
end
"abc".reverse # => "xcbax"
# For more information: http://www.pragprog.com/titles/ppmetr/metaprogramming-ruby

# =================
# Spell: Refinement
# =================
# Patch a class until the end of the file, or (from Ruby 2.1) until the end of the including module.
module MyRefinement
refine String do
def reverse
"my reverse"
end
end
end
"abc".reverse # => "cba"
using MyRefinement
"abc".reverse # => "my reverse"
# For more information: http://www.pragprog.com/titles/ppmetr/metaprogramming-ruby
view raw refinement.rb hosted with ❤ by GitHub

# =========================
# Spell: Refinement Wrapper
# =========================
# Call an unrefined method from its refinement.
module StringRefinement
refine String do
def reverse
"x#{super}x"
end
end
end
using StringRefinement
"abc".reverse # => "xcbax"
# For more information: http://www.pragprog.com/titles/ppmetr/metaprogramming-ruby

# ==============
# Spell: Sandbox
# ==============
# Execute untrusted code in a safe environment.
def sandbox(&code)
proc {
$SAFE = 2
yield
}.call
end
begin
sandbox { File.delete 'a_file' }
rescue Exception => ex
ex # => #<SecurityError: Insecure operation `delete' at level 2>
end
# For more information: http://www.pragprog.com/titles/ppmetr/metaprogramming-ruby
view raw sandbox.rb hosted with ❤ by GitHub

# =================
# Spell: Scope Gate
# =================
# Isolate a scope with the class, module, or def keyword.
a = 1
defined? a # => "local-variable"
module MyModule
b = 1
defined? a # => nil
defined? b # => "local-variable"
end
defined? a # => "local-variable"
defined? b # => nil
# For more information: http://www.pragprog.com/titles/ppmetr/metaprogramming-ruby
view raw scope_gate.rb hosted with ❤ by GitHub

# =================
# Spell: Self Yield
# =================
# Pass self to the current block.
class Person
attr_accessor :name, :surname
def initialize
yield self
end
end
joe = Person.new do |p|
p.name = 'Joe'
p.surname = 'Smith'
end
# For more information: http://www.pragprog.com/titles/ppmetr/metaprogramming-ruby
view raw self_yield.rb hosted with ❤ by GitHub

# ===================
# Spell: Shared Scope
# ===================
# Share variables among multiple contexts in the same Flat Scope (https://gist.github.com/nusco/535082).
lambda {
shared = 10
self.class.class_eval do
define_method :counter do
shared
end
define_method :down do
shared -= 1
end
end
}.call
counter # => 10
3.times { down }
counter # => 7
# For more information: http://www.pragprog.com/titles/ppmetr/metaprogramming-ruby
view raw shared_scope.rb hosted with ❤ by GitHub

# =======================
# Spell: Singleton Method
# =======================
# Define a method on a single object.
obj = "abc"
class << obj
def my_singleton_method
"x"
end
end
obj.my_singleton_method # => "x"
# For more information: http://www.pragprog.com/titles/ppmetr/metaprogramming-ruby

# =====================
# Spell: String of Code
# =====================
# Evaluate a string of Ruby code.
my_string_of_code = "1 + 1"
eval(my_string_of_code) # => 2
# For more information: http://www.pragprog.com/titles/ppmetr/metaprogramming-ruby

# =====================
# Spell: Symbol to Proc
# =====================
# Convert a symbol to a block that calls a single method.
[1, 2, 3, 4].map(&:even?) # => [false, true, false, true]
# For more information: http://www.pragprog.com/titles/ppmetr/metaprogramming-ruby

Saturday, August 28, 2010

RubyKaigi 2010

The scarily efficient organizers of RubyKaigi 2010 only took a few hours to publish the video of my talk on metaprogramming and the Ruby object model (slides and audio only).

Yesterday I also lived the Andy Warhol moment of my life when I signed the first hundred or so copies of Metaprogramming Ruby Japanese Edition together with translator Kado Masanori and Matz. I looked at the long line of people happily waiting half an hour to have their copy signed, and suddenly spending three years to write a book seemed like a perfectly rational thing to do.

Friday, August 27, 2010

Metaprogramming Ruby in Japanese is Out

This is selling great at RubyKaigi in Tsukuba. I'm a happy little author.


Big thanks to my translator, Kado Masanori, and my Japanese editor-in-Chief, Kahei Suzuki.

Wednesday, August 18, 2010

Why Inheritance Sucks (in Ruby, at least)

[Update: Some readers went up in arms over this post, probably because the original title didn't specify which language I'm talking about. To clarify, I'm talking about inheritance in Ruby, compared to using modules, also in Ruby. My point here is that inheritance is an essential feature in C++/Java/C#, but not as much in Ruby. No, I'm not saying that Java should drop inheritance anytime soon.]

I came to Ruby from a static language background (C++, Java), and I had a hard time leaving my hold habits behind. In particular, as a Ruby beginner I tended to overuse inheritance. These days, I rarely use inheritance at all. Instead, I use modules. Let's look at the difference.

When you use inheritance, the superclass becomes an ancestor of the subclass. When you call a method, Ruby walks up the chain of ancestors until it finds the method. So, objects of the subclass also get the methods defined in the superclass.

class Bird
def fly
"Wheee!"
end
end
class Duck < Bird
end
donald = Duck.new
donald.fly # => "Wheee!"

When you use modules, the module also becomes an ancestor of the class, just like the superclass does:

module Bird
def fly
"Wheee!"
end
end
class Duck
include Bird
end
donald = Duck.new
donald.fly # => "Wheee!"

When you call a method, Ruby still walks up the ancestors chain until it finds the method. The net effect is exactly the same as the picture above, except that Bird is now a module instead of a class. So, having a method in a superclass or having the same method in a module doesn't make much difference in practice.

However, modules are generally more flexible than superclasses. Modules can be managed at runtime, because include is just a regular method call, while superclasses are set in stone as you write your class definitions. Modules are much easier to use and test in isolation than tightly coupled hierarchy of classes. You can include as many modules as you like, while you can only have one superclass per class. And finally, when you get into advanced Ruby, modules give you much more flexibility than classes, so you can use modules to cast magic metaprogramming spells like Singleton Methods and Class Extensions.

If inheritance is so much worse than modules in Ruby, then why do languages like Java and C# rely on inheritance so much? There are two reasons why you use inheritance in these languages. The first reason is that you want to manage your methods - for example, re-use the same method in different subclasses. The second reason is because you want to upcast the type of a reference from a subclass to a superclass - that's the only way to get polimorphism in Java. The first reason is not as valid in Ruby, because you can just as well use modules to manage your methods. However, upcasting is more interesting.

Java is both compiled and statically typed, so the compiler can analyze your code and spot type-related mistakes. In particular, it can spot upcasting mistakes: if you have a method that takes Minerals, and you pass a Dog to the method, then the compiler will complain that a Dog is an Animal, not a Mineral, so you cannot upcast a Dog reference to a Mineral reference. In Ruby you don't declare your types, so you don't have upcasting at all. Even if you did have upcasting, you wouldn't have a compiler double-checking it. So you don't get the same advantages out of inheritance in Ruby compared to Java.

"Wait a minute," I hear you say. "Some of the limitations of inheritance are actually a good thing! Including multiple modules in Ruby is just like having multiple inheritance in C++, and multiple inheritance is a big mess. That's why Java and C# force you to inherit from a single class". This is the "diamond" problem that my original C++ mentor used to warn me about: if your class has two superclasses, and they both inherit from yet another superclass, then you get a diamond-shaped inheritance chain that can potentially be confusing. Wouldn't modules be a throwback to this kind of headaches?

In practice, however, Ruby modules tend to be more manageable than multiple superclasses. In Ruby, the chain of ancestors always follows a single path, where each module or class can only appear once - so you can't have diamond-shaped inheritance. If you understand how Ruby builds the chain of ancestors, you're never going to find yourself in an ambiguous situation where you don't know which method is called: simply enough, Ruby always calls the version of the method that's lower on the ancestors chain. (You can still get a clash if two separate modules reference instance variables with the same name, but that rarely happens in practice.) More crucially, the way you write code in Ruby is different from the way you write code in a static language. If you get used to crazy stuff like replacing methods with Monkeypatches, then there is no reason why you shouldn't get used to managing methods with modules.

I took literally years to get rid of my tendency to think in inheritance. Now I finally understand why large Ruby projects such as Rails barely use inheritance at all, and rely almost exclusively on modules.

Use inheritance sparingly.

Friday, June 25, 2010

The New Ruby Ecosystem

Last week I finally caught up with the state of Ruby - the language, the community and the tools in fashion. I'm impressed. These guys seem to be doing a lot of things right.

I'd taken a break from Ruby a few months ago, after Metaprogramming Ruby had gone out of beta. On my comeback, I was a tad disappointed at first. I couldn't see much progress on things like standard language specs, Windows support or in-process concurrency. I didn't even see an increase in Ruby's popularity. In fact, even as the language is going strong in the marketplace, the burst of adrenaline that used to accompany its rise seems to have faded.

So, where did the once-vocal Ruby community go? As it turns out, they're hard at work building a new environment. It's an ecosystem composed of many small moving pieces, mostly interchangeable and in continuous flux, yet integrating nicely with each other. It's young and rough around the edges, but it's working well enough, and it pushes the envelope far enough, that it deserves its own name: I'll call it the New Ruby Ecosystem.

Here are a few examples:

- It seems yesterday that the Rails team announced their merging with Merb, their primary competitor for web developer love. In less than one and a half years, these guys merged two large frameworks into one better, cleaner framework that some people are already using in production. Most companies I worked with would take multiple years to accomplish such a feat - if ever.

- Any Ruby web framework now jives great with any Ruby web server. I can scale down from a fully load-balanced production system, to a local Apache, to a quick in-process web server without even thinking about it. I can also add components to the HTTP chain in a snap. Thanks to Rack's amazing simplicity, it took less than one year to move from Rack 1.0 to widespread Rack compliance. As Sam Ruby put it: I love it when a plan comes together.

- In what seems like one year, pretty much all Ruby-related development migrated to git and GitHub. GitHub is now a one-stop shop to do whatever you wish to whatever piece of open code in the Ruby world. Forking and contributing has never been so easy. The technology behind the library repositories took the time to split, experiment and then merge again, so that publishing a project to the community is now boringly easy. The barrier to contribute, join efforts or part efforts in the Ruby community is as low as they get.

- No other technology offers a comparable number of options to connect to just about any cool recent thingie out there, from noSQL databases to clouds. Yesterday I was looking for libraries to access MongoDB, and I was totally overwhelmed. These libraries also tend to be simple enough that switching from one to the other is as easy as I could hope. I just picked up the default Mongo driver for my next project, and I'm confident I'll be able to switch quickly to another driver or even a relational DB if I need to.

- There are now about ten flavours of Ruby interpreters, ranging from experimental, to cutting-edge, to enterprise-stable. I can have all of those interpreters, or even multiple versions of the same interpreter, on one computer. I can install a new Ruby, select a default Ruby, switch Ruby on the fly - each of those with a single command. I can switch Ruby automatically for a specific project directory, and I don't even have to notice: I just type ruby or irb, and bang - I'm using the “right” interpreter. I can even have multiple sets of gems (libraries) for each Ruby, and switch gemsets with one command like I switch Rubies. I cannot imagine a more flexible language environment.

- I can declare all the libraries that my project needs, install them all at once, instruct the project to ignore any unlisted library, and package all libraries inside the project itself. I can even use Bundler and rvm together to isolate each project within its own little bubble of libraries. The result comes close to copy-and-run installation from development to production. Complex library management tools suddenly feel so last year.

- The popular Ruby libraries, especially the Rails add-ons and the testing tools, evolve at a scary pace. Projects like Cucumber release production updates faster than you'll probably care to get them. Still, the tools are remarkably stable (unless you choose to ride beta versions) and play together nicely. There is something to be learned from a project that releases stable production versions every few days, even taking the time to go through a few large refactorings, and still manages not to break its thousands of clients.

- I can mix-and-match libraries to get my own ideal environment. Libraries are built to work together by default. I'm now switching a small testing project from Cucumber-RSpec+Webrat+Mechanize to Cucumber+RSpec+Capybara+Culerity+Mechanize, so that I can test simpler browser interactions in-process and switch to a real browser when I test JavaScript. The community is relentlessly looking for ways to make my life easier.

- I can deploy my Rails application to the cloud with a single command. The server will update all its libraries, tweak my app's configuration to run on its own backend, and generally do everything so that my application just works©.

Nothing of the above is revolutionary. Taken all together, though, these changes are a great display of the power of simplicity, testing, openness and relentless experimentation. In fact, I think that the development community at large should look hard at the Ruby community for inspiration. Sure, you can wait for the better stuff to make its way to your environment of choice - but no matter how good those efforts, you'll be lagging a few steps behind. If you fancy to be in the place where things happen, there is no current replacement for the Ruby community.

In a world where a standard can take years to be discussed, approved, implemented and supported, Ruby standards such as Rack skip from conception to widespread support at blazing speed. Every time I take a peek, the New Ruby Ecosystem seems to have reinvented itself in many different ways. This is inspect-and-adapt taken to new heights, and a wonderful showcase of the power of emergent design over big up-front design.

Final disclaimer: As usual, cutting-edge Ruby is not for the faint of heart. In particular, using Ruby on Windows still gives me more headaches than I'd expect, so I'd hesitate to suggest some of the above tools to customers still deeply mired in Windows. If you use the beta versions of all tools, expect your own share of bumps in the road and frustrating error messages. It will take a few more months until Ruby 1.9, Rails 3, Bundler, RSpec, Cucumber, Heroku and all the related technologies work together without a hitch.

I know I'll be waiting. With a big smile on my face.

Wednesday, March 10, 2010

Big in Japan

I was stunned and delighted to see Metaprogramming Ruby topping the Amazon.co.jp chart for English-language computer books. At one point, Amazon.co.jp even listed Metaprogramming Ruby amongst the 50 best selling English books overall! I could get a good sight of Dan Brown and Harry Potter from there (not to mention a lot of Michael Jackson biographies). It didn't last long, but it was nice to hang around with the big boys for a few hours.

As a first-time author, I couldn't have asked for more. To all the people in Japan who bought the book: thank you! Stay tuned, because there might be more news for you in a few months...

Saturday, January 9, 2010

Bill is back, in article form!

Those of you who read the beta version of Metaprogramming Ruby already know Bill, the grumpy old Ruby mentor. If you didn't get enough of him yet, here's a treat for you.

The January 2010 issue of PragPub magazine contains a short article where Bill lectures you about the ins and outs of Ruby's nil value. In this article, I also introduce a couple of spells that fell on the cutting room floor as I struggled to make the book shorter: the Null Object spell and the Black Hole spell.

You can download PragPub for free (in formats suited for Kindles, iPhones and plain old-fashioned computers). Enjoy!