xeme 0.1
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 +7 -0
- data/README.md +286 -0
- data/lib/xeme.rb +613 -0
- metadata +45 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: d484d6f5c5b9cfdf0ae85dd4a1ca69c46b2b1c7eed711eae02352b59d3c929fe
|
4
|
+
data.tar.gz: e8d0a6527bafc7dff93ba1cd584a4527114e5dc3a40b165c3c86baf9ece744a2
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 291925e898631853c7a64f49327954b0b1c309b73e330d511561dc5c017ae7b15ab51f0fc22264e5c800a80c59530733776eba813840c06e179fd7db221aa166
|
7
|
+
data.tar.gz: 13d3459ee59daf94a4bc2841ed308ec498b14f361ad8eb76abb0d919d44823ba2fdf4df19e3b93df89d54d3c0c9564fdaf408c6f7c60d352e22844a79746b4c1
|
data/README.md
ADDED
@@ -0,0 +1,286 @@
|
|
1
|
+
# Xeme
|
2
|
+
|
3
|
+
Xeme provides a common format for returning the results of a process. In its
|
4
|
+
simplest use, you create a Xeme object and add errors as necessary:
|
5
|
+
|
6
|
+
require 'xeme'
|
7
|
+
xeme = Xeme.new
|
8
|
+
xeme.error 'error-1'
|
9
|
+
|
10
|
+
If there are any errors, such as in this first example, <tt>success</tt> returns false:
|
11
|
+
|
12
|
+
puts xeme.success # false
|
13
|
+
|
14
|
+
A Xeme object without any errors returns true for <tt>success</tt>:
|
15
|
+
|
16
|
+
xeme = Xeme.new
|
17
|
+
puts xeme.success # true
|
18
|
+
|
19
|
+
Xeme is a good way to report results that are more complex than just success or
|
20
|
+
failure. For example, consider the situation in which a record must have a name
|
21
|
+
field and an email field. If the record does not meet these requirements, it is
|
22
|
+
not sufficient to merely report success or failure, nor to just report that one
|
23
|
+
field or the other is missing. With Xeme you can report all of that information
|
24
|
+
in a single object:
|
25
|
+
|
26
|
+
xeme = Xeme.new
|
27
|
+
xeme.error 'name'
|
28
|
+
xeme.error 'email'
|
29
|
+
|
30
|
+
You can return the Xeme object to another Ruby process, or you can output it to
|
31
|
+
JSON for use in some other program:
|
32
|
+
|
33
|
+
puts xeme.to_json
|
34
|
+
|
35
|
+
# outputs:
|
36
|
+
{"success":false,"messages":{"errors":[{"id":"name"},{"id":"email"}]}}
|
37
|
+
|
38
|
+
You might prefer to add the <tt>pretty</tt> option for more readable output:
|
39
|
+
|
40
|
+
puts xeme.to_json('pretty'=>true)
|
41
|
+
|
42
|
+
# outputs:
|
43
|
+
{
|
44
|
+
"success": false,
|
45
|
+
"messages": {
|
46
|
+
"errors": [
|
47
|
+
{
|
48
|
+
"id": "name"
|
49
|
+
},
|
50
|
+
{
|
51
|
+
"id": "email"
|
52
|
+
}
|
53
|
+
]
|
54
|
+
}
|
55
|
+
}
|
56
|
+
|
57
|
+
In some situations, it's useful to give details about the error. For example,
|
58
|
+
if the <tt>name</tt> field is too long, you can report not just that there is
|
59
|
+
an error in that field, but specifics about that error:
|
60
|
+
|
61
|
+
xeme.error('name') do |msg|
|
62
|
+
msg['too-long'] = true
|
63
|
+
msg['max-length'] = 45
|
64
|
+
end
|
65
|
+
|
66
|
+
That would produce a structure like this:
|
67
|
+
|
68
|
+
{
|
69
|
+
"success": false,
|
70
|
+
"messages": {
|
71
|
+
"errors": [
|
72
|
+
{
|
73
|
+
"id": "name",
|
74
|
+
"details": {
|
75
|
+
"too-long": true,
|
76
|
+
"max-length": 45
|
77
|
+
}
|
78
|
+
}
|
79
|
+
]
|
80
|
+
}
|
81
|
+
}
|
82
|
+
|
83
|
+
|
84
|
+
You can also add warnings and notes. A warning indicates a problem but not an
|
85
|
+
actual failure. A note does not indicate any problem at all but just some
|
86
|
+
information that should be passed along.
|
87
|
+
|
88
|
+
xeme.warning 'database-reset'
|
89
|
+
xeme.note 'database ok'
|
90
|
+
|
91
|
+
It's often useful to add misc details to the results. For example, a database
|
92
|
+
query might result in some rows from a table. You might add these rows to the
|
93
|
+
<tt>misc</tt> property like this:
|
94
|
+
|
95
|
+
xeme = Xeme.new
|
96
|
+
xeme.misc['rows'] = []
|
97
|
+
xeme.misc['rows'].push 'a'
|
98
|
+
xeme.misc['rows'].push 'b'
|
99
|
+
xeme.misc['rows'].push 'c'
|
100
|
+
puts xeme.to_json('pretty'=>true)
|
101
|
+
|
102
|
+
# outputs:
|
103
|
+
{
|
104
|
+
"success": true,
|
105
|
+
"misc": {
|
106
|
+
"rows": [
|
107
|
+
"a",
|
108
|
+
"b",
|
109
|
+
"c"
|
110
|
+
]
|
111
|
+
}
|
112
|
+
}
|
113
|
+
|
114
|
+
Xeme can input a Xeme JSON structure to make a new Xeme object:
|
115
|
+
|
116
|
+
# json
|
117
|
+
json = <<~'JSON'
|
118
|
+
{
|
119
|
+
"success": false,
|
120
|
+
"messages": {
|
121
|
+
"errors": [
|
122
|
+
{ "id": "location" }
|
123
|
+
]
|
124
|
+
}
|
125
|
+
}
|
126
|
+
JSON
|
127
|
+
|
128
|
+
# xeme
|
129
|
+
xeme = Xeme.from_json(json)
|
130
|
+
|
131
|
+
## Xeme structure
|
132
|
+
|
133
|
+
The Xeme structure can be used by any software, Ruby or otherwise, as a standard
|
134
|
+
way to report results. A Xeme structure can be stored in any format that
|
135
|
+
recognizes hashes, arrays, strings, numbers, booleans, and null. Such formats
|
136
|
+
include JSON and YAML. Xeme would be a good way for REST applications to
|
137
|
+
provide results from a query. The Xeme#to_json method outputs such a structure.
|
138
|
+
We'll use JSON for these examples.
|
139
|
+
|
140
|
+
An empty structure indicates success:
|
141
|
+
|
142
|
+
{}
|
143
|
+
|
144
|
+
That structure indicates no messages such as errors, and no details. To make the
|
145
|
+
structure easier to use by software that doesn't have a Xeme module, it is
|
146
|
+
customary to also output an explicit indication of success or failure:
|
147
|
+
|
148
|
+
{"success":true}
|
149
|
+
|
150
|
+
A Xeme structure can report any number of messages. A message is an error,
|
151
|
+
warning, or note. An error indicates that the process failed, and gives a resaon
|
152
|
+
why. If there are any errors, the process is considered a failure. A warning
|
153
|
+
indicates a problem, but not an actual failure. A note reports useful
|
154
|
+
information that does not indicate any kind of problem at all. Neither a warning
|
155
|
+
nor a note cause failure.
|
156
|
+
|
157
|
+
Messages, if any, are stored in a messages hash, which consists of arrays for
|
158
|
+
errors, warnings, and notes.
|
159
|
+
|
160
|
+
{
|
161
|
+
"success": false,
|
162
|
+
"messages": {
|
163
|
+
"errors": [
|
164
|
+
{
|
165
|
+
"id": "missing-city"
|
166
|
+
}
|
167
|
+
],
|
168
|
+
"warnings": [
|
169
|
+
{
|
170
|
+
"id": "database-reset"
|
171
|
+
}
|
172
|
+
],
|
173
|
+
"notes": [
|
174
|
+
{
|
175
|
+
"id": "database-online"
|
176
|
+
}
|
177
|
+
]
|
178
|
+
}
|
179
|
+
}
|
180
|
+
|
181
|
+
Any number of messages can be reported. So, for example, the following structure
|
182
|
+
has two errors and no warnings or notes.
|
183
|
+
|
184
|
+
{
|
185
|
+
"success": false,
|
186
|
+
"messages": {
|
187
|
+
"errors": [
|
188
|
+
{
|
189
|
+
"id": "missing-city"
|
190
|
+
}
|
191
|
+
],
|
192
|
+
"warnings": [
|
193
|
+
{
|
194
|
+
"id": "missing-zip-code"
|
195
|
+
}
|
196
|
+
]
|
197
|
+
}
|
198
|
+
}
|
199
|
+
|
200
|
+
A message can have associated details. For example, the following error
|
201
|
+
indicates that the problem with the name field is that it's too long, and the
|
202
|
+
maximum length is 45:
|
203
|
+
|
204
|
+
{
|
205
|
+
"success": false,
|
206
|
+
"messages": {
|
207
|
+
"errors": [
|
208
|
+
{
|
209
|
+
"id": "name",
|
210
|
+
"details": {
|
211
|
+
"too-long": true,
|
212
|
+
"max-length": 45
|
213
|
+
}
|
214
|
+
}
|
215
|
+
]
|
216
|
+
}
|
217
|
+
}
|
218
|
+
|
219
|
+
Sometimes it is useful to add arbitrary information to the results. For example,
|
220
|
+
the results from a database query might return the rows that the query produced.
|
221
|
+
In that situation, the misc element is handy.
|
222
|
+
|
223
|
+
{
|
224
|
+
"success": true,
|
225
|
+
"misc": {
|
226
|
+
"rows": [
|
227
|
+
"Fred",
|
228
|
+
"Mary",
|
229
|
+
"Jane"
|
230
|
+
]
|
231
|
+
}
|
232
|
+
}
|
233
|
+
|
234
|
+
Sometimes it is useful to provide information about the specific transaction,
|
235
|
+
e.g. the call to a REST server. The transaction element can be used to provide
|
236
|
+
that information. If there is a transaction element, it should always provide a
|
237
|
+
timestamp for the transaction and a unique ID for it.
|
238
|
+
|
239
|
+
{
|
240
|
+
"success": true,
|
241
|
+
"transaction": {
|
242
|
+
"response": "08214643960776591",
|
243
|
+
"timestamp": "2020-01-07T18:41:37+00:00"
|
244
|
+
}
|
245
|
+
}
|
246
|
+
|
247
|
+
If the request includes an ID for that specific request, then that ID can be
|
248
|
+
supplied as part of the transaction element:
|
249
|
+
|
250
|
+
{
|
251
|
+
"success": true,
|
252
|
+
"transaction": {
|
253
|
+
"request": "64e57c8a-bd3e-47b9-9221-e9e3e0263341",
|
254
|
+
"response": "3301315461336113",
|
255
|
+
"timestamp": "2020-01-07T18:42:01+00:00"
|
256
|
+
}
|
257
|
+
}
|
258
|
+
|
259
|
+
The general adoption of the Xeme structure could simplify implementing
|
260
|
+
interoperable applications such as REST APIs.
|
261
|
+
|
262
|
+
## The name
|
263
|
+
|
264
|
+
The word "xeme" has no particular association with the concept of results
|
265
|
+
reporting. I got the word from a random word generator and I liked it. The xeme,
|
266
|
+
also known as Sabine's gull, is a type of gull. See
|
267
|
+
[the Wikipedia page](https://en.wikipedia.org/wiki/Sabine's_gull)
|
268
|
+
if you'd like to know more.
|
269
|
+
|
270
|
+
## Install
|
271
|
+
|
272
|
+
```
|
273
|
+
gem install xeme
|
274
|
+
```
|
275
|
+
|
276
|
+
## Author
|
277
|
+
|
278
|
+
Mike O'Sullivan
|
279
|
+
mike@idocs.com
|
280
|
+
|
281
|
+
## History
|
282
|
+
|
283
|
+
| version | date | notes |
|
284
|
+
|---------|-------------|-----------------|
|
285
|
+
| 0.1 | Jan 7, 2020 | Initial upload. |
|
286
|
+
|
data/lib/xeme.rb
ADDED
@@ -0,0 +1,613 @@
|
|
1
|
+
require 'date'
|
2
|
+
require 'json'
|
3
|
+
require 'forwardable'
|
4
|
+
|
5
|
+
#===============================================================================
|
6
|
+
# Xeme
|
7
|
+
#
|
8
|
+
|
9
|
+
##
|
10
|
+
# Objects of this class represent a set of results. See the README file for
|
11
|
+
# more details.
|
12
|
+
|
13
|
+
class Xeme
|
14
|
+
# Version 0.1
|
15
|
+
VERSION = '0.1'
|
16
|
+
|
17
|
+
# A Xeme::Messages object, which is basically just a hash containing arrays
|
18
|
+
# for errors, warnings, and notes.
|
19
|
+
attr_reader :messages
|
20
|
+
|
21
|
+
# A hash of any miscellaneous details you want to include.
|
22
|
+
attr_reader :misc
|
23
|
+
|
24
|
+
#---------------------------------------------------------------------------
|
25
|
+
# initialize
|
26
|
+
#
|
27
|
+
|
28
|
+
##
|
29
|
+
# new() does not take any parameters.
|
30
|
+
|
31
|
+
def initialize
|
32
|
+
# initialize errors, warnings, notes, misc
|
33
|
+
@messages = Xeme::Messages.new
|
34
|
+
@misc = {}
|
35
|
+
|
36
|
+
# prefix and auto_misc
|
37
|
+
@prefixes = []
|
38
|
+
@auto_details_val = nil
|
39
|
+
|
40
|
+
# transaction
|
41
|
+
@transaction = nil
|
42
|
+
end
|
43
|
+
#
|
44
|
+
# initialize
|
45
|
+
#---------------------------------------------------------------------------
|
46
|
+
|
47
|
+
|
48
|
+
#---------------------------------------------------------------------------
|
49
|
+
# errors, warnings, notes
|
50
|
+
#
|
51
|
+
|
52
|
+
##
|
53
|
+
# Returns the array of errors.
|
54
|
+
def errors
|
55
|
+
return @messages['errors']
|
56
|
+
end
|
57
|
+
|
58
|
+
##
|
59
|
+
# Returns the array of warnings.
|
60
|
+
def warnings
|
61
|
+
return @messages['warnings']
|
62
|
+
end
|
63
|
+
|
64
|
+
##
|
65
|
+
# Returns the array of notes.
|
66
|
+
def notes
|
67
|
+
return @messages['notes']
|
68
|
+
end
|
69
|
+
#
|
70
|
+
# errors, warnings, notes
|
71
|
+
#---------------------------------------------------------------------------
|
72
|
+
|
73
|
+
|
74
|
+
#---------------------------------------------------------------------------
|
75
|
+
# transaction
|
76
|
+
#
|
77
|
+
|
78
|
+
##
|
79
|
+
# Returns the transaction object, creating it if necessary. See
|
80
|
+
# Xeme::Transaction. If the transaction object is never created then it is
|
81
|
+
# not output with #to_json. The transaction object can be used to provide
|
82
|
+
# meta information about the request and response that produced the
|
83
|
+
# results. See Xeme::Transaction for more information.
|
84
|
+
#
|
85
|
+
# You can create the transaction object by simply calling this method:
|
86
|
+
#
|
87
|
+
# xeme.transaction
|
88
|
+
#
|
89
|
+
# If you want to assign a request ID, call this method and assign to its
|
90
|
+
# request property. For example, if there's a request object that provides
|
91
|
+
# an id, you could assign the transaction.request ID like this:
|
92
|
+
#
|
93
|
+
# xeme.transaction.request = request.id
|
94
|
+
|
95
|
+
def transaction
|
96
|
+
@transaction ||= Xeme::Transaction.new()
|
97
|
+
return @transaction
|
98
|
+
end
|
99
|
+
#
|
100
|
+
# transaction
|
101
|
+
#---------------------------------------------------------------------------
|
102
|
+
|
103
|
+
|
104
|
+
#---------------------------------------------------------------------------
|
105
|
+
# to_h
|
106
|
+
#
|
107
|
+
|
108
|
+
##
|
109
|
+
# Returns a hash with all the results. This hash can be used to create a JSON
|
110
|
+
# object. Empty arrays, such as if there are no warnings, are not included.
|
111
|
+
|
112
|
+
def to_h
|
113
|
+
# $tm.hrm
|
114
|
+
|
115
|
+
# initialize return value
|
116
|
+
rv = {}
|
117
|
+
|
118
|
+
# success
|
119
|
+
rv['success'] = success()
|
120
|
+
|
121
|
+
# transaction
|
122
|
+
if @transaction
|
123
|
+
rv['transaction'] = @transaction.to_h
|
124
|
+
end
|
125
|
+
|
126
|
+
# if any messages
|
127
|
+
if @messages.any?
|
128
|
+
msgs = rv['messages'] = {}
|
129
|
+
|
130
|
+
@messages.each do |key, arr|
|
131
|
+
if arr.any?
|
132
|
+
msgs[key] = []
|
133
|
+
|
134
|
+
arr.each do |msg|
|
135
|
+
msgs[key].push msg.to_h
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
# misc
|
142
|
+
if @misc.any?
|
143
|
+
rv['misc'] = @misc
|
144
|
+
end
|
145
|
+
|
146
|
+
# return
|
147
|
+
return rv
|
148
|
+
end
|
149
|
+
#
|
150
|
+
# to_h
|
151
|
+
#---------------------------------------------------------------------------
|
152
|
+
|
153
|
+
|
154
|
+
#---------------------------------------------------------------------------
|
155
|
+
# to_json
|
156
|
+
#
|
157
|
+
|
158
|
+
##
|
159
|
+
# Returns a JSON string containing all the result information.
|
160
|
+
def to_json(opts={})
|
161
|
+
if opts['pretty']
|
162
|
+
return JSON.pretty_generate(self.to_h)
|
163
|
+
else
|
164
|
+
return JSON.generate(self.to_h)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
#
|
168
|
+
# to_json
|
169
|
+
#---------------------------------------------------------------------------
|
170
|
+
|
171
|
+
|
172
|
+
#---------------------------------------------------------------------------
|
173
|
+
# success
|
174
|
+
#
|
175
|
+
|
176
|
+
##
|
177
|
+
# Returns false if there are any errors, true otherwise.
|
178
|
+
|
179
|
+
def success
|
180
|
+
return @messages['errors'].empty?
|
181
|
+
end
|
182
|
+
|
183
|
+
##
|
184
|
+
# Returns the opposite of <tt>success()</tt>.
|
185
|
+
def failure
|
186
|
+
return success ? false : true
|
187
|
+
end
|
188
|
+
|
189
|
+
#
|
190
|
+
# success
|
191
|
+
#---------------------------------------------------------------------------
|
192
|
+
|
193
|
+
|
194
|
+
#---------------------------------------------------------------------------
|
195
|
+
# exception
|
196
|
+
#
|
197
|
+
|
198
|
+
##
|
199
|
+
# Use this method to hold on to details about an exception. An error object
|
200
|
+
# will be created, along with the exception's to_s backtrace.
|
201
|
+
|
202
|
+
def exception(id, e, details={})
|
203
|
+
message = self.error(id, details)
|
204
|
+
message.details['error'] = e.to_s
|
205
|
+
message.details['backtrace'] = e.backtrace
|
206
|
+
end
|
207
|
+
#
|
208
|
+
# exception
|
209
|
+
#---------------------------------------------------------------------------
|
210
|
+
|
211
|
+
|
212
|
+
#---------------------------------------------------------------------------
|
213
|
+
# prefix
|
214
|
+
#
|
215
|
+
|
216
|
+
##
|
217
|
+
# <tt>prefix</tt> allows you to automatically add prefixes to your message
|
218
|
+
# codes in situations where many messages will start with the same string.
|
219
|
+
# Consider the following situation.
|
220
|
+
#
|
221
|
+
# xeme.error '/virginia/slope'
|
222
|
+
# xeme.error '/virginia/angle'
|
223
|
+
# xeme.error '/virginia/departure'
|
224
|
+
#
|
225
|
+
# That works, but the redundancy can get confusing (and annoying). With
|
226
|
+
# <tt>prefix</tt> you can avoid the redundancy like this:
|
227
|
+
#
|
228
|
+
# xeme.prefix('virginia') do
|
229
|
+
# xeme.error 'slope'
|
230
|
+
# xeme.error 'angle'
|
231
|
+
# xeme.error 'departure'
|
232
|
+
# end
|
233
|
+
#
|
234
|
+
# puts xeme.errors[0].id # /virginia/slope
|
235
|
+
# puts xeme.errors[1].id # /virginia/angle
|
236
|
+
# puts xeme.errors[2].id # /virginia/departure
|
237
|
+
#
|
238
|
+
# Prefixes can also be nested, like this:
|
239
|
+
#
|
240
|
+
# xeme.prefix('virginia') do
|
241
|
+
# xeme.prefix('fairfax') do
|
242
|
+
# xeme.error 'slope'
|
243
|
+
# end
|
244
|
+
# end
|
245
|
+
#
|
246
|
+
# puts xeme.errors[0].id # /virginia/fairfax/slope
|
247
|
+
|
248
|
+
def prefix(p_prefix)
|
249
|
+
# $tm.hrm
|
250
|
+
hold_prefixes = @prefixes.clone
|
251
|
+
@prefixes = @prefixes.clone
|
252
|
+
@prefixes.push p_prefix
|
253
|
+
|
254
|
+
begin
|
255
|
+
yield
|
256
|
+
ensure
|
257
|
+
@prefixes = hold_prefixes
|
258
|
+
end
|
259
|
+
end
|
260
|
+
#
|
261
|
+
# prefix
|
262
|
+
#---------------------------------------------------------------------------
|
263
|
+
|
264
|
+
|
265
|
+
#---------------------------------------------------------------------------
|
266
|
+
# error, warning, note
|
267
|
+
#
|
268
|
+
|
269
|
+
##
|
270
|
+
# Creates a message object and stores it in the errors array.
|
271
|
+
#
|
272
|
+
# xeme.error('error-1').
|
273
|
+
#
|
274
|
+
# A hash of arbitrary details can be given. Those details will be stored
|
275
|
+
# with the message object in its Xeme::Message#details property.
|
276
|
+
#
|
277
|
+
# xeme.error('error-0', 'choice'=>'a')
|
278
|
+
#
|
279
|
+
# This method returns the message object, which can be used as another
|
280
|
+
# way to store details.
|
281
|
+
#
|
282
|
+
# msg = xeme.error('error-1').
|
283
|
+
# msg['location'] = 1
|
284
|
+
#
|
285
|
+
# If a block is given, the block is called with the error object as the
|
286
|
+
# single param.
|
287
|
+
#
|
288
|
+
# xeme.error('error-2') do |error|
|
289
|
+
# error['location'] = 2
|
290
|
+
# end
|
291
|
+
def error(id, details={}, &block)
|
292
|
+
return add_comment(Xeme::Message, 'errors', id, details={}, &block)
|
293
|
+
end
|
294
|
+
|
295
|
+
##
|
296
|
+
# Works just #error() except it stores the message in the warnings array.
|
297
|
+
def warning(id, details={}, &block)
|
298
|
+
return add_comment(Xeme::Message, 'warnings', id, details={}, &block)
|
299
|
+
end
|
300
|
+
|
301
|
+
##
|
302
|
+
# Works just #error() except it stores the message in the notes array.
|
303
|
+
def note(id, details={}, &block)
|
304
|
+
return add_comment(Xeme::Message, 'notes', id, details={}, &block)
|
305
|
+
end
|
306
|
+
#
|
307
|
+
# error, warning, note
|
308
|
+
#---------------------------------------------------------------------------
|
309
|
+
|
310
|
+
|
311
|
+
#---------------------------------------------------------------------------
|
312
|
+
# from_json
|
313
|
+
#
|
314
|
+
|
315
|
+
##
|
316
|
+
# Creates a new Xeme object from a JSON structure.
|
317
|
+
|
318
|
+
def self.from_json(raw_json)
|
319
|
+
# $tm.hrm
|
320
|
+
hsh = JSON.parse(raw_json)
|
321
|
+
rv = self.new
|
322
|
+
|
323
|
+
# explicit success or failure
|
324
|
+
if not hsh['success'].nil?
|
325
|
+
rv.success = hsh['success']
|
326
|
+
end
|
327
|
+
|
328
|
+
# messages
|
329
|
+
if messages = hsh['messages']
|
330
|
+
messages.each do |msg_type, arr|
|
331
|
+
rv.messages[msg_type] ||= []
|
332
|
+
rv.messages[msg_type] += arr
|
333
|
+
end
|
334
|
+
end
|
335
|
+
|
336
|
+
# return
|
337
|
+
return rv
|
338
|
+
end
|
339
|
+
#
|
340
|
+
# from_json
|
341
|
+
#---------------------------------------------------------------------------
|
342
|
+
|
343
|
+
|
344
|
+
# private methods
|
345
|
+
private
|
346
|
+
|
347
|
+
|
348
|
+
#---------------------------------------------------------------------------
|
349
|
+
# messages_to_h
|
350
|
+
#
|
351
|
+
def messages_to_h(arr)
|
352
|
+
rv = []
|
353
|
+
|
354
|
+
# messages
|
355
|
+
arr.each do |msg|
|
356
|
+
rv.push msg.to_h
|
357
|
+
end
|
358
|
+
|
359
|
+
# return
|
360
|
+
return rv
|
361
|
+
end
|
362
|
+
#
|
363
|
+
# messages_to_h
|
364
|
+
#---------------------------------------------------------------------------
|
365
|
+
|
366
|
+
|
367
|
+
#---------------------------------------------------------------------------
|
368
|
+
# add_comment
|
369
|
+
#
|
370
|
+
def add_comment(cls, type, id, details={}, &block)
|
371
|
+
# $tm.hrm
|
372
|
+
|
373
|
+
# build message
|
374
|
+
msg = cls.new(prefix_id(id), details)
|
375
|
+
|
376
|
+
# ensure array
|
377
|
+
@messages[type] ||= []
|
378
|
+
|
379
|
+
# add to errors array
|
380
|
+
@messages[type].push(msg)
|
381
|
+
|
382
|
+
# yield
|
383
|
+
if block_given?
|
384
|
+
yield msg
|
385
|
+
end
|
386
|
+
|
387
|
+
# return
|
388
|
+
return msg
|
389
|
+
end
|
390
|
+
#
|
391
|
+
# add_comment
|
392
|
+
#---------------------------------------------------------------------------
|
393
|
+
|
394
|
+
|
395
|
+
#---------------------------------------------------------------------------
|
396
|
+
# prefix_id
|
397
|
+
#
|
398
|
+
def prefix_id(p_id)
|
399
|
+
if @prefixes.any?
|
400
|
+
return '/' + @prefixes.join('/') + '/' + p_id
|
401
|
+
else
|
402
|
+
return p_id
|
403
|
+
end
|
404
|
+
end
|
405
|
+
#
|
406
|
+
# prefix_id
|
407
|
+
#---------------------------------------------------------------------------
|
408
|
+
end
|
409
|
+
#
|
410
|
+
# Xeme
|
411
|
+
#===============================================================================
|
412
|
+
|
413
|
+
|
414
|
+
#===============================================================================
|
415
|
+
# Xeme::Message
|
416
|
+
#
|
417
|
+
|
418
|
+
##
|
419
|
+
# A Xeme::Message object represents a single error, warning, or note.
|
420
|
+
# It is the base class for Xeme::Message::Error,
|
421
|
+
# Xeme::Message::Warning, and
|
422
|
+
# Xeme::Message::Note. Don't instantiate Xeme::Message
|
423
|
+
# directly, use Xeme#error, Xeme#warning, or Xeme#note.
|
424
|
+
#
|
425
|
+
# Each message contains at least an id. It can also have an options details
|
426
|
+
# hash which can be used to store misc information about the message.
|
427
|
+
#
|
428
|
+
# The message itself can be treated like a hash to give details:
|
429
|
+
#
|
430
|
+
# msg = xeme.error('my-error')
|
431
|
+
# msg['location'] = 1
|
432
|
+
|
433
|
+
class Xeme::Message
|
434
|
+
# The id of the message.
|
435
|
+
attr_reader :id
|
436
|
+
|
437
|
+
# delegate to hsh
|
438
|
+
extend Forwardable
|
439
|
+
delegate %w([] []= each length clear delete) => :@details
|
440
|
+
|
441
|
+
|
442
|
+
#---------------------------------------------------------------------------
|
443
|
+
# initialize
|
444
|
+
#
|
445
|
+
|
446
|
+
# Initialize a Xeme::Message object with the id of the message. The id can
|
447
|
+
# be any string. The optional <tt>details</tt> hash will be stored with the
|
448
|
+
# message.
|
449
|
+
|
450
|
+
def initialize(id, details={})
|
451
|
+
# $tm.hr __method__
|
452
|
+
|
453
|
+
# set id
|
454
|
+
@id = id
|
455
|
+
|
456
|
+
# initialize details
|
457
|
+
@details = details.clone
|
458
|
+
end
|
459
|
+
#
|
460
|
+
# initialize
|
461
|
+
#---------------------------------------------------------------------------
|
462
|
+
|
463
|
+
|
464
|
+
#---------------------------------------------------------------------------
|
465
|
+
# to_h
|
466
|
+
#
|
467
|
+
|
468
|
+
##
|
469
|
+
# Returns a hash structure of the message. This structure is used by
|
470
|
+
# Xeme#to_h.
|
471
|
+
|
472
|
+
def to_h
|
473
|
+
rv = {}
|
474
|
+
rv['id'] = @id
|
475
|
+
|
476
|
+
if @details.any?
|
477
|
+
rv['details'] = @details
|
478
|
+
end
|
479
|
+
|
480
|
+
return rv
|
481
|
+
end
|
482
|
+
#
|
483
|
+
# to_h
|
484
|
+
#---------------------------------------------------------------------------
|
485
|
+
end
|
486
|
+
#
|
487
|
+
# Xeme::Message
|
488
|
+
#===============================================================================
|
489
|
+
|
490
|
+
|
491
|
+
#===============================================================================
|
492
|
+
# Xeme::Messages
|
493
|
+
#
|
494
|
+
|
495
|
+
##
|
496
|
+
# This object holds the arrays of errors, warnings, and notes. It can mostly be
|
497
|
+
# treated like a hash. The main difference between a Xeme::Messages object and
|
498
|
+
# a hash is that any? returns true or false based on if there are any messages,
|
499
|
+
# not if there are any elements in the hash.
|
500
|
+
|
501
|
+
class Xeme::Messages
|
502
|
+
# delegate to hsh
|
503
|
+
extend Forwardable
|
504
|
+
delegate %w([] []= each length keys has_key?) => :@hsh
|
505
|
+
|
506
|
+
#---------------------------------------------------------------------------
|
507
|
+
# initialize
|
508
|
+
#
|
509
|
+
|
510
|
+
##
|
511
|
+
# new() takes no parameters.
|
512
|
+
|
513
|
+
def initialize
|
514
|
+
@hsh = {}
|
515
|
+
@hsh['errors'] = []
|
516
|
+
@hsh['warnings'] = []
|
517
|
+
@hsh['notes'] = []
|
518
|
+
end
|
519
|
+
#
|
520
|
+
# initialize
|
521
|
+
#---------------------------------------------------------------------------
|
522
|
+
|
523
|
+
|
524
|
+
#---------------------------------------------------------------------------
|
525
|
+
# any?
|
526
|
+
#
|
527
|
+
|
528
|
+
##
|
529
|
+
# Returns if there are any messages in any of the errors, warnings, or
|
530
|
+
# notes arrays.
|
531
|
+
|
532
|
+
def any?
|
533
|
+
@hsh.values.each do |arr|
|
534
|
+
arr.any? and return true
|
535
|
+
end
|
536
|
+
|
537
|
+
return false
|
538
|
+
end
|
539
|
+
#
|
540
|
+
# any?
|
541
|
+
#---------------------------------------------------------------------------
|
542
|
+
end
|
543
|
+
#
|
544
|
+
# Xeme::Messages
|
545
|
+
#===============================================================================
|
546
|
+
|
547
|
+
|
548
|
+
#===============================================================================
|
549
|
+
# Xeme::Transaction
|
550
|
+
#
|
551
|
+
|
552
|
+
##
|
553
|
+
# An object of this class provdes meta information about the request and
|
554
|
+
# results. It always provides a a timestamp and a unique ID for the results. It
|
555
|
+
# may also optionally include a request ID that was provided by the process
|
556
|
+
# that made the request, such as a call to a REST application. Do not directly
|
557
|
+
# instantiate this class; use Xeme#transaction.
|
558
|
+
|
559
|
+
class Xeme::Transaction
|
560
|
+
|
561
|
+
# Optional. An ID for the request that was sent by the calling process. Do
|
562
|
+
# not generate this value yourself; use the ID that was sent with the
|
563
|
+
# request (if one was sent).
|
564
|
+
attr_accessor :request
|
565
|
+
|
566
|
+
# Gives a unique ID for these results.
|
567
|
+
attr_reader :response
|
568
|
+
|
569
|
+
# Gives a timestamp for when these results were generated.
|
570
|
+
attr_reader :timestamp
|
571
|
+
|
572
|
+
#---------------------------------------------------------------------------
|
573
|
+
# initialize
|
574
|
+
#
|
575
|
+
|
576
|
+
##
|
577
|
+
# Initialize does not take any parameters.
|
578
|
+
|
579
|
+
def initialize
|
580
|
+
@timestamp = DateTime.now()
|
581
|
+
@response = rand().to_s.sub(/\A0\./mu, '')
|
582
|
+
@request = nil
|
583
|
+
end
|
584
|
+
#
|
585
|
+
# initialize
|
586
|
+
#---------------------------------------------------------------------------
|
587
|
+
|
588
|
+
|
589
|
+
#---------------------------------------------------------------------------
|
590
|
+
# to_h
|
591
|
+
#
|
592
|
+
def to_h
|
593
|
+
rv = {}
|
594
|
+
|
595
|
+
# request
|
596
|
+
if @request
|
597
|
+
rv['request'] = @request
|
598
|
+
end
|
599
|
+
|
600
|
+
# response and timestamp
|
601
|
+
rv['response'] = @response
|
602
|
+
rv['timestamp'] = @timestamp.to_s
|
603
|
+
|
604
|
+
# return
|
605
|
+
return rv
|
606
|
+
end
|
607
|
+
#
|
608
|
+
# to_h
|
609
|
+
#---------------------------------------------------------------------------
|
610
|
+
end
|
611
|
+
#
|
612
|
+
# Xeme::Transaction
|
613
|
+
#===============================================================================
|
metadata
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: xeme
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.1'
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Mike O'Sullivan
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-01-06 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: Provides a common means for reporting results between processes
|
14
|
+
email: mike@idocs.com
|
15
|
+
executables: []
|
16
|
+
extensions: []
|
17
|
+
extra_rdoc_files: []
|
18
|
+
files:
|
19
|
+
- README.md
|
20
|
+
- lib/xeme.rb
|
21
|
+
homepage: https://rubygems.org/gems/xeme
|
22
|
+
licenses:
|
23
|
+
- MIT
|
24
|
+
metadata: {}
|
25
|
+
post_install_message:
|
26
|
+
rdoc_options: []
|
27
|
+
require_paths:
|
28
|
+
- lib
|
29
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
35
|
+
requirements:
|
36
|
+
- - ">="
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: '0'
|
39
|
+
requirements: []
|
40
|
+
rubyforge_project:
|
41
|
+
rubygems_version: 2.7.6
|
42
|
+
signing_key:
|
43
|
+
specification_version: 4
|
44
|
+
summary: General purpose structure for reporting results
|
45
|
+
test_files: []
|