Skip to content

Instantly share code, notes, and snippets.

@babney
Created October 31, 2011 21:58
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save babney/1329145 to your computer and use it in GitHub Desktop.
Save babney/1329145 to your computer and use it in GitHub Desktop.
MethodCop
module MethodCop
# Guard methods that have side effects with a callback that fires before the method is invoked.
# If the callback returns a "falsey" value, the method is halted and will not be called.
# The callback will return nil instead.
# if the method does not have side effects or you depend on its return value, you should NOT
# use this on that method! This will break the _hell_ out of design by contract.
# currently does not work with methods that accept blocks so be aware of that. Fixes, improvements,
# pull requests, and general "why on earth did you do this" notes are encouraged.
#TODO: DRY this up
@@guards = {}
@@class_guards = {}
def guard_method(name, options)
@@guards[name] = options
end
def method_added(name)
unless @@guards[name].nil?
options = @@guards[name]
@@guards = @@guards.delete(name)
guard_method_internal(name, options)
end
end
def guard_method_internal(name, options)
method = name.to_sym
original_method = "#{name}_orig".to_sym
alias_method original_method, method
define_method(method) do |*args|
unless options[:with].nil?
unless !self.send(options[:with].to_sym)
self.send(original_method, *args)
else
# THIS METHOD IS UNDER ARREST
nil
end
else
# no method to guard with was given
self.send(original_method, *args)
end
end
end
def guard_class_method(name, options)
@@class_guards[name] = options
end
def singleton_method_added(name)
unless @@class_guards[name].nil?
options = @@class_guards[name]
@@class_guards = @@class_guards.delete(name)
guard_class_method_internal(name, options)
end
end
def guard_class_method_internal(name, options)
@@method_to_guard_name = name
@@method_to_guard_options = options
# there HAS to be a better way. need to look into it.
class << self
name = @@method_to_guard_name
options = @@method_to_guard_options
@@method_to_guard_name = nil
@@method_to_guard_options = nil
method = name.to_sym
original_method = "#{name}_orig".to_sym
alias_method original_method, method
define_method(method) do |*args|
unless options[:with].nil?
unless !self.send(options[:with].to_sym)
self.send(original_method, *args)
else
# THIS METHOD IS UNDER ARREST
nil
end
else
# no method to guard with was given
self.send(original_method, *args)
end
end
end
end
end
class Testing
extend MethodCop
guard_method :some_method, :with => :run_method?
guard_class_method :some_class_method, :with => :run_method?
def some_method(name)
return "I'm about to do something horribly wrong to #{name}."
end
def run_method?
# block everything
return false
end
def self.run_method?
return false
end
def self.some_class_method(name)
return "I'm about to do something awful, even while uninstantiated, to #{name}."
end
end
@babney
Copy link
Author

babney commented Oct 31, 2011

ree-1.8.7-2010.02 :041 > Testing.new.some_method
=> nil

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