audrey 0.2

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 +7 -0
  2. data/README.md +610 -0
  3. data/lib/audrey.rb +2503 -0
  4. metadata +74 -0
@@ -0,0 +1,2503 @@
1
+ require 'securerandom'
2
+ require 'forwardable'
3
+ require 'fileutils'
4
+ require 'xeme'
5
+
6
+
7
+ #===============================================================================
8
+ # Audrey
9
+ #
10
+ class Audrey
11
+ attr_reader :engine
12
+ attr_accessor :autopurge
13
+ attr_reader :partition
14
+ attr_reader :closers
15
+ @@current_db = nil
16
+
17
+ # version
18
+ VERSION = '0.2'
19
+
20
+ #---------------------------------------------------------------------------
21
+ # delegate
22
+ #
23
+ extend Forwardable
24
+
25
+ # root object
26
+ delegate(%w(
27
+ [] []= each length keys values delete clear
28
+ == <= >= ===
29
+ has_key? any?
30
+ ) => :@root);
31
+
32
+ # engine
33
+ delegate %w(commit rollback in_transaction?) => :@engine
34
+
35
+ #
36
+ # delegate
37
+ #---------------------------------------------------------------------------
38
+
39
+
40
+ #---------------------------------------------------------------------------
41
+ # initialize
42
+ #
43
+ def initialize(*params)
44
+ @org_opts = self.class.params_to_opts(*params)
45
+
46
+ # mode
47
+ # KLUDGE: For now, just always using the mode that was sent with the
48
+ # "new" command. Eventually we'll need to recognize that different
49
+ # partitions will have different modes
50
+ @mode = Audrey::Mode.new(@org_opts['mode'])
51
+
52
+ # finalizer
53
+ ObjectSpace.define_finalizer(self, proc { self.class.finalize(self.object_id) })
54
+
55
+ # engine opts
56
+ @engine_opts = {}
57
+
58
+ # immediate_commit
59
+ if @org_opts.has_key?('immediate_commit')
60
+ @engine_opts['immediate_commit'] = @org_opts['immediate_commit']
61
+ end
62
+
63
+ # default autopurge
64
+ @autopurge = true
65
+
66
+ # connect engine
67
+ connect_engine()
68
+ end
69
+ #
70
+ # initialize
71
+ #---------------------------------------------------------------------------
72
+
73
+
74
+ #---------------------------------------------------------------------------
75
+ # connect_engine
76
+ #
77
+ def connect_engine
78
+ # objects that should be closed before the database is closed
79
+ @closers = {}
80
+
81
+ # sqlite3
82
+ if @org_opts['engine'] == 'sqlite3'
83
+ require 'audrey/engine/sqlite3'
84
+ @engine = Audrey::Engine::SQLite3.new(@org_opts['connect'], @mode, @engine_opts)
85
+ else
86
+ # @engine = self.class.initialize_engine(opts)
87
+ raise 'not-yet-implemented-non-sqlite-engine'
88
+ end
89
+
90
+ # default partition to m
91
+ self.partition = 'm'
92
+ end
93
+ #
94
+ # connect_engine
95
+ #---------------------------------------------------------------------------
96
+
97
+
98
+ #---------------------------------------------------------------------------
99
+ # reset
100
+ #
101
+ def reset
102
+ close()
103
+ connect_engine()
104
+ end
105
+ #
106
+ # reset
107
+ #---------------------------------------------------------------------------
108
+
109
+
110
+ #---------------------------------------------------------------------------
111
+ # partition=
112
+ #
113
+ def partition=(val)
114
+ # $tm.hrm
115
+
116
+ # set partition
117
+ @partition = val
118
+ @engine.partition = val
119
+
120
+ # set root
121
+ @root = Audrey::Node::Hash.new(self, 'pk'=>@engine.root_pk)
122
+ end
123
+ #
124
+ # partition=
125
+ #---------------------------------------------------------------------------
126
+
127
+
128
+ #---------------------------------------------------------------------------
129
+ # connect
130
+ #
131
+ def self.connect(*opts)
132
+ if block_given?
133
+ begin
134
+ db = self.new(*opts)
135
+
136
+ db.current do
137
+ yield db
138
+ end
139
+
140
+ # implement db.exit by catching Audrey::Exception::DatabaseExit
141
+ rescue Audrey::Exception::DatabaseExit => e
142
+ # NOTE: for whatever reason, e.db == db doesn't return true,
143
+ # even though they're the same object. Using object_id to
144
+ # bypass that problem.
145
+ unless e.db.object_id == db.object_id
146
+ raise e
147
+ end
148
+
149
+ # return out of this method
150
+ return nil
151
+
152
+ # ensure database is closed
153
+ ensure
154
+ db and db.close
155
+ end
156
+ else
157
+ return self.new(*opts)
158
+ end
159
+ end
160
+ #
161
+ # connect
162
+ #---------------------------------------------------------------------------
163
+
164
+
165
+ #---------------------------------------------------------------------------
166
+ # exit
167
+ #
168
+ def exit()
169
+ e = Audrey::Exception::DatabaseExit.new(self)
170
+ raise e
171
+ end
172
+ #
173
+ # exit
174
+ #---------------------------------------------------------------------------
175
+
176
+
177
+ #---------------------------------------------------------------------------
178
+ # current
179
+ #
180
+ def current
181
+ hold_current = Audrey.current_db
182
+ Audrey.current_db = self
183
+ yield
184
+ ensure
185
+ Audrey.current_db = hold_current
186
+ end
187
+
188
+ def self.current_db(opts={})
189
+ return @@current_db
190
+ end
191
+
192
+ def self.explicit_or_current_db(accessor)
193
+ # $tm.hrm
194
+
195
+ # explicit accessor was sent
196
+ if accessor
197
+ return accessor
198
+
199
+ # else return current database
200
+ else
201
+ return @@current_db
202
+ end
203
+ end
204
+
205
+ def self.current_db=(db)
206
+ @@current_db = db
207
+ end
208
+ #
209
+ # current
210
+ #---------------------------------------------------------------------------
211
+
212
+
213
+ #---------------------------------------------------------------------------
214
+ # session_pk
215
+ #
216
+ def session_pk
217
+ if not instance_variable_defined?(:@session_pk)
218
+ @session_pk = Audrey::Util.uuid
219
+ end
220
+
221
+ return @session_pk
222
+ end
223
+ #
224
+ # session_pk
225
+ #---------------------------------------------------------------------------
226
+
227
+
228
+ #---------------------------------------------------------------------------
229
+ # finalize
230
+ #
231
+ def self.finalize(id)
232
+ # $tm.hrm
233
+ obj = ObjectSpace._id2ref(id)
234
+ obj.close()
235
+ end
236
+ #
237
+ # finalize
238
+ #---------------------------------------------------------------------------
239
+
240
+
241
+ #---------------------------------------------------------------------------
242
+ # close
243
+ #
244
+ def close
245
+ # closers
246
+ @closers.keys.each do |k|
247
+ @closers[k].close
248
+ @closers.delete(k)
249
+ end
250
+
251
+ # purge
252
+ if @engine
253
+ @engine.close 'purge'=>@autopurge
254
+ end
255
+ end
256
+ #
257
+ # close
258
+ #---------------------------------------------------------------------------
259
+
260
+
261
+ #---------------------------------------------------------------------------
262
+ # build_array
263
+ #
264
+ def build_array(row)
265
+ node = Audrey::Node::Array.new(self, 'pk'=>row['pk'])
266
+ rv = []
267
+ node.attach_to rv
268
+ return rv
269
+ end
270
+ #
271
+ # build_array
272
+ #---------------------------------------------------------------------------
273
+
274
+
275
+ #---------------------------------------------------------------------------
276
+ # purge
277
+ #
278
+ def purge
279
+ # $tm.hrm
280
+ @engine.purge
281
+ end
282
+ #
283
+ # purge
284
+ #---------------------------------------------------------------------------
285
+
286
+
287
+ #---------------------------------------------------------------------------
288
+ # read_check, write_check
289
+ #
290
+ def read_check
291
+ # $tm.hrm
292
+ @mode.read or raise Audrey::Exception::Permission::Read.new()
293
+ end
294
+
295
+ def write_check
296
+ @mode.write or raise Audrey::Exception::Permission::Write.new()
297
+ end
298
+ #
299
+ # read_check, write_check
300
+ #---------------------------------------------------------------------------
301
+
302
+
303
+ #---------------------------------------------------------------------------
304
+ # ensure_object_record
305
+ #
306
+ def ensure_object_record(object)
307
+ # $tm.hrm
308
+ write_check()
309
+
310
+ # early exit: object already has a audrey object
311
+ if object.respond_to?('audrey')
312
+ return object.audrey.pk
313
+ end
314
+
315
+ # scalar
316
+ if dfn = Audrey::RUBY_OBJECT_TO_AUDREY[object.class]
317
+ if dfn['nclass']
318
+ pk = @engine.insert_object(object)
319
+ node = dfn['nclass'].new(self, 'pk'=>pk)
320
+ node.attach_to object
321
+ return pk
322
+ else
323
+ return @engine.insert_object(object)
324
+ end
325
+ end
326
+ end
327
+ #
328
+ # ensure_object_record
329
+ #---------------------------------------------------------------------------
330
+
331
+
332
+ #---------------------------------------------------------------------------
333
+ # object_from_row
334
+ #
335
+ def object_from_row(row)
336
+ # $tm.hrm
337
+
338
+ # scalar
339
+ if dfn = Audrey::AUDREY_SCALAR_TO_RUBY[row['fclass']]
340
+ return dfn.call(row['scalar'])
341
+ end
342
+
343
+ # collection
344
+ if dfn = Audrey::AUDREY_COLLECTION_TO_RUBY[row['fclass']]
345
+ return dfn['nclass'].create_object(self, 'row'=>row)
346
+ end
347
+
348
+ # custom class
349
+ if Module.const_defined?(row['fclass'])
350
+ clss = Module.const_get(row['fclass'])
351
+ nclass = Audrey::AUDREY_COLLECTION_TO_RUBY[clss.bclass.to_s]['nclass']
352
+ node = nclass.new(self, 'row'=>row)
353
+ rv = clss.new('db'=>self, 'node'=>node)
354
+ return rv
355
+ end
356
+
357
+ # if we get this far then we don't know how to turn the row into a
358
+ # Audrey object
359
+ raise 'unknown-fclass: ' + row['fclass'].to_s
360
+ end
361
+ #
362
+ # object_from_row
363
+ #---------------------------------------------------------------------------
364
+
365
+
366
+ #---------------------------------------------------------------------------
367
+ # insert_object
368
+ #
369
+ def insert_object(object)
370
+ write_check()
371
+ checks_outside_transaction object.class
372
+ return @engine.insert_object(object)
373
+ end
374
+ #
375
+ # insert_object
376
+ #---------------------------------------------------------------------------
377
+
378
+
379
+ #---------------------------------------------------------------------------
380
+ # object_from_pk
381
+ #
382
+ def object_from_pk(pk)
383
+ row = @engine.row_by_pk(pk)
384
+
385
+ if row
386
+ return object_from_row(row)
387
+ else
388
+ return nil
389
+ end
390
+ end
391
+ #
392
+ # object_from_pk
393
+ #---------------------------------------------------------------------------
394
+
395
+
396
+ #---------------------------------------------------------------------------
397
+ # q0
398
+ #
399
+ def q0
400
+ require 'audrey/query/q0'
401
+ return Audrey::Query::Q0.new(self)
402
+ end
403
+ #
404
+ # q0
405
+ #---------------------------------------------------------------------------
406
+
407
+
408
+ #---------------------------------------------------------------------------
409
+ # import_csv
410
+ #
411
+ def import_csv(fclass, path, opts={})
412
+ # $tm.hrm
413
+ return @engine.import_csv(fclass, path, opts)
414
+ end
415
+ #
416
+ # import_csv
417
+ #---------------------------------------------------------------------------
418
+
419
+
420
+ #---------------------------------------------------------------------------
421
+ # checks_outside_transaction
422
+ #
423
+ def checks_outside_transaction(fclass)
424
+ if fclass.respond_to?('has_checks?')
425
+ if (not in_transaction?) and fclass.has_checks?
426
+ raise 'cannot-create-or-modify-object-that-has-checks-when-not-in-a-transaction'
427
+ end
428
+ end
429
+ end
430
+ #
431
+ # checks_outside_transaction
432
+ #---------------------------------------------------------------------------
433
+
434
+
435
+ #---------------------------------------------------------------------------
436
+ # transaction
437
+ #
438
+ def transaction(opts={})
439
+ # $tm.hrm
440
+
441
+ # yield to block and return nil
442
+ if block_given?
443
+ @engine.transaction(opts) do |tr|
444
+ # yield
445
+ yield tr
446
+
447
+ # run checks
448
+ # xeme = run_checks()
449
+
450
+ # autocommit
451
+ if opts['autocommit']
452
+ tr.commit
453
+ end
454
+ end
455
+
456
+ # else just return transaction
457
+ else
458
+ raise 'not-yet-implemented-non-block-transaction'
459
+ end
460
+ end
461
+ #
462
+ # transaction
463
+ #---------------------------------------------------------------------------
464
+
465
+
466
+ #---------------------------------------------------------------------------
467
+ # autocommit
468
+ #
469
+ def autocommit(opts={}, &block)
470
+ # $tm.hrm
471
+ opts = {'autocommit'=>true}.merge(opts)
472
+ transaction opts, &block
473
+ end
474
+ #
475
+ # autocommit
476
+ #---------------------------------------------------------------------------
477
+
478
+
479
+ #---------------------------------------------------------------------------
480
+ # run_checks
481
+ #
482
+ def run_checks()
483
+ # $tm.hrm
484
+ xeme = Xeme.new()
485
+
486
+ # loop through records that have been changed
487
+ changed_objects() do |obj|
488
+ if obj.class.has_checks?
489
+ obj.class.fields.each do |fieldname, dfn|
490
+ val = obj.audrey[fieldname]
491
+
492
+ # require
493
+ if dfn.required and val.nil?
494
+ xeme.error('required') do |msg|
495
+ msg['field'] = fieldname
496
+ end
497
+
498
+ # wrong class
499
+ elsif dfn.fclass and (not val.is_a?(dfn.fclass))
500
+ xeme.error('wrong-fclass') do |msg|
501
+ msg['field'] = fieldname
502
+ msg['fclass'] = dfn.fclass.to_s
503
+ end
504
+
505
+ # run checks
506
+ else
507
+ dfn.base_checks xeme, fieldname, val
508
+ end
509
+ end
510
+ else
511
+ obj.audrey.changed = false
512
+ end
513
+ end
514
+
515
+ # return
516
+ return xeme
517
+ end
518
+ #
519
+ # run_checks
520
+ #---------------------------------------------------------------------------
521
+
522
+
523
+ #---------------------------------------------------------------------------
524
+ # changed_objects
525
+ #
526
+ def changed_objects()
527
+ @engine.changed_objects do |row|
528
+ yield self.object_from_row(row)
529
+ end
530
+ end
531
+ #
532
+ # changed_objects
533
+ #---------------------------------------------------------------------------
534
+
535
+
536
+ # private
537
+ private
538
+
539
+
540
+ #---------------------------------------------------------------------------
541
+ # initialize_engine
542
+ #
543
+ def self.initialize_engine(opts)
544
+ # $tm.hrm
545
+ engine = opts['engine']
546
+
547
+ # if already an engine, return it
548
+ if engine.is_a?(Audrey::Engine)
549
+ return engine
550
+ end
551
+
552
+ # if class, return that
553
+ if engine.is_a?(Class) and (engine < Audrey::Engine)
554
+ return engine.new(opts)
555
+ end
556
+ end
557
+ #
558
+ # initialize_engine
559
+ #---------------------------------------------------------------------------
560
+
561
+
562
+ #---------------------------------------------------------------------------
563
+ # params_to_opts
564
+ #
565
+ def self.params_to_opts(*params)
566
+ # $tm.hrm
567
+ # $tm.show params
568
+
569
+ # if single value and it's a hash, return that
570
+ if (params.length == 1) and params[0].is_a?(Hash)
571
+ return params[0]
572
+ end
573
+
574
+ # default engine
575
+ default = {'engine'=>'sqlite3'}
576
+
577
+ # if three, and third is a hash, use that as the options
578
+ if (params.length == 3) and params[2].is_a?(Hash)
579
+ default = default.merge(params.pop)
580
+ end
581
+
582
+ # if two params, then they are the connection string and the mode
583
+ if (params.length == 2)
584
+ rv = {'connect'=>params[0], 'mode'=>params[1]}
585
+ rv = default.merge(rv)
586
+ return rv
587
+ end
588
+
589
+ # else invalid input
590
+ raise 'invalid-params-for-initialize'
591
+ end
592
+ #
593
+ # params_to_opts
594
+ #---------------------------------------------------------------------------
595
+ end
596
+ #
597
+ # Audrey
598
+ #===============================================================================
599
+
600
+
601
+ #===============================================================================
602
+ # Audrey::Transaction
603
+ #
604
+ class Audrey::Transaction
605
+ #---------------------------------------------------------------------------
606
+ # initialize
607
+ #
608
+ def initialize(db, opts={})
609
+ # $tm.hrm
610
+ @db = db
611
+ end
612
+ #
613
+ # initialize
614
+ #---------------------------------------------------------------------------
615
+ end
616
+ #
617
+ # Audrey::Transaction
618
+ #===============================================================================
619
+
620
+
621
+ #===============================================================================
622
+ # Audrey::Engine
623
+ #
624
+ class Audrey::Engine
625
+ end
626
+ #
627
+ # Audrey::Engine
628
+ #===============================================================================
629
+
630
+
631
+ #===============================================================================
632
+ # Audrey::Mode
633
+ #
634
+ class Audrey::Mode
635
+ attr_reader :read
636
+ attr_reader :write
637
+
638
+ #---------------------------------------------------------------------------
639
+ # initialize
640
+ #
641
+ def initialize(p_mode)
642
+ # $tm.hrm
643
+
644
+ # mode cannot be tainted
645
+ if p_mode.tainted?
646
+ raise 'cannot-use-tainted-read-write-mode'
647
+ end
648
+
649
+ # set read and write
650
+ if (p_mode == 'rw') or (p_mode == 'wr')
651
+ @read = true
652
+ @write = true
653
+ elsif p_mode == 'r'
654
+ @read = true
655
+ @write = false
656
+ elsif p_mode == 'w'
657
+ @read = false
658
+ @write = true
659
+ else
660
+ raise 'unknown-mode: ' + p_mode.to_s
661
+ end
662
+ end
663
+ #
664
+ # initialize
665
+ #---------------------------------------------------------------------------
666
+ end
667
+ #
668
+ # Audrey::Mode
669
+ #===============================================================================
670
+
671
+
672
+ #===============================================================================
673
+ # Audrey::Node
674
+ # This class defines an object that connects to the database for a single
675
+ # object record.
676
+ #
677
+ class Audrey::Node
678
+ attr_reader :pk
679
+ attr_reader :db
680
+ extend Forwardable
681
+
682
+ # delegate read and write checks to database
683
+ delegate(%w(read_check write_check) => :@db);
684
+
685
+
686
+ #---------------------------------------------------------------------------
687
+ # initialize
688
+ #
689
+ def initialize(p_db, opts={})
690
+ # param check
691
+ if not p_db.is_a?(Audrey)
692
+ raise 'db-param-not-audrey: ' + p_db.class.to_s
693
+ end
694
+
695
+ # db and engine
696
+ @db = p_db
697
+ @engine = @db.engine
698
+
699
+ # hold on to flcass if one was sent
700
+ @fclass = opts['fclass']
701
+
702
+ # if a row was sent, cache it and set pk
703
+ if opts['row']
704
+ @row_cache = opts['row']
705
+ @pk = @row_cache['pk']
706
+ else
707
+ @pk = opts['pk'] || Audrey::Util.uuid
708
+ @row_cache = nil
709
+ end
710
+ end
711
+ #
712
+ # initialize
713
+ #---------------------------------------------------------------------------
714
+
715
+
716
+ #---------------------------------------------------------------------------
717
+ # row
718
+ #
719
+ def row()
720
+ # $tm.hrm
721
+ read_check()
722
+
723
+ if not @row_cache
724
+ @row_cache = @engine.row_by_pk(@pk)
725
+ end
726
+
727
+ return @row_cache
728
+ end
729
+ #
730
+ # row
731
+ #---------------------------------------------------------------------------
732
+
733
+
734
+ #---------------------------------------------------------------------------
735
+ # fco, isolate
736
+ #
737
+ def fco
738
+ # $tm.hrm
739
+ return Audrey::Util.fclass_fco(row['fclass'])
740
+ end
741
+
742
+ def isolate
743
+ # $tm.hrm
744
+ return Audrey::Util.fclass_isolate(row['fclass'])
745
+ end
746
+ #
747
+ # fco, isolate
748
+ #---------------------------------------------------------------------------
749
+
750
+
751
+ #---------------------------------------------------------------------------
752
+ # ancestors
753
+ #
754
+ def ancestors
755
+ read_check()
756
+
757
+ @engine.ancestors(@pk) do |row|
758
+ yield @db.object_from_row(row)
759
+ end
760
+ end
761
+ #
762
+ # ancestors
763
+ #---------------------------------------------------------------------------
764
+
765
+
766
+ #---------------------------------------------------------------------------
767
+ # alive?
768
+ #
769
+ def alive?
770
+ # $tm.hrm
771
+ return @engine.object_exists?(@pk)
772
+ end
773
+ #
774
+ # alive?
775
+ #---------------------------------------------------------------------------
776
+
777
+
778
+ #---------------------------------------------------------------------------
779
+ # delete_object
780
+ #
781
+ def delete_object
782
+ write_check()
783
+ return @engine.delete_object(@pk)
784
+ end
785
+ #
786
+ # delete_object
787
+ #---------------------------------------------------------------------------
788
+ end
789
+ #
790
+ # Audrey::Node
791
+ #===============================================================================
792
+
793
+
794
+ #===============================================================================
795
+ # Audrey::Node::Collection
796
+ #
797
+ class Audrey::Node::Collection < Audrey::Node
798
+ #---------------------------------------------------------------------------
799
+ # create_object
800
+ #
801
+ def self.create_object(p_db, opts={})
802
+ # $tm.hrm
803
+ obj = new_object()
804
+ node = self.new(p_db, opts)
805
+ node.attach_to obj
806
+ return obj
807
+ end
808
+ #
809
+ # create_object
810
+ #---------------------------------------------------------------------------
811
+
812
+
813
+ #---------------------------------------------------------------------------
814
+ # clear
815
+ #
816
+ def clear
817
+ write_check()
818
+ return @engine.clear_collection(@pk)
819
+ end
820
+ #
821
+ # clear
822
+ #---------------------------------------------------------------------------
823
+
824
+
825
+ #---------------------------------------------------------------------------
826
+ # length
827
+ #
828
+ def length
829
+ read_check()
830
+ return @engine.collection_length(@pk)
831
+ end
832
+ #
833
+ # length
834
+ #---------------------------------------------------------------------------
835
+
836
+
837
+ #---------------------------------------------------------------------------
838
+ # any?
839
+ #
840
+ def any?
841
+ read_check()
842
+ return @engine.collection_length(@pk) > 0
843
+ end
844
+ #
845
+ # any?
846
+ #---------------------------------------------------------------------------
847
+
848
+
849
+ #---------------------------------------------------------------------------
850
+ # child_pks
851
+ #
852
+ def child_pks
853
+ # $tm.hrm
854
+ return @engine.collection_child_pks(@pk)
855
+ end
856
+ #
857
+ # child_pks
858
+ #---------------------------------------------------------------------------
859
+
860
+
861
+ #---------------------------------------------------------------------------
862
+ # changed
863
+ #
864
+ def changed
865
+ # $tm.hrm
866
+ return @engine.changed_get(@pk)
867
+ end
868
+
869
+ def changed=(bool)
870
+ # $tm.hrm
871
+ return @engine.changed_set(@pk, bool)
872
+ end
873
+ #
874
+ # changed
875
+ #---------------------------------------------------------------------------
876
+
877
+
878
+ # private
879
+ private
880
+
881
+
882
+ #---------------------------------------------------------------------------
883
+ # save_child
884
+ #
885
+ def save_child(child, opts={})
886
+ write_check()
887
+ child_pk = @db.ensure_object_record(child)
888
+ @engine.add_relationship(@pk, child_pk, 'hkey'=>opts['key'])
889
+ end
890
+ #
891
+ # save_child
892
+ #---------------------------------------------------------------------------
893
+ end
894
+ #
895
+ # Audrey::Node::Collection
896
+ #===============================================================================
897
+
898
+
899
+ #===============================================================================
900
+ # Audrey::Node::Hash
901
+ #
902
+ class Audrey::Node::Hash < Audrey::Node::Collection
903
+ #---------------------------------------------------------------------------
904
+ # new_object
905
+ #
906
+ def self.new_object
907
+ return({})
908
+ end
909
+ #
910
+ # new_object
911
+ #---------------------------------------------------------------------------
912
+
913
+
914
+ #---------------------------------------------------------------------------
915
+ # []
916
+ #
917
+ def [](key)
918
+ # $tm.hrm
919
+ # puts "@pk: #{@pk}"
920
+ # puts "key: #{key}"
921
+
922
+ read_check()
923
+ hsh = @engine.row_hash_by_key(@pk, key)
924
+
925
+ # if we got a hash
926
+ if hsh
927
+ return @db.object_from_row(hsh)
928
+
929
+ # else return nil
930
+ else
931
+ return nil
932
+ end
933
+ end
934
+ #
935
+ # []
936
+ #---------------------------------------------------------------------------
937
+
938
+
939
+ #---------------------------------------------------------------------------
940
+ # []=
941
+ #
942
+ def []=(key, child)
943
+ write_check()
944
+
945
+ # test for checks outside of a transaction
946
+ @db.checks_outside_transaction @fclass
947
+
948
+ @engine.hash_element_delete @pk, key
949
+ save_child child, 'key'=>key.to_s
950
+ return child
951
+ end
952
+ #
953
+ # []=
954
+ #---------------------------------------------------------------------------
955
+
956
+
957
+ #---------------------------------------------------------------------------
958
+ # keys
959
+ #
960
+ def keys
961
+ read_check()
962
+ return @engine.hash_keys(@pk)
963
+ end
964
+ #
965
+ # keys
966
+ #---------------------------------------------------------------------------
967
+
968
+
969
+ #---------------------------------------------------------------------------
970
+ # values
971
+ #
972
+ def values
973
+ read_check()
974
+ rv = []
975
+
976
+ # loop through records
977
+ @engine.hash_each(@pk) do |key, row|
978
+ rv.push @db.object_from_row(row)
979
+ end
980
+
981
+ # return
982
+ return rv
983
+ end
984
+ #
985
+ # values
986
+ #---------------------------------------------------------------------------
987
+
988
+
989
+ #---------------------------------------------------------------------------
990
+ # each
991
+ #
992
+ def each
993
+ read_check()
994
+ rv = nil
995
+
996
+ # loop through records
997
+ @engine.hash_each(@pk) do |key, row|
998
+ object = @db.object_from_row(row)
999
+
1000
+ if block_given?
1001
+ yield key, object
1002
+ else
1003
+ rv ||= []
1004
+ rv.push [key, object]
1005
+ end
1006
+ end
1007
+
1008
+ # return
1009
+ return rv
1010
+ end
1011
+ #
1012
+ # each
1013
+ #---------------------------------------------------------------------------
1014
+
1015
+
1016
+ #---------------------------------------------------------------------------
1017
+ # delete
1018
+ #
1019
+ def delete(key)
1020
+ write_check()
1021
+ record = @engine.hash_element_delete(@pk, key)
1022
+
1023
+ # if record, return node, else return nil
1024
+ if record
1025
+ return @db.object_from_row(record)
1026
+ else
1027
+ return nil
1028
+ end
1029
+ end
1030
+ #
1031
+ # delete
1032
+ #---------------------------------------------------------------------------
1033
+
1034
+
1035
+ #---------------------------------------------------------------------------
1036
+ # has_key?
1037
+ #
1038
+ def has_key?(key)
1039
+ read_check()
1040
+ return @engine.hash_has_key?(@pk, key)
1041
+ end
1042
+ #
1043
+ # has_key?
1044
+ #---------------------------------------------------------------------------
1045
+
1046
+
1047
+ #---------------------------------------------------------------------------
1048
+ # attach_to
1049
+ #
1050
+ def attach_to(hsh)
1051
+ # hold on to original values
1052
+ orgs = hsh.clone
1053
+
1054
+ # audrey()
1055
+ def hsh.audrey(p_node = nil)
1056
+ if p_node
1057
+ @audrey_node = p_node
1058
+ end
1059
+
1060
+ # return
1061
+ return @audrey_node
1062
+ end
1063
+
1064
+ # []
1065
+ def hsh.[](key)
1066
+ return @audrey_node[key]
1067
+ end
1068
+
1069
+ # []=
1070
+ def hsh.[]=(key, child)
1071
+ return @audrey_node[key] = child
1072
+ end
1073
+
1074
+ # each
1075
+ def hsh.each()
1076
+ if block_given?
1077
+ @audrey_node.each do |k, v|
1078
+ yield k, v
1079
+ end
1080
+ else
1081
+ return @audrey_node.each
1082
+ end
1083
+ end
1084
+
1085
+ # length
1086
+ def hsh.length()
1087
+ return @audrey_node.length
1088
+ end
1089
+
1090
+ # keys
1091
+ def hsh.keys()
1092
+ return @audrey_node.keys
1093
+ end
1094
+
1095
+ # values
1096
+ def hsh.values()
1097
+ return @audrey_node.values
1098
+ end
1099
+
1100
+ # delete
1101
+ def hsh.delete(*opts)
1102
+ return @audrey_node.delete(*opts)
1103
+ end
1104
+
1105
+ # clear
1106
+ def hsh.clear()
1107
+ return @audrey_node.clear()
1108
+ end
1109
+
1110
+ # has_key?
1111
+ def hsh.has_key?(key)
1112
+ return @audrey_node.has_key?(key)
1113
+ end
1114
+
1115
+ # any?
1116
+ def hsh.any?()
1117
+ return @audrey_node.any?
1118
+ end
1119
+
1120
+ # ==
1121
+ def hsh.==(*opts)
1122
+ return @audrey_node.==(*opts)
1123
+ end
1124
+
1125
+ # <=
1126
+ def hsh.<=(*opts)
1127
+ return @audrey_node.<=(*opts)
1128
+ end
1129
+
1130
+ # >=
1131
+ def hsh.>=(*opts)
1132
+ return @audrey_node.>=(*opts)
1133
+ end
1134
+
1135
+ # ===
1136
+ def hsh.===(*opts)
1137
+ return @audrey_node.===(*opts)
1138
+ end
1139
+
1140
+ # initial call to audrey object
1141
+ hsh.audrey self
1142
+
1143
+ # save child elements
1144
+ orgs.each do |k, v|
1145
+ hsh[k] = v
1146
+ end
1147
+ end
1148
+ #
1149
+ # attach_to
1150
+ #---------------------------------------------------------------------------
1151
+ end
1152
+ #
1153
+ # Audrey::Node::Hash
1154
+ #===============================================================================
1155
+
1156
+
1157
+ #===============================================================================
1158
+ # Audrey::Node::Array
1159
+ #
1160
+ class Audrey::Node::Array < Audrey::Node::Collection
1161
+ #---------------------------------------------------------------------------
1162
+ # new_object
1163
+ #
1164
+ def self.new_object
1165
+ return([])
1166
+ end
1167
+ #
1168
+ # new_object
1169
+ #---------------------------------------------------------------------------
1170
+
1171
+
1172
+ #---------------------------------------------------------------------------
1173
+ # []
1174
+ #
1175
+ def [](idx)
1176
+ read_check()
1177
+ row = @engine.row_by_array_index(@pk, idx)
1178
+
1179
+ # if we got a hash
1180
+ if row
1181
+ return @db.object_from_row(row)
1182
+
1183
+ # else return nil
1184
+ else
1185
+ return nil
1186
+ end
1187
+ end
1188
+ #
1189
+ # []
1190
+ #---------------------------------------------------------------------------
1191
+
1192
+
1193
+ #---------------------------------------------------------------------------
1194
+ # []=
1195
+ #
1196
+ def []=(key, child)
1197
+ end
1198
+ #
1199
+ # []=
1200
+ #---------------------------------------------------------------------------
1201
+
1202
+
1203
+ #---------------------------------------------------------------------------
1204
+ # push
1205
+ #
1206
+ def push(child)
1207
+ write_check()
1208
+ save_child child
1209
+ return child
1210
+ end
1211
+ #
1212
+ # push
1213
+ #---------------------------------------------------------------------------
1214
+
1215
+
1216
+ #---------------------------------------------------------------------------
1217
+ # each
1218
+ #
1219
+ def each
1220
+ # $tm.hrm
1221
+ read_check()
1222
+ rv = nil
1223
+
1224
+ # loop through rows
1225
+ @engine.array_each(@pk) do |row|
1226
+ obj = @db.object_from_row(row)
1227
+
1228
+ if block_given?
1229
+ yield obj
1230
+ else
1231
+ rv ||= []
1232
+ rv.push obj
1233
+ end
1234
+ end
1235
+
1236
+ # return
1237
+ return rv
1238
+ end
1239
+
1240
+ def each_with_index
1241
+ idx = 0
1242
+
1243
+ each do |el|
1244
+ yield el, idx
1245
+ idx += 1
1246
+ end
1247
+ end
1248
+ #
1249
+ # each
1250
+ #---------------------------------------------------------------------------
1251
+
1252
+
1253
+ #---------------------------------------------------------------------------
1254
+ # to_array
1255
+ #
1256
+ def to_array
1257
+ rv = []
1258
+
1259
+ each do |el|
1260
+ rv.push el
1261
+ end
1262
+
1263
+ return rv
1264
+ end
1265
+ #
1266
+ # to_array
1267
+ #---------------------------------------------------------------------------
1268
+
1269
+
1270
+ #---------------------------------------------------------------------------
1271
+ # include?
1272
+ # NOTE: This routine could probably be better implemented by the database
1273
+ # engine. For now just doing it the brute force way.
1274
+ #
1275
+ def include?(val)
1276
+ # $tm.hrm
1277
+
1278
+ # if val has a audrey object
1279
+ if val.respond_to?('audrey')
1280
+ each do |object|
1281
+ if object.respond_to?('audrey')
1282
+ if val.audrey.pk == object.audrey.pk
1283
+ return true
1284
+ end
1285
+ end
1286
+ end
1287
+ else
1288
+ each do |object|
1289
+ if val === object
1290
+ return true
1291
+ end
1292
+ end
1293
+ end
1294
+
1295
+ # didn't find it
1296
+ return false
1297
+ end
1298
+ #
1299
+ # include?
1300
+ #---------------------------------------------------------------------------
1301
+
1302
+
1303
+ #---------------------------------------------------------------------------
1304
+ # attach_to
1305
+ #
1306
+ def attach_to(arr)
1307
+ # hold on to original values
1308
+ orgs = arr.clone
1309
+
1310
+ # audrey()
1311
+ def arr.audrey(p_node = nil)
1312
+ if p_node
1313
+ @audrey_node = p_node
1314
+ end
1315
+
1316
+ # return
1317
+ return @audrey_node
1318
+ end
1319
+
1320
+ # NOTE: There must be a simpler way to delegate all these hash methods
1321
+ # to @audrey_node
1322
+
1323
+ # []
1324
+ def arr.[](idx)
1325
+ return @audrey_node[idx]
1326
+ end
1327
+
1328
+ # push
1329
+ def arr.push(val)
1330
+ return @audrey_node.push(val)
1331
+ end
1332
+
1333
+ # each
1334
+ def arr.each()
1335
+ if block_given?
1336
+ @audrey_node.each do |k, v|
1337
+ yield k, v
1338
+ end
1339
+ else
1340
+ return @audrey_node.each
1341
+ end
1342
+ end
1343
+
1344
+ # each_with_index
1345
+ def arr.each_with_index()
1346
+ @audrey_node.each_with_index do |el, idx|
1347
+ yield el, idx
1348
+ end
1349
+ end
1350
+
1351
+ # length
1352
+ def arr.length()
1353
+ return @audrey_node.length
1354
+ end
1355
+
1356
+ # any?
1357
+ def arr.any?()
1358
+ return @audrey_node.any?
1359
+ end
1360
+
1361
+ # clear
1362
+ def arr.clear()
1363
+ return @audrey_node.clear()
1364
+ end
1365
+
1366
+ # join
1367
+ def arr.join(sep)
1368
+ rv = []
1369
+
1370
+ @audrey_node.each do |el|
1371
+ rv.push el
1372
+ end
1373
+
1374
+ return rv.join(sep)
1375
+ end
1376
+
1377
+ # include?
1378
+ def arr.include?(val)
1379
+ return @audrey_node.include?(val)
1380
+ end
1381
+
1382
+ # initial call to audrey object
1383
+ arr.audrey self
1384
+
1385
+ # save child elements
1386
+ orgs.each do |v|
1387
+ arr.push v
1388
+ end
1389
+ end
1390
+ #
1391
+ # attach_to
1392
+ #---------------------------------------------------------------------------
1393
+ end
1394
+ #
1395
+ # Audrey::Node::Array
1396
+ #===============================================================================
1397
+
1398
+
1399
+ #===============================================================================
1400
+ # Audrey::Util
1401
+ #
1402
+ module Audrey::Util
1403
+ #---------------------------------------------------------------------------
1404
+ # uuid
1405
+ #
1406
+ def self.uuid
1407
+ return SecureRandom.uuid
1408
+ end
1409
+ #
1410
+ # uuid
1411
+ #---------------------------------------------------------------------------
1412
+
1413
+
1414
+ #---------------------------------------------------------------------------
1415
+ # randstr
1416
+ # Always returns a string consisting of just alphabetic characters. String
1417
+ # is untainted.
1418
+ #
1419
+ def self.randstr(len=5)
1420
+ return (0...len).map { ('a'..'z').to_a[rand(26)] }.join.untaint
1421
+ end
1422
+ #
1423
+ # uuid
1424
+ #---------------------------------------------------------------------------
1425
+
1426
+
1427
+ #---------------------------------------------------------------------------
1428
+ # rclass_from_fclass
1429
+ #
1430
+ RCLASS_FROM_FCLASS_CACHE = {}
1431
+
1432
+ def self.rclass_from_fclass(fclass)
1433
+ if early = RCLASS_FROM_FCLASS_CACHE[fclass]
1434
+ return early
1435
+ end
1436
+
1437
+ if Module.const_defined?(fclass)
1438
+ rclass = Module.const_get(fclass)
1439
+
1440
+ if rclass < Audrey::Object
1441
+ RCLASS_FROM_FCLASS_CACHE[fclass] = rclass
1442
+ return rclass
1443
+ else
1444
+ raise 'not-custom-class: ' + fclass.to_s
1445
+ end
1446
+ else
1447
+ raise 'unknown-fclass: ' + fclass.to_s
1448
+ end
1449
+ end
1450
+ #
1451
+ # rclass_from_fclass
1452
+ #---------------------------------------------------------------------------
1453
+
1454
+
1455
+ #---------------------------------------------------------------------------
1456
+ # fclass_fco, fclass_isolate
1457
+ #
1458
+ def self.fclass_fco(fclass)
1459
+ return self.rclass_from_fclass(fclass).fco
1460
+ end
1461
+
1462
+ def self.fclass_isolate(fclass)
1463
+ return self.rclass_from_fclass(fclass).isolate
1464
+ end
1465
+ #
1466
+ # fclass_fco, fclass_isolate
1467
+ #---------------------------------------------------------------------------
1468
+
1469
+
1470
+ #---------------------------------------------------------------------------
1471
+ # graph_field?
1472
+ #
1473
+ def self.graph_field?(fclass, hkey)
1474
+ # $tm.hrm
1475
+
1476
+ if fclass and Module.const_defined?(fclass)
1477
+ clss = Module.const_get(fclass)
1478
+
1479
+ if clss.respond_to?('fields')
1480
+ if dfn = clss.fields[hkey]
1481
+ return dfn.graph
1482
+ end
1483
+ end
1484
+ end
1485
+
1486
+ # if we get this far then it's not a graph field
1487
+ return false
1488
+ end
1489
+ #
1490
+ # graph_field?
1491
+ #---------------------------------------------------------------------------
1492
+
1493
+
1494
+ #---------------------------------------------------------------------------
1495
+ # custom_class?
1496
+ #
1497
+ @@CUSTOM_CLASSES = {}
1498
+
1499
+ def self.custom_class?(fclass)
1500
+ # $tm.hrm
1501
+
1502
+ # cache
1503
+ if not @@CUSTOM_CLASSES.has_key?(fclass)
1504
+ # puts fclass
1505
+ @@CUSTOM_CLASSES[fclass] = rclass_from_fclass(fclass) < Audrey::Object::Custom
1506
+ end
1507
+
1508
+ # return
1509
+ return @@CUSTOM_CLASSES[fclass]
1510
+ end
1511
+ #
1512
+ # custom_class?
1513
+ #---------------------------------------------------------------------------
1514
+ end
1515
+ #
1516
+ # Audrey::Util
1517
+ #===============================================================================
1518
+
1519
+
1520
+ #===============================================================================
1521
+ # Audrey::Object
1522
+ #
1523
+ class Audrey::Object
1524
+ #---------------------------------------------------------------------------
1525
+ # inherited, descendants, fclasses
1526
+ #
1527
+ def self.inherited(desc)
1528
+ if not instance_variable_defined?(:@children)
1529
+ @children = []
1530
+ end
1531
+
1532
+ @children.push desc
1533
+ end
1534
+
1535
+ def self.descendants
1536
+ rv = []
1537
+
1538
+ if instance_variable_defined?(:@children)
1539
+ @children.each do |child|
1540
+ rv.push child
1541
+ rv += child.descendants
1542
+ end
1543
+ end
1544
+
1545
+ return rv
1546
+ end
1547
+
1548
+ def self.fclasses
1549
+ rv = [self.to_s]
1550
+
1551
+ descendants.each do |desc|
1552
+ rv.push desc.to_s
1553
+ end
1554
+
1555
+ return rv
1556
+ end
1557
+ #
1558
+ # inherited, descendants, fclasses
1559
+ #---------------------------------------------------------------------------
1560
+
1561
+
1562
+ #---------------------------------------------------------------------------
1563
+ # fco, isolate
1564
+ #
1565
+ def self.fco()
1566
+ if not instance_variable_defined?(:@fco)
1567
+ @fco = superclass.fco
1568
+
1569
+ if @fco.nil?
1570
+ raise 'must-override-fco: class=' + self.to_s
1571
+ end
1572
+ end
1573
+
1574
+ return @fco
1575
+ end
1576
+
1577
+ def self.isolate()
1578
+ if not instance_variable_defined?(:@isolate)
1579
+ @isolate = superclass.isolate
1580
+ end
1581
+
1582
+ return @isolate
1583
+ end
1584
+ #
1585
+ # fco, isolate
1586
+ #---------------------------------------------------------------------------
1587
+
1588
+
1589
+ # private
1590
+ private
1591
+
1592
+
1593
+ #---------------------------------------------------------------------------
1594
+ # isolate=, fco=
1595
+ #
1596
+ def self.isolate=(val)
1597
+ @isolate = val
1598
+ end
1599
+
1600
+ def self.fco=(val)
1601
+ @fco = val
1602
+ end
1603
+
1604
+ self.fco = false
1605
+ self.isolate = false
1606
+ #
1607
+ # isolate=, fco=
1608
+ #---------------------------------------------------------------------------
1609
+ end
1610
+ #
1611
+ # Audrey::Object
1612
+ #===============================================================================
1613
+
1614
+
1615
+ #===============================================================================
1616
+ # scalar types
1617
+ #
1618
+ class Audrey::Object::Scalar < Audrey::Object
1619
+ def self.bclass
1620
+ return self
1621
+ end
1622
+ end
1623
+
1624
+ class Audrey::Object::Scalar::String < Audrey::Object::Scalar
1625
+ def self.field_definition
1626
+ return Audrey::Object::Scalar::String::Field
1627
+ end
1628
+ end
1629
+
1630
+ class Audrey::Object::Scalar::Number < Audrey::Object::Scalar
1631
+ end
1632
+
1633
+ class Audrey::Object::Scalar::Boolean < Audrey::Object::Scalar
1634
+ end
1635
+
1636
+ class Audrey::Object::Scalar::Null < Audrey::Object::Scalar
1637
+ end
1638
+ #
1639
+ # scalar types
1640
+ #===============================================================================
1641
+
1642
+
1643
+ #===============================================================================
1644
+ # Audrey::Object::Collection
1645
+ #
1646
+ class Audrey::Object::Collection < Audrey::Object
1647
+ end
1648
+ #
1649
+ # Audrey::Object::Collection
1650
+ #===============================================================================
1651
+
1652
+
1653
+ #===============================================================================
1654
+ # Audrey::Object::Array
1655
+ #
1656
+ class Audrey::Object::Array < Audrey::Object::Collection
1657
+ def self.bclass
1658
+ return self
1659
+ end
1660
+
1661
+ def self.node_class
1662
+ return Audrey::Node::Hash
1663
+ end
1664
+ end
1665
+ #
1666
+ # Audrey::Object::Array
1667
+ #===============================================================================
1668
+
1669
+
1670
+ #===============================================================================
1671
+ # Audrey::Object::Hash
1672
+ #
1673
+ class Audrey::Object::Hash < Audrey::Object::Collection
1674
+ def self.bclass
1675
+ return Audrey::Object::Hash
1676
+ end
1677
+
1678
+ def self.node_class
1679
+ return Audrey::Node::Hash
1680
+ end
1681
+ end
1682
+ #
1683
+ # Audrey::Object::Hash
1684
+ #===============================================================================
1685
+
1686
+
1687
+ #===============================================================================
1688
+ # Audrey::Object::Root
1689
+ #
1690
+ class Audrey::Object::Root < Audrey::Object::Hash
1691
+ def self.bclass
1692
+ return Audrey::Object::Hash
1693
+ end
1694
+
1695
+ def self.node_class
1696
+ return Audrey::Node::Hash
1697
+ end
1698
+
1699
+ # fco
1700
+ self.fco = true
1701
+ end
1702
+ #
1703
+ # Audrey::Object::Root
1704
+ #===============================================================================
1705
+
1706
+
1707
+ #===============================================================================
1708
+ # Audrey::Object::Searchable
1709
+ #
1710
+ module Audrey::Object::Searchable
1711
+ #---------------------------------------------------------------------------
1712
+ # each
1713
+ #
1714
+ def each(opts={})
1715
+ prepare_query(opts).each do |object|
1716
+ yield object
1717
+ end
1718
+ end
1719
+ #
1720
+ # each
1721
+ #---------------------------------------------------------------------------
1722
+
1723
+
1724
+ #---------------------------------------------------------------------------
1725
+ # count
1726
+ #
1727
+ def count(opts={})
1728
+ return prepare_query(opts).count
1729
+ end
1730
+ #
1731
+ # count
1732
+ #---------------------------------------------------------------------------
1733
+
1734
+
1735
+ #---------------------------------------------------------------------------
1736
+ # first
1737
+ #
1738
+ def first(opts={})
1739
+ prepare_query(opts).each do |object|
1740
+ return object
1741
+ end
1742
+ end
1743
+ #
1744
+ # each
1745
+ #---------------------------------------------------------------------------
1746
+
1747
+
1748
+ #---------------------------------------------------------------------------
1749
+ # sample, samples
1750
+ #
1751
+ def sample(opts={})
1752
+ query = prepare_query(opts)
1753
+ return query.sample
1754
+ end
1755
+
1756
+ def samples(c, opts={})
1757
+ query = prepare_query(opts)
1758
+ return query.samples(c)
1759
+ end
1760
+ #
1761
+ # sample, samples
1762
+ #---------------------------------------------------------------------------
1763
+
1764
+
1765
+ # private
1766
+ private
1767
+
1768
+
1769
+ #---------------------------------------------------------------------------
1770
+ # prepare_query
1771
+ #
1772
+ def prepare_query(opts)
1773
+ db = Audrey.explicit_or_current_db(opts['accessor'])
1774
+ query = db.q0
1775
+ query.fclass = self
1776
+ return query
1777
+ end
1778
+ #
1779
+ # prepare_query
1780
+ #---------------------------------------------------------------------------
1781
+ end
1782
+ #
1783
+ # Audrey::Object::Searchable
1784
+ #===============================================================================
1785
+
1786
+
1787
+ #===============================================================================
1788
+ # Audrey::Object::Custom
1789
+ #
1790
+ class Audrey::Object::Custom < Audrey::Object::Hash
1791
+ extend Audrey::Object::Searchable
1792
+
1793
+ # default fco to nil, which means fco must be explicitly overridden in
1794
+ # subclasses
1795
+ self.fco = nil
1796
+
1797
+ #---------------------------------------------------------------------------
1798
+ # initialize
1799
+ #
1800
+ def initialize(opts={})
1801
+ # If a node is passed as an option, use that node and don't create a
1802
+ # new object.
1803
+ if @node = opts['node']
1804
+ if block_given?
1805
+ @node.db.transaction() do |tr|
1806
+ yield self
1807
+ tr.commit
1808
+ end
1809
+ end
1810
+
1811
+ # If no node is passed, create a new object. If a block is given, run
1812
+ # that block within a transaction.
1813
+ else
1814
+ db = Audrey.explicit_or_current_db(opts['accessor'])
1815
+
1816
+ # if within block
1817
+ if block_given?
1818
+ db.transaction() do |tr|
1819
+ within_initialize db, opts
1820
+ yield self
1821
+ tr.commit
1822
+ end
1823
+ else
1824
+ within_initialize db, opts
1825
+ end
1826
+ end
1827
+ end
1828
+ #
1829
+ # initialize
1830
+ #---------------------------------------------------------------------------
1831
+
1832
+
1833
+ #---------------------------------------------------------------------------
1834
+ # audrey
1835
+ #
1836
+ def audrey
1837
+ return @node
1838
+ end
1839
+ #
1840
+ # audrey
1841
+ #---------------------------------------------------------------------------
1842
+
1843
+
1844
+ #---------------------------------------------------------------------------
1845
+ # own_fields
1846
+ #
1847
+ def self.own_fields
1848
+ # $tm.hrm
1849
+
1850
+ # ensure @own_fields exists
1851
+ if not instance_variable_defined?(:@own_fields)
1852
+ @own_fields = {}
1853
+ end
1854
+
1855
+ # return
1856
+ return @own_fields
1857
+ end
1858
+ #
1859
+ # own_fields
1860
+ #---------------------------------------------------------------------------
1861
+
1862
+
1863
+ #---------------------------------------------------------------------------
1864
+ # fields
1865
+ #
1866
+ def self.fields
1867
+ # $tm.hrm
1868
+
1869
+ # get superclass fields
1870
+ if self.superclass.respond_to?('fields')
1871
+ rv = self.superclass.fields()
1872
+ else
1873
+ rv = {}
1874
+ end
1875
+
1876
+ # get own fields
1877
+ rv = rv.merge(own_fields())
1878
+
1879
+ # return
1880
+ return rv
1881
+ end
1882
+ #
1883
+ # fields
1884
+ #---------------------------------------------------------------------------
1885
+
1886
+
1887
+ #---------------------------------------------------------------------------
1888
+ # descendant?
1889
+ #
1890
+ def self.descendant?(object)
1891
+ if object.is_a?(Class)
1892
+ return object < Audrey::Object::Custom
1893
+ else
1894
+ return object.class < Audrey::Object::Custom
1895
+ end
1896
+ end
1897
+ #
1898
+ # descendant?
1899
+ #---------------------------------------------------------------------------
1900
+
1901
+
1902
+ #---------------------------------------------------------------------------
1903
+ # new_auto_fields
1904
+ #
1905
+ def self.new_auto_fields(object)
1906
+ # $tm.hrm
1907
+
1908
+ # loop through fields
1909
+ fields.each do |name, dfn|
1910
+ dfn.auto_add object
1911
+ end
1912
+
1913
+ # rescurse to super class
1914
+ unless self == Audrey::Object::Custom
1915
+ self.superclass.new_auto_fields(object)
1916
+ end
1917
+ end
1918
+ #
1919
+ # new_auto_fields
1920
+ #---------------------------------------------------------------------------
1921
+
1922
+
1923
+ #---------------------------------------------------------------------------
1924
+ # has_checks?
1925
+ #
1926
+ def self.has_checks?
1927
+ # cache return value
1928
+ if not instance_variable_defined?(:@has_checks_cache)
1929
+ @has_checks_cache = has_checks_search()
1930
+ end
1931
+
1932
+ # return
1933
+ return @has_checks_cache
1934
+ end
1935
+ #
1936
+ # has_checks?
1937
+ #---------------------------------------------------------------------------
1938
+
1939
+
1940
+ # private
1941
+ private
1942
+
1943
+
1944
+ #---------------------------------------------------------------------------
1945
+ # within_initialize
1946
+ #
1947
+ def within_initialize(db, opts)
1948
+ uuid = db.insert_object(self)
1949
+ @node = Audrey::Node::Hash.new(db, 'fclass'=>self.class, 'pk'=>uuid)
1950
+ self.class.new_auto_fields self
1951
+ end
1952
+ #
1953
+ # within_initialize
1954
+ #---------------------------------------------------------------------------
1955
+
1956
+
1957
+ #---------------------------------------------------------------------------
1958
+ # has_checks_search
1959
+ #
1960
+ def self.has_checks_search
1961
+ fields.each do |field_name, dfn|
1962
+ dfn.has_checks? and return true
1963
+ end
1964
+
1965
+ return false
1966
+ end
1967
+ #
1968
+ # has_checks_search
1969
+ #---------------------------------------------------------------------------
1970
+
1971
+
1972
+ #---------------------------------------------------------------------------
1973
+ # id_field
1974
+ #
1975
+ def self.id_field=(val)
1976
+ @id_field = val
1977
+ end
1978
+
1979
+ def self.id_field()
1980
+ if instance_variable_defined?(:@id_field)
1981
+ return @id_field
1982
+ else
1983
+ return self.superclass.id_field
1984
+ end
1985
+ end
1986
+
1987
+ self.id_field = nil
1988
+
1989
+ def self.[](key)
1990
+ # $tm.hrm
1991
+
1992
+ # if this class has an id field
1993
+ if idf = self.id_field
1994
+ db = Audrey.explicit_or_current_db(nil)
1995
+ q0 = db.q0
1996
+ q0.fclass = self
1997
+ q0.fields[idf] = key
1998
+ return q0.first
1999
+
2000
+ # else exception
2001
+ else
2002
+ raise 'no-id-field-defined'
2003
+ end
2004
+ end
2005
+ #
2006
+ # id_field
2007
+ #---------------------------------------------------------------------------
2008
+
2009
+
2010
+ #---------------------------------------------------------------------------
2011
+ # field
2012
+ #
2013
+ def self.field(name, opts={})
2014
+ use_dfn = Audrey::Object::Custom::Field
2015
+
2016
+ # get custom field definition class
2017
+ if fclass = opts['fclass']
2018
+ if Audrey::RUBY_OBJECT_TO_AUDREY[fclass]
2019
+ fclass = Audrey::RUBY_OBJECT_TO_AUDREY[fclass]['fclass']
2020
+ end
2021
+
2022
+ if fclass.respond_to?('field_definition')
2023
+ use_dfn = fclass.field_definition
2024
+ end
2025
+ end
2026
+
2027
+ # create definition
2028
+ dfn = use_dfn.new(name)
2029
+ own_fields[dfn.name] = dfn
2030
+
2031
+ # field rules
2032
+ if block_given?
2033
+ yield dfn
2034
+ end
2035
+
2036
+ # add to class
2037
+ dfn.add_to_class self
2038
+ end
2039
+ #
2040
+ # field
2041
+ #---------------------------------------------------------------------------
2042
+ end
2043
+ #
2044
+ # Audrey::Object::Custom
2045
+ #===============================================================================
2046
+
2047
+
2048
+ #===============================================================================
2049
+ # Audrey::Object::Custom::Field
2050
+ #
2051
+ class Audrey::Object::Custom::Field
2052
+ attr_accessor :name
2053
+ attr_accessor :read
2054
+ attr_accessor :write
2055
+ attr_accessor :clss
2056
+ attr_accessor :auto
2057
+ attr_accessor :publish
2058
+ attr_accessor :graph
2059
+ attr_accessor :required
2060
+
2061
+ #---------------------------------------------------------------------------
2062
+ # initialize
2063
+ #
2064
+ def initialize(p_name)
2065
+ @name = p_name
2066
+ @read = true
2067
+ @write = true
2068
+ @clss = nil
2069
+ @auto = false
2070
+ @publish = false
2071
+ @graph = false
2072
+
2073
+ # normalize name
2074
+ @name = @name.strip
2075
+ @name = @name.gsub(/\s+/mu, ' ')
2076
+ end
2077
+ #
2078
+ # initialize
2079
+ #---------------------------------------------------------------------------
2080
+
2081
+
2082
+ #---------------------------------------------------------------------------
2083
+ # add_to_class
2084
+ #
2085
+ def add_to_class(tgt)
2086
+ # $tm.hrm
2087
+
2088
+ # Ruby doesn't like it if we use an @ variable in the method definition
2089
+ # of another class.
2090
+ nme = @name.dup
2091
+
2092
+ # read
2093
+ if @read
2094
+ tgt.define_method(nme) do
2095
+ return @node[nme]
2096
+ end
2097
+ end
2098
+
2099
+ # write
2100
+ if @write
2101
+ # reference self inside the method for assigning the instance
2102
+ # variable
2103
+ me = self
2104
+
2105
+ # definew method
2106
+ tgt.define_method("#{nme}=") do |val|
2107
+ val = me.normalize(val)
2108
+ return @node[nme] = val
2109
+ end
2110
+ end
2111
+ end
2112
+ #
2113
+ # add_to_class
2114
+ #---------------------------------------------------------------------------
2115
+
2116
+
2117
+ #---------------------------------------------------------------------------
2118
+ # auto_add
2119
+ #
2120
+ def auto_add(object)
2121
+ # early exit
2122
+ @auto or return
2123
+
2124
+ # get audrey node
2125
+ node = object.audrey
2126
+
2127
+ # init
2128
+ new_val = nil
2129
+
2130
+ # add to node
2131
+ if Audrey::Object::Custom.descendant?(@auto)
2132
+ new_val = @auto.new('accessor'=>node.db)
2133
+ elsif @auto.is_a?(Proc)
2134
+ new_val = @auto.call(object)
2135
+ end
2136
+
2137
+ # set new value
2138
+ node[@name] = new_val
2139
+ end
2140
+ #
2141
+ # auto_add
2142
+ #---------------------------------------------------------------------------
2143
+
2144
+
2145
+ #---------------------------------------------------------------------------
2146
+ # normalize
2147
+ #
2148
+ def normalize(val)
2149
+ return val
2150
+ end
2151
+ #
2152
+ # normalize
2153
+ #---------------------------------------------------------------------------
2154
+
2155
+
2156
+ #---------------------------------------------------------------------------
2157
+ # has_checks?
2158
+ #
2159
+ def has_checks?
2160
+ return false
2161
+ end
2162
+ #
2163
+ # has_checks?
2164
+ #---------------------------------------------------------------------------
2165
+
2166
+
2167
+ #---------------------------------------------------------------------------
2168
+ # base_checks
2169
+ #
2170
+ def base_checks(*opts)
2171
+ end
2172
+ #
2173
+ # base_checks
2174
+ #---------------------------------------------------------------------------
2175
+ end
2176
+ #
2177
+ # Audrey::Object::Custom::Field
2178
+ #===============================================================================
2179
+
2180
+
2181
+ #===============================================================================
2182
+ # Audrey::Object::Custom::Graph
2183
+ #
2184
+ class Audrey::Object::Custom::Graph < Audrey::Object::Custom
2185
+ end
2186
+ #
2187
+ # Audrey::Object::Custom::Graph
2188
+ #===============================================================================
2189
+
2190
+
2191
+
2192
+ #===============================================================================
2193
+ # Audrey::Object::Custom::Graph::Directed
2194
+ #
2195
+ class Audrey::Object::Custom::Graph::Directed < Audrey::Object::Custom::Graph
2196
+ end
2197
+ #
2198
+ # Audrey::Object::Custom::Graph::Directed
2199
+ #===============================================================================
2200
+
2201
+
2202
+
2203
+ #===============================================================================
2204
+ # Audrey::Object::Custom::Graph::Undirected
2205
+ #
2206
+ class Audrey::Object::Custom::Graph::Undirected < Audrey::Object::Custom::Graph
2207
+ self.fco = true
2208
+ self.isolate = true
2209
+ field 'a'
2210
+ field 'b'
2211
+
2212
+ #---------------------------------------------------------------------------
2213
+ # records
2214
+ #
2215
+ def records(*recs)
2216
+ # $tm.hrm
2217
+
2218
+ # sort by pk
2219
+ recs.sort! do |x, y|
2220
+ x.audrey.pk <=> y.audrey.pk
2221
+ end
2222
+
2223
+ # store
2224
+ self.a = recs[0]
2225
+ self.b = recs[1]
2226
+ end
2227
+ #
2228
+ # records
2229
+ #---------------------------------------------------------------------------
2230
+ end
2231
+ #
2232
+ # Audrey::Object::Custom::Graph::Undirected
2233
+ #===============================================================================
2234
+
2235
+
2236
+
2237
+ #===============================================================================
2238
+ # Audrey::Transaction
2239
+ #
2240
+ class Audrey::Transaction
2241
+ def exit()
2242
+ e = Audrey::Exception::TransactionExit.new(self)
2243
+ raise e
2244
+ end
2245
+ end
2246
+ #
2247
+ # Audrey::Transaction
2248
+ #===============================================================================
2249
+
2250
+
2251
+ #===============================================================================
2252
+ # Audrey::Exception
2253
+ #
2254
+ class Audrey::Exception < StandardError
2255
+ end
2256
+ #
2257
+ # Audrey::Exception
2258
+ #===============================================================================
2259
+
2260
+
2261
+ #===============================================================================
2262
+ # Audrey::Exception::TransactionExit
2263
+ #
2264
+ class Audrey::Exception::TransactionExit < Audrey::Exception
2265
+ attr_reader :tr
2266
+
2267
+ #---------------------------------------------------------------------------
2268
+ # initialize
2269
+ #
2270
+ def initialize(p_tr)
2271
+ @tr = p_tr
2272
+ end
2273
+ #
2274
+ # initialize
2275
+ #---------------------------------------------------------------------------
2276
+ end
2277
+ #
2278
+ # Audrey::Exception::TransactionExit
2279
+ #===============================================================================
2280
+
2281
+
2282
+ #===============================================================================
2283
+ # Audrey::Exception::DatabaseExit
2284
+ #
2285
+ class Audrey::Exception::DatabaseExit < Audrey::Exception
2286
+ attr_reader :db
2287
+
2288
+ #---------------------------------------------------------------------------
2289
+ # initialize
2290
+ #
2291
+ def initialize(p_db)
2292
+ @db = p_db
2293
+ end
2294
+ #
2295
+ # initialize
2296
+ #---------------------------------------------------------------------------
2297
+ end
2298
+ #
2299
+ # Audrey::Exception::DatabaseExit
2300
+ #===============================================================================
2301
+
2302
+
2303
+ #===============================================================================
2304
+ # read/write permission
2305
+ #
2306
+ class Audrey::Exception::Permission < Audrey::Exception
2307
+ end
2308
+
2309
+ class Audrey::Exception::Permission::Read < Audrey::Exception::Permission
2310
+ end
2311
+
2312
+ class Audrey::Exception::Permission::Write < Audrey::Exception::Permission
2313
+ end
2314
+ #
2315
+ # read/write permission
2316
+ #===============================================================================
2317
+
2318
+
2319
+ #===============================================================================
2320
+ # Audrey::Query
2321
+ # abstract class
2322
+ #
2323
+ class Audrey::Query
2324
+ end
2325
+ #
2326
+ # Audrey::Query
2327
+ #===============================================================================
2328
+
2329
+
2330
+ #===============================================================================
2331
+ # Audrey::Object::Scalar::String::Field
2332
+ #
2333
+ class Audrey::Object::Scalar::String::Field < Audrey::Object::Custom::Field
2334
+ attr_accessor :collapse
2335
+ attr_accessor :downcase
2336
+ attr_accessor :upcase
2337
+ attr_accessor :min_length
2338
+ attr_accessor :max_length
2339
+ attr_accessor :hascontent
2340
+ attr_reader :fclass
2341
+
2342
+ #---------------------------------------------------------------------------
2343
+ # initialize
2344
+ #
2345
+ def initialize(*opts)
2346
+ super(*opts)
2347
+ @fclass = String
2348
+
2349
+ # normalize
2350
+ @collapse = false
2351
+ @downcase = false
2352
+ @upcase = false
2353
+
2354
+ # checks
2355
+ @min_length = nil
2356
+ @max_length = nil
2357
+ @hascontent = false
2358
+ end
2359
+ #
2360
+ # initialize
2361
+ #---------------------------------------------------------------------------
2362
+
2363
+
2364
+ #---------------------------------------------------------------------------
2365
+ # normalize
2366
+ #
2367
+ def normalize(val)
2368
+ # early exit
2369
+ if not val.is_a?(String)
2370
+ return
2371
+ end
2372
+
2373
+ # collapse
2374
+ if @collapse
2375
+ val.strip!
2376
+ val.gsub!(/[^[:graph:]]+/mu, ' ')
2377
+ end
2378
+
2379
+ # downcase, upcase
2380
+ if @downcase
2381
+ val.downcase!
2382
+ elsif @upcase
2383
+ val.upcase!
2384
+ end
2385
+
2386
+ # return
2387
+ return val
2388
+ end
2389
+ #
2390
+ # normalize
2391
+ #---------------------------------------------------------------------------
2392
+
2393
+
2394
+ #---------------------------------------------------------------------------
2395
+ # has_checks?
2396
+ #
2397
+ def has_checks?
2398
+ @min_length and return true
2399
+ @max_length and return true
2400
+ @hascontent and return true
2401
+ end
2402
+ #
2403
+ # has_checks?
2404
+ #---------------------------------------------------------------------------
2405
+
2406
+
2407
+ #---------------------------------------------------------------------------
2408
+ # base_checks
2409
+ #
2410
+ def base_checks(xeme, fieldname, val)
2411
+ # $tm.hrm
2412
+
2413
+ # min_length
2414
+ if @min_length
2415
+ if val.length < @min_length
2416
+ xeme.error('min-length') do |msg|
2417
+ msg['val'] = val
2418
+ msg['min-length'] = @min_length
2419
+ end
2420
+ end
2421
+ end
2422
+
2423
+ # max_length
2424
+ if @max_length
2425
+ if val.length > @max_length
2426
+ xeme.error('max-length') do |msg|
2427
+ msg['val'] = val
2428
+ msg['max-length'] = @max_length
2429
+ end
2430
+ end
2431
+ end
2432
+
2433
+ # hascontent
2434
+ if @hascontent
2435
+ if not val.match(/\S/mu)
2436
+ xeme.error('hascontent') do |msg|
2437
+ msg['val'] = val
2438
+ end
2439
+ end
2440
+ end
2441
+ end
2442
+ #
2443
+ # base_checks
2444
+ #---------------------------------------------------------------------------
2445
+ end
2446
+ #
2447
+ # Audrey::Object::Scalar::String::Field
2448
+ #===============================================================================
2449
+
2450
+
2451
+ #===============================================================================
2452
+ # build fclasses for collections
2453
+ # This code needs to go at the bottom of this file because the classes being
2454
+ # referenced aren't defined until after the COLLECTIONS hash is used.
2455
+ #
2456
+ class Audrey
2457
+ #---------------------------------------------------------------------------
2458
+ # RUBY_OBJECT_TO_AUDREY
2459
+ #
2460
+ RUBY_OBJECT_TO_AUDREY = {}
2461
+ RUBY_OBJECT_TO_AUDREY[String] = { 'fclass'=>Audrey::Object::Scalar::String}
2462
+ RUBY_OBJECT_TO_AUDREY[Integer] = { 'fclass'=>Audrey::Object::Scalar::Number}
2463
+ RUBY_OBJECT_TO_AUDREY[Float] = RUBY_OBJECT_TO_AUDREY[Integer]
2464
+ RUBY_OBJECT_TO_AUDREY[TrueClass] = { 'fclass'=>Audrey::Object::Scalar::Number}
2465
+ RUBY_OBJECT_TO_AUDREY[FalseClass] = RUBY_OBJECT_TO_AUDREY[TrueClass]
2466
+ RUBY_OBJECT_TO_AUDREY[NilClass] = { 'fclass'=>Audrey::Object::Scalar::Null}
2467
+
2468
+ RUBY_OBJECT_TO_AUDREY[Hash] = {'fclass'=>Audrey::Object::Hash, 'nclass'=>Audrey::Node::Hash}
2469
+ RUBY_OBJECT_TO_AUDREY[Array] = {'fclass'=>Audrey::Object::Array, 'nclass'=>Audrey::Node::Array}
2470
+ RUBY_OBJECT_TO_AUDREY.freeze
2471
+ #
2472
+ # RUBY_OBJECT_TO_AUDREY
2473
+ #---------------------------------------------------------------------------
2474
+
2475
+
2476
+ #---------------------------------------------------------------------------
2477
+ # AUDREY_SCALAR_TO_RUBY
2478
+ #
2479
+ AUDREY_SCALAR_TO_RUBY = {}
2480
+ AUDREY_SCALAR_TO_RUBY[Audrey::Object::Scalar::String.to_s] = proc {|obj| obj}
2481
+ AUDREY_SCALAR_TO_RUBY[Audrey::Object::Scalar::Number.to_s] = proc {|obj| obj.to_f}
2482
+ AUDREY_SCALAR_TO_RUBY[Audrey::Object::Scalar::Boolean.to_s] = proc {|obj| obj == 0 ? false : true}
2483
+ AUDREY_SCALAR_TO_RUBY[Audrey::Object::Scalar::Null.to_s] = proc {|obj| nil}
2484
+ AUDREY_SCALAR_TO_RUBY.freeze
2485
+ #
2486
+ # AUDREY_SCALAR_TO_RUBY
2487
+ #---------------------------------------------------------------------------
2488
+
2489
+
2490
+ #---------------------------------------------------------------------------
2491
+ # AUDREY_COLLECTION_TO_RUBY
2492
+ #
2493
+ AUDREY_COLLECTION_TO_RUBY = {}
2494
+ AUDREY_COLLECTION_TO_RUBY[Audrey::Object::Hash.to_s] = {'nclass'=>Audrey::Node::Hash}
2495
+ AUDREY_COLLECTION_TO_RUBY[Audrey::Object::Array.to_s] = {'nclass'=>Audrey::Node::Array}
2496
+ AUDREY_COLLECTION_TO_RUBY.freeze
2497
+ #
2498
+ # AUDREY_COLLECTION_TO_RUBY
2499
+ #---------------------------------------------------------------------------
2500
+ end
2501
+ #
2502
+ # build fclasses for collections
2503
+ #===============================================================================