xeme 0.3 → 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 +423 -414
  4. metadata +24 -11
data/lib/xeme.rb CHANGED
@@ -1,44 +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
- ##
10
- # Objects of this class represent a set of results. See the README file for
11
- # more details.
12
-
13
8
  class Xeme
14
- # version 0.3
15
- VERSION = '0.3'
9
+ # Returns the underlying hash that the xeme manages.
10
+ attr_reader :hsh
16
11
 
17
- # A Xeme::Messages object, which is basically just a hash containing arrays
18
- # for errors, warnings, and notes.
19
- attr_reader :messages
12
+ #---------------------------------------------------------------------------
13
+ # delegate
14
+ #
15
+ extend Forwardable
16
+ delegate %w([] []= each length clear delete to_json) => :hsh
17
+ #
18
+ # delegate
19
+ #---------------------------------------------------------------------------
20
20
 
21
- # A hash of any miscellaneous details you want to include.
22
- attr_reader :misc
23
21
 
24
22
  #---------------------------------------------------------------------------
25
23
  # initialize
26
24
  #
27
25
 
28
- ##
29
- # 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.
30
28
 
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
29
+ def initialize(id=nil)
30
+ @hsh = {}
39
31
 
40
- # transaction
41
- @transaction = nil
32
+ if id
33
+ meta id
34
+ end
42
35
  end
43
36
  #
44
37
  # initialize
@@ -46,583 +39,599 @@ class Xeme
46
39
 
47
40
 
48
41
  #---------------------------------------------------------------------------
49
- # errors, warnings, notes
42
+ # meta
50
43
  #
51
44
 
52
- ##
53
- # Returns the array of errors.
54
- def errors
55
- 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']
70
+ end
71
+
72
+ # Returns the timestamp in the meta hash.
73
+ def timestamp
74
+ return meta['timestamp']
56
75
  end
57
76
 
58
- ##
59
- # Returns the array of warnings.
60
- def warnings
61
- return @messages['warnings']
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
62
84
  end
63
85
 
64
- ##
65
- # Returns the array of notes.
66
- def notes
67
- return @messages['notes']
86
+ # Sets id element of the meta.
87
+ def id=(v)
88
+ return meta['id'] = v
68
89
  end
69
90
  #
70
- # errors, warnings, notes
91
+ # meta
71
92
  #---------------------------------------------------------------------------
72
93
 
73
94
 
74
95
  #---------------------------------------------------------------------------
75
- # transaction
96
+ # flatten
76
97
  #
77
98
 
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.
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
84
120
  #
85
- # You can create the transaction object by simply calling this method:
121
+ # flatten
122
+ #---------------------------------------------------------------------------
123
+
124
+
125
+ #---------------------------------------------------------------------------
126
+ # to_h
86
127
  #
87
- # 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
88
141
  #
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:
142
+ # to_h
143
+ #---------------------------------------------------------------------------
144
+
145
+
146
+ #---------------------------------------------------------------------------
147
+ # succeed
92
148
  #
93
- # xeme.transaction.request = request.id
94
149
 
95
- def transaction
96
- @transaction ||= Xeme::Transaction.new()
97
- 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
98
166
  end
167
+
99
168
  #
100
- # transaction
169
+ # succeed
101
170
  #---------------------------------------------------------------------------
102
171
 
103
172
 
104
173
  #---------------------------------------------------------------------------
105
- # to_h
174
+ # try_succeed
106
175
  #
107
176
 
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.
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.
111
179
 
112
- def to_h
113
- # $tm.hrm
114
-
115
- # initialize return value
116
- rv = {}
180
+ def try_succeed(resolve_self=true)
181
+ enforce_nil = false
117
182
 
118
- # success
119
- rv['success'] = success()
183
+ # resolve self and descendents
184
+ if resolve_self
185
+ resolve()
186
+ end
120
187
 
121
- # transaction
122
- if @transaction
123
- rv['transaction'] = @transaction.to_h
188
+ # if any promises
189
+ if as_arr('promises').any?
190
+ @hsh.delete 'success'
191
+ enforce_nil = true
124
192
  end
125
193
 
126
- # if any messages
127
- if @messages.any?
128
- msgs = rv['messages'] = {}
194
+ # try_succeed for descendents
195
+ as_arr('nested').each do |child|
196
+ child.try_succeed false
129
197
 
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
198
+ if @hsh['success'].nil?
199
+ if child['success'].nil?
200
+ enforce_nil = true
201
+ elsif not child['success']
202
+ @hsh['success'] = false
137
203
  end
138
204
  end
