schematic 0.0.4 → 0.0.5

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.
@@ -7,75 +7,125 @@ module Schematic
7
7
  end
8
8
  end
9
9
 
10
- def to_xsd(options = {}, builder = nil)
11
- if builder.nil?
12
- output = ""
13
- builder = Builder::XmlMarkup.new(:target => output)
14
- builder.instruct!
15
- builder.xs :schema, "xmlns:xs" => "http://www.w3.org/2001/XMLSchema" do |schema|
16
- schema.xs :element, "name" => xsd_element_collection_name, "type" => xsd_type_collection_name
17
- self.to_xsd(options, schema)
18
- end
19
- output
20
- else
21
- xsd_nested_attributes.each do |nested_attribute|
22
- nested_attribute.klass.to_xsd(options, builder)
10
+ def to_xsd(options = {})
11
+ output = ""
12
+ builder = Builder::XmlMarkup.new(:target => output)
13
+ builder.instruct!
14
+ builder.xs :schema, "xmlns:xs" => "http://www.w3.org/2001/XMLSchema" do |schema|
15
+ schema.xs :element, "name" => xsd_element_collection_name, "type" => xsd_type_collection_name
16
+ generate_xsd(options, schema, self)
17
+ end
18
+ output
19
+ end
20
+
21
+ def map_column_type_to_xsd_type(column)
22
+ {
23
+ :integer => "xs:integer",
24
+ :float => "xs:float",
25
+ :string => "xs:string",
26
+ :text => "xs:string",
27
+ :datetime => "xs:dateTime",
28
+ :date => "xs:date",
29
+ :boolean => "xs:boolean"
30
+ }[column.type]
31
+ end
32
+
33
+ def xsd_minimum_occurrences_for_column(column)
34
+ self._validators[column.name.to_sym].each do |column_validation|
35
+ next unless column_validation.is_a? ActiveModel::Validations::PresenceValidator
36
+ return "1" if column_validation.options[:allow_blank] != true && column_validation.options[:if].nil?
37
+ end
38
+ "0"
39
+ end
40
+
41
+ def xsd_element_collection_name
42
+ xsd_element_name.pluralize
43
+ end
44
+
45
+ def xsd_type_collection_name
46
+ xsd_type_name.pluralize
47
+ end
48
+
49
+ def xsd_type_name
50
+ self.name.demodulize
51
+ end
52
+
53
+ def xsd_element_name
54
+ xsd_type_name.underscore.dasherize
55
+ end
56
+
57
+ def generate_xsd(options, builder, klass)
58
+ xsd_nested_attributes.each do |nested_attribute|
59
+ next if nested_attribute.klass == klass || nested_attribute.klass == klass.superclass
60
+
61
+ nested_attribute.klass.generate_xsd(options, builder, klass)
62
+ end
63
+
64
+ generate_xsd_complex_type_for_collection(builder)
65
+ generate_xsd_complex_type_for_model(options, builder)
66
+ end
67
+
68
+ private
69
+
70
+ def generate_xsd_complex_type_for_collection(builder)
71
+ builder.xs :complexType, "name" => xsd_type_collection_name do |complex_type|
72
+ complex_type.xs :sequence do |sequence|
73
+ sequence.xs :element, "name" => xsd_element_name, "type" => xsd_type_name, "minOccurs" => "0", "maxOccurs" => "unbounded"
23
74
  end
24
- builder.xs :complexType, "name" => xsd_type_collection_name do |complex_type|
25
- complex_type.xs :sequence do |sequence|
26
- sequence.xs :element, "name" => xsd_element_name, "type" => xsd_type_name, "minOccurs" => "0", "maxOccurs" => "unbounded"
75
+ complex_type.xs :attribute, "name" => "type", "type" => "xs:string", "fixed" => "array"
76
+ end
77
+ end
78
+
79
+ def generate_xsd_complex_type_for_model(options, builder)
80
+ builder.xs :complexType, "name" => xsd_type_name do |complex_type|
81
+ additional_methods = xsd_methods.merge(options[:methods] || {})
82
+ ignored_methods = xsd_ignore_methods | (options[:exclude] || [])
83
+ complex_type.xs :all do |all|
84
+ generate_xsd_column_elements(all, additional_methods, ignored_methods)
85
+
86
+ xsd_nested_attributes.each do |nested_attribute|
87
+ all.xs :element, "name" => "#{nested_attribute.name.to_s.dasherize}-attributes", "type" => nested_attribute.klass.xsd_type_collection_name, "minOccurs" => "0", "maxOccurs" => "1"
27
88
  end
28
- complex_type.xs :attribute, "name" => "type", "type" => "xs:string", "fixed" => "array"
89
+
90
+ generate_xsd_additional_methods(all, additional_methods)
29
91
  end
