kvo 0.0.2 → 0.0.3

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.
data/.gitignore CHANGED
@@ -2,3 +2,4 @@ pkg/*
2
2
  *.gem
3
3
  .bundle
4
4
  *.lock
5
+ tags
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  Description
2
2
  ===========
3
3
 
4
- Key-Value Observing in ruby
4
+ Key-Value Observing and Binding in ruby
5
5
 
6
6
  ### Synopsis
7
7
 
@@ -9,16 +9,34 @@ Fires a [Publisher](http://atomicobject.github.com/publisher/) event whenever th
9
9
 
10
10
  ### How do I use it?
11
11
 
12
+ ```ruby
13
+ # kvo example
12
14
  class Foo
13
15
  include Kvo
14
16
  kvo_attr_accessor :bar, :baz
15
17
  end
16
-
18
+
17
19
  f = Foo.new
18
20
  f.when :bar_changed do |oldVal, newVal|
19
21
  puts "bar was changed from #{oldVal} to #{newVal}"
20
22
  end
21
23
 
24
+
25
+ # kvb example
26
+ class Baz
27
+ attr_accessor :bar
28
+ end
29
+
30
+ b = Bar.new
31
+ # b's bar will be updated whenever f's is
32
+ f.kvo_bind_attr :bar, b
33
+
34
+ # optional transforms
35
+ f.kvo_bind_attr :bar, b do |val|
36
+ val + 5
37
+ end
38
+ ```
39
+
22
40
  Authors
23
41
  =======
24
42
  * Shawn Anderson (shawn.anderson@atomicobject.com)
data/lib/kvo.rb CHANGED
@@ -3,21 +3,42 @@ module Kvo
3
3
  module ClassMethods
4
4
  def kvo_attr_accessor(*kvo_symbols)
5
5
  kvo_symbols.each do |kvo|
6
+
7
+ raise "the method #{kvo} already exists" if self.instance_methods.include? kvo
8
+ raise "the method #{kvo}= already exists" if self.instance_methods.include? "#{kvo}=".to_sym
9
+
6
10
  class_eval <<-METHODS
7
11
  can_fire :#{kvo}_changed unless published_events == :any_event_is_ok
8
12
  def #{kvo}
9
13
  @kvo_#{kvo}
10
14
  end
11
- def #{kvo}=(newVal)
15
+ def #{kvo}=(new_val)
12
16
  old = @kvo_#{kvo}
13
- @kvo_#{kvo}=newVal
14
- fire :#{kvo}_changed, old, newVal
17
+ @kvo_#{kvo}=new_val
18
+ fire :#{kvo}_changed, old, new_val
15
19
  end
16
20
  METHODS
17
21
  end
18
22
  end
19
23
  end
20
24
 
25
+ def kvo_bind_attr(attr_sym, target, opts={})
26
+ target_attr = opts[:to] || attr_sym
27
+ change_event = "#{attr_sym}_changed".to_sym
28
+
29
+ unless self.class.published_events.include? change_event
30
+ raise "#{attr_sym} is not a kvo attr; use #{self.class}.kvo_attr_accessor #{attr_sym}"
31
+ end
32
+
33
+ self.when change_event do |old, new_val|
34
+ val = new_val
35
+ # transform
36
+ val = yield(new_val) if block_given?
37
+
38
+ target.send "#{target_attr}=", val
39
+ end
40
+ end
41
+
21
42
  def self.included(receiver)
22
43
  receiver.extend Publisher
23
44
  receiver.extend ClassMethods
data/lib/kvo/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Kvo
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
data/spec/kvo_spec.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  require File.join(File.dirname(__FILE__),'helper')
2
2
 
3
- describe 'kvo' do
3
+ describe '.kvo_attr_accessor' do
4
4
  before do
5
5
  @target = Foo.new
6
6
  @target.bar = :old
@@ -32,6 +32,21 @@ describe 'kvo' do
32
32
  @target.instance_variable_get("@bar").should be_nil
33
33
  end
34
34
 
35
+ it 'can call back a block of arity 0' do
36
+ @baz_fired = nil
37
+ @target.when :baz_changed do @baz_fired = true end
38
+ @target.baz = :something
39
+ @baz_fired.should be
40
+ end
41
+
42
+ it 'raises an error if attr setter is already defined' do
43
+ lambda{ ThingOne.kvo_attr_accessor :one }.should raise_exception(RuntimeError, /one= already exists/)
44
+ end
45
+
46
+ it 'raises an error if attr getter is already defined' do
47
+ lambda{ ThingTwo.kvo_attr_accessor :two }.should raise_exception(RuntimeError, /two already exists/)
48
+ end
49
+
35
50
  describe "inheritance" do
36
51
  before do
37
52
  @target = SubFoo.new
@@ -66,6 +81,44 @@ describe 'kvo' do
66
81
  end
67
82
  end
68
83
 
84
+ describe '#kvo_bind_attr' do
85
+ let(:peter_pan) {
86
+ peter = PeterPan.new
87
+ peter.location = "OVER THERE"
88
+ peter.flying = false
89
+ peter
90
+ }
91
+ let(:shadow) { Shadow.new }
92
+
93
+ it 'modifies attr when bound objects attr changes' do
94
+ peter_pan.kvo_bind_attr :location, shadow
95
+ peter_pan.location = "NEVERLAND"
96
+
97
+ shadow.location.should == "NEVERLAND"
98
+ end
99
+
100
+ it 'can use a different method name on target' do
101
+ peter_pan.kvo_bind_attr :location, shadow, to: :place
102
+
103
+ peter_pan.location = "NEVERLAND"
104
+
105
+ shadow.location.should be_nil
106
+ shadow.place.should == "NEVERLAND"
107
+ end
108
+
109
+ it 'can transform new value with a block' do
110
+ peter_pan.kvo_bind_attr :location, shadow do |new_value|
111
+ new_value.downcase
112
+ end
113
+ peter_pan.location = "NEVERLAND"
114
+ shadow.location.should == "neverland"
115
+ end
116
+
117
+ it 'can bind to non-kvo attrs (by making them kvo)' do
118
+ lambda{ peter_pan.kvo_bind_attr :flying, shadow}.should raise_exception(RuntimeError, /flying is not a kvo attr/)
119
+ end
120
+ end
121
+
69
122
  class Foo
70
123
  include Kvo
71
124
  kvo_attr_accessor :bar, :baz
@@ -74,3 +127,26 @@ end
74
127
  class SubFoo < Foo
75
128
  kvo_attr_accessor :qux
76
129
  end
130
+
131
+ class PeterPan
132
+ include Kvo
133
+ kvo_attr_accessor :location
134
+ attr_accessor :flying
135
+ end
136
+
137
+ class Shadow
138
+ attr_accessor :location, :place, :flying
139
+ end
140
+
141
+ class ThingOne
142
+ include Kvo
143
+ attr_writer :one
144
+ end
145
+
146
+ class ThingTwo
147
+ include Kvo
148
+ attr_reader :two
149
+ end
150
+
151
+
152
+
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kvo
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-03-28 00:00:00.000000000 Z
12
+ date: 2012-06-20 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: publisher
16
- requirement: &2157009500 !ruby/object:Gem::Requirement
16
+ requirement: !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,15 @@ dependencies:
21
21
  version: '0'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *2157009500
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
25
30
  - !ruby/object:Gem::Dependency
26
31
  name: rspec
27
- requirement: &2157008500 !ruby/object:Gem::Requirement
32
+ requirement: !ruby/object:Gem::Requirement
28
33
  none: false
29
34
  requirements:
30
35
  - - ! '>='
@@ -32,10 +37,15 @@ dependencies:
32
37
  version: '0'
33
38
  type: :development
34
39
  prerelease: false
35
- version_requirements: *2157008500
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
36
46
  - !ruby/object:Gem::Dependency
37
47
  name: mocha
38
- requirement: &2157007540 !ruby/object:Gem::Requirement
48
+ requirement: !ruby/object:Gem::Requirement
39
49
  none: false
40
50
  requirements:
41
51
  - - ! '>='
@@ -43,7 +53,12 @@ dependencies:
43
53
  version: '0'
44
54
  type: :development
45
55
  prerelease: false
46
- version_requirements: *2157007540
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
47
62
  description: KVO in ruby.
48
63
  email:
49
64
  - shawn42@gmail.com
@@ -80,7 +95,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
80
95
  version: '0'
81
96
  requirements: []
82
97
  rubyforge_project: kvo
83
- rubygems_version: 1.8.10
98
+ rubygems_version: 1.8.24
84
99
  signing_key:
85
100
  specification_version: 3
86
101
  summary: KVO in ruby