motion-kit-events 0.1.0

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