139
205
  end
140
206
 
141
- # misc
142
- if @misc.any?
143
- 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
144
210
  end
145
211
 
146
212
  # return
147
- return rv
213
+ return @hsh['success']
148
214
  end
215
+
149
216
  #
150
- # to_h
217
+ # try_succeed
151
218
  #---------------------------------------------------------------------------
152
219
 
153
220
 
154
221
  #---------------------------------------------------------------------------
155
- # to_json
222
+ # fail
156
223
  #
157
224
 
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
225
+ # Explicitly set success to false. Does not add an error.
226
+
227
+ def fail
228
+ @hsh['success'] = false
229
+ resolve()
166
230
  end
231
+
167
232
  #
168
- # to_json
233
+ # fail
169
234
  #---------------------------------------------------------------------------
170
235
 
171
236
 
172
237
  #---------------------------------------------------------------------------
173
- # success
238
+ # success?
174
239
  #
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
240
+ def success?
241
+ resolve()
242
+ return @hsh['success']
187
243
  end
188
-
189
244
  #
190
- # success
245
+ # success?
191
246
  #---------------------------------------------------------------------------
192
247
 
193
248
 
194
249
  #---------------------------------------------------------------------------
195
- # exception
250
+ # messages
196
251
  #
197
252
 
198
- ##
199
- # Use this method to hold on to details about an exception. An error message
200
- # object will be created, along with the exception's to_s 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
201
264
 
202
- def exception(id, e, details={})
203
- message = self.error(id, details)
204
- message['error'] = e.to_s
205
- message['backtrace'] = e.backtrace
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)
206
269
  end
270
+
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)
275
+ end
276
+
207
277
  #
208
- # exception
278
+ # messages
209
279
  #---------------------------------------------------------------------------
210
280
 
211
281
 
212
282
  #---------------------------------------------------------------------------
213
- # prefix
283
+ # message hashes
214
284
  #
215
285
 
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
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
247
292
 
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
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')
259
312
  end
313
+
260
314
  #
261
- # prefix
315
+ # message hashes
262
316
  #---------------------------------------------------------------------------
263
317
 
264
318
 
265
319
  #---------------------------------------------------------------------------
266
- # error, warning, note
320
+ # add message
267
321
  #
268
322
 
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)
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)
293
329
  end
294
330
 
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)
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)
299
336
  end
300
337
 
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)
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)
305
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
+
306
356
  #
307
- # error, warning, note
357
+ # add message
308
358
  #---------------------------------------------------------------------------
309
359
 
310
360
 
311
361
  #---------------------------------------------------------------------------
312
- # from_json
362
+ # nest
313
363
  #
314
364
 
315
- # 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.
316
367
 
317
- def self.from_json(raw_json)
318
- # $tm.hrm
319
- hsh = JSON.parse(raw_json)
320
- rv = self.new
368
+ def nest(id=nil)
369
+ @hsh['nested'] ||= []
370
+ child = self.class.new()
371
+ @hsh['nested'].push child
321
372
 
322
- # messages
323
- if messages = hsh['messages']
324
- messages.each do |msg_type, arr|
325
- rv.messages[msg_type] ||= []
326
- rv.messages[msg_type] += arr
327
- end
373
+ # give id
374
+ if id
375
+ child.meta['id'] = id
328
376
  end
329
377
 
330
- # misc
331
- if misc = hsh['misc']
332
- misc.each do |key, val|
333
- rv.misc[key] = val
334
- end
335
- end
336
-
337
- # transaction
338
- if transaction = hsh['transaction']
339
- if transaction['timestamp']
340
- rv.transaction.timestamp = DateTime.parse(transaction['timestamp'])
341
- end
342
-
343
- if transaction['request']
344
- rv.transaction.request = transaction['request']
345
- end
346
-
347
- if transaction['response']
348
- rv.transaction.response = transaction['response']
349
- end
378
+ # yield in block
379
+ if block_given?
380
+ yield child
350
381
  end
351
382
 
352
383
  # return
353
- return rv
384
+ return child
354
385
  end
386
+
355
387
  #
356
- # from_json
388
+ # nest
357
389
  #---------------------------------------------------------------------------
358
390
 
359
391
 
360
- # private methods
361
- private
362
-
363
-
364
392
  #---------------------------------------------------------------------------
365
- # messages_to_h
393
+ # nested
366
394
  #
367
- def messages_to_h(arr)
368
- rv = []
369
-
370
- # messages
371
- arr.each do |msg|
372
- rv.push msg.to_h
373
- end
374
-
375
- # return
376
- return rv
377
- end
395
+ # def nested()
396
+ # @hsh['nested'] ||= []
397
+ # return @hsh['nested']
398
+ # end
378
399
  #
