weak_observable 1.0.0 → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +7 -0
- data/CHANGELOG +6 -0
- data/Gemfile +7 -1
- data/README.md +54 -4
- data/Rakefile +4 -1
- data/lib/weak_observable/hub.rb +6 -5
- data/lib/weak_observable/version.rb +1 -1
- data/spec/weak_observable/hub_spec.rb +14 -37
- data/spec/weak_observable/weak_observable_spec.rb +10 -17
- metadata +9 -2
data/.travis.yml
ADDED
data/CHANGELOG
CHANGED
@@ -1,5 +1,11 @@
|
|
1
|
+
# [v1.0.1][]
|
2
|
+
|
3
|
+
- Updated README to fix faulty example.
|
4
|
+
- Fix Hub throwing warnings when adding an observer.
|
5
|
+
|
1
6
|
# [v1.0.0][]
|
2
7
|
|
3
8
|
Initial release. WeakObservable and Hub!
|
4
9
|
|
10
|
+
[v1.0.1]: https://github.com/Burgestrand/weak_observable/compare/v1.0.0...v1.0.1
|
5
11
|
[v1.0.0]: https://github.com/Burgestrand/weak_observable/tree/v1.0.0
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# WeakObservable
|
1
|
+
# WeakObservable [![Build Status](https://secure.travis-ci.org/Burgestrand/weak_observable.png)](http://travis-ci.org/Burgestrand/weak_observable)
|
2
2
|
|
3
3
|
WeakObservable is very similar to Observable from ruby’ standard library, but
|
4
4
|
with the very important difference in that it allows it’s subscribers to be
|
@@ -31,8 +31,7 @@ end
|
|
31
31
|
observer = Observer.new
|
32
32
|
|
33
33
|
playlist = Playlist.new
|
34
|
-
playlist.observers
|
35
|
-
playlist.observers.count # => 1
|
34
|
+
playlist.observers.add(observer)
|
36
35
|
|
37
36
|
# Notify all observers. Any arguments and given block goes out to them all.
|
38
37
|
playlist.observers.notify(:ping)
|
@@ -42,10 +41,61 @@ playlist.observers.notify(:ping)
|
|
42
41
|
observer = nil
|
43
42
|
|
44
43
|
# Some time passes, ruby garbage collection eventually kicks in, and now…
|
45
|
-
playlist.observers.count # => 0
|
46
44
|
playlist.observers.notify(:ping) # nothing happens, we have no observers.
|
47
45
|
```
|
48
46
|
|
47
|
+
There is also the Hub, which is a mix between a WeakObservable and a Hash.
|
48
|
+
You add observers by associating them with a key. A key can have multiple
|
49
|
+
observers. When you notify observers, you supply the key, and observers on
|
50
|
+
that key will be notified, but not any other observers.
|
51
|
+
|
52
|
+
```ruby
|
53
|
+
require 'weak_observable'
|
54
|
+
|
55
|
+
hub = WeakObservable::Hub.new
|
56
|
+
|
57
|
+
class Ninja
|
58
|
+
def update(event, *args)
|
59
|
+
puts "Ninjas: #{event}"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
class Pirate
|
64
|
+
def update(event, *args)
|
65
|
+
puts "Pirates: #{event}"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
ninja_a = Ninja.new
|
70
|
+
ninja_b = Ninja.new
|
71
|
+
|
72
|
+
pirate_a = Pirate.new
|
73
|
+
pirate_b = Pirate.new
|
74
|
+
|
75
|
+
hub.add(Ninja, ninja_a)
|
76
|
+
hub.add(Ninja, ninja_b)
|
77
|
+
hub.add(Pirate, pirate_a)
|
78
|
+
hub.add(Pirate, pirate_b)
|
79
|
+
|
80
|
+
hub.notify(Pirate, "Ninjarrrs arrr attacking!")
|
81
|
+
# ^ notifies all the pirates that ninjas are attacking.
|
82
|
+
|
83
|
+
hub.notify(Ninja, "All pirates are down.")
|
84
|
+
# ^ notifies all ninjas the pirates are all dead.
|
85
|
+
```
|
86
|
+
|
87
|
+
When all objects of a given key has been garbage collected, that key will
|
88
|
+
also be garbage collected. Because of this property Hubs can be extremely
|
89
|
+
useful interfacing with asynchronous C libraries and their callbacks.
|
90
|
+
|
91
|
+
## Supported platforms
|
92
|
+
|
93
|
+
- CRuby 1.9.2, 1.9.3
|
94
|
+
- JRuby 1.9-mode
|
95
|
+
- Rubinius 1.9-mode
|
96
|
+
|
97
|
+
I will not be supporting Ruby 1.8.
|
98
|
+
|
49
99
|
## Contributing
|
50
100
|
|
51
101
|
Please fork the repository and clone it. To get started with development you’ll
|
data/Rakefile
CHANGED
@@ -7,9 +7,12 @@ begin
|
|
7
7
|
require 'yard'
|
8
8
|
require 'yard/rake/yardoc_task'
|
9
9
|
YARD::Rake::YardocTask.new
|
10
|
+
rescue LoadError
|
10
11
|
end
|
11
12
|
|
12
13
|
require 'rspec/core/rake_task'
|
13
|
-
RSpec::Core::RakeTask.new
|
14
|
+
RSpec::Core::RakeTask.new do |task|
|
15
|
+
task.ruby_opts = '-W'
|
16
|
+
end
|
14
17
|
|
15
18
|
task :default => :spec
|
data/lib/weak_observable/hub.rb
CHANGED
@@ -82,11 +82,12 @@ class WeakObservable
|
|
82
82
|
# when all observers are gone, the observables for the address
|
83
83
|
# they are registered to is also eligible for garbage collection.
|
84
84
|
def backrefs_of(object)
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
85
|
+
ivar = :@__observable__hubs__
|
86
|
+
back_hash = if object.instance_variable_defined?(ivar)
|
87
|
+
object.instance_variable_get(ivar)
|
88
|
+
else
|
89
|
+
Hash.new
|
90
|
+
end
|
90
91
|
object.instance_variable_set(ivar, back_hash)
|
91
92
|
|
92
93
|
# An object may also be attached to several addresses within
|
@@ -127,51 +127,28 @@ describe WeakObservable::Hub do
|
|
127
127
|
end
|
128
128
|
|
129
129
|
specify "observers can be garbage collected" do
|
130
|
-
|
131
|
-
|
130
|
+
Ref::Mock.use do
|
131
|
+
observer1 = double
|
132
|
+
observer2 = double
|
132
133
|
|
133
|
-
|
134
|
-
|
134
|
+
observer1.should_not_receive(:update)
|
135
|
+
observer2.should_receive(:update)
|
135
136
|
|
136
|
-
|
137
|
-
|
138
|
-
observer = OpenStruct.new(:update => nil)
|
139
|
-
observable.add(key, observer)
|
140
|
-
ObjectSpace.define_finalizer(observer, finalizer)
|
141
|
-
end
|
137
|
+
observable.add(key, observer1)
|
138
|
+
observable.add(key, observer2)
|
142
139
|
|
143
|
-
|
144
|
-
GC.start
|
145
|
-
sleep 0.001
|
146
|
-
end
|
140
|
+
Ref::Mock.gc(observer1)
|
147
141
|
|
148
|
-
|
149
|
-
|
150
|
-
counter.should be > 1
|
142
|
+
observable.notify(key)
|
143
|
+
end
|
151
144
|
end
|
152
145
|
|
153
146
|
specify "entire keys can be garbage collected" do
|
154
|
-
|
155
|
-
finalizer = lambda { |_| counter += 1 }
|
147
|
+
mapping = lambda { |key| observable.instance_eval { @mapping }[key] }
|
156
148
|
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
threshold.times do |i|
|
161
|
-
# stub with return value for method cannot be garbage collected.
|
162
|
-
observer = OpenStruct.new(:update => nil)
|
163
|
-
observable.add(key + i, observer)
|
164
|
-
ObjectSpace.define_finalizer(observer, finalizer)
|
165
|
-
end
|
166
|
-
|
167
|
-
(threshold * stretches).times do
|
168
|
-
GC.start
|
169
|
-
sleep 0.001
|
170
|
-
end
|
171
|
-
|
172
|
-
# some very intimate introspection here
|
173
|
-
observable.instance_eval do
|
174
|
-
@mapping.to_a.length.should < threshold
|
149
|
+
Ref::Mock.use do
|
150
|
+
observable.add(key, observer)
|
151
|
+
expect { Ref::Mock.gc }.to change { mapping[key] }.to(nil)
|
175
152
|
end
|
176
153
|
end
|
177
154
|
end
|
@@ -85,26 +85,19 @@ describe WeakObservable do
|
|
85
85
|
end
|
86
86
|
|
87
87
|
specify "observers can be garbage collected" do
|
88
|
-
|
89
|
-
|
88
|
+
Ref::Mock.use do
|
89
|
+
observer1 = double
|
90
|
+
observer2 = double
|
90
91
|
|
91
|
-
|
92
|
-
|
92
|
+
observer1.should_not_receive(:update)
|
93
|
+
observer2.should_receive(:update)
|
93
94
|
|
94
|
-
|
95
|
-
|
96
|
-
observer = OpenStruct.new(:update => nil)
|
97
|
-
observable.add(observer)
|
98
|
-
ObjectSpace.define_finalizer(observer, finalizer)
|
99
|
-
end
|
95
|
+
observable.add(observer1)
|
96
|
+
observable.add(observer2)
|
100
97
|
|
101
|
-
|
102
|
-
GC.start
|
103
|
-
sleep 0.001
|
104
|
-
end
|
98
|
+
Ref::Mock.gc(observer1)
|
105
99
|
|
106
|
-
|
107
|
-
|
108
|
-
counter.should be > 1
|
100
|
+
observable.notify
|
101
|
+
end
|
109
102
|
end
|
110
103
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: weak_observable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-09-02 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: ref
|
@@ -59,6 +59,7 @@ extra_rdoc_files: []
|
|
59
59
|
files:
|
60
60
|
- .gitignore
|
61
61
|
- .rspec
|
62
|
+
- .travis.yml
|
62
63
|
- CHANGELOG
|
63
64
|
- Gemfile
|
64
65
|
- LICENSE.txt
|
@@ -85,12 +86,18 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
85
86
|
- - ! '>='
|
86
87
|
- !ruby/object:Gem::Version
|
87
88
|
version: '0'
|
89
|
+
segments:
|
90
|
+
- 0
|
91
|
+
hash: 269781825206150069
|
88
92
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
89
93
|
none: false
|
90
94
|
requirements:
|
91
95
|
- - ! '>='
|
92
96
|
- !ruby/object:Gem::Version
|
93
97
|
version: '0'
|
98
|
+
segments:
|
99
|
+
- 0
|
100
|
+
hash: 269781825206150069
|
94
101
|
requirements: []
|
95
102
|
rubyforge_project:
|
96
103
|
rubygems_version: 1.8.24
|