audrey 0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. checksums.yaml +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
+ #===============================================================================