object-inspector 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +18 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +45 -0
- data/README.md +335 -0
- data/Rakefile +13 -0
- data/examples/example.rb +131 -0
- data/features/error_display.feature +80 -0
- data/features/support/env.rb +1 -0
- data/features/syntax.feature +223 -0
- data/inspector.gemspec +24 -0
- data/lib/inspector.rb +27 -0
- data/lib/inspector/attribute_metadata.rb +19 -0
- data/lib/inspector/constraint.rb +18 -0
- data/lib/inspector/constraint/validators.rb +8 -0
- data/lib/inspector/constraint/validators/simple.rb +13 -0
- data/lib/inspector/constraint/validators/validity.rb +24 -0
- data/lib/inspector/constraint/violation.rb +34 -0
- data/lib/inspector/constraint/violation/list.rb +93 -0
- data/lib/inspector/constraints.rb +61 -0
- data/lib/inspector/constraints/email.rb +57 -0
- data/lib/inspector/constraints/empty.rb +21 -0
- data/lib/inspector/constraints/eq.rb +23 -0
- data/lib/inspector/constraints/false.rb +19 -0
- data/lib/inspector/constraints/have.rb +85 -0
- data/lib/inspector/constraints/predicate.rb +38 -0
- data/lib/inspector/constraints/true.rb +19 -0
- data/lib/inspector/constraints/valid.rb +31 -0
- data/lib/inspector/metadata.rb +75 -0
- data/lib/inspector/metadata/map.rb +24 -0
- data/lib/inspector/metadata/walker.rb +46 -0
- data/lib/inspector/property_metadata.rb +19 -0
- data/lib/inspector/type_metadata.rb +5 -0
- data/lib/inspector/validator.rb +26 -0
- data/lib/inspector/version.rb +3 -0
- data/lib/object_inspector.rb +1 -0
- data/spec/inspector/attribute_metadata_spec.rb +8 -0
- data/spec/inspector/constraint/violation/list_spec.rb +45 -0
- data/spec/inspector/constraints/false_spec.rb +18 -0
- data/spec/inspector/constraints_spec.rb +15 -0
- data/spec/inspector/metadata/map_spec.rb +38 -0
- data/spec/inspector/metadata/walker_spec.rb +7 -0
- data/spec/inspector/property_metadata_spec.rb +8 -0
- data/spec/inspector/type_metadata_spec.rb +7 -0
- data/spec/inspector/validator_spec.rb +65 -0
- data/spec/inspector_spec.rb +22 -0
- data/spec/shared_examples/metadata.rb +33 -0
- data/spec/spec_helper.rb +3 -0
- 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,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 @@
|
|
1
|
+
require_relative 'inspector'
|
@@ -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,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
|