object-inspector 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. data/.gitignore +18 -0
  2. data/Gemfile +4 -0
  3. data/Gemfile.lock +45 -0
  4. data/README.md +335 -0
  5. data/Rakefile +13 -0
  6. data/examples/example.rb +131 -0
  7. data/features/error_display.feature +80 -0
  8. data/features/support/env.rb +1 -0
  9. data/features/syntax.feature +223 -0
  10. data/inspector.gemspec +24 -0
  11. data/lib/inspector.rb +27 -0
  12. data/lib/inspector/attribute_metadata.rb +19 -0
  13. data/lib/inspector/constraint.rb +18 -0
  14. data/lib/inspector/constraint/validators.rb +8 -0
  15. data/lib/inspector/constraint/validators/simple.rb +13 -0
  16. data/lib/inspector/constraint/validators/validity.rb +24 -0
  17. data/lib/inspector/constraint/violation.rb +34 -0
  18. data/lib/inspector/constraint/violation/list.rb +93 -0
  19. data/lib/inspector/constraints.rb +61 -0
  20. data/lib/inspector/constraints/email.rb +57 -0
  21. data/lib/inspector/constraints/empty.rb +21 -0
  22. data/lib/inspector/constraints/eq.rb +23 -0
  23. data/lib/inspector/constraints/false.rb +19 -0
  24. data/lib/inspector/constraints/have.rb +85 -0
  25. data/lib/inspector/constraints/predicate.rb +38 -0
  26. data/lib/inspector/constraints/true.rb +19 -0
  27. data/lib/inspector/constraints/valid.rb +31 -0
  28. data/lib/inspector/metadata.rb +75 -0
  29. data/lib/inspector/metadata/map.rb +24 -0
  30. data/lib/inspector/metadata/walker.rb +46 -0
  31. data/lib/inspector/property_metadata.rb +19 -0
  32. data/lib/inspector/type_metadata.rb +5 -0
  33. data/lib/inspector/validator.rb +26 -0
  34. data/lib/inspector/version.rb +3 -0
  35. data/lib/object_inspector.rb +1 -0
  36. data/spec/inspector/attribute_metadata_spec.rb +8 -0
  37. data/spec/inspector/constraint/violation/list_spec.rb +45 -0
  38. data/spec/inspector/constraints/false_spec.rb +18 -0
  39. data/spec/inspector/constraints_spec.rb +15 -0
  40. data/spec/inspector/metadata/map_spec.rb +38 -0
  41. data/spec/inspector/metadata/walker_spec.rb +7 -0
  42. data/spec/inspector/property_metadata_spec.rb +8 -0
  43. data/spec/inspector/type_metadata_spec.rb +7 -0
  44. data/spec/inspector/validator_spec.rb +65 -0
  45. data/spec/inspector_spec.rb +22 -0
  46. data/spec/shared_examples/metadata.rb +33 -0
  47. data/spec/spec_helper.rb +3 -0
  48. metadata +175 -0
