nitron 0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,2 @@
1
+ .repl_history
2
+ build
@@ -0,0 +1,83 @@
1
+ Nitron
2
+ ===================
3
+
4
+ Introduction
5
+ ----------
6
+ Nitron is an opinionated, loosely-coupled set of RubyMotion components designed to accelerate iOS
7
+ development, especially with simpler iOS apps. It provides meaningful
8
+ abstractions atop the strong foundation present in the iOS SDK.
9
+
10
+ This first release focuses on making Storyboard-based workflows enjoyable.
11
+
12
+ Installation
13
+ ----------
14
+ Add the following line to your `Gemfile`:
15
+
16
+ `gem "nitron"`
17
+
18
+ If you haven't already, update your Rakefile to use Bundler. Insert the
19
+ following immediately before `Motion::Project::App.setup`:
20
+
21
+ ```ruby
22
+ require 'rubygems'
23
+ require 'bundler'
24
+
25
+ Bundler.require
26
+ ```
27
+
28
+ Example
29
+ ------
30
+ A modal view controller responsible for creating new `Tasks`:
31
+
32
+ ```ruby
33
+ class TaskCreateViewController < Nitron::ViewController
34
+ # The on class method is part of Nitron's Action DSL.
35
+ # It wires the provided block to be an event handler for the specified outlet using the iOS target/action pattern.
36
+ on :cancel do
37
+ close
38
+ end
39
+
40
+ # Nitron emulates 'native' outlet support, allowing you to easily define outlets through Xcode.
41
+ # The titleField and datePicker methods are created upon initial load by using metadata contained in the Storyboard.
42
+ on :save do
43
+ Task.create(title: titleField.text, due: datePicker.date)
44
+
45
+ close
46
+ end
47
+ end
48
+ ```
49
+
50
+ Features
51
+ ----------
52
+
53
+ * **Data Binding** - declaratively bind your model data to controls, either
54
+ via code or Interface Builder
55
+ * **Outlet Support** - expose controls to your controllers via Interface Builder
56
+ * **Action Support** - Ruby DSL to attach event handlers to outlets
57
+ * **CoreData Models** - beginnings of a CoreData model abstraction uses
58
+ XCode's data modeling tools with an ActiveRecord-like syntax
59
+
60
+ If you notice, many of these features aim at slimming down your
61
+ controllers. This is no accident: many iOS controllers have far too many
62
+ responsibilities. Glue code is a perfect target for metaprogramming, so
63
+ we're focusing on making beautiful controllers presently.
64
+
65
+ We're also careful to make these features modular, so you can mix them
66
+ into your existing controllers as needed.
67
+
68
+ Tutorial
69
+ ----------
70
+ TBD
71
+
72
+ Examples
73
+ ----------
74
+ https://github.com/mattgreen/nitron-examples
75
+
76
+ Caveats
77
+ ---------
78
+
79
+ * Data binding doesn't use KVO presently. This is already in the works.
80
+ * Action support is limited to selecting a button or a table cell.
81
+ Future releases will expand the DSL to support additional events.
82
+ * CoreData needs support for relationships and migrations.
83
+
@@ -0,0 +1,20 @@
1
+ require 'nitron/version'
2
+
3
+ unless defined?(Motion::Project::Config)
4
+ raise "This file must be required within a RubyMotion project Rakefile."
5
+ end
6
+
7
+ Motion::Project::App.setup do |app|
8
+ Dir.glob(File.join(File.dirname(__FILE__), "nitron/**/*.rb")).each do |file|
9
+ app.files.unshift(file)
10
+ end
11
+
12
+ app.files.unshift(File.join(File.dirname(__FILE__), 'nitron/view_controller.rb'))
13
+ app.files.unshift(File.join(File.dirname(__FILE__), 'nitron/ui/data_binding_support.rb'))
14
+ app.files.unshift(File.join(File.dirname(__FILE__), 'nitron/ui/outlet_support.rb'))
15
+ app.files.unshift(File.join(File.dirname(__FILE__), 'nitron/ui/action_support.rb'))
16
+
17
+ unless app.frameworks.include?("CoreData")
18
+ app.frameworks << "CoreData"
19
+ end
20
+ end
@@ -0,0 +1,43 @@
1
+ class AppDelegate
2
+ def managedObjectContext
3
+ @managedObjectContext ||= begin
4
+ applicationName = NSBundle.mainBundle.infoDictionary.objectForKey("CFBundleName")
5
+
6
+ documentsDirectory = NSFileManager.defaultManager.URLsForDirectory(NSDocumentDirectory, inDomains:NSUserDomainMask).lastObject;
7
+ storeURL = documentsDirectory.URLByAppendingPathComponent("#{applicationName}.sqlite")
8
+
9
+ error_ptr = Pointer.new(:object)
10
+ unless persistentStoreCoordinator.addPersistentStoreWithType(NSSQLiteStoreType, configuration:nil, URL:storeURL, options:nil, error:error_ptr)
11
+ raise "Can't add persistent SQLite store: #{error_ptr[0].description}"
12
+ end
13
+
14
+ context = NSManagedObjectContext.alloc.init
15
+ context.persistentStoreCoordinator = persistentStoreCoordinator
16
+
17
+ context
18
+ end
19
+ end
20
+
21
+ def managedObjectModel
22
+ @managedObjectModel ||= begin
23
+ model = NSManagedObjectModel.mergedModelFromBundles([NSBundle.mainBundle]).mutableCopy
24
+
25
+ model.entities.each do |entity|
26
+ begin
27
+ Kernel.const_get(entity.name)
28
+ entity.setManagedObjectClassName(entity.name)
29
+
30
+ rescue NameError
31
+ entity.setManagedObjectClassName("Model")
32
+ end
33
+ end
34
+
35
+ model
36
+ end
37
+ end
38
+
39
+ def persistentStoreCoordinator
40
+ @coordinator ||= NSPersistentStoreCoordinator.alloc.initWithManagedObjectModel(managedObjectModel)
41
+ end
42
+ end
43
+
@@ -0,0 +1,100 @@
1
+ module Nitron
2
+ class Model < NSManagedObject
3
+ class << self
4
+ def all
5
+ Data::Relation.alloc.initWithClass(self)
6
+ end
7
+
8
+ def create(attributes={})
9
+ model = new(attributes)
10
+ model.save
11
+
12
+ model
13
+ end
14
+
15
+ def destroy(object)
16
+ if context = object.managedObjectContext
17
+ context.deleteObject(object)
18
+
19
+ error = Pointer.new(:object)
20
+ context.save(error)
21
+ end
22
+ end
23
+
24
+ def entityDescription
25
+ @_metadata ||= UIApplication.sharedApplication.delegate.managedObjectModel.entitiesByName[name]
26
+ end
27
+
28
+ def find(object_id)
29
+ unless entity = find_by_id(object_id)
30
+ raise "No record found!"
31
+ end
32
+
33
+ entity
34
+ end
35
+
36
+ def first
37
+ relation.first
38
+ end
39
+
40
+ def method_missing(method, *args, &block)
41
+ if method.start_with?("find_by_")
42
+ attribute = method.gsub("find_by_", "")
43
+ relation.where("#{attribute} = ?", *args).first
44
+ else
45
+ super
46
+ end
47
+ end
48
+
49
+ def new(attributes={})
50
+ self.alloc.initWithEntity(entityDescription, insertIntoManagedObjectContext:nil).tap do |model|
51
+ attributes.each do |keyPath, value|
52
+ model.setValue(value, forKey:keyPath)
53
+ end
54
+ end
55
+ end
56
+
57
+ def respond_to?(method)
58
+ if method.start_with?("find_by_")
59
+ true
60
+ else
61
+ super
62
+ end
63
+ end
64
+
65
+ def order(*args)
66
+ relation.order(*args)
67
+ end
68
+
69
+ def where(*args)
70
+ relation.where(*args)
71
+ end
72
+
73
+ private
74
+
75
+ def relation
76
+ Data::Relation.alloc.initWithClass(self)
77
+ end
78
+ end
79
+
80
+ def destroy
81
+ self.class.destroy(self)
82
+ end
83
+
84
+ def inspect
85
+ properties = entity.properties.map { |property| "#{property.name}: #{valueForKey(property.name).inspect}" }
86
+
87
+ "#<#{entity.name} #{properties.join(", ")}>"
88
+ end
89
+
90
+ def save
91
+ unless context = managedObjectContext
92
+ context = UIApplication.sharedApplication.delegate.managedObjectContext
93
+ context.insertObject(self)
94
+ end
95
+
96
+ error = Pointer.new(:object)
97
+ context.save(error)
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,59 @@
1
+ module Nitron
2
+ module Data
3
+ class Relation < NSFetchRequest
4
+ def initWithClass(entityClass)
5
+ if init
6
+ setEntity(entityClass.entityDescription)
7
+ end
8
+
9
+ self
10
+ end
11
+
12
+ def all
13
+ self
14
+ end
15
+
16
+ def first
17
+ setFetchLimit(1)
18
+
19
+ to_a[0]
20
+ end
21
+
22
+ def inspect
23
+ to_a
24
+ end
25
+
26
+ def order(column, opts={})
27
+ descriptors = sortDescriptors || []
28
+
29
+ descriptors << NSSortDescriptor.alloc.initWithKey(column.to_s, ascending:opts.fetch(:ascending, true))
30
+ setSortDescriptors(descriptors)
31
+
32
+ self
33
+ end
34
+
35
+ def to_a
36
+ error = Pointer.new(:object)
37
+ context.executeFetchRequest(self, error:error)
38
+ end
39
+
40
+ def where(format, *args)
41
+ predicate = NSPredicate.predicateWithFormat(format.gsub("?", "%@"), argumentArray:args)
42
+
43
+ if self.predicate
44
+ self.predicate = NSCompoundPredicate.andPredicateWithSubpredicates([predicate])
45
+ else
46
+ self.predicate = predicate
47
+ end
48
+
49
+ self
50
+ end
51
+
52
+ private
53
+
54
+ def context
55
+ UIApplication.sharedApplication.delegate.managedObjectContext
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,38 @@
1
+ module Nitron
2
+ class StaticTableViewController < ViewController
3
+ def setValue(value, forKey: key)
4
+ if key == "staticDataSource"
5
+ @_dataSource = value
6
+ else
7
+ super
8
+ end
9
+ end
10
+
11
+ def tableView(tableView, didSelectRowAtIndexPath:indexPath)
12
+ cell = tableView.cellForRowAtIndexPath(indexPath)
13
+
14
+ if outlet = cell.outlets.first
15
+ handler = self.class.outletHandlers[outlet[0]]
16
+
17
+ if handler
18
+ self.instance_eval(&handler[:handler])
19
+ end
20
+ end
21
+ end
22
+
23
+ def tableView(tableView, heightForRowAtIndexPath:indexPath)
24
+ cell = @_dataSource.tableView(tableView, cellForRowAtIndexPath:indexPath)
25
+
26
+ cell.bounds.size.height
27
+ end
28
+
29
+ def viewWillAppear(animated)
30
+ view.dataSource = @_dataSource
31
+ view.delegate = self
32
+
33
+ # The data binding module may wrap view.delegate, so run it after we've set up.
34
+ super
35
+ end
36
+ end
37
+ end
38
+
@@ -0,0 +1,172 @@
1
+ module Nitron
2
+ class TableViewController < ViewController
3
+ def self.collection(&block)
4
+ options[:collection] = block
5
+ end
6
+
7
+ def self.group_by(name, opts={})
8
+ options[:groupBy] = name.to_s
9
+ options[:groupIndex] = opts[:index] || false
10
+ end
11
+
12
+ def self.options
13
+ @options ||= {
14
+ collection: lambda { [] },
15
+ groupBy: nil,
16
+ groupIndex: false,
17
+ }
18
+ end
19
+
20
+ protected
21
+
22
+ def controllerDidChangeContent(controller)
23
+ view.reloadData()
24
+ end
25
+
26
+ def dataSource
27
+ @_dataSource ||= begin
28
+ collection = self.instance_eval(&self.class.options[:collection])
29
+
30
+ case collection
31
+ when Array
32
+ ArrayDataSource.alloc.initWithCollection(collection, className:self.class.name)
33
+ when NSFetchRequest
34
+ CoreDataSource.alloc.initWithRequest(collection, owner:self, sectionNameKeyPath:self.class.options[:groupBy], options:self.class.options)
35
+ else
36
+ raise "Collection block must return an Array, or an NSFetchRequest"
37
+ end
38
+ end
39
+ end
40
+
41
+ def prepareForSegue(segue, sender:sender)
42
+ model = nil
43
+
44
+ if view.respond_to?(:indexPathForSelectedRow)
45
+ if view.indexPathForSelectedRow
46
+ model = dataSource.objectAtIndexPath(view.indexPathForSelectedRow)
47
+ end
48
+ end
49
+
50
+ if model
51
+ controller = segue.destinationViewController
52
+ if controller.respond_to?(:model=)
53
+ controller.model = model
54
+ end
55
+ end
56
+ end
57
+
58
+ def setValue(value, forKey: key)
59
+ if key == "staticDataSource"
60
+ raise "Static tables are not supported by TableViewController! Please use StaticTableViewController instead."
61
+ else
62
+ super
63
+ end
64
+ end
65
+
66
+ def viewDidLoad
67
+ super
68
+
69
+ view.dataSource = dataSource
70
+ end
71
+
72
+ protected
73
+
74
+ class ArrayDataSource
75
+ def initWithCollection(collection, className:className)
76
+ if init
77
+ @collection = collection
78
+ @className = className
79
+ end
80
+
81
+ self
82
+ end
83
+
84
+ def numberOfSectionsInTableView(tableView)
85
+ 1
86
+ end
87
+
88
+ def objectAtIndexPath(indexPath)
89
+ @collection[indexPath.row]
90
+ end
91
+
92
+ def sectionForSectionIndexTitle(title, atIndex:index)
93
+ nil
94
+ end
95
+
96
+ def tableView(tableView, cellForRowAtIndexPath:indexPath)
97
+ @cellReuseIdentifier ||= "#{@className.gsub("ViewController", "")}Cell"
98
+ unless cell = tableView.dequeueReusableCellWithIdentifier(@cellReuseIdentifier)
99
+ puts "Unable to find a cell named #{@cellReuseIdentifier}. Have you set the reuse identifier of the UITableViewCell?"
100
+ return
101
+ end
102
+
103
+ cell
104
+ end
105
+
106
+ def tableView(tableView, numberOfRowsInSection:section)
107
+ @collection.size
108
+ end
109
+ end
110
+
111
+ class CoreDataSource
112
+ def initWithRequest(request, owner:owner, sectionNameKeyPath:sectionNameKeyPath, options:options)
113
+ if init
114
+ context = UIApplication.sharedApplication.delegate.managedObjectContext
115
+
116
+ @className = owner.class.name
117
+ @controller = NSFetchedResultsController.alloc.initWithFetchRequest(request,
118
+ managedObjectContext:context,
119
+ sectionNameKeyPath:sectionNameKeyPath,
120
+ cacheName:nil)
121
+ @controller.delegate = owner
122
+ @options = options
123
+
124
+ errorPtr = Pointer.new(:object)
125
+ unless @controller.performFetch(errorPtr)
126
+ raise "Error fetching data"
127
+ end
128
+ end
129
+
130
+ self
131
+ end
132
+
133
+ def numberOfSectionsInTableView(tableView)
134
+ @controller.sections.size
135
+ end
136
+
137
+ def objectAtIndexPath(indexPath)
138
+ @controller.objectAtIndexPath(indexPath)
139
+ end
140
+
141
+ def sectionForSectionIndexTitle(title, atIndex:index)
142
+ @collection.sectionForSectionIndexTitle(title, atIndex:index)
143
+ end
144
+
145
+ def sectionIndexTitlesForTableView(tableView)
146
+ if @options[:groupIndex]
147
+ @controller.sectionIndexTitles
148
+ else
149
+ nil
150
+ end
151
+ end
152
+
153
+ def tableView(tableView, cellForRowAtIndexPath:indexPath)
154
+ @cellReuseIdentifier ||= "#{@className.gsub("ViewController", "")}Cell"
155
+ unless cell = tableView.dequeueReusableCellWithIdentifier(@cellReuseIdentifier)
156
+ puts "Unable to find a cell named #{@cellReuseIdentifier}. Have you set the reuse identifier of the UITableViewCell?"
157
+ return nil
158
+ end
159
+
160
+ cell
161
+ end
162
+
163
+ def tableView(tableView, numberOfRowsInSection:section)
164
+ @controller.sections[section].numberOfObjects
165
+ end
166
+
167
+ def tableView(tableView, titleForHeaderInSection:section)
168
+ @controller.sections[section].name
169
+ end
170
+ end
171
+ end
172
+ end
@@ -0,0 +1,49 @@
1
+ module Nitron
2
+ module UI
3
+ module ActionSupport
4
+ def self.included(base)
5
+ base.extend(ClassMethods)
6
+ end
7
+
8
+ module ClassMethods
9
+ def on(outlet, &block)
10
+ actions[outlet.to_s] = { :handler => block }
11
+ end
12
+
13
+ def actions
14
+ @_actions ||= {
15
+ "cancel" => { :handler => proc { close }, :default => true },
16
+ "done" => { :handler => proc { close }, :default => true }
17
+ }
18
+ end
19
+ end
20
+
21
+ def _dispatch(sender)
22
+ if action = @_actions[sender]
23
+ instance_eval &action[:handler]
24
+ end
25
+ end
26
+
27
+ def dealloc
28
+ @_actions.clear
29
+
30
+ super
31
+ end
32
+
33
+ def viewDidLoad
34
+ super
35
+
36
+ @_actions = {}
37
+
38
+ self.class.actions.each do |outlet, action|
39
+ if respond_to?(outlet)
40
+ target = send(outlet)
41
+ @_actions[target] = self.class.actions[outlet]
42
+
43
+ target.addTarget(self, action:"_dispatch:", forControlEvents:UIControlEventTouchUpInside)
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,49 @@
1
+ module Nitron
2
+ module UI
3
+ class DataBinder
4
+ def self.shared
5
+ @singleton ||= alloc.init
6
+ end
7
+
8
+ def bind(model, view, options={})
9
+ if view.is_a?(UITableView)
10
+ view.delegate = DataBoundTableDelegate.alloc.initWithDelegate(view.delegate)
11
+ return [view.delegate]
12
+ end
13
+
14
+ view.dataBindings.each do |keyPath, subview|
15
+ bindControl(model, subview, keyPath)
16
+ end
17
+
18
+ nil
19
+ end
20
+
21
+ private
22
+
23
+ def bindControl(model, control, keyPath)
24
+ value = model.valueForKeyPath(keyPath)
25
+
26
+ if control.respond_to?(:text=)
27
+ control.text = value
28
+ elsif control.respond_to?(:image=)
29
+ control.image = value
30
+ elsif control.respond_to?(:value=)
31
+ control.value = value
32
+ elsif control.respond_to?(:on=)
33
+ control.on = value
34
+ elsif control.respond_to?(:progress=)
35
+ control.progress = value
36
+ elsif control.respond_to?(:date=)
37
+ control.date = value
38
+ else
39
+ puts "Sorry, data binding is not supported for an instance of '#{control.class.name}' :("
40
+ end
41
+
42
+ rescue
43
+ puts "***ERROR: Failed to bind value #{value.inspect} (read from '#{model.class.name}.#{keyPath}') to #{control.inspect}"
44
+
45
+ raise
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,33 @@
1
+ module Nitron
2
+ module UI
3
+ module DataBindingSupport
4
+ def dealloc
5
+ if @_bindings
6
+ @_bindings = nil
7
+ end
8
+
9
+ if @_model
10
+ @_model = nil
11
+ end
12
+
13
+ super
14
+ end
15
+
16
+ def model
17
+ @_model
18
+ end
19
+
20
+ def model=(model)
21
+ @_model = model
22
+
23
+ DataBinder.shared.bind(model, view)
24
+ end
25
+
26
+ def viewDidLoad
27
+ super
28
+
29
+ @_bindings = DataBinder.shared.bind(model, view)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,41 @@
1
+ module Nitron
2
+ module UI
3
+ class DataBoundTableDelegate
4
+ def initWithDelegate(delegate)
5
+ if init
6
+ @delegate = delegate
7
+ end
8
+
9
+ self
10
+ end
11
+
12
+ def method_missing(method, *args, &block)
13
+ if @delegate
14
+ @delegate.send(method, *args, &block)
15
+ else
16
+ super
17
+ end
18
+ end
19
+
20
+ def respond_to?(method)
21
+ if method == "tableView:willDisplayCell:forRowAtIndexPath"
22
+ true
23
+ elsif @delegate
24
+ @delegate.respond_to?(method)
25
+ else
26
+ super
27
+ end
28
+ end
29
+
30
+ def tableView(tableView, willDisplayCell:cell, forRowAtIndexPath:indexPath)
31
+ if @delegate && @delegate.respond_to?("tableView:willDisplayCell:forRowAtIndexPath:")
32
+ @delegate.tableView(tableView, willDisplayCell:cell, forRowAtIndexPath:indexPath)
33
+ end
34
+
35
+ model = tableView.dataSource.objectAtIndexPath(indexPath)
36
+
37
+ Nitron::UI::DataBinder.shared.bind(model, cell)
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,19 @@
1
+ class AppDelegate
2
+ def application(application, didFinishLaunchingWithOptions:launchOptions)
3
+ @window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds)
4
+
5
+ if storyboard
6
+ @window.rootViewController = storyboard.instantiateInitialViewController
7
+ end
8
+
9
+ @window.rootViewController.wantsFullScreenLayout = true
10
+ @window.makeKeyAndVisible
11
+
12
+ true
13
+ end
14
+
15
+ def storyboard
16
+ @storyboard ||= UIStoryboard.storyboardWithName("MainStoryboard", bundle:nil)
17
+ end
18
+ end
19
+
@@ -0,0 +1,9 @@
1
+ class UIBarButtonItem
2
+ def setValue(value, forUndefinedKey:key)
3
+ if key == "outlet"
4
+ view.setValue(value, forUndefinedKey:key)
5
+ else
6
+ super
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,32 @@
1
+ class UIView
2
+ def dataBindings
3
+ @_dataBindings ||= {}
4
+ end
5
+
6
+ def outlets
7
+ @_outlets ||= {}
8
+ end
9
+
10
+ def setValue(value, forUndefinedKey:key)
11
+ if key == "dataBinding" || key == "outlet"
12
+ raise "Runtime attribute '#{key}' must be a String (declared on #{self.class.name})" unless value.is_a?(String)
13
+
14
+ container = self
15
+ while container.superview
16
+ container = container.superview
17
+ end
18
+
19
+ if key == "dataBinding"
20
+ unless value.start_with?("model.")
21
+ raise "Data binding expression must start with 'model.'; you provided '#{value}'"
22
+ end
23
+
24
+ container.dataBindings[value[6..-1]] = self
25
+ else
26
+ container.outlets[value] = self
27
+ end
28
+ else
29
+ super
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,12 @@
1
+ module Nitron
2
+ module UI
3
+ class OutletBinder
4
+ def bind(controller, view)
5
+ # Emulate IB's outlets by using KVC.
6
+ view.outlets.each do |outlet, subview|
7
+ controller.setValue(subview, forKey:outlet)
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,20 @@
1
+ module Nitron
2
+ module UI
3
+ module OutletSupport
4
+ def setValue(value, forUndefinedKey:key)
5
+ unless self.class.respond_to?(key)
6
+ self.class.send(:attr_reader, key)
7
+ end
8
+
9
+ instance_variable_set("@#{key}", value)
10
+ end
11
+
12
+ def viewDidLoad
13
+ super
14
+
15
+ outletBinder = OutletBinder.new
16
+ outletBinder.bind(self, view)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,3 @@
1
+ module Nitron
2
+ VERSION = "0.2"
3
+ end
@@ -0,0 +1,12 @@
1
+ module Nitron
2
+ class ViewController < UIViewController
3
+ include UI::DataBindingSupport
4
+ include UI::OutletSupport
5
+ include UI::ActionSupport
6
+
7
+ def close
8
+ dismissModalViewControllerAnimated(true)
9
+ end
10
+ end
11
+ end
12
+
@@ -0,0 +1,18 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/nitron/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Matt Green"]
6
+ gem.email = ["mattgreenrocks@gmail.com"]
7
+ gem.description = "Turbocharged iOS development via RubyMotion"
8
+ gem.summary = "Turbocharged iOS development via RubyMotion"
9
+ gem.homepage = "https://github.com/mattgreen/nitron"
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
13
+ gem.name = "nitron"
14
+ gem.require_paths = ["lib"]
15
+ gem.version = Nitron::VERSION
16
+
17
+ gem.add_dependency 'motion-cocoapods', '>= 1.0.1'
18
+ end
@@ -0,0 +1,9 @@
1
+ describe "Application 'spry'" do
2
+ before do
3
+ @app = UIApplication.sharedApplication
4
+ end
5
+
6
+ it "has one window" do
7
+ @app.windows.size.should == 1
8
+ end
9
+ end
metadata ADDED
@@ -0,0 +1,95 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: nitron
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 2
8
+ version: "0.2"
9
+ platform: ruby
10
+ authors:
11
+ - Matt Green
12
+ autorequire:
13
+ bindir: bin
14
+ cert_chain: []
15
+
16
+ date: 2012-06-12 00:00:00 -04:00
17
+ default_executable:
18
+ dependencies:
19
+ - !ruby/object:Gem::Dependency
20
+ name: motion-cocoapods
21
+ prerelease: false
22
+ requirement: &id001 !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ segments:
27
+ - 1
28
+ - 0
29
+ - 1
30
+ version: 1.0.1
31
+ type: :runtime
32
+ version_requirements: *id001
33
+ description: Turbocharged iOS development via RubyMotion
34
+ email:
35
+ - mattgreenrocks@gmail.com
36
+ executables: []
37
+
38
+ extensions: []
39
+
40
+ extra_rdoc_files: []
41
+
42
+ files:
43
+ - .gitignore
44
+ - README.md
45
+ - lib/nitron.rb
46
+ - lib/nitron/data/extensions/app_delegate+core_data.rb
47
+ - lib/nitron/data/model.rb
48
+ - lib/nitron/data/relation.rb
49
+ - lib/nitron/static_table_view_controller.rb
50
+ - lib/nitron/table_view_controller.rb
51
+ - lib/nitron/ui/action_support.rb
52
+ - lib/nitron/ui/data_binder.rb
53
+ - lib/nitron/ui/data_binding_support.rb
54
+ - lib/nitron/ui/data_bound_table_delegate.rb
55
+ - lib/nitron/ui/extensions/app_delegate+storyboard.rb
56
+ - lib/nitron/ui/extensions/ui_bar_button_item.rb
57
+ - lib/nitron/ui/extensions/ui_view.rb
58
+ - lib/nitron/ui/outlet_binder.rb
59
+ - lib/nitron/ui/outlet_support.rb
60
+ - lib/nitron/version.rb
61
+ - lib/nitron/view_controller.rb
62
+ - nitron.gemspec
63
+ - spec/main_spec.rb
64
+ has_rdoc: true
65
+ homepage: https://github.com/mattgreen/nitron
66
+ licenses: []
67
+
68
+ post_install_message:
69
+ rdoc_options: []
70
+
71
+ require_paths:
72
+ - lib
73
+ required_ruby_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ segments:
78
+ - 0
79
+ version: "0"
80
+ required_rubygems_version: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ segments:
85
+ - 0
86
+ version: "0"
87
+ requirements: []
88
+
89
+ rubyforge_project:
90
+ rubygems_version: 1.3.6
91
+ signing_key:
92
+ specification_version: 3
93
+ summary: Turbocharged iOS development via RubyMotion
94
+ test_files:
95
+ - spec/main_spec.rb