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/Rakefile
CHANGED
@@ -9,7 +9,7 @@ end
|
|
9
9
|
# Loads bundler tasks
|
10
10
|
Bundler::GemHelper.install_tasks
|
11
11
|
|
12
|
-
# Loads the Hexx::
|
12
|
+
# Loads the Hexx::RSpec and its tasks
|
13
13
|
begin
|
14
14
|
require "hexx-suit"
|
15
15
|
Hexx::Suit.install_tasks
|
@@ -18,5 +18,5 @@ rescue LoadError
|
|
18
18
|
Hexx::RSpec.install_tasks
|
19
19
|
end
|
20
20
|
|
21
|
-
# Sets the Hexx::RSpec task
|
22
|
-
task default: :
|
21
|
+
# Sets the Hexx::RSpec :test task to default
|
22
|
+
task default: "test:coverage:run"
|
data/config/metrics/churn.yml
CHANGED
data/config/metrics/flay.yml
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
---
|
2
|
-
minimum_score:
|
2
|
+
minimum_score: 5
|
data/config/metrics/rubocop.yml
CHANGED
@@ -3,6 +3,10 @@
|
|
3
3
|
# output: "tmp/rubocop"
|
4
4
|
# format: "html"
|
5
5
|
|
6
|
+
AllCops:
|
7
|
+
Exclude:
|
8
|
+
- '**/db/schema.rb'
|
9
|
+
|
6
10
|
Lint/HandleExceptions:
|
7
11
|
Exclude:
|
8
12
|
- '**/*_spec.rb'
|
@@ -46,10 +50,6 @@ Style/FileName:
|
|
46
50
|
Style/RaiseArgs:
|
47
51
|
EnforcedStyle: compact
|
48
52
|
|
49
|
-
Style/RescueModifier:
|
50
|
-
Exclude:
|
51
|
-
- '**/*_spec.rb'
|
52
|
-
|
53
53
|
Style/SingleLineMethods:
|
54
54
|
Exclude:
|
55
55
|
- '**/*_spec.rb'
|
data/lib/service_objects.rb
CHANGED
@@ -1,14 +1,11 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
-
|
4
|
-
|
3
|
+
require_relative "service_objects/version"
|
4
|
+
require_relative "service_objects/message"
|
5
|
+
require_relative "service_objects/listener"
|
6
|
+
require_relative "service_objects/base"
|
5
7
|
|
6
|
-
|
7
|
-
|
8
|
-
require_relative "service_objects/utils/normal_hash"
|
9
|
-
require_relative "service_objects/listener"
|
10
|
-
require_relative "service_objects/message"
|
11
|
-
require_relative "service_objects/invalid"
|
12
|
-
require_relative "service_objects/base"
|
8
|
+
# Namespace for the code of the 'service_objects' gem
|
9
|
+
module ServiceObjects
|
13
10
|
|
14
11
|
end # module ServiceObjects
|
data/lib/service_objects/base.rb
CHANGED
@@ -1,36 +1,209 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
+
|
3
|
+
require "attestor"
|
4
|
+
require "attr_coerced"
|
5
|
+
require "virtus"
|
2
6
|
require "wisper"
|
3
7
|
|
4
8
|
module ServiceObjects
|
5
9
|
|
6
|
-
require_relative "helpers"
|
7
|
-
|
8
10
|
# Base class for service objects
|
9
11
|
#
|
10
12
|
# @example
|
11
|
-
#
|
13
|
+
# class ChangeFoo < ServiceObjects::Base
|
14
|
+
#
|
15
|
+
# # Attributes definition
|
16
|
+
# attribute :id, Integer
|
17
|
+
# attribute :name
|
18
|
+
# attr_coerced :name, String
|
19
|
+
#
|
20
|
+
# # Object validation rules
|
21
|
+
# validate { invalid :blank_id unless id }
|
22
|
+
# validate { invalid :blank_name unless name }
|
23
|
+
#
|
24
|
+
# # Injectable dependencies
|
25
|
+
# dependency :get_foo, default: GetFoo
|
26
|
+
#
|
27
|
+
# private
|
28
|
+
#
|
29
|
+
# # The main algorithm
|
30
|
+
# def run!
|
31
|
+
# validate
|
32
|
+
# get_foo
|
33
|
+
# change_foo
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# attr_accessor :foo
|
12
37
|
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
38
|
+
# def get_foo
|
39
|
+
# run_service get_foo.new(id: id), Listener.new(self)
|
40
|
+
# end
|
41
|
+
#
|
42
|
+
# def change_foo
|
43
|
+
# foo.save! name: name
|
44
|
+
# publish :changed, foo, message(:success, :changed, name: name)
|
45
|
+
# rescue => error
|
46
|
+
# publish :error, message(:error, error.message)
|
47
|
+
# end
|
48
|
+
#
|
49
|
+
# class Listener < ServiceObjects::Listener
|
50
|
+
# def on_found(foo, *)
|
51
|
+
# self.foo = foo
|
52
|
+
# end
|
53
|
+
#
|
54
|
+
# def otherwise
|
55
|
+
# publish :not_found, message(:error, :not_found, id: id)
|
56
|
+
# end
|
57
|
+
# end
|
58
|
+
# end
|
59
|
+
#
|
60
|
+
# @see https://github.com/solnic/virtus
|
61
|
+
# Virtus API
|
62
|
+
# @see http://www.rubydoc.info/gems/wisper
|
63
|
+
# Wisper::Publisher API
|
64
|
+
# @see http://www.rubydoc.info/gems/attestor
|
65
|
+
# Attestor API
|
66
|
+
# @see http://www.rubydoc.info/gems/attr_coerced
|
67
|
+
# AttrCorerced API
|
17
68
|
class Base
|
18
69
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
include
|
23
|
-
include Helpers::Validations
|
70
|
+
# @!parse include Virtus::Model::Core
|
71
|
+
__send__ :include, Virtus.model
|
72
|
+
include AttrCoerced
|
73
|
+
include Attestor::Validations
|
24
74
|
include Wisper::Publisher
|
25
|
-
# @!parse include ServiceObjects::Helpers::Parameters
|
26
|
-
# @!parse include ServiceObjects::Helpers::Messages
|
27
|
-
# @!parse include ActiveModel::Validations
|
28
75
|
|
29
|
-
#
|
30
|
-
#
|
76
|
+
# Declares the dependency from another class
|
77
|
+
#
|
78
|
+
# @param [#to_sym] name
|
79
|
+
# @option [Class] :default
|
80
|
+
#
|
81
|
+
# @return [undefined]
|
82
|
+
def self.dependency(name, default: nil)
|
83
|
+
attr_accessor(name)
|
84
|
+
return unless default
|
85
|
+
define_method(name) { instance_eval "@#{ name } ||= #{ default }" }
|
86
|
+
end
|
87
|
+
|
88
|
+
# @!method subscribe(listener, prefix: nil)
|
89
|
+
# Subscribes a listener for the service object's notifications
|
90
|
+
#
|
91
|
+
# @param [Object] listener
|
92
|
+
# @option [#to_sym, nil] :prefix
|
93
|
+
#
|
94
|
+
# @return [undefined]
|
95
|
+
#
|
96
|
+
# @see https://github.com/krisleech/wisper 'wisper' gem API
|
97
|
+
|
98
|
+
# Runs the service by calling {#run!} and catching its :published throws
|
99
|
+
#
|
100
|
+
# @raise [NotImplementedError] if {#run!} method not implemented yet
|
31
101
|
#
|
32
102
|
# @return [undefined]
|
33
103
|
def run
|
104
|
+
catch(:published) { run! }
|
105
|
+
end
|
106
|
+
|
107
|
+
# Validates the object in given context
|
108
|
+
#
|
109
|
+
# Publishes error if validation fails
|
110
|
+
#
|
111
|
+
# @param [#to_sym] context
|
112
|
+
#
|
113
|
+
# @return [undefined]
|
114
|
+
def validate(context)
|
115
|
+
validate!(context)
|
116
|
+
rescue Attestor::InvalidError => error
|
117
|
+
publish :error, error.messages.map { |text| Message.new :error, text }
|
118
|
+
end
|
119
|
+
|
120
|
+
# Runs the service and receives its notifications to listener
|
121
|
+
#
|
122
|
+
# @param [ServiceObjects::Base] service
|
123
|
+
# @param [ServiceObjects::Listener] listener
|
124
|
+
#
|
125
|
+
# @return [undefined]
|
126
|
+
def run_service(service, listener)
|
127
|
+
service.subscribe listener, prefix: :on
|
128
|
+
service.run
|
129
|
+
listener.finalize
|
130
|
+
end
|
131
|
+
|
132
|
+
# @!method message(type, key, options)
|
133
|
+
# Creates the message of given type
|
134
|
+
#
|
135
|
+
# @overload message(type, key, options)
|
136
|
+
# Translates the symbolic key
|
137
|
+
#
|
138
|
+
# @example
|
139
|
+
# # config/locales/en.yml
|
140
|
+
# en:
|
141
|
+
# service_objects:
|
142
|
+
# get_foo:
|
143
|
+
# info:
|
144
|
+
# found: "Item with id %{id} has been found"
|
145
|
+
#
|
146
|
+
# @example
|
147
|
+
# result = GetFoo.new.message(:info, :found, id: 1)
|
148
|
+
#
|
149
|
+
# result.class # => ServiceObjects::Message
|
150
|
+
# result.type # => :info
|
151
|
+
# result.text # => "Item with id 1 has been found"
|
152
|
+
#
|
153
|
+
# @param [Symbol] type
|
154
|
+
# @param [Symbol] key
|
155
|
+
# @param [Hash] options
|
156
|
+
#
|
157
|
+
# @return [ServiceObjects::Message]
|
158
|
+
#
|
159
|
+
# @overload message(type, key)
|
160
|
+
# Stringifies non-symbolic key
|
161
|
+
#
|
162
|
+
# @example
|
163
|
+
# result = GetFoo.new.message(:info, 1)
|
164
|
+
#
|
165
|
+
# result.class # => ServiceObjects::Message
|
166
|
+
# result.type # => :info
|
167
|
+
# result.text # => "1"
|
168
|
+
#
|
169
|
+
# @param [Symbol] type
|
170
|
+
# @param [#to_s] key
|
171
|
+
#
|
172
|
+
# @return [ServiceObjects::Message]
|
173
|
+
def message(type, *args)
|
174
|
+
Message.new type, translate(type, *args)
|
175
|
+
end
|
176
|
+
|
177
|
+
# @!method publish(notification, *args)
|
178
|
+
# Notifies subscribers and then throws :published
|
179
|
+
#
|
180
|
+
# @param [#to_sym] notification
|
181
|
+
# @param [Array] args
|
182
|
+
#
|
183
|
+
# @raise [UncaughtThrowError] :published
|
184
|
+
#
|
185
|
+
# @return [undefined]
|
186
|
+
#
|
187
|
+
# @see https://github.com/krisleech/wisper 'wisper' gem API
|
188
|
+
def publish(*)
|
189
|
+
super
|
190
|
+
throw :published
|
191
|
+
end
|
192
|
+
|
193
|
+
private
|
194
|
+
|
195
|
+
def run!
|
196
|
+
fail NotImplementedError.new "#{ self.class }#run! not implemented"
|
197
|
+
end
|
198
|
+
|
199
|
+
def __class_path__
|
200
|
+
@__class_path__ ||= self.class.name.to_const_path.to_sym
|
201
|
+
end
|
202
|
+
|
203
|
+
def translate(type, message, **options)
|
204
|
+
return message unless message.instance_of? Symbol
|
205
|
+
scope = [:service_objects, __class_path__, type]
|
206
|
+
I18n.t message, options.merge(scope: scope)
|
34
207
|
end
|
35
208
|
|
36
209
|
end # class Base
|
@@ -1,110 +1,56 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
+
require "chronicles"
|
4
|
+
|
3
5
|
module ServiceObjects
|
4
6
|
|
5
|
-
#
|
6
|
-
#
|
7
|
-
# @example (see #new)
|
8
|
-
#
|
9
|
-
# @example (see #finalize)
|
10
|
-
#
|
11
|
-
# @api public
|
7
|
+
# Describes a decorator with callbacks to receive notifications from services
|
12
8
|
class Listener
|
9
|
+
include Chronicles
|
13
10
|
|
14
11
|
# @!scope class
|
15
12
|
# @!method new(object)
|
16
|
-
#
|
17
|
-
#
|
18
|
-
# Decorates given object by adding methods to be called by service objects
|
19
|
-
#
|
20
|
-
# @example Decorates an object
|
21
|
-
# object = Object.new
|
22
|
-
#
|
23
|
-
# listener = ServiceObjects::Listener.new object
|
24
|
-
# listener.send :object
|
25
|
-
# # => object
|
13
|
+
# Creates the listener decorator for given object
|
26
14
|
#
|
27
15
|
# @param [Object] object
|
28
16
|
#
|
29
17
|
# @return [ServiceObjects::Listener]
|
30
|
-
|
31
|
-
# @
|
18
|
+
|
19
|
+
# @private
|
32
20
|
def initialize(object)
|
33
|
-
@
|
21
|
+
@__getobj__ = object
|
22
|
+
start_chronicles except: %i(finalize __getobj__)
|
34
23
|
end
|
35
24
|
|
36
|
-
#
|
25
|
+
# @abstract
|
37
26
|
#
|
38
|
-
#
|
27
|
+
# The method to be called by {#finalize} when no own methods has been called
|
39
28
|
#
|
40
|
-
# @
|
29
|
+
# @return [undefined]
|
41
30
|
def otherwise
|
42
31
|
end
|
43
32
|
|
44
|
-
# Calls {#otherwise}
|
45
|
-
#
|
46
|
-
# @example Calls {#otherwise} in case no method has been checked
|
47
|
-
# class MyListener < ServiceObjects::Listener
|
48
|
-
# def on_success
|
49
|
-
# "success"
|
50
|
-
# end
|
51
|
-
#
|
52
|
-
# def otherwise
|
53
|
-
# "nothing"
|
54
|
-
# end
|
55
|
-
# end
|
33
|
+
# Calls {#otherwise} when no own methods has been called
|
56
34
|
#
|
57
|
-
#
|
58
|
-
# listener.finalize
|
59
|
-
# # => "nothing"
|
35
|
+
# Clears chronicles.
|
60
36
|
#
|
61
|
-
# @
|
62
|
-
#
|
63
|
-
# listener = MyListener.new
|
64
|
-
# listener.respond_to? :on_error
|
65
|
-
# # => false
|
66
|
-
#
|
67
|
-
# listener.finalize
|
68
|
-
# # => "nothing"
|
69
|
-
#
|
70
|
-
# @example Skips {#otherwise} in case a defined method has been checked
|
71
|
-
#
|
72
|
-
# listener = MyListener.new
|
73
|
-
# listener.respond_to? :on_success
|
74
|
-
# # => true
|
75
|
-
#
|
76
|
-
# listener.finalize
|
77
|
-
# # => nil
|
78
|
-
#
|
79
|
-
# @return [self]
|
37
|
+
# @return [undefined]
|
80
38
|
def finalize
|
81
|
-
otherwise unless
|
82
|
-
|
83
|
-
self
|
84
|
-
end
|
85
|
-
|
86
|
-
# Checks whether the method is defined
|
87
|
-
#
|
88
|
-
# Remembers the fact that any defined method has been checked
|
89
|
-
#
|
90
|
-
# @return [Boolean]
|
91
|
-
#
|
92
|
-
# @api private
|
93
|
-
def respond_to?(*)
|
94
|
-
super ? (@notified = true) : false
|
39
|
+
otherwise unless chronicles.empty?
|
40
|
+
chronicles.clear
|
95
41
|
end
|
96
42
|
|
97
43
|
# @private
|
98
44
|
def respond_to_missing?(*args)
|
99
|
-
|
45
|
+
__getobj__.respond_to?(*args)
|
100
46
|
end
|
101
47
|
|
102
48
|
private
|
103
49
|
|
104
|
-
attr_reader :
|
50
|
+
attr_reader :__getobj__
|
105
51
|
|
106
|
-
def method_missing(*args
|
107
|
-
|
52
|
+
def method_missing(*args)
|
53
|
+
__getobj__.__send__(*args)
|
108
54
|
end
|
109
55
|
|
110
56
|
end # class Listener
|
@@ -1,116 +1,35 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
-
require "json"
|
3
2
|
|
4
3
|
module ServiceObjects
|
5
4
|
|
6
|
-
# Describes messages
|
5
|
+
# Describes messages to be returned by service objects
|
7
6
|
class Message
|
8
|
-
include Comparable
|
9
|
-
|
10
|
-
# @!attribute [r] type
|
11
|
-
# The type of the message
|
12
|
-
#
|
13
|
-
# @return [String]
|
14
|
-
attr_reader :type
|
15
|
-
|
16
|
-
# @!attribute [r] text
|
17
|
-
# The text of the message
|
18
|
-
#
|
19
|
-
# @return [String]
|
20
|
-
attr_reader :text
|
21
|
-
|
22
|
-
# @!attribute [r] priority
|
23
|
-
# The priority level for the message
|
24
|
-
#
|
25
|
-
# If priority hasn't been set on initialization, sets it to -1.0 for errors,
|
26
|
-
# or 0.0 otherwise.
|
27
|
-
#
|
28
|
-
# @return [Float]
|
29
|
-
def priority
|
30
|
-
return @custom_priority.to_f if @custom_priority
|
31
|
-
type == "error" ? -1.0 : 0.0
|
32
|
-
end
|
33
7
|
|
34
8
|
# @!scope class
|
35
|
-
# @!method new(type
|
36
|
-
#
|
37
|
-
#
|
38
|
-
# @example
|
39
|
-
# Message.new type: "info", text: "foo", priority: 3
|
9
|
+
# @!method new(type, text)
|
10
|
+
# Creates the immutable message with type and text
|
40
11
|
#
|
41
|
-
# @param [#
|
12
|
+
# @param [#to_sym] type
|
42
13
|
# @param [#to_s] text
|
43
|
-
# @param [#to_f] priority
|
44
|
-
# optional custom priority of the message (the less the higher)
|
45
14
|
#
|
46
15
|
# @return [ServiceObjects::Message]
|
47
|
-
|
48
|
-
|
49
|
-
|
16
|
+
|
17
|
+
# @private
|
18
|
+
def initialize(type, text)
|
19
|
+
@type = type.to_sym
|
50
20
|
@text = text.to_s.freeze
|
51
|
-
@custom_priority = priority
|
52
21
|
freeze
|
53
22
|
end
|
54
23
|
|
55
|
-
#
|
56
|
-
#
|
57
|
-
# @param [Object] other
|
58
|
-
#
|
59
|
-
# @return [true]
|
60
|
-
# if messages has the same attributes
|
61
|
-
# @return [false]
|
62
|
-
# if the other object is not a message or has different argument(s)
|
63
|
-
def ==(other)
|
64
|
-
(self <=> other) == 0
|
65
|
-
end
|
66
|
-
|
67
|
-
# Compares the message with the other object
|
68
|
-
#
|
69
|
-
# @param [Object] other
|
70
|
-
#
|
71
|
-
# @return [-1, 0, 1]
|
72
|
-
# if the argument is a message
|
73
|
-
# @return [nil]
|
74
|
-
# if the other object is not a message
|
75
|
-
def <=>(other)
|
76
|
-
return unless other.is_a? self.class
|
77
|
-
[:priority, :type, :text]
|
78
|
-
.map { |key| __compare_to__ other, by: key }
|
79
|
-
.detect { |value| value } || 0
|
80
|
-
end
|
81
|
-
|
82
|
-
# Converts the message object to hash
|
83
|
-
#
|
84
|
-
# @return [Hash]
|
85
|
-
def to_h
|
86
|
-
{ type: type, text: text }
|
87
|
-
end
|
88
|
-
|
89
|
-
# Converts the message object to json
|
90
|
-
#
|
91
|
-
# @return [String]
|
92
|
-
def to_json
|
93
|
-
to_h.to_json
|
94
|
-
end
|
95
|
-
|
96
|
-
# A human-readable representation of the message
|
97
|
-
#
|
24
|
+
# @!attribute [r] text
|
25
|
+
# The text of the message
|
98
26
|
# @return [String]
|
99
|
-
|
100
|
-
%W(
|
101
|
-
#<ServiceObjects::Message:#{ object_id }
|
102
|
-
type=\"#{ type }\"
|
103
|
-
text=\"#{ text }\"
|
104
|
-
priority=#{ priority }>
|
105
|
-
).join(" ")
|
106
|
-
end
|
107
|
-
|
108
|
-
private
|
27
|
+
attr_reader :text
|
109
28
|
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
29
|
+
# @!attribute [r] type
|
30
|
+
# The type of the message
|
31
|
+
# @return [Symbol]
|
32
|
+
attr_reader :type
|
114
33
|
|
115
34
|
end # class Message
|
116
35
|
|