Skip to content

Instantly share code, notes, and snippets.

@vertiginous
Created August 15, 2008 22:05
Show Gist options
  • Save vertiginous/5665 to your computer and use it in GitHub Desktop.
Save vertiginous/5665 to your computer and use it in GitHub Desktop.
An alternative api
require 'rubygems'
require 'spec'
# Here is the problem, I want to replace statements like this:
#
# puts (EvenRecognizer.new.knows?(1) or EvenRecognizer.new.knows?(3)) ? "there are odds" : "there is no odd"
#
# With this syntax:
# puts EvenRecognizer.new.knows?(1).or?(3) ? "there are odds" : "there is no odd"
#
# EvenRecognizer is just a trivial class just to prove my concept:
class EvenRecognizer
def knows?(value)
(value % 2) == 0
end
end
describe EvenRecognizer do
before :each do
@recognizer = EvenRecognizer.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 EvenRecognizer
def mknows?(*args)
args.inject do |memo, hash|
meth, arg = *hash.to_a.flatten
BooleanEvaluator.new(self, :knows?, *memo).send(meth, arg)
end.true?
end
end
# Let's test it.
describe EvenRecognizer do
before :each do
@recognizer = EvenRecognizer.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 false for 1 or 3 or 5 without having to call true at the end" do
# pending "Impossible in ruby?" do
lambda do
# needed functionality is to remove last true? and make it work anyway.
raise if @recognizer.mknows? 1, :or => 3, :or => 5
end.should_not raise_error
# end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment