Skip to content

Commit 3c63435

Browse files
committed
Merge pull request #63 from jdantonio/block_observer
Observable Shared Specs and Block Observer
2 parents 31c9da9 + c2bbb01 commit 3c63435

14 files changed

+302
-17
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ ibm.value #=> 187.57
142142
* [Michele Della Torre](https://github.com/mighe)
143143
* [Chris Seaton](https://github.com/chrisseaton)
144144
* [Lucas Allan](https://github.com/lucasallan)
145+
* [Ravil Bayramgalin](https://github.com/brainopia)
145146
* [Giuseppe Capizzi](https://github.com/gcapizzi)
146147
* [Brian Shirai](https://github.com/brixen)
147148
* [Chip Miller](https://github.com/chip-miller)

lib/concurrent/atomic/copy_on_notify_observer_set.rb

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,23 @@ def initialize
1515
# @param [Object] observer the observer to add
1616
# @param [Symbol] func the function to call on the observer during notification. Default is :update
1717
# @return [Symbol] the added function
18-
def add_observer(observer, func=:update)
18+
def add_observer(observer=nil, func=:update, &block)
19+
if observer.nil? && block.nil?
20+
raise ArgumentError, 'should pass observer as a first argument or block'
21+
elsif observer && block
22+
raise ArgumentError.new('cannot provide both an observer and a block')
23+
end
24+
25+
if block
26+
observer = block
27+
func = :call
28+
end
29+
1930
@mutex.lock
2031
@observers[observer] = func
2132
@mutex.unlock
2233

23-
func
34+
observer
2435
end
2536

2637
# @param [Object] observer the observer to remove

lib/concurrent/atomic/copy_on_write_observer_set.rb

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,25 @@ def initialize
1414
# @param [Object] observer the observer to add
1515
# @param [Symbol] func the function to call on the observer during notification. Default is :update
1616
# @return [Symbol] the added function
17-
def add_observer(observer, func=:update)
17+
def add_observer(observer=nil, func=:update, &block)
18+
if observer.nil? && block.nil?
19+
raise ArgumentError, 'should pass observer as a first argument or block'
20+
elsif observer && block
21+
raise ArgumentError.new('cannot provide both an observer and a block')
22+
end
23+
24+
if block
25+
observer = block
26+
func = :call
27+
end
28+
1829
@mutex.lock
1930
new_observers = @observers.dup
2031
new_observers[observer] = func
2132
@observers = new_observers
2233
@mutex.unlock
2334

24-
func
35+
observer
2536
end
2637

2738
# @param [Object] observer the observer to remove

lib/concurrent/ivar.rb

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,15 @@ def initialize(value = NO_VALUE, opts = {})
4242
#
4343
# @param [Object] observer the object that will be notified of changes
4444
# @param [Symbol] func symbol naming the method to call when this `Observable` has changes`
45-
def add_observer(observer, func = :update)
45+
def add_observer(observer = nil, func = :update, &block)
46+
raise ArgumentError.new('cannot provide both an observer and a block') if observer && block
4647
direct_notification = false
4748

49+
if block
50+
observer = block
51+
func = :call
52+
end
53+
4854
mutex.synchronize do
4955
if event.set?
5056
direct_notification = true
@@ -54,7 +60,7 @@ def add_observer(observer, func = :update)
5460
end
5561

5662
observer.send(func, Time.now, self.value, reason) if direct_notification
57-
func
63+
observer
5864
end
5965

6066
def set(value)

lib/concurrent/observable.rb

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ module Concurrent
55

66
module Observable
77

8-
def add_observer(*args)
9-
observers.add_observer(*args)
8+
def add_observer(*args, &block)
9+
observers.add_observer(*args, &block)
1010
end
1111

1212
def delete_observer(*args)
@@ -15,6 +15,7 @@ def delete_observer(*args)
1515

1616
def delete_observers
1717
observers.delete_observers
18+
self
1819
end
1920

2021
def count_observers

lib/concurrent/scheduled_task.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,9 @@ def cancel
5151
end
5252
alias_method :stop, :cancel
5353

54-
def add_observer(*args)
54+
def add_observer(*args, &block)
5555
if_state(:unscheduled, :pending, :in_progress) do
56-
observers.add_observer(*args)
56+
observers.add_observer(*args, &block)
5757
end
5858
end
5959

spec/concurrent/agent_spec.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
require 'spec_helper'
22
require_relative 'dereferenceable_shared'
3+
require_relative 'observable_shared'
34

45
module Concurrent
56

@@ -38,6 +39,17 @@ def execute_dereferenceable(subject)
3839
end
3940

4041
it_should_behave_like :dereferenceable
42+
43+
# observable
44+
45+
subject{ Agent.new(0) }
46+
47+
def trigger_observable(observable)
48+
observable.post{ nil }
49+
sleep(0.1)
50+
end
51+
52+
it_should_behave_like :observable
4153
end
4254

4355
context '#initialize' do

spec/concurrent/atomic/observer_set_shared.rb

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,16 @@
88

99
describe '#add_observer' do
1010

11-
context 'with argument' do
12-
it 'should return the passed function' do
13-
observer_set.add_observer(observer, :a_method).should eq(:a_method)
11+
context 'with arguments' do
12+
it 'should return the observer' do
13+
observer_set.add_observer(observer, :a_method).should == observer
1414
end
1515
end
1616

17-
context 'without arguments' do
18-
it 'should return the default function' do
19-
observer_set.add_observer(observer).should eq(:update)
17+
context 'with a block' do
18+
it 'should return the observer based on a block' do
19+
observer = observer_set.add_observer { :block }
20+
observer.call.should == :block
2021
end
2122
end
2223
end
@@ -61,6 +62,14 @@
6162
observer_set.notify_observers('a string arg')
6263
end
6364

65+
it 'should notify an observer from a block' do
66+
notification = double
67+
expect(notification).to receive(:catch)
68+
69+
observer_set.add_observer {|arg| arg.catch }
70+
observer_set.notify_observers notification
71+
end
72+
6473
it 'can be called many times' do
6574
expect(observer).to receive(:update).with(:an_arg).twice
6675
expect(observer).to receive(:update).with(no_args).once

spec/concurrent/channel/probe_spec.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
require 'spec_helper'
2+
require_relative '../observable_shared'
23

34
module Concurrent
45

@@ -7,6 +8,19 @@ module Concurrent
78
let(:channel) { Object.new }
89
let(:probe) { Channel::Probe.new }
910

11+
describe 'behavior' do
12+
13+
# observable
14+
15+
subject{ Channel::Probe.new }
16+
17+
def trigger_observable(observable)
18+
observable.set('value')
19+
end
20+
21+
it_should_behave_like :observable
22+
end
23+
1024
describe '#set_unless_assigned' do
1125
context 'empty probe' do
1226
it 'assigns the value' do

spec/concurrent/future_spec.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
require 'spec_helper'
22
require_relative 'dereferenceable_shared'
33
require_relative 'obligation_shared'
4+
require_relative 'observable_shared'
45

56
module Concurrent
67

@@ -54,6 +55,17 @@ def execute_dereferenceable(subject)
5455
end
5556

5657
it_should_behave_like :dereferenceable
58+
59+
# observable
60+
61+
subject{ Future.new{ nil } }
62+
63+
def trigger_observable(observable)
64+
observable.execute
65+
sleep(0.1)
66+
end
67+
68+
it_should_behave_like :observable
5769
end
5870

5971
context 'subclassing' do

0 commit comments

Comments
 (0)