30
- builder.xs :complexType, "name" => xsd_type_name do |complex_type|
31
- additional_methods = xsd_methods.merge(options[:methods] || {})
32
- ignored_methods = xsd_ignore_methods | (options[:exclude] || [])
33
- complex_type.xs :all do |all|
34
- xsd_columns.each do |column|
35
- next if additional_methods.keys.map(&:to_s).include?(column.name) || ignored_methods.map(&:to_s).include?(column.name)
36
-
37
- all.xs :element, "name" => column.name.dasherize, "minOccurs" => xsd_minimum_occurrences_for_column(column), "maxOccurs" => "1" do |field|
38
- field.xs :complexType do |complex_type|
39
- complex_type.xs :simpleContent do |simple_content|
40
- simple_content.xs :extension, "base" => map_column_type_to_xsd_type(column) do |extension|
41
- extension.xs :attribute, "name" => "type", "type" => "xs:string", "use" => "optional"
42
- end
43
- end
44
- end
45
- end
46
- end
47
- xsd_nested_attributes.each do |nested_attribute|
48
- all.xs :element, "name" => "#{nested_attribute.name.to_s.dasherize}-attributes", "type" => nested_attribute.klass.xsd_type_collection_name, "minOccurs" => "0", "maxOccurs" => "1"
49
- end
50
- additional_methods.each do |method_name, values|
51
- method_xsd_name = method_name.to_s.dasherize
52
- if values.present?
53
- all.xs :element, "name" => method_xsd_name, "minOccurs" => "0", "maxOccurs" => "1" do |element|
54
- element.xs :complexType do |complex_type|
55
- complex_type.xs :all do |nested_all|
56
- values.each do |value|
57
- nested_all.xs :element, "name" => value.to_s.dasherize, "minOccurs" => "0"
58
- end
59
- end
60
- complex_type.xs :attribute, "name" => "type", "type" => "xs:string", "fixed" => "array", "use" => "optional"
61
- end
62
- end
63
- else
64
- all.xs :element, "name" => method_xsd_name, "minOccurs" => "0", "maxOccurs" => "1"
92
+ end
93
+ end
94
+
95
+ def generate_xsd_column_elements(builder, additional_methods, ignored_methods)
96
+ xsd_columns.each do |column|
97
+ next if additional_methods.keys.map(&:to_s).include?(column.name) || ignored_methods.map(&:to_s).include?(column.name)
98
+
99
+ builder.xs :element, "name" => column.name.dasherize, "minOccurs" => xsd_minimum_occurrences_for_column(column), "maxOccurs" => "1" do |field|
100
+ field.xs :complexType do |complex_type|
101
+ complex_type.xs :simpleContent do |simple_content|
102
+ simple_content.xs :extension, "base" => map_column_type_to_xsd_type(column) do |extension|
103
+ extension.xs :attribute, "name" => "type", "type" => "xs:string", "use" => "optional"
65
104
  end
66
105
  end
67
106
  end
68
107
  end
69
- builder
70
108
  end
71
109
  end
72
110
 
73
- def xsd_minimum_occurrences_for_column(column)
74
- self._validators[column.name.to_sym].each do |column_validation|
75
- next unless column_validation.is_a? ActiveModel::Validations::PresenceValidator
76
- return "1" if column_validation.options[:allow_blank] != true
111
+ def generate_xsd_additional_methods(builder, additional_methods)
112
+ additional_methods.each do |method_name, values|
113
+ method_xsd_name = method_name.to_s.dasherize
114
+ if values.present?
115
+ builder.xs :element, "name" => method_xsd_name, "minOccurs" => "0", "maxOccurs" => "1" do |element|
116
+ element.xs :complexType do |complex_type|
117
+ complex_type.xs :all do |nested_all|
118
+ values.each do |value|
119
+ nested_all.xs :element, "name" => value.to_s.dasherize, "minOccurs" => "0"
120
+ end
121
+ end
122
+ complex_type.xs :attribute, "name" => "type", "type" => "xs:string", "fixed" => "array", "use" => "optional"
123
+ end
124
+ end
125
+ else
126
+ builder.xs :element, "name" => method_xsd_name, "minOccurs" => "0", "maxOccurs" => "1"
127
+ end
77
128
  end
78
- "0"
79
129
  end
80
130
 
81
131
  def xsd_methods
@@ -96,33 +146,7 @@ module Schematic
96
146
  self.columns
97
147
  end
98
148
 
99
- def map_column_type_to_xsd_type(column)
100
- {
101
- :integer => "xs:integer",
102
- :float => "xs:float",
103
- :string => "xs:string",
104
- :text => "xs:string",
105
- :datetime => "xs:dateTime",
106
- :date => "xs:date",
107
- :boolean => "xs:boolean"
108
- }[column.type]
109
- end
110
-
111
- def xsd_type_name
112
- self.name.demodulize
113
- end
114
149
 
115
- def xsd_type_collection_name
116
- xsd_type_name.pluralize
117
- end
118
-
119
- def xsd_element_name
120
- xsd_type_name.underscore.dasherize
121
- end
122
-
123
- def xsd_element_collection_name
124
- xsd_element_name.pluralize
125
- end
126
150
  end
127
151
  end
128
152
  end
@@ -1,3 +1,3 @@
1
1
  module Schematic