@@ -0,0 +1,31 @@
1
+ module Inspector
2
+ module Constraints
3
+ class Valid
4
+ include Constraint
5
+
6
+ def initialize(*args)
7
+ if args.length == 1
8
+ @use_custom_type = true
9
+ @type = args.first
10
+ end
11
+ end
12
+
13
+ def validate_as(value)
14
+ return @type if @use_custom_type
15
+ value.class
16
+ end
17
+
18
+ def validator
19
+ :validity
20
+ end
21
+
22
+ def to_s
23
+ "validate"
24
+ end
25
+
26
+ def inspect
27
+ "#<valid%{type}>" % { :type => " #{@type}".rstrip }
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,75 @@
1
+ module Inspector
2
+ module Metadata
3
+ autoload :Map, 'inspector/metadata/map'
4
+ autoload :Walker, 'inspector/metadata/walker'
5
+
6
+ include Constraints
7
+
8
+ attr_reader :type, \
9
+ :constraints, \
10
+ :children_metadata
11
+
12
+ def initialize(type)
13
+ @type = type
14
+ @constraints = []
15
+ @property_metadatas = {}
16
+ @attribute_metadatas = {}
17
+ end
18
+
19
+ def attribute(name, &block)
20
+ @attribute_metadatas[name] ||= AttributeMetadata.new(@type, name)
21
+ if block_given?
22
+ block.arity == 1 ? yield(@attribute_metadatas[name]) : @attribute_metadatas[name].instance_eval(&block)
23
+ end
24
+ @attribute_metadatas[name]
25
+ end
26
+
27
+ def property(name, &block)
28
+ @property_metadatas[name] ||= PropertyMetadata.new(@type, name)
29
+ if block_given?
30
+ block.arity == 1 ? yield(@property_metadatas[name]) : @property_metadatas[name].instance_eval(&block)
31
+ end
32
+ @property_metadatas[name]
33
+ end
34
+
35
+ def each_item(&block)
36
+ @children_metadata ||= TypeMetadata.new(@type)
37
+ if block_given?
38
+ block.arity == 1 ? yield(@children_metadata) : @children_metadata.instance_eval(&block)
39
+ end
40
+ @children_metadata
41
+ end
42
+
43
+ def should(constraint = nil)
44
+ return PositiveComparator.new(self) if constraint.nil?
45
+
46
+ @constraints << constraint
47
+
48
+ self
49
+ end
50
+
51
+ def should_not(constraint = nil)
52
+ return NegativeComparator.new(self) if constraint.nil?
53
+
54
+ constraint.negate!
55
+ @constraints << constraint
56
+
57
+ self
58
+ end
59
+
60
+ def attribute_metadatas
61
+ @attribute_metadatas.values
62
+ end
63
+
64
+ def property_metadatas
65
+ @property_metadatas.values
66
+ end
67
+
68
+ def children(object, &block)
69
+ object.each_with_index(&block)
70
+ rescue NoMethodError
71
+ raise "metadata for #{@type.inspect} contains children metadata, however " +
72
+ "#{object.inspect}.each_with_index is not defined"
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,24 @@
1
+ module Inspector
2
+ module Metadata
3
+ class Map
4
+ def initialize
5
+ @map = {}
6
+ end
7
+
8
+ def []=(type, metadata)
9
+ unless metadata.kind_of?(Metadata)
10
+ raise "#{metadata.inspect} is not a Inspector::Metadata"
11
+ end
12
+
13
+ @map[type] = metadata
14
+ end
15
+
16
+ def [](type)
17
+ @map.fetch(type)
18
+ rescue KeyError
19
+ raise "validation information for #{type.inspect} doesn't exist, use " +
20
+ "Inspector.valid(#{type.inspect}) to define it"
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,46 @@
1
+ module Inspector
2
+ module Metadata
3
+ class Walker
4
+ def initialize(violation_list_class, validator_map)
5
+ @violation_list_class = violation_list_class
6
+ @validator_map = validator_map
7
+ end
8
+
9
+ def walk_object(metadata, object)
10
+ violations = @violation_list_class.new
11
+
12
+ metadata.constraints.each do |constraint|
13
+ not_found = "validator #{constraint.validator} cannot be found"
14
+ validator = @validator_map.fetch(constraint.validator) { raise not_found }
15
+
16
+ validator.validate(object, constraint, violations)
17
+ end
18
+
19
+ # walk object attributes, properties and children if object constraints passed
20
+ if violations.empty?
21
+ metadata.attribute_metadatas.each do |metadata|
22
+ path = metadata.attribute_name
23
+ value = metadata.attribute_value(object)
24
+
25
+ violations[path] = walk_object(metadata, value)
26
+ end
27
+
28
+ metadata.property_metadatas.each do |metadata|
29
+ path = "[#{metadata.property_name}]"
30
+ value = metadata.property_value(object)
31
+
32
+ violations[path] = walk_object(metadata, value)
33
+ end
34
+
35
+ metadata.children_metadata.children(object) do |child, index|
36
+ path = "[#{index}]"
37
+
38
+ violations[path] = walk_object(metadata.children_metadata, child)
39
+ end unless metadata.children_metadata.nil?
40
+ end
41
+
42
+ violations
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,19 @@
1
+ module Inspector
2
+ class PropertyMetadata
3
+ include Metadata
4
+
5
+ attr_reader :property_name
6
+
7
+ def initialize(type, property_name)
8
+ @property_name = property_name
9
+ super(type)
10
+ end
11
+
12
+ def property_value(object)
13
+ object.__send__(:[], @property_name)
14
+ rescue NoMethodError
15
+ raise "metadata for #{@type.inspect} contains property metadata, however " +
16
+ "#{object.inspect}[#{@property_name.inspect}] is not defined"
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,5 @@
1
+ module Inspector
2
+ class TypeMetadata
3
+ include Metadata
4
+ end
5
+ end
@@ -0,0 +1,26 @@
1
+ module Inspector
2
+ class Validator
3
+ def initialize(metadata_map, walker, type_metadata_class)
4
+ @metadata_map = metadata_map
5
+ @walker = walker
6
+ @type_metadata_class = type_metadata_class
7
+ end
8
+
9
+ def valid(type, &block)
10
+ metadata = @type_metadata_class.new(type)
11
+ if block_given?
12
+ block.arity == 1 ? yield(metadata) : metadata.instance_eval(&block)
13
+ end
14
+ @metadata_map[type] = metadata
15
+
16
+ nil
17
+ end
18
+
19
+ def validate(object, opts = {})
20
+ type = opts.fetch(:as) { object.class }
21
+ metadata = @metadata_map[type]
22
+
23
+ @walker.walk_object(metadata, object)
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,3 @@
1
+ module Inspector
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1 @@
1
+ require_relative 'inspector'
@@ -0,0 +1,8 @@
1
+ require 'spec_helper'
2
+
3
+ describe(Inspector::AttributeMetadata) do
4
+ let(:attribute) { 'name' }
5
+ let(:metadata) { Inspector::AttributeMetadata.new(type, attribute) }
6
+
7
+ it_behaves_like Inspector::Metadata
8
+ end
@@ -0,0 +1,45 @@
1
+ require 'spec_helper'
2
+
3
+ describe(Inspector::Constraint::Violation::List) do
4
+ let(:list) { Inspector::Constraint::Violation::List.new }
5
+
6
+ describe "#<<" do
7
+ let(:violation) { double() }
8
+
9
+ it "adds constraints" do
10
+ violation.stub(:kind_of?) { true }
11
+
12
+ expect { list << violation }.to change(list, :length).from(0).to(1)
13
+ end
14
+
15
+ it "raises when adding not a constraint violation" do
16
+ expect {
17
+ list << violation
18
+ }.to raise_error("#{violation.inspect} is not a Inspector::Constraint::Violation")
19
+ end
20
+ end
21
+
22
+ describe "#[]=" do
23
+ let(:property_path) { 'username' }
24
+ let(:violation_list) { nil }
25
+
26
+ it "raises when adding not a constraint violation list" do
27
+ expect {
28
+ list[property_path] = violation_list
29
+ }.to raise_error("#{violation_list.inspect} is not a Inspector::Constraint::Violation::List")
30
+ end
31
+ end
32
+
33
+ describe "#[]" do
34
+ before(:each) do
35
+ list["attribute"] = Inspector::Constraint::Violation::List.new([
36
+ Inspector::Constraint::Violation.new(nil),
37
+ Inspector::Constraint::Violation.new(nil)
38
+ ])
39
+ end
40
+
41
+ it "returns list of matching violations" do
42
+ list["attribute"].length.should == 2
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,18 @@
1
+ require 'spec_helper'
2
+
3
+ describe Inspector::Constraints::False do
4
+ let(:constraint) { Inspector::Constraints::False.new }
5
+
6
+ [
7
+ [true, false],
8
+ [false, true],
9
+ [nil, true],
10
+ ["string", false]
11
+ ].each do |value, expected|
12
+ describe "#valid?(#{value.inspect})" do
13
+ it "is #{expected}" do
14
+ constraint.valid?(value).should be(expected)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ describe(Inspector::Constraints) do
4
+ let(:constraint_helper) { Object.new }
5
+
6
+ before(:each) do
7
+ constraint_helper.extend Inspector::Constraints
8
+ end
9
+
10
+ describe ".be_false" do
11
+ it "creates False constraint" do
12
+ expect(constraint_helper.be_false).to be_kind_of(Inspector::Constraints::False)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+
3
+ describe(Inspector::Metadata::Map) do
4
+ let(:metadata_map) { Inspector::Metadata::Map.new }
5
+
6
+ describe "#[]=" do
7
+ let(:metadata) { double() }
8
+
9
+ it "raises if not a Metadata given" do
10
+ expect {
11
+ metadata_map[NilClass] = metadata
12
+ }.to raise_error("#{metadata.inspect} is not a Inspector::Metadata")
13
+ end
14
+
15
+ it "returns Metadata" do
16
+ metadata.stub(:kind_of?) { true }
17
+
18
+ expect(metadata_map[NilClass] = metadata).to be(metadata)
19
+ end
20
+ end
21
+
22
+ describe "#[]" do
23
+ it "raises if no Metadata registered" do
24
+ expect {
25
+ metadata_map[NilClass]
26
+ }.to raise_error("validation information for NilClass doesn't exist, use " +
27
+ "Inspector.valid(NilClass) to define it")
28
+ end
29
+
30
+ it "fetches pre-registered metadata" do
31
+ metadata = double()
32
+ metadata.stub(:kind_of?) { true }
33
+ metadata_map[NilClass] = metadata
34
+
35
+ expect(metadata_map[NilClass]).to be(metadata)
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+
3
+ describe(Inspector::Metadata::Walker) do
4
+ describe "#walk_object" do
5
+ it ""
6
+ end
7
+ end
@@ -0,0 +1,8 @@
1
+ require 'spec_helper'
2
+
3
+ describe(Inspector::PropertyMetadata) do
4
+ let(:property) { 'name' }
5
+ let(:metadata) { Inspector::PropertyMetadata.new(type, property) }
6
+
7
+ it_behaves_like Inspector::Metadata
8
+ end
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+
3
+ describe(Inspector::TypeMetadata) do
4
+ let(:metadata) { Inspector::TypeMetadata.new(type) }
5
+
6
+ it_behaves_like Inspector::Metadata
7
+ end
@@ -0,0 +1,65 @@
1
+ require 'spec_helper'
2
+
3
+ describe(Inspector::Validator) do
4
+ let(:metadata_map) { double() }
5
+ let(:walker) { double() }
6
+ let(:type_metadata_class) { double() }
7
+ let(:validator) {
8
+ Inspector::Validator.new(
9
+ metadata_map,
10
+ walker,
11
+ type_metadata_class
12
+ )
13
+ }
14
+
15
+ describe "#valid" do
16
+ let(:metadata) { double() }
17
+
18
+ before(:each) do
19
+ type_metadata_class.should_receive(:new).with(NilClass).and_return(metadata)
20
+ metadata_map.should_receive(:[]=).with(NilClass, metadata).and_return(metadata)
21
+ end
22
+
23
+ it "registers new TypeMetadata in metadata map" do
24
+ expect(validator.valid(NilClass)).to be(nil)
25
+ end
26
+
27
+ it "yields registered TypeMetadata" do
28
+ validator.valid(NilClass) do |nil_metadata|
29
+ expect(nil_metadata).to be(metadata)
30
+ end
31
+ end
32
+
33
+ it "evaluates block in context of registered TypeMetadata" do
34
+ context = nil
35
+ validator.valid(NilClass) do
36
+ context = self
37
+ end
38
+
39
+ expect(context).to be(metadata)
40
+ end
41
+ end
42
+
43
+ describe "#validate" do
44
+ let(:metadata) { double() }
45
+ let(:violations) { double() }
46
+ let(:object) { double() }
47
+
48
+ before(:each) do
49
+ walker.should_receive(:walk_object).with(metadata, object).and_return(violations)
50
+ end
51
+
52
+ it "walks object using its class" do
53
+ object.stub(:class) { NilClass }
54
+ metadata_map.should_receive(:[]).with(NilClass).and_return(metadata)
55
+
56
+ expect(validator.validate(object)).to be(violations)
57
+ end
58
+
59
+ it "walks object using metadata type specified" do
60
+ metadata_map.should_receive(:[]).with("metadata type").and_return(metadata)
61
+
62
+ expect(validator.validate(object, :as => "metadata type")).to be(violations)
63
+ end
64
+ end
65
+ end