spectifly 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/.ruby-version +1 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +128 -0
- data/Rakefile +8 -0
- data/lib/entities/association.entity +11 -0
- data/lib/entities/entity.entity +16 -0
- data/lib/entities/field.entity +35 -0
- data/lib/entities/related_entity.entity +28 -0
- data/lib/spectifly.rb +7 -0
- data/lib/spectifly/base.rb +8 -0
- data/lib/spectifly/base/association.rb +18 -0
- data/lib/spectifly/base/builder.rb +102 -0
- data/lib/spectifly/base/configuration.rb +19 -0
- data/lib/spectifly/base/entity_node.rb +55 -0
- data/lib/spectifly/base/field.rb +42 -0
- data/lib/spectifly/base/types.rb +27 -0
- data/lib/spectifly/configuration.rb +17 -0
- data/lib/spectifly/entity.rb +106 -0
- data/lib/spectifly/json.rb +8 -0
- data/lib/spectifly/json/association.rb +19 -0
- data/lib/spectifly/json/builder.rb +21 -0
- data/lib/spectifly/json/field.rb +28 -0
- data/lib/spectifly/json/types.rb +6 -0
- data/lib/spectifly/support.rb +32 -0
- data/lib/spectifly/tasks.rb +20 -0
- data/lib/spectifly/version.rb +3 -0
- data/lib/spectifly/xsd.rb +8 -0
- data/lib/spectifly/xsd/association.rb +31 -0
- data/lib/spectifly/xsd/builder.rb +43 -0
- data/lib/spectifly/xsd/field.rb +92 -0
- data/lib/spectifly/xsd/types.rb +32 -0
- data/lib/tasks/spectifly.rake +6 -0
- data/spec/expectations/extended.xsd +15 -0
- data/spec/expectations/group.json +37 -0
- data/spec/expectations/group.xsd +39 -0
- data/spec/expectations/individual.json +57 -0
- data/spec/expectations/individual.xsd +47 -0
- data/spec/expectations/presented/masterless_group.json +30 -0
- data/spec/expectations/presented/masterless_group.xsd +34 -0
- data/spec/expectations/presented/positionless_individual.json +44 -0
- data/spec/expectations/presented/positionless_individual.xsd +35 -0
- data/spec/fixtures/group.entity +23 -0
- data/spec/fixtures/individual.entity +33 -0
- data/spec/fixtures/invalid/multiple_root.entity +8 -0
- data/spec/fixtures/invalid/no_fields.entity +2 -0
- data/spec/fixtures/presenters/masterless_group.entity +8 -0
- data/spec/fixtures/presenters/positionless_individual.entity +12 -0
- data/spec/spec_helper.rb +10 -0
- data/spec/spectifly/base/builder_spec.rb +29 -0
- data/spec/spectifly/base/entity_node_spec.rb +29 -0
- data/spec/spectifly/base/field_spec.rb +100 -0
- data/spec/spectifly/configuration_spec.rb +42 -0
- data/spec/spectifly/entity_spec.rb +189 -0
- data/spec/spectifly/json/builder_spec.rb +42 -0
- data/spec/spectifly/json/field_spec.rb +26 -0
- data/spec/spectifly/support_spec.rb +53 -0
- data/spec/spectifly/xsd/builder_spec.rb +51 -0
- data/spec/spectifly/xsd/field_spec.rb +12 -0
- data/spec/spectifly/xsd/types_spec.rb +11 -0
- data/spec/support/path_helper.rb +28 -0
- data/spectifly.gemspec +32 -0
- metadata +251 -0
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'builder'
|
2
|
+
require_relative 'field'
|
3
|
+
require_relative 'association'
|
4
|
+
require_relative 'types'
|
5
|
+
|
6
|
+
module Spectifly
|
7
|
+
module Xsd
|
8
|
+
class Builder < Spectifly::Base::Builder
|
9
|
+
def root_type
|
10
|
+
Spectifly::Support.lower_camelize("#{root}Type")
|
11
|
+
end
|
12
|
+
|
13
|
+
def build_type(xml = nil)
|
14
|
+
xml ||= ::Builder::XmlMarkup.new(:indent => 2)
|
15
|
+
xml.xs 'complexType'.to_sym, :name => root_type do
|
16
|
+
xml.xs :sequence do
|
17
|
+
fields.each do |field|
|
18
|
+
field.to_xsd(xml)
|
19
|
+
end
|
20
|
+
associations.each do |association|
|
21
|
+
association.to_xsd(xml)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def build(xml = nil)
|
28
|
+
xml ||= ::Builder::XmlMarkup.new(:indent => 2)
|
29
|
+
xml.instruct! :xml, :version => '1.0', :encoding => 'UTF-8'
|
30
|
+
xml.xs :schema, 'xmlns:xs' => "http://www.w3.org/2001/XMLSchema", 'elementFormDefault' => "qualified" do
|
31
|
+
custom_types.each do |cust|
|
32
|
+
xml.xs :include, 'schemaLocation' => "#{cust}.xsd"
|
33
|
+
end
|
34
|
+
unless utilized_extended_types.empty?
|
35
|
+
xml.xs :include, 'schemaLocation' => "extended.xsd"
|
36
|
+
end
|
37
|
+
xml.xs :element, :name => Spectifly::Support.camelize(root), :type => root_type
|
38
|
+
build_type(xml)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require_relative 'types'
|
2
|
+
|
3
|
+
module Spectifly
|
4
|
+
module Xsd
|
5
|
+
class Field < Spectifly::Base::Field
|
6
|
+
def extract_restrictions
|
7
|
+
super
|
8
|
+
@restrictions.delete('unique')
|
9
|
+
@restrictions
|
10
|
+
end
|
11
|
+
|
12
|
+
def name
|
13
|
+
Spectifly::Support.camelize(@field_name).gsub(/\W/, '')
|
14
|
+
end
|
15
|
+
|
16
|
+
def name_as_type
|
17
|
+
Spectifly::Support.lower_camelize("#{name}Type")
|
18
|
+
end
|
19
|
+
|
20
|
+
def display_type
|
21
|
+
prefix = if @validations.include?('Must be positive')
|
22
|
+
'positive_'
|
23
|
+
elsif @validations.include?('Must be non-negative')
|
24
|
+
'non_negative_'
|
25
|
+
end
|
26
|
+
prefixed_type = "#{prefix}#{type}"
|
27
|
+
camel_type = Spectifly::Support.lower_camelize(prefixed_type)
|
28
|
+
if Spectifly::Xsd::Types::Native.include?(prefixed_type)
|
29
|
+
"xs:#{camel_type}"
|
30
|
+
else
|
31
|
+
"#{camel_type}Type"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def to_xsd(builder = nil)
|
36
|
+
builder ||= ::Builder::XmlMarkup.new(:indent => 2)
|
37
|
+
attributes['type'] = display_type if restrictions.empty?
|
38
|
+
attributes['minOccurs'] = '0' unless required?
|
39
|
+
attributes['maxOccurs'] = 'unbounded' if multiple?
|
40
|
+
block = embedded_block
|
41
|
+
builder.xs :element, { :name => name }.merge(attributes), &block
|
42
|
+
end
|
43
|
+
|
44
|
+
def regex
|
45
|
+
return nil unless restrictions['regex']
|
46
|
+
regex = restrictions['regex'].source
|
47
|
+
if regex =~ /^(\^)?([^\$]*)(\$)?$/
|
48
|
+
prefix = '[\s\S]*' unless $1
|
49
|
+
suffix = '[\s\S]*' unless $3
|
50
|
+
regex = "#{prefix}#{$2}#{suffix}"
|
51
|
+
end
|
52
|
+
regex
|
53
|
+
end
|
54
|
+
|
55
|
+
def type_block(use_name = false)
|
56
|
+
type_attributes = { :name => name_as_type } if use_name
|
57
|
+
Proc.new { |el|
|
58
|
+
el.xs :simpleType, type_attributes do |st|
|
59
|
+
st.xs :restriction, :base => display_type, &restrictions_block
|
60
|
+
end
|
61
|
+
}
|
62
|
+
end
|
63
|
+
|
64
|
+
def restrictions_block
|
65
|
+
if !restrictions.empty?
|
66
|
+
Proc.new { |el|
|
67
|
+
el.xs :minInclusive, :value => restrictions['minimum_value'] if restrictions['minimum_value']
|
68
|
+
el.xs :maxInclusive, :value => restrictions['maximum_value'] if restrictions['maximum_value']
|
69
|
+
el.xs :pattern, :value => regex if regex
|
70
|
+
(restrictions['valid_values'] || []).each do |vv|
|
71
|
+
el.xs :enumeration, :value => vv
|
72
|
+
end
|
73
|
+
}
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def embedded_block
|
78
|
+
if description || example || !restrictions.empty?
|
79
|
+
Proc.new { |el|
|
80
|
+
if description || example
|
81
|
+
el.xs :annotation do |ann|
|
82
|
+
ann.xs :documentation, description if description
|
83
|
+
ann.xs :documentation, "Example: #{example}" if example
|
84
|
+
end
|
85
|
+
end
|
86
|
+
type_block.call(el) unless restrictions.empty?
|
87
|
+
}
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Spectifly
|
2
|
+
module Xsd
|
3
|
+
class Types
|
4
|
+
Native = [
|
5
|
+
'boolean',
|
6
|
+
'string',
|
7
|
+
'date',
|
8
|
+
'date_time',
|
9
|
+
'integer',
|
10
|
+
'non_negative_integer',
|
11
|
+
'positive_integer',
|
12
|
+
'decimal',
|
13
|
+
'base64_binary'
|
14
|
+
]
|
15
|
+
|
16
|
+
Extended = Spectifly::Base::Types::Extended
|
17
|
+
|
18
|
+
class << self
|
19
|
+
def build_extended(xml = nil)
|
20
|
+
xml ||= ::Builder::XmlMarkup.new(:indent => 2)
|
21
|
+
xml.instruct! :xml, :version => '1.0', :encoding => 'UTF-8'
|
22
|
+
xml.xs :schema, 'xmlns:xs' => "http://www.w3.org/2001/XMLSchema", 'elementFormDefault' => "qualified" do
|
23
|
+
Extended.each_pair do |name, attributes|
|
24
|
+
field = Spectifly::Xsd::Field.new(name.dup, attributes.dup)
|
25
|
+
field.type_block(true).call(xml)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
|
3
|
+
<xs:simpleType name="percentType">
|
4
|
+
<xs:restriction base="xs:decimal"/>
|
5
|
+
</xs:simpleType>
|
6
|
+
<xs:simpleType name="currencyType">
|
7
|
+
<xs:restriction base="xs:decimal"/>
|
8
|
+
</xs:simpleType>
|
9
|
+
<xs:simpleType name="yearType">
|
10
|
+
<xs:restriction base="xs:integer"/>
|
11
|
+
</xs:simpleType>
|
12
|
+
<xs:simpleType name="phoneType">
|
13
|
+
<xs:restriction base="xs:string"/>
|
14
|
+
</xs:simpleType>
|
15
|
+
</xs:schema>
|
@@ -0,0 +1,37 @@
|
|
1
|
+
{
|
2
|
+
"group": {
|
3
|
+
"has_many": {
|
4
|
+
"peeps": {
|
5
|
+
"type": "individual",
|
6
|
+
"required": false,
|
7
|
+
"description": "Who is in the group"
|
8
|
+
}
|
9
|
+
},
|
10
|
+
"has_one": {
|
11
|
+
"master": {
|
12
|
+
"type": "individual",
|
13
|
+
"required": true,
|
14
|
+
"description": "Who is the master of the group"
|
15
|
+
}
|
16
|
+
},
|
17
|
+
"group_id": {
|
18
|
+
"type": "string",
|
19
|
+
"multiple": false,
|
20
|
+
"required": false,
|
21
|
+
"description": "Identifier used to register group for scams",
|
22
|
+
"restrictions": {
|
23
|
+
"unique": true,
|
24
|
+
"regex": "^[0-9]{2}(-?[0-9]{5})$"
|
25
|
+
}
|
26
|
+
},
|
27
|
+
"name": {
|
28
|
+
"type": "string",
|
29
|
+
"multiple": false,
|
30
|
+
"required": true,
|
31
|
+
"description": "The name of this group",
|
32
|
+
"restrictions": {
|
33
|
+
"regex": "group"
|
34
|
+
}
|
35
|
+
}
|
36
|
+
}
|
37
|
+
}
|
@@ -0,0 +1,39 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
|
3
|
+
<xs:include schemaLocation="individual.xsd"/>
|
4
|
+
<xs:element name="Group" type="groupType"/>
|
5
|
+
<xs:complexType name="groupType">
|
6
|
+
<xs:sequence>
|
7
|
+
<xs:element name="GroupID" minOccurs="0">
|
8
|
+
<xs:annotation>
|
9
|
+
<xs:documentation>Identifier used to register group for scams</xs:documentation>
|
10
|
+
</xs:annotation>
|
11
|
+
<xs:simpleType>
|
12
|
+
<xs:restriction base="xs:string">
|
13
|
+
<xs:pattern value="[0-9]{2}(-?[0-9]{5})"/>
|
14
|
+
</xs:restriction>
|
15
|
+
</xs:simpleType>
|
16
|
+
</xs:element>
|
17
|
+
<xs:element name="Name">
|
18
|
+
<xs:annotation>
|
19
|
+
<xs:documentation>The name of this group</xs:documentation>
|
20
|
+
</xs:annotation>
|
21
|
+
<xs:simpleType>
|
22
|
+
<xs:restriction base="xs:string">
|
23
|
+
<xs:pattern value="[\s\S]*group[\s\S]*"/>
|
24
|
+
</xs:restriction>
|
25
|
+
</xs:simpleType>
|
26
|
+
</xs:element>
|
27
|
+
<xs:element name="Peeps" type="individualType" minOccurs="0" maxOccurs="unbounded">
|
28
|
+
<xs:annotation>
|
29
|
+
<xs:documentation>Who is in the group</xs:documentation>
|
30
|
+
</xs:annotation>
|
31
|
+
</xs:element>
|
32
|
+
<xs:element name="Master" type="individualType">
|
33
|
+
<xs:annotation>
|
34
|
+
<xs:documentation>Who is the master of the group</xs:documentation>
|
35
|
+
</xs:annotation>
|
36
|
+
</xs:element>
|
37
|
+
</xs:sequence>
|
38
|
+
</xs:complexType>
|
39
|
+
</xs:schema>
|
@@ -0,0 +1,57 @@
|
|
1
|
+
{
|
2
|
+
"individual": {
|
3
|
+
"belongs_to": {
|
4
|
+
"party": {
|
5
|
+
"type": "group",
|
6
|
+
"required": true,
|
7
|
+
"description": "Which funtime party this individual happy with is"
|
8
|
+
}
|
9
|
+
},
|
10
|
+
"name": {
|
11
|
+
"type": "string",
|
12
|
+
"multiple": false,
|
13
|
+
"required": true,
|
14
|
+
"description": "The individual's name",
|
15
|
+
"example": "Randy McTougherson",
|
16
|
+
"restrictions": {
|
17
|
+
"unique": true
|
18
|
+
}
|
19
|
+
},
|
20
|
+
"age": {
|
21
|
+
"type": "integer",
|
22
|
+
"multiple": false,
|
23
|
+
"required": false,
|
24
|
+
"validations": [
|
25
|
+
"Must be non-negative"
|
26
|
+
]
|
27
|
+
},
|
28
|
+
"happiness": {
|
29
|
+
"type": "percent",
|
30
|
+
"multiple": false,
|
31
|
+
"required": false,
|
32
|
+
"restrictions": {
|
33
|
+
"minimum_value": 0,
|
34
|
+
"maximum_value": 100
|
35
|
+
}
|
36
|
+
},
|
37
|
+
"positions": {
|
38
|
+
"type": "string",
|
39
|
+
"multiple": true,
|
40
|
+
"required": false,
|
41
|
+
"description": "Which positions individual occupies in a group",
|
42
|
+
"restrictions": {
|
43
|
+
"valid_values": [
|
44
|
+
"Lotus",
|
45
|
+
"Pole",
|
46
|
+
"Third"
|
47
|
+
]
|
48
|
+
}
|
49
|
+
},
|
50
|
+
"pickled": {
|
51
|
+
"type": "boolean",
|
52
|
+
"multiple": false,
|
53
|
+
"required": true,
|
54
|
+
"description": "Whether or not this individual is pickled"
|
55
|
+
}
|
56
|
+
}
|
57
|
+
}
|
@@ -0,0 +1,47 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
|
3
|
+
<xs:include schemaLocation="group.xsd"/>
|
4
|
+
<xs:include schemaLocation="extended.xsd"/>
|
5
|
+
<xs:element name="Individual" type="individualType"/>
|
6
|
+
<xs:complexType name="individualType">
|
7
|
+
<xs:sequence>
|
8
|
+
<xs:element name="Name" type="xs:string">
|
9
|
+
<xs:annotation>
|
10
|
+
<xs:documentation>The individual's name</xs:documentation>
|
11
|
+
<xs:documentation>Example: Randy McTougherson</xs:documentation>
|
12
|
+
</xs:annotation>
|
13
|
+
</xs:element>
|
14
|
+
<xs:element name="Age" type="xs:nonNegativeInteger" minOccurs="0"/>
|
15
|
+
<xs:element name="Happiness" minOccurs="0">
|
16
|
+
<xs:simpleType>
|
17
|
+
<xs:restriction base="percentType">
|
18
|
+
<xs:minInclusive value="0"/>
|
19
|
+
<xs:maxInclusive value="100"/>
|
20
|
+
</xs:restriction>
|
21
|
+
</xs:simpleType>
|
22
|
+
</xs:element>
|
23
|
+
<xs:element name="Positions" minOccurs="0" maxOccurs="unbounded">
|
24
|
+
<xs:annotation>
|
25
|
+
<xs:documentation>Which positions individual occupies in a group</xs:documentation>
|
26
|
+
</xs:annotation>
|
27
|
+
<xs:simpleType>
|
28
|
+
<xs:restriction base="xs:string">
|
29
|
+
<xs:enumeration value="Lotus"/>
|
30
|
+
<xs:enumeration value="Pole"/>
|
31
|
+
<xs:enumeration value="Third"/>
|
32
|
+
</xs:restriction>
|
33
|
+
</xs:simpleType>
|
34
|
+
</xs:element>
|
35
|
+
<xs:element name="Pickled" type="xs:boolean">
|
36
|
+
<xs:annotation>
|
37
|
+
<xs:documentation>Whether or not this individual is pickled</xs:documentation>
|
38
|
+
</xs:annotation>
|
39
|
+
</xs:element>
|
40
|
+
<xs:element name="Party" type="groupType" minOccurs="0">
|
41
|
+
<xs:annotation>
|
42
|
+
<xs:documentation>Which funtime party this individual happy with is</xs:documentation>
|
43
|
+
</xs:annotation>
|
44
|
+
</xs:element>
|
45
|
+
</xs:sequence>
|
46
|
+
</xs:complexType>
|
47
|
+
</xs:schema>
|
@@ -0,0 +1,30 @@
|
|
1
|
+
{
|
2
|
+
"group": {
|
3
|
+
"has_many": {
|
4
|
+
"peeps": {
|
5
|
+
"type": "individual",
|
6
|
+
"required": false,
|
7
|
+
"description": "Who is in the group"
|
8
|
+
}
|
9
|
+
},
|
10
|
+
"group_id": {
|
11
|
+
"type": "string",
|
12
|
+
"multiple": false,
|
13
|
+
"required": false,
|
14
|
+
"description": "Identifier used to register group for scams",
|
15
|
+
"restrictions": {
|
16
|
+
"unique": true,
|
17
|
+
"regex": "^[0-9]{2}(-?[0-9]{5})$"
|
18
|
+
}
|
19
|
+
},
|
20
|
+
"name": {
|
21
|
+
"type": "string",
|
22
|
+
"multiple": false,
|
23
|
+
"required": true,
|
24
|
+
"description": "The name of this group",
|
25
|
+
"restrictions": {
|
26
|
+
"regex": "group"
|
27
|
+
}
|
28
|
+
}
|
29
|
+
}
|
30
|
+
}
|
@@ -0,0 +1,34 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
|
3
|
+
<xs:include schemaLocation="individual.xsd"/>
|
4
|
+
<xs:element name="Group" type="groupType"/>
|
5
|
+
<xs:complexType name="groupType">
|
6
|
+
<xs:sequence>
|
7
|
+
<xs:element name="GroupID" minOccurs="0">
|
8
|
+
<xs:annotation>
|
9
|
+
<xs:documentation>Identifier used to register group for scams</xs:documentation>
|
10
|
+
</xs:annotation>
|
11
|
+
<xs:simpleType>
|
12
|
+
<xs:restriction base="xs:string">
|
13
|
+
<xs:pattern value="[0-9]{2}(-?[0-9]{5})"/>
|
14
|
+
</xs:restriction>
|
15
|
+
</xs:simpleType>
|
16
|
+
</xs:element>
|
17
|
+
<xs:element name="Name">
|
18
|
+
<xs:annotation>
|
19
|
+
<xs:documentation>The name of this group</xs:documentation>
|
20
|
+
</xs:annotation>
|
21
|
+
<xs:simpleType>
|
22
|
+
<xs:restriction base="xs:string">
|
23
|
+
<xs:pattern value="[\s\S]*group[\s\S]*"/>
|
24
|
+
</xs:restriction>
|
25
|
+
</xs:simpleType>
|
26
|
+
</xs:element>
|
27
|
+
<xs:element name="Peeps" type="individualType" minOccurs="0" maxOccurs="unbounded">
|
28
|
+
<xs:annotation>
|
29
|
+
<xs:documentation>Who is in the group</xs:documentation>
|
30
|
+
</xs:annotation>
|
31
|
+
</xs:element>
|
32
|
+
</xs:sequence>
|
33
|
+
</xs:complexType>
|
34
|
+
</xs:schema>
|