motion-kit-events 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 1dff0c162d7aa79745f3688409745e9a096b5af0
4
+ data.tar.gz: 9e8bc6302c39eb7c3fa2ee82a2f2e3c42221fda7
5
+ SHA512:
6
+ metadata.gz: 4bfcebe914349985e9d232779761fa8d579058daf73f76d1912a54d6ee1e5a8174524cc9d6bea87dd3a90c6f1ffb2c679530b5d86d63dc45d7cd67b1be32bfda
7
+ data.tar.gz: 8ce463671100d3867c42658b3f10def5192a461eb6df7cb9e3873ffcd863273b157eee5597ab4d741b76ff56b6e811e054b5756cca37102406a7e370fe0b6ee7
data/README.md ADDED
@@ -0,0 +1,119 @@
1
+ MotionKit::Events
2
+ --------
3
+
4
+ In an effort to encourage even MORE separation of concerns in your Controllers
5
+ and Layouts, the `motion-kit-events` gem provides a very way to emit generic
6
+ events from your layout that you can respond to in your controller.
7
+
8
+ ## LoginController
9
+
10
+ An example of a UIViewController using MotionKit::Events:
11
+
12
+ ```ruby
13
+ class LoginController < UIViewController
14
+
15
+ def loadView
16
+ @layout = LoginLayout.new
17
+ self.view = @layout.view
18
+
19
+ @layout.on :login do |username, password|
20
+ @layout.pause_ui
21
+ # send login info to the API (I would recommend using a separate class
22
+ # to handle the API conversation, e.g. a LoginStorage class).
23
+ storage.login(username, password) do |user, errors|
24
+ handle_login(user, errors)
25
+ end
26
+ end
27
+ end
28
+
29
+ def storage
30
+ @storage ||= LoginStorage.new
31
+ end
32
+
33
+ def handle_login(user, errors)
34
+ # ...
35
+ @layout.resume_ui
36
+ end
37
+
38
+ end
39
+ ```
40
+
41
+ Before we go on to show the `LoginLayout`, consider for a second how *easy* it
42
+ would be to write specs for a controller like this. We don't need to test the
43
+ UI (that will be done in isolation, using the `LoginLayout`) and our controller
44
+ doesn't directly send any *requests*, so we can assign it (in our specs) a
45
+ `TestLoginLayout` class as the storage, which can imitate the behaviors we are
46
+ interested in. We should also use a fake layout class in there, too.
47
+
48
+ TL;DR: we can test just the behavior of the controller. When it receives a
49
+ `:login` event, it should send a `login` request to its storage, and handle
50
+ `user` or `errors`.
51
+
52
+
53
+ ## LoginLayout
54
+
55
+ We send the `:login` event along with the username and password when the user
56
+ presses the login button or presses "Return" from the password field. This is
57
+ all code that would normally be in the UIViewController. The tight coupling of
58
+ the UIViewController and the UI is very common in iOS apps, but there is much to
59
+ be gained be decoupling these roles. The Controller can focus on the *movement*
60
+ of the user.
61
+
62
+ The user starts out in a "logging in" state. Until they submit credentials, the
63
+ controller need not be interested in what the UI is doing to accomodate this
64
+ procedure. It just cares about when the user is *done*, and it pauses the UI.
65
+
66
+ When the login attempt is complete, the controller tells the UI what state to go
67
+ in next, either resuming the UI if an error occurred, or just dimissing the
68
+ controller and passing along the successful login info.
69
+
70
+ If you look at the code provided by MotionKit::Events you'll be happy to see
71
+ that it is a *tiny* little gem. But using it to decouple your UI from your
72
+ controller can provide a huge long term benefit in terms of keeping your code
73
+ maintainable!
74
+
75
+ ```ruby
76
+ class LoginLayout < MK::Layout
77
+
78
+ def layout
79
+ add UITextField, :username_field do
80
+ delegate self
81
+ end
82
+
83
+ add UITextField, :password_field do
84
+ delegate self
85
+ end
86
+
87
+ add UIButton, :login_button do
88
+ on :touch do
89
+ trigger_login
90
+ end
91
+ end
92
+ end
93
+
94
+ def trigger_login
95
+ # send the username and password to our controller
96
+ trigger :login, get(:username_field).text.to_s, get(:password_field).text.to_s
97
+ end
98
+
99
+ def textFieldShouldReturn(field)
100
+ if field == get(:password_field)
101
+ trigger_login
102
+ else
103
+ get(:password_field).becomeFirstResponder
104
+ end
105
+ end
106
+
107
+ end
108
+ ```
109
+
110
+ The sample app (most of the code is in [app/ios/login/][login]) includes a working
111
+ version of this example.
112
+
113
+ [login]: https://github.com/rubymotion/motion-kit-events/tree/master/app/ios/login/
114
+
115
+ ###### Note
116
+
117
+ The example and specs all require SugarCube to run; this is just because I
118
+ wanted to have the specs make sure that the `on` method (used in so many gems)
119
+ behaves the way you would expect it to.
@@ -0,0 +1,45 @@
1
+ # @requires MotionKit::BaseLayout
2
+ module MotionKit
3
+ class BaseLayout
4
+
5
+ def motion_kit_event_handlers
6
+ @motion_kit_event_handlers ||= {}
7
+ end
8
+
9
+ def on(*args, &handler)
10
+ if @context || @assign_root
11
+ apply_with_target(:on, *args, &handler)
12
+ else
13
+ event = args.first
14
+ unless event
15
+ raise ArgumentError.new('`event` is a required argument to Layout#on')
16
+ end
17
+ motion_kit_event_handlers[event] ||= []
18
+ motion_kit_event_handlers[event] << handler.weak!
19
+
20
+ self
21
+ end
22
+ end
23
+
24
+ def trigger(*args, &handler)
25
+ if @context
26
+ apply(:trigger, *args, &handler)
27
+ else
28
+ event = args.first
29
+ unless event
30
+ raise ArgumentError.new('`event` is a required argument to Layout#on')
31
+ end
32
+
33
+ if motion_kit_event_handlers[event]
34
+ motion_kit_event_handlers[event].each do |handler|
35
+ handler.call(args[1..-1])
36
+ end
37
+ end
38
+
39
+ self
40
+ end
41
+ end
42
+
43
+ end
44
+
45
+ end
@@ -0,0 +1,5 @@
1
+ module MotionKit
2
+ module Events
3
+ VERSION = '0.1.0'
4
+ end
5
+ end
@@ -0,0 +1,23 @@
1
+ unless defined?(Motion::Project::Config)
2
+ raise "The MotionKit::Events gem must be required within a RubyMotion project Rakefile."
3
+ end
4
+
5
+
6
+ require 'motion-kit'
7
+ require 'dbt'
8
+
9
+
10
+ Motion::Project::App.setup do |app|
11
+ lib = File.join(File.dirname(__FILE__), 'motion-kit-events')
12
+
13
+ # scans app.files until it finds app/ (the default)
14
+ # if found, it inserts just before those files, otherwise it will insert to
15
+ # the end of the list
16
+ insert_point = app.files.find_index { |file| file =~ /^(?:\.\/)?app\// } || 0
17
+
18
+ Dir.glob(File.join(lib, '**/*.rb')).reverse.each do |file|
19
+ app.files.insert(insert_point, file)
20
+ end
21
+
22
+ DBT.analyze(app)
23
+ end
@@ -0,0 +1,16 @@
1
+ describe 'MotionKit::Events' do
2
+ tests EventsController
3
+
4
+ it 'should respond to the layout trigger' do
5
+ controller.success.should.not == true
6
+ controller.layout.trigger(:test)
7
+ controller.success.should == true
8
+ end
9
+
10
+ it 'should respond to the test button' do
11
+ controller.success.should.not == true
12
+ controller.test_button.trigger(:touch)
13
+ controller.success.should == true
14
+ end
15
+
16
+ end
metadata ADDED
@@ -0,0 +1,79 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: motion-kit-events
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Colin T.A. Gray
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-05-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: motion-kit
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: dbt
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description: |
42
+ Use +on+ and +trigger+ to send generic events from the layout to the controller.
43
+ email:
44
+ - colinta@gmail.com
45
+ executables: []
46
+ extensions: []
47
+ extra_rdoc_files: []
48
+ files:
49
+ - lib/motion-kit-events/layout.rb
50
+ - lib/motion-kit-events/version.rb
51
+ - lib/motion-kit-events.rb
52
+ - README.md
53
+ - spec/ios/events_spec.rb
54
+ homepage: https://github.com/rubymotion/motion-kit-events
55
+ licenses:
56
+ - BSD
57
+ metadata: {}
58
+ post_install_message:
59
+ rdoc_options: []
60
+ require_paths:
61
+ - lib
62
+ required_ruby_version: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - '>='
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ required_rubygems_version: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - '>='
70
+ - !ruby/object:Gem::Version
71
+ version: '0'
72
+ requirements: []
73
+ rubyforge_project:
74
+ rubygems_version: 2.0.3
75
+ signing_key:
76
+ specification_version: 4
77
+ summary: Adds simple event methods to MotionKit::Layout classes.
78
+ test_files:
79
+ - spec/ios/events_spec.rb