respect 0.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.
Files changed (97) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.md +289 -0
  3. data/RELATED_WORK.md +40 -0
  4. data/RELEASE_NOTES.md +23 -0
  5. data/Rakefile +31 -0
  6. data/STATUS_MATRIX.html +137 -0
  7. data/lib/respect.rb +231 -0
  8. data/lib/respect/any_schema.rb +22 -0
  9. data/lib/respect/array_def.rb +28 -0
  10. data/lib/respect/array_schema.rb +203 -0
  11. data/lib/respect/boolean_schema.rb +32 -0
  12. data/lib/respect/composite_schema.rb +86 -0
  13. data/lib/respect/core_statements.rb +206 -0
  14. data/lib/respect/datetime_schema.rb +27 -0
  15. data/lib/respect/def_without_name.rb +6 -0
  16. data/lib/respect/divisible_by_validator.rb +20 -0
  17. data/lib/respect/doc_helper.rb +24 -0
  18. data/lib/respect/doc_parser.rb +37 -0
  19. data/lib/respect/dsl_dumper.rb +181 -0
  20. data/lib/respect/equal_to_validator.rb +20 -0
  21. data/lib/respect/fake_name_proxy.rb +116 -0
  22. data/lib/respect/float_schema.rb +27 -0
  23. data/lib/respect/format_validator.rb +136 -0
  24. data/lib/respect/global_def.rb +79 -0
  25. data/lib/respect/greater_than_or_equal_to_validator.rb +19 -0
  26. data/lib/respect/greater_than_validator.rb +19 -0
  27. data/lib/respect/has_constraints.rb +34 -0
  28. data/lib/respect/hash_def.rb +40 -0
  29. data/lib/respect/hash_schema.rb +218 -0
  30. data/lib/respect/in_validator.rb +19 -0
  31. data/lib/respect/integer_schema.rb +27 -0
  32. data/lib/respect/ip_addr_schema.rb +23 -0
  33. data/lib/respect/ipv4_addr_schema.rb +27 -0
  34. data/lib/respect/ipv6_addr_schema.rb +27 -0
  35. data/lib/respect/items_def.rb +21 -0
  36. data/lib/respect/json_schema_html_formatter.rb +143 -0
  37. data/lib/respect/less_than_or_equal_to_validator.rb +19 -0
  38. data/lib/respect/less_than_validator.rb +19 -0
  39. data/lib/respect/match_validator.rb +19 -0
  40. data/lib/respect/max_length_validator.rb +20 -0
  41. data/lib/respect/min_length_validator.rb +20 -0
  42. data/lib/respect/multiple_of_validator.rb +10 -0
  43. data/lib/respect/null_schema.rb +26 -0
  44. data/lib/respect/numeric_schema.rb +33 -0
  45. data/lib/respect/org3_dumper.rb +213 -0
  46. data/lib/respect/regexp_schema.rb +19 -0
  47. data/lib/respect/schema.rb +285 -0
  48. data/lib/respect/schema_def.rb +16 -0
  49. data/lib/respect/string_schema.rb +21 -0
  50. data/lib/respect/unit_test_helper.rb +37 -0
  51. data/lib/respect/uri_schema.rb +23 -0
  52. data/lib/respect/utc_time_schema.rb +17 -0
  53. data/lib/respect/validator.rb +51 -0
  54. data/lib/respect/version.rb +3 -0
  55. data/test/any_schema_test.rb +79 -0
  56. data/test/array_def_test.rb +113 -0
  57. data/test/array_schema_test.rb +487 -0
  58. data/test/boolean_schema_test.rb +89 -0
  59. data/test/composite_schema_test.rb +30 -0
  60. data/test/datetime_schema_test.rb +83 -0
  61. data/test/doc_helper_test.rb +34 -0
  62. data/test/doc_parser_test.rb +109 -0
  63. data/test/dsl_dumper_test.rb +395 -0
  64. data/test/fake_name_proxy_test.rb +138 -0
  65. data/test/float_schema_test.rb +146 -0
  66. data/test/format_validator_test.rb +224 -0
  67. data/test/hash_def_test.rb +126 -0
  68. data/test/hash_schema_test.rb +613 -0
  69. data/test/integer_schema_test.rb +142 -0
  70. data/test/ip_addr_schema_test.rb +78 -0
  71. data/test/ipv4_addr_schema_test.rb +71 -0
  72. data/test/ipv6_addr_schema_test.rb +71 -0
  73. data/test/json_schema_html_formatter_test.rb +214 -0
  74. data/test/null_schema_test.rb +46 -0
  75. data/test/numeric_schema_test.rb +294 -0
  76. data/test/org3_dumper_test.rb +784 -0
  77. data/test/regexp_schema_test.rb +54 -0
  78. data/test/respect_test.rb +108 -0
  79. data/test/schema_def_test.rb +405 -0
  80. data/test/schema_test.rb +290 -0
  81. data/test/string_schema_test.rb +209 -0
  82. data/test/support/circle.rb +11 -0
  83. data/test/support/color.rb +24 -0
  84. data/test/support/point.rb +11 -0
  85. data/test/support/respect/circle_schema.rb +16 -0
  86. data/test/support/respect/color_def.rb +19 -0
  87. data/test/support/respect/color_schema.rb +33 -0
  88. data/test/support/respect/point_schema.rb +19 -0
  89. data/test/support/respect/rgba_schema.rb +20 -0
  90. data/test/support/respect/universal_validator.rb +25 -0
  91. data/test/support/respect/user_macros.rb +12 -0
  92. data/test/support/rgba.rb +11 -0
  93. data/test/test_helper.rb +90 -0
  94. data/test/uri_schema_test.rb +54 -0
  95. data/test/utc_time_schema_test.rb +63 -0
  96. data/test/validator_test.rb +22 -0
  97. metadata +288 -0
