xeme 0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|