datoki 2.0.1 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f8ba5eae546ee0b466bbb634b3b05203ad3cfef1
4
- data.tar.gz: c5fca1d1cc64342d30538e9850b16c2995d55dd2
3
+ metadata.gz: 43a52b61961fb85e568c481ec205a0367f13a50b
4
+ data.tar.gz: 9b9064f20a24ebe7b141ea54f9e201ce447fa1d7
5
5
  SHA512:
6
- metadata.gz: f356726aa8abe2b29ec6bfd0ff86594d81621e8bdbde5276d60aec70dc85aab1288c13893a6505f053f14d1c8d57f049707c39fdb93ce1db1fbfc51c33f90d7c
7
- data.tar.gz: 49c0f8bbbd20b2785c2be44bea06f650e76cc01a8b30783c524fc2ab278ff3a4ae94010a74bf3b467ee1d3aeab341647a958aaedd9573565ea3c653d4a2d09a2
6
+ metadata.gz: 389d463817cdad3695f0355216e3e6eba492ed092c63ffe18a7c391351f9a66305121b7242d4076c74fdb5cd64ff68195e980caba5298fc3149771c9a8e3a591
7
+ data.tar.gz: b23b19e80b655b8020bd5f57ef71772093c5b351c7a47026dbe08490e5074223e29653ae532afb649c895a9c5efa85ae55d9f3ec3b5cd0520c7b5ffe09ba49a4
data/README.md CHANGED
@@ -1,7 +1,8 @@
1
1
 
2
2
  # Datoki
3
3
 
4
- A Ruby gem for managing validation and records using PostgreSQL.
4
+ A Ruby gem that is part abstraction layer, part validator...
5
+ for managing data in PostgreSQL.
5
6
 
6
7
  ## Installation
7
8
 
@@ -39,3 +40,7 @@ A Ruby gem for managing validation and records using PostgreSQL.
39
40
  end # === class
