spectifly 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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>