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.
- checksums.yaml +7 -0
- data/.coveralls.yml +2 -0
- data/.gitignore +9 -0
- data/.metrics +9 -0
- data/.rspec +2 -0
- data/.rubocop.yml +2 -0
- data/.travis.yml +15 -0
- data/.yardopts +3 -0
- data/CHANGELOG.md +0 -0
- data/Gemfile +5 -0
- data/Guardfile +14 -0
- data/LICENSE +21 -0
- data/README.md +141 -0
- data/Rakefile +27 -0
- data/config/metrics/STYLEGUIDE +226 -0
- data/config/metrics/cane.yml +5 -0
- data/config/metrics/churn.yml +6 -0
- data/config/metrics/flay.yml +2 -0
- data/config/metrics/metric_fu.yml +15 -0
- data/config/metrics/reek.yml +1 -0
- data/config/metrics/roodi.yml +24 -0
- data/config/metrics/rubocop.yml +76 -0
- data/config/metrics/saikuro.yml +3 -0
- data/config/metrics/simplecov.yml +8 -0
- data/config/metrics/yardstick.yml +37 -0
- data/informator.gemspec +23 -0
- data/lib/informator.rb +82 -0
- data/lib/informator/event.rb +80 -0
- data/lib/informator/reporter.rb +67 -0
- data/lib/informator/subscriber.rb +88 -0
- data/lib/informator/version.rb +9 -0
- data/spec/integration/informator_spec.rb +147 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/unit/informator/event_spec.rb +71 -0
- data/spec/unit/informator/reporter_spec.rb +75 -0
- data/spec/unit/informator/subscriber_spec.rb +86 -0
- metadata +100 -0
@@ -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,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
|
data/spec/spec_helper.rb
ADDED
@@ -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
|