object-inspector 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 (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