Skip to content

Instantly share code, notes, and snippets.

@sdeming
Created January 29, 2012 01:48
Show Gist options
  • Save sdeming/1696658 to your computer and use it in GitHub Desktop.
Save sdeming/1696658 to your computer and use it in GitHub Desktop.
predicate method chaining
module PredicateMethods
module Firewalls
class Allow
def initialize(what)
@wrappee = what
end
def method_missing(name, *args, &blk)
begin
@wrappee.send(name, *args, &blk)
rescue NoMethodError
eval "#{name} *args, &blk"
end
end
end
class Deny
def method_missing name, *args, &blk
self
end
end
end
def define_predicate name, predicate
name = name.to_s.sub(/\?$/, '')
p, m = :"#{name}?", :"#{name}"
send(:define_method, p) do |*args, &blk|
predicate.call(*args)
end
send(:define_method, m) do |*args, &blk|
if send(p, *args)
if blk
blk.call
else
@_predwrapper ||= Firewalls::Allow.new(self)
end
else
Firewalls::Deny.new
end
end
end
end
class T
extend PredicateMethods
define_predicate :yep, lambda { true }
define_predicate :nope, lambda { false }
define_predicate :maybe, lambda { |eh| eh }
end
x = T.new
x.yep.puts "x:Yep"
x.yep.yep.yep.yep { puts "x:Yeeeeep" }
x.nope.nope.nope.nope { puts "x:Nooooope" }
x.nope.puts "x:Nope"
x.yep.nope.puts "x:Nuh uh"
x.puts "x:I should raise NoMethodError" rescue nil
y = T.new
y.nope.puts "y:Nope"
y.puts "y:I should raise NoMethodError" rescue nil
y.maybe(true).puts "y:Maybe"
y.yep.maybe(true).yep.puts "y:Yep-Maybe-Yep"
y.maybe(false).puts "y:Maybe not"
@sdeming
Copy link
Author

sdeming commented Jan 29, 2012

First problem is that method_missing is permanently attached to the instance of an abused class, once it's abused. It would best if "x.puts" did not eval "puts" unless it came at the end of the predicate chain. So x.puts would raise no method error while x.yet.puts would work as designed. Time for some meta? Not sure if that'll actually work here.

@sdeming
Copy link
Author

sdeming commented Jan 29, 2012

By abused class I mean any class extended by PredicateMethods. I should minitest this instead of relying on my eyes to read puts. But I'm just not that unlazy.

@jmazzi
Copy link

jmazzi commented Jan 29, 2012

Please check out rainmain. We rewrote it.3 times. It solves a lot of this.

@sdeming
Copy link
Author

sdeming commented Jan 29, 2012

Updated to accomplish my goals. Rainman is for far more complex mechanisms. This is really just about replacing code like:

if verbose?
  puts "Hello"
end

with:

verbose.puts "Hello"

I have a few things that are pretty deeply nested that was using lambda's for. This cleans it up quite a bit.

@sdeming
Copy link
Author

sdeming commented Jan 29, 2012

Scott, don't end your sentences in prepositions. It clearly breaks reading comprehensibility then. Oops.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment