motion_bindable 0.2.5 → 0.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 52e8be3ba802d2d3a4a0f1bdddbac59a32eea455
4
- data.tar.gz: 6c14152e5fee498b339fe107141b0cf86aa778a3
3
+ metadata.gz: 28fbdbec6c2d3b77ecad6a2ca7c24b8ea3f87934
4
+ data.tar.gz: 30d6ce55f370a6a6c39b3cc40ba23d8f2d17480c
5
5
  SHA512:
6
- metadata.gz: 089f88fb16423348824e9e04d3f5384e7f1cf9ced50c82779cd17cb3d3433783de8a8ec060abe40b4524375e6df3e84aff77da6b2d01db62393ae4f8ddccad89
7
- data.tar.gz: ea79259bdcc11b2d20b75f53a4a11b95733b1691b1aab7ab2e77fb18141fd1b7b576655094befe3c3550f4f1faf6463467676f6270bb2f95e65396b042a3773c
6
+ metadata.gz: 4acbbb6b7e780bf08f12cd655513dc289990b81ac7056e492dca53e16175621b0dcbf67510e3ff8f1d8dc465d0d901c0aa85d1dd81f66b79820b308bcab86bd9
7
+ data.tar.gz: f5bb69ca4a6aebbaad375b4cb2f2ef5ddd81eb3119887dbb7986846322c863e17318c75aa3564a9e86389e971c2602127885d5ca920679df8d5bba4c3ce47f4d
data/Gemfile CHANGED
@@ -2,5 +2,6 @@ source 'https://rubygems.org'
2
2
 
3
3
  gem 'rake'
4
4
  # Add your dependencies here:
5
+ gem 'motion_model'
5
6
  gem 'webstub'
6
7
  gem 'motion-facon'
@@ -1,7 +1,14 @@
1
1
  GEM
2
2
  remote: https://rubygems.org/
3
3
  specs:
4
+ bubble-wrap (1.5.0)
4
5
  motion-facon (0.5.0.1)
6
+ motion-require (0.2.0)
7
+ motion-support (0.2.6)
8
+ motion-require (>= 0.0.6)
9
+ motion_model (0.5.0)
10
+ bubble-wrap (>= 1.3.0)
11
+ motion-support (>= 0.1.0)
5
12
  rake (10.1.0)
6
13
  webstub (1.0.1)
7
14
 
@@ -10,5 +17,6 @@ PLATFORMS
10
17
 
11
18
  DEPENDENCIES
12
19
  motion-facon
20
+ motion_model
13
21
  rake
14
22
  webstub
data/README.md CHANGED
@@ -5,6 +5,11 @@
5
5
 
6
6
  A simple two-way data binding library for RubyMotion.
7
7
 
8
+ ## NOTICE
9
+
10
+ Version `0.3.0` introduces breaking changes and deprecations. If you're upgrading, please check the [release
11
+ notes](nathankot/motion-bindable/releases/tag/v0.3.0).
12
+
8
13
  ## Installation
9
14
 
10
15
  Add this line to your application's Gemfile:
@@ -24,7 +29,7 @@ this to your `app_delegate.rb`:
24
29
 
25
30
  ``` ruby
26
31
  def application(application, didFinishLaunchingWithOptions: launch_options)
27
- MotionBindable::Strategies.use
32
+ MotionBindable::Strategies.apply
28
33
  true
29
34
  end
30
35
  ```
@@ -75,6 +80,9 @@ class ItemListViewController
75
80
  view.addSubview @address_field
76
81
 
77
82
  @item = Item.new
83
+ end
84
+
85
+ def viewWillAppear(animated)
78
86
  @item.bind_attributes({
79
87
  name: @name_field,
80
88
  location: {
@@ -82,6 +90,11 @@ class ItemListViewController
82
90
  }
83
91
  })
84
92
  end
93
+
94
+ # Recommended: Clean everything up when the view leaves
95
+ def viewWillDisappear(animated)
96
+ @item.unbind_all
97
+ end
85
98
  end
