service_objects 0.1.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.coveralls.yml +1 -0
- data/.metrics +1 -0
- data/.travis.yml +9 -1
- data/.yardopts +1 -1
- data/Gemfile +1 -1
- data/Guardfile +29 -8
- data/LICENSE +1 -1
- data/README.md +179 -342
- data/Rakefile +3 -3
- data/config/metrics/churn.yml +1 -1
- data/config/metrics/flay.yml +1 -1
- data/config/metrics/metric_fu.yml +1 -0
- data/config/metrics/rubocop.yml +4 -4
- data/config/metrics/simplecov.yml +1 -1
- data/lib/service_objects.rb +6 -9
- data/lib/service_objects/base.rb +190 -17
- data/lib/service_objects/listener.rb +21 -75
- data/lib/service_objects/message.rb +15 -96
- data/lib/service_objects/version.rb +1 -1
- data/service_objects.gemspec +11 -9
- data/spec/lib/base_spec.rb +247 -0
- data/spec/lib/listener_spec.rb +96 -0
- data/spec/lib/message_spec.rb +48 -0
- data/spec/spec_helper.rb +8 -6
- metadata +56 -93
- data/bin/service +0 -17
- data/config/metrics/pippi.yml +0 -3
- data/lib/service_objects/cli.rb +0 -117
- data/lib/service_objects/cli/locale.erb +0 -20
- data/lib/service_objects/cli/service.erb +0 -125
- data/lib/service_objects/cli/spec.erb +0 -87
- data/lib/service_objects/helpers.rb +0 -17
- data/lib/service_objects/helpers/dependable.rb +0 -63
- data/lib/service_objects/helpers/exceptions.rb +0 -64
- data/lib/service_objects/helpers/messages.rb +0 -95
- data/lib/service_objects/helpers/parameterized.rb +0 -85
- data/lib/service_objects/helpers/parameters.rb +0 -71
- data/lib/service_objects/helpers/validations.rb +0 -54
- data/lib/service_objects/invalid.rb +0 -55
- data/lib/service_objects/null.rb +0 -26
- data/lib/service_objects/parsers.rb +0 -13
- data/lib/service_objects/parsers/dependency.rb +0 -69
- data/lib/service_objects/parsers/notification.rb +0 -85
- data/lib/service_objects/rspec.rb +0 -75
- data/lib/service_objects/utils/normal_hash.rb +0 -34
- data/spec/tests/base_spec.rb +0 -43
- data/spec/tests/bin/service_spec.rb +0 -18
- data/spec/tests/cli_spec.rb +0 -179
- data/spec/tests/helpers/dependable_spec.rb +0 -77
- data/spec/tests/helpers/exceptions_spec.rb +0 -112
- data/spec/tests/helpers/messages_spec.rb +0 -64
- data/spec/tests/helpers/parameterized_spec.rb +0 -136
- data/spec/tests/helpers/parameters_spec.rb +0 -71
- data/spec/tests/helpers/validations_spec.rb +0 -60
- data/spec/tests/invalid_spec.rb +0 -69
- data/spec/tests/listener_spec.rb +0 -73
- data/spec/tests/message_spec.rb +0 -191
- data/spec/tests/null_spec.rb +0 -17
- data/spec/tests/parsers/dependency_spec.rb +0 -29
- data/spec/tests/parsers/notification_spec.rb +0 -84
- data/spec/tests/rspec_spec.rb +0 -86
- data/spec/tests/utils/normal_hash_spec.rb +0 -16
data/service_objects.gemspec
CHANGED
@@ -13,18 +13,20 @@ Gem::Specification.new do |gem|
|
|
13
13
|
" both the Interactor and Observer design patterns."
|
14
14
|
gem.license = "MIT"
|
15
15
|
|
16
|
-
gem.require_paths = ["lib"]
|
17
16
|
gem.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
|
18
|
-
gem.executables = ["service"]
|
19
17
|
gem.test_files = Dir["spec/**/*.rb"]
|
20
18
|
gem.extra_rdoc_files = Dir["README.md", "LICENSE"]
|
19
|
+
gem.require_paths = ["lib"]
|
21
20
|
|
22
|
-
gem.required_ruby_version = "~> 2.
|
23
|
-
|
24
|
-
gem.add_runtime_dependency "
|
25
|
-
gem.add_runtime_dependency "
|
21
|
+
gem.required_ruby_version = "~> 2.0"
|
22
|
+
|
23
|
+
gem.add_runtime_dependency "attestor", "~> 2.2"
|
24
|
+
gem.add_runtime_dependency "attr_coerced", "~> 0.0"
|
25
|
+
gem.add_runtime_dependency "chronicles", "~> 0.0"
|
26
|
+
gem.add_runtime_dependency "virtus", "~> 1.0"
|
26
27
|
gem.add_runtime_dependency "wisper", "~> 1.6"
|
27
|
-
gem.add_runtime_dependency "hexx-cli", "~> 0.0"
|
28
|
-
gem.add_development_dependency "hexx-rspec", "~> 0.3"
|
29
28
|
|
30
|
-
|
29
|
+
gem.add_development_dependency "hexx-cli", "~> 0.0"
|
30
|
+
gem.add_development_dependency "hexx-rspec", "~> 0.4"
|
31
|
+
|
32
|
+
end # Gem::Specification
|
@@ -0,0 +1,247 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
describe ServiceObjects::Base do
|
4
|
+
|
5
|
+
let(:test_class) { Class.new(described_class) }
|
6
|
+
|
7
|
+
before { ServiceObjects::Test = test_class }
|
8
|
+
after { ServiceObjects.send :remove_const, :Test }
|
9
|
+
|
10
|
+
subject { test_class.new }
|
11
|
+
|
12
|
+
describe ".new" do
|
13
|
+
|
14
|
+
it "includes Virtus.model" do
|
15
|
+
expect(subject).to be_kind_of Virtus::Model::Core
|
16
|
+
end
|
17
|
+
|
18
|
+
it "includes Attestor::Validations" do
|
19
|
+
expect(subject).to be_kind_of Attestor::Validations
|
20
|
+
end
|
21
|
+
|
22
|
+
it "includes a Wisper::Publisher" do
|
23
|
+
expect(subject).to be_kind_of Wisper::Publisher
|
24
|
+
end
|
25
|
+
|
26
|
+
it "includes an Attr::Coerced" do
|
27
|
+
expect(subject).to be_kind_of AttrCoerced
|
28
|
+
end
|
29
|
+
|
30
|
+
end # describe .new
|
31
|
+
|
32
|
+
describe ".dependency" do
|
33
|
+
|
34
|
+
let(:implementation) { String }
|
35
|
+
|
36
|
+
context "with default value" do
|
37
|
+
|
38
|
+
before { test_class.dependency :foo, default: implementation }
|
39
|
+
|
40
|
+
it "declares the getter" do
|
41
|
+
expect(subject).to respond_to :foo
|
42
|
+
end
|
43
|
+
|
44
|
+
it "declares the setter" do
|
45
|
+
expect(subject).to respond_to :foo=
|
46
|
+
expect { subject.foo = Object }.to change { subject.foo }.to Object
|
47
|
+
end
|
48
|
+
|
49
|
+
it "sets the default value" do
|
50
|
+
expect(subject.foo).to eq implementation
|
51
|
+
end
|
52
|
+
|
53
|
+
end # context
|
54
|
+
|
55
|
+
context "without default value" do
|
56
|
+
|
57
|
+
before { test_class.dependency :foo }
|
58
|
+
|
59
|
+
it "declares the getter" do
|
60
|
+
expect(subject).to respond_to :foo
|
61
|
+
end
|
62
|
+
|
63
|
+
it "declares the setter" do
|
64
|
+
expect(subject).to respond_to :foo=
|
65
|
+
expect { subject.foo = Object }.to change { subject.foo }.to Object
|
66
|
+
end
|
67
|
+
|
68
|
+
it "sets the default value" do
|
69
|
+
expect(subject.foo).to be_nil
|
70
|
+
end
|
71
|
+
|
72
|
+
end # context
|
73
|
+
|
74
|
+
end # describe .dependency
|
75
|
+
|
76
|
+
describe "#validate" do
|
77
|
+
|
78
|
+
before { allow(subject).to receive :publish }
|
79
|
+
|
80
|
+
it "calls #validate!" do
|
81
|
+
expect(subject).to receive(:validate!).with(:foo)
|
82
|
+
subject.validate :foo
|
83
|
+
end
|
84
|
+
|
85
|
+
context "when #validate! fails" do
|
86
|
+
|
87
|
+
let(:error) { Attestor::InvalidError.new subject, "foo" }
|
88
|
+
|
89
|
+
before { allow(subject).to receive(:validate!) { fail error } }
|
90
|
+
|
91
|
+
it "doesn't fail" do
|
92
|
+
expect { subject.validate :foo }.not_to raise_error
|
93
|
+
end
|
94
|
+
|
95
|
+
it "publishes an error" do
|
96
|
+
expect(subject).to receive(:publish) do |name, *|
|
97
|
+
expect(name).to eq :error
|
98
|
+
end
|
99
|
+
subject.validate :foo
|
100
|
+
end
|
101
|
+
|
102
|
+
it "publishes error messages" do
|
103
|
+
expect(subject).to receive(:publish) do |_, messages|
|
104
|
+
expect(messages).to be_kind_of Array
|
105
|
+
expect(messages.count).to eq 1
|
106
|
+
expect(messages.first.type).to eq :error
|
107
|
+
expect(messages.first.text).to eq "foo"
|
108
|
+
end
|
109
|
+
subject.validate :foo
|
110
|
+
end
|
111
|
+
|
112
|
+
end # context
|
113
|
+
|
114
|
+
context "when #validate! passes" do
|
115
|
+
|
116
|
+
before { allow(subject).to receive(:validate!) }
|
117
|
+
|
118
|
+
it "publishes nothing" do
|
119
|
+
expect(subject).not_to receive(:publish)
|
120
|
+
subject.validate :foo
|
121
|
+
end
|
122
|
+
|
123
|
+
end # context
|
124
|
+
|
125
|
+
end # describe #validate
|
126
|
+
|
127
|
+
describe "#run" do
|
128
|
+
|
129
|
+
it "calls #run!" do
|
130
|
+
expect(subject).to receive(:run!)
|
131
|
+
subject.run
|
132
|
+
end
|
133
|
+
|
134
|
+
it "catches :published" do
|
135
|
+
allow(subject).to receive(:run!) { throw :published }
|
136
|
+
expect { subject.run }.not_to raise_error
|
137
|
+
end
|
138
|
+
|
139
|
+
it "raises NotImplementedError when #run! not implemented" do
|
140
|
+
expect { subject.run }.to raise_error do |err|
|
141
|
+
expect(err).to be_kind_of NotImplementedError
|
142
|
+
expect(err.message).to eq "#{ subject.class.name }#run! not implemented"
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
end # describe #run
|
147
|
+
|
148
|
+
describe "#message" do
|
149
|
+
|
150
|
+
context "with a symbolic key" do
|
151
|
+
|
152
|
+
let(:scope) { %i(service_objects service_objects/test foo) }
|
153
|
+
let(:result) { subject.message :foo, :bar, baz: :qux }
|
154
|
+
|
155
|
+
it "returns the message" do
|
156
|
+
expect(result).to be_kind_of ServiceObjects::Message
|
157
|
+
end
|
158
|
+
|
159
|
+
it "sets the proper message type" do
|
160
|
+
expect(result.type).to eq :foo
|
161
|
+
end
|
162
|
+
|
163
|
+
it "translates the key" do
|
164
|
+
expect(result.text).to eq I18n.t(:bar, scope: scope)
|
165
|
+
end
|
166
|
+
|
167
|
+
it "uses options" do
|
168
|
+
expect(I18n).to receive(:t).with(:bar, scope: scope, baz: :qux)
|
169
|
+
result
|
170
|
+
end
|
171
|
+
|
172
|
+
it "ignores the :scope option" do
|
173
|
+
expect(I18n).to receive(:t).with(:bar, scope: scope)
|
174
|
+
subject.message :foo, :bar, scope: :qux
|
175
|
+
end
|
176
|
+
|
177
|
+
end # context
|
178
|
+
|
179
|
+
context "with non-symbolic key" do
|
180
|
+
|
181
|
+
let(:result) { subject.message :foo, 11, baz: :qux }
|
182
|
+
|
183
|
+
it "returns the message" do
|
184
|
+
expect(result).to be_kind_of ServiceObjects::Message
|
185
|
+
end
|
186
|
+
|
187
|
+
it "sets the proper message type" do
|
188
|
+
expect(result.type).to eq :foo
|
189
|
+
end
|
190
|
+
|
191
|
+
it "stringifies the argument" do
|
192
|
+
expect(result.text).to eq "11"
|
193
|
+
end
|
194
|
+
|
195
|
+
end # context
|
196
|
+
|
197
|
+
end # describe #translate
|
198
|
+
|
199
|
+
describe "#publish" do
|
200
|
+
|
201
|
+
let(:listener) { double foo: nil }
|
202
|
+
before { subject.subscribe listener }
|
203
|
+
|
204
|
+
it "publishes notification" do
|
205
|
+
expect(listener).to receive :foo
|
206
|
+
begin
|
207
|
+
subject.send :publish, :foo
|
208
|
+
rescue
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
it "throws :published" do
|
213
|
+
expect { subject.send :publish, :success }.to raise_error do |error|
|
214
|
+
expect(error.message).to eq "uncaught throw :published"
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
end # describe #publish
|
219
|
+
|
220
|
+
describe "#run_service" do
|
221
|
+
|
222
|
+
let(:service) { double subscribe: nil, run: nil }
|
223
|
+
let(:listener) { double finalize: nil }
|
224
|
+
|
225
|
+
after { subject.run_service service, listener }
|
226
|
+
|
227
|
+
it "subscribes service for the listener" do
|
228
|
+
expect(service).to receive(:subscribe).with(listener, prefix: :on).once
|
229
|
+
end
|
230
|
+
|
231
|
+
it "runs the service" do
|
232
|
+
expect(service).to receive(:run).once
|
233
|
+
end
|
234
|
+
|
235
|
+
it "finalizes the listener" do
|
236
|
+
expect(listener).to receive(:finalize).once
|
237
|
+
end
|
238
|
+
|
239
|
+
it "runs commands in the proper order" do
|
240
|
+
expect(service).to receive(:subscribe).ordered
|
241
|
+
expect(service).to receive(:run).ordered
|
242
|
+
expect(listener).to receive(:finalize).ordered
|
243
|
+
end
|
244
|
+
|
245
|
+
end # describe #run_service
|
246
|
+
|
247
|
+
end # describe ServiceObjects::Attributes
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
describe ServiceObjects::Listener do
|
4
|
+
|
5
|
+
let(:test_class) { Class.new(described_class) { attr_reader :foo } }
|
6
|
+
let(:object) { Class.new { private def bar; end }.new }
|
7
|
+
subject { test_class.new(object) }
|
8
|
+
|
9
|
+
describe "#respond_to?" do
|
10
|
+
|
11
|
+
it "returns true if method defined" do
|
12
|
+
expect(subject).to respond_to :foo
|
13
|
+
end
|
14
|
+
|
15
|
+
it "returns true if method defined by object" do
|
16
|
+
expect(subject.respond_to?(:bar)).to be_falsey
|
17
|
+
expect(subject.respond_to?(:bar, true)).to be_truthy
|
18
|
+
end
|
19
|
+
|
20
|
+
it "returns false if method neither defined by itself nor by object" do
|
21
|
+
expect(subject).not_to respond_to :baz
|
22
|
+
end
|
23
|
+
|
24
|
+
end # describe #respond_to?
|
25
|
+
|
26
|
+
describe "arbitrary method" do
|
27
|
+
|
28
|
+
it "is delegated to object" do
|
29
|
+
expect(object).to receive(:bar)
|
30
|
+
subject.bar
|
31
|
+
end
|
32
|
+
|
33
|
+
end # describe arbitrary method
|
34
|
+
|
35
|
+
describe "#otherwise" do
|
36
|
+
|
37
|
+
it "is defined" do
|
38
|
+
expect(subject).to respond_to :otherwise
|
39
|
+
end
|
40
|
+
|
41
|
+
it "passes" do
|
42
|
+
expect { subject.otherwise }.not_to raise_error
|
43
|
+
end
|
44
|
+
|
45
|
+
end # describe #otherwise
|
46
|
+
|
47
|
+
describe "#finalize" do
|
48
|
+
|
49
|
+
context "[when own method has been called]" do
|
50
|
+
|
51
|
+
before { subject.foo }
|
52
|
+
|
53
|
+
it "calls #otherwise" do
|
54
|
+
expect(subject).to receive(:otherwise)
|
55
|
+
subject.finalize
|
56
|
+
end
|
57
|
+
|
58
|
+
it "doesn't call #otherwise twice" do
|
59
|
+
expect(subject).to receive(:otherwise).once
|
60
|
+
2.times { subject.finalize }
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
context "[when own method has been called]" do
|
65
|
+
|
66
|
+
it "doesn't call it again" do
|
67
|
+
expect(subject).to receive(:otherwise).once
|
68
|
+
subject.otherwise
|
69
|
+
subject.finalize
|
70
|
+
end
|
71
|
+
|
72
|
+
end # context
|
73
|
+
|
74
|
+
context "[when delegated method has been called]" do
|
75
|
+
|
76
|
+
before { subject.bar }
|
77
|
+
|
78
|
+
it "doesn't call #otherwise" do
|
79
|
+
expect(subject).not_to receive(:otherwise)
|
80
|
+
subject.finalize
|
81
|
+
end
|
82
|
+
|
83
|
+
end # context
|
84
|
+
|
85
|
+
context "[when no methods has been called]" do
|
86
|
+
|
87
|
+
it "doesn't call #otherwise" do
|
88
|
+
expect(subject).not_to receive(:otherwise)
|
89
|
+
subject.finalize
|
90
|
+
end
|
91
|
+
|
92
|
+
end # context
|
93
|
+
|
94
|
+
end # describe #finalize
|
95
|
+
|
96
|
+
end # describe ServiceObjects::Listener
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
describe ServiceObjects::Message do
|
4
|
+
|
5
|
+
let(:type) { :foo }
|
6
|
+
let(:text) { "foo" }
|
7
|
+
|
8
|
+
subject { described_class.new type, text }
|
9
|
+
|
10
|
+
describe ".new" do
|
11
|
+
|
12
|
+
it "creates immutable object" do
|
13
|
+
expect(subject).to be_frozen
|
14
|
+
end
|
15
|
+
|
16
|
+
end # describe .new
|
17
|
+
|
18
|
+
describe "#type" do
|
19
|
+
|
20
|
+
it "is initialized" do
|
21
|
+
expect(subject.type).to eq type
|
22
|
+
end
|
23
|
+
|
24
|
+
it "is symbolized" do
|
25
|
+
subject = described_class.new type.to_s, text
|
26
|
+
expect(subject.type).to eq type
|
27
|
+
end
|
28
|
+
|
29
|
+
end # describe #type
|
30
|
+
|
31
|
+
describe "#text" do
|
32
|
+
|
33
|
+
it "is initialized" do
|
34
|
+
expect(subject.text).to eq text
|
35
|
+
end
|
36
|
+
|
37
|
+
it "is stringified" do
|
38
|
+
subject = described_class.new type, text.to_sym
|
39
|
+
expect(subject.text).to eq text
|
40
|
+
end
|
41
|
+
|
42
|
+
it "is immutable" do
|
43
|
+
expect(subject.text).to be_frozen
|
44
|
+
end
|
45
|
+
|
46
|
+
end # describe #text
|
47
|
+
|
48
|
+
end # describe ServiceObjects::Message
|
data/spec/spec_helper.rb
CHANGED
@@ -1,10 +1,12 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
-
|
4
|
-
require "hexx-
|
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
|
5
10
|
|
6
|
-
# Loads
|
7
|
-
Hexx::RSpec.load_metrics_for(self)
|
8
|
-
|
9
|
-
# Loads the code of the module.
|
11
|
+
# Loads the code under test
|
10
12
|
require "service_objects"
|