formotion 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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