Earlier this week Nate already said that I was doing some work on Ninject, now I have it working
. Everything I’m about to talk about is currently in the master tree of the ninject github repository. Getting IronRuby to play nice with Ninject was surprisingly easy
,
There was only one place that required some kind of weird workaround and from that workaround I’m entirely sure that it will go away by the time .NET 4.0 will be here. The DLR duplicates a number of delegates from .NET 4.0 but .NET 3.5 also defines them (i.e. System.Func<T, TT>) and then you get great exception messages like: System.Func is not of type System.Func. The solution is to not reference System.Core in your project. Except that Ninject expects the System.Core variant at some point and that was solved by aliasing the System.Core assembly and talking to the types in that assembly by their alias.
Anyway the juicy stuff
How can you take advantage of Ninjects newly found friendship with IronRuby.
Ninject now has 2 flavors of Kernels. We have a StandardKernel that knows how to deal with the module configuration system that uses a fluent interface defined in C#. And now we also have a DlrKernel that extends the StandardKernel with a RubyModuleLoader plugin. If you tell the DlrKernel to look inside a path for configuration files it will scan those folders for *.dll or *.rb files. Those files should contain the configuration for the ninject bindings.
So to create a Kernel that is ruby enabled you would use the following code:
IKernel kernel = new DlrKernel(); kernel.AutoLoadModulesRecursively(); var samurai = kernel.Get<IWarrior>(); System.Console.WriteLine(samurai.Weapon.Name);
The above snippet could then for example load a configuration file that has been defined like this:
require File.dirname(__FILE__) + '/../Ninject.Tests.dll' include Ninject::Tests::Fakes to_configure_ninject do |ninject| ninject.bind IWeapon, :to => Sword ninject.bind IWarrior, :to => Samurai end
The configuration above shows how most of a typical configuration would be defined by you the full configuration API at your disposal. All the options for the configuration can be specified in 2 ways. The first way is in a hash like syntax and the second way uses a more fluent syntax.
to_configure_ninject do |ninject|
ninject.bind IServiceA, :to => ServiceA, :as => :singleton,
:meta => { :type => "superservice" },
:name => "aaaaa",
:with => {
:parameter => { :my_param => lambda { |context| "param_value" } },
:constructor_arguments => {:const_arg => 56 },
:property_values => {:property_name => 94 },
},
n_activation => lambda { |obj| obj.do_some_work },
n_deativated => lambda { |obj| obj.do_some_cleanup },
:when => lambda { |context| "a value" } or
:when => { :injected_into => ServiceB } or
:when => { :target_has => AnAttribute } or
:when => { :member_has => AnAttribute } or
:when => { :class_has => AnAttribute }
}
end
Or
to_configure_ninject do |ninject|
ninject.bind IServiceA, :to => ServiceA, :as => :singleton do
meta :type => "superservice"
name "aaaaa"
with :parameter => { :my_param => lambda { |context| "param_value" } }
with :constructor_arguments => { :const_arg => 56 }
with :property_values => { property_name => 94 }
on_activation do |obj|
obj.do_some_work
end
on_deativation { |obj| obj.do_some_cleanup }
condition do |context|
true
end or
condition :injected_into => SomeClass or …
end
end
Some of the nicer consequences of using Ruby as a configuration language is the syntax for open generics. The example below shows how to configure types with open generics.
require File.dirname(__FILE__) + '/../Ninject.Tests.dll' include Ninject::Tests::Fakes include Ninject::Tests::Integration::StandardKernelTests # IGeneric is a generic interface and GenericService is a generic type # we don't have to specify any special notation for open generics to_configure_ninject do |ninject| ninject.bind IGeneric, :to => GenericService, :as => :transient ninject.bind IGeneric, :to => GenericService2 end
To specify a condition the syntax would look like this
require File.dirname(__FILE__) + '/../Ninject.Tests.dll' include Ninject::Tests::Fakes to_configure_ninject do |ninject| ninject.bind IWeapon, :to => Shuriken do condition do |request| request.target.nil?
? false
: request.target.member.reflected_type == Samurai.to_clr_type end end ninject.bind IWeapon, :to => Sword ninject.bind IWarrior, :to => Samurai end
Well that’s all. I hope you like it. I will be looking into more ways to integrate DLR stuff into Ninject the most obvious is allowing you to inject dynamic types into static classes.
March 15, 2009 at 01:35
[...] Note: this entry was cross-posted from flanders.co.nz [...]
March 15, 2009 at 22:44
Ninject knows a new trick – Ivan Porto Carrero…
Thank you for submitting this cool story – Trackback from DotNetShoutout…
March 16, 2009 at 18:03
Nice!
May 25, 2009 at 22:34
Do you know a way to add a Ruby class that implements a .net Interface? and bind that so ninject can use that?, im breen trying but no luck,
May 25, 2009 at 22:41
you have to include the interface in the ruby class.
and then define all the methods of that interface with snake case naming.
public interface IAmAnInterface {
void SomeMethod();
void AnotherMethod();
}
class Implementor
include IAmAnInterface
def some_method
#implement here
end
def another_method
#implement here
end
end
May 26, 2009 at 14:08
When i do that i get:
Additional information: wrong argument type Class (expected Module)
im doing
to_configure_ninject do |ninject|
ninject.bind Interfaces::IEaea, :to => Rubyclass
end
May 26, 2009 at 14:14
Rene can you submit an issue with steps to reproduce on the github page for this project.
http://github.com/casualjim/ninject-dynamic/issues