Superators Add New Operators to Ruby 27
This one’s for the fellow DSL mavens. I’m releasing now a new library I’ve developed that adds new operators to Ruby. Install the superators gem and run the following code in irb:
require 'rubygems'
require 'superators'
class Array
superator "<---" do |operand|
self << operand.reverse
end
end
["jay"] <--- "spillihp"Dear demagogues of domain specificity! Yes, this actually works. Now, how about this cutie:
require 'rubygems'
require 'superators'
class String
superator "++" do |operand|
upcase + operand.upcase
end
superator "-~+~-" do |operand|
puts "Using pretty superators"
self + operand
end
end
p "Tangy" ++ "Erlangy"
p "Super" -~+~- "ators"Superators are a superset of Ruby operators. And they’re pretty super too.

To elaborate more, they work by exploiting the way Ruby parses binary and unary operators. For example, the code
foo ++- baris equivalent to
foo.+(bar.-@().+@())and the Superators library keeps track of the unary operators called on the operand in their appropriate order. A valid Superator’s format is simple: take (almost) any Ruby binary operator and tail on an indefinite number of unary operators after it.
These are the Ruby binary operators you can use: ** * / % + - << >> & | ^ <=> >= <= < > === == =~And here are the unary operators: - ~ +
The only operators you can’t use are the logical ones (e.g. &&, ||, !, and, or, not, etc.) Ruby offers no way of overloading these.
I’m releasing this into the wild to hopefully see what mischief it stirs. But take heed! Superators are second-class citizens still and are victims of vicious discrimination in certain circumstances. In the current implementation the second operand is extend()ed with a Ruby module (unobtrusively setting a flag for later inspection), therefore a Superator can only have a second operand capable of possessing its own eigenclass. Superators will not work with second operands such as true, false, nil, Symbols or Fixnums yet. I’m working on a branch that could fix this if my mad scientist lab experiments bear fruit (or a resentful monster whom I chase to the Arctic Circle after he destroys those I love). Stay tuned.
Despite this limitation, Superators can still be used effectively! Use it when you expect the second operand to be an Array, String, Hash, Range, Regexp, Class, or whatever.
I should note that I strongly frown upon any use of Superators outside of the DSL space. Don’t use it in your company’s Rails app controllers. Use it in your tenderly crafted Ruby DSLs where they make sense.
Enjoy, comrades.
Update: There seems to be some confusion that Superators are a C extension that modify Ruby’s internals. A valid Superator is actually just valid Ruby—something we’ve had at our disposal this whole time!
Full Video Recording of my Ruby Hoedown Presentation 6
On August 10th I gave a presentation about Adhearsion at the Ruby Hoedown conference. Despite only two hours of sleep the night before, I think it came out quite well.
The ConFreaks guys were there and recorded my presentation. It’s completely available online on their website here. It can also be downloaded in AVI format at 640×240 or 960×360.
Here are a few snapshots from my talk:


