xml_active 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +144 -77
- data/lib/xml_active.rb +231 -156
- data/lib/xml_active/version.rb +1 -1
- data/xml_active.gemspec +3 -2
- metadata +34 -8
data/README.rdoc
CHANGED
@@ -9,78 +9,147 @@ Rails has some really fantastic features for serialisation of ActiveRecord to XM
|
|
9
9
|
* Choose to combine update, create and remove actions
|
10
10
|
* Synchronise your database with an XML Document
|
11
11
|
|
12
|
+
This will be the final version for xml_active. The gem will start a new life as data_active as it envisaged that it will
|
13
|
+
support more than just XML. You can check out the new gem at https://github.com/michael-harrison/data_active
|
14
|
+
|
12
15
|
== The Background
|
13
16
|
|
14
17
|
== XML Documents
|
15
18
|
|
16
|
-
XML Active
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
19
|
+
XML Active supports specific XML document styles including those created by ActiveRecord and Microsoft Access. Following are soem samples of an XML documents that are supported.
|
20
|
+
|
21
|
+
Active Record Style
|
22
|
+
<books type="array">
|
23
|
+
<book>
|
24
|
+
<id type="Integer">4</id>
|
25
|
+
<name>Book 1</name>
|
26
|
+
</book>
|
27
|
+
<book>
|
28
|
+
<id type="integer">5</id>
|
29
|
+
<name>Book 1</name>
|
30
|
+
<chapters type="array">
|
31
|
+
<chapter>
|
32
|
+
<id type="integer">1</id>
|
33
|
+
<title>Chapter 1</title>
|
34
|
+
<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>
|
35
|
+
<pages type="array">
|
36
|
+
<page>
|
37
|
+
<id type="integer">1</id>
|
38
|
+
<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>
|
39
|
+
<number>1</number>
|
40
|
+
</page>
|
41
|
+
<page>
|
42
|
+
<id type="integer">2</id>
|
43
|
+
<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>
|
44
|
+
<number>2</number>
|
45
|
+
</page>
|
46
|
+
<page>
|
47
|
+
<id type="integer">3</id>
|
48
|
+
<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>
|
49
|
+
<number>3</number>
|
50
|
+
</page>
|
51
|
+
</pages>
|
52
|
+
</chapter>
|
53
|
+
<chapter>
|
54
|
+
<id type="integer">2</id>
|
55
|
+
<title>Chapter 2</title>
|
56
|
+
<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>
|
57
|
+
<pages type="array">
|
58
|
+
<page>
|
59
|
+
<id type="integer">5</id>
|
60
|
+
<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>
|
61
|
+
<number>1</number>
|
62
|
+
</page>
|
63
|
+
<page>
|
64
|
+
<id type="integer">6</id>
|
65
|
+
<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>
|
66
|
+
<number>2</number>
|
67
|
+
</page>
|
68
|
+
<page>
|
69
|
+
<id type="integer">7</id>
|
70
|
+
<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>
|
71
|
+
<number>3</number>
|
72
|
+
</page>
|
73
|
+
<page>
|
74
|
+
<id type="integer">8</id>
|
75
|
+
<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>
|
76
|
+
<number>4</number>
|
77
|
+
</page>
|
78
|
+
</pages>
|
79
|
+
</chapter>
|
80
|
+
</chapters>
|
81
|
+
</book>
|
82
|
+
<book>
|
83
|
+
<id type="integer">6</id>
|
84
|
+
<name>Book 1</name>
|
85
|
+
</book>
|
86
|
+
</books>
|
87
|
+
</code>
|
88
|
+
|
89
|
+
|
90
|
+
Microsoft Style
|
91
|
+
<dataroot xmlns:od="urn:schemas-microsoft-com:officedata" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
92
|
+
xsi:noNamespaceSchemaLocation="book.xsd" generated="2012-07-11T16:01:54">
|
93
|
+
<book>
|
94
|
+
<id >1</id>
|
95
|
+
<name>Book 1</name>
|
96
|
+
<book_price>
|
97
|
+
<id >1</id>
|
98
|
+
<sell>50.00</sell>
|
99
|
+
<educational>35.00</educational>
|
100
|
+
<cost>20.00</cost>
|
101
|
+
</book_price>
|
102
|
+
<chapter>
|
103
|
+
<id >1</id>
|
104
|
+
<title>Chapter 1</title>
|
105
|
+
<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>
|
106
|
+
<page>
|
107
|
+
<id >1</id>
|
108
|
+
<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>
|
109
|
+
<number>1</number>
|
110
|
+
</page>
|
111
|
+
<page>
|
112
|
+
<id >2</id>
|
113
|
+
<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>
|
114
|
+
<number>2</number>
|
115
|
+
</page>
|
116
|
+
<page>
|
117
|
+
<id >3</id>
|
118
|
+
<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>
|
119
|
+
<number>3</number>
|
120
|
+
</page>
|
121
|
+
</chapter>
|
122
|
+
</book>
|
123
|
+
<book>
|
124
|
+
<id >2</id>
|
125
|
+
<name>Book 12</name>
|
126
|
+
<chapter>
|
127
|
+
<id >4</id>
|
128
|
+
<title>Chapter 1</title>
|
129
|
+
<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>
|
130
|
+
<page>
|
131
|
+
<id >12</id>
|
132
|
+
<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>
|
133
|
+
<number>1</number>
|
134
|
+
</page>
|
135
|
+
<page>
|
136
|
+
<id >13</id>
|
137
|
+
<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>
|
138
|
+
<number>2</number>
|
139
|
+
</page>
|
140
|
+
<page>
|
141
|
+
<id >14</id>
|
142
|
+
<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>
|
143
|
+
<number>3</number>
|
144
|
+
</page>
|
145
|
+
<page>
|
146
|
+
<id >15</id>
|
147
|
+
<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>
|
148
|
+
<number>4</number>
|
149
|
+
</page>
|
150
|
+
</chapter>
|
151
|
+
</book>
|
152
|
+
</dataroot>
|
84
153
|
|
85
154
|
=== Database Schema
|
86
155
|
|
@@ -192,19 +261,17 @@ XML Active supports has_one association with all options and following arr the b
|
|
192
261
|
=== Versions
|
193
262
|
|
194
263
|
* 0.0.2 First Release
|
195
|
-
* 0.0.3 Addition of support for One to One associations
|
264
|
+
* 0.0.3 Addition of support for One to One associations, various bug fixes and testing in Rails 3.0.7, 3.1.0 and 3.2.2
|
265
|
+
* 0.0.4 Dropped support for 3.0.x and continued testing on 3.1.x and 3.2.x. Added support for Microsoft Office style XML
|
196
266
|
|
197
267
|
=== Future Features
|
198
268
|
|
199
|
-
|
200
|
-
|
201
|
-
* <b>Selective Upates</b> Specify a set of active record objects to effect and leave all the rest alone
|
202
|
-
* <b>Many to Many</b> Tested support for has_and_belongs_to_many
|
203
|
-
* <b>Polymorphic Associations</b> Tested support for these associations
|
269
|
+
This will be the final version for xml_active. The gem will start a new life as data_active as it envisaged that it will
|
270
|
+
support more than just XML. You can check out the new gem at https://github.com/michael-harrison/data_active
|
204
271
|
|
205
272
|
=== Testing
|
206
|
-
In order to perform testing I created
|
273
|
+
In order to perform testing I created a number of test applications which can be found in the test_apps folder. These
|
274
|
+
applications target specific versions of rails utilising cucumber for testing
|
207
275
|
|
208
|
-
At this stage
|
276
|
+
At this stage data_active has been tested with Rails 3.1.x and 3.2.x
|
209
277
|
|
210
|
-
This testing will be incorporated in the primary repo in the next major release
|
data/lib/xml_active.rb
CHANGED
@@ -14,209 +14,284 @@ module XmlActive
|
|
14
14
|
VALID_FROM_XML_OPTIONS = [:sync, :create, :update, :destroy]
|
15
15
|
|
16
16
|
module ClassMethods
|
17
|
-
def many_from_xml(
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
17
|
+
def many_from_xml(source_xml, options = [])
|
18
|
+
@data_active_options = options
|
19
|
+
many_from root_node_in source_xml
|
20
|
+
end
|
21
|
+
|
22
|
+
def many_from(current_node)
|
23
|
+
case
|
24
|
+
when self.name.pluralize.underscore.eql?(current_node.name.underscore)
|
25
|
+
many_from_rails_xml current_node
|
26
|
+
|
27
|
+
when (current_node.name.eql?('dataroot') \
|
28
|
+
and current_node.namespace_definitions.map { |ns| ns.href }.include?('urn:schemas-microsoft-com:officedata'))
|
29
|
+
# Identified as data generated from Microsoft Access
|
30
|
+
many_from_ms_xml current_node
|
31
|
+
|
32
|
+
when self.name.underscore.eql?(current_node.name.underscore)
|
33
|
+
raise "The supplied XML (#{current_node.name}) is a single instance of '#{self.name}'. Please use one_from_xml"
|
34
|
+
|
35
|
+
else
|
36
|
+
raise "The supplied XML (#{current_node.name}) cannot be mapped to this class (#{self.name})"
|
37
|
+
|
23
38
|
end
|
39
|
+
end
|
24
40
|
|
41
|
+
def many_from_ms_xml(current_node)
|
25
42
|
records = []
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
if
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
records[records.length] = record
|
35
|
-
end
|
43
|
+
recorded_ids = []
|
44
|
+
|
45
|
+
current_node.element_children.each do |node|
|
46
|
+
if self.name.underscore.eql?(node.name.underscore)
|
47
|
+
record = self.one_from_xml(node, @data_active_options)
|
48
|
+
if record
|
49
|
+
recorded_ids << record[primary_key.to_sym]
|
50
|
+
records << record
|
36
51
|
end
|
37
|
-
else
|
38
|
-
records[records.length] = self.one_from_xml current_node
|
39
52
|
end
|
53
|
+
end
|
40
54
|
|
55
|
+
remove_records_not_in recorded_ids
|
41
56
|
|
42
|
-
|
43
|
-
|
44
|
-
self.destroy_all [self.primary_key.to_s + " not in (?)", ids.collect]
|
45
|
-
end
|
46
|
-
elsif options.include?(:destroy)
|
47
|
-
if ids.length > 0
|
48
|
-
self.destroy_all [self.primary_key.to_s + " not in (?)", ids.collect]
|
49
|
-
else
|
50
|
-
self.destroy_all
|
51
|
-
end
|
52
|
-
end
|
57
|
+
records
|
58
|
+
end
|
53
59
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
60
|
+
def many_from_rails_xml(current_node)
|
61
|
+
records = []
|
62
|
+
recorded_ids = []
|
63
|
+
|
64
|
+
current_node.element_children.each do |node|
|
65
|
+
record = self.one_from_xml(node, @data_active_options)
|
66
|
+
if record
|
67
|
+
recorded_ids << record[primary_key.to_sym]
|
68
|
+
records << record
|
69
|
+
end
|
58
70
|
end
|
59
71
|
|
72
|
+
remove_records_not_in recorded_ids
|
73
|
+
|
60
74
|
records
|
61
75
|
end
|
62
76
|
|
63
|
-
def
|
64
|
-
if
|
65
|
-
|
66
|
-
|
77
|
+
def remove_records_not_in(recorded_ids)
|
78
|
+
if @data_active_options.include?(:sync)
|
79
|
+
if recorded_ids.length > 0
|
80
|
+
self.destroy_all [self.primary_key.to_s + " not in (?)", recorded_ids.collect]
|
81
|
+
end
|
82
|
+
elsif @data_active_options.include?(:destroy)
|
83
|
+
if recorded_ids.length > 0
|
84
|
+
self.destroy_all [self.primary_key.to_s + " not in (?)", recorded_ids.collect]
|
85
|
+
else
|
86
|
+
self.destroy_all
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def root_node_in(source_xml)
|
92
|
+
if source_xml.is_a?(String)
|
93
|
+
doc = Nokogiri::XML(source_xml)
|
94
|
+
doc.children.first
|
67
95
|
else
|
68
|
-
|
96
|
+
source_xml
|
69
97
|
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def one_from_xml(source_xml, options = [])
|
101
|
+
@data_active_options = options
|
102
|
+
|
103
|
+
current_node = root_node_in source_xml
|
70
104
|
|
71
|
-
if
|
105
|
+
if current_node.name.underscore.eql?(self.name.underscore)
|
72
106
|
# Load or create a new record
|
73
|
-
pk_value = 0
|
74
107
|
pk_node = current_node.xpath self.primary_key.to_s
|
75
|
-
if pk_node
|
76
|
-
begin
|
77
|
-
ar = find pk_node.text
|
78
|
-
pk_value = pk_node.text
|
79
|
-
rescue
|
80
|
-
# No record exists, create a new one
|
81
|
-
if options.include?(:sync) or options.include?(:create)
|
82
|
-
ar = self.new
|
83
|
-
else
|
84
|
-
# must have only have :destroy and/or :update so exit
|
85
|
-
return nil
|
86
|
-
end
|
87
|
-
end
|
88
|
-
else
|
89
|
-
# No primary key value, must be a new record
|
90
|
-
if options.include?(:sync) or options.include?(:create)
|
91
|
-
ar = self.new
|
92
|
-
else
|
93
|
-
# must have only have :destroy and/or :update so exit
|
94
|
-
return nil
|
95
|
-
end
|
96
|
-
end
|
97
108
|
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
# Check to see if xml contains elements for the association
|
112
|
-
if pk_value == 0
|
113
|
-
containers = current_node.xpath("//#{self.name.underscore}[#{self.primary_key}=#{pk_node.text}]/#{association.name}")
|
114
|
-
else
|
115
|
-
containers = current_node.xpath("//#{self.name.underscore}[#{self.primary_key}=#{pk_value}]/#{association.name}")
|
116
|
-
end
|
117
|
-
if containers.count > 0
|
118
|
-
container = containers[0]
|
119
|
-
klass = association.klass
|
109
|
+
active_record = find_record_based_on(pk_node)
|
110
|
+
|
111
|
+
|
112
|
+
unless active_record.nil?
|
113
|
+
# Check through associations and apply sync appropriately
|
114
|
+
self.reflect_on_all_associations.each do |association|
|
115
|
+
foreign_key = foreign_key_from(association)
|
116
|
+
klass = association.klass
|
117
|
+
|
118
|
+
case
|
119
|
+
when association.macro == :has_many, association.macro == :has_and_belongs_to_many
|
120
|
+
instances = instances_for association, :from => current_node, :for => active_record
|
121
|
+
|
120
122
|
child_ids = []
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
ar.__send__(container.name.underscore.to_sym) << new_record
|
123
|
+
instances.each do |instance|
|
124
|
+
new_record = klass.one_from_xml(instance, options)
|
125
|
+
if new_record != nil
|
126
|
+
child_ids << new_record[klass.primary_key]
|
127
|
+
active_record.__send__(klass.name.underscore.pluralize.to_sym) << new_record
|
127
128
|
end
|
128
129
|
end
|
129
130
|
|
130
|
-
|
131
|
-
if options.include?(:sync)
|
132
|
-
if child_ids.length > 0
|
133
|
-
klass.destroy_all [klass.primary_key.to_s + " not in (?) and #{foreign_key} = ?", child_ids.collect, pk_value]
|
134
|
-
end
|
135
|
-
elsif options.include?(:destroy)
|
131
|
+
unless active_record.new_record?
|
132
|
+
if options.include?(:sync) or options.include?(:destroy)
|
136
133
|
if child_ids.length > 0
|
137
|
-
klass.destroy_all [klass.primary_key.to_s + " not in (?) and #{foreign_key} = ?", child_ids.collect,
|
134
|
+
klass.destroy_all [klass.primary_key.to_s + " not in (?) and #{foreign_key} = ?", child_ids.collect, active_record.attributes[self.primary_key.to_s]]
|
138
135
|
else
|
139
136
|
klass.destroy_all
|
140
137
|
end
|
141
138
|
end
|
142
139
|
end
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
140
|
+
|
141
|
+
when association.macro == :has_one
|
142
|
+
pk_value = active_record.new_record? ? 0 : active_record.attributes[self.primary_key.to_s]
|
143
|
+
single_objects = current_node.xpath("//#{self.name.underscore}[#{self.primary_key}=#{pk_value}]/#{association.name}")
|
144
|
+
klass = association.klass
|
145
|
+
record = klass.where(foreign_key => active_record.attributes[self.primary_key.to_s]).all
|
146
|
+
if single_objects.count == 1
|
147
|
+
# Check to see if the already record exists
|
148
|
+
if record.count == 1
|
149
|
+
db_pk_value = record[0][klass.primary_key]
|
150
|
+
xml_pk_value = Integer(single_objects[0].element_children.xpath("//#{self.name.underscore}/#{klass.primary_key}").text)
|
151
|
+
|
152
|
+
if db_pk_value != xml_pk_value
|
153
|
+
# Different record in xml
|
154
|
+
if options.include?(:sync) or options.include?(:destroy)
|
155
|
+
# Delete the one in the database
|
156
|
+
klass.destroy(record[0][klass.primary_key])
|
157
|
+
end
|
160
158
|
end
|
159
|
+
elsif record.count > 1
|
160
|
+
raise "Too many records for one to one association in the database. Found #{record.count} records of '#{association.name}' for association with '#{self.name}'"
|
161
161
|
end
|
162
|
-
elsif record.count > 1
|
163
|
-
raise "Too many records for one to one association in the database. Found #{record.count} records of '#{association.name}' for association with '#{self.name}'"
|
164
|
-
end
|
165
162
|
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
163
|
+
if options.include?(:create) or options.include?(:update) or options.include?(:sync)
|
164
|
+
new_record = klass.one_from_xml(single_objects[0], options)
|
165
|
+
if new_record != nil
|
166
|
+
new_record[foreign_key.to_sym] = active_record[self.primary_key.to_s]
|
167
|
+
new_record.save!
|
168
|
+
end
|
169
|
+
end
|
170
|
+
elsif single_objects.count > 1
|
171
|
+
# There are more than one associations
|
172
|
+
raise "Too many records for one to one association in the provided XML. Found #{single_objects.count} records of '#{association.name}' for association with '#{self.name}'"
|
173
|
+
else
|
174
|
+
# There are no records in the XML
|
175
|
+
if record.count > 0 and options.include?(:sync) or options.include?(:destroy)
|
176
|
+
# Found some in the database: destroy then
|
177
|
+
klass.destroy_all("#{foreign_key} = #{active_record.attributes[self.primary_key.to_s]}")
|
171
178
|
end
|
172
179
|
end
|
173
|
-
elsif single_objects.count > 1
|
174
|
-
# There are more than one associations
|
175
|
-
raise "Too many records for one to one association in the provided XML. Found #{single_objects.count} records of '#{association.name}' for association with '#{self.name}'"
|
176
|
-
else
|
177
|
-
# There are no records in the XML
|
178
|
-
if record.count > 0 and options.include?(:sync) or options.include?(:destroy)
|
179
|
-
# Found some in the database: destroy then
|
180
|
-
klass.destroy_all("#{foreign_key} = #{pk_value}")
|
181
|
-
end
|
182
|
-
end
|
183
180
|
|
184
|
-
|
181
|
+
when association.macro == :belongs_to
|
185
182
|
|
186
|
-
|
187
|
-
|
183
|
+
else
|
184
|
+
raise "unsupported association #{association.macro} for #{association.name } on #{self.name}"
|
185
|
+
end
|
188
186
|
end
|
189
|
-
end
|
190
187
|
|
191
|
-
if options.include? :update or options.include? :sync or options.include? :create
|
192
188
|
# Process the attributes
|
193
|
-
|
194
|
-
|
195
|
-
|
189
|
+
if options.include? :update or options.include? :sync or options.include? :create
|
190
|
+
assign_attributes_from current_node, :to => active_record
|
191
|
+
end
|
196
192
|
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
193
|
+
# Save the record
|
194
|
+
if options.include? :sync
|
195
|
+
# Doing complete synchronisation with XML
|
196
|
+
active_record.save
|
197
|
+
elsif options.include?(:create) and active_record.new_record?
|
198
|
+
active_record.save
|
199
|
+
elsif options.include?(:update) and not active_record.new_record?
|
200
|
+
active_record.save
|
204
201
|
end
|
205
202
|
end
|
206
203
|
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
204
|
+
active_record
|
205
|
+
else
|
206
|
+
raise "The supplied XML (#{current_node.name}) cannot be mapped to this class (#{self.name})"
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
def foreign_key_from(association)
|
211
|
+
if ActiveRecord::Reflection::AssociationReflection.method_defined? :foreign_key
|
212
|
+
# Support for Rails 3.1 and later
|
213
|
+
foreign_key = association.foreign_key
|
214
|
+
elsif ActiveRecord::Reflection::AssociationReflection.method_defined? :primary_key_name
|
215
|
+
# Support for Rails earlier than 3.1
|
216
|
+
foreign_key = association.primary_key_name
|
217
|
+
else
|
218
|
+
raise "Unsupported version of ActiveRecord. Unable to identify the foreign key."
|
219
|
+
end
|
220
|
+
foreign_key
|
221
|
+
end
|
222
|
+
|
223
|
+
def instances_for(association, options)
|
224
|
+
current_node = options[:from]
|
225
|
+
active_record = options[:for]
|
226
|
+
|
227
|
+
# Attempt to find instances which are in the following format
|
228
|
+
# <books>
|
229
|
+
# <book>
|
230
|
+
# ...
|
231
|
+
# </book>
|
232
|
+
# <book>
|
233
|
+
# ...
|
234
|
+
# </book>
|
235
|
+
# </books>
|
236
|
+
if active_record.new_record?
|
237
|
+
results = current_node.xpath("//#{self.name.underscore}[#{self.primary_key}=#{current_node.xpath(self.primary_key.to_s).text}]/#{association.name}")
|
238
|
+
else
|
239
|
+
results = current_node.xpath("//#{self.name.underscore}[#{self.primary_key}=#{active_record.attributes[self.primary_key.to_s]}]/#{association.name}")
|
240
|
+
end
|
241
|
+
|
242
|
+
|
243
|
+
if results.count.eql? 0
|
244
|
+
# Attempt to find instances which are in the following format
|
245
|
+
# <book>
|
246
|
+
# ...
|
247
|
+
# </book>
|
248
|
+
if active_record.new_record?
|
249
|
+
results = current_node.xpath("//#{self.name.underscore}[#{self.primary_key}=#{current_node.xpath(self.primary_key.to_s).text}]/#{association.name.to_s.singularize}")
|
250
|
+
else
|
251
|
+
results = current_node.xpath("//#{self.name.underscore}[#{self.primary_key}=#{active_record.attributes[self.primary_key.to_s]}]/#{association.name.to_s.singularize}")
|
252
|
+
end
|
253
|
+
else
|
254
|
+
results = results.first.element_children
|
255
|
+
end
|
256
|
+
|
257
|
+
results
|
258
|
+
end
|
259
|
+
|
260
|
+
def assign_attributes_from(current_node, options)
|
261
|
+
record = options[:to]
|
262
|
+
|
263
|
+
record.attributes.each do |name, value|
|
264
|
+
attribute_nodes = current_node.xpath name.to_s
|
265
|
+
if attribute_nodes.count == 1
|
266
|
+
if attribute_nodes[0].attributes['nil'].try(:value)
|
267
|
+
record[name] = nil
|
268
|
+
else
|
269
|
+
record[name] = attribute_nodes[0].text
|
270
|
+
end
|
271
|
+
elsif attribute_nodes.count > 1
|
272
|
+
raise "Found duplicate elements in xml for active record attribute '#{name}'"
|
214
273
|
end
|
274
|
+
end
|
275
|
+
end
|
215
276
|
|
216
|
-
|
277
|
+
def find_record_based_on(pk_node)
|
278
|
+
ar = nil
|
279
|
+
if pk_node
|
280
|
+
begin
|
281
|
+
ar = find pk_node.text
|
282
|
+
rescue
|
283
|
+
# No record exists, create a new one
|
284
|
+
if @data_active_options.include?(:sync) or @data_active_options.include?(:create)
|
285
|
+
ar = self.new
|
286
|
+
end
|
287
|
+
end
|
217
288
|
else
|
218
|
-
|
289
|
+
# No primary key value, must be a new record
|
290
|
+
if @data_active_options.include?(:sync) or @data_active_options.include?(:create)
|
291
|
+
ar = self.new
|
292
|
+
end
|
219
293
|
end
|
294
|
+
ar
|
220
295
|
end
|
221
296
|
|
222
297
|
def xml_node_matches_class(xml_node)
|
data/lib/xml_active/version.rb
CHANGED
data/xml_active.gemspec
CHANGED
@@ -6,7 +6,7 @@ Gem::Specification.new do |s|
|
|
6
6
|
s.name = "xml_active"
|
7
7
|
s.version = XmlActive::VERSION
|
8
8
|
s.authors = ["Michael Harrison"]
|
9
|
-
s.email =
|
9
|
+
s.email = %w(michael@focalpause.com)
|
10
10
|
s.homepage = "https://github.com/michael-harrison/xml_active"
|
11
11
|
s.summary = "xml_active #{s.version}"
|
12
12
|
s.description = %q{XML Active is an extension of ActiveRecord that provides features to synchronise an ActiveRecord Model with a supplied XML document}
|
@@ -14,12 +14,13 @@ Gem::Specification.new do |s|
|
|
14
14
|
s.rubyforge_project = "xml_active"
|
15
15
|
|
16
16
|
s.add_dependency 'nokogiri'
|
17
|
+
s.add_dependency 'rails'
|
17
18
|
s.add_development_dependency 'rake'
|
18
19
|
|
19
20
|
s.files = `git ls-files`.split("\n")
|
20
21
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
21
22
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
22
|
-
s.require_paths =
|
23
|
+
s.require_paths = %w(lib)
|
23
24
|
|
24
25
|
# specify any dependencies here; for example:
|
25
26
|
# s.add_development_dependency "rspec"
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: xml_active
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-07-12 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: nokogiri
|
16
|
-
requirement:
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,31 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements:
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rails
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
25
46
|
- !ruby/object:Gem::Dependency
|
26
47
|
name: rake
|
27
|
-
requirement:
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
28
49
|
none: false
|
29
50
|
requirements:
|
30
51
|
- - ! '>='
|
@@ -32,7 +53,12 @@ dependencies:
|
|
32
53
|
version: '0'
|
33
54
|
type: :development
|
34
55
|
prerelease: false
|
35
|
-
version_requirements:
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
36
62
|
description: XML Active is an extension of ActiveRecord that provides features to
|
37
63
|
synchronise an ActiveRecord Model with a supplied XML document
|
38
64
|
email:
|
@@ -68,8 +94,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
68
94
|
version: '0'
|
69
95
|
requirements: []
|
70
96
|
rubyforge_project: xml_active
|
71
|
-
rubygems_version: 1.8.
|
97
|
+
rubygems_version: 1.8.22
|
72
98
|
signing_key:
|
73
99
|
specification_version: 3
|
74
|
-
summary: xml_active 0.0.
|
100
|
+
summary: xml_active 0.0.4
|
75
101
|
test_files: []
|