-
-
Save avdi/6123055 to your computer and use it in GitHub Desktop.
class SomeORM | |
def self.attributes(*names) | |
if const_defined?(:DynamicAttributes, false) | |
mod = const_get(:DynamicAttributes) | |
else | |
mod = const_set(:DynamicAttributes, Module.new) | |
include mod | |
end | |
mod.module_eval do | |
names.each do |name| | |
define_method(name) do | |
# Stuff | |
end | |
end | |
end | |
end | |
end |
One thing I dislike slightly about this compared to my original solution is that Post::DynamicAttributes
perhaps gives less of a clue where the module comes from than SomeORM::DynamicAttributes123
. But I suppose "where it comes from" is kind of hard to decide anyway – SomeORM generates it, but you declared the attributes in Post, which is-a SomeORM anyway.
I suppose you could call it Post::SomeORMDynamicAttributes
if you want to be very clear.
I also like to define a custom #to_s
on these modules so that when listed with #ancestors
you see e.g. DynamicAttributes(attr1, attr2, attr3)
. I think I showed that in one of the RubyTapas episodes I mentioned on Twitter.
I checked out episode 28, it's in there. Clever!
For my own future reference, the above could be achieved with something along the lines of (after line 15):
def mod.to_s
"DynamicAttributes(#{instance_methods(false).join(', ')})"
end
Thanks for the feedback!
I like how this solution uses plain constants to keep down the number of modules (one per class in an inheritance hierarchy), unique-enough names (no need for object ids as it's again one per class), and of course for naming.