J
Joel VanderWerf
MinDI is a DI (dependency injection) or IoC (invesion of control)
framework for ruby inspired by Jamis Buck's Needle
(http://needle.rubyforge.org)
and Jim Weirich's article on DI in Ruby
(http://onestepback.org/index.cgi/Tech/Ruby/DependencyInjectionInRuby.rdoc).
DOWNLOAD
MinDI is available at:
http://redshift.sourceforge.net/mindi/
Includes examples, tests, and basic docs. It's a proof of concept,
but has enough features for production use.
DESCRIPTION
MinDI is minimalist in that it attempts to map concepts of DI into
basic ruby constructs, rather than into a layer of specialized
constructs.
In particular, classes and modules function as containers and
registries, and methods and method definitions function as service
points and services.
There are some inherent advantages and disadvantages to this approach.
Advantages:
- Compact implementation.
- Compact syntax.
- Familiar constructs and idioms, like subclassing, module inclusion,
nested classes, protected and private, all apply.
- Use of classes and methods as containers and services means you can
apply a standard AOP or debugging lib.
- Services can take arguments, and this permits multiton services.
Disadvantages:
- A container's services live in the same namespace as the methods
inherited from Kernel and Object, so a service called "dup", for
example, will prevent calling Object#dup on the container (except in
the implementation of the dup service, which can use super to invoke
Object#dup). The MinDI framework itself adds a few methods that
could conflict with services (#singleton, #generic, etc.).
- No built-in AOP, debugging, or reflection interface.
Notes:
- Supports threaded, deferred, singleton, and multiton service models
(though these are not yet independent choices). Additional service
models can be easily added in modules which include Container. The
"generic" model can be used like "prototype" in Needle, or for
manual service management.
- Use mixins to build apps out of groups of services that need to
coexist in one name space.
- Use a nested class for a group of services when you want them to
live in their own namespace.
EXAMPLES
An example with parametric services:
class Point < Struct.newx, :y); end
class MyContainer
extend MinDI::Container
# A multiton service: a unique value is generated for
# each requested parameter list.
point_for_xy { |x,y| Point.new(x,y) }
rect_with_width { |w| [point_for(0,0), point_for(w,200)] }
end
The example from Jim Weirich's article would look like this in MinDI:
class JWApplicationContainer
extend MinDI::Container
logfilename { "logfile.log" }
db_user { "jim" }
db_password { "secret" }
dbi_string { "DBIg:example_data" }
app {
app = WebApp.new(quotes, authenticator, database)
app.logger = logger
app.set_error_handler error_handler
app
}
quotes { StockQuotes.new(error_handler, logger) }
authenticator { Authenticator.new(database, logger,
error_handler) }
database { DBI.connect(dbi_string, db_user, db_password) }
logger { Logger.new(logfilename) }
error_handler {
errh = ErrorHandler.new
errh.logger = logger
errh
}
end
def create_application
JWApplicationContainer.new.app
end
framework for ruby inspired by Jamis Buck's Needle
(http://needle.rubyforge.org)
and Jim Weirich's article on DI in Ruby
(http://onestepback.org/index.cgi/Tech/Ruby/DependencyInjectionInRuby.rdoc).
DOWNLOAD
MinDI is available at:
http://redshift.sourceforge.net/mindi/
Includes examples, tests, and basic docs. It's a proof of concept,
but has enough features for production use.
DESCRIPTION
MinDI is minimalist in that it attempts to map concepts of DI into
basic ruby constructs, rather than into a layer of specialized
constructs.
In particular, classes and modules function as containers and
registries, and methods and method definitions function as service
points and services.
There are some inherent advantages and disadvantages to this approach.
Advantages:
- Compact implementation.
- Compact syntax.
- Familiar constructs and idioms, like subclassing, module inclusion,
nested classes, protected and private, all apply.
- Use of classes and methods as containers and services means you can
apply a standard AOP or debugging lib.
- Services can take arguments, and this permits multiton services.
Disadvantages:
- A container's services live in the same namespace as the methods
inherited from Kernel and Object, so a service called "dup", for
example, will prevent calling Object#dup on the container (except in
the implementation of the dup service, which can use super to invoke
Object#dup). The MinDI framework itself adds a few methods that
could conflict with services (#singleton, #generic, etc.).
- No built-in AOP, debugging, or reflection interface.
Notes:
- Supports threaded, deferred, singleton, and multiton service models
(though these are not yet independent choices). Additional service
models can be easily added in modules which include Container. The
"generic" model can be used like "prototype" in Needle, or for
manual service management.
- Use mixins to build apps out of groups of services that need to
coexist in one name space.
- Use a nested class for a group of services when you want them to
live in their own namespace.
EXAMPLES
An example with parametric services:
class Point < Struct.newx, :y); end
class MyContainer
extend MinDI::Container
# A multiton service: a unique value is generated for
# each requested parameter list.
point_for_xy { |x,y| Point.new(x,y) }
rect_with_width { |w| [point_for(0,0), point_for(w,200)] }
end
The example from Jim Weirich's article would look like this in MinDI:
class JWApplicationContainer
extend MinDI::Container
logfilename { "logfile.log" }
db_user { "jim" }
db_password { "secret" }
dbi_string { "DBIg:example_data" }
app {
app = WebApp.new(quotes, authenticator, database)
app.logger = logger
app.set_error_handler error_handler
app
}
quotes { StockQuotes.new(error_handler, logger) }
authenticator { Authenticator.new(database, logger,
error_handler) }
database { DBI.connect(dbi_string, db_user, db_password) }
logger { Logger.new(logfilename) }
error_handler {
errh = ErrorHandler.new
errh.logger = logger
errh
}
end
def create_application
JWApplicationContainer.new.app
end