motion-objection ================ Motion-objection wraps the Objective-C dependency injection library [Objection](https://github.com/atomicobject/objection) in Ruby so that it can be used in [RubyMotion](http://www.rubymotion.com/). It has all of the power (and speed) of Objection and the declarative affordances that the Ruby language provides. [![Gem Version](https://badge.fury.io/rb/motion-objection.png)](http://badge.fury.io/rb/motion-objection) [![Code Climate](https://codeclimate.com/github/atomicobject/motion-objection.png)](https://codeclimate.com/github/atomicobject/motion-objection) ## Installation ```bash gem install motion-objection ``` ## Basic Usage A class can declare requires component objects by mixing in Objection::Compose and calling the .compose_with method ```ruby 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. ```ruby 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. ```ruby class Brakes compose_with factory: JSObjectFactory end ``` ## Singletons 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. ```ruby 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. The `awoken` class method can be given a block which will be invoked once the object has been fully instantiated. ```ruby class Ship awoken do # Bootstrap listeners end awoken do # Setup other stuff end end ``` ## Default Initializers Objection uses [Key-Value Coding](http://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/KeyValueCoding/Articles/KeyValueCoding.html) 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. ```ruby 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 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. ```ruby 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](https://github.com/atomicobject/objection#modules). ## Bootstraping an Application Typically an application is bootstrapped in the application delegate where an injector is created and set as the default injector via .default_injector=. ```ruby 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 ```