Skip to content

Instantly share code, notes, and snippets.

@michaelglass
Last active December 17, 2015 17:19
Show Gist options
  • Save michaelglass/5645640 to your computer and use it in GitHub Desktop.
Save michaelglass/5645640 to your computer and use it in GitHub Desktop.
HandleWithCare allows classes to specify certain methods as DANGEROUS!

HandleWithCare

HandleWithCare allows classes to specify certain methods as DANGEROUS! when you decorate a class with handle_with_care :toss_nuclear_rods, :lazer_pubes, :delete_the_database

when you try to call these methods, they raise an error unless you prefix params with {force: true}

# HandleWithCare
# ==============
#
# HandleWithCare allows classes to specify
# certain methods as DANGEROUS!
# when you decorate a class with
# handle_with_care :toss_nuclear_rods, :lazer_pubes, :delete_the_database
#
# when you try to call these methods, they raise an error unless you prefix params
# with {force: true}
module HandleWithCare
def handle_with_care(*method_names)
method_names -= handled_with_care.to_a
handled_with_care.merge method_names
method_names.each do |method_name|
original_method_name = "original_#{method_name}"
while methods.include?(original_method_name)
original_method_name = "original_#{method_name}_#{rand}"
end
alias_method original_method_name, method_name
define_method(method_name) do |*args, &block|
if args.shift != {force: true}
raise "You have called a dangerous method. Please call the method with your args prepended with {forced: true} if you did this on purpose"
else
send original_method_name, *args, &block
end
end
end
end
def handled_with_care
@methods_handled_with_care ||= Set.new
end
end
class Foo
extend HandleWithCare
def func(*args, &block)
if block
block.call(*args) if block
elsif args.any?
args.join(' ')
else
'no args'
end
end
public :eval
handle_with_care :func
end
describe HandleWithCare do
describe '::handle_with_care' do
shared_examples_for 'a method handled carefully' do |*args, &block|
def build_method_call(method_name, args, block)
eval_me = "send \"#{method_name}\""
eval_me << ', *args' if args.any?
eval_me << ', &block' if block
eval_me
end
let(:method_name) { :func }
let(:method_call) {build_method_call method_name, args, block}
let(:original_method_call) {build_method_call "original_#{method_name}", args, block}
it 'without the prefix, raises an error' do
expect { Foo.new.eval method_call }.to raise_error(RuntimeError, 'You have called a dangerous method. Please call the method with your args prepended with {forced: true} if you did this on purpose')
end
it 'with the prefix, calls the original function' do
original_result = Foo.new.eval(original_method_call)
args.unshift({force: true})
Foo.new.eval(method_call).should == original_result
end
end
context 'method with no arguments' do
it_behaves_like 'a method handled carefully'
end
context 'method with arguments' do
it_behaves_like 'a method handled carefully', 'some', 'args'
end
context 'method with a block' do
it_behaves_like('a method handled carefully') {'a block'}
end
context 'method with args and a block' do
it_behaves_like('a method handled carefully', 'args', 'and') {|a, b| "#{a} #{b} a block"}
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment