datoki 1.0.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
|