Created
November 14, 2012 02:33
-
-
Save codeodor/4069922 to your computer and use it in GitHub Desktop.
Friend functions in Ruby
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
# 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 | |
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
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}" | |
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
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
There is a bug in this code if you run both examples together. What is it?