xeme 0.3.1 → 1.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 (4) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +352 -260
  3. data/lib/xeme.rb +422 -450
  4. metadata +24 -11
data/lib/xeme.rb CHANGED
@@ -1,37 +1,37 @@
1
- require 'date'
2
- require 'json'
3
1
  require 'forwardable'
2
+ require 'declutter'
3
+
4
4
 
5
5
  #===============================================================================
6
6
  # Xeme
7
7
  #
8
-
9
- # Objects of this class represent a set of results. See the README file for
10
- # more details.
11
-
12
8
  class Xeme
13
- # version 0.3.1
14
- VERSION = '0.3.1'
9
+ # Returns the underlying hash that the xeme manages.
10
+ attr_reader :hsh
15
11
 
16
- # A Xeme::Messages object, which is basically just a hash containing arrays
17
- # for errors, warnings, and notes.
18
- attr_reader :messages
12
+ #---------------------------------------------------------------------------
13
+ # delegate
14
+ #
15
+ extend Forwardable
16
+ delegate %w([] []= each length clear delete to_json) => :hsh
17
+ #
18
+ # delegate
19
+ #---------------------------------------------------------------------------
19
20
 
20
- # A hash of any miscellaneous details you want to include.
21
- attr_reader :misc
22
21
 
23
22
  #---------------------------------------------------------------------------
24
23
  # initialize
25
24
  #
26
25
 
27
- # new() does not take any parameters.
26
+ # Creates a new Xeme object. Optionally takes a single strin parameter which
27
+ # is set as the id for the xeme.
28
28
 
29
- def initialize
30
- @messages = Xeme::Messages.new
31
- @misc = {}
32
- @prefixes = []
33
- @auto_details_val = nil
34
- @transaction = nil
29
+ def initialize(id=nil)
30
+ @hsh = {}
31
+
32
+ if id
33
+ meta id
34
+ end
35
35
  end
36
36
  #
37
37
  # initialize
@@ -39,627 +39,599 @@ class Xeme
39
39
 
40
40
 
41
41
  #---------------------------------------------------------------------------
42
- # errors, warnings, notes
42
+ # meta
43
43
  #
44
44
 
45
- ##
46
- # Returns the array of errors.
47
- def errors
48
- return @messages['errors']
45
+ # Returns the meta hash, creating it if necessary. The meta hash
46
+ # contains at least a timestamp and a UUID.
47
+
48
+ def meta(id=nil)
49
+ require 'securerandom'
50
+
51
+ # initialize meta element if necessary
52
+ @hsh['meta'] ||= {}
53
+
54
+ # populate meta
55
+ @hsh['meta']['uuid'] ||= SecureRandom.uuid().to_s
56
+ @hsh['meta']['timestamp'] ||= Time.now
57
+
58
+ # give child id if given
59
+ if id
60
+ meta['id'] = id
61
+ end
62
+
63
+ # return
64
+ return @hsh['meta']
65
+ end
66
+
67
+ # Returns the UUID in the meta hash.
68
+ def uuid
69
+ return meta['uuid']
49
70
  end
50
71
 
51
- ##
52
- # Returns the array of warnings.
53
- def warnings
54
- return @messages['warnings']
72
+ # Returns the timestamp in the meta hash.
73
+ def timestamp
74
+ return meta['timestamp']
75
+ end
76
+
77
+ # Returns the id element of the meta hash if there is one.
78
+ def id
79
+ if @hsh['meta']
80
+ return @hsh['meta']['id']
81
+ else
82
+ return nil
83
+ end
55
84
  end
56
85
 
57
- ##
58
- # Returns the array of notes.
59
- def notes
60
- return @messages['notes']
86
+ # Sets id element of the meta.
87
+ def id=(v)
88
+ return meta['id'] = v
61
89
  end
62
90
  #
63
- # errors, warnings, notes
91
+ # meta
64
92
  #---------------------------------------------------------------------------
65
93
 
66
94
 
67
95
  #---------------------------------------------------------------------------
68
- # transaction
96
+ # flatten
69
97
  #
70
98
 
