opal-vienna 0.7.0
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.
- checksums.yaml +7 -0
- data/.gitignore +3 -0
- data/.travis.yml +8 -0
- data/Gemfile +7 -0
- data/README.md +294 -0
- data/Rakefile +7 -0
- data/config.ru +8 -0
- data/lib/opal-vienna.rb +1 -0
- data/lib/opal/vienna.rb +7 -0
- data/lib/opal/vienna/version.rb +5 -0
- data/opal-vienna.gemspec +26 -0
- data/opal/vienna.rb +5 -0
- data/opal/vienna/adapters/base.rb +45 -0
- data/opal/vienna/adapters/local.rb +50 -0
- data/opal/vienna/adapters/rest.rb +97 -0
- data/opal/vienna/eventable.rb +35 -0
- data/opal/vienna/history_router.rb +44 -0
- data/opal/vienna/model.rb +222 -0
- data/opal/vienna/observable.rb +90 -0
- data/opal/vienna/observable_array.rb +73 -0
- data/opal/vienna/output_buffer.rb +13 -0
- data/opal/vienna/record_array.rb +31 -0
- data/opal/vienna/router.rb +85 -0
- data/opal/vienna/template_view.rb +41 -0
- data/opal/vienna/view.rb +93 -0
- data/spec/eventable_spec.rb +94 -0
- data/spec/history_router_spec.rb +47 -0
- data/spec/model/accessing_attributes_spec.rb +29 -0
- data/spec/model/as_json_spec.rb +28 -0
- data/spec/model/attribute_spec.rb +22 -0
- data/spec/model/initialize_spec.rb +42 -0
- data/spec/model/load_spec.rb +17 -0
- data/spec/model/persistence_spec.rb +84 -0
- data/spec/model_spec.rb +84 -0
- data/spec/observable_array_spec.rb +130 -0
- data/spec/observable_spec.rb +116 -0
- data/spec/output_buffer_spec.rb +37 -0
- data/spec/record_array_spec.rb +50 -0
- data/spec/route_spec.rb +89 -0
- data/spec/router_spec.rb +103 -0
- data/spec/spec_helper.rb +53 -0
- data/spec/template_view_spec.rb +47 -0
- data/spec/vendor/jquery.js +2 -0
- data/spec/view_spec.rb +78 -0
- metadata +181 -0
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class AccessingAttributesSpec < Vienna::Model
|
4
|
+
attributes :first_name, :last_name
|
5
|
+
|
6
|
+
def last_name=(name)
|
7
|
+
raise "Cannot set last_name"
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
describe Vienna::Model do
|
12
|
+
|
13
|
+
let(:model) { AccessingAttributesSpec.new }
|
14
|
+
|
15
|
+
describe "#[]" do
|
16
|
+
it "retrieves attributes from the model" do
|
17
|
+
model[:first_name].should be_nil
|
18
|
+
model.first_name = "Adam"
|
19
|
+
model[:first_name].should eq("Adam")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "#[]=" do
|
24
|
+
it "sets attributes on the model" do
|
25
|
+
model[:first_name] = "Adam"
|
26
|
+
model.first_name.should eq("Adam")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Vienna::Model do
|
4
|
+
describe "#as_json" do
|
5
|
+
let(:model) { User.new }
|
6
|
+
|
7
|
+
it "returns a hash" do
|
8
|
+
expect(model.as_json).to be_kind_of(Hash)
|
9
|
+
end
|
10
|
+
|
11
|
+
it "contains all attributes on model" do
|
12
|
+
expect(model.as_json).to eq({ "foo" => nil, "bar" => nil, "baz" => nil })
|
13
|
+
|
14
|
+
model.foo = "Adam"
|
15
|
+
expect(model.as_json).to eq({ "foo" => "Adam", "bar" => nil, "baz" => nil })
|
16
|
+
|
17
|
+
model.bar = "Beynon"
|
18
|
+
|
19
|
+
expect(model.as_json).to eq({ "foo" => "Adam", "bar" => "Beynon", "baz" => nil })
|
20
|
+
end
|
21
|
+
|
22
|
+
it "includes the id, if set" do
|
23
|
+
model.id = 42
|
24
|
+
|
25
|
+
expect(model.as_json).to eq({ "id" => 42, "foo" => nil, "bar" => nil, "baz" => nil })
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Vienna::Model do
|
4
|
+
describe ".attribute" do
|
5
|
+
let(:model) { User.new }
|
6
|
+
|
7
|
+
it "should create a reader method for attribute" do
|
8
|
+
expect(model).to respond_to(:foo)
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should create a writer method for attribute" do
|
12
|
+
expect(model).to respond_to(:foo=)
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "writer" do
|
16
|
+
it "sets value on model" do
|
17
|
+
model.foo = 'Tommy'
|
18
|
+
expect(model.foo).to eq('Tommy')
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Vienna::Model do
|
4
|
+
describe "#initialize" do
|
5
|
+
it "should have all nil values for attributes when passed no attrs" do
|
6
|
+
model = User.new
|
7
|
+
|
8
|
+
expect(model.foo).to be_nil
|
9
|
+
expect(model.bar).to be_nil
|
10
|
+
expect(model.baz).to be_nil
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should set a given value for a given attributes" do
|
14
|
+
model = User.new foo: 3.142
|
15
|
+
|
16
|
+
expect(model.foo).to eq(3.142)
|
17
|
+
expect(model.bar).to be_nil
|
18
|
+
expect(model.baz).to be_nil
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should be able to set many attributes" do
|
22
|
+
model = User.new foo: 'hello', bar: 'world', baz: 42
|
23
|
+
|
24
|
+
expect(model.foo).to eq('hello')
|
25
|
+
expect(model.bar).to eq('world')
|
26
|
+
expect(model.baz).to eq(42)
|
27
|
+
end
|
28
|
+
|
29
|
+
it "creates @attributes as an empty hash" do
|
30
|
+
model = User.new
|
31
|
+
model.instance_variable_get(:@attributes).should eq({})
|
32
|
+
end
|
33
|
+
|
34
|
+
it "marks the model as being a new record" do
|
35
|
+
expect(User.new).to be_new_record
|
36
|
+
end
|
37
|
+
|
38
|
+
it "marks the model as not being loaded" do
|
39
|
+
expect(User.new).to_not be_loaded
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Vienna::Model do
|
4
|
+
describe "#load" do
|
5
|
+
let(:model) { SimpleModel.new }
|
6
|
+
|
7
|
+
it "marks the model instance as loaded" do
|
8
|
+
model.load({})
|
9
|
+
expect(model).to be_loaded
|
10
|
+
end
|
11
|
+
|
12
|
+
it "marks the model as not being a new record" do
|
13
|
+
model.load({})
|
14
|
+
expect(model).to_not be_new_record
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Vienna::Model do
|
4
|
+
let(:model_class) { SimpleModel }
|
5
|
+
let(:model) { model_class.new }
|
6
|
+
let(:loaded_model) { model_class.load(:first_name => "Adam", id: 872) }
|
7
|
+
describe "#did_destroy" do
|
8
|
+
it "triggers a :destroy event on the record" do
|
9
|
+
called = false
|
10
|
+
model.on(:destroy) { called = true }
|
11
|
+
model.did_destroy
|
12
|
+
called.should eq(true)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "triggers a :destroy event on the class" do
|
16
|
+
called = false
|
17
|
+
model_class.on(:destroy) { called = true }
|
18
|
+
model.did_destroy
|
19
|
+
called.should eq(true)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "removes the record from the class identity_map" do
|
23
|
+
loaded_model.did_destroy
|
24
|
+
model_class.identity_map[loaded_model.id].should eq(nil)
|
25
|
+
end
|
26
|
+
|
27
|
+
it "removes the record from the class record_array" do
|
28
|
+
loaded_model.did_destroy
|
29
|
+
model_class.all.should_not include(loaded_model)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe '#destroyed?' do
|
34
|
+
it 'is false for "living" models' do
|
35
|
+
model.destroyed?.should be_falsey
|
36
|
+
end
|
37
|
+
|
38
|
+
it "is true for destroyed models" do
|
39
|
+
loaded_model.did_destroy
|
40
|
+
loaded_model.destroyed?.should be_truthy
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe "#did_create" do
|
45
|
+
it "sets @new_record to false" do
|
46
|
+
model.did_create
|
47
|
+
model.new_record?.should eq(false)
|
48
|
+
end
|
49
|
+
|
50
|
+
it "adds record to class identity_map" do
|
51
|
+
model.id = 863
|
52
|
+
model.did_create
|
53
|
+
model.class.identity_map[863].should eq(model)
|
54
|
+
end
|
55
|
+
|
56
|
+
it "adds record to class record array" do
|
57
|
+
model.class.all.should == []
|
58
|
+
model.did_create
|
59
|
+
model.class.all.should == [model]
|
60
|
+
end
|
61
|
+
|
62
|
+
it "triggers a :create event on the record" do
|
63
|
+
called = false
|
64
|
+
model.on(:create) { called = true }
|
65
|
+
model.did_create
|
66
|
+
called.should eq(true)
|
67
|
+
end
|
68
|
+
|
69
|
+
it "triggers a :create event on the class" do
|
70
|
+
called = false
|
71
|
+
model.class.on(:create) { called = true }
|
72
|
+
model.did_create
|
73
|
+
called.should eq(true)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
describe ".all" do
|
78
|
+
it "is a record array of all models" do
|
79
|
+
model.class.all.should == []
|
80
|
+
model.did_create
|
81
|
+
model.class.all.should == [model]
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
data/spec/model_spec.rb
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Vienna::Model do
|
4
|
+
describe ".new" do
|
5
|
+
it "should set @new_record to true" do
|
6
|
+
SimpleModel.new.new_record?.should eq(true)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
describe ".primary_key" do
|
11
|
+
it "should have a default primary key" do
|
12
|
+
SimpleModel.primary_key.should eq(:id)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should be changeable" do
|
16
|
+
AdvancedModel.primary_key.should eq(:title)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe ".load" do
|
21
|
+
it "raises an ArgumentError if no id present" do
|
22
|
+
lambda {
|
23
|
+
SimpleModel.load({})
|
24
|
+
}.should raise_error(ArgumentError)
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should be able to load models with own primary_key" do
|
28
|
+
AdvancedModel.load(title: 100).should be_kind_of(AdvancedModel)
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should set @new_record to false on the model" do
|
32
|
+
model = SimpleModel.load(id: 42)
|
33
|
+
model.new_record?.should eq(false)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should cache model" do
|
37
|
+
SimpleModel.identity_map[8].should be_nil
|
38
|
+
model = SimpleModel.load(id: 8)
|
39
|
+
SimpleModel.identity_map[8].should eq(model)
|
40
|
+
end
|
41
|
+
|
42
|
+
it "saves the model in .all record array" do
|
43
|
+
SimpleModel.all.should == []
|
44
|
+
model = SimpleModel.load(id: 10)
|
45
|
+
SimpleModel.all.should == [model]
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should update existing models" do
|
49
|
+
foo = SimpleModel.load(id: 9, name: 'Adam')
|
50
|
+
bar = SimpleModel.load(id: 9, name: 'Beynon')
|
51
|
+
foo.should equal(bar)
|
52
|
+
foo.name.should eq("Beynon")
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe ".load_json" do
|
57
|
+
it "should load a model from native json/js object" do
|
58
|
+
obj = `{"id": 13, "name": "Bob"}`
|
59
|
+
model = SimpleModel.load_json obj
|
60
|
+
model.id.should eq(13)
|
61
|
+
model.name.should eq("Bob")
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe ".adapter" do
|
66
|
+
it "sets a new instance of the passed adapter as @adapter" do
|
67
|
+
klass = Class.new(Vienna::Model) do
|
68
|
+
adapter Vienna::Adapter
|
69
|
+
end
|
70
|
+
|
71
|
+
klass.instance_variable_get(:@adapter).should be_kind_of(Vienna::Adapter)
|
72
|
+
end
|
73
|
+
|
74
|
+
it "returns the adapter set on the model subclass" do
|
75
|
+
klass = Class.new(Vienna::Model) { adapter Vienna::Adapter }
|
76
|
+
klass.adapter.should be_kind_of(Vienna::Adapter)
|
77
|
+
end
|
78
|
+
|
79
|
+
it "raises an error when no adapter set on model subclass" do
|
80
|
+
klass = Class.new(Vienna::Model)
|
81
|
+
lambda { klass.adapter }.should raise_error(Exception)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'vienna/observable_array'
|
3
|
+
|
4
|
+
class ObservableArraySpec
|
5
|
+
include Vienna::ObservableArray
|
6
|
+
end
|
7
|
+
|
8
|
+
describe Vienna::ObservableArray do
|
9
|
+
let(:klass) { ObservableArraySpec }
|
10
|
+
let(:empty) { klass.new }
|
11
|
+
let(:foobar) { klass.new([:foo, :bar]) }
|
12
|
+
|
13
|
+
describe ".new" do
|
14
|
+
it "takes an optional content" do
|
15
|
+
klass.new.content.should == []
|
16
|
+
klass.new([1, 2, 3]).content.should == [1, 2, 3]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "#<<" do
|
21
|
+
it "pushes each object into the content" do
|
22
|
+
empty << :foo
|
23
|
+
empty << :bar
|
24
|
+
empty << :baz
|
25
|
+
|
26
|
+
empty.content.should == [:foo, :bar, :baz]
|
27
|
+
end
|
28
|
+
|
29
|
+
it "generates a change event for starting index and single added item" do
|
30
|
+
expect(empty).to receive(:array_content_did_change).once.with(0, 0, 1)
|
31
|
+
empty << :some_object
|
32
|
+
|
33
|
+
expect(foobar).to receive(:array_content_did_change).once.with(2, 0, 1)
|
34
|
+
foobar << :other_object
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe "#delete" do
|
39
|
+
it "deletes the object from the content array" do
|
40
|
+
foobar.delete :foo
|
41
|
+
foobar.content.should == [:bar]
|
42
|
+
end
|
43
|
+
|
44
|
+
it "notifies observers with single remove object" do
|
45
|
+
expect(foobar).to receive(:array_content_did_change).once.with(1, 1, 0)
|
46
|
+
foobar.delete :bar
|
47
|
+
end
|
48
|
+
|
49
|
+
it "has no effect when object not present" do
|
50
|
+
expect(foobar).to_not receive(:array_content_did_change)
|
51
|
+
foobar.delete :baz
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe "#insert" do
|
56
|
+
it "adds the object to the content array at specified index" do
|
57
|
+
empty.insert 0, :foo
|
58
|
+
empty.content.should == [:foo]
|
59
|
+
|
60
|
+
foobar.insert 1, :baz
|
61
|
+
foobar.content.should == [:foo, :baz, :bar]
|
62
|
+
end
|
63
|
+
|
64
|
+
it "notifies observers with single added object at index" do
|
65
|
+
expect(empty).to receive(:array_content_did_change).once.with(0, 0, 1)
|
66
|
+
empty.insert 0, :omg
|
67
|
+
|
68
|
+
expect(foobar).to receive(:array_content_did_change).once.with(1, 0, 1)
|
69
|
+
foobar.insert 1, :wow
|
70
|
+
end
|
71
|
+
|
72
|
+
it "raises an ArgumentError for index out of range" do
|
73
|
+
expect { empty.insert 2, :foo }.to raise_error(ArgumentError)
|
74
|
+
expect { foobar.insert 90, :foo }.to raise_error(ArgumentError)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
describe "#clear" do
|
79
|
+
it "removes all items from the content array" do
|
80
|
+
empty.clear
|
81
|
+
empty.content.should == []
|
82
|
+
|
83
|
+
foobar.clear
|
84
|
+
foobar.content.should == []
|
85
|
+
end
|
86
|
+
|
87
|
+
it "notifies observers to remove all existing objects" do
|
88
|
+
expect(empty).to receive(:array_content_did_change).once.with(0, 0, 0)
|
89
|
+
empty.clear
|
90
|
+
|
91
|
+
expect(foobar).to receive(:array_content_did_change).once.with(0, 2, 0)
|
92
|
+
foobar.clear
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
describe "#array_content_did_change" do
|
97
|
+
it "triggers a :size attribute change" do
|
98
|
+
called = false
|
99
|
+
empty.add_observer(:size) { called = true }
|
100
|
+
empty.clear
|
101
|
+
|
102
|
+
called.should == true
|
103
|
+
end
|
104
|
+
|
105
|
+
it "triggers a :content attribute change" do
|
106
|
+
called = false
|
107
|
+
empty.add_observer(:content) { called = true }
|
108
|
+
empty.clear
|
109
|
+
|
110
|
+
called.should == true
|
111
|
+
end
|
112
|
+
|
113
|
+
it "triggers a :empty? attribute change" do
|
114
|
+
called = false
|
115
|
+
empty.add_observer(:empty?) { called = true }
|
116
|
+
empty.clear
|
117
|
+
|
118
|
+
called.should == true
|
119
|
+
end
|
120
|
+
|
121
|
+
it "notifies each observer added using add_array_observer" do
|
122
|
+
observer = double(:observer)
|
123
|
+
expect(observer).to receive(:array_did_change).once.with(empty, 0, 0, 0)
|
124
|
+
|
125
|
+
empty.add_array_observer observer
|
126
|
+
|
127
|
+
empty.clear
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'vienna/observable'
|
3
|
+
|
4
|
+
class ObservableSpec
|
5
|
+
include Vienna::Observable
|
6
|
+
|
7
|
+
attr_accessor :foo, :bar, :baz
|
8
|
+
|
9
|
+
def baz=(b)
|
10
|
+
@baz = b + 10
|
11
|
+
end
|
12
|
+
|
13
|
+
def bar=(b)
|
14
|
+
@bar = b
|
15
|
+
"#bar"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe Vienna::Observable do
|
20
|
+
|
21
|
+
let(:object) { ObservableSpec.new }
|
22
|
+
|
23
|
+
describe "#replace_writer_for" do
|
24
|
+
it "still calls the original method using super" do
|
25
|
+
object.replace_writer_for(:foo)
|
26
|
+
object.baz = 100
|
27
|
+
object.baz.should eq(110)
|
28
|
+
end
|
29
|
+
|
30
|
+
it "does not add a setter unless an existing attribute= method exists" do
|
31
|
+
object.replace_writer_for(:bob)
|
32
|
+
object.should_not respond_to(:bob=)
|
33
|
+
end
|
34
|
+
|
35
|
+
it "the new writer calls attribute_did_change with attribute name" do
|
36
|
+
object.replace_writer_for(:foo)
|
37
|
+
def object.attribute_did_change(attribute); @_called = attribute; end
|
38
|
+
|
39
|
+
object.instance_variable_get(:@_called).should be_nil
|
40
|
+
object.foo = 100
|
41
|
+
object.instance_variable_get(:@_called).should eq(:foo)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe "#add_observer" do
|
46
|
+
it "handlers can be added to observe specific attributes" do
|
47
|
+
count = 0
|
48
|
+
object.add_observer(:foo) { count += 1 }
|
49
|
+
|
50
|
+
object.foo = 100
|
51
|
+
count.should eq(1)
|
52
|
+
|
53
|
+
object.foo = 150
|
54
|
+
count.should eq(2)
|
55
|
+
end
|
56
|
+
|
57
|
+
it "allows more than one handler to be added for an attribute" do
|
58
|
+
result = []
|
59
|
+
object.add_observer(:foo) { result << :first }
|
60
|
+
object.add_observer(:foo) { result << :second }
|
61
|
+
|
62
|
+
object.foo = 42
|
63
|
+
result.should eq([:first, :second])
|
64
|
+
|
65
|
+
object.foo = 3.142
|
66
|
+
result.should eq([:first, :second, :first, :second])
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe "#attribute_did_change" do
|
71
|
+
it "does not break for an object with no observers" do
|
72
|
+
object.attribute_did_change(:foo_bar)
|
73
|
+
end
|
74
|
+
|
75
|
+
it "does not break when given an attriubte with no observers (but another observable exists)" do
|
76
|
+
object.add_observer(:foo) {}
|
77
|
+
object.attribute_did_change(:bar)
|
78
|
+
end
|
79
|
+
|
80
|
+
it "passes new value to each handler" do
|
81
|
+
result = nil
|
82
|
+
object.add_observer(:foo) { |val| result = val }
|
83
|
+
|
84
|
+
object.foo = 42
|
85
|
+
result.should eq(42)
|
86
|
+
|
87
|
+
object.foo = 3.142
|
88
|
+
result.should eq(3.142)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
describe "#remove_observer" do
|
93
|
+
it "has no effect when no observers setup for attribute" do
|
94
|
+
object.remove_observer(:foo, proc {})
|
95
|
+
end
|
96
|
+
|
97
|
+
it "has no effect if the handler doesnt exist for attribute" do
|
98
|
+
p = proc {}
|
99
|
+
object.add_observer(:foo) {}
|
100
|
+
object.remove_observer(:foo, p)
|
101
|
+
end
|
102
|
+
|
103
|
+
it "removes an existing handler for given attribute" do
|
104
|
+
count = 0
|
105
|
+
handler = proc { count += 1 }
|
106
|
+
object.add_observer(:foo, &handler)
|
107
|
+
|
108
|
+
object.foo = 42
|
109
|
+
count.should eq(1)
|
110
|
+
|
111
|
+
object.remove_observer(:foo, handler)
|
112
|
+
object.foo = 49
|
113
|
+
count.should eq(1)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|