379
- # messages_to_h
400
+ # nested
380
401
  #---------------------------------------------------------------------------
381
402
 
382
403
 
383
404
  #---------------------------------------------------------------------------
384
- # add_comment
405
+ # resolve
385
406
  #
386
- def add_comment(cls, type, id, details={}, &block)
387
- # $tm.hrm
388
-
389
- # build message
390
- msg = cls.new(prefix_id(id), details)
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.
410
+
411
+ def resolve
412
+ # resolve descendents
413
+ as_arr('nested').each do |child|
414
+ child.resolve
415
+ end
391
416
 
392
- # ensure array
393
- @messages[type] ||= []
417
+ # if own errors, fail
418
+ if as_arr('errors').any?
419
+ @hsh['success'] = false
420
+ return
421
+ end
394
422
 
395
- # add to errors array
396
- @messages[type].push(msg)
423
+ # if explicitly set to false, return
424
+ if @hsh['success'].is_a?(FalseClass)
425
+ return
426
+ end
397
427
 
398
- # yield
399
- if block_given?
400
- yield msg
428
+ # if any promises, set success to nil
429
+ if as_arr('promises').any?
430
+ @hsh.delete 'success'
401
431
  end
402
432
 
403
- # return
404
- 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
405
443
  end
406
444
  #
407
- # add_comment
445
+ # resolve
408
446
  #---------------------------------------------------------------------------
409
447
 
410
448
 
411
449
  #---------------------------------------------------------------------------
412
- # prefix_id
450
+ # declutter
413
451
  #
414
- def prefix_id(p_id)
415
- if @prefixes.any?
416
- return '/' + @prefixes.join('/') + '/' + p_id
417
- else
418
- return p_id
419
- end
452
+
453
+ # Removes empty arrays and hahes.
454
+
455
+ def declutter
456
+ resolve()
457
+ Declutter.process @hsh
458
+ return true
420
459
  end
421
460
  #
422
- # prefix_id
461
+ # declutter
423
462
  #---------------------------------------------------------------------------
424
- end
425
- #
426
- # Xeme
427
- #===============================================================================
428
-
429
-
430
- #===============================================================================
431
- # Xeme::Message
432
- #
433
-
434
- ##
435
- # A Xeme::Message object represents a single error, warning, or note.
436
- # It is the base class for Xeme::Message::Error,
437
- # Xeme::Message::Warning, and
438
- # Xeme::Message::Note. Don't instantiate Xeme::Message
439
- # directly, use Xeme#error, Xeme#warning, or Xeme#note.
440
- #
441
- # Each message contains at least an id. It can also have an options details
442
- # hash which can be used to store misc information about the message.
443
- #
444
- # The message itself can be treated like a hash to give details:
445
- #
446
- # msg = xeme.error('my-error')
447
- # msg['location'] = 1
448
-
449
- class Xeme::Message
450
- # The id of the message.
451
- attr_reader :id
452
-
453
- # delegate to hsh
454
- extend Forwardable
455
- delegate %w([] []= each length clear delete) => :@details
456
463
 
457
464
 
458
465
  #---------------------------------------------------------------------------
459
- # initialize
466
+ # all
460
467
  #
461
468
 
462
- # Initialize a Xeme::Message object with the id of the message. The id can
463
- # be any string. The optional <tt>details</tt> hash will be stored with the
464
- # message.
469
+ # Returns an array consisting of the xeme and all nested xemes.
465
470
 
466
- def initialize(id, details={})
467
- # $tm.hr __method__
471
+ def all(seek_id=nil)
472
+ rv = []
468
473
 
469
- # set id
470
- @id = id
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
471
482
 
472
- # initialize details
473
- @details = details.clone
483
+ # loop through nested children
484
+ as_arr('nested').each do |child|
485
+ rv += child.all(seek_id)
486
+ end
487
+
488
+ # freeze return value
489
+ rv.freeze
490
+
491
+ # return
492
+ return rv
474
493
  end
475
494
  #
476
- # initialize
495
+ # all
477
496
  #---------------------------------------------------------------------------
478
497
 
479
498
 
480
499
  #---------------------------------------------------------------------------
481
- # to_h
500
+ # misc
482
501
  #
483
502
 
484
- ##
485
- # Returns a hash structure of the message. This structure is used by
486
- # Xeme#to_h.
503
+ # A handy place to puts miscellaneous information.
487
504
 