71
- ##
72
- # Returns the transaction object, creating it if necessary. See
73
- # Xeme::Transaction. If the transaction object is never created then it is
74
- # not output with #to_json. The transaction object can be used to provide
75
- # meta information about the request and response that produced the
76
- # results. See Xeme::Transaction for more information.
99
+ # Folds all nested messages into the outermost xeme, and deletes nested
100
+ # xemes. Only the messages are folded in, not any metainformation or other
101
+ # information that might be in the nested xemes.
102
+
103
+ def flatten
104
+ resolve()
105
+
106
+ as_arr('nested').each do |child|
107
+ child.flatten
108
+
109
+ %w{errors warnings notes promises}.each do |msg_key|
110
+ if child[msg_key]
111
+ @hsh[msg_key] ||= []
112
+ @hsh[msg_key] += child[msg_key]
113
+ child.delete(msg_key)
114
+ end
115
+ end
116
+ end
117
+
118
+ @hsh.delete('nested')
119
+ end
77
120
  #
78
- # You can create the transaction object by simply calling this method:
121
+ # flatten
122
+ #---------------------------------------------------------------------------
123
+
124
+
125
+ #---------------------------------------------------------------------------
126
+ # to_h
79
127
  #
80
- # xeme.transaction
128
+ # def to_h
129
+ # rv = @hsh.to_h
130
+ #
131
+ # # loop through nested xemes
132
+ # if rv['nested']
133
+ # rv['nested'] = rv['nested'].map do |child|
134
+ # child.to_h
135
+ # end
136
+ # end
137
+ #
138
+ # # return
139
+ # return rv
140
+ # end
81
141
  #
82
- # If you want to assign a request ID, call this method and assign to its
83
- # request property. For example, if there's a request object that provides
84
- # an id, you could assign the transaction.request ID like this:
142
+ # to_h
143
+ #---------------------------------------------------------------------------
144
+
145
+
146
+ #---------------------------------------------------------------------------
147
+ # succeed
85
148
  #
86
- # xeme.transaction.request = request.id
87
149
 
88
- def transaction
89
- @transaction ||= Xeme::Transaction.new()
90
- return @transaction
150
+ # Attempt to set success to true. Raises an exception if there are any
151
+ # errors or promises in this or any nested Xeme.
152
+
153
+ def succeed
154
+ # if any errors, don't succeed
155
+ if errors.any?
156
+ raise 'cannot-set-to-success: errors'
157
+ end
158
+
159
+ # if any promises, don't succeed
160
+ if promises.any?
161
+ raise 'cannot-set-to-success: promises'
162
+ end
163
+
164
+ # set to success
165
+ return @hsh['success'] = true
91
166
  end
167
+
92
168
  #
93
- # transaction
169
+ # succeed
94
170
  #---------------------------------------------------------------------------
95
171
 
96
172
 
97
173
  #---------------------------------------------------------------------------
98
- # to_h
174
+ # try_succeed
99
175
  #
100
176
 
101
- ##
102
- # Returns a hash with all the results. This hash can be used to create a JSON
103
- # object. Empty arrays, such as if there are no warnings, are not included.
177
+ # Attempt to set success to true. Does not raise an exception if there are
178
+ # errors or promises, but in those cases will not set success to true.
104
179
 
105
- def to_h
106
- # $tm.hrm
107
-
108
- # initialize return value
109
- rv = {}
180
+ def try_succeed(resolve_self=true)
181
+ enforce_nil = false
110
182
 
111
- # success
112
- rv['success'] = success()
183
+ # resolve self and descendents
184
+ if resolve_self
185
+ resolve()
186
+ end
113
187
 
114
- # transaction
115
- if @transaction
116
- rv['transaction'] = @transaction.to_h
188
+ # if any promises
189
+ if as_arr('promises').any?
190
+ @hsh.delete 'success'
191
+ enforce_nil = true
117
192
  end
118
193
 
119
- # if any messages
120
- if @messages.any?
121
- msgs = rv['messages'] = {}
194
+ # try_succeed for descendents
195
+ as_arr('nested').each do |child|
196
+ child.try_succeed false
122
197
 
