motion-objection

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.

Build Status Gem Version Code Climate

Installation

gem install motion-objection

Basic Usage

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

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.

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

Default Initializers

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

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 .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