datoki 1.0.1 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +29 -1
- data/VERSION +1 -1
- data/lib/datoki.rb +322 -247
- data/specs/0010-Datoki.db.new.rb +31 -0
- data/specs/0010-Datoki.db.rb +67 -0
- data/specs/0010-Numeric.rb +66 -0
- data/specs/0010-Numeric_db.rb +28 -0
- data/specs/0010-href.rb +75 -0
- data/specs/0010-html_escape.rb +44 -0
- data/specs/0010-no_type.rb +14 -0
- data/specs/0010-schema_conflict.rb +38 -0
- data/specs/0010-varchar.rb +98 -0
- data/specs/0010-varchar_db.rb +33 -0
- data/specs/0020-clean.rb +58 -0
- data/specs/0020-on.rb +64 -0
- metadata +14 -3
- data/specs/datoki.rb +0 -556
data/lib/datoki.rb
CHANGED
@@ -15,6 +15,10 @@ module Datoki
|
|
15
15
|
Numeric_Types = [:smallint, :integer, :bigint, :decimal, :numeric]
|
16
16
|
Types = Char_Types + Numeric_Types + [:datetime]
|
17
17
|
|
18
|
+
Key_Not_Found = lambda { |hash, key|
|
19
|
+
fail ArgumentError, "Key not found: #{key.inspect}"
|
20
|
+
}
|
21
|
+
|
18
22
|
class << self
|
19
23
|
|
20
24
|
def included klass
|
@@ -44,16 +48,17 @@ module Datoki
|
|
44
48
|
|
45
49
|
module Def_Field
|
46
50
|
|
47
|
-
attr_reader :ons, :fields
|
51
|
+
attr_reader :ons, :fields, :fields_as_required
|
48
52
|
|
49
53
|
def initialize_def_field
|
50
|
-
@
|
51
|
-
@ons
|
52
|
-
@fields
|
53
|
-
@
|
54
|
-
@
|
55
|
-
@
|
56
|
-
@
|
54
|
+
@on_doc = []
|
55
|
+
@ons = {}
|
56
|
+
@fields = {} # Ex: {:name=>{}, :age=>{}}
|
57
|
+
@fields_as_required = {} # Ex: {:name!=>:name}
|
58
|
+
@current_field = nil
|
59
|
+
@schema = {}
|
60
|
+
@schema_match = false
|
61
|
+
@table_name = nil
|
57
62
|
name = self.to_s.downcase.to_sym
|
58
63
|
table(name) if Datoki.db.tables.include?(name)
|
59
64
|
end
|
@@ -62,14 +67,6 @@ module Datoki
|
|
62
67
|
@schema_match
|
63
68
|
end
|
64
69
|
|
65
|
-
def record_errors?
|
66
|
-
@record_errors
|
67
|
-
end
|
68
|
-
|
69
|
-
def record_errors
|
70
|
-
@record_errors = true
|
71
|
-
end
|
72
|
-
|
73
70
|
def table name
|
74
71
|
if !@schema.empty? || @table_name
|
75
72
|
fail "Schema/table already defined: #{@table_name.inspect}"
|
@@ -137,6 +134,10 @@ module Datoki
|
|
137
134
|
inspect_field?(:type, field[:name], *args)
|
138
135
|
end
|
139
136
|
|
137
|
+
def allow sym
|
138
|
+
fields[@current_field][:allow][sym] = true;
|
139
|
+
end
|
140
|
+
|
140
141
|
def field *args
|
141
142
|
return fields[@current_field] if args.empty?
|
142
143
|
return fields[args.first] unless block_given?
|
@@ -144,6 +145,7 @@ module Datoki
|
|
144
145
|
name = args.first
|
145
146
|
|
146
147
|
fail "#{name.inspect} already defined." if fields[name]
|
148
|
+
fields_as_required[:"#{name}!"] = name
|
147
149
|
|
148
150
|
fields[name] = {
|
149
151
|
:name => name,
|
@@ -271,7 +273,14 @@ module Datoki
|
|
271
273
|
field[:schema_match] = true
|
272
274
|
end
|
273
275
|
|
274
|
-
|
276
|
+
attr_reader :on_doc
|
277
|
+
def on *args
|
278
|
+
return(field_on *args) if !block_given?
|
279
|
+
@on_doc << [args, Proc.new]
|
280
|
+
self
|
281
|
+
end
|
282
|
+
|
283
|
+
def field_on action, meth_name_sym
|
275
284
|
fail "Invalid action: #{action.inspect}" unless Actions.include? action
|
276
285
|
if field
|
277
286
|
field[:on][action] ||= {}
|
@@ -304,7 +313,7 @@ module Datoki
|
|
304
313
|
field[:html_escape] = :href
|
305
314
|
case args.map(&:class)
|
306
315
|
when []
|
307
|
-
varchar
|
316
|
+
varchar 1, 255
|
308
317
|
when [NilClass]
|
309
318
|
varchar nil, 1, (schema[field[:name]] ? schema[field[:name]][:max_length] : 255)
|
310
319
|
else
|
@@ -436,48 +445,296 @@ module Datoki
|
|
436
445
|
self
|
437
446
|
end
|
438
447
|
|
439
|
-
def create
|
440
|
-
|
441
|
-
|
448
|
+
def create raw
|
449
|
+
raw[:create] = self
|
450
|
+
new raw
|
442
451
|
end
|
443
452
|
|
444
|
-
end # === Def_Field
|
453
|
+
end # === Def_Field =====================================================
|
445
454
|
|
446
455
|
# ================= Instance Methods ===============
|
447
456
|
|
448
|
-
|
457
|
+
attr_reader :error
|
458
|
+
def initialize unknown = nil
|
449
459
|
@data = nil
|
450
|
-
@new_data = nil
|
451
460
|
@field_name = nil
|
452
|
-
@
|
453
|
-
@
|
461
|
+
@clean = nil
|
462
|
+
@error = nil
|
463
|
+
@skips = {}
|
464
|
+
|
465
|
+
if unknown
|
466
|
+
if unknown.keys.all? { |f| self.class.fields.has_key?(f) }
|
467
|
+
@data = unknown
|
468
|
+
@data.default_proc = Key_Not_Found
|
469
|
+
else
|
470
|
+
@raw = unknown
|
471
|
+
@raw.default_proc = Key_Not_Found
|
472
|
+
end
|
473
|
+
end
|
474
|
+
|
475
|
+
if @raw
|
476
|
+
self.class.on_doc.each { |raw_arr|
|
477
|
+
|
478
|
+
conds = raw_arr.first
|
479
|
+
func = raw_arr.last
|
480
|
+
instance_eval(&func) if conds.all? { |cond|
|
481
|
+
case cond
|
482
|
+
when Symbol
|
483
|
+
send(cond)
|
484
|
+
when Proc
|
485
|
+
cond.arity == 1 ? cond.call(@raw) : instance_eval(&cond)
|
486
|
+
when TrueClass, FalseClass
|
487
|
+
cond
|
488
|
+
else
|
489
|
+
fail ArgumentError, "Unknown: #{cond.inspect}"
|
490
|
+
end
|
491
|
+
}
|
492
|
+
|
493
|
+
} # === on_doc.each
|
494
|
+
|
495
|
+
if !@clean
|
496
|
+
@raw.each { |k, v|
|
497
|
+
clean(k) if self.class.fields.has_key?(k)
|
498
|
+
}
|
499
|
+
end
|
500
|
+
|
501
|
+
if create?
|
502
|
+
self.class.fields.each { |k, meta|
|
503
|
+
if !clean.has_key?(k) && !meta[:allow][:null] && !meta[:primary_key]
|
504
|
+
fail ArgumentError, "#{k.inspect} is not set."
|
505
|
+
end
|
506
|
+
}
|
507
|
+
end
|
508
|
+
|
509
|
+
case
|
510
|
+
when create?
|
511
|
+
insert_into_table unless !respond_to?(:insert_into_table)
|
512
|
+
when update?
|
513
|
+
alter_record unless !respond_to?(:alter_record)
|
514
|
+
when delete?
|
515
|
+
delete_from_table unless !respond_to?(:delete_from_table)
|
516
|
+
end unless @skips[:db]
|
517
|
+
end # === if @raw
|
454
518
|
|
455
519
|
self.class.schema_match(:all)
|
456
520
|
end
|
457
521
|
|
458
|
-
def
|
459
|
-
@
|
522
|
+
def skip name
|
523
|
+
@skips[name] = true
|
460
524
|
end
|
461
525
|
|
462
|
-
def
|
463
|
-
@
|
526
|
+
def error?
|
527
|
+
@error && !@error.empty?
|
464
528
|
end
|
465
529
|
|
466
|
-
def
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
530
|
+
def clean *args
|
531
|
+
if args.empty?
|
532
|
+
@clean ||= begin
|
533
|
+
h = {}
|
534
|
+
h.default_proc = Key_Not_Found
|
535
|
+
h
|
536
|
+
end
|
537
|
+
return @clean
|
538
|
+
end
|
472
539
|
|
473
|
-
|
474
|
-
|
475
|
-
|
540
|
+
if args.size > 1
|
541
|
+
return args.each { |f| clean f }
|
542
|
+
end
|
543
|
+
|
544
|
+
name = args.first
|
545
|
+
required = false
|
546
|
+
|
547
|
+
if self.class.fields_as_required[name]
|
548
|
+
name = self.class.fields_as_required[name]
|
549
|
+
required = true
|
550
|
+
end
|
551
|
+
|
552
|
+
field_name(name)
|
553
|
+
f_meta = self.class.fields[name]
|
554
|
+
required = true if (!field[:allow][:null] && (!@raw.has_key?(name) || @raw[name] == nil))
|
555
|
+
|
556
|
+
# === Did the programmer forget to set the value?:
|
557
|
+
if required && (!@raw.has_key?(name) || @raw[name].nil?)
|
558
|
+
fail ArgumentError, "#{name.inspect} is not set."
|
559
|
+
end
|
560
|
+
|
561
|
+
# === Skip this if nothing is set and is null-able:
|
562
|
+
if !required && field[:allow][:null] && !@raw.has_key?(name) && !clean.has_key?(name)
|
563
|
+
return nil
|
564
|
+
end
|
565
|
+
|
566
|
+
clean[name] = @raw[name] unless clean.has_key?(name)
|
567
|
+
|
568
|
+
# === Should we let the DB set the value?
|
569
|
+
if self.class.schema[name] && self.class.schema[name][:default] && (!clean.has_key?(name) || !clean[name])
|
570
|
+
clean.delete name
|
571
|
+
return self.class.schema[name][:default]
|
572
|
+
end
|
573
|
+
|
574
|
+
# === Strip the value:
|
575
|
+
if clean[name].is_a?(String) && field[:allow][:strip]
|
576
|
+
clean[name].strip!
|
577
|
+
end
|
578
|
+
|
579
|
+
if field?(:chars) && !field.has_key?(:min) && clean[name].is_a?(String) && field[:allow][:null]
|
580
|
+
clean[name] = nil
|
581
|
+
end
|
582
|
+
|
583
|
+
if field?(:numeric) && clean[name].is_a?(String)
|
584
|
+
clean_val = Integer(clean[name]) rescue String
|
585
|
+
if clean_val == String
|
586
|
+
fail! "!English_name must be numeric."
|
587
|
+
else
|
588
|
+
clean[name] = clean_val
|
589
|
+
end
|
590
|
+
end
|
591
|
+
|
592
|
+
if field?(:text) && clean[name].is_a?(String) && clean[name].empty? && field[:min].to_i > 0
|
593
|
+
fail! "!English_name is required."
|
594
|
+
end
|
595
|
+
# ================================
|
596
|
+
|
597
|
+
# === check min, max ======
|
598
|
+
if clean[name].is_a?(String) || clean[name].is_a?(Numeric)
|
599
|
+
case [field[:min], field[:max]].map(&:class)
|
600
|
+
|
601
|
+
when [NilClass, NilClass]
|
602
|
+
# do nothing
|
603
|
+
|
604
|
+
when [NilClass, Fixnum]
|
605
|
+
case
|
606
|
+
when clean[name].is_a?(String) && clean[name].size > field[:max]
|
607
|
+
fail! "!English_name can't be longer than !max characters."
|
608
|
+
when clean[name].is_a?(Numeric) && clean[name] > field[:max]
|
609
|
+
fail! "!English_name can't be higher than !max."
|
610
|
+
end
|
611
|
+
|
612
|
+
when [Fixnum, NilClass]
|
613
|
+
case
|
614
|
+
when clean[name].is_a?(String) && clean[name].size < field[:min]
|
615
|
+
fail! "!English_name can't be shorter than !min characters."
|
616
|
+
when clean[name].is_a?(Numeric) && clean[name] < field[:min]
|
617
|
+
fail! "!English_name can't be less than !min."
|
618
|
+
end
|
619
|
+
|
620
|
+
when [Fixnum, Fixnum]
|
621
|
+
case
|
622
|
+
when clean[name].is_a?(String) && (clean[name].size < field[:min] || clean[name].size > field[:max])
|
623
|
+
fail! "!English_name must be between !min and !max characters."
|
624
|
+
when clean[name].is_a?(Numeric) && (clean[name] < field[:min] || clean[name] > field[:max])
|
625
|
+
fail! "!English_name must be between !min and !max."
|
626
|
+
end
|
627
|
+
|
628
|
+
else
|
629
|
+
fail "Unknown values for :min, :max: #{field[:min].inspect}, #{field[:max].inspect}"
|
630
|
+
end
|
631
|
+
end # === if
|
632
|
+
# ================================
|
633
|
+
|
634
|
+
# === to_i if necessary ==========
|
635
|
+
if field?(:numeric)
|
636
|
+
if clean[name].nil? && !field[:allow][:null]
|
637
|
+
clean[name] = clean[name].to_i
|
638
|
+
end
|
639
|
+
end
|
640
|
+
# ================================
|
641
|
+
|
642
|
+
# === :strip if necessary ========
|
643
|
+
if field?(:chars) && field[:allow][:strip] && clean[name].is_a?(String)
|
644
|
+
clean[name] = clean[name].strip
|
645
|
+
end
|
646
|
+
# ================================
|
647
|
+
|
648
|
+
# === Is value in options? =======
|
649
|
+
if field[:options]
|
650
|
+
if !field[:options].include?(clean[name])
|
651
|
+
fail! "!English_name can only be: #{field[:options].map(&:inspect).join ', '}"
|
652
|
+
end
|
653
|
+
end
|
654
|
+
# ================================
|
655
|
+
|
656
|
+
field[:cleaners].each { |cleaner, args|
|
657
|
+
next if args === false # === cleaner has been disabled.
|
658
|
+
|
659
|
+
case cleaner
|
660
|
+
|
661
|
+
when :type
|
662
|
+
case
|
663
|
+
when field?(:numeric) && !clean[name].is_a?(Integer)
|
664
|
+
fail! "!English_name needs to be an integer."
|
665
|
+
when field?(:chars) && !clean[name].is_a?(String)
|
666
|
+
fail! "!English_name needs to be a String."
|
667
|
+
end
|
668
|
+
|
669
|
+
when :exact_size
|
670
|
+
if clean[name].size != field[:exact_size]
|
671
|
+
case
|
672
|
+
when field?(:chars) || clean[name].is_a?(String)
|
673
|
+
fail! "!English_name needs to be !exact_size in length."
|
674
|
+
else
|
675
|
+
fail! "!English_name can only be !exact_size in size."
|
676
|
+
end
|
677
|
+
end
|
678
|
+
|
679
|
+
when :set_to
|
680
|
+
args.each { |meth|
|
681
|
+
clean[name] = send(meth)
|
682
|
+
}
|
683
|
+
|
684
|
+
when :equal_to
|
685
|
+
args.each { |pair|
|
686
|
+
meth, msg, other = pair
|
687
|
+
target = send(meth)
|
688
|
+
fail!(msg || "!English_name must be equal to: #{target.inspect}") unless clean[name] == target
|
689
|
+
}
|
690
|
+
|
691
|
+
when :included_in
|
692
|
+
arr, msg, other = args
|
693
|
+
fail!(msg || "!English_name must be one of these: #{arr.join ', '}") unless arr.include?(clean[name])
|
694
|
+
|
695
|
+
when :upcase
|
696
|
+
clean[name] = clean[name].upcase
|
697
|
+
|
698
|
+
when :match
|
699
|
+
args.each { |pair|
|
700
|
+
regex, msg, other = pair
|
701
|
+
if clean[name] !~ regex
|
702
|
+
fail!(msg || "!English_name must match #{regex.inspect}")
|
703
|
+
end
|
704
|
+
}
|
705
|
+
|
706
|
+
when :not_match
|
707
|
+
args.each { |pair|
|
708
|
+
regex, msg, other = pair
|
709
|
+
if clean[name] =~ regex
|
710
|
+
fail!(msg || "!English_name must not match #{regex.inspect}")
|
711
|
+
end
|
712
|
+
}
|
713
|
+
|
714
|
+
else
|
715
|
+
fail "Cleaner not implemented: #{cleaner.inspect}"
|
716
|
+
end # === case cleaner
|
717
|
+
} # === field[:cleaners].each
|
718
|
+
end # === def clean
|
476
719
|
|
477
720
|
def new_data
|
478
721
|
@new_data ||= {}
|
479
722
|
end
|
480
723
|
|
724
|
+
def on *args
|
725
|
+
fail ArgumentError, "No conditions." if args.empty?
|
726
|
+
yield if args.all? { |cond|
|
727
|
+
case cond
|
728
|
+
when Symbol
|
729
|
+
send(cond)
|
730
|
+
when TrueClass, FalseClass
|
731
|
+
cond
|
732
|
+
else
|
733
|
+
fail ArgumentError, "Unknown value: #{cond.inspect}"
|
734
|
+
end
|
735
|
+
}
|
736
|
+
end
|
737
|
+
|
481
738
|
def fail! msg
|
482
739
|
err_msg = msg.gsub(/!([a-z\_\-]+)/i) { |raw|
|
483
740
|
name = $1
|
@@ -493,12 +750,8 @@ module Datoki
|
|
493
750
|
end
|
494
751
|
}
|
495
752
|
|
496
|
-
|
497
|
-
|
498
|
-
throw :error_saved
|
499
|
-
else
|
500
|
-
fail Invalid, err_msg
|
501
|
-
end
|
753
|
+
@error = {:field_name=>field_name, :msg=>err_msg, :value=>clean[field_name]}
|
754
|
+
throw :invalid, self
|
502
755
|
end
|
503
756
|
|
504
757
|
def field_name *args
|
@@ -507,24 +760,13 @@ module Datoki
|
|
507
760
|
fail "Field name not set." unless @field_name
|
508
761
|
@field_name
|
509
762
|
when 1
|
763
|
+
fail ArgumentError, "Unknown field: #{args.first.inspect}" unless self.class.fields[args.first]
|
510
764
|
@field_name = args.first
|
511
765
|
else
|
512
766
|
fail "Unknown args: #{args.inspect}"
|
513
767
|
end
|
514
768
|
end
|
515
769
|
|
516
|
-
def val
|
517
|
-
if clean_data.has_key?(field_name)
|
518
|
-
clean_data[field_name]
|
519
|
-
else
|
520
|
-
new_data[field_name]
|
521
|
-
end
|
522
|
-
end
|
523
|
-
|
524
|
-
def val! new_val
|
525
|
-
clean_data[field_name] = new_val
|
526
|
-
end
|
527
|
-
|
528
770
|
def field *args
|
529
771
|
case args.size
|
530
772
|
when 0
|
@@ -540,196 +782,6 @@ module Datoki
|
|
540
782
|
self.class.inspect_field? :type, field_name, *args
|
541
783
|
end
|
542
784
|
|
543
|
-
def run action
|
544
|
-
self.class.fields.each { |f_name, f_meta|
|
545
|
-
|
546
|
-
field_name f_name
|
547
|
-
is_set = new_data.has_key?(field_name)
|
548
|
-
is_update = action == :update
|
549
|
-
is_nil = is_set && new_data[field_name].nil?
|
550
|
-
|
551
|
-
# === Should the field be skipped? ===============
|
552
|
-
next if !is_set && is_update
|
553
|
-
next if !is_set && field[:primary_key]
|
554
|
-
next if field[:allow][:null] && (!is_set || is_nil)
|
555
|
-
|
556
|
-
if is_set
|
557
|
-
val! new_data[field_name]
|
558
|
-
elsif field.has_key?(:default)
|
559
|
-
val! field[:default]
|
560
|
-
end
|
561
|
-
|
562
|
-
if val.is_a?(String) && field[:allow][:strip]
|
563
|
-
val! val.strip
|
564
|
-
end
|
565
|
-
|
566
|
-
if field?(:chars) && !field.has_key?(:min) && val.is_a?(String) && field[:allow][:null]
|
567
|
-
val! nil
|
568
|
-
end
|
569
|
-
|
570
|
-
catch :error_saved do
|
571
|
-
|
572
|
-
if field?(:numeric) && val.is_a?(String)
|
573
|
-
clean_val = Integer(val) rescue String
|
574
|
-
if clean_val == String
|
575
|
-
fail! "!English_name must be numeric."
|
576
|
-
else
|
577
|
-
val! clean_val
|
578
|
-
end
|
579
|
-
end
|
580
|
-
|
581
|
-
# === check required. ============
|
582
|
-
if val.nil? && !field[:allow][:null]
|
583
|
-
fail! "!English_name is required."
|
584
|
-
end
|
585
|
-
|
586
|
-
if field?(:text) && val.is_a?(String) && val.empty? && field[:min].to_i > 0
|
587
|
-
fail! "!English_name is required."
|
588
|
-
end
|
589
|
-
# ================================
|
590
|
-
|
591
|
-
# === check min, max ======
|
592
|
-
if val.is_a?(String) || val.is_a?(Numeric)
|
593
|
-
case [field[:min], field[:max]].map(&:class)
|
594
|
-
|
595
|
-
when [NilClass, NilClass]
|
596
|
-
# do nothing
|
597
|
-
|
598
|
-
when [NilClass, Fixnum]
|
599
|
-
case
|
600
|
-
when val.is_a?(String) && val.size > field[:max]
|
601
|
-
fail! "!English_name can't be longer than !max characters."
|
602
|
-
when val.is_a?(Numeric) && val > field[:max]
|
603
|
-
fail! "!English_name can't be higher than !max."
|
604
|
-
end
|
605
|
-
|
606
|
-
when [Fixnum, NilClass]
|
607
|
-
case
|
608
|
-
when val.is_a?(String) && val.size < field[:min]
|
609
|
-
fail! "!English_name can't be shorter than !min characters."
|
610
|
-
when val.is_a?(Numeric) && val < field[:min]
|
611
|
-
fail! "!English_name can't be less than !min."
|
612
|
-
end
|
613
|
-
|
614
|
-
when [Fixnum, Fixnum]
|
615
|
-
case
|
616
|
-
when val.is_a?(String) && (val.size < field[:min] || val.size > field[:max])
|
617
|
-
fail! "!English_name must be between !min and !max characters."
|
618
|
-
when val.is_a?(Numeric) && (val < field[:min] || val > field[:max])
|
619
|
-
fail! "!English_name must be between !min and !max."
|
620
|
-
end
|
621
|
-
|
622
|
-
else
|
623
|
-
fail "Unknown values for :min, :max: #{field[:min].inspect}, #{field[:max].inspect}"
|
624
|
-
end
|
625
|
-
end # === if
|
626
|
-
# ================================
|
627
|
-
|
628
|
-
# === to_i if necessary ==========
|
629
|
-
if field?(:numeric)
|
630
|
-
val! val.to_i
|
631
|
-
end
|
632
|
-
# ================================
|
633
|
-
|
634
|
-
# === :strip if necessary ========
|
635
|
-
if field?(:chars) && field[:allow][:strip] && val.is_a?(String)
|
636
|
-
val! val.strip
|
637
|
-
end
|
638
|
-
# ================================
|
639
|
-
|
640
|
-
# === Is value in options? =======
|
641
|
-
if field[:options]
|
642
|
-
if !field[:options].include?(val)
|
643
|
-
fail! "!English_name can only be: #{field[:options].map(&:inspect).join ', '}"
|
644
|
-
end
|
645
|
-
end
|
646
|
-
# ================================
|
647
|
-
|
648
|
-
field[:cleaners].each { |cleaner, args|
|
649
|
-
next if args === false # === cleaner has been disabled.
|
650
|
-
|
651
|
-
case cleaner
|
652
|
-
|
653
|
-
when :type
|
654
|
-
case
|
655
|
-
when field?(:numeric) && !val.is_a?(Integer)
|
656
|
-
fail! "!English_name needs to be an integer."
|
657
|
-
when field?(:chars) && !val.is_a?(String)
|
658
|
-
fail! "!English_name needs to be a String."
|
659
|
-
end
|
660
|
-
|
661
|
-
when :exact_size
|
662
|
-
if val.size != field[:exact_size]
|
663
|
-
case
|
664
|
-
when field?(:chars) || val.is_a?(String)
|
665
|
-
fail! "!English_name needs to be !exact_size in length."
|
666
|
-
else
|
667
|
-
fail! "!English_name can only be !exact_size in size."
|
668
|
-
end
|
669
|
-
end
|
670
|
-
|
671
|
-
when :set_to
|
672
|
-
args.each { |meth|
|
673
|
-
val! send(meth)
|
674
|
-
}
|
675
|
-
|
676
|
-
when :equal_to
|
677
|
-
args.each { |pair|
|
678
|
-
meth, msg, other = pair
|
679
|
-
target = send(meth)
|
680
|
-
fail!(msg || "!English_name must be equal to: #{target.inspect}") unless val == target
|
681
|
-
}
|
682
|
-
|
683
|
-
when :included_in
|
684
|
-
arr, msg, other = args
|
685
|
-
fail!(msg || "!English_name must be one of these: #{arr.join ', '}") unless arr.include?(val)
|
686
|
-
|
687
|
-
when :upcase
|
688
|
-
val! val.upcase
|
689
|
-
|
690
|
-
when :match
|
691
|
-
args.each { |pair|
|
692
|
-
regex, msg, other = pair
|
693
|
-
if val !~ regex
|
694
|
-
fail!(msg || "!English_name must match #{regex.inspect}")
|
695
|
-
end
|
696
|
-
}
|
697
|
-
|
698
|
-
when :not_match
|
699
|
-
args.each { |pair|
|
700
|
-
regex, msg, other = pair
|
701
|
-
if val =~ regex
|
702
|
-
fail!(msg || "!English_name must not match #{regex.inspect}")
|
703
|
-
end
|
704
|
-
}
|
705
|
-
|
706
|
-
else
|
707
|
-
fail "Cleaner not implemented: #{cleaner.inspect}"
|
708
|
-
end # === case cleaner
|
709
|
-
|
710
|
-
|
711
|
-
} # === field[:cleaners].each
|
712
|
-
|
713
|
-
field[:on][action].each { |meth, is_enabled|
|
714
|
-
next unless is_enabled
|
715
|
-
send meth
|
716
|
-
} if field[:on][action]
|
717
|
-
|
718
|
-
end # === catch :error_saved
|
719
|
-
} # === field
|
720
|
-
|
721
|
-
return if errors?
|
722
|
-
|
723
|
-
self.class.ons.each { |action, meths|
|
724
|
-
meths.each { |meth, is_enabled|
|
725
|
-
next unless is_enabled
|
726
|
-
catch :error_saved do
|
727
|
-
send meth
|
728
|
-
end
|
729
|
-
}
|
730
|
-
}
|
731
|
-
end
|
732
|
-
|
733
785
|
def create new_data
|
734
786
|
@new_data = new_data
|
735
787
|
run :create
|
@@ -742,6 +794,29 @@ module Datoki
|
|
742
794
|
self
|
743
795
|
end
|
744
796
|
|
797
|
+
def primary_key
|
798
|
+
arr = self.class.fields.detect { |k, v| v[:primary_key] }
|
799
|
+
fail "Primary key not found." unless arr
|
800
|
+
arr.last
|
801
|
+
end
|
802
|
+
|
803
|
+
def create?
|
804
|
+
(@raw.has_key?(:create) && @raw[:create]) ||
|
805
|
+
@raw.has_key?(primary_key[:name]) && !@raw[primary_key[:name]]
|
806
|
+
end
|
807
|
+
|
808
|
+
def read?
|
809
|
+
!!(@raw.has_key?(:read) && @raw[:read])
|
810
|
+
end
|
811
|
+
|
812
|
+
def update?
|
813
|
+
!!(@raw.has_key?(:update) && @raw[:update])
|
814
|
+
end
|
815
|
+
|
816
|
+
def delete?
|
817
|
+
!!(@raw.has_key?(:delete) && !@raw[:delete])
|
818
|
+
end
|
819
|
+
|
745
820
|
end # === module Datoki ===
|
746
821
|
|
747
822
|
|
@@ -0,0 +1,31 @@
|
|
1
|
+
|
2
|
+
describe 'Datoki.db :new' do
|
3
|
+
|
4
|
+
before {
|
5
|
+
CACHE[:datoki_db_new] ||= begin
|
6
|
+
reset_db <<-EOF
|
7
|
+
CREATE TABLE "datoki_test" (
|
8
|
+
id serial NOT NULL PRIMARY KEY,
|
9
|
+
parent_id smallint NOT NULL,
|
10
|
+
title varchar(123) NOT NULL,
|
11
|
+
body text
|
12
|
+
);
|
13
|
+
EOF
|
14
|
+
end
|
15
|
+
}
|
16
|
+
|
17
|
+
it "raises Schema_Conflict if field has not been defined, but exists in the db schema" do
|
18
|
+
should.raise(Datoki::Schema_Conflict) {
|
19
|
+
Class.new {
|
20
|
+
include Datoki
|
21
|
+
table :datoki_test
|
22
|
+
field(:id) { primary_key }
|
23
|
+
field(:parent_id) { smallint }
|
24
|
+
field(:body) { text nil, 1, 222 }
|
25
|
+
}.new
|
26
|
+
}.message.should.match /:title has not been defined/
|
27
|
+
end
|
28
|
+
|
29
|
+
end # === describe Datoki.db :new
|
30
|
+
|
31
|
+
|