488
- def to_h
489
- rv = {}
490
- rv['id'] = @id
491
-
492
- if @details.any?
493
- rv['details'] = @details
494
- end
495
-
496
- return rv
505
+ def misc
506
+ @hsh['misc'] ||= {}
507
+ return @hsh['misc']
497
508
  end
509
+
498
510
  #
499
- # to_h
511
+ # misc
500
512
  #---------------------------------------------------------------------------
501
- end
502
- #
503
- # Xeme::Message
504
- #===============================================================================
505
-
506
-
507
- #===============================================================================
508
- # Xeme::Messages
509
- #
510
-
511
- ##
512
- # This object holds the arrays of errors, warnings, and notes. It can mostly be
513
- # treated like a hash. The main difference between a Xeme::Messages object and
514
- # a hash is that any? returns true or false based on if there are any messages,
515
- # not if there are any elements in the hash.
516
-
517
- class Xeme::Messages
518
- # delegate to hsh
519
- extend Forwardable
520
- delegate %w([] []= each length keys has_key?) => :@hsh
513
+
514
+
515
+ # private
516
+ private
517
+
521
518
 
522
519
  #---------------------------------------------------------------------------
523
- # initialize
520
+ # as_arr
524
521
  #
525
522
 
526
- ##
527
- # new() takes no parameters.
523
+ # Convenience function for reading messages as arrays even if they don't
524
+ # exist.
528
525
 
529
- def initialize
530
- @hsh = {}
531
- @hsh['errors'] = []
532
- @hsh['warnings'] = []
533
- @hsh['notes'] = []
526
+ def as_arr(key)
527
+ return @hsh[key] || []
534
528
  end
535
529
  #
536
- # initialize
530
+ # as_arr
537
531
  #---------------------------------------------------------------------------
538
532
 
539
533
 
540
534
  #---------------------------------------------------------------------------
541
- # any?
535
+ # message
542
536
  #
543
537
 
544
- ##
545
- # Returns if there are any messages in any of the errors, warnings, or
546
- # notes arrays.
538
+ # Creates a message object of the given type.
547
539
 
548
- def any?
549
- @hsh.values.each do |arr|
550
- 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
551
552
  end
552
553
 
553
- return false
554
+ # return
555
+ return msg
554
556
  end
555
557
  #
556
- # any?
558
+ # message
557
559
  #---------------------------------------------------------------------------
558
- end
559
- #
560
- # Xeme::Messages
561
- #===============================================================================
562
-
563
-
564
- #===============================================================================
565
- # Xeme::Transaction
566
- #
567
-
568
- ##
569
- # An object of this class provdes meta information about the request and
570
- # results. It always provides a a timestamp and a unique ID for the results. It
571
- # may also optionally include a request ID that was provided by the process
572
- # that made the request, such as a call to a REST application. Do not directly
573
- # instantiate this class; use Xeme#transaction.
574
-
575
- class Xeme::Transaction
576
-
577
- # Optional. An ID for the request that was sent by the calling process. Do
578
- # not generate this value yourself; use the ID that was sent with the
579
- # request (if one was sent).
580
- attr_accessor :request
581
560
 
582
- # Gives a unique ID for these results.
583
- attr_accessor :response
584
-
585
- # Gives a timestamp for when these results were generated.
586
- attr_accessor :timestamp
587
561
 
588
562
  #---------------------------------------------------------------------------
589
- # initialize
563
+ # all_messages
590
564
  #
591
565
 
592
- # Initialize does not take any parameters.
566
+ # Returns a locked array of all messages (including nested) or the given
567
+ # type.
593
568
 
594
- def initialize
595
- @timestamp = DateTime.now()
596
- @response = rand().to_s.sub(/\A0\./mu, '')
597
- @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
598
595
  end
599
596
  #
600
- # initialize
597
+ # all_messages
601
598
  #---------------------------------------------------------------------------
602
599
 
603
600
 
604
601
  #---------------------------------------------------------------------------
605
- # to_h
602
+ # messages_hash
606
603
  #
607
- 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)
608
610
  rv = {}
609
611
 
610
- # request
611
- if @request
612
- 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
613
618
  end
614
619
 
615
- # response and timestamp
616
- rv['response'] = @response
617
- 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
618
627
 
619
628
  # return
620
629
  return rv
621
630
  end
622
631
  #
623
- # to_h
632
+ # messages_hash
624
633
  #---------------------------------------------------------------------------
625
634
  end
626
635
  #
627
- # Xeme::Transaction
628
- #===============================================================================
636
+ # Xeme
637
+ #===============================================================================