keyvalue 0.1.1

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.
Files changed (5) hide show
  1. data/LICENSE +19 -0
  2. data/README +27 -0
  3. data/lib/keyvalue.rb +130 -0
  4. data/test/keyvalue-test.rb +172 -0
  5. metadata +66 -0
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2010 Melchior Brislinger
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,27 @@
1
+ Key Value Coding, Key Value Observing & Bindings for Ruby.
2
+
3
+ Bindings
4
+ --------
5
+
6
+ class A
7
+ include KeyValue::KVC
8
+ include KeyValue::KVO::Dynamic
9
+ include KeyValue::Binding
10
+
11
+ attr_kvo_accessor :value
12
+ end
13
+
14
+ class B
15
+ attr_accessor :value
16
+ end
17
+
18
+ a = A.new
19
+ b = B.new
20
+
21
+ a.bind :value, b, :value
22
+
23
+ a.value = 5
24
+ b.value # => 5
25
+
26
+ a.value = 10
27
+ b.value # => 10
data/lib/keyvalue.rb ADDED
@@ -0,0 +1,130 @@
1
+ module KeyValue
2
+ module KVC
3
+ def value_for_key key
4
+ instance_variable_get '@' + key.to_s
5
+ end
6
+
7
+ def set_value_for_key key, value
8
+ instance_variable_set '@' + key.to_s, value
9
+ end
10
+
11
+ def value_for_key_path path
12
+ obj = self
13
+
14
+ path.split('.').each do |component|
15
+ if component == '@sum'
16
+ obj = obj.inject(nil) { |sum,x| sum ? sum+x : x }
17
+ elsif component == '@avg'
18
+ obj = obj.inject(nil) { |sum,x| sum ? sum+x : x } / obj.size
19
+ elsif component == '@max'
20
+ obj = obj.max
21
+ elsif component == '@min'
22
+ obj = obj.min
23
+ else
24
+ if obj.is_a? Array
25
+ obj = obj.map { |o| o.instance_variable_get '@' + component }
26
+ else
27
+ obj = obj.instance_variable_get '@' + component
28
+ end
29
+ end
30
+ end
31
+
32
+ obj
33
+ end
34
+ end
35
+
36
+ module KVO
37
+ module Static
38
+ def add_observer key, obs, func=:update
39
+ unless obs.respond_to? func
40
+ raise NoMethodError, "observer does not respond to `#{func.to_s}'"
41
+ end
42
+ @kvo_observers ||= {}
43
+ @kvo_observers[key] ||= []
44
+ @kvo_observers[key] << {:obs => obs, :func => func}
45
+ end
46
+
47
+ def remove_observer key, obs
48
+ @kvo_observers[key].delete_if { |o| o[:obs] == obs }
49
+ end
50
+
51
+ def set_value_for_key key, value
52
+ instance_variable_set '@' + key.to_s, value
53
+
54
+ @kvo_observers[key].each do |obs|
55
+ obs[:obs].send obs[:func], self, key, value
56
+ end if @kvo_observers and @kvo_observers[key]
57
+ end
58
+
59
+ module ClassMethods
60
+ def attr_kvo_accessor *accessors
61
+ accessors.each do |m|
62
+ define_method(m) do
63
+ value_for_key m
64
+ end
65
+
66
+ define_method("#{m}=") do |val|
67
+ set_value_for_key m, val
68
+ end
69
+ end
70
+ end
71
+ end
72
+
73
+ def self.included base
74
+ base.extend ClassMethods
75
+ end
76
+ end
77
+
78
+ module Dynamic
79
+ def add_observer key, obs, &block
80
+ @kvo_observers ||= {}
81
+ @kvo_observers[key] ||= []
82
+ @kvo_observers[key] << {:obs => obs, :block => block}
83
+ end
84
+
85
+ def remove_observer key, obs
86
+ @kvo_observers[key].delete_if { |o| o[:obs] == obs }
87
+ end
88
+
89
+ def set_value_for_key key, value
90
+ instance_variable_set '@' + key.to_s, value
91
+
92
+ @kvo_observers[key].each do |obs|
93
+ obs[:block].call self, key, value
94
+ end if @kvo_observers and @kvo_observers[key]
95
+ end
96
+
97
+ module ClassMethods
98
+ def attr_kvo_accessor *accessors
99
+ accessors.each do |m|
100
+ define_method(m) do
101
+ value_for_key m
102
+ end
103
+
104
+ define_method("#{m}=") do |val|
105
+ set_value_for_key m, val
106
+ end
107
+ end
108
+ end
109
+ end
110
+
111
+ def self.included base
112
+ base.extend ClassMethods
113
+ end
114
+ end
115
+ end
116
+
117
+ module Binding
118
+ def bind key, obs, target, transformer=nil
119
+ self.add_observer key, obs do |source, k, value|
120
+ method = target.to_s + '='
121
+ value = transformer.nil? ? value : transformer.call(value)
122
+ obs.send method.to_sym, value
123
+ end
124
+ end
125
+
126
+ def unbind key, obs
127
+ self.remove_observer key, obs
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,172 @@
1
+ require 'keyvalue'
2
+ require 'test/unit'
3
+
4
+ class Member
5
+ attr_accessor :value
6
+
7
+ def initialize x
8
+ @value = x
9
+ end
10
+ end
11
+
12
+ class DynamicObservee
13
+ include KeyValue::KVC
14
+ include KeyValue::KVO::Dynamic
15
+ include KeyValue::Binding
16
+
17
+ attr_kvo_accessor :variable, :variable2
18
+ attr_accessor :member
19
+
20
+ def initialize
21
+ @variable = 'original value'
22
+ @member = [Member.new(1), Member.new(2), Member.new(3)]
23
+ end
24
+ end
25
+
26
+ class StaticObservee
27
+ include KeyValue::KVC
28
+ include KeyValue::KVO::Static
29
+
30
+ attr_kvo_accessor :variable, :variable2
31
+ attr_accessor :member
32
+
33
+ def initialize
34
+ @variable = 'original value'
35
+ @member = [Member.new(1), Member.new(2), Member.new(3)]
36
+ end
37
+ end
38
+
39
+ class Observing
40
+ attr_accessor :target
41
+
42
+ def initialize
43
+ @target = nil
44
+ end
45
+
46
+ def update source, key, value
47
+ @target = value if key == :variable
48
+ end
49
+ end
50
+
51
+ class TestKeyValue < Test::Unit::TestCase
52
+ def test_static_kvc
53
+ observee = StaticObservee.new
54
+
55
+ assert_equal [1, 2, 3], observee.value_for_key_path('member.value')
56
+ assert_equal 6, observee.value_for_key_path('member.value.@sum')
57
+ assert_equal 2, observee.value_for_key_path('member.value.@avg')
58
+ assert_equal 3, observee.value_for_key_path('member.value.@max')
59
+ assert_equal 1, observee.value_for_key_path('member.value.@min')
60
+ end
61
+
62
+ def test_dynamic_kvc
63
+ observee = DynamicObservee.new
64
+
65
+ assert_equal [1, 2, 3], observee.value_for_key_path('member.value')
66
+ assert_equal 6, observee.value_for_key_path('member.value.@sum')
67
+ assert_equal 2, observee.value_for_key_path('member.value.@avg')
68
+ assert_equal 3, observee.value_for_key_path('member.value.@max')
69
+ assert_equal 1, observee.value_for_key_path('member.value.@min')
70
+ end
71
+
72
+ def test_static_kvo
73
+ observee = StaticObservee.new
74
+ observer = Observing.new
75
+
76
+ assert_equal 'original value', observee.variable
77
+
78
+ observee.variable = 'test'
79
+ observee.variable = 'original value'
80
+
81
+ observee.add_observer :variable, observer
82
+
83
+ observee.variable = 'new value'
84
+
85
+ assert_equal 'new value', observee.variable
86
+ assert_equal observee.variable, observer.target
87
+
88
+ observee.remove_observer :variable, observer
89
+
90
+ observee.variable = 'another new value'
91
+
92
+ assert_equal 'another new value', observee.variable
93
+ assert_equal 'new value', observer.target
94
+ end
95
+
96
+ def test_dynamic_kvo
97
+ observee = DynamicObservee.new
98
+
99
+ assert_equal 'original value', observee.variable
100
+
101
+ observee.variable = 'test'
102
+ observee.variable = 'original value'
103
+
104
+ observee.add_observer :variable, :obs do |source,key,value|
105
+ assert_equal observee, source
106
+ assert_equal :variable, key
107
+ assert_equal 'new value', value
108
+ end
109
+
110
+ observee.variable = 'new value'
111
+
112
+ assert_equal 'new value', observee.variable
113
+ end
114
+
115
+ def test_static_kvo_accessors
116
+ observee = DynamicObservee.new
117
+
118
+ assert_respond_to observee, :variable
119
+ assert_respond_to observee, :variable=
120
+ end
121
+
122
+ def test_dynamic_kvo_accessors
123
+ observee = DynamicObservee.new
124
+
125
+ assert_respond_to observee, :variable
126
+ assert_respond_to observee, :variable=
127
+ end
128
+
129
+ def test_bindings
130
+ observee = DynamicObservee.new
131
+ observing = Observing.new
132
+
133
+ observee.bind :variable, observing, :target
134
+
135
+ assert_respond_to observee, :variable
136
+ assert_respond_to observee, :variable=
137
+
138
+ assert_equal 'original value', observee.variable
139
+ assert_equal nil, observing.target
140
+
141
+ observee.variable = 'new value'
142
+
143
+ assert_equal 'new value', observee.variable
144
+ assert_equal observee.variable, observing.target
145
+
146
+ observee.unbind :variable, observing
147
+
148
+ observee.variable = 'another new value'
149
+
150
+ assert_equal 'another new value', observee.variable
151
+ assert_equal 'new value', observing.target
152
+ end
153
+
154
+ def test_transformer
155
+ observee = DynamicObservee.new
156
+ observing = Observing.new
157
+
158
+ observee.bind :variable, observing, :target, Proc.new { |value| value.upcase }
159
+
160
+ assert_respond_to observee, :variable
161
+ assert_respond_to observee, :variable=
162
+
163
+ assert_equal 'original value', observee.variable
164
+ assert_equal nil, observing.target
165
+
166
+ observee.variable = 'new value'
167
+
168
+ assert_equal 'new value', observee.variable
169
+ assert_equal 'NEW VALUE', observing.target
170
+ assert_equal observee.variable.upcase, observing.target
171
+ end
172
+ end
metadata ADDED
@@ -0,0 +1,66 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: keyvalue
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 1
9
+ version: 0.1.1
10
+ platform: ruby
11
+ authors:
12
+ - Melchior Brislinger
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-03-09 00:00:00 +01:00
18
+ default_executable:
19
+ dependencies: []
20
+
21
+ description: Key-Value Coding, Key-Value Observing & Bindings for Ruby.
22
+ email: mb@ultravague.com
23
+ executables: []
24
+
25
+ extensions: []
26
+
27
+ extra_rdoc_files:
28
+ - README
29
+ - LICENSE
30
+ files:
31
+ - lib/keyvalue.rb
32
+ - test/keyvalue-test.rb
33
+ - README
34
+ - LICENSE
35
+ has_rdoc: true
36
+ homepage: http://github.com/melchior-b/KeyValue
37
+ licenses: []
38
+
39
+ post_install_message:
40
+ rdoc_options: []
41
+
42
+ require_paths:
43
+ - lib
44
+ required_ruby_version: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ segments:
49
+ - 0
50
+ version: "0"
51
+ required_rubygems_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ segments:
56
+ - 0
57
+ version: "0"
58
+ requirements: []
59
+
60
+ rubyforge_project: keyvalue
61
+ rubygems_version: 1.3.6
62
+ signing_key:
63
+ specification_version: 3
64
+ summary: KVC, KVO & Bindings for Ruby.
65
+ test_files:
66
+ - test/keyvalue-test.rb