Created
June 28, 2017 15:23
-
-
Save alisnic/1ccc6e1357e0365ee4ed8655f2e1d7dd to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# test.rb | |
module Guardian | |
def self.ensure_method_called!(location) | |
unless caller.any? {|line| line.include?(location)} | |
raise ArgumentError.new("nice try lol") | |
end | |
end | |
def self.ensure_instance_method_called!(klass, method_name) | |
file_name, source_lines = instance_method_info(klass, method_name) | |
is_method_called = caller.any? do |frame| | |
file, line = frame.split(":")[0..1] | |
file == file_name && source_lines.include?(line.to_i) | |
end | |
raise ArgumentError.new("nice try lol") unless is_method_called | |
end | |
def self.instance_method_info(klass, method_name) | |
methods = klass.instance_methods - Object.methods | |
# We start by building an array with all the instance methods of the class. | |
# Each entry would look like this: | |
# | |
# { | |
# name: :other_method, | |
# file: "/Users/andrei/Play/alisnic.github.com/test.rb", | |
# start_line: 24 | |
# } | |
candidates = methods.map do |name| | |
location = klass.instance_method(name).source_location | |
{ | |
name: name, | |
file: location[0], | |
start_line: location[1] | |
} | |
end | |
# Next, we sort those entries by start line, in reverse order. We will start | |
# guessing method locations from bottom to up | |
reversed = candidates.sort_by {|entry| -entry[:start_line] } | |
# We don't know where is the end of the last method of the class, hence we | |
# assume it is 99999. Starting from there, each method's last line is the | |
# line before the previous method start line | |
with_lines = reversed.reduce([]) do |result, entry| | |
previous_method = result.last | |
previous_line = previous_method ? previous_method[:start_line] : 99999 | |
result << entry.merge(lines: entry[:start_line]..previous_line) | |
end | |
# Next, we find the method we are interested in | |
method = with_lines.find {|entry| entry[:name] == method_name } | |
# And return only the information we need | |
[method[:file], method[:lines]] | |
end | |
end | |
class Foo | |
def bar | |
Guardian.ensure_instance_method_called!(Work, :execute) | |
"bar" | |
end | |
end | |
class Work | |
def execute | |
# calling Foo.new.bar here should work | |
Foo.new.bar + " from execute" | |
end | |
def other_method | |
# calling Foo.new.bar here should raise error | |
Foo.new.bar | |
end | |
end | |
puts Work.new.execute | |
puts Work.new.other_method |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment