schematic 0.0.4 → 0.0.5

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