deeply_valid 0.1.5 → 0.1.7

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.5
1
+ 0.1.7
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{deeply_valid}
8
- s.version = "0.1.5"
8
+ s.version = "0.1.7"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Starr Horne"]
12
- s.date = %q{2010-06-11}
12
+ s.date = %q{2010-06-15}
13
13
  s.description = %q{This gem lets you declaratively specify validations for complex data structures, like those returned from an api.}
14
14
  s.email = %q{starr@chromahq.com}
15
15
  s.extra_rdoc_files = [
@@ -51,8 +51,18 @@ module DeeplyValid
51
51
  (@definitions ||= {})[name.to_sym]
52
52
  end
53
53
 
54
- def structure(name)
55
- Validation.new { |d| self[name.to_sym].valid?(d) }
54
+ def structure(name = nil, &block)
55
+ if name
56
+ Validation.new { |d| self[name.to_sym].valid?(d) }
57
+ elsif block_given?
58
+ block
59
+ else
60
+ raise "structure requires name or block"
61
+ end
62
+ end
63
+
64
+ def value(&block)
65
+ block
56
66
  end
57
67
 
58
68
  end
@@ -6,6 +6,8 @@ module DeeplyValid
6
6
  #
7
7
  class Validation
8
8
 
9
+ attr_accessor :options
10
+
9
11
  #
10
12
  # The initializer defines the conditions that will
11
13
  # satasfy this validation.
@@ -15,12 +17,13 @@ module DeeplyValid
15
17
  # @param [Object] rule When `rule` is any other non-nil object, use `==` to validate
16
18
  # @param [Proc] &block An optional block, which will take one param and return true or false
17
19
  #
18
- def initialize(rule = nil, &block)
20
+ def initialize(rule = nil, options = {}, &block)
19
21
 
20
22
  if rule.nil? && !block_given?
21
23
  raise "No validation rule specified"
22
24
  end
23
25
 
26
+ @options = options
24
27
  @rule = rule
25
28
  @block = block
26
29
  end
@@ -58,8 +61,10 @@ module DeeplyValid
58
61
  def valid_structure?(data, fragment_rule = nil)
59
62
  (fragment_rule || @rule).all? do |k, v|
60
63
 
64
+ v = v.call(@data) if v.is_a?(Proc)
65
+
61
66
  if v.is_a?(Validation)
62
- v.valid?(data[k])
67
+ (v.options[:optional] && !data.has_key?(k)) || v.valid?(data[k])
63
68
 
64
69
  elsif v.is_a?(Regexp)
65
70
  !!(data[k] =~ v)
@@ -67,6 +72,9 @@ module DeeplyValid
67
72
  elsif v.is_a?(Hash)
68
73
  valid_structure?(data[k], v)
69
74
 
75
+ elsif v.nil?
76
+ !data.has_key?(k)
77
+
70
78
  else
71
79
  data[k] == v
72
80
  end
@@ -81,10 +89,17 @@ module DeeplyValid
81
89
  #
82
90
  def valid?(data)
83
91
 
84
- if @rule.kind_of?(Regexp)
92
+ @data = data
93
+
94
+ rule = @rule.is_a?(Proc) ? @rule.call(@data) : @rule
95
+
96
+ result = if rule.kind_of?(Regexp)
85
97
  valid_pattern?(data)
86
98
 
87
- elsif @rule.kind_of?(Hash)
99
+ elsif rule.kind_of?(Validation)
100
+ rule.valid?(data)
101
+
102
+ elsif rule.kind_of?(Hash)
88
103
  valid_structure?(data)
89
104
 
90
105
  elsif @block
@@ -94,6 +109,10 @@ module DeeplyValid
94
109
  data == @rule
95
110
  end
96
111
 
112
+ @data = nil
113
+
114
+ result
115
+
97
116
  end
98
117
 
99
118
  end
@@ -1,4 +1,5 @@
1
1
  require 'json'
2
+ require 'date'
2
3
 
3
4
  module DeeplyValid
4
5
 
@@ -66,23 +67,23 @@ module DeeplyValid
66
67
  end
67
68
 
68
69
  #
69
- # Validates datetime with optional limit
70
+ # Validates time with optional limit
70
71
  #
71
72
  # @param [Integer, Range] limit
72
73
  # @return [Validation] The validation
73
74
  #
74
- def datetime(limit = nil)
75
- Validation.new { |d| d.is_a?(DateTime) && in_range?(d, limit) }
75
+ def time(limit = nil)
76
+ Validation.new { |d| d.is_a?(Time) && in_range?(d, limit) }
76
77
  end
77
78
 
78
79
  #
79
- # Validates time with optional limit
80
+ # Validates nil. Normally a nil means "don't validate". Use this
81
+ # if you're expecting an actual nil
80
82
  #
81
- # @param [Integer, Range] limit
82
83
  # @return [Validation] The validation
83
84
  #
84
- def time(limit = nil)
85
- Validation.new { |d| d.is_a?(Time) && in_range?(d, limit) }
85
+ def nil_value
86
+ Validation.new { |d| d.nil? }
86
87
  end
87
88
 
88
89
  #
@@ -228,8 +229,29 @@ module DeeplyValid
228
229
  range = [range] unless range.respond_to?(:include?)
229
230
  range.include?(val)
230
231
  end
232
+ end
233
+
234
+ #
235
+ # Set a validation as optional. This only has any effect
236
+ # if the validation is part of a structure.
237
+ #
238
+ # The following validation:
239
+ #
240
+ # {:key => optional(integer)}
241
+ #
242
+ # Will validate both:
243
+ #
244
+ # {:key => 1}
245
+ # {}
246
+ #
247
+ # @param [Validation] validation
248
+ # @return [Validation]
249
+ #
250
+ def optional(validation)
251
+ validation.options[:optional] = true
252
+ validation
253
+ end
231
254
 
232
- end
233
255
  end
234
256
  end
235
257
  end
@@ -12,6 +12,7 @@ class BaseTest < Test::Unit::TestCase
12
12
  define :manual, DeeplyValid::Validation.new { |d| d > 10 }
13
13
  define :literal, "x"
14
14
  define :hash, { :key => "val" }
15
+ define :nested_self_reference, structure { |data| structure(:regexp) }
15
16
  end
16
17
  end
17
18
 
@@ -35,6 +36,12 @@ class BaseTest < Test::Unit::TestCase
35
36
  assert Sample[:self_reference].valid?("abc")
36
37
  assert !Sample[:self_reference].valid?("123")
37
38
  end
39
+
40
+ should "Handle nested self-reference" do
41
+ assert Sample[:nested_self_reference]
42
+ assert Sample[:nested_self_reference].valid?("abc")
43
+ assert !Sample[:nested_self_reference].valid?("123")
44
+ end
38
45
 
39
46
  end
40
47
 
@@ -1,6 +1,7 @@
1
1
  require 'rubygems'
2
2
  require 'test/unit'
3
3
  require 'shoulda'
4
+ begin; require 'turn'; rescue LoadError; end
4
5
 
5
6
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
7
  $LOAD_PATH.unshift(File.dirname(__FILE__))
@@ -16,7 +16,10 @@ class SystemTest < Test::Unit::TestCase
16
16
  :name => any('sales', 'accounting', 'engineering'),
17
17
  :building => integer
18
18
  },
