spectifly 0.0.1
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 +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>
|