123
- @messages.each do |key, arr|
124
- if arr.any?
125
- msgs[key] = []
126
-
127
- arr.each do |msg|
128
- msgs[key].push msg.to_h
129
- end
198
+ if @hsh['success'].nil?
199
+ if child['success'].nil?
200
+ enforce_nil = true
201
+ elsif not child['success']
202
+ @hsh['success'] = false
130
203
  end
131
204
  end
132
205
  end
133
206
 
134
- # misc
135
- if @misc.any?
136
- rv['misc'] = @misc
207
+ # set to success if success is nil and no promises
208
+ if @hsh['success'].nil? and (! enforce_nil)
209
+ @hsh['success'] = true
137
210
  end
138
211
 
139
212
  # return
140
- return rv
213
+ return @hsh['success']
141
214
  end
215
+
142
216
  #
143
- # to_h
217
+ # try_succeed
144
218
  #---------------------------------------------------------------------------
145
219
 
146
220
 
147
221
  #---------------------------------------------------------------------------
148
- # to_json
222
+ # fail
149
223
  #
150
224
 
151
- ##
152
- # Returns a JSON string containing all the result information.
153
- def to_json(opts={})
154
- if opts['pretty']
155
- return JSON.pretty_generate(self.to_h)
156
- else
157
- return JSON.generate(self.to_h)
158
- end
225
+ # Explicitly set success to false. Does not add an error.
226
+
227
+ def fail
228
+ @hsh['success'] = false
229
+ resolve()
159
230
  end
231
+
160
232
  #
161
- # to_json
233
+ # fail
162
234
  #---------------------------------------------------------------------------
163
235
 
164
236
 
165
237
  #---------------------------------------------------------------------------
166
- # success
238
+ # success?
167
239
  #
168
-
169
- ##
170
- # Returns false if there are any errors, true otherwise.
171
-
172
- def success
173
- return @messages['errors'].empty?
240
+ def success?
241
+ resolve()
242
+ return @hsh['success']
174
243
  end
175
-
176
- ##
177
- # Returns the opposite of <tt>success()</tt>.
178
- def failure
179
- return success ? false : true
180
- end
181
-
182
244
  #
183
- # success
245
+ # success?
184
246
  #---------------------------------------------------------------------------
185
247
 
186
248
 
187
249
  #---------------------------------------------------------------------------
188
- # save_exception
250
+ # messages
189
251
  #
190
252
 
191
- # Use this method to hold on to details about an exception. An error message
192
- # object will be created, along with the exception's to_s and backtrace.
253
+ # Returns a locked array of all errors, including errors in nested xemes.
254
+ # If the optional id param is sent, returns only errors with that id.
255
+ def errors(id=nil)
256
+ return all_messages('errors', id)
257
+ end
258
+
259
+ # Returns a locked array of all warnings, including warnings in nested xemes.
260
+ # If the optional id param is sent, returns only warnings with that id.
261
+ def warnings(id=nil)
262
+ return all_messages('warnings', id)
263
+ end
264
+
265
+ # Returns a locked array of all notes, including notes in nested xemes.
266
+ # If the optional id param is sent, returns only notes with that id.
267
+ def notes(id=nil)
268
+ return all_messages('notes', id)
269
+ end
193
270
 
194
- def save_exception(id, e, details={})
195
- message = self.error(id, details)
196
- message['error'] = e.to_s
197
- message['backtrace'] = e.backtrace
271
+ # Returns a locked array of all promises, including promises in nested xemes.
272
+ # If the optional id param is sent, returns only promises with that id.
273
+ def promises(id=nil)
274
+ return all_messages('promises', id)
198
275
  end
276
+
199
277
  #
200
- # save_exception
278
+ # messages
201
279
  #---------------------------------------------------------------------------
202
280
 
203
281
 
204
282
  #---------------------------------------------------------------------------
205
- # prefix
283
+ # message hashes
206
284
  #
207
285
 
