Skip to content

Instantly share code, notes, and snippets.

@rlb3
Forked from codeodor/friend.rb
Created November 14, 2012 15:10
Show Gist options
  • Save rlb3/4072670 to your computer and use it in GitHub Desktop.
Save rlb3/4072670 to your computer and use it in GitHub Desktop.
Friend functions in Ruby
# See http://www.ruby-doc.org/core-1.9.3/Kernel.html#method-i-set_trace_func for docs on this function
require 'ostruct'
module Friendly
def self.included(base)
base.extend(ClassMethods)
end
def method_missing(name, *args, &block)
self.class.disable_trace_function
friends = self.class.friends
call_history = self.class.call_history
called_by_friend = friends.include?(call_history[0].id) || friends.include?(call_history[0].class_constant)
# puts friends.inspect
# puts call_history.inspect
# puts called_by_friend.inspect
if self.private_methods.include?(name) && called_by_friend
return self.send(name, *args, &block)
else
super(name, *args, &block)
end
ensure
self.class.enable_trace_function
end
module ClassMethods
def friend(method_symbols_and_or_classes)
@call_history ||= [OpenStruct.new(id: nil)] * 1
@trace_function =
proc do |event, file, line, id, binding, class_constant|
trace_info = OpenStruct.new(
event: event,
file: file,
line: line,
id: id,
binding: binding,
class_constant: class_constant,
time: Time.now.to_f
)
@call_history = @call_history[1..-1] << trace_info if we_care_about?(event, class_constant)
end
enable_trace_function
@friends ||= []
@friends += [method_symbols_and_or_classes].flatten
end
attr_reader :friends, :call_history, :trace_function
def enable_trace_function
set_trace_func(@trace_function)
end
def disable_trace_function
set_trace_func(nil)
end
def we_care_about?(event, class_constant)
event == "call" && !(class_constant.to_s =~ /Friendly/)
end
end
end
require_relative 'friend'
class Rectangle
include Friendly
def initialize(width, height)
@width = width
@height = height
end
friend :duplicate
def from_square(square)
#return new(square.side, square.side)
@width = square.side
@height = square.side
return self
end
private
def width
@width
end
def height
@height
end
end
rect = Rectangle.new(1, 2)
puts "Rectangle has declared any function named 'duplicate' as a friend.\n\n"
puts "Rectangle width is private:"
val = rect.width rescue puts("\t#{$!}")
puts "Rectangle height is private:"
val = rect.height rescue puts("\t#{$!}")
def duplicate(rectangle)
Rectangle.new(rectangle.width, rectangle.height)
end
puts
puts "'duplicate' uses rectangles privates, but does not fail:"
new_rect = duplicate(rect)
puts "\t#{rect.inspect}"
puts "\t#{new_rect.inspect}"
require_relative 'friend'
class Rectangle
include Friendly
def initialize(width, height)
@width = width
@height = height
end
friend :duplicate
def self.from_square(square)
return new(square.side, square.side)
@width = square.side
@height = square.side
return self
end
private
def width
@width
end
def height
@height
end
end
class Square
include Friendly
friend Rectangle
def initialize(side)
@side = side
end
private
def side
@side
end
end
puts "Square declares the Rectangle class to be it's friend.\n\n"
square = Square.new(3)
puts "Square#side is private:"
puts square.side rescue puts("\t#{$!}")
puts
puts "But a Rectangle can use it:"
rect = Rectangle.from_square(square)
puts "\t#{rect.inspect}"
puts "\t#{square.inspect}"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment