motion_bindable 0.1.1 → 0.2.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 +4 -4
- data/Gemfile +1 -2
- data/Gemfile.lock +2 -4
- data/README.md +8 -34
- data/lib/motion_bindable/bindable.rb +0 -5
- data/lib/motion_bindable/strategy.rb +64 -12
- data/lib/motion_bindable/version.rb +1 -1
- data/lib/strategies/proc.rb +4 -6
- data/lib/strategies/ui_text_field.rb +24 -18
- data/motion_bindable.gemspec +0 -2
- data/spec/bindable_spec.rb +8 -13
- data/spec/helpers/facon.rb +3 -0
- data/spec/proc_strategy_spec.rb +5 -4
- data/spec/strategy_spec.rb +14 -6
- metadata +4 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4a89a39aab018219f267f3eff32cde82ac521544
|
4
|
+
data.tar.gz: 1447ebff62c07174943bb0cb3bf30c48794b8267
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3bccfc36a62b72c7fe972263f465ea6b14985a215077f51018ffdc1d99e336097f5e0d21b126ccdd276d7862ce154d3c57c1aaeb43e21e86d4e461e27baa4b72
|
7
|
+
data.tar.gz: 256257f81086382161edc6b8462d4129946628cd0d801128be7b26f2802f92c098e926c4aa6e29ef36c65db67e8ba6e47c8a12ea4130dc0b615989777598a59f
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
GEM
|
2
2
|
remote: https://rubygems.org/
|
3
3
|
specs:
|
4
|
-
|
5
|
-
motion-stump (0.3.0)
|
4
|
+
motion-facon (0.5.0.1)
|
6
5
|
rake (10.1.0)
|
7
6
|
webstub (1.0.1)
|
8
7
|
|
@@ -10,7 +9,6 @@ PLATFORMS
|
|
10
9
|
ruby
|
11
10
|
|
12
11
|
DEPENDENCIES
|
13
|
-
|
14
|
-
motion-stump
|
12
|
+
motion-facon
|
15
13
|
rake
|
16
14
|
webstub
|
data/README.md
CHANGED
@@ -79,48 +79,22 @@ end
|
|
79
79
|
|
80
80
|
When `@name_field.text` or `@address_field.text` changes, so will your model!
|
81
81
|
|
82
|
-
### Refresh
|
83
|
-
|
84
|
-
To refresh the values on your bindable object use this:
|
85
|
-
|
86
|
-
```ruby
|
87
|
-
@bindable.refresh
|
88
|
-
```
|
89
|
-
|
90
|
-
Some strategies only make an update when a `#refresh` is called. See the
|
91
|
-
_Frequency_ column in the table below.
|
92
|
-
|
93
82
|
### Custom Strategies
|
94
83
|
|
95
|
-
The above example uses the `MotionBindable::Strategies::UITextField`.
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
```ruby
|
101
|
-
class CustomBindableStrategy < MotionBindable::Strategy
|
102
|
-
|
103
|
-
def on_bind
|
104
|
-
# This runs once when the object is bound.
|
105
|
-
end
|
106
|
-
|
107
|
-
def refresh
|
108
|
-
# This runs when the object is bound, and each time `@bindable.refresh`
|
109
|
-
# is called.
|
110
|
-
end
|
111
|
-
|
112
|
-
end
|
113
|
-
```
|
84
|
+
The above example uses the `MotionBindable::Strategies::UITextField`. which
|
85
|
+
comes with MotionBindable. Take a look in `lib/motion_bindable/strategies` for
|
86
|
+
the available defaults. You can implement your own strategies by extending
|
87
|
+
`MotionBindable::Strategy`. Please use the existing strategies as a guideline.
|
114
88
|
|
115
89
|
### Defaults Strategies
|
116
90
|
|
117
91
|
The following strategies come with motion-bindable and are setup when
|
118
92
|
`MotionBindable::Strategies.use` is called.
|
119
93
|
|
120
|
-
| Name | Object Candidates | Direction |
|
121
|
-
| ----------------------------------------- | ----------------- | --------- |
|
122
|
-
| `MotionBindable::Strategies::UITextField` | Any `UITextField` | Two-way |
|
123
|
-
| `MotionBindable::Strategies::Proc` | Any `Proc` | One-way |
|
94
|
+
| Name | Object Candidates | Direction |
|
95
|
+
| ----------------------------------------- | ----------------- | --------- |
|
96
|
+
| `MotionBindable::Strategies::UITextField` | Any `UITextField` | Two-way |
|
97
|
+
| `MotionBindable::Strategies::Proc` | Any `Proc` | One-way |
|
124
98
|
|
125
99
|
## Contributing
|
126
100
|
|
@@ -1,10 +1,9 @@
|
|
1
1
|
module MotionBindable
|
2
2
|
|
3
|
-
#
|
4
|
-
# Represents a binding strategy. Designed to be as flexible as possible.
|
5
|
-
#
|
6
3
|
class Strategy
|
7
4
|
|
5
|
+
WATCH_TICK = 0.2
|
6
|
+
|
8
7
|
@strategies_map = [{ class: Strategy, candidates: [Object] }]
|
9
8
|
|
10
9
|
def self.register_strategy(strategy, *objects)
|
@@ -25,24 +24,31 @@ module MotionBindable
|
|
25
24
|
self.object = object
|
26
25
|
end
|
27
26
|
|
27
|
+
public # Methods to override
|
28
|
+
|
29
|
+
# def start_observing_bound; end
|
30
|
+
# def start_observing_object; end
|
31
|
+
# def refresh_bound; end
|
32
|
+
# def on_object_change; end
|
33
|
+
# def on_bound_change; end
|
34
|
+
|
28
35
|
def bind(bound)
|
29
36
|
self.bound = bound
|
30
|
-
|
31
|
-
|
37
|
+
initial_state
|
38
|
+
start_listen
|
32
39
|
self
|
33
40
|
end
|
34
41
|
|
35
|
-
def
|
42
|
+
def refresh_object
|
43
|
+
attribute
|
36
44
|
end
|
37
45
|
|
38
|
-
|
39
|
-
|
40
|
-
# bound with a callback.
|
41
|
-
def refresh; end
|
42
|
-
def on_bind
|
43
|
-
refresh
|
46
|
+
def unbind
|
47
|
+
@watch_bound, @watch_object = nil
|
44
48
|
end
|
45
49
|
|
50
|
+
private # Methods to leave alone
|
51
|
+
|
46
52
|
def attribute
|
47
53
|
object.send(@attr_name)
|
48
54
|
end
|
@@ -51,6 +57,52 @@ module MotionBindable
|
|
51
57
|
object.send(:"#{@attr_name.to_s}=", value)
|
52
58
|
end
|
53
59
|
|
60
|
+
def initial_state
|
61
|
+
if attribute.nil?
|
62
|
+
if respond_to?(:refresh_bound) then on_bound_change(refresh_bound)
|
63
|
+
else on_bound_change
|
64
|
+
end if respond_to?(:on_bound_change)
|
65
|
+
else
|
66
|
+
if respond_to?(:refresh_object) then on_object_change(refresh_object)
|
67
|
+
else on_object_change(attribute)
|
68
|
+
end if respond_to?(:on_object_change)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def start_listen
|
73
|
+
if respond_to?(:start_observing_bound) then start_observing_bound
|
74
|
+
elsif respond_to?(:refresh_bound) && respond_to?(:on_bound_change)
|
75
|
+
watch_bound
|
76
|
+
end
|
77
|
+
|
78
|
+
if respond_to?(:start_observing_object) then start_observing_object
|
79
|
+
elsif respond_to?(:refresh_object) && respond_to?(:on_object_change)
|
80
|
+
watch_object
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def watch_bound
|
85
|
+
@watch_bound = dispatcher.async do
|
86
|
+
result = refresh_bound
|
87
|
+
on_bound_change(result) if result
|
88
|
+
dispatcher.after(WATCH_TICK) { watch_bound } unless @watch_bound
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def watch_object
|
93
|
+
@watch_object = dispatcher.async do
|
94
|
+
result = refresh_object
|
95
|
+
on_object_change(result) if result
|
96
|
+
dispatcher.after(WATCH_TICK) { watch_object } unless @watch_object
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def dispatcher
|
101
|
+
@dispatcher ||= begin
|
102
|
+
Dispatch::Queue.concurrent 'motion.bindable'
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
54
106
|
end
|
55
107
|
|
56
108
|
end
|
data/lib/strategies/proc.rb
CHANGED
@@ -2,14 +2,12 @@ module MotionBindable::Strategies
|
|
2
2
|
|
3
3
|
class Proc < ::MotionBindable::Strategy
|
4
4
|
|
5
|
-
def
|
6
|
-
|
5
|
+
def refresh_bound
|
6
|
+
bound.call
|
7
7
|
end
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
def update_attribute
|
12
|
-
self.attribute = bound.call
|
9
|
+
def on_bound_change(new = nil)
|
10
|
+
self.attribute = new || bound.call
|
13
11
|
end
|
14
12
|
|
15
13
|
end
|
@@ -2,38 +2,44 @@ module MotionBindable::Strategies
|
|
2
2
|
|
3
3
|
class UITextField < ::MotionBindable::Strategy
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
NSNotificationCenter.defaultCenter.addObserver(
|
12
|
-
self,
|
13
|
-
selector: :on_bound_change,
|
14
|
-
name: UITextFieldTextDidChangeNotification,
|
15
|
-
object: bound
|
5
|
+
def start_observing_bound
|
6
|
+
NSNotificationCenter.defaultCenter.addObserverForName(
|
7
|
+
UITextFieldTextDidChangeNotification,
|
8
|
+
object: bound,
|
9
|
+
queue: nil,
|
10
|
+
usingBlock: proc { |_| on_bound_change }
|
16
11
|
)
|
12
|
+
end
|
17
13
|
|
14
|
+
def start_observing_object
|
18
15
|
# Observe the attribute
|
19
|
-
|
16
|
+
object.addObserver(
|
17
|
+
self,
|
18
|
+
forKeyPath: @attr_name,
|
19
|
+
options: NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld,
|
20
|
+
context: nil
|
21
|
+
)
|
20
22
|
end
|
21
23
|
|
22
|
-
def on_bound_change
|
23
|
-
self.attribute =
|
24
|
+
def on_bound_change(new = nil)
|
25
|
+
self.attribute = new || bound.text
|
24
26
|
end
|
25
27
|
|
26
|
-
def on_object_change
|
27
|
-
@bound.text = attribute
|
28
|
+
def on_object_change(new = nil)
|
29
|
+
@bound.text = new || attribute
|
28
30
|
end
|
29
31
|
|
30
32
|
def unbind
|
31
33
|
App.notification_center.unobserve(@bound_observer)
|
32
34
|
unobserve(object, @attr_name)
|
35
|
+
super
|
33
36
|
end
|
34
37
|
|
35
|
-
#
|
36
|
-
|
38
|
+
# NSKeyValueObserving Protocol
|
39
|
+
|
40
|
+
def observeValueForKeyPath(_, ofObject: _, change: _, context: _)
|
41
|
+
on_object_change
|
42
|
+
end
|
37
43
|
|
38
44
|
end
|
39
45
|
|
data/motion_bindable.gemspec
CHANGED
data/spec/bindable_spec.rb
CHANGED
@@ -12,9 +12,10 @@ describe 'MotionBindable::Bindable' do
|
|
12
12
|
before do
|
13
13
|
@object = FakeBindable.new
|
14
14
|
@object.nested = FakeBindable.new
|
15
|
-
@object.stub!(:strategy_for) { |_| FakeStrategy }
|
16
15
|
@bound = Object.new
|
17
|
-
|
16
|
+
@object.should.receive(:strategy_for) do
|
17
|
+
mock('FakeStrategy', new: @strategy)
|
18
|
+
end.times(:any)
|
18
19
|
end
|
19
20
|
|
20
21
|
describe '#bind_attributes' do
|
@@ -25,18 +26,14 @@ describe 'MotionBindable::Bindable' do
|
|
25
26
|
end
|
26
27
|
|
27
28
|
it 'accepts an array of objects' do
|
28
|
-
@attributes = []
|
29
|
-
@strategy.stub!(:bind) { |attribute| @attributes << attribute }
|
30
29
|
@bound2 = Object.new
|
30
|
+
@strategy.should.receive(:bind).times(2)
|
31
31
|
@object.bind_attributes(attribute: [@bound, @bound2])
|
32
|
-
@attributes.length.should.equal 2
|
33
32
|
end
|
34
33
|
|
35
34
|
it 'passes the strategy to bind' do
|
36
|
-
@
|
37
|
-
@object.stub!(:bind) { |_| @called = true }
|
35
|
+
@strategy.should.receive(:bind).once
|
38
36
|
@object.bind_attributes({ attribute: @bound })
|
39
|
-
@called.should.equal true
|
40
37
|
end
|
41
38
|
end
|
42
39
|
|
@@ -46,7 +43,7 @@ describe 'MotionBindable::Bindable' do
|
|
46
43
|
end
|
47
44
|
|
48
45
|
it 'accepts nested attributes' do
|
49
|
-
@strategy.
|
46
|
+
@strategy.should.receive(:bind) { |bound| @bounded = bound }.times(:any)
|
50
47
|
@object.bind_attributes nested: { attribute: @bound }
|
51
48
|
@bounded.should.equal @bound
|
52
49
|
end
|
@@ -77,11 +74,9 @@ describe 'MotionBindable::Bindable' do
|
|
77
74
|
end
|
78
75
|
|
79
76
|
it 'should send unbind to all strategies' do
|
80
|
-
@
|
81
|
-
@
|
82
|
-
@strategy2.stub!(:unbind) { @called += 1 }
|
77
|
+
@strategy1.should.receive(:unbind).once
|
78
|
+
@strategy2.should.receive(:unbind).once
|
83
79
|
@object.unbind_all
|
84
|
-
@called.should.equal 2
|
85
80
|
end
|
86
81
|
end
|
87
82
|
|
data/spec/proc_strategy_spec.rb
CHANGED
@@ -42,10 +42,11 @@ describe 'MotionBindable::Strategies::Proc' do
|
|
42
42
|
@object.nested.attribute.should.equal 'Testing.'
|
43
43
|
end
|
44
44
|
|
45
|
-
it '
|
46
|
-
@bound.attribute = '
|
47
|
-
|
48
|
-
|
45
|
+
it 'attribute is updated when the bound object is updated' do
|
46
|
+
@bound.attribute = 'updated'
|
47
|
+
wait(0.5) do
|
48
|
+
@object.attribute.should.equal 'updated'
|
49
|
+
end
|
49
50
|
end
|
50
51
|
|
51
52
|
end
|
data/spec/strategy_spec.rb
CHANGED
@@ -32,6 +32,8 @@ describe 'MotionBindable::Strategy' do
|
|
32
32
|
@object = ObjectOne.new
|
33
33
|
@bound = Object.new
|
34
34
|
@strategy = Strategy.new(@object, :attribute)
|
35
|
+
@strategy.stub!(:watch_bound)
|
36
|
+
@strategy.stub!(:watch_object)
|
35
37
|
end
|
36
38
|
|
37
39
|
describe '#bind' do
|
@@ -47,18 +49,24 @@ describe 'MotionBindable::Strategy' do
|
|
47
49
|
it 'should return self' do
|
48
50
|
@strategy.bind(@bound).should.equal @strategy
|
49
51
|
end
|
50
|
-
end
|
51
52
|
|
52
|
-
|
53
|
-
|
54
|
-
@strategy.
|
53
|
+
it 'should call on_object_change if the attribute is not nil' do
|
54
|
+
@object.attribute = 'Test'
|
55
|
+
@strategy.should.receive(:on_object_change).and_return(true)
|
56
|
+
@strategy.bind(@bound)
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'should call on_bound_change if the attribute is nil' do
|
60
|
+
@object.attribute = nil
|
61
|
+
@strategy.should.receive(:on_bound_change).and_return(true)
|
62
|
+
@strategy.bind(@bound)
|
55
63
|
end
|
56
64
|
end
|
57
65
|
|
58
66
|
describe '#attribute=' do
|
59
67
|
it 'should be a proxy to set the attribute on the bound object' do
|
60
|
-
@strategy.attribute
|
61
|
-
@object.attribute.should.equal 'test'
|
68
|
+
@strategy.send(:attribute=, 'test')
|
69
|
+
@object.send(:attribute).should.equal 'test'
|
62
70
|
end
|
63
71
|
end
|
64
72
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: motion_bindable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nathan Kot
|
@@ -9,21 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
date: 2013-12-23 00:00:00.000000000 Z
|
12
|
-
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: bubble-wrap
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - ~>
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: 1.4.0
|
20
|
-
type: :runtime
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - ~>
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: 1.4.0
|
12
|
+
dependencies: []
|
27
13
|
description: A simple data binding library for RubyMotion.
|
28
14
|
email:
|
29
15
|
- nk@nathankot.com
|
@@ -50,6 +36,7 @@ files:
|
|
50
36
|
- motion_bindable.gemspec
|
51
37
|
- resources/Default-568h@2x.png
|
52
38
|
- spec/bindable_spec.rb
|
39
|
+
- spec/helpers/facon.rb
|
53
40
|
- spec/proc_strategy_spec.rb
|
54
41
|
- spec/strategy_spec.rb
|
55
42
|
- spec/ui_text_field_strategy_spec.rb
|
@@ -79,6 +66,7 @@ specification_version: 4
|
|
79
66
|
summary: Inspired by RivetsJS
|
80
67
|
test_files:
|
81
68
|
- spec/bindable_spec.rb
|
69
|
+
- spec/helpers/facon.rb
|
82
70
|
- spec/proc_strategy_spec.rb
|
83
71
|
- spec/strategy_spec.rb
|
84
72
|
- spec/ui_text_field_strategy_spec.rb
|