datoki 2.0.1 → 3.1.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 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