Created

Embed URL

HTTPS clone URL

SSH clone URL

You can clone with HTTPS or SSH.

Download Gist

Friend functions in Ruby

View friend.rb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
# 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
 
 
 
 
 
View friend.rb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
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}"
 
View friend.rb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
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}"
Owner

There is a bug in this code if you run both examples together. What is it?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.