Motion-objection wraps the Objective-C dependency injection library Objection in Ruby so that it can be used in RubyMotion. It has all of the power (and speed) of Objection and the declarative affordances that the Ruby language provides.
gem install motion-objection
A class can declare requires component objects by mixing in
Objection::Compose and calling the
class Car include Objection::Compose compose_with :engine, :brakes end
:brakes are assumed to be the
Brakes classes. Classes that are namespaced can be declared as well by separating the namespaces using the
class Engine include Objection::Compose compose_with 'engine/crank_shaft', 'engine/rod' class CrankShaft end class Rod end end
Sometimes you may need to declare the component object and the class that is associated with it.
class Brakes compose_with factory: JSObjectFactory end
Singletons can be declared by calling the
.singleton method in the class body. Singletons should really only be necessary if they contain shared state. Otherwise it behooves you to avoid singletons in order to reduce the memory footprint of an application.
class Holder include Objection::Compose singleton end
Awaking from Objection
Since Objection utilizes setter based injection the initializer does not guarentee that all the object's dependencies have been satisfied.
awoken class method can be given a block which will be invoked once the object has been fully instantiated.
class Ship awoken do # Bootstrap listeners end awoken do # Setup other stuff end end
Objection uses Key-Value Coding to compose an instance with its dependencies -- it does not use initializer injection.
By default Objection initializes an object using the
init initializer. A custom initializer can be declared using the
class ViewController < UIViewController include Objection::Compose initializer "initWithNibName:bundle:", "Home" attr_reader :name def initWithNibName(name, bundle: bundle) self.init self.tap do @name = name end end end
Modules contribution configuration information to the Injector. Typically, this includes bindings for dependencies that the Injector cannot provide. For example,
UIApplication.sharedApplication or the main application window.
class AppModule < JSObjectionModule def initialize(window, application: application) @window = window @application = application end def configure bind @window, toClass: UIWindow bind @application, toClass: UIApplication end end
There are a number of other configuration methods a module provides.
Bootstraping an Application
Typically an application is bootstrapped in the application delegate where an injector is created and set as the default injector via
class AppDelegate def application(application, didFinishLaunchingWithOptions:launchOptions) initialize_objection Objection.default_injector[ApplicationBootstrapper].bootstrap! true end def initialize_objection @window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds) injector = Objection.injector(BankersDashboardModule.new(@window, UIApplication.sharedApplication)) Objection.default_injector = injector end end class ApplicationBootstrapper def bootstrap! # Bootstrap end end