40
41
  ```
41
42
 
43
+ ## NOTE:
44
+
45
+ 1) Raises an error if a mismatch between field definition and schema.
46
+ Example: `:allow_null != field[:allow][:null]`
data/VERSION CHANGED
@@ -1 +1 @@
1
- 2.0.1
1
+ 3.1.0
data/lib/datoki.rb CHANGED
@@ -11,7 +11,7 @@ module Datoki
11
11
  Schema_Conflict = Class.new RuntimeError
12
12
 
13
13
  Actions = [:all, :create, :read, :update, :update_or_create, :trash, :delete]
14
- Char_Types = [:varchar, :text]
14
+ Char_Types = [:varchar, :text, :string_ish]
15
15
  Numeric_Types = [:smallint, :integer, :bigint, :decimal, :numeric]
16
16
  Types = Char_Types + Numeric_Types + [:datetime]
17
17
 
@@ -51,7 +51,6 @@ module Datoki
51
51
  attr_reader :ons, :fields, :fields_as_required
52
52
 
53
53
  def initialize_def_field
54
- @on_doc = []
55
54
  @ons = {}
56
55
  @fields = {} # Ex: {:name=>{}, :age=>{}}
57
56
  @fields_as_required = {} # Ex: {:name!=>:name}
@@ -59,8 +58,6 @@ module Datoki
59
58
  @schema = {}
60
59
  @schema_match = false
61
60
  @table_name = nil
62
- name = self.to_s.downcase.to_sym
63
- table(name) if Datoki.db.tables.include?(name)
64
61
  end
65
62
 
66
63
  def schema_match?
@@ -68,6 +65,7 @@ module Datoki
68
65
  end
69
66
 
70
67
  def table name
68
+ fail ArgumentError, "Table name must be a Symbol: #{name.inspect}" unless name.is_a?(Symbol)
71
69
  if !@schema.empty? || @table_name
72
70
  fail "Schema/table already defined: #{@table_name.inspect}"
73
71
  end
@@ -75,10 +73,11 @@ module Datoki
75
73
  db_schema = Datoki.db.schema(name)
76
74
 
77
75
  if !db_schema
78
- fail "Schema not found for: #{name.inspect}"
76
+ fail ArgumentError, "Schema not found for: #{name.inspect}"
79
77
  end
80
78
 
81
79
  @table_name = name
80
+ self.const_set(:TABLE, DB[@table_name])
82
81
 
83
82
  db_schema.each { |pair|
84
83
  @schema[pair.first] = pair.last
@@ -130,15 +129,25 @@ module Datoki
130
129
  end
131
130
  end
132
131
 
133
- def field? *args
134
- inspect_field?(:type, field[:name], *args)
132
+ def pseudo
133
+ fields[@current_field][:pseudo] = true
135
134
  end
136
135
 
137
136
  def allow sym
138
137
  fields[@current_field][:allow][sym] = true;
139
138
  end
140
139
 
140
+ def field? *args
141
+ inspect_field?(:type, field[:name], *args)
142
+ end
143
+
141
144
  def field *args
145
+ # === Setup a default table if none specified:
146
+ if !@table_name
147
+ t_name = self.to_s.downcase.to_sym
148
+ table(name) if Datoki.db.tables.include?(t_name)
149
+ end
150
+
142
151
  return fields[@current_field] if args.empty?
143
152
  return fields[args.first] unless block_given?
144
153
 
@@ -214,11 +223,9 @@ module Datoki
214
223
  name = @current_field
215
224
  db_schema = schema[@current_field]
216
225
 
217
- if db_schema && !field && db_schema[:type] != :datetime
218
- fail Schema_Conflict, "#{name}: #{name.inspect} has not been defined."
219
- end
220
-
221
- return true if field[:schema_match]
226
+ return true if db_schema && !field
227
+ return true if field[:schema_has_been_matched]
228
+ return true if field[:pseudo]
222
229
 
223
230
  if db_schema[:allow_null] != field[:allow][:null]
224
231
  fail Schema_Conflict, "#{name}: :allow_null: #{db_schema[:allow_null].inspect} != #{field[:allow][:null].inspect}"
@@ -245,10 +252,12 @@ module Datoki
245
252
  end
246
253
 
247
254
  # === match :type
248
- db_type = Datoki.db_type_to_ruby db_schema[:db_type], db_schema[:type]
249
- type = field[:type]
250
- if db_type != type
251
- fail Schema_Conflict, "#{name}: :type: #{db_type.inspect} != #{type.inspect}"
255
+ if field[:type] != :string_ish
256
+ db_type = Datoki.db_type_to_ruby db_schema[:db_type], db_schema[:type]
257
+ type = field[:type]
258
+ if db_type != type
259
+ fail Schema_Conflict, "#{name}: :type: #{db_type.inspect} != #{type.inspect}"
260
+ end
252
261
  end
253
262
 
254
263
  # === match :max_length
@@ -270,14 +279,7 @@ module Datoki
270
279
  fail Schema_Conflict, "#{name}: :allow_null: #{db_schema[:allow_null].inspect} != #{field[:allow][:null].inspect}"
271
280
  end
272
281
 
273
- field[:schema_match] = true
274
- end
275
-
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
282
+ field[:schema_has_been_matched] = true
281
283
  end
282
284
 
283
285
  def field_on action, meth_name_sym
@@ -373,6 +375,14 @@ module Datoki
373
375
  when [Fixnum, Fixnum]
374
376
  field[:min], field[:max] = args
375
377
 
378
+ when [Proc], [Regexp]
379
+ matches *args
380
+
381
+ when [Fixnum, Fixnum, Proc], [Fixnum, Fixnum, Regexp]
382
+ field[:min] = args.shift
383
+ field[:max] = args.shift
384
+ matches *args
385
+
376
386
  else
377
387
  fail "Unknown args: #{args.inspect}"
378
388
 
@@ -380,6 +390,15 @@ module Datoki
380
390
 
381
391
  end # === def
382
392
 
393
+ [:mis_match, :small, :big].each { |name|
394
+ eval <<-EOF
395
+ def #{name} msg
396
+ field[:error_msgs] ||= {}
397
+ field[:error_msgs][:#{name}] = msg
398
+ end
399
+ EOF
400
+ }
401
+
383
402
  def enable *props
384
403
  props.each { |prop|
385
404
  case prop
@@ -404,9 +423,9 @@ module Datoki
404
423
  }
405
424
  end
406
425
 
407
- def set_to *args
426
+ def set_to v = :blok
408
427
  field[:cleaners][:set_to] ||= []
409
- field[:cleaners][:set_to].concat args
428
+ field[:cleaners][:set_to] << (v == :blok ? Proc.new : v)
410
429
  end
411
430
 
412
431
  def equal_to *args
@@ -432,17 +451,10 @@ module Datoki
432
451
  EOF
433
452
  }
434
453
 
435
- def match *args
454
+ def matches v = :blok
436
455
  fail "Not allowed for #{field[:type].inspect}" unless field?(:chars)
437
456
  field[:cleaners][:match] ||= []
438
- field[:cleaners][:match] << args
439
- end
440
-
441
- def not_match *args
442
- fail "Not allowed for #{field[:type].inspect}" unless field?(:chars)
443
- field[:cleaners][:not_match] ||= []
444
- field[:cleaners][:not_match] << args
445
- self
457
+ field[:cleaners][:match] << (v == :blok ? Proc.new : v)
446
458
  end
447
459
 
448
460
  def create raw
@@ -454,13 +466,14 @@ module Datoki
454
466
 
455
467
  # ================= Instance Methods ===============
456
468
 
457
- attr_reader :error
469
+ attr_reader :error, :data
458
470
  def initialize unknown = nil
459
471
  @data = nil
460
472
  @field_name = nil
461
473
  @clean = nil
462
474
  @error = nil
463
475
  @skips = {}
476
+ @db_ops = {} # Ex: :db_insert=>true, :db_update=>true
464
477
 
465
478
  if unknown
466
479
  if unknown.keys.all? { |f| self.class.fields.has_key?(f) }
@@ -472,56 +485,62 @@ module Datoki
472
485
  end
473
486
 
474
487
  if @raw
475
- self.class.on_doc.each { |raw_arr|
476
-
477
- conds = raw_arr.first
478
- func = raw_arr.last
479
- instance_eval(&func) if conds.all? { |cond|
480
- case cond
481
- when Symbol
482
- send(cond)
483
- when Proc
484
- cond.arity == 1 ? cond.call(@raw) : instance_eval(&cond)
485
- when TrueClass, FalseClass
486
- cond
487
- else
488
- fail ArgumentError, "Unknown: #{cond.inspect}"
489
- end
490
- }
491
488
 
492
- } # === on_doc.each
489
+ schema = self.class.schema
490
+
491
+ case
492
+ when create? && respond_to?(:create)
493
+ create
494
+ when update? && respond_to?(:update)
495
+ update
496
+ when delete? && respond_to?(:delete)
497
+ delete
498
+ end
493
499
 
494
- if !@clean
495
- @raw.each { |k, v|
496
- clean(k) if self.class.fields.has_key?(k)
500
+ if @clean
501
+ @clean.each { |k, v|
502
+ # === Delete nil value if schema has a default value:
503
+ @clean.delete(k) if @clean[k].nil? && schema[k] && schema[k][:default]
497
504
  }
498
505
  end
499
506
 
500
- schema = self.class.schema
501
- if create?
502
- # === Did the programmer forget to set the value?:
503
- self.class.fields.each { |k, meta|
504
- has_default = schema[k] && schema[k][:default]
505
- if clean[k].nil? && !meta[:allow][:null] && !meta[:primary_key] && !has_default
506
- fail ArgumentError, "#{k.inspect} is not set."
507
+ fail "No clean values found." if (!@clean || @clean.empty?)
508
+
509
+ if !@skips[:db] && !self.class.schema.empty?
510
+
511
+ final = db_clean
512
+ begin
513
+ case
514
+
515
+ when create?
516
+ db_insert
517
+
518
+ when update?
519
+
520
+ DB[self.class.table].
521
+ where(primary_key[:name] => final.delete(primary_key[:name])).
522
+ update(final)
523
+
524
+ when delete?
525
+ DB[self.class.table].
526
+ where(primary_key[:name] => final.delete(primary_key[:name])).
527
+ delete
528
+
507
529
  end
508
530
 
509
- # === Should we let the DB set the value?
510
- @clean.delete(k) if @clean[k].nil? && has_default
511
- }
512
- end
531
+ rescue Sequel::UniqueConstraintViolation => e
513
532
 
514
- case
515
- when create?
516
- insert_into_table unless !respond_to?(:insert_into_table)
517
- when update?
518
- alter_record unless !respond_to?(:alter_record)
519
- when delete?
520
- delete_from_table unless !respond_to?(:delete_from_table)
521
- end unless @skips[:db]
522
- end # === if @raw
533
+ self.class.fields.each { |f|
534
+ if e.message["'\"#{f}_"]
535
+ field_name f
536
+ fail! :unique, "{{English name}} already taken: #{final[f]}"
537
+ end
538
+ }
539
+ raise e
523
540
 
524
- self.class.schema_match(:all)
541
+ end # === begin/rescue
542
+ end # === if !@skips[:db]
543
+ end # === if @raw
525
544
  end
526
545
 
527
546
  def skip name
@@ -532,6 +551,22 @@ module Datoki
532
551
  @error && !@error.empty?
533
552
  end
534
553
 
554
+ def db_clean
555
+ @clean.select { |k, v|
556
+ !self.class.fields[k][:pseudo]
557
+ }
558
+ end
559
+
560
+ def clean! *args
561
+ args.each { |name|
562
+ if @raw[name].nil? && (!@clean || @clean[name].nil?)
563
+ fail ArgumentError, "#{name.inspect} is not set."
564
+ else
565
+ clean name
566
+ end
567
+ }
568
+ end
569
+
535
570
  def clean *args
536
571
  @clean ||= {}
537
572
 
@@ -546,10 +581,8 @@ module Datoki
546
581
 
547
582
  name = args.first
548
583
 
549
- if (real_name = self.class.fields_as_required[name]) && @raw[real_name].nil? && @clean[real_name].nil?
550
- fail ArgumentError, "#{real_name.inspect} is not set."
551
- else
552
- name = real_name || name
584
+ if (real_name = self.class.fields_as_required[name])
585
+ return(clean! real_name)
553
586
  end
554
587
 
555
588
  @clean[name] = @raw[name] if !clean.has_key?(name) && @raw.has_key?(name)
@@ -572,14 +605,14 @@ module Datoki
572
605
  if field?(:numeric) && clean[name].is_a?(String)
573
606
  clean_val = Integer(clean[name]) rescue String
574
607
  if clean_val == String
575
- fail! "!English_name must be numeric."
608
+ fail! :wrong_type, "{{English name}} must be numeric."
576
609
  else
577
610
  clean[name] = clean_val
578
611
  end
579
612
  end
580
613
 
581
614
  if field?(:text) && clean[name].is_a?(String) && clean[name].empty? && field[:min].to_i > 0
582
- fail! "!English_name is required."
615
+ fail! :required, "{{English name}} is required."
583
616
  end
584
617
  # ================================
585
618
 
@@ -593,25 +626,30 @@ module Datoki
593
626
  when [NilClass, Fixnum]
594
627
  case
595
628
  when clean[name].is_a?(String) && clean[name].size > field[:max]
596
- fail! "!English_name can't be longer than !max characters."
629
+ fail! :big, "{{English name}} can't be longer than {{max}} characters."
597
630
  when clean[name].is_a?(Numeric) && clean[name] > field[:max]
598
- fail! "!English_name can't be higher than !max."
631
+ fail! :big, "{{English name}} can't be higher than {{max}}."
599
632
  end
600
633
 
601
634
  when [Fixnum, NilClass]
602
635
  case
603
636
  when clean[name].is_a?(String) && clean[name].size < field[:min]
604
- fail! "!English_name can't be shorter than !min characters."
637
+ fail! :short, "{{English name}} can't be shorter than {{min}} characters."
605
638
  when clean[name].is_a?(Numeric) && clean[name] < field[:min]
606
- fail! "!English_name can't be less than !min."
639
+ fail! :short, "{{English name}} can't be less than {{min}."
607
640
  end
608
641
 
609
642
  when [Fixnum, Fixnum]
610
643
  case
611
- when clean[name].is_a?(String) && (clean[name].size < field[:min] || clean[name].size > field[:max])
612
- fail! "!English_name must be between !min and !max characters."
613
- when clean[name].is_a?(Numeric) && (clean[name] < field[:min] || clean[name] > field[:max])
614
- fail! "!English_name must be between !min and !max."
644
+ when field?(:chars) && clean[name].size > field[:max]
645
+ fail! :big, "{{English name}} must be between {{min}} and {{max}} characters."
646
+ when field?(:chars) && clean[name].size < field[:min]
647
+ fail! :small, "{{English name}} must be between {{min}} and {{max}} characters."
648
+
649
+ when field?(:numeric) && clean[name] > field[:max]
650
+ fail! :big, "{{English name}} must be between {{min}} and {{max}}."
651
+ when field?(:numeric) && clean[name] < field[:min]
652
+ fail! :small, "{{English name}} must be between {{min}} and {{max}}."
615
653
  end
616
654
 
617
655
  else
@@ -637,7 +675,7 @@ module Datoki
637
675
  # === Is value in options? =======
638
676
  if field[:options]
639
677
  if !field[:options].include?(clean[name])
640
- fail! "!English_name can only be: #{field[:options].map(&:inspect).join ', '}"
678
+ fail! :mis_match, "{{English name}} can only be: #{field[:options].map(&:inspect).join ', '}"
641
679
  end
642
680
  end
643
681
  # ================================
@@ -650,53 +688,55 @@ module Datoki
650
688
  when :type
651
689
  case
652
690
  when field?(:numeric) && !clean[name].is_a?(Integer)
653
- fail! "!English_name needs to be an integer."
691
+ fail! :wrong_type, "{{English name}} needs to be an integer."
654
692
  when field?(:chars) && !clean[name].is_a?(String)
655
- fail! "!English_name needs to be a String."
693
+ fail! :wrong_type, "{{English name}} needs to be a String."
656
694
  end
657
695
 
658
696
  when :exact_size
659
697
  if clean[name].size != field[:exact_size]
660
698
  case
661
699
  when field?(:chars) || clean[name].is_a?(String)
662
- fail! "!English_name needs to be !exact_size in length."
700
+ fail! :mis_match, "{{English name}} needs to be {{exact_size}} in length."
663
701
  else
664
- fail! "!English_name can only be !exact_size in size."
702
+ fail! :mis_match, "{{English name}} can only be {{exact_size}} in size."
665
703
  end
666
704
  end
667
705
 
668
706
  when :set_to
669
707
  args.each { |meth|
670
- clean[name] = send(meth)
708
+ clean[name] = (meth.is_a?(Symbol) ? send(meth) : meth.call(self, clean[name]))
671
709
  }
672
710
 
673
711
  when :equal_to
674
712
  args.each { |pair|
675
713
  meth, msg, other = pair
676
714
  target = send(meth)
677
- fail!(msg || "!English_name must be equal to: #{target.inspect}") unless clean[name] == target
715
+ fail!(msg || "{{English name}} must be equal to: #{target.inspect}") unless clean[name] == target
678
716
  }
679
717
 
680
718
  when :included_in
681
719
  arr, msg, other = args
682
- fail!(msg || "!English_name must be one of these: #{arr.join ', '}") unless arr.include?(clean[name])
720
+ fail!(msg || "{{English name}} must be one of these: #{arr.join ', '}") unless arr.include?(clean[name])
683
721
 
684
722
  when :upcase
685
723
  clean[name] = clean[name].upcase
686
724
 
687
725
  when :match
688
- args.each { |pair|
689
- regex, msg, other = pair
690
- if clean[name] !~ regex
691
- fail!(msg || "!English_name must match #{regex.inspect}")
692
- end
693
- }
726
+ args.each { |regex|
727
+ case regex
728
+ when Regexp
729
+ if clean[name] !~ regex
730
+ fail!(:mis_match, "{{English name}} is invalid.")
731
+ end
732
+
733
+ when Proc
734
+ if !regex.call(self, clean[name])
735
+ fail!(:mis_match, "{{English name}} is invalid.")
736
+ end
694
737
 
695
- when :not_match
696
- args.each { |pair|
697
- regex, msg, other = pair
698
- if clean[name] =~ regex
699
- fail!(msg || "!English_name must not match #{regex.inspect}")
738
+ else
739
+ fail ArgumentError, "Unknown matcher: #{regex.inspect}"
700
740
  end
701
741
  }
702
742
 
@@ -706,34 +746,31 @@ module Datoki
706
746
  } # === field[:cleaners].each
707
747
  end # === def clean
708
748
 
709
- def new_data
710
- @new_data ||= {}
749
+ def error_msg type
750
+ field[:error_msgs] && field[:error_msgs][type]
711
751
  end
712
752
 
713
- def on *args
714
- fail ArgumentError, "No conditions." if args.empty?
715
- yield if args.all? { |cond|
716
- case cond
717
- when Symbol
718
- send(cond)
719
- when TrueClass, FalseClass
720
- cond
721
- else
722
- fail ArgumentError, "Unknown value: #{cond.inspect}"
723
- end
724
- }
725
- end
753
+ def fail! *args
754
+ case args.size
755
+ when 1
756
+ msg = args.shift
757
+ when 2
758
+ msg = error_msg(args.shift) || args.shift
759
+ else
760
+ fail ArgumentError, "Unknown args: #{args.inspect}"
761
+ end
726
762
 
727
- def fail! msg
728
- err_msg = msg.gsub(/!([a-z\_\-]+)/i) { |raw|
763
+ err_msg = msg.gsub(/\{\{([a-z\_\-\ ]+)\}\}/i) { |raw|
729
764
  name = $1
730
765
  case name
731
- when "English_name"
766
+ when "English name"
732
767
  self.class.fields[field_name][:english_name].capitalize.gsub('_', ' ')
733
- when "ENGLISH_NAME"
768
+ when "ENGLISH NAME"
734
769
  self.class.fields[field_name][:english_name].upcase.gsub('_', ' ')
735
770
  when "max", "min", "exact_size"
736
771
  self.class.fields[field_name][name.downcase.to_sym]
772
+ when "val"
773
+ clean[field_name]
737
774
  else
738
775
  fail "Unknown value: #{name}"
739
776
  end
@@ -771,24 +808,16 @@ module Datoki
771
808
  self.class.inspect_field? :type, field_name, *args
772
809
  end
773
810
 
774
- def create new_data
775
- @new_data = new_data
776
- run :create
777
- self
778
- end
779
-
780
- def update new_data
781
- @new_data = new_data
782
- run :update
783
- self
784
- end
785
-
786
811
  def primary_key
787
812
  arr = self.class.fields.detect { |k, v| v[:primary_key] }
788
813
  fail "Primary key not found." unless arr
789
814
  arr.last
790
815
  end
791
816
 
817
+ def new?
818
+ !@data
819
+ end
820
+
792
821
  def create?
793
822
  (@raw.has_key?(:create) && @raw[:create]) ||
794
823
  @raw.has_key?(primary_key[:name]) && !@raw[primary_key[:name]]
@@ -806,6 +835,18 @@ module Datoki
806
835
  !!(@raw.has_key?(:delete) && !@raw[:delete])
807
836
  end
808
837
 
838
+ def TABLE
839
+ self.class::TABLE
840
+ end
841
+
842
+ def db_insert
843
+ k = :db_insert
844
+ final = db_clean
845
+ fail "Already inserted." if @db_ops[k]
846
+ @data = (@data || {}).merge(TABLE().returning.insert(final).first)
847
+ @db_ops[k] = true
848
+ end
849
+
809
850
  end # === module Datoki ===
810
851
 
811
852
 
@@ -14,13 +14,29 @@ describe "Datoki.db" do
14
14
 
15
15
  @klass = Class.new {
16
16
  include Datoki
17
- table "datoki_test"
17
+ table :datoki_test
18
18
  field(:id) { integer; primary_key }
19
19
  field(:title) { varchar 1, 123 }
20
20
  field(:body) { text nil, 1, 123 }
21
+
22
+ def create
23
+ clean :title, :body
24
+ end
21
25
  }
22
26
  }
23
27
 
28
+ it "allows an undefined field that exists in the db schema" do
29
+ Class.new {
30
+ include Datoki
31
+ table :datoki_test
32
+ field(:id) { primary_key }
33
+ field(:title) { varchar 1, 123 }
34
+ def create
35
+ clean :title
36
+ end
37
+ }.create(:title=>'title').data.should == {:id=>1, :title=>'title', :body=>'hello'}
38
+ end
39
+
24
40
  it 'raises Schema_Conflict if a field is found that allows null, but not specifed to do so' do
25
41
  should.raise(Datoki::Schema_Conflict) {
26
42
  Class.new {
@@ -30,13 +46,13 @@ describe "Datoki.db" do
30
46
  field(:title) { varchar 1, 123 }
31
47
  field(:body) { text 1, 123 }
32
48
  }
33
- }.message.should.match /:allow_null: true != false/
49
+ }.message.should.match /body: :allow_null: true != false/
34
50
  end
35
51
 
36
52
  it "requires field if value = null and :allow_null = false" do
37
- should.raise(ArgumentError) {
53
+ should.raise(Sequel::NotNullConstraintViolation) {
38
54
  @klass.create :title=>nil, :body=>"hiya"
39
- }.message.should.match /:title is not set/
55
+ }.message.should.match /null value in column "title" violates not-null constraint/
40
56
  end
41
57
 
42
58
  it "requires a value if: :text field, value = (empty string), min = 1, allow null" do
@@ -7,6 +7,9 @@ describe Numeric do
7
7
  include Datoki
8
8
  field(:id) { primary_key }
9
9
  field(:age) { smallint 1, 150 }
10
+ def create
11
+ clean :age
12
+ end
10
13
  }
11
14
  end
12
15
  }
@@ -31,8 +34,11 @@ describe Numeric do
31
34
  include Datoki
32
35
  field(:id) { primary_key }
33
36
  field(:age) { smallint nil, 1, 99 }
37
+ def create
38
+ clean :age
39
+ end
34
40
  }.create(:age=>nil).
35
- clean[:age].should == nil
41
+ clean.should == {:age=>nil}
36
42
  end
37
43
 
38
44
  it "allows nil in an array" do
@@ -40,8 +46,11 @@ describe Numeric do
40
46
  include Datoki
41
47
  field(:id) { primary_key }
42
48
  field(:age) { smallint [nil, 1,2,3,4] }
49
+ def create
50
+ clean :age
51
+ end
43
52
  }.create(:age=>nil).
44
- clean[:age].should == nil
53
+ clean.should == {:age=>nil}
45
54
  end
46
55
 
47
56
  it "allows to specify an Array of possible values" do
@@ -49,6 +58,9 @@ describe Numeric do
49
58
  include Datoki
50
59
  field(:id) { primary_key }
51
60
  field(:age) { smallint [1,2,3,4] }
61
+ def create
62
+ clean :age
63
+ end
52
64
  }.create(:age=>2).
53
65
  clean[:age].should == 2
54
66
  end
@@ -59,8 +71,41 @@ describe Numeric do
59
71
  include Datoki
60
72
  field(:id) { primary_key }
61
73
  field(:num) { smallint [1,2,3,4] }
74
+ def create
75
+ clean :num
76
+ end
62
77
  }.create :num=>0
63
78
  }.error[:msg].should.match /Num can only be: 1, 2, 3, 4/
64
79
  end
65
80
 
81
+ it "uses :big error msg" do
82
+ catch(:invalid) {
83
+ Class.new {
84
+ include Datoki
85
+ field(:squirrels) {
86
+ smallint 10, 10
87
+ big '{{val}} is too big.'
88
+ }
89
+ def create
90
+ clean :squirrels
91
+ end
92
+ }.create :squirrels=>50
93
+ }.error[:msg].should == "50 is too big."
94
+ end # === it uses :big error msg
95
+
96
+ it "uses :small error msg" do
97
+ catch(:invalid) {
98
+ Class.new {
99
+ include Datoki
100
+ field(:squirrels) {
101
+ smallint 10, 10
102
+ small '{{val}} is too small.'
103
+ }
104
+ def create
105
+ clean :squirrels
106
+ end
107
+ }.create :squirrels=>5
108
+ }.error[:msg].should == "5 is too small."
109
+ end # === it uses :small error msg
110
+
66
111
  end # === describe Numeric
@@ -17,7 +17,7 @@ describe 'Datoki.db number' do
17
17
  it "does not set :min = 1" do
18
18
  Class.new {
19
19
  include Datoki
20
- table "datoki_test"
20
+ table :datoki_test
21
21
  field(:parent_id) { smallint }
22
22
  }.
23
23
  fields[:parent_id][:min].should == nil
@@ -1,20 +1,14 @@
1
1
 
2
2
  describe :varchar do # ================================================
3
3
 
4
- it "requires field by default during :create" do
5
- should.raise(ArgumentError) {
6
- Class.new {
7
- include Datoki
8
- field(:title) { varchar }
9
- }.create({})
10
- }.message.should.match /:title is not set/
11
- end
12
-
13
4
  it "raises RuntimeError if allow :null and :min = 0" do
14
5
  should.raise(RuntimeError) {
15
6
  Class.new {
16
7
  include Datoki
17
8
  field(:name) { varchar nil, 0, 50 }
9
+ def create
10
+ clean :name
11
+ end
18
12
  }
19
13
  }.message.should.match /varchar can't be both: allow :null && :min = 0/
20
14
  end
@@ -24,6 +18,9 @@ describe :varchar do # ================================================
24
18
  Class.new {
25
19
  include Datoki
26
20
  field(:title) { varchar 3, 255 }
21
+ def create
22
+ clean :title
23
+ end
27
24
  }.create :title => '1'
28
25
  }.error[:msg].should.match /Title must be between 3 and 255 characters/i
29
26
  end
@@ -33,6 +30,9 @@ describe :varchar do # ================================================
33
30
  Class.new {
34
31
  include Datoki
35
32
  field(:title) { varchar 0, 5 }
33
+ def create
34
+ clean :title
35
+ end
36
36
  }.create :title => '123456'
37
37
  }.error[:msg].should.match /Title must be between 0 and 5 characters/
38
38
  end
@@ -42,8 +42,11 @@ describe :varchar do # ================================================
42
42
  Class.new {
43
43
  include Datoki
44
44
  field :title do
45
- varchar
46
- match /\A[a-zA-Z0-9]+\z/i, "Title must be only: alphanumeric"
45
+ varchar /\A[a-zA-Z0-9]+\z/i
46
+ mis_match "Title must be only: alphanumeric"
47
+ end
48
+ def create
49
+ clean :title
47
50
  end
48
51
  }.create :title => '$! title'
49
52
  }.error[:msg].should.match /Title must be only: alphanumeric/
@@ -52,10 +55,12 @@ describe :varchar do # ================================================
52
55
  it "allows varchar to be nil" do
53
56
  r = Class.new {
54
57
  include Datoki
55
- field(:title) {
56
- varchar nil, 1, 123
57
- }
58
- }.create({})
58
+ field(:title) { varchar nil, 1, 123 }
59
+ field(:body) { varchar nil, 1, 123 }
60
+ def create
61
+ clean :title, :body
62
+ end
63
+ }.create({:body=>'yo'})
59
64
  r.clean.has_key?(:title).should == false
60
65
  end
61
66
 
@@ -69,6 +74,9 @@ describe :varchar do # ================================================
69
74
  'Custom title'
70
75
  end
71
76
  }
77
+ def create
78
+ clean :title
79
+ end
72
80
  }.
73
81
  create(:title => 'My Title').
74
82
  clean[:title].should.match /Custom title/
@@ -78,6 +86,9 @@ describe :varchar do # ================================================
78
86
  Class.new {
79
87
  include Datoki
80
88
  field(:title) { varchar }
89
+ def create
90
+ clean :title
91
+ end
81
92
  }.
82
93
  create(:title => ' my title ').
83
94
  clean[:title].should == 'my title'
@@ -90,6 +101,9 @@ describe :varchar do # ================================================
90
101
  varchar
91
102
  disable :strip
92
103
  }
104
+ def create
105
+ clean :title
106
+ end
93
107
  }.
94
108
  create(:title => ' my title ').
95
109
  clean[:title].should == ' my title '
@@ -11,7 +11,7 @@ describe "Datoki.db :varchar" do
11
11
  EOF
12
12
  @klass = Class.new {
13
13
  include Datoki
14
- table "datoki_test"
14
+ table :datoki_test
15
15
  field(:id) { primary_key }
16
16
  field(:title) { varchar 1, 123 }
17
17
  field(:body) { text nil, 1, 3000 }
@@ -0,0 +1,60 @@
1
+
2
+ describe :string do
3
+
4
+ before {
5
+ CACHE[:datoki_string] ||= begin
6
+ reset_db <<-EOF
7
+ CREATE TABLE "datoki_test" (
8
+ id serial NOT NULL PRIMARY KEY,
9
+ ip inet NOT NULL
10
+ );
11
+ EOF
12
+ end
13
+ }
14
+
15
+ it "treats special PG types as a string" do
16
+ Class.new {
17
+ include Datoki
18
+ table :datoki_test
19
+ field(:ip) {
20
+ string_ish 5, 50, /\A[0-9\:\.]+\Z/
21
+ mis_match "Invalid format for ip: !val"
22
+ }
23
+ def create
24
+ clean :ip
25
+ end
26
+ }.create(:ip=>'127.0.0.2')
27
+ DB[:datoki_test].all.should == [{:id=>1, :ip=>'127.0.0.2'}]
28
+ end # === it treats special PG types as a string
29
+
30
+ it "uses :big error msg" do
31
+ catch(:invalid) {
32
+ Class.new {
33
+ include Datoki
34
+ field(:note) {
35
+ string_ish 1,5
36
+ big '{{English name}} can\'t be bigger than {{max}}.'
37
+ }
38
+ def create
39
+ clean :note
40
+ end
41
+ }.create :note=>"1234567"
42
+ }.error[:msg].should == "Note can\'t be bigger than 5."
43
+ end # === it uses :big error msg
44
+
45
+ it "uses :small error msg" do
46
+ catch(:invalid) {
47
+ Class.new {
48
+ include Datoki
49
+ field(:note) {
50
+ string_ish 2,5
51
+ small '{{English name}} can\'t be smaller than {{min}}.'
52
+ }
53
+ def create
54
+ clean :note
55
+ end
56
+ }.create :note=>"1"
57
+ }.error[:msg].should == "Note can\'t be smaller than 2."
58
+ end # === it uses :small error msg
59
+
60
+ end # === describe :string
@@ -0,0 +1,34 @@
1
+
2
+ describe :pseudo do
3
+
4
+ before {
5
+ CACHE[:datoki_pseudo] ||= begin
6
+ reset_db <<-EOF
7
+ CREATE TABLE "datoki_test" (
8
+ id serial NOT NULL PRIMARY KEY,
9
+ title varchar(15) NOT NULL,
10
+ body text NOT NULL
11
+ );
12
+ EOF
13
+ end
14
+ }
15
+
16
+ it "does not save values to database" do
17
+ Class.new {
18
+ include Datoki
19
+ table :datoki_test
20
+ field(:title) { varchar 5, 15 }
21
+ field(:body) { text 5, 15 }
22
+ field(:password) {
23
+ string_ish 5, 10, /\A[a-z0-9\ ]+\Z/
24
+ pseudo
25
+ }
26
+ def create
27
+ clean :title, :body, :password
28
+ end
29
+ }.create(:title=>'Yo yo yo', :body=>'The body',:password=>'11111111')
30
+
31
+ DB[:datoki_test].all.should == [{:id=>1,:title=>'Yo yo yo', :body=>'The body'}]
32
+ end # === it does not save values to database
33
+
34
+ end # === describe :pseudo
@@ -0,0 +1,19 @@
1
+
2
+ describe :clean! do
3
+
4
+ it "fails w/ArgumentError if field is undefined" do
5
+ c = Class.new {
6
+ include Datoki
7
+
8
+ field(:name) { varchar }
9
+ def create
10
+ clean! :name, :nick
11
+ end
12
+ }
13
+
14
+ should.raise(ArgumentError) {
15
+ c.create(:nick=>'Bob')
16
+ }.message.should.match /:name is not set/
17
+ end # === it fails w/ArgumentError if underfined
18
+
19
+ end # === describe :clean!
data/specs/0020-clean.rb CHANGED
@@ -6,7 +6,10 @@ describe :clean do
6
6
  include Datoki
7
7
 
8
8
  field(:nick_name) { varchar 1,50 }
9
- on(:happy?) { clean :nick_name }
9
+ def create
10
+ clean :nick_name
11
+ skip :db
12
+ end
10
13
 
11
14
  def happy?
12
15
  true
@@ -24,12 +27,9 @@ describe :clean do
24
27
  field(:nick_name) { varchar 3, 255 }
25
28
  field(:age) { smallint; allow :null }
26
29
 
27
- on :happy? do
30
+ def create
28
31
  clean :nick_name, :age
29
- end
30
-
31
- def happy?
32
- true
32
+ skip :db
33
33
  end
34
34
  }
35
35
  c.create(:nick_name=>'Wiley').
@@ -41,17 +41,13 @@ describe :clean do
41
41
  include Datoki
42
42
 
43
43
  field(:name) { varchar }
44
- on :happy? do
44
+ def create
45
45
  clean :name!
46
46
  end
47
-
48
- def happy?
49
- true
50
- end
51
47
  }
52
48
 
53
49
  should.raise(ArgumentError) {
54
- c.new(:happy=>true, :nick=>'Bob')
50
+ c.create(:nick=>'Bob')
55
51
  }.message.should.match /:name is not set/
56
52
  end # === it fails w/ArgumentError if underfined
57
53
 
@@ -0,0 +1,93 @@
1
+
2
+ describe :matching do
3
+
4
+ before {
5
+ CACHE[:datoki_matching] ||= begin
6
+ reset_db <<-EOF
7
+ CREATE TABLE "datoki_test" (
8
+ id serial NOT NULL PRIMARY KEY,
9
+ title varchar(15) NOT NULL,
10
+ body text NOT NULL,
11
+ age smallint NOT NULL
12
+ );
13
+ EOF
14
+ end
15
+ }
16
+
17
+ it "uses error message from :mis_match" do
18
+ catch(:invalid) {
19
+ Class.new {
20
+ include Datoki
21
+ table :datoki_test
22
+ field(:title) { varchar 5, 15, lambda { |r, val| false }; mis_match "Title is bad." }
23
+ field(:body) { text 5, 10 }
24
+ field(:age) { smallint }
25
+ def create
26
+ clean :title, :body, :age
27
+ end
28
+ }.create(:title=>'title', :body=>'body', :age=>50)
29
+ }.error[:msg].should.match /Title is bad/
30
+ end # === it uses error message from :mis_match
31
+
32
+ it "inserts data into db if match w/Regexp matcher" do
33
+ Class.new {
34
+ include Datoki
35
+ table :datoki_test
36
+ field(:title) { varchar 5, 15, /title 4/ }
37
+ field(:body) { text 5, 10 }
38
+ field(:age) { smallint }
39
+ def create
40
+ clean :title, :body, :age
41
+ end
42
+ }.create(:title=>'title 4', :body=>'body 4', :age=>50)
43
+
44
+ DB[:datoki_test].all.last.should == {:id=>1, :title=>'title 4', :body=>'body 4', :age=>50}
45
+ end # === it inserts data into db if matcher returns true
46
+
47
+ it "inserts data into db if lambda matcher returns true" do
48
+ Class.new {
49
+ include Datoki
50
+ table :datoki_test
51
+ field(:title) { varchar 5, 15, lambda { |r, val| true } }
52
+ field(:body) { text 5, 10 }
53
+ field(:age) { smallint }
54
+ def create
55
+ clean :title, :body, :age
56
+ end
57
+ }.create(:title=>'title 5', :body=>'body 5', :age=>50)
58
+
59
+ DB[:datoki_test].all.last.should == {:id=>2, :title=>'title 5', :body=>'body 5', :age=>50}
60
+ end # === it inserts data into db if matcher returns true
61
+
62
+ it "throws :invalid if mis-match w/Regexp matcher" do
63
+ catch(:invalid) {
64
+ Class.new {
65
+ include Datoki
66
+ table :datoki_test
67
+ field(:title) { varchar 5, 15, /\Agood\Z/; mis_match "Title is really bad." }
68
+ field(:body) { text 5, 10 }
69
+ field(:age) { smallint }
70
+ def create
71
+ clean :title, :body, :age
72
+ end
73
+ }.create(:title=>'baddd', :body=>'body', :age=>50)
74
+ }.error[:msg].should.match /Title is really bad/
75
+ end # === it accepts a Regexp as a matcher
76
+
77
+ it "throws :invalid if lambda matcher returns false" do
78
+ r = catch(:invalid) {
79
+ Class.new {
80
+ include Datoki
81
+ table :datoki_test
82
+ field(:title) { varchar 5, 15, lambda { |r, v| false } }
83
+ field(:body) { text 5, 10 }
84
+ field(:age) { smallint }
85
+ def create
86
+ clean :title, :body, :age
87
+ end
88
+ }.create(:title=>'title', :body=>'body', :age=>50)
89
+ }
90
+ r.error[:msg].should.match /Title is invalid/
91
+ end # === it throws :invalid if matcher returns false
92
+
93
+ end # === describe :matching
@@ -0,0 +1,32 @@
1
+
2
+ describe :set_to do
3
+
4
+ it "sets val to block" do
5
+ Class.new {
6
+ include Datoki
7
+ field(:age) {
8
+ smallint
9
+ set_to { |raw, val| val + 50 }
10
+ }
11
+ def create
12
+ clean :age
13
+ end
14
+ }.create(:age=>20).
15
+ clean.should == {:age=>70}
16
+ end # === it sets val to lambda
17
+
18
+ it "sets val to lambda" do
19
+ Class.new {
20
+ include Datoki
21
+ field(:age) {
22
+ smallint
23
+ set_to(lambda { |raw, val| val + 10 })
24
+ }
25
+ def create
26
+ clean :age
27
+ end
28
+ }.create(:age=>20).
29
+ clean.should == {:age=>30}
30
+ end # === it sets val to lambda
31
+
32
+ end # === describe :set_to
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: datoki
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.1
4
+ version: 3.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - da99
@@ -108,7 +108,6 @@ files:
108
108
  - VERSION
109
109
  - datoki.gemspec
110
110
  - lib/datoki.rb
111
- - specs/0010-Datoki.db.new.rb
112
111
  - specs/0010-Datoki.db.rb
113
112
  - specs/0010-Numeric.rb
114
113
  - specs/0010-Numeric_db.rb
@@ -118,8 +117,12 @@ files:
118
117
  - specs/0010-schema_conflict.rb
119
118
  - specs/0010-varchar.rb
120
119
  - specs/0010-varchar_db.rb
120
+ - specs/0011-string_ish.rb
121
+ - specs/0012-pseudo.rb
122
+ - specs/0020-clean!.rb
121
123
  - specs/0020-clean.rb
122
- - specs/0020-on.rb
124
+ - specs/0030-matching.rb
125
+ - specs/0031-set_to.rb
123
126
  - specs/lib/helpers.rb
124
127
  homepage: https://github.com/da99/datoki
125
128
  licenses:
@@ -1,31 +0,0 @@
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
-
data/specs/0020-on.rb DELETED
@@ -1,64 +0,0 @@
1
-
2
- describe :on do
3
-
4
- it "executes proc if condition is true" do
5
- c = Class.new {
6
- include Datoki
7
-
8
- on :happy? do
9
- @result = :happy
10
- end
11
-
12
- on :sad? do
13
- @result= :happy
14
- end
15
-
16
- attr_reader :result
17
-
18
- def happy?
19
- @raw[:state] == :happy
20
- end
21
-
22
- def sad?
23
- @raw[:state] == :sad
24
- end
25
- }
26
-
27
- c.create(:state => :happy).
28
- result.should == :happy
29
- end # === it executes proc if condition is true
30
-
31
- it "executes nested :on if condition matches" do
32
- c = Class.new {
33
- include Datoki
34
-
35
- RESULT = []
36
-
37
- attr_reader :result
38
-
39
- on :true? do
40
- on :filled? do
41
- @result ||= []
42
- @result << :found
43
- end
44
- on :false? do
45
- fail
46
- end
47
- end
48
-
49
- def false?
50
- false
51
- end
52
-
53
- def filled?
54
- true
55
- end
56
-
57
- def true?
58
- true
59
- end
60
- }
61
- c.create({}).result.should == [:found]
62
- end # === it executes nested :on if condition matches
63
-
64
- end # === describe :on