The Methodphitamine 23
What amazes me most about Ruby is the emergence of hidden, powerful subtleties that lie under its surface just waiting to be discovered. A few months ago I discovered this useless, albeit cute snippet of Ruby’s fringe English-like qualities and, despite tens of thousands of people using Ruby, it wasn’t until just last year that the Symbol#to_proc hack was discovered that happily changed the way we refine and iterate our collections.
Well, ladies and gentlemen, I think I’ve developed a way that trumps Symbol#to_proc. Observe a few examples:
(1..100).select &it % 2 == 0
File.read("/etc/passwd").split.sort_by &it.split(":")[2]
User.find(:all).map &its.contacts.map(&its.last_name.capitalize)I call it The Methodphitamine. That’s Method ph it amine, a drug for Ruby addicts to code faster. Unlike Symbol#to_proc...
- arguments can be given to the method
- it’s not limited to just one method. Tail on as many as you like.
- it’s more English-like with the intuitive use of it or its
- even a method with its own block can be given to it
Let’s take the last of the examples above and convert this into its equivalent pure-Ruby and Symbol#to_proc way.
First, the pure-Ruby way:
User.find(:all).map{|x| x.contacts.map{|y| y.last_name.capitalize }}And now with Symbol#to_proc:
User.find(:all).map{|x|x.contacts.map(&:last_name).map(&:capitalize)}And now with the Methodphitamine once more:
User.find(:all).map &its.contacts.map(&its.last_name.capitalize)Notice how even with Symbol#to_proc a block literal is still necessary because it simply can’t do nested arguments. The Methodphitamine fixes this by preserving all arguments and being more readable to boot.
So now, without further ado, here’s The Methodphitamine’s code, weighing in at just over 20 lines.
module Kernel
protected
def it() It.new end
alias its it
end
class It
undef_method(*(instance_methods - %w*__id__ __send__*))
def initialize
@methods = []
end
def method_missing(*args, &block)
@methods << [args, block] unless args == [:respond_to?, :to_proc]
self
end
def to_proc
lambda do |obj|
@methods.inject(obj) do |current,(args,block)|
current.send(*args, &block)
end
end
end
endSo how does The Methodphitamine work?
The it() and its() protected methods are added to Kernel so it can be called from anywhere in a Ruby script but not on any particular Object instance. They each simply return a new It instance.
The It class has all instance methods stripped from it (except the ones Ruby complains about) to ensure method_missing() catches everything. In the example [1,"2",3].map &its.class.name, the It object first receives the class() method with no arguments via method_missing(). This gets enqueued in the @methods Array and it returns itself to receive any more methods. It then receives the next method, namely name(), and enqueues that alongside the previous method. When no more methods exist, Ruby determines if the It instance has a to_proc() method by calling respond_to?(:to_proc) so this has to be ignored and self suffices as a Ruby true boolean.
Then the magic happens. Because map() takes a block (essentially a Proc argument), this can be substituted with a variable or method that returns a block as long as an ampersand prepends it to let the Ruby interpreter know to call to_proc() on it. The It#to_proc() method is invoked, building a custom, dynamic Proc. Because these enumerations yield a variable, the dynamic Proc is executed for each item in the collection and obj consequentially becomes a reference to the current item in the collection. We then run through the enqueued methods with inject(), passing along the return value of executing each method in the order received with arguments intact. When inject() is done, it simply returns the grand product which becomes the return value of the Proc itself. Simple, right? :)
I chose to define both it() and its() since methods in Ruby can semantically either mean “the result of this action” or the possessive “this attribute.” For example, it.to_s and it.sort_by are both conceptual actions and its.class.name and its.last are both conceptual attributes.
The idea of an it implied block argument comes from the Groovy guys. For example, this is valid Groovy:
[1,2,3,4].each { println it }From the Groovy documentation on closures, “A closure always has at least one argument, which will be available within the body of the closure via the implicit parameter it if no explicit parameters are defined. The developer never has to declare the it variable – like the this parameter within objects, it is implicitly available.”
It’s about time for Ruby be on the receiving end of language beauty cross-pollination among the current generation of scripting languages.
I released this code in the public domain on RubyForge as the methodphitamine gem. To use, simply do
- gem install methodphitamine
- require 'methodphitamine'
When I wrote the Methodphitamine’s RSpec specifications, I noticed that RSpec’s own it() method gets undefined within the scope of each assertion’s block, allowing The Methodphitamine to behave normally even in specs. Very cool, RSpec!
If you discover any issues or have any improvement suggestions, feel free to post on The Methodphitamine Google Group.
It’s now standard in Adhearsion v0.8.0 among many other metaprogramming goodies. :)
Selectively handle undefined methods in Ruby
xml.channel {
xml.title Option[:blogname]
xml.link Option[:siteurl]
xml.description Option[:blogdescription]
xml.pubDate @posts.first.post_date_gmt
xml.generator $GOSLING_URL
xml.language Option[:rss_language]
xml.<< render(
:partial => "item", :collection => @posts)
}
This is the code that actually generates the RSS feed for this blog.
Note that calling xml.channel encapsulates everything in its curly braces with the tag <channel>. xml.title Option[:blogname] asks the ActiveRecord model Option for a record from the database, so it executes the SQL find this (along with the other Option[] calls in this script) and wraps what’s returned with the XML element <title>.
These methods of the xml object weren’t prefined anywhere. The line xml.morrisseyquote "For the Good Life is out there somewhere, so stay on my arm, you little charmer" would generate the XML <morrisseyquote>For the Good Life is out there somewhere, so stay on my arm, you little charmer</morrisseyquote>
Ruby allows this dynamism through its clever handling of its namespace. When the Ruby interpreter encounters a token not in the namespace, it invokes a method—namely Kernel#method_missing. By default Kernel#method_missing simply raises a NameError politely notifying you of your typo.
But the XmlMarkup class actually overrides this method with the signature XmlMarkup#method_missing(sym, *args, &block). When constructing XML in this way, you’re calling methods that don’t exist, which are caught, parsed, and recompiled as XML.
Astute readers may be calling foul here. One may say, “sure, it works for Builder, but what if only certain methods should be overridden?” Because of Ruby’s clever inheriting here, the super keyword can be used to invoke Kernel#method_missing as normal if the given method doesn’t match a set of conditions. For example:def method_missing(name, *args)
super unless name.to_s =~ /^w+_ismyname$/
puts "Why, hello #{name}!"
end
This allows virtually any combination of letters and numbers to be given as a method, as long as they’re succeeded by “_ismyname”. If this is declared in an Object instance called greeter, this can be invoked with greeter.Jay_ismyname, which evokes the output “Why, hello Jay!”
Now that you’re all excited, go fall in love.
Potty-training your tarballs 2
So it’s late at night and you’re up reading Slashdot while the source code for that program you thought seemed pretty cool so you’d check it out downloads.
Firefox tells you it’s finished downloading and you begin the old extract, configure, compile, install, run routine you’ve done a hundred times.
And then it happens.
Your neighbors, sleeping in bed, wake to your resounding shriek in the night:
“Ahhh!!! That tarball just crapped all over my home!”
Yes, it happened to you. Don’t feel bad. It’s the moron who didn’t pack his archive in a subfolder’s fault. There’s no way you could have known all those compressed files were going to mix with the dozens of files already in your home directory. No, really — don’t be so hard on yourself.
Potty-train your tarball and have it clean up its mess by typing this:
tar ztf poopypants.tar.gz | xargs rm -rIf rm starts giving you confirmation messages ad nauseam, add the letter f after the -r, but remember — if the tarball contained a subfolder with the same name as an existing folder in your home directory, removing the folder with -rf won’t discriminate between files inside it added by tar and files rightfully there. Careful!
Now to figure out how to change a baby’s diaper with piped Unix commands…
