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
@@ -1,54 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
require "active_model"
|
3
|
-
|
4
|
-
module ServiceObjects
|
5
|
-
|
6
|
-
module Helpers
|
7
|
-
|
8
|
-
# Features for service attributes validation
|
9
|
-
#
|
10
|
-
# @note
|
11
|
-
# A target class should **include** the module
|
12
|
-
#
|
13
|
-
# @see http://apidock.com/rails/v4.1.8/ActiveModel/Validations
|
14
|
-
# ActiveModel::Validations
|
15
|
-
module Validations
|
16
|
-
|
17
|
-
# @!method valid?
|
18
|
-
# Runs validations and checks if the object is valid
|
19
|
-
# @return [Boolean]
|
20
|
-
|
21
|
-
# Raises <tt>ServiceObjects::Invalid</tt> when {#valid?} method fails
|
22
|
-
#
|
23
|
-
# Mutates the current object by populating its messages
|
24
|
-
# with errors, added by {#valid?}
|
25
|
-
#
|
26
|
-
# @raise [ServiceObjects::Invalid]
|
27
|
-
# when a validation fails
|
28
|
-
#
|
29
|
-
# @return [self] (not changed)
|
30
|
-
# when a validation passes
|
31
|
-
def validate!
|
32
|
-
return self if valid?
|
33
|
-
__errors__.each { |text| add_message text: text, type: "error" }
|
34
|
-
fail Invalid.new(self)
|
35
|
-
end
|
36
|
-
|
37
|
-
private
|
38
|
-
|
39
|
-
# @!parse include ServiceObjects::Helpers::Messages
|
40
|
-
# @!parse include ActiveModel::Validations
|
41
|
-
def self.included(klass)
|
42
|
-
klass.include ActiveModel::Validations, Messages
|
43
|
-
super
|
44
|
-
end
|
45
|
-
|
46
|
-
def __errors__
|
47
|
-
errors.messages.values.flatten
|
48
|
-
end
|
49
|
-
|
50
|
-
end # module Validations
|
51
|
-
|
52
|
-
end # module Helpers
|
53
|
-
|
54
|
-
end # module ServiceObjects
|
@@ -1,55 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
module ServiceObjects
|
4
|
-
|
5
|
-
# The exception to be risen by invalid services
|
6
|
-
#
|
7
|
-
# @example
|
8
|
-
# object = ServiceObjects::Base.new
|
9
|
-
# fail ServiceObjects::Invalid.new object
|
10
|
-
class Invalid < ::RuntimeError
|
11
|
-
|
12
|
-
# @!scope class
|
13
|
-
# @!method new(object)
|
14
|
-
#
|
15
|
-
# Constructs the exception for given service object
|
16
|
-
#
|
17
|
-
# @example (see ServiceObjects::Invalid)
|
18
|
-
#
|
19
|
-
# @param [#messages] object
|
20
|
-
#
|
21
|
-
# @return [ServiceObjects::Invalid]
|
22
|
-
def initialize(object)
|
23
|
-
@object = object
|
24
|
-
validate
|
25
|
-
end
|
26
|
-
|
27
|
-
# @!attribute [r] object
|
28
|
-
# Invalid service object
|
29
|
-
#
|
30
|
-
# @return [#messages]
|
31
|
-
attr_reader :object
|
32
|
-
|
33
|
-
# The array of messages from the invalid {#object}
|
34
|
-
#
|
35
|
-
# @return [Array<ServiceObjects::Message>]
|
36
|
-
def messages
|
37
|
-
Array(object.messages).flatten
|
38
|
-
end
|
39
|
-
|
40
|
-
private
|
41
|
-
|
42
|
-
# Checks if the {#object} respond to {#messages}
|
43
|
-
#
|
44
|
-
# @raise [TypeError]
|
45
|
-
# when the validation fails
|
46
|
-
#
|
47
|
-
# @return [undefined]
|
48
|
-
def validate
|
49
|
-
return if object.respond_to? :messages
|
50
|
-
fail TypeError.new "#{ object.inspect } doesn't respond to #messages"
|
51
|
-
end
|
52
|
-
|
53
|
-
end # class Invalid
|
54
|
-
|
55
|
-
end # module ServiceObjects
|
data/lib/service_objects/null.rb
DELETED
@@ -1,26 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
require "naught"
|
3
|
-
|
4
|
-
module ServiceObjects #:nodoc:
|
5
|
-
|
6
|
-
# Implements Null Object pattern
|
7
|
-
#
|
8
|
-
# @note
|
9
|
-
# The constant is a singleton black hole object that returns
|
10
|
-
# itself to any method call
|
11
|
-
#
|
12
|
-
# @example
|
13
|
-
# ServiceObjects::NULL.respond_to? :arbitrary_method
|
14
|
-
# # => true
|
15
|
-
#
|
16
|
-
# ServiceObjects::NULL.arbitrary_method
|
17
|
-
# # => ServiceObjects::NULL
|
18
|
-
#
|
19
|
-
# @see https://github.com/avdi/naught
|
20
|
-
# documentation for the 'naught' gem by Avdi Grimm
|
21
|
-
NULL = Naught.build do |config|
|
22
|
-
config.black_hole
|
23
|
-
config.singleton
|
24
|
-
end.instance
|
25
|
-
|
26
|
-
end # module ServiceObjects
|
@@ -1,69 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
require "hexx-cli"
|
3
|
-
|
4
|
-
module ServiceObjects
|
5
|
-
|
6
|
-
module Parsers
|
7
|
-
|
8
|
-
# Parses and decorates a string, describing a notification
|
9
|
-
#
|
10
|
-
# @example
|
11
|
-
# result = ServiceObjects::Parsers::Dependency.new "add_item{get_item}"
|
12
|
-
# result.name # => add_item
|
13
|
-
# result.type # => GetItem
|
14
|
-
# result.listener # => AddItemListener
|
15
|
-
class Dependency
|
16
|
-
|
17
|
-
# @!scope class
|
18
|
-
# @!method new(source)
|
19
|
-
# Constructs the object from source
|
20
|
-
#
|
21
|
-
# @param [#to_s] source
|
22
|
-
#
|
23
|
-
# @return [Hexx::Services::Parsers::Dependency]
|
24
|
-
def initialize(source)
|
25
|
-
@source = source.to_s
|
26
|
-
end
|
27
|
-
|
28
|
-
# The name for the dependency default implementation
|
29
|
-
#
|
30
|
-
# @return [String]
|
31
|
-
def name
|
32
|
-
@name ||= injector.item
|
33
|
-
end
|
34
|
-
|
35
|
-
# The type for the dependency default implementation
|
36
|
-
#
|
37
|
-
# @return [String]
|
38
|
-
def type
|
39
|
-
@type ||= injection.type
|
40
|
-
end
|
41
|
-
|
42
|
-
# The listener name for the dependency
|
43
|
-
#
|
44
|
-
# @return [String]
|
45
|
-
def listener
|
46
|
-
@listener ||= "#{ injector.const }Listener"
|
47
|
-
end
|
48
|
-
|
49
|
-
private
|
50
|
-
|
51
|
-
attr_reader :source
|
52
|
-
|
53
|
-
def matcher
|
54
|
-
@matcher ||= source.match(/^(.+){(.+)}$/)
|
55
|
-
end
|
56
|
-
|
57
|
-
def injector
|
58
|
-
@injector ||= Hexx::CLI::Name.new matcher[1]
|
59
|
-
end
|
60
|
-
|
61
|
-
def injection
|
62
|
-
@injection ||= Hexx::CLI::Name.new matcher[2]
|
63
|
-
end
|
64
|
-
|
65
|
-
end # class Dependency
|
66
|
-
|
67
|
-
end # module Parsers
|
68
|
-
|
69
|
-
end # module ServiceObjects
|
@@ -1,85 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
require "extlib"
|
3
|
-
|
4
|
-
module ServiceObjects
|
5
|
-
|
6
|
-
module Parsers
|
7
|
-
|
8
|
-
# Parses and decorates a string, describing a notification
|
9
|
-
#
|
10
|
-
# @example
|
11
|
-
# result = ServiceObjects::Parsers::Notification.new "FOund:iTem:messages"
|
12
|
-
# result.name # => found
|
13
|
-
# result.arguments # => ["item", "messages"]
|
14
|
-
# result.non_messages # => ["item"]
|
15
|
-
# result.error? # => false
|
16
|
-
# result.publish_messages? # => true
|
17
|
-
class Notification
|
18
|
-
|
19
|
-
# @!scope class
|
20
|
-
# @!method new(source)
|
21
|
-
# Initializes the object from source string
|
22
|
-
#
|
23
|
-
# @param [#to_s] source
|
24
|
-
#
|
25
|
-
# @return [ServiceObjects::Parsers::Notification]
|
26
|
-
def initialize(source)
|
27
|
-
@source = source.to_s
|
28
|
-
end
|
29
|
-
|
30
|
-
# The name for the parsed value
|
31
|
-
# @return [String]
|
32
|
-
def name
|
33
|
-
@name ||= list.first
|
34
|
-
end
|
35
|
-
|
36
|
-
# The type of exception to be raised and catched by service object
|
37
|
-
# @return [String]
|
38
|
-
def exception
|
39
|
-
@exception ||= name.camel_case
|
40
|
-
end
|
41
|
-
|
42
|
-
# The list of published values
|
43
|
-
# @return [Array<String>]
|
44
|
-
def args
|
45
|
-
@args ||= Array(list[1..-1])
|
46
|
-
end
|
47
|
-
|
48
|
-
# The list of pulished values except for messages
|
49
|
-
# @return [Array<String>]
|
50
|
-
def non_messages
|
51
|
-
@non_messages ||= args.reject { |item| item == "messages" }
|
52
|
-
end
|
53
|
-
|
54
|
-
# Checks whether a notification reports an error
|
55
|
-
# @return [Boolean]
|
56
|
-
def error?
|
57
|
-
name == "error"
|
58
|
-
end
|
59
|
-
|
60
|
-
# Checks whether a notification publishes messages
|
61
|
-
# @return [Boolean]
|
62
|
-
def publish_messages?
|
63
|
-
args.include? "messages"
|
64
|
-
end
|
65
|
-
|
66
|
-
# Returns value to order notifications by
|
67
|
-
# @return [0] if a notification is successful
|
68
|
-
# @return [1] if a notification is an error
|
69
|
-
def order
|
70
|
-
@order = error? ? 1 : 0
|
71
|
-
end
|
72
|
-
|
73
|
-
private
|
74
|
-
|
75
|
-
attr_reader :source
|
76
|
-
|
77
|
-
def list
|
78
|
-
@list ||= source.scan(/\w+/).map(&:snake_case)
|
79
|
-
end
|
80
|
-
|
81
|
-
end # module ServiceObjects
|
82
|
-
|
83
|
-
end # module Parsers
|
84
|
-
|
85
|
-
end # class Notification
|
@@ -1,75 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
require "rspec/mocks"
|
3
|
-
|
4
|
-
module ServiceObjects
|
5
|
-
|
6
|
-
# Collection of helpers and matchers for testing services
|
7
|
-
#
|
8
|
-
# @example
|
9
|
-
# require "service_objects/rspec"
|
10
|
-
# describe MyService do
|
11
|
-
# include ServiceObjects::RSpec
|
12
|
-
# end
|
13
|
-
module RSpec
|
14
|
-
include ::RSpec::Mocks::ExampleMethods
|
15
|
-
|
16
|
-
# Params to be given to service object constructor
|
17
|
-
#
|
18
|
-
# @return [Hash]
|
19
|
-
def params
|
20
|
-
@params ||= {}
|
21
|
-
end
|
22
|
-
|
23
|
-
# The service object of described_class, that subscribed the listener
|
24
|
-
#
|
25
|
-
# @return [ServiceObjects::Base]
|
26
|
-
def service
|
27
|
-
@service ||= described_class.new(params).subscribe(listener)
|
28
|
-
end
|
29
|
-
|
30
|
-
# The spy object to listen a service's notifications
|
31
|
-
#
|
32
|
-
# @return [::RSpec::Mocks::Double]
|
33
|
-
def listener
|
34
|
-
spy
|
35
|
-
end
|
36
|
-
|
37
|
-
# Makes given dependency to construct object when called with params
|
38
|
-
#
|
39
|
-
# @example
|
40
|
-
# before { inject(:get_item, with: { name: :foo }, constructs: foo) }
|
41
|
-
# service.get_item.new(name: :foo) # => foo
|
42
|
-
#
|
43
|
-
# @return [undefined]
|
44
|
-
def inject(dependency, with: {}, constructs: service_double)
|
45
|
-
klass = Class.new ServiceObjects::Base
|
46
|
-
allow(klass).to receive(:new).with(with).and_return constructs
|
47
|
-
allow(service).to receive(dependency).and_return klass
|
48
|
-
end
|
49
|
-
|
50
|
-
# Mock for another services, the service under test depends from
|
51
|
-
#
|
52
|
-
# The block contains the code to be executed on service #run.
|
53
|
-
#
|
54
|
-
# @example
|
55
|
-
# let(:another_service) { service_double(:foo) { |name| publish name } }
|
56
|
-
# another_service.run
|
57
|
-
# # another_service sends :foo to its listeners
|
58
|
-
#
|
59
|
-
# @param [Array<Object>] args
|
60
|
-
# the list of arguments for the block
|
61
|
-
# @param [Proc] block
|
62
|
-
# the block to be yielded by #run method
|
63
|
-
#
|
64
|
-
# @return [ServiceObjects::Base]
|
65
|
-
def service_double(*args, &block)
|
66
|
-
object = ServiceObjects::Base.new
|
67
|
-
allow(object).to receive(:run) do
|
68
|
-
object.instance_exec(*args, &block) if block_given?
|
69
|
-
end
|
70
|
-
object
|
71
|
-
end
|
72
|
-
|
73
|
-
end
|
74
|
-
|
75
|
-
end # module ServiceObjects
|
@@ -1,34 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
module ServiceObjects
|
4
|
-
|
5
|
-
# Utilitites for ServiceObjects module
|
6
|
-
module Utils
|
7
|
-
|
8
|
-
# Constructor of hash with keys, symbolized at any level
|
9
|
-
#
|
10
|
-
# @example
|
11
|
-
# NormalHash.from { "foo" => "bar", "bar" => { "baz", "bar" => "baz" } }
|
12
|
-
# # => { foo: "baz", bar: { "baz", bar: "baz" } }
|
13
|
-
#
|
14
|
-
# @api private
|
15
|
-
module NormalHash
|
16
|
-
|
17
|
-
# Recursively symbolizes keys of hash at any level
|
18
|
-
#
|
19
|
-
# @param [Hash] hash
|
20
|
-
#
|
21
|
-
# @return [Hash]
|
22
|
-
def self.from(hash)
|
23
|
-
return {} unless hash.is_a? Hash
|
24
|
-
|
25
|
-
hash.inject({}) do |out, (key, val)|
|
26
|
-
out.merge(key.to_sym => (val.is_a?(Hash) ? from(val) : val))
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
end # class NormalHash
|
31
|
-
|
32
|
-
end # module Utils
|
33
|
-
|
34
|
-
end # module ServiceObjects
|
data/spec/tests/base_spec.rb
DELETED
@@ -1,43 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
describe ServiceObjects::Base do
|
4
|
-
|
5
|
-
let(:publisher) { Wisper::Publisher }
|
6
|
-
let(:helpers) { ServiceObjects::Helpers }
|
7
|
-
|
8
|
-
it "is Dependable" do
|
9
|
-
expect(described_class).to be_kind_of helpers::Dependable
|
10
|
-
end
|
11
|
-
|
12
|
-
it "is Parameterized" do
|
13
|
-
expect(described_class).to be_kind_of helpers::Parameterized
|
14
|
-
end
|
15
|
-
|
16
|
-
it "includes Messages helper" do
|
17
|
-
expect(described_class).to include helpers::Messages
|
18
|
-
end
|
19
|
-
|
20
|
-
it "includes Parameters helper" do
|
21
|
-
expect(described_class).to include helpers::Parameters
|
22
|
-
end
|
23
|
-
|
24
|
-
it "includes Validations helper" do
|
25
|
-
expect(described_class).to include helpers::Validations
|
26
|
-
end
|
27
|
-
|
28
|
-
it "includes Exceptions helper" do
|
29
|
-
expect(described_class).to include helpers::Exceptions
|
30
|
-
end
|
31
|
-
|
32
|
-
it "includes Wisper::Publisher" do
|
33
|
-
expect(described_class).to include publisher
|
34
|
-
end
|
35
|
-
|
36
|
-
describe "#run" do
|
37
|
-
|
38
|
-
it "is defined" do
|
39
|
-
expect(subject).to respond_to(:run).with(0).arguments
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
end # describe ServiceObjects::Base
|