2
- VERSION = "0.0.4"
2
+ VERSION = "0.0.5"
3
3
  end
@@ -59,13 +59,7 @@ describe Schematic::Serializers::Xsd do
59
59
 
60
60
  end
61
61
  it "should generate a valid XSD" do
62
- xsd_schema_file = File.join(File.dirname(__FILE__), "xsd", "XMLSchema.xsd")
63
- meta_xsd = Nokogiri::XML::Schema(File.open(xsd_schema_file))
64
-
65
- doc = Nokogiri::XML.parse(subject)
66
- meta_xsd.validate(doc).each do |error|
67
- error.message.should be_nil
68
- end
62
+ validate_xsd(subject)
69
63
  end
70
64
  end
71
65
 
@@ -93,14 +87,69 @@ describe Schematic::Serializers::Xsd do
93
87
  end
94
88
 
95
89
  it "should generate a valid XSD" do
96
- xsd_schema_file = File.join(File.dirname(__FILE__), "xsd", "XMLSchema.xsd")
97
- meta_xsd = Nokogiri::XML::Schema(File.open(xsd_schema_file))
90
+ validate_xsd(subject)
91
+ end
92
+ end
93
+
94
+ context "when the model has a nested attribute on a subclass with a reference to the superclass" do
95
+ with_model :parent do
96
+ table {}
97
+ model do
98
+ has_many :children
99
+ accepts_nested_attributes_for :children
100
+ end
101
+ end
102
+
103
+ with_model :child do
104
+ table do |t|
105
+ t.integer :parent_id
106
+ end
107
+
108
+ model do
109
+ belongs_to :parent
110
+ end
111
+ end
112
+
113
+ before do
114
+ module Namespace; end
115
+ class Namespace::Child < Child
116
+ accepts_nested_attributes_for :parent
117
+ end
118
+ end
119
+
120
+ subject { Namespace::Child.to_xsd }
121
+
122
+ it "should generate a valid XSD" do
123
+ validate_xsd(subject)
124
+ end
125
+ end
126
+
127
+ context "when the model has a circular nested attribute reference" do
128
+ with_model :blog do
129
+ table {}
130
+ model do
131
+ has_many :posts
132
+ accepts_nested_attributes_for :posts
133
+ end
134
+ end
98
135
 
99
- doc = Nokogiri::XML.parse(subject)
100
- meta_xsd.validate(doc).each do |error|
101
- error.message.should be_nil
136
+ with_model :post do
137
+ table do |t|
138
+ t.integer :blog_id
102
139
  end
140
+
141
+ model do
142
+ belongs_to :blog
143
+ accepts_nested_attributes_for :blog
144
+ end
145
+ end
146
+
147
+ subject { Post.to_xsd }
148
+
149
+ it "should generate a valid XSD" do
150
+ validate_xsd(subject)
103
151
  end
152
+
104
153
  end
105
154
  end
106
155
 
@@ -255,6 +304,36 @@ describe Schematic::Serializers::Xsd do
255
304
  subject.should == sanitize_xml(xsd)
256
305
  end
257
306
  end
307
+
308
+ context "when there is a condition" do
309
+ with_model :some_model do
310
+ table :id => false do |t|
311
+ t.string "title"
312
+ end
313
+
314
+ model do
315
+ validates :title, :presence => true, :if => lambda { |model| false }
316
+ end
317
+ end
318
+
319
+ it "should mark that the field minimum occurrences is 0" do
320
+ xsd = generate_xsd_for_model(SomeModel) do
321
+ <<-XML
322
+ <xs:element name="title" minOccurs="0" maxOccurs="1">
323
+ <xs:complexType>
324
+ <xs:simpleContent>
325
+ <xs:extension base="xs:string">
326
+ <xs:attribute name="type" type="xs:string" use="optional"/>
327
+ </xs:extension>
328
+ </xs:simpleContent>
329
+ </xs:complexType>
330
+ </xs:element>
331
+ XML
332
+ end
333
+
334
+ subject.should == sanitize_xml(xsd)
335
+ end
336
+ end
258
337
  end
259
338
 
260
339
  describe "length validation" do
@@ -368,6 +447,16 @@ describe Schematic::Serializers::Xsd do
368
447
 
369
448
  private
370
449
 
450
+ def validate_xsd(xml)
451
+ xsd_schema_file = File.join(File.dirname(__FILE__), "xsd", "XMLSchema.xsd")
452
+ meta_xsd = Nokogiri::XML::Schema(File.open(xsd_schema_file))
453
+
454
+ doc = Nokogiri::XML.parse(xml)
455
+ meta_xsd.validate(doc).each do |error|
456
+ error.message.should be_nil
457
+ end
458
+ end
459
+
371
460
  def sanitize_xml(xml)
372
461
  xml.split("\n").map(&:strip).join("")
373
462
  end
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: schematic
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.0.4
5
+ version: 0.0.5
6
6
  platform: ruby
7
7
  authors:
8
8
  - Case Commons, LLC