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 .compose_with
method
class Car
include Objection::Compose
compose_with :engine, :brakes
end
Where :engine
and :brakes
are assumed to be the Engine
and Brakes
classes. Classes that are namespaced can be declared as well by separating the namespaces using the /
character.
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
Since Objection utilizes setter based injection the initializer does not guarentee that all the object's dependencies have been satisfied.
The 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 .initializer
method.
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.
Typically an application is bootstrapped in the application delegate where an injector is created and set as the default injector via .default_injector=
.
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