data_active 0.0.6 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,12 @@
1
+ module DataActive
2
+ class Attribute
3
+ attr_reader :name
4
+ attr_accessor :content
5
+ attr_reader :known
6
+
7
+ def initialize(name, known = true)
8
+ @name = name
9
+ @known = known
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,151 @@
1
+ require 'set'
2
+
3
+ module DataActive
4
+ class Entity
5
+ attr_reader :attributes
6
+ attr_reader :klass
7
+ attr_reader :associations
8
+ attr_reader :tag_name
9
+ attr_accessor :belongs_to
10
+ attr_accessor :record
11
+ attr_accessor :excluded
12
+
13
+ def initialize(tag_name, options = [], excluded = false)
14
+ @tag_name = tag_name
15
+ @options = options
16
+ @attributes = {}
17
+ @associations = {}
18
+ @excluded = excluded
19
+
20
+ unless @excluded
21
+ begin
22
+ @klass = Kernel.const_get(@tag_name.camelize)
23
+ raise "Class '#{@tag_name.camelize}' is not inherit ActiveRecord" unless @klass.ancestors.include? ActiveRecord::Base
24
+ @associations = Hash[@klass.reflect_on_all_associations.map { |a| [a.plural_name.to_sym, a] }]
25
+ @record = @klass.new
26
+ rescue
27
+ @excluded = true
28
+ end
29
+ end
30
+ end
31
+
32
+
33
+ def options_include? (context, options)
34
+ ((@options & options).count.eql? options.count and context.eql? :all) ||
35
+ ((@options & options).count > 0 and context.eql? :any)
36
+ end
37
+
38
+ def has_attribute?(name)
39
+ if @excluded
40
+ false
41
+ else
42
+ @record.attributes.include? name
43
+ end
44
+ end
45
+
46
+ def has_association_with?(name)
47
+ @associations.has_key? name.pluralize.to_sym
48
+ end
49
+
50
+ def commit
51
+ id = nil
52
+ existing_record = find_existing
53
+ @attributes.delete(@klass.primary_key.to_s)
54
+
55
+ if existing_record.nil? and options_include? :any, [:create, :sync]
56
+ id = save
57
+ elsif existing_record.present? and options_include? :any, [:update, :sync]
58
+ @record = existing_record
59
+ id = save
60
+ elsif options_include? :any, [:destroy, :sync]
61
+ id = @record.attributes[@klass.primary_key.to_s]
62
+ end
63
+
64
+ id
65
+ end
66
+
67
+ def update_associations
68
+ if belongs_to.present? and not belongs_to.excluded
69
+ association = belongs_to.klass.reflect_on_all_associations.select { |a| a.plural_name == @klass.name.underscore.pluralize }[0]
70
+ if belongs_to.record.new_record?
71
+ existing = belongs_to.find_existing
72
+ belongs_to.record = existing if existing.present?
73
+ end
74
+
75
+ if belongs_to.record.new_record?
76
+ case association.macro
77
+ when :has_many, :has_many_and_blongs_to
78
+ belongs_to.record.__send__(association.name) << @record
79
+
80
+ when :has_one
81
+ belongs_to.record.__send__("#{association.name}=", @record)
82
+
83
+ else
84
+ raise "unsupported association #{association.macro} for #{association.name} on #{@klass.name}"
85
+
86
+ end
87
+ else
88
+ foreign_key = foreign_key_from(association)
89
+ @record.__send__("#{foreign_key}=", belongs_to.record.__send__(belongs_to.klass.primary_key.to_sym))
90
+ end
91
+ end
92
+ end
93
+
94
+ def foreign_key_from(association)
95
+ if ActiveRecord::Reflection::AssociationReflection.method_defined? :foreign_key
96
+ # Support for Rails 3.1 and later
97
+ foreign_key = association.foreign_key
98
+ elsif ActiveRecord::Reflection::AssociationReflection.method_defined? :primary_key_name
99
+ # Support for Rails earlier than 3.1
100
+ foreign_key = association.primary_key_name
101
+ else
102
+ raise 'Unsupported version of ActiveRecord. Unable to identify the foreign key.'
103
+ end
104
+ foreign_key
105
+ end
106
+
107
+ def find_existing
108
+ existing_record = nil
109
+
110
+ begin
111
+ existing_record = @klass.find(@attributes[@klass.primary_key.to_s].content) if @attributes[@klass.primary_key.to_s].present?
112
+ rescue
113
+ existing_record = nil
114
+ end
115
+
116
+ existing_record
117
+ end
118
+
119
+ def save
120
+ commit_attributes
121
+ update_associations
122
+
123
+ if will_violate_association?
124
+ @record.save!
125
+ @record.attributes[@klass.primary_key.to_s]
126
+ end
127
+ end
128
+
129
+ def will_violate_association?
130
+ ok = true
131
+ if belongs_to.present? and belongs_to.associations.count > 0
132
+ if belongs_to.associations[@tag_name.pluralize.to_sym].macro == :has_one
133
+ existing = belongs_to.record.__send__(@tag_name)
134
+ if existing.present?
135
+ if @record.__send__(@klass.primary_key.to_s) != existing.__send__(@klass.primary_key.to_s)
136
+ ok = false
137
+ end
138
+ end
139
+ end
140
+ end
141
+
142
+ ok
143
+ end
144
+
145
+ def commit_attributes
146
+ @attributes.each do |key, a|
147
+ @record.__send__("#{a.name}=", a.content)
148
+ end
149
+ end
150
+ end
151
+ end
@@ -0,0 +1,159 @@
1
+ module DataActive
2
+ class Parser
3
+ attr_accessor :options
4
+ attr_reader :stack
5
+ attr_reader :first_element_name
6
+ attr_reader :root_klass
7
+
8
+ def initialize(first_element_name, options = [])
9
+ @options = options
10
+ @stack = []
11
+ @first_element_name = first_element_name
12
+ @started_parsing = false
13
+ @processed_entities = {}
14
+ @root_klass = nil
15
+ end
16
+
17
+ def options_include? (context, options)
18
+ ((@options & options).count.eql? options.count and context.eql? :all) ||
19
+ ((@options & options).count > 0 and context.eql? :any)
20
+ end
21
+
22
+ def begin(name)
23
+ if not @started_parsing and @first_element_name == name
24
+ @started_parsing = true
25
+ begin
26
+ @root_klass = Kernel.const_get(name.camelize)
27
+ rescue
28
+ raise "Unable to find class for '#{name}'"
29
+ end
30
+ end
31
+
32
+ process_element(name)
33
+
34
+ self
35
+ end
36
+
37
+ def process_element(name)
38
+ if @stack.last.nil?
39
+ @stack << DataActive::Entity.new(name, @options, !@started_parsing)
40
+ elsif @stack.last.class.name == 'DataActive::Entity'
41
+ process_entity_child(name)
42
+ elsif @stack.last.class.name == 'DataActive::Attribute' and not @stack.last.known
43
+ @stack << DataActive::Attribute.new(name, false)
44
+ end
45
+ end
46
+
47
+ def process_entity_child(name)
48
+ parent = @stack.last
49
+ if parent.has_association_with? name or parent.tag_name == name.pluralize
50
+ create_child_entity(name)
51
+ elsif parent.has_attribute? name
52
+ @stack << DataActive::Attribute.new(name)
53
+ elsif klass_exists? name
54
+ create_child_entity(name)
55
+ elsif parent.excluded
56
+ @stack << DataActive::Attribute.new(name, false)
57
+ else
58
+ raise "Unknown element '#{name}' for #{parent.klass.name}" if @options.include? :strict
59
+ @stack << DataActive::Attribute.new(name, false)
60
+ end
61
+ end
62
+
63
+ def klass_exists? name
64
+ is_klass = true
65
+
66
+ begin
67
+ klass = Kernel.const_get(name.camelize)
68
+ fail unless klass.ancestors.include? ActiveRecord::Base
69
+ rescue
70
+ is_klass = false
71
+ end
72
+
73
+ is_klass
74
+ end
75
+
76
+ def create_child_entity(name)
77
+ entity = DataActive::Entity.new(name, @options, !@started_parsing)
78
+
79
+ if @stack.last.tag_name == name.pluralize
80
+ entity.belongs_to = @stack.last.belongs_to
81
+ else
82
+ entity.belongs_to = @stack.last
83
+ end
84
+
85
+ @stack << entity
86
+ end
87
+
88
+ def content(value)
89
+ if @stack.last.class.name == 'DataActive::Entity'
90
+ raise "'#{@stack.last.tag_name}' contains text '#{value}'" if value.strip.length > 0
91
+ else
92
+ @stack.last.content = @stack.last.content.nil? ? value : @stack.last.content + value
93
+ end
94
+
95
+ self
96
+ end
97
+
98
+ def end(name)
99
+ case @stack.last.class.name
100
+ when 'DataActive::Entity'
101
+ end_entity(name)
102
+
103
+ when 'DataActive::Attribute'
104
+ end_attribute(name)
105
+
106
+ else
107
+ raise "Unhandled class '#{@stack.last.class.name}'"
108
+ end
109
+
110
+ self
111
+ end
112
+
113
+ def end_attribute(name)
114
+ raise "Mismatched closing tag '#{name}' when opening tag was #{@stack.last.name}" unless @stack.last.name.eql? name
115
+ attribute = @stack.pop()
116
+ @stack.last.attributes[attribute.name] = attribute if attribute.known
117
+ end
118
+
119
+ def end_entity(name)
120
+ raise "Mismatched closing tag '#{name}' when opening tag was #{@stack.last.tag_name}" unless @stack.last.tag_name.eql? name
121
+ entity = @stack.pop()
122
+ store_processed_entity_id(entity.klass, entity.commit()) if not entity.excluded
123
+ end
124
+
125
+ def destroy(klass = self.root_klass)
126
+ @destroyed ||= []
127
+ if options_include? :any, [:destroy, :sync]
128
+ klass.reflect_on_all_associations.each do |a|
129
+ if [:has_many, :has_many_and_belongs_to, :has_one].include? a.macro and @destroyed.exclude? a.klass.name
130
+ @destroyed << a.klass.name
131
+ destroy(a.klass)
132
+ end
133
+ end
134
+
135
+ destroy_records(klass)
136
+ @destroyed << klass.name
137
+ end
138
+ end
139
+
140
+ def destroy_records(klass)
141
+ ids = @processed_entities[klass.name.to_sym]
142
+ ids.nil? ? klass.destroy_all : klass.destroy_all([klass.primary_key.to_s + ' not in (?)', ids.collect])
143
+ end
144
+
145
+ def store_processed_entity_id(klass, id)
146
+ return if id.nil?
147
+
148
+ if @processed_entities[klass.name.to_sym].present?
149
+ @processed_entities[klass.name.to_sym] << id
150
+ else
151
+ @processed_entities[klass.name.to_sym] = [id]
152
+ end
153
+ end
154
+
155
+ def has_started_parsing?
156
+ @started_parsing
157
+ end
158
+ end
159
+ end
@@ -0,0 +1,29 @@
1
+ require 'nokogiri'
2
+
3
+ module DataActive
4
+ class SaxDocument < ::Nokogiri::XML::SAX::Document
5
+ def initialize(first_element_name, options = {})
6
+ @parser = DataActive::Parser.new(first_element_name, options)
7
+ end
8
+
9
+ def start_element(name, attr)
10
+ @parser.begin(name)
11
+ end
12
+
13
+ def end_element(name)
14
+ @parser.end(name)
15
+ end
16
+
17
+ def cdata_block(value)
18
+ @parser.content(value)
19
+ end
20
+
21
+ def characters(value)
22
+ @parser.content(value)
23
+ end
24
+
25
+ def end_document
26
+ @parser.destroy
27
+ end
28
+ end
29
+ end
@@ -1,3 +1,3 @@
1
1
  module DataActive