208
- ##
209
- # <tt>prefix</tt> allows you to automatically add prefixes to your message
210
- # codes in situations where many messages will start with the same string.
211
- # Consider the following situation.
212
- #
213
- # xeme.error '/virginia/slope'
214
- # xeme.error '/virginia/angle'
215
- # xeme.error '/virginia/departure'
216
- #
217
- # That works, but the redundancy can get confusing (and annoying). With
218
- # <tt>prefix</tt> you can avoid the redundancy like this:
219
- #
220
- # xeme.prefix('virginia') do
221
- # xeme.error 'slope'
222
- # xeme.error 'angle'
223
- # xeme.error 'departure'
224
- # end
225
- #
226
- # puts xeme.errors[0].id # /virginia/slope
227
- # puts xeme.errors[1].id # /virginia/angle
228
- # puts xeme.errors[2].id # /virginia/departure
229
- #
230
- # Prefixes can also be nested, like this:
231
- #
232
- # xeme.prefix('virginia') do
233
- # xeme.prefix('fairfax') do
234
- # xeme.error 'slope'
235
- # end
236
- # end
237
- #
238
- # puts xeme.errors[0].id # /virginia/fairfax/slope
286
+ # Returns a hash of all errors, including nested errors. The key for each
287
+ # element is the id of the error(s). Does not return errors that don't have
288
+ # ids.
289
+ def errors_hash()
290
+ return messages_hash('errors')
291
+ end
239
292
 
240
- def prefix(p_prefix)
241
- # $tm.hrm
242
- hold_prefixes = @prefixes.clone
243
- @prefixes = @prefixes.clone
244
- @prefixes.push p_prefix
245
-
246
- begin
247
- yield
248
- ensure
249
- @prefixes = hold_prefixes
250
- end
293
+ # Returns a hash of all warnings, including nested warnings. The key for
294
+ # each element is the id of the warnings(s). Does not return warnings that
295
+ # don't have ids.
296
+ def warnings_hash(id=nil)
297
+ return messages_hash('warnings')
298
+ end
299
+
300
+ # Returns a hash of all notes, including nested notes. The key for each
301
+ # element is the id of the notes(s). Does not return notes that don't have
302
+ # ids.
303
+ def notes_hash(id=nil)
304
+ return messages_hash('notes')
305
+ end
306
+
307
+ # Returns a hash of all promises, including nested promises. The key for
308
+ # each element is the id of the promise(s). Does not return promises that
309
+ # don't have ids.
310
+ def promises_hash(id=nil)
311
+ return messages_hash('promises')
251
312
  end
313
+
252
314
  #
253
- # prefix
315
+ # message hashes
254
316
  #---------------------------------------------------------------------------
255
317
 
256
318
 
257
319
  #---------------------------------------------------------------------------
258
- # error, warning, note
320
+ # add message
259
321
  #
260
322
 
261
- ##
262
- # Creates a message object and stores it in the errors array.
263
- #
264
- # xeme.error('error-1').
265
- #
266
- # A hash of arbitrary details can be given. Those details will be stored
267
- # with the message object in its Xeme::Message#details property.
268
- #
269
- # xeme.error('error-0', 'choice'=>'a')
270
- #
271
- # This method returns the message object, which can be used as another
272
- # way to store details.
273
- #
274
- # msg = xeme.error('error-1').
275
- # msg['location'] = 1
276
- #
277
- # If a block is given, the block is called with the error object as the
278
- # single param.
279
- #
280
- # xeme.error('error-2') do |error|
281
- # error['location'] = 2
282
- # end
283
- def error(id, details={}, &block)
284
- return add_comment(Xeme::Message, 'errors', id, details={}, &block)
323
+ # Creates and returns an error message. Accepts a do block which yields the
324
+ # new message. If given the opional id param, sets that value as the id for
325
+ # the message.
326
+ def error(id, &block)
327
+ @hsh['success'] = false
328
+ return message('errors', id, &block)
285
329
  end
286
330
 
