Skip to content

Instantly share code, notes, and snippets.

@vertiginous
Created August 15, 2008 18:19
Show Gist options
  • Save vertiginous/5619 to your computer and use it in GitHub Desktop.
Save vertiginous/5619 to your computer and use it in GitHub Desktop.
require 'rubygems'
require 'spec'
# Here is the problem, I want to replace statements like this:
#
# puts (OddRecognizer.new.knows?(1) or OddRecognizer.new.knows?(3)) ? "there are odds" : "there is no odd"
#
# With this syntax:
# puts OddRecognizer.new.knows?(1).or?(3) ? "there are odds" : "there is no odd"
#
# OddRecognizer is just a trivial class just to prove my concept:
class OddRecognizer
def knows?(value)
(value % 2) == 0
end
end
describe OddRecognizer do
before :each do
@recognizer = OddRecognizer.new
end
it "should recognize 2" do
@recognizer.knows?(2).should be_true
end
it "should not recognize 3" do
@recognizer.knows?(3).should be_false
end
end
# Here's the implementation
class BooleanEvaluator
Operation = Struct.new :name, :arguments
def initialize(target, method, *args)
@target, @method = target, method
@arguments = Array.new << Operation.new(:first, args)
end
def or?(*args)
@arguments << Operation.new(:or, args)
self
end
def and?(*args)
@arguments << Operation.new(:and, args)
self
end
def true?
@arguments.inject(false) do |result, operation|
case operation.name
when :or then result or @target.send(@method, *operation.arguments)
when :and then result and @target.send(@method, *operation.arguments)
else @target.send(@method, *operation.arguments)
end
end
end
def ==(bool)
!!true?.equal?(bool)
end
end
# For now let's add a new method that will behave like I want.
class OddRecognizer
def mknows?(*args)
BooleanEvaluator.new(self, :knows?, *args)
end
end
# Let's test it.
describe OddRecognizer do
before :each do
@recognizer = OddRecognizer.new
end
it "should return false for 1 or 3 or 5" do
@recognizer.mknows?(1).or?(3).or?(5).should be_false
end
it "should return true for 1 or 3 or 6" do
@recognizer.mknows?(1).or?(3).or?(6).should be_true
end
# Here lies the problem: in ruby, everything that is not nil nor false evaluates
# as true in conditional expressions.... Is there any way to modify BooleanEvaluator
# (or implement it in a different way) to make this work? I don't think so,
# but maybe there is a way to implement something more close to the thing I want.
it "should return true for 1 or 3 or 6 without having to call true at the end" do
# pending "Impossible in ruby?" do
@recognizer.mknows?(1).or?(3).or?(6).should be_true
# end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment