leah blogs

« RubyPants 0.2 released November 2004 Learning Regexp Optimization the Hard Way »

15nov2004 · attr_inject: A step towards interface injection

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_readers 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?

Copyright © 2004–2022 Leah Neukirchen

AltStyle によって変換されたページ (->オリジナル) /