2
- VERSION = '0.0.6'
2
+ VERSION = '0.0.7'
3
3
  end
@@ -0,0 +1,11 @@
1
+ require 'spec_helper'
2
+ describe DataActive::Entity do
3
+ context 'context' do
4
+ let(:book_entity) { DataActive::Entity.new('book') }
5
+ it ('should have a class of Book') { book_entity.klass.name == 'Book' }
6
+ it ('should have an association with chapters') { book_entity.has_association_with? 'chapter' }
7
+ it ('should have an association with book_price') { book_entity.has_association_with? 'book_price' }
8
+ it ('should have an attribute named id') { book_entity.has_attribute? 'id' }
9
+ it ('should have an attribute named title') { book_entity.has_attribute? 'title' }
10
+ end
11
+ end
@@ -0,0 +1,173 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <books type="array">
3
+ <book>
4
+ <id type="integer">1</id>
5
+ <name>Book 1</name>
6
+ <book_price>
7
+ <id type="integer">1</id>
8
+ <sell>50.00</sell>
9
+ <educational>35.00</educational>
10
+ <cost>20.00</cost>
11
+ </book_price>
12
+ <chapters type="array">
13
+ <chapter>
14
+ <id type="integer">1</id>
15
+ <title>Chapter 1</title>
16
+ <introduction>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus purus nulla, condimentum vitae hendrerit nec, blandit et felis. Suspendisse vulputate mollis suscipit. Vivamus non libero quis urna gravida euismod quis in nisi. Morbi turpis orci, posuere nec ultrices ut, egestas ac purus. Morbi id pretium erat. In ullamcorper, ligula id porta pellentesque, sem turpis ultricies libero, non elementum ipsum neque at dui. Donec auctor nulla id mi dapibus id faucibus felis mollis. Curabitur imperdiet tristique nisi, consectetur molestie purus accumsan id. Curabitur lacinia diam et nisl iaculis eleifend. Quisque turpis elit, volutpat eget dapibus sed, egestas nec leo. Mauris dignissim tellus non lorem fringilla pharetra.</introduction>
17
+ <pages type="array">
18
+ <page>
19
+ <id type="integer">1</id>
20
+ <content>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus purus nulla, condimentum vitae hendrerit nec, blandit et felis. Suspendisse vulputate mollis suscipit. Vivamus non libero quis urna gravida euismod quis in nisi. Morbi turpis orci, posuere nec ultrices ut, egestas ac purus. Morbi id pretium erat. In ullamcorper, ligula id porta pellentesque, sem turpis ultricies libero, non elementum ipsum neque at dui. Donec auctor nulla id mi dapibus id faucibus felis mollis. Curabitur imperdiet tristique nisi, consectetur molestie purus accumsan id. Curabitur lacinia diam et nisl iaculis eleifend. Quisque turpis elit, volutpat eget dapibus sed, egestas nec leo. Mauris dignissim tellus non lorem fringilla pharetra.</content>
21
+ <number>1</number>
22
+ </page>
23
+ <page>
24
+ <id type="integer">2</id>
25
+ <content>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus purus nulla, condimentum vitae hendrerit nec, blandit et felis. Suspendisse vulputate mollis suscipit. Vivamus non libero quis urna gravida euismod quis in nisi. Morbi turpis orci, posuere nec ultrices ut, egestas ac purus. Morbi id pretium erat. In ullamcorper, ligula id porta pellentesque, sem turpis ultricies libero, non elementum ipsum neque at dui. Donec auctor nulla id mi dapibus id faucibus felis mollis. Curabitur imperdiet tristique nisi, consectetur molestie purus accumsan id. Curabitur lacinia diam et nisl iaculis eleifend. Quisque turpis elit, volutpat eget dapibus sed, egestas nec leo. Mauris dignissim tellus non lorem fringilla pharetra.</content>
26
+ <number>2</number>
27
+ </page>
28
+ <page>
29
+ <id type="integer">3</id>
30
+ <content>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus purus nulla, condimentum vitae hendrerit nec, blandit et felis. Suspendisse vulputate mollis suscipit. Vivamus non libero quis urna gravida euismod quis in nisi. Morbi turpis orci, posuere nec ultrices ut, egestas ac purus. Morbi id pretium erat. In ullamcorper, ligula id porta pellentesque, sem turpis ultricies libero, non elementum ipsum neque at dui. Donec auctor nulla id mi dapibus id faucibus felis mollis. Curabitur imperdiet tristique nisi, consectetur molestie purus accumsan id. Curabitur lacinia diam et nisl iaculis eleifend. Quisque turpis elit, volutpat eget dapibus sed, egestas nec leo. Mauris dignissim tellus non lorem fringilla pharetra.</content>
31
+ <number>3</number>
32
+ </page>
33
+ <page>
34
+ <id type="integer">4</id>
35
+ <content>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus purus nulla, condimentum vitae hendrerit nec, blandit et felis. Suspendisse vulputate mollis suscipit. Vivamus non libero quis urna gravida euismod quis in nisi. Morbi turpis orci, posuere nec ultrices ut, egestas ac purus. Morbi id pretium erat. In ullamcorper, ligula id porta pellentesque, sem turpis ultricies libero, non elementum ipsum neque at dui. Donec auctor nulla id mi dapibus id faucibus felis mollis. Curabitur imperdiet tristique nisi, consectetur molestie purus accumsan id. Curabitur lacinia diam et nisl iaculis eleifend. Quisque turpis elit, volutpat eget dapibus sed, egestas nec leo. Mauris dignissim tellus non lorem fringilla pharetra.</content>
36
+ <number>4</number>
37
+ </page>
38
+ </pages>
39
+ </chapter>
40
+ <chapter>
41
+ <id type="integer">2</id>
42
+ <title>Chapter 2</title>
43
+ <introduction>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus purus nulla, condimentum vitae hendrerit nec, blandit et felis. Suspendisse vulputate mollis suscipit. Vivamus non libero quis urna gravida euismod quis in nisi. Morbi turpis orci, posuere nec ultrices ut, egestas ac purus. Morbi id pretium erat. In ullamcorper, ligula id porta pellentesque, sem turpis ultricies libero, non elementum ipsum neque at dui. Donec auctor nulla id mi dapibus id faucibus felis mollis. Curabitur imperdiet tristique nisi, consectetur molestie purus accumsan id. Curabitur lacinia diam et nisl iaculis eleifend. Quisque turpis elit, volutpat eget dapibus sed, egestas nec leo. Mauris dignissim tellus non lorem fringilla pharetra.</introduction>
44
+ <pages type="array">
45
+ <page>
46
+ <id type="integer">5</id>
47
+ <content>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus purus nulla, condimentum vitae hendrerit nec, blandit et felis. Suspendisse vulputate mollis suscipit. Vivamus non libero quis urna gravida euismod quis in nisi. Morbi turpis orci, posuere nec ultrices ut, egestas ac purus. Morbi id pretium erat. In ullamcorper, ligula id porta pellentesque, sem turpis ultricies libero, non elementum ipsum neque at dui. Donec auctor nulla id mi dapibus id faucibus felis mollis. Curabitur imperdiet tristique nisi, consectetur molestie purus accumsan id. Curabitur lacinia diam et nisl iaculis eleifend. Quisque turpis elit, volutpat eget dapibus sed, egestas nec leo. Mauris dignissim tellus non lorem fringilla pharetra.</content>
48
+ <number>1</number>
49
+ </page>
50
+ <page>
51
+ <id type="integer">6</id>
52
+ <content>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus purus nulla, condimentum vitae hendrerit nec, blandit et felis. Suspendisse vulputate mollis suscipit. Vivamus non libero quis urna gravida euismod quis in nisi. Morbi turpis orci, posuere nec ultrices ut, egestas ac purus. Morbi id pretium erat. In ullamcorper, ligula id porta pellentesque, sem turpis ultricies libero, non elementum ipsum neque at dui. Donec auctor nulla id mi dapibus id faucibus felis mollis. Curabitur imperdiet tristique nisi, consectetur molestie purus accumsan id. Curabitur lacinia diam et nisl iaculis eleifend. Quisque turpis elit, volutpat eget dapibus sed, egestas nec leo. Mauris dignissim tellus non lorem fringilla pharetra.</content>
53
+ <number>2</number>
54
+ </page>
55
+ <page>
56
+ <id type="integer">7</id>
57
+ <content>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus purus nulla, condimentum vitae hendrerit nec, blandit et felis. Suspendisse vulputate mollis suscipit. Vivamus non libero quis urna gravida euismod quis in nisi. Morbi turpis orci, posuere nec ultrices ut, egestas ac purus. Morbi id pretium erat. In ullamcorper, ligula id porta pellentesque, sem turpis ultricies libero, non elementum ipsum neque at dui. Donec auctor nulla id mi dapibus id faucibus felis mollis. Curabitur imperdiet tristique nisi, consectetur molestie purus accumsan id. Curabitur lacinia diam et nisl iaculis eleifend. Quisque turpis elit, volutpat eget dapibus sed, egestas nec leo. Mauris dignissim tellus non lorem fringilla pharetra.</content>
58
+ <number>3</number>
59
+ </page>
60
+ <page>
61
+ <id type="integer">8</id>
62
+ <content>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus purus nulla, condimentum vitae hendrerit nec, blandit et felis. Suspendisse vulputate mollis suscipit. Vivamus non libero quis urna gravida euismod quis in nisi. Morbi turpis orci, posuere nec ultrices ut, egestas ac purus. Morbi id pretium erat. In ullamcorper, ligula id porta pellentesque, sem turpis ultricies libero, non elementum ipsum neque at dui. Donec auctor nulla id mi dapibus id faucibus felis mollis. Curabitur imperdiet tristique nisi, consectetur molestie purus accumsan id. Curabitur lacinia diam et nisl iaculis eleifend. Quisque turpis elit, volutpat eget dapibus sed, egestas nec leo. Mauris dignissim tellus non lorem fringilla pharetra.</content>
63
+ <number>4</number>
64
+ </page>
65
+ </pages>
66
+ </chapter>
67
+ <chapter>
68
+ <id type="integer">3</id>
69
+ <title>Chapter 3</title>
70
+ <introduction>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus purus nulla, condimentum vitae hendrerit nec, blandit et felis. Suspendisse vulputate mollis suscipit. Vivamus non libero quis urna gravida euismod quis in nisi. Morbi turpis orci, posuere nec ultrices ut, egestas ac purus. Morbi id pretium erat. In ullamcorper, ligula id porta pellentesque, sem turpis ultricies libero, non elementum ipsum neque at dui. Donec auctor nulla id mi dapibus id faucibus felis mollis. Curabitur imperdiet tristique nisi, consectetur molestie purus accumsan id. Curabitur lacinia diam et nisl iaculis eleifend. Quisque turpis elit, volutpat eget dapibus sed, egestas nec leo. Mauris dignissim tellus non lorem fringilla pharetra.</introduction>
71
+ <pages type="array">
72
+ <page>
73
+ <id type="integer">9</id>
74
+ <content>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus purus nulla, condimentum vitae hendrerit nec, blandit et felis. Suspendisse vulputate mollis suscipit. Vivamus non libero quis urna gravida euismod quis in nisi. Morbi turpis orci, posuere nec ultrices ut, egestas ac purus. Morbi id pretium erat. In ullamcorper, ligula id porta pellentesque, sem turpis ultricies libero, non elementum ipsum neque at dui. Donec auctor nulla id mi dapibus id faucibus felis mollis. Curabitur imperdiet tristique nisi, consectetur molestie purus accumsan id. Curabitur lacinia diam et nisl iaculis eleifend. Quisque turpis elit, volutpat eget dapibus sed, egestas nec leo. Mauris dignissim tellus non lorem fringilla pharetra.</content>
75
+ <number>1</number>
76
+ </page>
77
+ <page>
78
+ <id type="integer">10</id>
79
+ <content>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus purus nulla, condimentum vitae hendrerit nec, blandit et felis. Suspendisse vulputate mollis suscipit. Vivamus non libero quis urna gravida euismod quis in nisi. Morbi turpis orci, posuere nec ultrices ut, egestas ac purus. Morbi id pretium erat. In ullamcorper, ligula id porta pellentesque, sem turpis ultricies libero, non elementum ipsum neque at dui. Donec auctor nulla id mi dapibus id faucibus felis mollis. Curabitur imperdiet tristique nisi, consectetur molestie purus accumsan id. Curabitur lacinia diam et nisl iaculis eleifend. Quisque turpis elit, volutpat eget dapibus sed, egestas nec leo. Mauris dignissim tellus non lorem fringilla pharetra.</content>
80
+ <number>2</number>
81
+ </page>
82
+ <page>
83
+ <id type="integer">11</id>
84
+ <content>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus purus nulla, condimentum vitae hendrerit nec, blandit et felis. Suspendisse vulputate mollis suscipit. Vivamus non libero quis urna gravida euismod quis in nisi. Morbi turpis orci, posuere nec ultrices ut, egestas ac purus. Morbi id pretium erat. In ullamcorper, ligula id porta pellentesque, sem turpis ultricies libero, non elementum ipsum neque at dui. Donec auctor nulla id mi dapibus id faucibus felis mollis. Curabitur imperdiet tristique nisi, consectetur molestie purus accumsan id. Curabitur lacinia diam et nisl iaculis eleifend. Quisque turpis elit, volutpat eget dapibus sed, egestas nec leo. Mauris dignissim tellus non lorem fringilla pharetra.</content>
85
+ <number>3</number>
86
+ </page>
87
+ </pages>
88
+ </chapter>
89
+ </chapters>
90
+ </book>
91
+ <book>
92
+ <id type="integer">2</id>
93
+ <name>Book 1</name>
94
+ <chapters type="array">
95
+ <chapter>
96
+ <id type="integer">4</id>
97
+ <title>Chapter 1</title>
98
+ <introduction>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus purus nulla, condimentum vitae hendrerit nec, blandit et felis. Suspendisse vulputate mollis suscipit. Vivamus non libero quis urna gravida euismod quis in nisi. Morbi turpis orci, posuere nec ultrices ut, egestas ac purus. Morbi id pretium erat. In ullamcorper, ligula id porta pellentesque, sem turpis ultricies libero, non elementum ipsum neque at dui. Donec auctor nulla id mi dapibus id faucibus felis mollis. Curabitur imperdiet tristique nisi, consectetur molestie purus accumsan id. Curabitur lacinia diam et nisl iaculis eleifend. Quisque turpis elit, volutpat eget dapibus sed, egestas nec leo. Mauris dignissim tellus non lorem fringilla pharetra.</introduction>
99
+ <pages type="array">
100
+ <page>
101
+ <id type="integer">12</id>
102
+ <content>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus purus nulla, condimentum vitae hendrerit nec, blandit et felis. Suspendisse vulputate mollis suscipit. Vivamus non libero quis urna gravida euismod quis in nisi. Morbi turpis orci, posuere nec ultrices ut, egestas ac purus. Morbi id pretium erat. In ullamcorper, ligula id porta pellentesque, sem turpis ultricies libero, non elementum ipsum neque at dui. Donec auctor nulla id mi dapibus id faucibus felis mollis. Curabitur imperdiet tristique nisi, consectetur molestie purus accumsan id. Curabitur lacinia diam et nisl iaculis eleifend. Quisque turpis elit, volutpat eget dapibus sed, egestas nec leo. Mauris dignissim tellus non lorem fringilla pharetra.</content>
103
+ <number>1</number>
104
+ </page>
105
+ <page>
106
+ <id type="integer">13</id>
107
+ <content>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus purus nulla, condimentum vitae hendrerit nec, blandit et felis. Suspendisse vulputate mollis suscipit. Vivamus non libero quis urna gravida euismod quis in nisi. Morbi turpis orci, posuere nec ultrices ut, egestas ac purus. Morbi id pretium erat. In ullamcorper, ligula id porta pellentesque, sem turpis ultricies libero, non elementum ipsum neque at dui. Donec auctor nulla id mi dapibus id faucibus felis mollis. Curabitur imperdiet tristique nisi, consectetur molestie purus accumsan id. Curabitur lacinia diam et nisl iaculis eleifend. Quisque turpis elit, volutpat eget dapibus sed, egestas nec leo. Mauris dignissim tellus non lorem fringilla pharetra.</content>
108
+ <number>2</number>
109
+ </page>
110
+ <page>
111
+ <id type="integer">14</id>
112
+ <content>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus purus nulla, condimentum vitae hendrerit nec, blandit et felis. Suspendisse vulputate mollis suscipit. Vivamus non libero quis urna gravida euismod quis in nisi. Morbi turpis orci, posuere nec ultrices ut, egestas ac purus. Morbi id pretium erat. In ullamcorper, ligula id porta pellentesque, sem turpis ultricies libero, non elementum ipsum neque at dui. Donec auctor nulla id mi dapibus id faucibus felis mollis. Curabitur imperdiet tristique nisi, consectetur molestie purus accumsan id. Curabitur lacinia diam et nisl iaculis eleifend. Quisque turpis elit, volutpat eget dapibus sed, egestas nec leo. Mauris dignissim tellus non lorem fringilla pharetra.</content>
113
+ <number>3</number>
114
+ </page>
115
+ <page>
116
+ <id type="integer">15</id>
117
+ <content>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus purus nulla, condimentum vitae hendrerit nec, blandit et felis. Suspendisse vulputate mollis suscipit. Vivamus non libero quis urna gravida euismod quis in nisi. Morbi turpis orci, posuere nec ultrices ut, egestas ac purus. Morbi id pretium erat. In ullamcorper, ligula id porta pellentesque, sem turpis ultricies libero, non elementum ipsum neque at dui. Donec auctor nulla id mi dapibus id faucibus felis mollis. Curabitur imperdiet tristique nisi, consectetur molestie purus accumsan id. Curabitur lacinia diam et nisl iaculis eleifend. Quisque turpis elit, volutpat eget dapibus sed, egestas nec leo. Mauris dignissim tellus non lorem fringilla pharetra.</content>
118
+ <number>4</number>
119
+ </page>
120
+ </pages>
121
+ </chapter>
122
+ <chapter>
123
+ <id type="integer">5</id>
124
+ <title>Chapter 2</title>
125
+ <introduction>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus purus nulla, condimentum vitae hendrerit nec, blandit et felis. Suspendisse vulputate mollis suscipit. Vivamus non libero quis urna gravida euismod quis in nisi. Morbi turpis orci, posuere nec ultrices ut, egestas ac purus. Morbi id pretium erat. In ullamcorper, ligula id porta pellentesque, sem turpis ultricies libero, non elementum ipsum neque at dui. Donec auctor nulla id mi dapibus id faucibus felis mollis. Curabitur imperdiet tristique nisi, consectetur molestie purus accumsan id. Curabitur lacinia diam et nisl iaculis eleifend. Quisque turpis elit, volutpat eget dapibus sed, egestas nec leo. Mauris dignissim tellus non lorem fringilla pharetra.</introduction>
126
+ <pages type="array">
127
+ <page>
128
+ <id type="integer">16</id>
129
+ <content>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus purus nulla, condimentum vitae hendrerit nec, blandit et felis. Suspendisse vulputate mollis suscipit. Vivamus non libero quis urna gravida euismod quis in nisi. Morbi turpis orci, posuere nec ultrices ut, egestas ac purus. Morbi id pretium erat. In ullamcorper, ligula id porta pellentesque, sem turpis ultricies libero, non elementum ipsum neque at dui. Donec auctor nulla id mi dapibus id faucibus felis mollis. Curabitur imperdiet tristique nisi, consectetur molestie purus accumsan id. Curabitur lacinia diam et nisl iaculis eleifend. Quisque turpis elit, volutpat eget dapibus sed, egestas nec leo. Mauris dignissim tellus non lorem fringilla pharetra.</content>
130
+ <number>1</number>
131
+ </page>
132
+ <page>
133
+ <id type="integer">17</id>
134
+ <content>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus purus nulla, condimentum vitae hendrerit nec, blandit et felis. Suspendisse vulputate mollis suscipit. Vivamus non libero quis urna gravida euismod quis in nisi. Morbi turpis orci, posuere nec ultrices ut, egestas ac purus. Morbi id pretium erat. In ullamcorper, ligula id porta pellentesque, sem turpis ultricies libero, non elementum ipsum neque at dui. Donec auctor nulla id mi dapibus id faucibus felis mollis. Curabitur imperdiet tristique nisi, consectetur molestie purus accumsan id. Curabitur lacinia diam et nisl iaculis eleifend. Quisque turpis elit, volutpat eget dapibus sed, egestas nec leo. Mauris dignissim tellus non lorem fringilla pharetra.</content>
135
+ <number>2</number>
136
+ </page>
137
+ <page>
138
+ <id type="integer">18</id>
139
+ <content>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus purus nulla, condimentum vitae hendrerit nec, blandit et felis. Suspendisse vulputate mollis suscipit. Vivamus non libero quis urna gravida euismod quis in nisi. Morbi turpis orci, posuere nec ultrices ut, egestas ac purus. Morbi id pretium erat. In ullamcorper, ligula id porta pellentesque, sem turpis ultricies libero, non elementum ipsum neque at dui. Donec auctor nulla id mi dapibus id faucibus felis mollis. Curabitur imperdiet tristique nisi, consectetur molestie purus accumsan id. Curabitur lacinia diam et nisl iaculis eleifend. Quisque turpis elit, volutpat eget dapibus sed, egestas nec leo. Mauris dignissim tellus non lorem fringilla pharetra.</content>
140
+ <number>3</number>
141
+ </page>
142
+ <page>
143
+ <id type="integer">19</id>
144
+ <content>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus purus nulla, condimentum vitae hendrerit nec, blandit et felis. Suspendisse vulputate mollis suscipit. Vivamus non libero quis urna gravida euismod quis in nisi. Morbi turpis orci, posuere nec ultrices ut, egestas ac purus. Morbi id pretium erat. In ullamcorper, ligula id porta pellentesque, sem turpis ultricies libero, non elementum ipsum neque at dui. Donec auctor nulla id mi dapibus id faucibus felis mollis. Curabitur imperdiet tristique nisi, consectetur molestie purus accumsan id. Curabitur lacinia diam et nisl iaculis eleifend. Quisque turpis elit, volutpat eget dapibus sed, egestas nec leo. Mauris dignissim tellus non lorem fringilla pharetra.</content>
145
+ <number>4</number>
146
+ </page>
147
+ </pages>
148
+ </chapter>
149
+ <chapter>
150
+ <id type="integer">6</id>
151
+ <title>Chapter 3</title>
152
+ <introduction>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus purus nulla, condimentum vitae hendrerit nec, blandit et felis. Suspendisse vulputate mollis suscipit. Vivamus non libero quis urna gravida euismod quis in nisi. Morbi turpis orci, posuere nec ultrices ut, egestas ac purus. Morbi id pretium erat. In ullamcorper, ligula id porta pellentesque, sem turpis ultricies libero, non elementum ipsum neque at dui. Donec auctor nulla id mi dapibus id faucibus felis mollis. Curabitur imperdiet tristique nisi, consectetur molestie purus accumsan id. Curabitur lacinia diam et nisl iaculis eleifend. Quisque turpis elit, volutpat eget dapibus sed, egestas nec leo. Mauris dignissim tellus non lorem fringilla pharetra.</introduction>
153
+ <pages type="array">
154
+ <page>
155
+ <id type="integer">20</id>
156
+ <content>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus purus nulla, condimentum vitae hendrerit nec, blandit et felis. Suspendisse vulputate mollis suscipit. Vivamus non libero quis urna gravida euismod quis in nisi. Morbi turpis orci, posuere nec ultrices ut, egestas ac purus. Morbi id pretium erat. In ullamcorper, ligula id porta pellentesque, sem turpis ultricies libero, non elementum ipsum neque at dui. Donec auctor nulla id mi dapibus id faucibus felis mollis. Curabitur imperdiet tristique nisi, consectetur molestie purus accumsan id. Curabitur lacinia diam et nisl iaculis eleifend. Quisque turpis elit, volutpat eget dapibus sed, egestas nec leo. Mauris dignissim tellus non lorem fringilla pharetra.</content>
157
+ <number>1</number>
158
+ </page>
159
+ <page>
160
+ <id type="integer">21</id>
161
+ <content>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus purus nulla, condimentum vitae hendrerit nec, blandit et felis. Suspendisse vulputate mollis suscipit. Vivamus non libero quis urna gravida euismod quis in nisi. Morbi turpis orci, posuere nec ultrices ut, egestas ac purus. Morbi id pretium erat. In ullamcorper, ligula id porta pellentesque, sem turpis ultricies libero, non elementum ipsum neque at dui. Donec auctor nulla id mi dapibus id faucibus felis mollis. Curabitur imperdiet tristique nisi, consectetur molestie purus accumsan id. Curabitur lacinia diam et nisl iaculis eleifend. Quisque turpis elit, volutpat eget dapibus sed, egestas nec leo. Mauris dignissim tellus non lorem fringilla pharetra.</content>
162
+ <number>2</number>
163
+ </page>
164
+ <page>
165
+ <id type="integer">22</id>
166
+ <content>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus purus nulla, condimentum vitae hendrerit nec, blandit et felis. Suspendisse vulputate mollis suscipit. Vivamus non libero quis urna gravida euismod quis in nisi. Morbi turpis orci, posuere nec ultrices ut, egestas ac purus. Morbi id pretium erat. In ullamcorper, ligula id porta pellentesque, sem turpis ultricies libero, non elementum ipsum neque at dui. Donec auctor nulla id mi dapibus id faucibus felis mollis. Curabitur imperdiet tristique nisi, consectetur molestie purus accumsan id. Curabitur lacinia diam et nisl iaculis eleifend. Quisque turpis elit, volutpat eget dapibus sed, egestas nec leo. Mauris dignissim tellus non lorem fringilla pharetra.</content>
167
+ <number>3</number>
168
+ </page>
169
+ </pages>
170
+ </chapter>
171
+ </chapters>
172
+ </book>
173
+ </books>