287
- ##
288
- # Works just #error() except it stores the message in the warnings array.
289
- def warning(id, details={}, &block)
290
- return add_comment(Xeme::Message, 'warnings', id, details={}, &block)
331
+ # Creates and returns a warning message. Accepts a do block which yields
332
+ # the new message. If given the opional id param, sets that value as the id
333
+ # for the new message.
334
+ def warning(id, &block)
335
+ return message('warnings', id, &block)
291
336
  end
292
337
 
293
- ##
294
- # Works just #error() except it stores the message in the notes array.
295
- def note(id, details={}, &block)
296
- return add_comment(Xeme::Message, 'notes', id, details={}, &block)
338
+ # Creates and returns a note message. Accepts a do block which yields
339
+ # the new message. If given the opional id param, sets that value as the id
340
+ # for the new message.
341
+ def note(id, &block)
342
+ return message('notes', id, &block)
297
343
  end
344
+
345
+ # Creates and returns a promise message. Accepts a do block which yields
346
+ # the new message. If given the opional id param, sets that value as the id
347
+ # for the new message.
348
+ def promise(id, &block)
349
+ unless @hsh['success'].is_a?(FalseClass)
350
+ @hsh.delete 'success'
351
+ end
352
+
353
+ return message('promises', id, &block)
354
+ end
355
+
298
356
  #
299
- # error, warning, note
357
+ # add message
300
358
  #---------------------------------------------------------------------------
301
359
 
302
360
 
303
361
  #---------------------------------------------------------------------------
304
- # from_json
362
+ # nest
305
363
  #
306
364
 
307
- # Creates a new Xeme object from a JSON structure.
365
+ # Creates a new xeme and nests it in the current xeme. Yields to a do block
366
+ # if one is sent. Returns the new child xeme.
308
367
 
309
- def self.from_json(raw_json)
310
- # $tm.hrm
311
- hsh = JSON.parse(raw_json)
312
- rv = self.new
368
+ def nest(id=nil)
369
+ @hsh['nested'] ||= []
370
+ child = self.class.new()
371
+ @hsh['nested'].push child
313
372
 
314
- # messages
315
- if messages = hsh['messages']
316
- messages.each do |msg_type, arr|
317
- rv.messages[msg_type] ||= []
318
- rv.messages[msg_type] += arr
319
- end
320
- end
321
-
322
- # misc
323
- if misc = hsh['misc']
324
- misc.each do |key, val|
325
- rv.misc[key] = val
326
- end
373
+ # give id
374
+ if id
375
+ child.meta['id'] = id
327
376
  end
328
377
 
329
- # transaction
330
- if transaction = hsh['transaction']
331
- if transaction['timestamp']
332
- rv.transaction.timestamp = DateTime.parse(transaction['timestamp'])
333
- end
334
-
335
- if transaction['request']
336
- rv.transaction.request = transaction['request']
337
- end
338
-
339
- if transaction['response']
340
- rv.transaction.response = transaction['response']
341
- end
378
+ # yield in block
379
+ if block_given?
380
+ yield child
342
381
  end
343
382
 
344
383
  # return
345
- return rv
384
+ return child
346
385
  end
386
+
347
387
  #
348
- # from_json
388
+ # nest
349
389
  #---------------------------------------------------------------------------
350
390
 
351
391
 
352
392
  #---------------------------------------------------------------------------
353
- # new_exception
393
+ # nested
354
394
  #
355
-
356
- # Creates and returns a Xeme::Exception object. The Xeme object is attached
357
- # to the Xeme::Exception object.
358
-
359
- def new_exception(error_id)
360
- return Xeme::Exception.new(error_id, self)
361
- end
395
+ # def nested()
396
+ # @hsh['nested'] ||= []
397
+ # return @hsh['nested']
398
+ # end
362
399
  #
363
- # new_exception
400
+ # nested
364
401
  #---------------------------------------------------------------------------
365
402
 
366
403
 
367
- # private methods
368
- private
369
-
370
-
371
404
  #---------------------------------------------------------------------------
372
- # messages_to_h
405
+ # resolve
373
406
  #
