module ViewFromTo
KLASSES = []
RULES = []
def self.extended(klass)
KLASSES << klass
RULES.each do |a, o, b, l|
if klass.to_s == a || a == '*'
m = klass.instance_method(o) if o != :method_missing and klass.method_defined?(o) # should exist better way
klass.define_method(o) do |*args, &blk|
m.bind(self).call(*args, &blk) if m
l.call(self, *args, &blk) if args[o == :method_missing ? 1 : 0].kind_of?(b) # should exist better way
end
end
end
end
def def_from pattern='*', meth=:method_missing, &blk
RULES << [pattern, meth, self, blk]
end
def def_to pattern='*', meth=:method_missing, &blk
klass = pattern == '*' ? Object : KLASSES.find { |k| k.to_s == pattern }
define_method meth do |*args|
self.instance_exec(*args, &blk) if args[meth == :method_missing ? 1 : 0].kind_of?(klass) # should exist better way
end
end
end
# the better way:
# 1. add full type check, see https://glot.io/snippets/f48os6pmhy
# 2. better wrap method_missing, this one is a fake one
class Apple
extend ViewFromTo
def_from '*' do # *.*(apple)
puts "i'm just an apple"
end
def_from 'Human', :taste do |human| # human.taste(apple)
puts "i'm taste #{human.age < 1 ? 'juicy' : 'odd'}"
end
end
class Human
extend ViewFromTo
attr_accessor :age
def initialize(age) @age = age end
def_to 'Apple', :taste do |apple| # human.taste(apple) # override previous one
puts age < 1 ? 'juicy' : 'cannot eat'
end
end
class OtherThing
extend ViewFromTo
end
Human.new(0).taste(Apple.new) # juicy
Human.new(2).taste(Apple.new) # cannot eat
OtherThing.new.taste(Apple.new) # i'm just an apple