formotion 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.gitignore ADDED
@@ -0,0 +1,10 @@
1
+ .repl_history
2
+ build
3
+ resources/*.nib
4
+ resources/*.momd
5
+ resources/*.storyboardc
6
+ .DS_STORE
7
+ *.gem
8
+ .bundle
9
+ Gemfile.lock
10
+ pkg/*
data/Formotion.gemspec ADDED
@@ -0,0 +1,19 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/formotion/version', __FILE__)
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "formotion"
6
+ s.version = Formotion::VERSION
7
+ s.authors = ["Clay Allsopp"]
8
+ s.email = ["clay.allsopp@gmail.com"]
9
+ s.homepage = "https://github.com/clayallsopp/Formotion"
10
+ s.summary = "Making iOS Forms insanely great with RubyMotion"
11
+ s.description = "Making iOS Forms insanely great with RubyMotion"
12
+
13
+ s.files = `git ls-files`.split($\)
14
+ s.test_files = s.files.grep(%r{^(test|spec|features)/})
15
+ s.require_paths = ["lib"]
16
+
17
+ s.add_dependency "bubble-wrap"
18
+ s.add_development_dependency 'rake'
19
+ end
data/README.md ADDED
@@ -0,0 +1,168 @@
1
+ # Formotion
2
+
3
+ Make this:
4
+
5
+ ![Complex data form](http://i.imgur.com/TMwXI.png)
6
+
7
+ using just this:
8
+
9
+ ```ruby
10
+ @form = Formotion::Form.new({
11
+ sections: [{
12
+ title: "Register",
13
+ rows: [{
14
+ title: "Email",
15
+ key: :email,
16
+ placeholder: "me@mail.com",
17
+ type: :email,
18
+ auto_correction: :no,
19
+ auto_capitalization: :none
20
+ }, {
21
+ title: "Password",
22
+ key: :password,
23
+ placeholder: "required",
24
+ type: :string,
25
+ secure: true
26
+ }, {
27
+ title: "Password",
28
+ subtitle: "Confirmation"
29
+ key: :confirm,
30
+ placeholder: "required",
31
+ type: :string,
32
+ secure: true
33
+ }, {
34
+ title: "Remember?",
35
+ key: :remember,
36
+ type: :switch,
37
+ }]
38
+ }, {
39
+ title: "Account Type",
40
+ key: :account_type,
41
+ select_one: true,
42
+ rows: [{
43
+ title: "Free",
44
+ key: :free,
45
+ type: :check,
46
+ }, {
47
+ title: "Basic",
48
+ value: true,
49
+ key: :basic,
50
+ type: :check,
51
+ }, {
52
+ title: "Pro",
53
+ key: :pro,
54
+ type: :check,
55
+ }]
56
+ }, {
57
+ rows: [{
58
+ title: "Sign Up",
59
+ type: :submit,
60
+ }]
61
+ }]
62
+ })
63
+
64
+ @form_controller = FormController.alloc.initWithForm(@form)
65
+ @window.rootViewController = @form_controller
66
+ ```
67
+
68
+ And after the user enters some data, do this:
69
+
70
+ ```ruby
71
+ @form.render
72
+ => {:email=>"me@email.com", :password=>"password",
73
+ :confirm=>"password", :remember=>true, :account_type=>:pro}
74
+ ```
75
+
76
+ ## Installation
77
+
78
+ `gem install formotion`
79
+
80
+ In your `Rakefile`:
81
+
82
+ `require 'formotion'`
83
+
84
+ ## Usage
85
+
86
+ ### Initialize
87
+
88
+ You can initialize a `Formotion::Form` using either a hash (as above) or the DSL:
89
+
90
+ ```ruby
91
+ form = Formotion::Form.new
92
+
93
+ form.build_section do |section|
94
+ section.title = "Title"
95
+
96
+ section.build_row do |row|
97
+ row.title = "Label"
98
+ row.subtitle = "Placeholder"
99
+ end
100
+ end
101
+ ```
102
+
103
+ Then attach it to a `Formotion::Controller` and you're ready to rock and roll:
104
+
105
+ ```ruby
106
+ @controller = Formotion::Controller.alloc.initWithForm(form)
107
+ self.navigationController.pushViewController(@controller, animated: true)
108
+ ```
109
+
110
+ ### Retreive
111
+
112
+ You have `form#submit`, `form#on_submit`, and `form#render` at your disposal. Here's an example:
113
+
114
+ ```ruby
115
+ class PeopleController < Formotion::Controller
116
+ def viewDidLoad
117
+ self.navigationItem.rightBarButtonItem = UIBarButtonItem.alloc.initWithBarButtonSystemItem(UIBarButtonSystemItemSave, target:self, action:'submit')
118
+ end
119
+
120
+ def submit
121
+ data = self.form.render
122
+
123
+ person.name = data[:name]
124
+ person.address = data[:address]
125
+ end
126
+ end
127
+ ```
128
+
129
+ Why would you use `form#on_submit`? In case you want to use `Formotion::RowType::SUBMIT`. Ex:
130
+
131
+ ```ruby
132
+ @form = Formotion::Form.new({
133
+ sections: [{
134
+ ...
135
+ }, {
136
+ rows: [{
137
+ title: "Save",
138
+ type: Formotion::RowType::SUBMIT
139
+ }]
140
+ }]
141
+ })
142
+
143
+ @form.on_submit do |form|
144
+ # do something with form.render
145
+ end
146
+ ```
147
+
148
+ `form#submit` just triggers `form#on_submit`.
149
+
150
+ ### Data Types
151
+
152
+ Formotion current supports static and editable text, switches, and checkboxes.
153
+
154
+ `Formotion::Form`, `Formotion::Section`, and `Formotion::Row` all respond to a `::PROPERTIES` attribute. These are settable as an attribute (ie `section.title = 'title'`) or in the initialization hash (ie `{sections: [{title: 'title', ...}]}`). Check the comments in the 3 main files (`form.rb`, `section.rb`, and `row.rb` for details on what these do).
155
+
156
+ See the [KitchenSink example](https://github.com/clayallsopp/formotion/tree/master/examples/KitchenSink) for a bunch of options in action.
157
+
158
+ ## Forking
159
+
160
+ Feel free to fork and submit pull requests! And if you end up using Formotion in your app, I'd love to hear about your experience.
161
+
162
+ ## Todo
163
+
164
+ - Not very efficient right now (creates a unique reuse idenitifer for each cell)
165
+ - More data entry types (dates, etc)
166
+ - More tests
167
+ - Styling/overriding the form for custom UITableViewDelegate/Data Source behaviors.
168
+ - Custom cell text field alignments
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ $:.unshift("/Library/RubyMotion/lib")
2
+ require 'motion/project'
3
+ require "bundler/gem_tasks"
4
+
5
+ $:.unshift("./lib/")
6
+ require './lib/formotion'
7
+
8
+ Motion::Project::App.setup do |app|
9
+ # Use `rake config' to see complete project settings.
10
+ app.name = 'Formotion'
11
+ end
@@ -0,0 +1,68 @@
1
+ class AppDelegate
2
+ def application(application, didFinishLaunchingWithOptions:launchOptions)
3
+ @window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds)
4
+
5
+ form = Formotion::Form.new({
6
+ sections: [{
7
+ title: "Register",
8
+ rows: [{
9
+ title: "Email",
10
+ key: :email,
11
+ placeholder: "me@mail.com",
12
+ type: :email,
13
+ auto_correction: :no,
14
+ auto_capitalization: :none
15
+ }, {
16
+ title: "Password",
17
+ key: :password,
18
+ placeholder: "required",
19
+ type: :string,
20
+ secure: true
21
+ }, {
22
+ title: "Password",
23
+ subtitle: "Confirmation",
24
+ key: :confirm,
25
+ placeholder: "required",
26
+ type: :string,
27
+ secure: true
28
+ }, {
29
+ title: "Switch",
30
+ key: :switch,
31
+ type: :switch,
32
+ }]
33
+ }, {
34
+ title: "Account Type",
35
+ key: :account_type,
36
+ select_one: true,
37
+ rows: [{
38
+ title: "Free",
39
+ key: :free,
40
+ type: :check,
41
+ }, {
42
+ title: "Basic",
43
+ value: true,
44
+ key: :basic,
45
+ type: :check,
46
+ }, {
47
+ title: "Pro",
48
+ key: :pro,
49
+ type: :check,
50
+ }]
51
+ }, {
52
+ rows: [{
53
+ title: "Sign Up",
54
+ type: :submit,
55
+ }]
56
+ }]
57
+ })
58
+
59
+ @view_controller = Formotion::FormController.alloc.initWithForm(form)
60
+ @view_controller.form.on_submit do |form|
61
+ p @view_controller.form.render
62
+ end
63
+
64
+ @window.rootViewController = @view_controller
65
+ @window.makeKeyAndVisible
66
+ true
67
+ end
68
+ end
@@ -0,0 +1,5 @@
1
+ .repl_history
2
+ build
3
+ resources/*.nib
4
+ resources/*.momd
5
+ resources/*.storyboardc
@@ -0,0 +1,5 @@
1
+ # KitchenSink
2
+
3
+ Check [app_delegate.rb](https://github.com/clayallsopp/formotion/blob/master/examples/KitchenSink/app/app_delegate.rb) for the code.
4
+
5
+ ![KitchenSink](http://i.imgur.com/fdCfV.png)
@@ -0,0 +1,9 @@
1
+ # -*- coding: utf-8 -*-
2
+ $:.unshift("/Library/RubyMotion/lib")
3
+ require 'motion/project'
4
+ require 'formotion'
5
+
6
+ Motion::Project::App.setup do |app|
7
+ # Use `rake config' to see complete project settings.
8
+ app.name = 'KitchenSink'
9
+ end
@@ -0,0 +1,101 @@
1
+ class AppDelegate
2
+ def application(application, didFinishLaunchingWithOptions:launchOptions)
3
+ @window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds)
4
+
5
+ @form = Formotion::Form.new({
6
+ title: "Kitchen Sink",
7
+ sections: [{
8
+ title: "Section Title",
9
+ rows: [{
10
+ title: "Static",
11
+ type: :static,
12
+ }, {
13
+ title: "Email",
14
+ key: :email,
15
+ placeholder: "me@mail.com",
16
+ type: :email,
17
+ auto_correction: :no,
18
+ auto_capitalization: :none
19
+ }, {
20
+ title: "Password",
21
+ key: :password,
22
+ placeholder: "required",
23
+ type: :string,
24
+ secure: true
25
+ }, {
26
+ title: "Phone",
27
+ key: :phone,
28
+ placeholder: "555-555-5555",
29
+ type: :phone,
30
+ auto_correction: :no,
31
+ auto_capitalization: :none
32
+ }, {
33
+ title: "Number",
34
+ key: :number,
35
+ placeholder: "12345",
36
+ type: :number,
37
+ auto_correction: :no,
38
+ auto_capitalization: :none
39
+ }, {
40
+ title: "Subtitle",
41
+ subtitle: "Confirmation",
42
+ key: :confirm,
43
+ placeholder: "required",
44
+ type: :string,
45
+ secure: true
46
+ }, {
47
+ title: "Remember?",
48
+ key: :remember,
49
+ type: :switch,
50
+ }]
51
+ }, {
52
+ title: "Select One",
53
+ key: :account_type,
54
+ select_one: true,
55
+ rows: [{
56
+ title: "A",
57
+ key: :a,
58
+ type: :check,
59
+ }, {
60
+ title: "B (value: true)",
61
+ value: true,
62
+ key: :b,
63
+ type: :check,
64
+ }, {
65
+ title: "C",
66
+ key: :c,
67
+ type: :check,
68
+ }]
69
+ }, {
70
+ rows: [{
71
+ title: "Submit",
72
+ type: :submit,
73
+ }]
74
+ }]
75
+ })
76
+
77
+ @navigation_controller = UINavigationController.alloc.init
78
+
79
+ @view_controller = Formotion::FormController.alloc.initWithForm(@form)
80
+ @view_controller.form.on_submit do |form|
81
+ alert = UIAlertView.alloc.init
82
+ alert.title = "@form.render"
83
+ alert.message = @form.render.to_s
84
+ alert.addButtonWithTitle("OK")
85
+ alert.show
86
+ end
87
+
88
+ @view_controller.navigationItem.rightBarButtonItem = UIBarButtonItem.alloc.initWithBarButtonSystemItem(UIBarButtonSystemItemSave, target:self, action:'submit')
89
+
90
+ @navigation_controller = UINavigationController.alloc.initWithRootViewController(@view_controller)
91
+
92
+ @window.rootViewController = @navigation_controller
93
+ @window.makeKeyAndVisible
94
+
95
+ true
96
+ end
97
+
98
+ def submit
99
+ @form.submit
100
+ end
101
+ end
@@ -0,0 +1,9 @@
1
+ describe "Application 'KitchenSink'" 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
data/lib/formotion.rb ADDED
@@ -0,0 +1,6 @@
1
+ require "formotion/version"
2
+ require 'bubble-wrap/core'
3
+
4
+ Dir.glob(File.join(File.dirname(__FILE__), 'formotion/**/*.rb')).each do |file|
5
+ BW.require file
6
+ end
@@ -0,0 +1,44 @@
1
+ module Formotion
2
+ class Base
3
+ def initialize(params = {})
4
+ params.each { |key, value|
5
+ if self.class.const_get(:PROPERTIES).member? key.to_sym
6
+ self.send((key.to_s + "=:").to_sym, value)
7
+ end
8
+ }
9
+ end
10
+
11
+ def to_hash
12
+ h = {}
13
+ self.class.const_get(:PROPERTIES).each { |prop|
14
+ val = self.send(prop)
15
+ h[prop] = val if val
16
+ }
17
+ h
18
+ end
19
+
20
+ # NSCoding + NSCopying
21
+ def encodeWithCoder(encoder)
22
+ self.class.const_get(:SERIALIZE_PROPERTIES).each {|prop|
23
+ encoder.encodeObject(self.send(prop), forKey: prop.to_s)
24
+ }
25
+ end
26
+
27
+ def initWithCoder(decoder)
28
+ self.init
29
+ self.class.const_get(:SERIALIZE_PROPERTIES).each {|prop|
30
+ value = decoder.decodeObjectForKey(prop.to_s)
31
+ self.send((prop.to_s + "=:").to_sym, value) if not value.nil?
32
+ }
33
+ self
34
+ end
35
+
36
+ def copyWithZone(zone)
37
+ copy = self.class.allocWithZone(zone).init
38
+ self.class.const_get(:SERIALIZE_PROPERTIES).each {|prop|
39
+ copy.send((prop.to_s + "=:").to_sym, self.send(prop))
40
+ }
41
+ copy
42
+ end
43
+ end
44
+ end