374
- def messages_to_h(arr)
375
- rv = []
376
-
377
- # messages
378
- arr.each do |msg|
379
- rv.push msg.to_h
380
- end
381
-
382
- # return
383
- return rv
384
- end
385
- #
386
- # messages_to_h
387
- #---------------------------------------------------------------------------
388
407
 
408
+ # Resolves conflicts between errors, promises, and success. See README.md
409
+ # for details. You generally don't need to call this method yourself.
389
410
 
390
- #---------------------------------------------------------------------------
391
- # add_comment
392
- #
393
- def add_comment(cls, type, id, details={}, &block)
394
- # $tm.hrm
395
-
396
- # build message
397
- msg = cls.new(prefix_id(id), details)
411
+ def resolve
412
+ # resolve descendents
413
+ as_arr('nested').each do |child|
414
+ child.resolve
415
+ end
398
416
 
399
- # ensure array
400
- @messages[type] ||= []
417
+ # if own errors, fail
418
+ if as_arr('errors').any?
419
+ @hsh['success'] = false
420
+ return
421
+ end
401
422
 
402
- # add to errors array
403
- @messages[type].push(msg)
423
+ # if explicitly set to false, return
424
+ if @hsh['success'].is_a?(FalseClass)
425
+ return
426
+ end
404
427
 
405
- # yield
406
- if block_given?
407
- yield msg
428
+ # if any promises, set success to nil
429
+ if as_arr('promises').any?
430
+ @hsh.delete 'success'
408
431
  end
409
432
 
410
- # return
411
- return msg
433
+ # if any child is set to nil, set self to nil
434
+ # if any child set to false, set self to false and return
435
+ as_arr('nested').each do |child|
436
+ if child['success'].nil?
437
+ @hsh.delete 'success'
438
+ elsif not child['success']
439
+ @hsh['success'] = false
440
+ return
441
+ end
442
+ end
412
443
  end
413
444
  #
414
- # add_comment
445
+ # resolve
415
446
  #---------------------------------------------------------------------------
416
447
 
417
448
 
418
449
  #---------------------------------------------------------------------------
419
- # prefix_id
450
+ # declutter
420
451
  #
421
- def prefix_id(p_id)
422
- if @prefixes.any?
423
- return '/' + @prefixes.join('/') + '/' + p_id
424
- else
425
- return p_id
426
- end
452
+
453
+ # Removes empty arrays and hahes.
454
+
455
+ def declutter
456
+ resolve()
457
+ Declutter.process @hsh
458
+ return true
427
459
  end
428
460
  #
429
- # prefix_id
461
+ # declutter
430
462
  #---------------------------------------------------------------------------
431
- end
432
- #
433
- # Xeme
434
- #===============================================================================
435
-
436
-
437
- #===============================================================================
438
- # Xeme::Message
439
- #
440
-
441
- # A Xeme::Message object represents a single error, warning, or note.
442
- # It is the base class for Xeme::Message::Error,
443
- # Xeme::Message::Warning, and
444
- # Xeme::Message::Note. Don't instantiate Xeme::Message
445
- # directly, use Xeme#error, Xeme#warning, or Xeme#note.
446
- #
447
- # Each message contains at least an id. It can also have an options details
448
- # hash which can be used to store misc information about the message.
449
- #
450
- # The message itself can be treated like a hash to give details:
451
- #
452
- # msg = xeme.error('my-error')
453
- # msg['location'] = 1
454
-
455
- class Xeme::Message
456
- # The id of the message.
457
- attr_reader :id
458
-
459
- # Details about the message.
460
- attr_reader :details
461
-
462
- # delegate to hsh
463
- extend Forwardable
464
- delegate %w([] []= each length clear delete) => :@details
465
463
 
466
464
 
467
465
  #---------------------------------------------------------------------------
468
- # initialize
466
+ # all
469
467
  #
470
468
 
471
- # Initialize a Xeme::Message object with the id of the message. The id can
472
- # be any string. The optional <tt>details</tt> hash will be stored with the
473
- # message.
469
+ # Returns an array consisting of the xeme and all nested xemes.
474
470
 