86
99
  ```
87
100
 
@@ -101,9 +114,9 @@ The following strategies come with motion-bindable and are setup when
101
114
 
102
115
  | Name | Object Candidates | Direction |
103
116
  | ----------------------------------------- | ----------------- | ------------------- |
104
- | `MotionBindable::Strategies::UITextField` | Any `UITextField` | Bound >-< Attribute |
105
- | `MotionBindable::Strategies::Proc` | Any `Proc` | Bound >-- Attribute |
106
- | `MotionBindable::Strategies::UILabel` | Any `UILabel` | Bound --< Attribute |
117
+ | `MotionBindable::Strategies::UITextField` | Any `UITextField` | Bound <-> Attribute |
118
+ | `MotionBindable::Strategies::Proc` | Any `Proc` | Bound --> Attribute |
119
+ | `MotionBindable::Strategies::UILabel` | Any `UILabel` | Bound <-- Attribute |
107
120
 
108
121
  ## Contributing
109
122
 
@@ -11,8 +11,11 @@ module MotionBindable
11
11
  def bind_attributes(attrs, object = self)
12
12
  attrs.each_pair do |k, v|
13
13
  case v
14
+ # Recurse if another hash
14
15
  when Hash then bind_attributes(v, object.send(k))
16
+ # Allow binding multiple bound if an array
15
17
  when Array then v.each { |v| bind strategy_for(v).new(object, k).bind(v) }
18
+ # Otherwise bind
16
19
  else bind strategy_for(v).new(object, k).bind(v)
17
20
  end
18
21
  end
@@ -1,9 +1,5 @@
1
1
  module MotionBindable
2
-
3
2
  class Strategy
4
-
5
- WATCH_TICK = 0.2
6
-
7
3
  @strategies_map = [{ class: Strategy, candidates: [Object] }]
8
4
 
9
5
  def self.register_strategy(strategy, *objects)
@@ -25,14 +21,20 @@ module MotionBindable
25
21
  self.object = object
26
22
  end
27
23
 
28
- public # Methods to override
24
+ public # (Optional) Methods to override
29
25
 
26
+ # def start_observing; end
30
27
  # def start_observing_bound; end
31
28
  # def start_observing_object; end
32
- # def refresh_bound; end
33
29
  # def on_object_change; end
34
30
  # def on_bound_change; end
35
31
 
32
+ def bound_value
33
+ end
34
+
35
+ def object_value
36
+ end
37
+
36
38
  def bind(bound)
37
39
  self.bound = bound
38
40
  initial_state
@@ -41,9 +43,12 @@ module MotionBindable
41
43
  end
42
44
 
43
45
  def unbind
44
- @watching = false
45
46
  end
46
47
 
48
+ # Deprecation support purposes
49
+ alias_method :refresh_bound, :bound_value
50
+ alias_method :refresh_object, :object_value
51
+
47
52
  private # Methods to leave alone
48
53
 
49
54
  def attribute
@@ -55,51 +60,18 @@ module MotionBindable
55
60
  end
56
61
 
57
62
  def initial_state
63
+ # We try to find an existing value and fill it up
58
64
  if attribute.nil? && respond_to?(:on_bound_change)
59
- if respond_to?(:refresh_bound) then on_bound_change(refresh_bound)
60
- else on_bound_change
61
- end
65
+ on_bound_change(bound_value)
62
66
  elsif respond_to?(:on_object_change)
63
- if respond_to?(:refresh_object) then on_object_change(refresh_object)
64
- else on_object_change(attribute)
65
- end
67
+ on_object_change(object_value)
66
68
  end
67
69
  end
68
70
 
69
71
  def start_listen
70
- sides = []
71
-
72
- if respond_to?(:start_observing_bound) then start_observing_bound
73
- elsif respond_to?(:refresh_bound) && respond_to?(:on_bound_change)
74
- sides << :bound
75
- end
76
- if respond_to?(:start_observing_object) then start_observing_object
77
- elsif respond_to?(:refresh_object) && respond_to?(:on_object_change)
78
- sides << :object
79
- end
80
-
81
- @watching = true
82
- watch(sides)
83
- end
84
-
85
- def watch(sides)
86
- dispatcher.async do
87
- if @watching
88
- bound_result = refresh_bound if sides.include?(:bound)
89
- object_result = refresh_object if sides.include?(:object)
90
- on_bound_change(bound_result) if bound_result
91
- on_object_change(object_result) if object_result
92
- dispatcher.after(WATCH_TICK) { watch(sides) }
93
- end
94
- end
72
+ start_observing if respond_to?(:start_observing)
73
+ start_observing_bound if respond_to?(:start_observing_bound)
74
+ start_observing_object if respond_to?(:start_observing_object)
95
75
  end
96
-
97
- def dispatcher
98
- @dispatcher ||= begin
99
- Dispatch::Queue.concurrent 'motion.bindable'
100
- end
101
- end
102
-
103
76
  end
104
-
105
77
  end
@@ -0,0 +1,72 @@
1
+ module MotionBindable::StrategyHelpers
2
+ # A generic method for observing an object's attribute that is
3
+ # being bound to.
4
+ #
5
+ # Where strategies deal with observing a large variety of possible bound
6
+ # objects, this method will only have to worry about a small set of possible
7
+ # objects being binded to, KVO should actually cover most cases.
8
+ def observe_object(&block)
9
+ @_observe_object_cb = block
10
+
11
+ if defined?(MotionModel::Model) && object.is_a?(MotionModel::Model)
12
+ @_observe_object_mode = :motion_model
13
+ start_observing_motion_model
14
+ else
15
+ @_observe_object_mode = :kvo
16
+ start_observing_kvo
17
+ end
18
+ end
19
+
20
+ def stop_observe_object
21
+ case @_observe_object_mode
22
+ when :kvo then stop_observing_kvo
23
+ end
24
+
25
+ @_observe_object_mode,
26
+ @_observe_object_block = nil
27
+ end
28
+
29
+ # NSKeyValueObserving Protocol
30
+
31
+ def observeValueForKeyPath(_, ofObject: _, change: change, context: _)
32
+ @_observe_object_cb.call(change[:old], change[:new])
33
+ end
34
+
35
+ private
36
+
37
+ def start_observing_motion_model
38
+ @_observe_object_old_value = object.send(attr_name)
39
+ NSNotificationCenter.defaultCenter.addObserverForName(
40
+ 'MotionModelDataDidChangeNotification',
41
+ object: object,
42
+ queue: nil,
43
+ usingBlock: proc do |notification|
44
+ new = notification.object.send(attr_name)
45
+ @_observe_object_cb.call(
46
+ @_observe_object_old_value,
47
+ new
48
+ ) if @_observe_object_cb
49
+ @_observe_object_old_value = new
50
+ end
51
+ )
52
+ end
53
+
54
+ def start_observing_kvo
55
+ object.addObserver(
56
+ self,
57
+ forKeyPath: attr_name,
58
+ options: NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld,
59
+ context: nil
60
+ )
61
+ end
62
+
63
+ def stop_observing_kvo
64
+ object.removeObserver(
65
+ self,
66
+ forKeyPath: attr_name
67
+ )
68
+ rescue
69
+ # See: http://nshipster.com/key-value-observing/
70
+ true
71
+ end
72
+ end
@@ -1,3 +1,3 @@
1
1
  module MotionBindable
2
- VERSION = "0.2.5"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -1,12 +1,12 @@
1
1
  module MotionBindable::Strategies
2
-
3
2
  class Proc < ::MotionBindable::Strategy
3
+ WATCH_TICK = 0.2
4
4
 
5
- def refresh_bound
5
+ def bound_value
6
6
  bound.call
7
7
  end
8
8
 
9
- def refresh_object
9
+ def object_value
10
10
  attribute
11
11
  end
12
12
 
@@ -19,6 +19,28 @@ module MotionBindable::Strategies
19
19
  super
20
20
  end
21
21
 
22
- end
22
+ def start_observing
23
+ @watching = true
24
+ watch
25
+ end
26
+
27
+ private
23
28
 
29
+ def watch
30
+ dispatcher.async do
31
+ if @watching
32
+ new = bound_value
33
+ on_bound_change(new) if new != @old_bound_value
34
+ @old_bound_value = nil
35
+ dispatcher.after(WATCH_TICK) { watch }
36
+ end
37
+ end
38
+ end
39
+
40
+ def dispatcher
41
+ @dispatcher ||= begin
42
+ Dispatch::Queue.concurrent 'org.motion.bindable'
43
+ end
44
+ end
45
+ end
24
46
  end
@@ -1,15 +1,9 @@
1
1
  module MotionBindable::Strategies
2
-
3
2
  class UILabel < ::MotionBindable::Strategy
3
+ include MotionBindable::StrategyHelpers
4
4
 
5
5
  def start_observing_object
6
- # Observe the attribute
7
- object.addObserver(
8
- self,
9
- forKeyPath: attr_name,
10
- options: NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld,
11
- context: nil
12
- )
6
+ observe_object { |_, new| on_object_change(new) }
13
7
  end
14
8
 
15
9
  def on_object_change(new = nil)
@@ -22,10 +16,8 @@ module MotionBindable::Strategies
22
16
  end
23
17
 
24
18
  def unbind
25
- object.removeObserver(self, forKeyPath: attr_name)
19
+ stop_observe_object
26
20
  super
27
21
  end
28
-
29
22
  end
30
-
31
23
  end
@@ -1,6 +1,6 @@
1
1
  module MotionBindable::Strategies
2
-
3
2
  class UITextField < ::MotionBindable::Strategy
3
+ include MotionBindable::StrategyHelpers
4
4
 
5
5
  def start_observing_bound
6
6
  @bound_observer = NSNotificationCenter.defaultCenter.addObserverForName(
@@ -12,13 +12,7 @@ module MotionBindable::Strategies
12
12
  end
13
13
 
14
14
  def start_observing_object
15
- # Observe the attribute
16
- object.addObserver(
17
- self,
18
- forKeyPath: @attr_name,
19
- options: NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld,
20
- context: nil
21
- )
15
+ observe_object { |_, new| on_object_change(new) }
22
16
  end
23
17
 
24
18
  def on_bound_change(new = nil)
@@ -31,16 +25,8 @@ module MotionBindable::Strategies
31
25
 
32
26
  def unbind
33
27
  NSNotificationCenter.defaultCenter.removeObserver(@bound_observer)
34
- object.removeObserver(self, forKeyPath: @attr_name)
28
+ stop_observe_object
35
29
  super
36
30
  end
37
-
38
- # NSKeyValueObserving Protocol
39
-
40
- def observeValueForKeyPath(_, ofObject: _, change: _, context: _)
41
- on_object_change
42
- end
43
-
44
31
  end
45
-
46
32
  end
@@ -0,0 +1,57 @@
1
+ describe 'MotionBindable::StrategyHelpers' do
2
+ class TestObject
3
+ attr_accessor :prop
4
+ end
5
+
6
+ class TestModel
7
+ include MotionModel::Model
8
+ include MotionModel::ArrayModelAdapter
9
+ columns :prop
10
+ end
11
+
12
+ class TestStrategy < MotionBindable::Strategy
13
+ include MotionBindable::StrategyHelpers
14
+
15
+ def observe(&block)
16
+ observe_object(&block)
17
+ end
18
+ end
19
+
20
+ describe '#observe_object' do
21
+ describe 'normal object' do
22
+ before do
23
+ @object = TestObject.new
24
+ @response = nil
25
+ @strategy = TestStrategy.new(@object, :prop)
26
+ end
27
+
28
+ it 'should call the cb for changes' do
29
+ @strategy.observe { |old, new| @response = new }
30
+ @object.prop = 'updated'
31
+ wait(0.5) do
32
+ @response.should.equal 'updated'
33
+ end
34
+ end
35
+ end
36
+
37
+ describe 'MotionModel' do
38
+ before do
39
+ @object = TestModel.new
40
+ @response = nil
41
+ @strategy = TestStrategy.new(@object, :prop)
42
+ end
43
+
44
+ it 'should call the cb for changes' do
45
+ @strategy.observe { |old, new| @response = new }
46
+ @object.prop = 'updated'
47
+ @object.save
48
+ wait(0.5) { @response.should.equal 'updated' }
49
+ end
50
+
51
+ it 'should not turn the model.class into a KVO thingy' do
52
+ @strategy.observe { |old, new| @response = new }
53
+ @object.is_a?(MotionModel::Model).should.equal true
54
+ end
55
+ end
56
+ end
57
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: motion_bindable
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.5
4
+ version: 0.3.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: 2014-01-19 00:00:00.000000000 Z
11
+ date: 2014-03-19 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: A simple data binding library for RubyMotion.
14
14
  email:
@@ -29,6 +29,7 @@ files:
29
29
  - lib/motion_bindable.rb
30
30
  - lib/motion_bindable/bindable.rb
31
31
  - lib/motion_bindable/strategy.rb
32
+ - lib/motion_bindable/strategy_helpers.rb
32
33
  - lib/motion_bindable/version.rb
33
34
  - lib/strategies/proc.rb
34
35
  - lib/strategies/strategies.rb
@@ -45,6 +46,7 @@ files:
45
46
  - spec/strategies/ui_label_spec.rb
46
47
  - spec/strategies/ui_label_x_ui_text_field_spec.rb
47
48
  - spec/strategies/ui_text_field_spec.rb
49
+ - spec/strategy_helpers_spec.rb
48
50
  homepage: https://github.com/nathankot/motion-bindable
49
51
  licenses:
50
52
  - MIT
@@ -79,3 +81,4 @@ test_files:
79
81
  - spec/strategies/ui_label_spec.rb
80
82
  - spec/strategies/ui_label_x_ui_text_field_spec.rb
81
83
  - spec/strategies/ui_text_field_spec.rb
84
+ - spec/strategy_helpers_spec.rb