xeme 0.3 → 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 +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
+ #===============================================================================