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.
- 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
|