Jay Fields has envisioned a beautiful future for software development with his EDRY dialect of Ruby. But what is Enhanced DRY without better CoC (Convention over Configuration)?
I have modified Jay's code to rely more on convention. Why have a distinct vocabulary for fields vs. mixins, when the right thing to do can be inferred from the types involved? The result is some really tight code:
C Enumerable, :first_name, :last_name, :favorite_color do
d.complete_info? { nd(first_name,last_name) }
d.white?.red?.blue?.black? { |color| favorite_color.to_s == color.to_s.chop }
end
I am including the full source at the bottom of this entry. Can you make it even DRYer and more convention-driven?
class Object
def C(*args, &block)
attrs = args.find_all {|arg| Symbol === arg}
includes = args.find_all {|inc| inc.instance_of?(Module)}
name = File.basename(eval("__FILE__", block.binding),".rb")
klass = Struct.new(name.capitalize, *attrs)
Kernel.const_set(name.capitalize, klass)
klass.class_eval(&block)
klass.send :include, *includes
end
def s
self
end
end
class Class
def ctor(&block)
define_method :initialize, &block
end
def i(mod)
include mod
end
def d
DefineHelper.new(self)
end
def a(*args)
attr_accessor(*args)
end
end
class DefineHelper
def initialize(klass)
@klass = klass
end
def method_stack
@method_stack ||= []
end
def method_missing(sym, *args, &block)
method_stack << sym
if block_given?
method_stack.each do |meth|
@klass.class_eval do
define_method meth do
instance_exec meth, &block
end
end
end
end
self
end
end
# http://eigenclass.org/hiki.rb?instance_exec
module Kernel
def instance_exec(*args, &block)
mname = "__instance_exec_#{Thread.current.object_id.abs}_#{object_id.abs}"
Object.class_eval{ define_method(mname, &block) }
begin
ret = send(mname, *args)
ensure
Object.class_eval{ undef_method(mname) } rescue nil
end
ret
end
end
def nd(*args)
args.each {|x| return false unless x}
true
end
# convention: symbols are attributes, modules are to be included
C Enumerable, :first_name, :last_name, :favorite_color do
d.complete_info? { nd(first_name,last_name) }
d.white?.red?.blue?.black? { |color| favorite_color.to_s == color.to_s.chop }
end