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
I can’t say I would recommend getting within 10 feet of that code. First of all, it can be written much more cleanly with a lot of things Ruby already provides. Second of all, it ignores a lot of things Ruby already has.
end
Twice as readable and no repetition. If you want to abstract some more then there are clean things to do. For example the alias part could become:
alt_names_for :color?, %w[white? red? green? blue? black?]
Finally, I’d say you are golfing and not solving dry with the above code. It looks like shit. Please stop writing such bad code in Ruby so I don’t need to go save yet another client/victim in my consulting practice. I still wonder why someone would want to avoid writing the file name down as a constant on your own. This sort of DRY defeats the original purpose… good luck on your recovery.
Wow, there are some humor-challenged people in the world. And so many of them are named Anonymous!