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.
- data/LICENSE +19 -0
- data/README +27 -0
- data/lib/keyvalue.rb +130 -0
- data/test/keyvalue-test.rb +172 -0
- 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
|