During the needlefication of Nukumi2, I came across a lot of service definitions like this:
class Blog
attr_accessor :backends
attr_accessor :page
attr_accessor :view
attr_accessor :topictree
# ...
end
blog {
b = Nukumi2::Blog.new
b.backends = backends
b.config = config
b.page = page
b.topictree = topictree
b
}
Needing to define all these accessors was a thorn in my side.
Although I would need some attr_reader
s for them, writing is not a
thing I want to allow.
The use of attr_reader
, attr_writer
and attr_accessor
is not
just a shortcut in Ruby; it also has a semantical meaning: You are
allowed to read this attribute, to write this attribute or to do
both. RDoc, for example, will base it’s documentation on them, there
will be a special section Attributes in the class documentation.
Therefore, I whipped up a new kind of
“accessor”—attr_inject
—that allows to declare an attribute for
dependency injection. Now, the class will look like:
class Blog
attr_inject :backends
attr_inject :page
attr_inject :view
attr_inject :topictree
attr_reader :topictree
# ...
end
It only opens the things for the “general public” that are actually wanted. However, having these declarations, we can now simply do:
blog {
Nukumi2::Blog.new.inject_attributes(this_container)
}
Pretty nice, heh? :-)
For now, I just have some very simple code; some additions will be needed to make use of all possibilities. It would be nice to allow users to override attribute mappings (for now, attributes and service names need to match):
blog {
Nukumi2::Blog.new.inject_attributes(this_container,
:backends => my_special_backend)
}
Allowing the use of inject_attributes
exactly once would be good too.
By the way, all that’s needed for attr_inject
is just these 15 lines:
class Class
def attr_inject(attribute)
(@__attributes_to_inject ||= []) << attribute
end
end
class Object
def inject_attributes(registry)
(self.class.instance_variable_get(:@__attributes_to_inject) ||
[]).each { |attribute|
instance_variable_set "@#{attribute}", registry[attribute]
}
self
end
end
For “serious” use, this code will need more checks and features, of
course. Another problem is that attr_inject
is not in the standard
library and therefore will break code that may be used with and
without Needle. This can easily be avoided by defining attr_inject
to do nothing unless it’s already defined, though…
NP: Bob Dylan—Is Your Love in Vain?