motion_bindable 0.0.6 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +1 -0
- data/Gemfile.lock +2 -0
- data/lib/motion_bindable/bindable.rb +8 -11
- data/lib/motion_bindable/strategy.rb +8 -5
- data/lib/motion_bindable/version.rb +1 -1
- data/lib/strategies/ui_text_field.rb +21 -12
- data/motion_bindable.gemspec +2 -0
- data/spec/bindable_spec.rb +51 -24
- data/spec/strategy_spec.rb +6 -0
- data/spec/ui_text_field_strategy_spec.rb +17 -8
- metadata +17 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 22f69831469c0aae59f8af6aa599f586276bd23c
|
4
|
+
data.tar.gz: 61181ef1e9254eb8378bc229eacc396851480867
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9581949f1fe049307b10aeb65e26ac65976662c1e5ad51e18c741ebe46340774c5d6296ac41a417ee17a7f2ef1bb957c5075eb590c586b674b1978957aac5283
|
7
|
+
data.tar.gz: 1f28ee9528235b42e9e4282e116e5fa54a279fad178f32977a6e34ffec35e4c78ff590faff1a763d9532170c4fc04ac483190d438abb0fbd35a46c5a51dba772
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -15,7 +15,9 @@ module MotionBindable
|
|
15
15
|
|
16
16
|
def bind_attributes(attrs, object = self)
|
17
17
|
attrs.each_pair do |k, v|
|
18
|
-
|
18
|
+
case v
|
19
|
+
when Hash then bind_attributes(v, object.send(k))
|
20
|
+
when Array then v.each { |v| bind strategy_for(v).new(object, k).bind(v) }
|
19
21
|
else bind strategy_for(v).new(object, k).bind(v)
|
20
22
|
end
|
21
23
|
end
|
@@ -27,25 +29,20 @@ module MotionBindable
|
|
27
29
|
self
|
28
30
|
end
|
29
31
|
|
32
|
+
def unbind_all
|
33
|
+
@bindings.each { |b| b.unbind }
|
34
|
+
@bindings = []
|
35
|
+
end
|
36
|
+
|
30
37
|
def refresh
|
31
38
|
@bindings.each { |b| b.refresh }
|
32
39
|
self
|
33
40
|
end
|
34
41
|
|
35
|
-
private
|
36
|
-
|
37
42
|
def strategy_for(reference)
|
38
43
|
Strategy.find_by_reference(reference)
|
39
44
|
end
|
40
45
|
|
41
|
-
def underscore(str)
|
42
|
-
str.gsub(/::/, '/')
|
43
|
-
.gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
|
44
|
-
.gsub(/([a-z\d])([A-Z])/,'\1_\2')
|
45
|
-
.tr("-", "_")
|
46
|
-
.downcase
|
47
|
-
end
|
48
|
-
|
49
46
|
end
|
50
47
|
|
51
48
|
end
|
@@ -20,9 +20,9 @@ module MotionBindable
|
|
20
20
|
attr_accessor :object
|
21
21
|
attr_accessor :bound
|
22
22
|
|
23
|
-
def initialize(object,
|
24
|
-
@
|
25
|
-
self.object =
|
23
|
+
def initialize(object, attr_name)
|
24
|
+
@attr_name = attr_name.to_sym
|
25
|
+
self.object = object
|
26
26
|
end
|
27
27
|
|
28
28
|
def bind(bound)
|
@@ -32,6 +32,9 @@ module MotionBindable
|
|
32
32
|
self
|
33
33
|
end
|
34
34
|
|
35
|
+
def unbind
|
36
|
+
end
|
37
|
+
|
35
38
|
# You can either choose to just override `#refresh` for objects that can't
|
36
39
|
# be bound with callbacks. Or override `#on_bind` for objects that can be
|
37
40
|
# bound with a callback.
|
@@ -41,11 +44,11 @@ module MotionBindable
|
|
41
44
|
end
|
42
45
|
|
43
46
|
def attribute
|
44
|
-
object.send(@
|
47
|
+
object.send(@attr_name)
|
45
48
|
end
|
46
49
|
|
47
50
|
def attribute=(value)
|
48
|
-
object.send(:"#{@
|
51
|
+
object.send(:"#{@attr_name.to_s}=", value)
|
49
52
|
end
|
50
53
|
|
51
54
|
end
|
@@ -2,28 +2,37 @@ module MotionBindable::Strategies
|
|
2
2
|
|
3
3
|
class UITextField < ::MotionBindable::Strategy
|
4
4
|
|
5
|
+
include BW::KVO
|
6
|
+
|
5
7
|
def on_bind
|
6
|
-
|
8
|
+
refresh
|
9
|
+
|
10
|
+
# Observe the bound object
|
7
11
|
NSNotificationCenter.defaultCenter.addObserver(
|
8
|
-
self,
|
9
|
-
|
10
|
-
name: UITextFieldTextDidChangeNotification,
|
11
|
-
object: bound
|
12
|
+
self, selector: :on_bound_change,
|
13
|
+
name: UITextFieldTextDidChangeNotification, object: bound
|
12
14
|
)
|
13
|
-
end
|
14
15
|
|
15
|
-
|
16
|
-
|
16
|
+
# Observe the attribute
|
17
|
+
observe(object, @attr_name) { |_, _| on_object_change }
|
17
18
|
end
|
18
19
|
|
19
|
-
|
20
|
+
def on_bound_change
|
21
|
+
self.attribute = bound.text
|
22
|
+
end
|
20
23
|
|
21
|
-
|
24
|
+
def on_object_change
|
25
|
+
@bound.text = attribute
|
26
|
+
end
|
22
27
|
|
23
|
-
def
|
24
|
-
|
28
|
+
def unbind
|
29
|
+
App.notification_center.unobserve(@bound_observer)
|
30
|
+
unobserve(object, @attr_name)
|
25
31
|
end
|
26
32
|
|
33
|
+
# Text field takes precedence
|
34
|
+
alias_method :refresh, :on_bound_change
|
35
|
+
|
27
36
|
end
|
28
37
|
|
29
38
|
end
|
data/motion_bindable.gemspec
CHANGED
data/spec/bindable_spec.rb
CHANGED
@@ -11,50 +11,77 @@ describe 'MotionBindable::Bindable' do
|
|
11
11
|
|
12
12
|
before do
|
13
13
|
@object = FakeBindable.new
|
14
|
-
@
|
14
|
+
@object.nested = FakeBindable.new
|
15
15
|
@object.stub!(:strategy_for) { |_| FakeStrategy }
|
16
|
-
@
|
16
|
+
@bound = Object.new
|
17
|
+
FakeStrategy.stub!(:new) { |_, _| @strategy }
|
17
18
|
end
|
18
19
|
|
19
20
|
describe '#bind_attributes' do
|
20
|
-
before do
|
21
|
-
@object.nested = FakeBindable.new
|
22
|
-
end
|
23
21
|
|
24
|
-
|
25
|
-
|
26
|
-
@
|
22
|
+
context 'un-nested' do
|
23
|
+
before do
|
24
|
+
@strategy = FakeStrategy.new(@object, :attribute)
|
27
25
|
end
|
28
26
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
27
|
+
it 'accepts an array of objects' do
|
28
|
+
@attributes = []
|
29
|
+
@strategy.stub!(:bind) { |attribute| @attributes << attribute }
|
30
|
+
@bound2 = Object.new
|
31
|
+
@object.bind_attributes(attribute: [@bound, @bound2])
|
32
|
+
@attributes.length.should.equal 2
|
33
|
+
end
|
35
34
|
|
36
|
-
|
35
|
+
it 'passes the strategy to bind' do
|
36
|
+
@called = false
|
37
|
+
@object.stub!(:bind) { |_| @called = true }
|
38
|
+
@object.bind_attributes({ attribute: @bound })
|
39
|
+
@called.should.equal true
|
40
|
+
end
|
37
41
|
end
|
38
42
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
43
|
+
context 'nested' do
|
44
|
+
before do
|
45
|
+
@strategy = FakeStrategy.new(@object.nested, :attribute)
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'accepts nested attributes' do
|
49
|
+
@strategy.stub!(:bind) { |bound| @bounded = bound }
|
50
|
+
@object.bind_attributes nested: { attribute: @bound }
|
51
|
+
@bounded.should.equal @bound
|
52
|
+
end
|
44
53
|
end
|
45
54
|
|
46
55
|
end
|
47
56
|
|
48
57
|
describe '#bind' do
|
58
|
+
before do
|
59
|
+
@strategy = FakeStrategy.new(@object, :attribute)
|
60
|
+
@strategy.bind(@bound)
|
61
|
+
end
|
62
|
+
|
49
63
|
it 'should be chainable' do
|
50
64
|
@object.bind(@strategy).should.equal @object
|
51
65
|
end
|
52
66
|
end
|
53
67
|
|
54
|
-
describe '#
|
55
|
-
|
56
|
-
@
|
57
|
-
@
|
68
|
+
describe '#unbind_all' do
|
69
|
+
before do
|
70
|
+
@strategy1 = FakeStrategy.new(@object, :attribute)
|
71
|
+
@strategy1.bind(@bound)
|
72
|
+
@object.bind(@strategy1)
|
73
|
+
|
74
|
+
@strategy2 = FakeStrategy.new(@object.nested, :attribute)
|
75
|
+
@strategy2.bind(@bound)
|
76
|
+
@object.bind(@strategy2)
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'should send unbind to all strategies' do
|
80
|
+
@called = 0
|
81
|
+
@strategy1.stub!(:unbind) { @called += 1 }
|
82
|
+
@strategy2.stub!(:unbind) { @called += 1 }
|
83
|
+
@object.unbind_all
|
84
|
+
@called.should.equal 2
|
58
85
|
end
|
59
86
|
end
|
60
87
|
|
data/spec/strategy_spec.rb
CHANGED
@@ -1,7 +1,10 @@
|
|
1
1
|
class FakeModel
|
2
2
|
include MotionBindable::Bindable
|
3
|
-
attr_accessor :attribute
|
4
3
|
attr_accessor :nested
|
4
|
+
attr_accessor :attribute
|
5
|
+
def attribute
|
6
|
+
@attribute
|
7
|
+
end
|
5
8
|
end
|
6
9
|
|
7
10
|
describe 'MotionBindable::Strategies::UITextField' do
|
@@ -24,14 +27,11 @@ describe 'MotionBindable::Strategies::UITextField' do
|
|
24
27
|
end
|
25
28
|
|
26
29
|
context 'text set and then bound' do
|
27
|
-
|
28
30
|
before do
|
29
31
|
@text_field.text = 'Just testing.'
|
30
32
|
@object.bind_attributes({
|
31
33
|
attribute: @text_field,
|
32
|
-
nested: {
|
33
|
-
attribute: @text_field
|
34
|
-
}
|
34
|
+
nested: { attribute: @text_field }
|
35
35
|
})
|
36
36
|
end
|
37
37
|
|
@@ -48,9 +48,10 @@ describe 'MotionBindable::Strategies::UITextField' do
|
|
48
48
|
before do
|
49
49
|
@text_field.text = 'Updated.'
|
50
50
|
NSNotificationCenter.defaultCenter.postNotificationName(
|
51
|
-
UITextFieldTextDidChangeNotification,
|
52
|
-
object: @text_field
|
51
|
+
UITextFieldTextDidChangeNotification, object: @text_field
|
53
52
|
)
|
53
|
+
# BW puts observer cb's into this queue
|
54
|
+
NSOperationQueue.mainQueue.waitUntilAllOperationsAreFinished
|
54
55
|
end
|
55
56
|
|
56
57
|
it 'should update the root attribute' do
|
@@ -60,12 +61,20 @@ describe 'MotionBindable::Strategies::UITextField' do
|
|
60
61
|
it 'should update the nested attribute' do
|
61
62
|
@object.nested.attribute.should.equal 'Updated.'
|
62
63
|
end
|
64
|
+
end
|
65
|
+
|
66
|
+
context 'bound attribute is updated' do
|
67
|
+
before do
|
68
|
+
@object.attribute = 'Reverse'
|
69
|
+
end
|
63
70
|
|
71
|
+
it 'should update the text field' do
|
72
|
+
@text_field.text.should.equal 'Reverse'
|
73
|
+
end
|
64
74
|
end
|
65
75
|
|
66
76
|
end
|
67
77
|
|
68
78
|
end
|
69
79
|
|
70
|
-
|
71
80
|
end
|
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: motion_bindable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nathan Kot
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-12-
|
12
|
-
dependencies:
|
11
|
+
date: 2013-12-21 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
|
13
27
|
description: A simple data binding library for RubyMotion.
|
14
28
|
email:
|
15
29
|
- nk@nathankot.com
|