475
- def initialize(id, details={})
476
- # $tm.hr __method__
471
+ def all(seek_id=nil)
472
+ rv = []
473
+
474
+ # add self
475
+ if seek_id
476
+ if id == seek_id
477
+ rv.push self
478
+ end
479
+ else
480
+ rv.push self
481
+ end
482
+
483
+ # loop through nested children
484
+ as_arr('nested').each do |child|
485
+ rv += child.all(seek_id)
486
+ end
477
487
 
478
- # set id
479
- @id = id
488
+ # freeze return value
489
+ rv.freeze
480
490
 
481
- # initialize details
482
- @details = details.clone
491
+ # return
492
+ return rv
483
493
  end
484
494
  #
485
- # initialize
495
+ # all
486
496
  #---------------------------------------------------------------------------
487
497
 
488
498
 
489
499
  #---------------------------------------------------------------------------
490
- # to_h
500
+ # misc
491
501
  #
492
502
 
493
- ##
494
- # Returns a hash structure of the message. This structure is used by
495
- # Xeme#to_h.
503
+ # A handy place to puts miscellaneous information.
496
504
 
497
- def to_h
498
- rv = {}
499
- rv['id'] = @id
500
-
501
- if @details.any?
502
- rv['details'] = @details
503
- end
504
-
505
- return rv
505
+ def misc
506
+ @hsh['misc'] ||= {}
507
+ return @hsh['misc']
506
508
  end
509
+
507
510
  #
508
- # to_h
511
+ # misc
509
512
  #---------------------------------------------------------------------------
510
- end
511
- #
512
- # Xeme::Message
513
- #===============================================================================
514
-
515
-
516
- #===============================================================================
517
- # Xeme::Messages
518
- #
519
-
520
- ##
521
- # This object holds the arrays of errors, warnings, and notes. It can mostly be
522
- # treated like a hash. The main difference between a Xeme::Messages object and
523
- # a hash is that any? returns true or false based on if there are any messages,
524
- # not if there are any elements in the hash.
525
-
526
- class Xeme::Messages
527
- # delegate to hsh
528
- extend Forwardable
529
- delegate %w([] []= each length keys has_key?) => :@hsh
513
+
514
+
515
+ # private
516
+ private
517
+
530
518
 
531
519
  #---------------------------------------------------------------------------
532
- # initialize
520
+ # as_arr
533
521
  #
534
522
 
535
- ##
536
- # new() takes no parameters.
523
+ # Convenience function for reading messages as arrays even if they don't
524
+ # exist.
537
525
 
538
- def initialize
539
- @hsh = {}
540
- @hsh['errors'] = []
541
- @hsh['warnings'] = []
542
- @hsh['notes'] = []
526
+ def as_arr(key)
527
+ return @hsh[key] || []
543
528
  end
544
529
  #
545
- # initialize
530
+ # as_arr
546
531
  #---------------------------------------------------------------------------
547
532
 
548
533
 
549
534
  #---------------------------------------------------------------------------
550
- # any?
535
+ # message
551
536
  #
552
537
 
553
- ##
554
- # Returns if there are any messages in any of the errors, warnings, or
555
- # notes arrays.
538
+ # Creates a message object of the given type.
556
539
 
557
- def any?
558
- @hsh.values.each do |arr|
559
- arr.any? and return true
540
+ def message(type, id, &block)
541
+ # build message
542
+ msg = {}
543
+ id and msg['id'] = id
544
+
545
+ # add to array
546
+ @hsh[type] ||= []
547
+ @hsh[type].push msg
548
+
549
+ # yield if necessary
550
+ if block_given?
551
+ yield msg
560
552
  end
561
553
 
562
- return false
554
+ # return
555
+ return msg
563
556
  end
564
557
  #
565
- # any?
558
+ # message
566
559
  #---------------------------------------------------------------------------