@@ -0,0 +1,11 @@
1
+ class Circle
2
+ def initialize(center, radius)
3
+ @center, @radius = center, radius
4
+ end
5
+
6
+ attr_reader :center, :radius
7
+
8
+ def ==(other)
9
+ @center == other.center && @radius == radius
10
+ end
11
+ end
@@ -0,0 +1,24 @@
1
+ class Color
2
+ class FormatError < StandardError
3
+ end
4
+
5
+ class << self
6
+ def from_string(str)
7
+ if str.to_s =~ /^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/
8
+ Color.new($1.to_i(16), $2.to_i(16), $3.to_i(16), $4.to_i(16))
9
+ else
10
+ raise FormatError, "'#{str}' does not match color regexp"
11
+ end
12
+ end
13
+ end
14
+
15
+ def initialize(red, green, blue, alpha)
16
+ @red, @green, @blue, @alpha = red, green, blue, alpha
17
+ end
18
+
19
+ attr_reader :red, :green, :blue, :alpha
20
+
21
+ def ==(other)
22
+ @red == other.red && @green == other.green && @blue == other.blue && @alpha == other.alpha
23
+ end
24
+ end
@@ -0,0 +1,11 @@
1
+ class Point
2
+ def initialize(x, y)
3
+ @x, @y = x, y
4
+ end
5
+
6
+ attr_reader :x, :y
7
+
8
+ def ==(other)
9
+ @x == other.x && @y == other.y
10
+ end
11
+ end
@@ -0,0 +1,16 @@
1
+ module Respect
2
+ # Test class proving that users can easily extend the schema hierarchy
3
+ # with a custom type based on another custom type.
4
+ class CircleSchema < CompositeSchema
5
+ def schema_definition
6
+ HashSchema.define do |s|
7
+ s.point "center"
8
+ s.float "radius", greater_than: 0.0
9
+ end
10
+ end
11
+
12
+ def sanitize(doc)
13
+ Circle.new(doc[:center], doc[:radius])
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,19 @@
1
+ module Respect
2
+ class ColorDef < GlobalDef
3
+ def initialize(options = {})
4
+ @color_schema = ColorSchema.new(options)
5
+ end
6
+
7
+ [ :red, :green, :blue, :alpha ].each do |name|
8
+ define_method(name) do |value|
9
+ @color_schema.send("#{name}=", value)
10
+ end
11
+ end
12
+
13
+ private
14
+
15
+ def evaluation_result
16
+ @color_schema
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,33 @@
1
+ module Respect
2
+ class ColorSchema < Schema
3
+
4
+ public_class_method :new
5
+
6
+ def initialize(options = {})
7
+ super
8
+ @red, @green, @blue, @alpha = 0, 0, 0, 0
9
+ end
10
+
11
+ attr_accessor :red, :green, :blue, :alpha
12
+
13
+ def validate(doc)
14
+ color = nil
15
+ begin
16
+ color = Color.from_string(doc.to_s)
17
+ rescue Color::FormatError => e
18
+ raise ValidationError, "invalid color: #{e.message}"
19
+ end
20
+ [ :red, :green, :blue, :alpha ].each do |part|
21
+ if expected = send(part)
22
+ value = color.send(part)
23
+ unless value == expected
24
+ raise ValidationError, "color part #{part} is #{value} but should be #{expected}"
25
+ end
26
+ end
27
+ end
28
+ self.sanitized_object = color
29
+ end
30
+
31
+ end # class ColorSchema
32
+
33
+ end # module Respect
@@ -0,0 +1,19 @@
1
+ module Respect
2
+ # Test class proving that users can easily extend the schema hierarchy
3
+ # with a custom type providing both a composition macro and a
4
+ # sanitizer block.
5
+ class PointSchema < CompositeSchema
6
+
7
+ def schema_definition
8
+ HashSchema.define do |s|
9
+ s.float "x"
10
+ s.float "y"
11
+ end
12
+ end
13
+
14
+ def sanitize(doc)
15
+ Point.new(doc["x"], doc["y"])
16
+ end
17
+
18
+ end
19
+ end
@@ -0,0 +1,20 @@
1
+ module Respect
2
+ # Test class proving that users can easily extend the schema hierarchy
3
+ # with a composite schema based on an array schema.
4
+ class RgbaSchema < CompositeSchema
5
+ def schema_definition
6
+ ArraySchema.define do |s|
7
+ s.items do |s|
8
+ s.color_channel # red
9
+ s.color_channel # green
10
+ s.color_channel # blue
11
+ s.color_channel # alpha
12
+ end
13
+ end
14
+ end
15
+
16
+ def sanitize(doc)
17
+ Rgba.new(doc[0], doc[1], doc[2], doc[3])
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,25 @@
1
+ module Respect
2
+ # Test class proving that users can easily extend the validator hierarchy
3
+ # with their own ones.
4
+ class UniversalValidator < Validator
5
+
6
+ def initialize(enabled)
7
+ @enabled = enabled
8
+ end
9
+
10
+ def validate(value)
11
+ if @enabled
12
+ unless value == 42
13
+ raise ValidationError, "#{value} is not a universal value"
14
+ end
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ def to_h_org3
21
+ { "minimum" => 42, "maximum" => 42 }
22
+ end
23
+
24
+ end # class UniversalValidator
25
+ end # module Respect
@@ -0,0 +1,12 @@
1
+ module Respect
2
+ # Test module proving that users can easily extend the definition DSL
3
+ # with their own macros bundled in their own modules organized as they
4
+ # want.
5
+ module UserMacros
6
+ def color_channel(name)
7
+ float name, in: 0.0..1.0
8
+ end
9
+ end
10
+
11
+ extend_dsl_with(UserMacros)
12
+ end
@@ -0,0 +1,11 @@
1
+ class Rgba
2
+ def initialize(red, green, blue, alpha)
3
+ @red, @green, @blue, @alpha = red, green, blue, alpha
4
+ end
5
+
6
+ attr_reader :red, :green, :blue, :alpha
7
+
8
+ def ==(other)
9
+ @red == other.red && @green == other.green && @blue == other.blue && @alpha == other.alpha
10
+ end
11
+ end
@@ -0,0 +1,90 @@
1
+ require 'test/unit'
2
+ require 'debugger'
3
+
4
+ require 'respect'
5
+
6
+ # Test that "hash" methods has been removed from GlabelDef as soon as we load "respect".
7
+ # We cannot do it in HashSchema since the "hash" method maybe call before hand.
8
+ if Respect::GlobalDef.new.respond_to? :hash
9
+ raise "'hash' method should have been removed before Respect::HashSchema class is loaded."
10
+ end
11
+
12
+ require 'respect/unit_test_helper'
13
+
14
+ class Test::Unit::TestCase
15
+ # Similar to assert_raises but return the exception object caught.
16
+ def assert_exception(exception_class, message = nil, &block)
17
+ begin
18
+ block.call
19
+ assert false, message
20
+ rescue exception_class => e
21
+ e
22
+ end
23
+ end
24
+ end
25
+
26
+ # Load support files
27
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
28
+
29
+ # It *MUST* be defined before EndUserDSLStatement.
30
+ module Respect
31
+ # A class to test DSL extension.
32
+ class CoreDef < GlobalDef
33
+ include_core_statements
34
+ include DefWithoutName
35
+ end
36
+ end
37
+
38
+ # A module to test statement extension helper.
39
+ module EndUserDSLStatement
40
+
41
+ def id(name = "id")
42
+ integer name, greater_than: 0
43
+ end
44
+
45
+ def call_to_kernel
46
+ # The point is to call an instance method of the Kernel module.
47
+ integer "kernel", equal_to: Integer(0)
48
+ end
49
+
50
+ def call_to_object
51
+ # The point is to call an instance method of the Object class.
52
+ string "object", equal_to: self.class.to_s
53
+ end
54
+
55
+ end
56
+
57
+ Respect.extend_dsl_with(EndUserDSLStatement)
58
+
59
+ FORMAT_HELPER_STATEMENTS_LIST = [
60
+ :phone_number,
61
+ :hostname,
62
+ :email,
63
+ ]
64
+
65
+ PRIMITIVE_STATEMENTS_LIST = [
66
+ :integer,
67
+ :string,
68
+ :any,
69
+ :boolean,
70
+ :null,
71
+ :float,
72
+ :numeric,
73
+ :uri,
74
+ :regexp,
75
+ :datetime,
76
+ :ipv4_addr,
77
+ :ipv6_addr,
78
+ :ip_addr,
79
+ ]
80
+
81
+ TERMINAL_STATEMENTS_LIST = FORMAT_HELPER_STATEMENTS_LIST + PRIMITIVE_STATEMENTS_LIST
82
+
83
+ COMPOSITE_STATEMENTS_LIST = [
84
+ :hash,
85
+ :array,
86
+ ]
87
+
88
+ BASIC_STATEMENTS_LIST = TERMINAL_STATEMENTS_LIST + COMPOSITE_STATEMENTS_LIST
89
+
90
+ require "mocha/setup"
@@ -0,0 +1,54 @@
1
+ require "test_helper"
2
+
3
+ class URISchemaTest < Test::Unit::TestCase
4
+
5
+ def test_uri_schema_creates_uri_object
6
+ s = Respect::URISchema.new
7
+ assert_nil s.sanitized_object
8
+ assert_schema_validate s, "http://foo.com"
9
+ assert s.sanitized_object.is_a?(URI::Generic)
10
+ assert_equal "http://foo.com", s.sanitized_object.to_s
11
+ end
12
+
13
+ def test_uri_schema_relies_on_format_validator
14
+ doc = "http://foo.com"
15
+ Respect::FormatValidator.any_instance.stubs(:validate_uri).with(doc).at_least_once
16
+ Respect::URISchema.new.validate(doc)
17
+ end
18
+
19
+ def test_failed_validation_reset_sanitized_object
20
+ s = Respect::URISchema.new
21
+ assert_schema_validate(s, "http://foo.com")
22
+ assert_not_nil(s.sanitized_object)
23
+ assert_schema_invalidate(s, "<")
24
+ assert_nil(s.sanitized_object)
25
+ end
26
+
27
+ def test_allow_nil
28
+ s = Respect::URISchema.new(allow_nil: true)
29
+ assert s.allow_nil?
30
+ assert_schema_validate s, nil
31
+ assert_equal(nil, s.sanitized_object)
32
+ assert_schema_validate s, "http://foo.com"
33
+ assert_not_nil(s.sanitized_object)
34
+ assert_schema_invalidate(s, "<")
35
+ assert_nil(s.sanitized_object)
36
+ end
37
+
38
+ def test_disallow_nil
39
+ s = Respect::URISchema.new
40
+ assert !s.allow_nil?
41
+ exception = assert_exception(Respect::ValidationError) { s.validate(nil) }
42
+ assert_match exception.message, /\bURISchema\b/
43
+ assert_equal(nil, s.sanitized_object)
44
+ assert_schema_validate s, "http://foo.com"
45
+ assert_not_nil(s.sanitized_object)
46
+ assert_schema_invalidate(s, "<")
47
+ assert_nil(s.sanitized_object)
48
+ end
49
+
50
+ def test_validate_uri_object
51
+ s = Respect::URISchema.new
52
+ assert_schema_validate(s, URI.parse("http://foo.com"))
53
+ end
54
+ end
@@ -0,0 +1,63 @@
1
+ require "test_helper"
2
+
3
+ class UTCTimeSchemaTest < Test::Unit::TestCase
4
+
5
+ def test_utc_time_schema_creates_time_object
6
+ s = Respect::UTCTimeSchema.new
7
+ assert_nil s.sanitized_object
8
+ t = Time.now.to_i
9
+ assert_schema_validate s, t.to_s
10
+ assert_equal Time, s.sanitized_object.class
11
+ assert_equal(t, s.sanitized_object.to_i)
12
+ end
13
+
14
+ def test_utc_time_schema_accept_float
15
+ s = Respect::UTCTimeSchema.new
16
+ assert_nil s.sanitized_object
17
+ t = Time.now.to_f
18
+ assert_schema_validate s, t.to_s
19
+ assert_equal Time, s.sanitized_object.class
20
+ assert_equal(t, s.sanitized_object.to_f)
21
+ end
22
+
23
+ def test_utc_time_schema_do_not_accept_negative
24
+ s = Respect::UTCTimeSchema.new
25
+ begin
26
+ s.validate(-1)
27
+ assert false
28
+ rescue Respect::ValidationError => e
29
+ assert_match(/-1/, e.message)
30
+ end
31
+ end
32
+
33
+ def test_failed_validation_reset_sanitized_object
34
+ s = Respect::UTCTimeSchema.new
35
+ assert_schema_validate(s, 42)
36
+ assert_not_nil(s.sanitized_object)
37
+ assert_schema_invalidate(s, "*")
38
+ assert_nil(s.sanitized_object)
39
+ end
40
+
41
+ def test_allow_nil
42
+ s = Respect::UTCTimeSchema.new(allow_nil: true)
43
+ assert s.allow_nil?
44
+ assert_schema_validate s, nil
45
+ assert_equal(nil, s.sanitized_object)
46
+ assert_schema_validate s, 42
47
+ assert_not_nil(s.sanitized_object)
48
+ assert_schema_invalidate(s, "wrong")
49
+ assert_nil(s.sanitized_object)
50
+ end
51
+
52
+ def test_disallow_nil
53
+ s = Respect::UTCTimeSchema.new
54
+ assert !s.allow_nil?
55
+ exception = assert_exception(Respect::ValidationError) { s.validate(nil) }
56
+ assert_match exception.message, /\bUTCTimeSchema\b/
57
+ assert_equal(nil, s.sanitized_object)
58
+ assert_schema_validate s, 42
59
+ assert_not_nil(s.sanitized_object)
60
+ assert_schema_invalidate(s, "wrong")
61
+ assert_nil(s.sanitized_object)
62
+ end
63
+ end
@@ -0,0 +1,22 @@
1
+ require "test_helper"
2
+
3
+ class ValidatorTest < Test::Unit::TestCase
4
+ def test_constraint_name
5
+ assert_equal "equal_to", Respect::EqualToValidator.constraint_name
6
+ end
7
+
8
+ def test_end_user_validator
9
+ s = Respect::IntegerSchema.new(universal: true)
10
+ assert_schema_invalidate s, 52
11
+ assert_schema_validate s, 42
12
+ end
13
+
14
+ def test_end_user_validate_get_called
15
+ value = 1664
16
+ v = Respect::UniversalValidator.new(true)
17
+ Respect::UniversalValidator.expects(:new).with(true).returns(v)
18
+ v.stubs(:validate).with(value).at_least_once
19
+ Respect::IntegerSchema.new(universal: true).validate(value)
20
+ end
21
+
22
+ end