xeme 0.3.1 → 1.0

Sign up to get free protection for your applications and to get access to all the features.
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
+ #===============================================================================