567
- end
568
- #
569
- # Xeme::Messages
570
- #===============================================================================
571
-
572
-
573
- #===============================================================================
574
- # Xeme::Transaction
575
- #
576
-
577
- # An object of this class provides meta information about the request and
578
- # results. It always provides a timestamp and a unique ID for the results. It
579
- # may also optionally include a request ID that was provided by the process
580
- # that made the request, such as a call to a REST application. Do not directly
581
- # instantiate this class; use Xeme#transaction.
582
-
583
- class Xeme::Transaction
584
-
585
- # Optional. An ID for the request that was sent by the calling process. Do
586
- # not generate this value yourself; use the ID that was sent with the
587
- # request (if one was sent).
588
- attr_accessor :request
589
560
 
590
- # Gives a unique ID for these results.
591
- attr_accessor :response
592
-
593
- # Gives a timestamp for when these results were generated.
594
- attr_accessor :timestamp
595
561
 
596
562
  #---------------------------------------------------------------------------
597
- # initialize
563
+ # all_messages
598
564
  #
599
565
 
600
- # Initialize does not take any parameters.
566
+ # Returns a locked array of all messages (including nested) or the given
567
+ # type.
601
568
 
602
- def initialize
603
- @timestamp = DateTime.now()
604
- @response = rand().to_s.sub(/\A0\./mu, '')
605
- @request = nil
569
+ def all_messages(plural, id)
570
+ rv = []
571
+
572
+ # add own messages
573
+ if @hsh[plural]
574
+ if id
575
+ @hsh[plural].each do |m|
576
+ if m['id'] == id
577
+ rv.push m
578
+ end
579
+ end
580
+ else
581
+ rv += @hsh[plural]
582
+ end
583
+ end
584
+
585
+ # recurse
586
+ as_arr('nested').each do |child|
587
+ rv += child.send(plural, id)
588
+ end
589
+
590
+ # freeze return value
591
+ rv.freeze
592
+
593
+ # return
594
+ return rv
606
595
  end
607
596
  #
608
- # initialize
597
+ # all_messages
609
598
  #---------------------------------------------------------------------------
610
599
 
611
600
 
612
601
  #---------------------------------------------------------------------------
613
- # to_h
602
+ # messages_hash
614
603
  #
615
- def to_h
604
+
605
+ # Returns a hash of the messages (including nested) of the given type. The
606
+ # keys to the hash are the message ids. Does not return messages that don't
607
+ # have ids.
608
+
609
+ def messages_hash(type)
616
610
  rv = {}
617
611
 
618
- # request
619
- if @request
620
- rv['request'] = @request
612
+ # add own messages
613
+ as_arr(type).each do |msg|
614
+ if msg['id']
615
+ rv[msg['id']] ||= []
616
+ rv[msg['id']].push msg
617
+ end
621
618
  end
622
619
 
623
- # response and timestamp
624
- rv['response'] = @response
625
- rv['timestamp'] = @timestamp.to_s
620
+ # loop through nested children
621
+ as_arr('nested').each do |child|
622
+ child.send("#{type}_hash").each do |k, msgs|
623
+ rv[k] ||= []
624
+ rv[k] += msgs
625
+ end
626
+ end
626
627
 
627
628
  # return
628
629
  return rv
629
630
  end
630
631
  #
631
- # to_h
632
+ # messages_hash
632
633
  #---------------------------------------------------------------------------
633
634
  end
634
635
  #
635
- # Xeme::Transaction
636
- #===============================================================================
637
-
638
-
639
- #===============================================================================
640
- # Xeme::Exception
641
- #
642
-
643
- # Xeme::Exception is a subclass of StandardError and can be used exactly like a
644
- # StandardError object. The one addition to StandardError is the `xeme` property
645
- # which holds a Xeme object. Don't instantiate this class directly; use
646
- # Xeme#new_exception.
647
-
648
- class Xeme::Exception < StandardError
649
- attr_reader :xeme # a xeme object
650
-
651
- #---------------------------------------------------------------------------
652
- # initialize
653
- #
654
- def initialize(error_code, xeme)
655
- @error_code = error_code
656
- @xeme = xeme
657
- super @error_code
658
- end
659
- #
660
- # initialize
661
- #---------------------------------------------------------------------------
662
- end
663
- #
664
- # Xeme::Exception
665
- #===============================================================================
636
+ # Xeme
637
+ #===============================================================================