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.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/.coveralls.yml +1 -0
  3. data/.metrics +1 -0
  4. data/.travis.yml +9 -1
  5. data/.yardopts +1 -1
  6. data/Gemfile +1 -1
  7. data/Guardfile +29 -8
  8. data/LICENSE +1 -1
  9. data/README.md +179 -342
  10. data/Rakefile +3 -3
  11. data/config/metrics/churn.yml +1 -1
  12. data/config/metrics/flay.yml +1 -1
  13. data/config/metrics/metric_fu.yml +1 -0
  14. data/config/metrics/rubocop.yml +4 -4
  15. data/config/metrics/simplecov.yml +1 -1
  16. data/lib/service_objects.rb +6 -9
  17. data/lib/service_objects/base.rb +190 -17
  18. data/lib/service_objects/listener.rb +21 -75
  19. data/lib/service_objects/message.rb +15 -96
  20. data/lib/service_objects/version.rb +1 -1
  21. data/service_objects.gemspec +11 -9
  22. data/spec/lib/base_spec.rb +247 -0
  23. data/spec/lib/listener_spec.rb +96 -0
  24. data/spec/lib/message_spec.rb +48 -0
  25. data/spec/spec_helper.rb +8 -6
  26. metadata +56 -93
  27. data/bin/service +0 -17
  28. data/config/metrics/pippi.yml +0 -3
  29. data/lib/service_objects/cli.rb +0 -117
  30. data/lib/service_objects/cli/locale.erb +0 -20
  31. data/lib/service_objects/cli/service.erb +0 -125
  32. data/lib/service_objects/cli/spec.erb +0 -87
  33. data/lib/service_objects/helpers.rb +0 -17
  34. data/lib/service_objects/helpers/dependable.rb +0 -63
  35. data/lib/service_objects/helpers/exceptions.rb +0 -64
  36. data/lib/service_objects/helpers/messages.rb +0 -95
  37. data/lib/service_objects/helpers/parameterized.rb +0 -85
  38. data/lib/service_objects/helpers/parameters.rb +0 -71
  39. data/lib/service_objects/helpers/validations.rb +0 -54
  40. data/lib/service_objects/invalid.rb +0 -55
  41. data/lib/service_objects/null.rb +0 -26
  42. data/lib/service_objects/parsers.rb +0 -13
  43. data/lib/service_objects/parsers/dependency.rb +0 -69
  44. data/lib/service_objects/parsers/notification.rb +0 -85
  45. data/lib/service_objects/rspec.rb +0 -75
  46. data/lib/service_objects/utils/normal_hash.rb +0 -34
  47. data/spec/tests/base_spec.rb +0 -43
  48. data/spec/tests/bin/service_spec.rb +0 -18
  49. data/spec/tests/cli_spec.rb +0 -179
  50. data/spec/tests/helpers/dependable_spec.rb +0 -77
  51. data/spec/tests/helpers/exceptions_spec.rb +0 -112
  52. data/spec/tests/helpers/messages_spec.rb +0 -64
  53. data/spec/tests/helpers/parameterized_spec.rb +0 -136
  54. data/spec/tests/helpers/parameters_spec.rb +0 -71
  55. data/spec/tests/helpers/validations_spec.rb +0 -60
  56. data/spec/tests/invalid_spec.rb +0 -69
  57. data/spec/tests/listener_spec.rb +0 -73
  58. data/spec/tests/message_spec.rb +0 -191
  59. data/spec/tests/null_spec.rb +0 -17
  60. data/spec/tests/parsers/dependency_spec.rb +0 -29
  61. data/spec/tests/parsers/notification_spec.rb +0 -84
  62. data/spec/tests/rspec_spec.rb +0 -86
  63. 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
@@ -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,13 +0,0 @@
1
- # encoding: utf-8
2
-
3
- module ServiceObjects
4
-
5
- # Collection of CLI arguments parsers
6
- module Parsers
7
-
8
- require_relative "parsers/dependency"
9
- require_relative "parsers/notification"
10
-
11
- end # module ServiceObjects
12
-
13
- end # module Parsers
@@ -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
@@ -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