informator 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,88 @@
1
+ # encoding: utf-8
2
+
3
+ module Informator
4
+
5
+ # Class Subscriber wraps the [#object] along with its [#callback] method
6
+ # to receive event notifications.
7
+ #
8
+ # @example The method [#notify] sends event to the [#object] via [#callback]
9
+ # object = Struct.new(:event).new
10
+ # subscriber = Subscriber.new object, :event=
11
+ # subscriber.frozen? # => true
12
+ #
13
+ # event = Informator::Event.new :success
14
+ # # => #<Event @type=:success @data={} @messages=[]>
15
+ #
16
+ # subscriber.notify event
17
+ # object.event
18
+ # # => #<Event @type=:success @data={} @messages=[]>
19
+ #
20
+ # @api private
21
+ #
22
+ class Subscriber
23
+
24
+ include Comparable
25
+
26
+ # @!attribute [r] object
27
+ #
28
+ # @return [Object] the object to send events to
29
+ #
30
+ attr_reader :object
31
+
32
+ # @!attribute [r] callback
33
+ #
34
+ # @return [Symbol] the name of the object methods to listen to events
35
+ #
36
+ attr_reader :callback
37
+
38
+ # @!scope class
39
+ # @!method new(object, callback)
40
+ # Builds the subscriber for given object and callback
41
+ #
42
+ # @param [Object] object
43
+ # @param [#to_sym] callback (:receive)
44
+ #
45
+ # @return [Informator::Subscriber]
46
+
47
+ # @private
48
+ def initialize(object, callback = :receive)
49
+ @object = object
50
+ @callback = callback.to_sym
51
+ freeze
52
+ end
53
+
54
+ # Sends the event to the subscriber object via its callback
55
+ #
56
+ # @param [Informator::Event] event
57
+ #
58
+ # @return [Informator::Event] published event
59
+ #
60
+ def notify(event)
61
+ object.public_send callback, event
62
+
63
+ event
64
+ end
65
+
66
+ # Compares the subscriber to another one by their objects and callbacks
67
+ #
68
+ # @param [Object] other
69
+ #
70
+ # @return [Boolean]
71
+ #
72
+ def ==(other)
73
+ other.instance_of?(self.class) ? other.value.eql?(value) : false
74
+ end
75
+
76
+ protected
77
+
78
+ # Returns value to compare subscribers by
79
+ #
80
+ # @return [Array]
81
+ #
82
+ def value
83
+ [object, callback]
84
+ end
85
+
86
+ end # class Subscriber
87
+
88
+ end # module Informator
@@ -0,0 +1,9 @@
1
+ # encoding: utf-8
2
+
3
+ module Informator
4
+
5
+ # The semantic version of the module.
6
+ # @see http://semver.org/ Semantic versioning 2.0
7
+ VERSION = "0.0.1".freeze
8
+
9
+ end # module Informator
@@ -0,0 +1,147 @@
1
+ # encoding: utf-8
2
+
3
+ describe Informator do
4
+
5
+ subject(:informator) { klass.new }
6
+
7
+ let(:klass) { Class.new { include Informator } }
8
+ let(:callback) { :on_received }
9
+ let(:listener) do
10
+ double receive: nil, callback => nil, foo: nil, bar: nil, receive: nil
11
+ end
12
+
13
+ before { informator.subscribe listener, callback }
14
+
15
+ describe "#subscribe" do
16
+
17
+ it "subscribes listeners one-by-one" do
18
+ informator.subscribe(listener, :foo).subscribe(listener, :bar)
19
+
20
+ expect(listener).to receive(callback).ordered
21
+ expect(listener).to receive(:foo).ordered
22
+ expect(listener).to receive(:bar).ordered
23
+ informator.publish :info
24
+ end
25
+
26
+ it "doesn't duplicate listener's callbacks" do
27
+ informator.subscribe(listener, callback)
28
+
29
+ expect(listener).to receive(callback).once
30
+ informator.publish :info
31
+ end
32
+
33
+ it "subscribes listener with :receive callback by default" do
34
+ informator.subscribe(listener)
35
+
36
+ expect(listener).to receive(:receive)
37
+ informator.publish :info
38
+ end
39
+
40
+ end # describe #subscribe
41
+
42
+ describe "#remember" do
43
+
44
+ it "keeps remembered events unpublished" do
45
+ expect(listener).not_to receive(callback)
46
+
47
+ informator.remember :info
48
+ end
49
+
50
+ it "remembers events until publishing" do
51
+ expect(listener).to receive(callback).once
52
+
53
+ informator.remember :info
54
+ informator.publish
55
+ end
56
+
57
+ it "returns itself to allow chaining" do
58
+ expect(informator.remember(:info).remember(:alert)).to eql informator
59
+ end
60
+
61
+ end # describe #remember
62
+
63
+ describe "#publish" do
64
+
65
+ it "publishes events" do
66
+ expect(listener).to receive(callback) do |event|
67
+ expect(event).to be_kind_of Informator::Event
68
+ expect(event.type).to eq :alert
69
+ expect(event.messages).to eql %w(foo)
70
+ expect(event.data).to eql(bar: :baz)
71
+ end
72
+
73
+ informator.publish :alert, "foo", bar: :baz
74
+ end
75
+
76
+ it "publishes all remembered events at once" do
77
+ expect(listener).to receive(callback).twice
78
+
79
+ informator.remember :info
80
+ informator.publish :alert
81
+ end
82
+
83
+ it "can publish only remembered events without a new one" do
84
+ expect(listener).to receive(callback).once
85
+
86
+ informator.remember :info
87
+ informator.publish
88
+ end
89
+
90
+ it "returns events having been published" do
91
+ informator.remember :info
92
+ events = informator.publish :alert
93
+
94
+ expect(events).to be_kind_of Array
95
+ expect(events.map(&:class).uniq).to contain_exactly Informator::Event
96
+ expect(events.map(&:type)).to eql [:info, :alert]
97
+ end
98
+
99
+ it "forgets published events" do
100
+ informator.publish :info
101
+ expect(listener).not_to receive(callback)
102
+
103
+ informator.publish
104
+ end
105
+
106
+ end # describe #publish
107
+
108
+ describe "#publish!" do
109
+
110
+ it "publishes all remembered events at once" do
111
+ expect(listener).to receive(callback).twice
112
+
113
+ informator.remember :info
114
+ informator.publish!(:alert) rescue nil
115
+ end
116
+
117
+ it "can publish only remembered events without a new one" do
118
+ expect(listener).to receive(callback).once
119
+
120
+ informator.remember :info
121
+ informator.publish! rescue nil
122
+ end
123
+
124
+ it "forgets published events" do
125
+ informator.publish! :info rescue nil
126
+ expect(listener).not_to receive(callback)
127
+
128
+ informator.publish
129
+ end
130
+
131
+ it "throws :published" do
132
+ expect { informator.publish! }.to raise_error
133
+ expect { catch(:published) { informator.publish! } }.not_to raise_error
134
+ end
135
+
136
+ it "throws events having been published" do
137
+ informator.remember :info
138
+ events = catch(:published) { informator.publish! :alert }
139
+
140
+ expect(events).to be_kind_of Array
141
+ expect(events.map(&:class).uniq).to contain_exactly Informator::Event
142
+ expect(events.map(&:type)).to eql [:info, :alert]
143
+ end
144
+
145
+ end # describe #publish!
146
+
147
+ end # describe Informator
@@ -0,0 +1,12 @@
1
+ # encoding: utf-8
2
+
3
+ begin
4
+ require "hexx-suit"
5
+ Hexx::Suit.load_metrics_for(self)
6
+ rescue LoadError
7
+ require "hexx-rspec"
8
+ Hexx::RSpec.load_metrics_for(self)
9
+ end
10
+
11
+ # Loads the code under test
12
+ require "informator"
@@ -0,0 +1,71 @@
1
+ # encoding: utf-8
2
+
3
+ describe Informator::Event do
4
+
5
+ subject(:event) { described_class.new type, :foo, [:bar], baz: :qux }
6
+
7
+ let(:type) { "success" }
8
+
9
+ describe ".new" do
10
+
11
+ it { is_expected.to be_kind_of Comparable }
12
+ it { is_expected.to be_frozen }
13
+
14
+ end # describe .new
15
+
16
+ describe "#type" do
17
+
18
+ subject { event.type }
19
+ it { is_expected.to eql type.to_sym }
20
+
21
+ end # describe #type
22
+
23
+ describe "#messages" do
24
+
25
+ subject { event.messages }
26
+ it { is_expected.to eql %w(foo bar) }
27
+
28
+ end # describe #messages
29
+
30
+ describe "#data" do
31
+
32
+ subject { event.data }
33
+ it { is_expected.to eq(baz: :qux) }
34
+
35
+ end # describe #data
36
+
37
+ describe "#==" do
38
+
39
+ subject { event == other }
40
+
41
+ context "to event with the same type and data" do
42
+
43
+ let(:other) { Class.new(described_class).new type, baz: :qux }
44
+ it { is_expected.to eql true }
45
+
46
+ end # context
47
+
48
+ context "to event with another type" do
49
+
50
+ let(:other) { described_class.new :error, baz: :qux }
51
+ it { is_expected.to eql false }
52
+
53
+ end # context
54
+
55
+ context "to event with other data" do
56
+
57
+ let(:other) { described_class.new :success, baz: "qux" }
58
+ it { is_expected.to eql false }
59
+
60
+ end # context
61
+
62
+ context "to non-event" do
63
+
64
+ let(:other) { :foo }
65
+ it { is_expected.to eql false }
66
+
67
+ end # context
68
+
69
+ end # describe #==
70
+
71
+ end # describe Informator::Event
@@ -0,0 +1,75 @@
1
+ # encoding: utf-8
2
+
3
+ describe Informator::Reporter do
4
+
5
+ subject(:reporter) { described_class.new }
6
+
7
+ describe ".new" do
8
+
9
+ it { is_expected.to be_frozen }
10
+
11
+ end # describe .new
12
+
13
+ describe "#events" do
14
+
15
+ subject { reporter.events }
16
+ it { is_expected.to eql [] }
17
+
18
+ end # describe #events
19
+
20
+ describe "#remember" do
21
+
22
+ subject { reporter.remember(*options) }
23
+
24
+ let(:options) { [:foo, "bar", baz: :qux] }
25
+ let(:event) { Informator::Event.new(*options) }
26
+
27
+ it "builds the event and adds it to #events" do
28
+ expect { subject }.to change { reporter.events }.by [event]
29
+ end
30
+
31
+ it { is_expected.to eql reporter }
32
+
33
+ end # describe #remember
34
+
35
+ describe "#notify" do
36
+
37
+ subject { reporter.notify(subscribers) }
38
+
39
+ let(:subscribers) { 2.times.map { double notify: nil } }
40
+ let!(:events) do
41
+ %i(foo bar).each(&reporter.method(:remember))
42
+ reporter.events.dup
43
+ end
44
+
45
+ it "notifies every subscriber on all #events" do
46
+ events.each do |event|
47
+ subscribers.each do |subscriber|
48
+ expect(subscriber).to receive(:notify).with(event).ordered
49
+ end
50
+ end
51
+
52
+ subject
53
+ end
54
+
55
+ it "clears #events" do
56
+ expect { subject }.to change { reporter.events }.to []
57
+ end
58
+
59
+ context "when error occured while publishing" do
60
+
61
+ before { allow(subscribers.first).to receive(:notify) { fail } }
62
+
63
+ it "removes tried #events" do
64
+ expect { subject rescue nil }
65
+ .to change { reporter.events }
66
+ .to [events.last]
67
+ end
68
+
69
+ end # context
70
+
71
+ it { is_expected.to eql events }
72
+
73
+ end # describe #notify
74
+
75
+ end # describe Informator::Reporter
@@ -0,0 +1,86 @@
1
+ # encoding: utf-8
2
+
3
+ describe Informator::Subscriber do
4
+
5
+ let(:callback) { "foo" }
6
+ let(:event) { double }
7
+ let(:object) { double callback.to_sym => callback }
8
+
9
+ subject(:subscriber) { described_class.new object, callback }
10
+
11
+ describe ".new" do
12
+
13
+ it { is_expected.to be_kind_of Comparable }
14
+ it { is_expected.to be_frozen }
15
+
16
+ end # describe .new
17
+
18
+ describe "#object" do
19
+
20
+ subject { subscriber.object }
21
+ it { is_expected.to eql object }
22
+
23
+ end # describe #object
24
+
25
+ describe "#callback" do
26
+
27
+ subject { subscriber.callback }
28
+ it { is_expected.to eql callback.to_sym }
29
+
30
+ context "when callback isn't defined" do
31
+
32
+ let(:subscriber) { described_class.new object }
33
+ it { is_expected.to eql :receive }
34
+
35
+ end # context
36
+
37
+ end # describe #callback
38
+
39
+ describe "#notify" do
40
+
41
+ subject { subscriber.notify event }
42
+
43
+ it "sends event to object via callback" do
44
+ expect(object).to receive(callback).with(event)
45
+ subject
46
+ end
47
+
48
+ it { is_expected.to eql event }
49
+
50
+ end # describe #notify
51
+
52
+ describe "#==" do
53
+
54
+ subject { subscriber == other }
55
+
56
+ context "to subscriber with the same object and callback" do
57
+
58
+ let(:other) { subscriber.dup }
59
+ it { is_expected.to eql true }
60
+
61
+ end # context
62
+
63
+ context "to event with another object" do
64
+
65
+ let(:other) { described_class.new object.dup, callback }
66
+ it { is_expected.to eql false }
67
+
68
+ end # context
69
+
70
+ context "to event with other data" do
71
+
72
+ let(:other) { described_class.new object, :other }
73
+ it { is_expected.to eql false }
74
+
75
+ end # context
76
+
77
+ context "to non-subscriber" do
78
+
79
+ let(:other) { :foo }
80
+ it { is_expected.to eql false }
81
+
82
+ end # context
83
+
84
+ end # describe #==
85
+
86
+ end # describe Informator::Subscriber