informator 0.0.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.
@@ -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