informator 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|