19
- :performace_reviews => array( structure(:review) )
19
+ :performace_reviews => array( structure(:review) ),
20
+ :pension => value { |data| integer if data[:age] > 50 },
21
+ :hobby => optional(string)
22
+
20
23
  }
21
24
 
22
25
  # This structure is referenced in the "person" structure
@@ -26,7 +29,7 @@ class SystemTest < Test::Unit::TestCase
26
29
  }
27
30
  end
28
31
 
29
- @valid_person = {
32
+ @valid_person1 = {
30
33
  :id => "x"*32,
31
34
  :age => 22,
32
35
  :name => "Bob Jones",
@@ -40,10 +43,44 @@ class SystemTest < Test::Unit::TestCase
40
43
  ]
41
44
  }
42
45
 
43
- @invalid_person = {
46
+ # this person is over 50, so there's a value for pension
47
+ @valid_person2 = {
48
+ :id => "x"*32,
49
+ :age => 61,
50
+ :name => "Bob Jones",
51
+ :pension => 666,
52
+ :hobby => "boating",
53
+ :department => {
54
+ :name => "sales",
55
+ :building => 33
56
+ },
57
+ :performace_reviews => [
58
+ { :author => "joe", :body => "a review" },
59
+ { :author => "bill", :body => "another review" }
60
+ ]
61
+ }
62
+
63
+ # this person is under 50, and has a value for pension,
64
+ # so the record is invalid.
65
+ @invalid_person1 = {
44
66
  :id => "x"*32,
45
67
  :age => 22,
46
68
  :name => "Bob Jones",
69
+ :pension => 666,
70
+ :department => {
71
+ :name => "sales",
72
+ :building => 33
73
+ },
74
+ :performace_reviews => [
75
+ { :author => "joe", :body => "a review" },
76
+ { :author => "bill", :body => "another review" }
77
+ ]
78
+ }
79
+
80
+ @invalid_person2 = {
81
+ :id => "x"*32,
82
+ :age => 32,
83
+ :name => "Bob Jones",
47
84
  :department => {
48
85
  :name => "sales",
49
86
  :building => 33
@@ -57,8 +94,10 @@ class SystemTest < Test::Unit::TestCase
57
94
  end
58
95
 
59
96
  should "validate correctly" do
60
- assert MyValidations[:person].valid?(@valid_person)
61
- assert !MyValidations[:person].valid?(@invalid_person)
97
+ assert MyValidations[:person].valid?(@valid_person1)
98
+ assert MyValidations[:person].valid?(@valid_person2)
99
+ assert !MyValidations[:person].valid?(@invalid_person1)
100
+ assert !MyValidations[:person].valid?(@invalid_person2)
62
101
  end
63
102
 
64
103
  end
@@ -92,17 +92,8 @@ class ValidationHelpersTest < Test::Unit::TestCase
92
92
  context "date helper" do
93
93
 
94
94
  should "validate correctly" do
95
- assert Sample.date.valid?(Date.today)
96
- assert !Sample.date(:before => Date.today).valid?(Date.today)
97
- end
98
-
99
- end
100
-
101
- context "datetime helper" do
102
-
103
- should "validate correctly" do
104
- assert Sample.datetime.valid?(DateTime.now)
105
- assert !Sample.datetime(:before => DateTime.now).valid?(DateTime.now)
95
+ assert Sample.date.valid?(Date.civil(2010, 3, 10))
96
+ assert !Sample.date(:before => Date.civil(2010, 3, 10)).valid?(Date.civil(2010, 4, 10))
106
97
  end
107
98
 
108
99
  end
@@ -101,4 +101,42 @@ class ValidationTest < Test::Unit::TestCase
101
101
 
102
102
  end
103
103
 
104
+ context "A structure validation for {:key => nil}" do
105
+ setup do
106
+ rule = { :key => nil, :bogus => 1 }
107
+ @validation = Validation.new(rule)
108
+ end
109
+
110
+ should_validate_all([
111
+ {:bogus => 1}
112
+ ])
113
+
114
+ should_not_validate_any([
115
+ {:key => false, :bogus => 1},
116
+ {:key => true, :bogus => 1},
117
+ {:key => nil, :bogus => 1},
118
+ {:key => "sesame", :bogus => 1},
119
+ ])
120
+
121
+ end
122
+
123
+ context "A structure with an optional validation" do
124
+ setup do
125
+ rule = { :key => Validation.new(1, :optional => true) }
126
+ @validation = Validation.new(rule)
127
+ end
128
+
129
+ should_validate_all([
130
+ {:key => 1},
131
+ {}
132
+ ])
133
+
134
+ should_not_validate_any([
135
+ {:key => 2},
136
+ {:key => nil},
137
+ {:key => false}
138
+ ])
139
+
140
+ end
141
+
104
142
  end
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 1
8
- - 5
9
- version: 0.1.5
8
+ - 7
9
+ version: 0.1.7
10
10
  platform: ruby
11
11
  authors:
12
12
  - Starr Horne
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-06-11 00:00:00 -05:00
17
+ date: 2010-06-15 00:00:00 -05:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency