service_objects 0.1.0 → 1.0.0
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 +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"
|