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.
Files changed (64) hide show
  1. data/.gitignore +17 -0
  2. data/.ruby-version +1 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +128 -0
  6. data/Rakefile +8 -0
  7. data/lib/entities/association.entity +11 -0
  8. data/lib/entities/entity.entity +16 -0
  9. data/lib/entities/field.entity +35 -0
  10. data/lib/entities/related_entity.entity +28 -0
  11. data/lib/spectifly.rb +7 -0
  12. data/lib/spectifly/base.rb +8 -0
  13. data/lib/spectifly/base/association.rb +18 -0
  14. data/lib/spectifly/base/builder.rb +102 -0
  15. data/lib/spectifly/base/configuration.rb +19 -0
  16. data/lib/spectifly/base/entity_node.rb +55 -0
  17. data/lib/spectifly/base/field.rb +42 -0
  18. data/lib/spectifly/base/types.rb +27 -0
  19. data/lib/spectifly/configuration.rb +17 -0
  20. data/lib/spectifly/entity.rb +106 -0
  21. data/lib/spectifly/json.rb +8 -0
  22. data/lib/spectifly/json/association.rb +19 -0
  23. data/lib/spectifly/json/builder.rb +21 -0
  24. data/lib/spectifly/json/field.rb +28 -0
  25. data/lib/spectifly/json/types.rb +6 -0
  26. data/lib/spectifly/support.rb +32 -0
  27. data/lib/spectifly/tasks.rb +20 -0
  28. data/lib/spectifly/version.rb +3 -0
  29. data/lib/spectifly/xsd.rb +8 -0
  30. data/lib/spectifly/xsd/association.rb +31 -0
  31. data/lib/spectifly/xsd/builder.rb +43 -0
  32. data/lib/spectifly/xsd/field.rb +92 -0
  33. data/lib/spectifly/xsd/types.rb +32 -0
  34. data/lib/tasks/spectifly.rake +6 -0
  35. data/spec/expectations/extended.xsd +15 -0
  36. data/spec/expectations/group.json +37 -0
  37. data/spec/expectations/group.xsd +39 -0
  38. data/spec/expectations/individual.json +57 -0
  39. data/spec/expectations/individual.xsd +47 -0
  40. data/spec/expectations/presented/masterless_group.json +30 -0
  41. data/spec/expectations/presented/masterless_group.xsd +34 -0
  42. data/spec/expectations/presented/positionless_individual.json +44 -0
  43. data/spec/expectations/presented/positionless_individual.xsd +35 -0
  44. data/spec/fixtures/group.entity +23 -0
  45. data/spec/fixtures/individual.entity +33 -0
  46. data/spec/fixtures/invalid/multiple_root.entity +8 -0
  47. data/spec/fixtures/invalid/no_fields.entity +2 -0
  48. data/spec/fixtures/presenters/masterless_group.entity +8 -0
  49. data/spec/fixtures/presenters/positionless_individual.entity +12 -0
  50. data/spec/spec_helper.rb +10 -0
  51. data/spec/spectifly/base/builder_spec.rb +29 -0
  52. data/spec/spectifly/base/entity_node_spec.rb +29 -0
  53. data/spec/spectifly/base/field_spec.rb +100 -0
  54. data/spec/spectifly/configuration_spec.rb +42 -0
  55. data/spec/spectifly/entity_spec.rb +189 -0
  56. data/spec/spectifly/json/builder_spec.rb +42 -0
  57. data/spec/spectifly/json/field_spec.rb +26 -0
  58. data/spec/spectifly/support_spec.rb +53 -0
  59. data/spec/spectifly/xsd/builder_spec.rb +51 -0
  60. data/spec/spectifly/xsd/field_spec.rb +12 -0
  61. data/spec/spectifly/xsd/types_spec.rb +11 -0
  62. data/spec/support/path_helper.rb +28 -0
  63. data/spectifly.gemspec +32 -0
  64. 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,6 @@
1
+ namespace :spectifly do
2
+ desc 'Do whatever'
3
+ Spectifly::Task.new('whatever') do |sfly|
4
+ sfly.config_path = File.join(Rake.original_dir, 'config', 'spectifly.yml')
5
+ end
6
+ 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>