data_active 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (184) hide show
  1. data/.gitignore +35 -0
  2. data/.rvmrc +1 -0
  3. data/Gemfile +4 -0
  4. data/README.rdoc +275 -0
  5. data/Rakefile +2 -0
  6. data/data_active.gemspec +28 -0
  7. data/features/remove_records_missing_in_xml.feature +30 -0
  8. data/features/step_definitions/remove_records_missing_in_xml.rb +85 -0
  9. data/features/step_definitions/step_helper.rb +15 -0
  10. data/features/step_definitions/sync_books_with_xml.rb +224 -0
  11. data/features/step_definitions/update_database_with_xml.rb +158 -0
  12. data/features/step_definitions/web_steps.rb +211 -0
  13. data/features/support/env.rb +50 -0
  14. data/features/support/fixtures/fresh/book_prices.yml +6 -0
  15. data/features/support/fixtures/fresh/books.yml +7 -0
  16. data/features/support/fixtures/fresh/chapters.yml +35 -0
  17. data/features/support/fixtures/fresh/pages.yml +149 -0
  18. data/features/support/fixtures/no_matching_records/book_prices.yml +6 -0
  19. data/features/support/fixtures/no_matching_records/books.yml +7 -0
  20. data/features/support/fixtures/no_matching_records/chapters.yml +35 -0
  21. data/features/support/fixtures/no_matching_records/pages.yml +149 -0
  22. data/features/support/fixtures/without_chapters/books.yml +7 -0
  23. data/features/support/fixtures/without_one_to_one/books.yml +7 -0
  24. data/features/support/fixtures/without_one_to_one/chapters.yml +35 -0
  25. data/features/support/fixtures/without_one_to_one/pages.yml +149 -0
  26. data/features/support/fixtures/xml/books_changed.xml +173 -0
  27. data/features/support/fixtures/xml/books_fresh.xml +173 -0
  28. data/features/support/fixtures/xml/books_one_to_one_changed.xml +173 -0
  29. data/features/support/fixtures/xml/books_one_to_one_mismatch.xml +173 -0
  30. data/features/support/fixtures/xml/books_one_to_one_removed.xml +167 -0
  31. data/features/support/fixtures/xml/books_simple.xml +15 -0
  32. data/features/support/fixtures/xml/books_with_changed_chapters.xml +40 -0
  33. data/features/support/fixtures/xml/books_with_chapters.xml +45 -0
  34. data/features/support/fixtures/xml/books_with_many_one_to_one_records.xml +179 -0
  35. data/features/support/fixtures/xml/books_with_mismatched_chapters.xml +173 -0
  36. data/features/support/fixtures/xml/books_with_moved_chapters.xml +173 -0
  37. data/features/support/fixtures/xml/books_with_one_to_one.xml +167 -0
  38. data/features/support/fixtures/xml/books_with_removed_chapters.xml +113 -0
  39. data/features/support/fixtures/xml/books_without_chapters.xml +11 -0
  40. data/features/support/fixtures/xml/books_without_one_to_one.xml +167 -0
  41. data/features/support/fixtures/xml/ms_access/book.xml +201 -0
  42. data/features/support/fixtures/xml/ms_access/book.xsd +425 -0
  43. data/features/support/fixtures/xml/ms_access/books_changed.xml +158 -0
  44. data/features/support/fixtures/xml/ms_access/books_fresh.xml +173 -0
  45. data/features/support/fixtures/xml/ms_access/books_one_to_one_changed.xml +173 -0
  46. data/features/support/fixtures/xml/ms_access/books_one_to_one_mismatch.xml +173 -0
  47. data/features/support/fixtures/xml/ms_access/books_one_to_one_removed.xml +167 -0
  48. data/features/support/fixtures/xml/ms_access/books_simple.xml +15 -0
  49. data/features/support/fixtures/xml/ms_access/books_with_changed_chapters.xml +40 -0
  50. data/features/support/fixtures/xml/ms_access/books_with_chapters.xml +45 -0
  51. data/features/support/fixtures/xml/ms_access/books_with_many_one_to_one_records.xml +179 -0
  52. data/features/support/fixtures/xml/ms_access/books_with_mismatched_chapters.xml +173 -0
  53. data/features/support/fixtures/xml/ms_access/books_with_moved_chapters.xml +173 -0
  54. data/features/support/fixtures/xml/ms_access/books_with_one_to_one.xml +167 -0
  55. data/features/support/fixtures/xml/ms_access/books_with_removed_chapters.xml +113 -0
  56. data/features/support/fixtures/xml/ms_access/books_without_chapters.xml +11 -0
  57. data/features/support/fixtures/xml/ms_access/books_without_one_to_one.xml +167 -0
  58. data/features/support/paths.rb +33 -0
  59. data/features/support/selectors.rb +39 -0
  60. data/features/sync_database_with_xml.feature +34 -0
  61. data/features/sync_one_to_many.feature +34 -0
  62. data/features/sync_one_to_one.feature +33 -0
  63. data/features/update_database_with_xml.feature +30 -0
  64. data/lib/data_active.rb +309 -0
  65. data/lib/data_active/version.rb +3 -0
  66. data/test_apps/book_store_rails_31x/.gitignore +15 -0
  67. data/test_apps/book_store_rails_31x/.rvmrc +1 -0
  68. data/test_apps/book_store_rails_31x/Gemfile +53 -0
  69. data/test_apps/book_store_rails_31x/README +261 -0
  70. data/test_apps/book_store_rails_31x/Rakefile +7 -0
  71. data/test_apps/book_store_rails_31x/app/assets/images/rails.png +0 -0
  72. data/test_apps/book_store_rails_31x/app/assets/javascripts/application.js +9 -0
  73. data/test_apps/book_store_rails_31x/app/assets/stylesheets/application.css +7 -0
  74. data/test_apps/book_store_rails_31x/app/controllers/application_controller.rb +3 -0
  75. data/test_apps/book_store_rails_31x/app/helpers/application_helper.rb +2 -0
  76. data/test_apps/book_store_rails_31x/app/mailers/.gitkeep +0 -0
  77. data/test_apps/book_store_rails_31x/app/models/.gitkeep +0 -0
  78. data/test_apps/book_store_rails_31x/app/models/book.rb +4 -0
  79. data/test_apps/book_store_rails_31x/app/models/book_price.rb +3 -0
  80. data/test_apps/book_store_rails_31x/app/models/chapter.rb +4 -0
  81. data/test_apps/book_store_rails_31x/app/models/page.rb +3 -0
  82. data/test_apps/book_store_rails_31x/app/views/layouts/application.html.erb +14 -0
  83. data/test_apps/book_store_rails_31x/config.ru +4 -0
  84. data/test_apps/book_store_rails_31x/config/application.rb +48 -0
  85. data/test_apps/book_store_rails_31x/config/boot.rb +6 -0
  86. data/test_apps/book_store_rails_31x/config/database.yml +28 -0
  87. data/test_apps/book_store_rails_31x/config/environment.rb +5 -0
  88. data/test_apps/book_store_rails_31x/config/environments/development.rb +30 -0
  89. data/test_apps/book_store_rails_31x/config/environments/production.rb +60 -0
  90. data/test_apps/book_store_rails_31x/config/environments/test.rb +39 -0
  91. data/test_apps/book_store_rails_31x/config/initializers/backtrace_silencers.rb +7 -0
  92. data/test_apps/book_store_rails_31x/config/initializers/inflections.rb +10 -0
  93. data/test_apps/book_store_rails_31x/config/initializers/mime_types.rb +5 -0
  94. data/test_apps/book_store_rails_31x/config/initializers/secret_token.rb +7 -0
  95. data/test_apps/book_store_rails_31x/config/initializers/session_store.rb +8 -0
  96. data/test_apps/book_store_rails_31x/config/initializers/wrap_parameters.rb +14 -0
  97. data/test_apps/book_store_rails_31x/config/locales/en.yml +5 -0
  98. data/test_apps/book_store_rails_31x/config/routes.rb +58 -0
  99. data/test_apps/book_store_rails_31x/db/migrate/20120422053943_create_books.rb +9 -0
  100. data/test_apps/book_store_rails_31x/db/migrate/20120422054008_create_chapters.rb +11 -0
  101. data/test_apps/book_store_rails_31x/db/migrate/20120422054033_create_pages.rb +11 -0
  102. data/test_apps/book_store_rails_31x/db/migrate/20120422054123_create_book_prices.rb +12 -0
  103. data/test_apps/book_store_rails_31x/db/schema.rb +47 -0
  104. data/test_apps/book_store_rails_31x/db/seeds.rb +7 -0
  105. data/test_apps/book_store_rails_31x/lib/assets/.gitkeep +0 -0
  106. data/test_apps/book_store_rails_31x/lib/tasks/.gitkeep +0 -0
  107. data/test_apps/book_store_rails_31x/log/.gitkeep +0 -0
  108. data/test_apps/book_store_rails_31x/public/404.html +26 -0
  109. data/test_apps/book_store_rails_31x/public/422.html +26 -0
  110. data/test_apps/book_store_rails_31x/public/500.html +26 -0
  111. data/test_apps/book_store_rails_31x/public/favicon.ico +0 -0
  112. data/test_apps/book_store_rails_31x/public/index.html +241 -0
  113. data/test_apps/book_store_rails_31x/public/robots.txt +5 -0
  114. data/test_apps/book_store_rails_31x/script/rails +6 -0
  115. data/test_apps/book_store_rails_31x/test/fixtures/.gitkeep +0 -0
  116. data/test_apps/book_store_rails_31x/test/functional/.gitkeep +0 -0
  117. data/test_apps/book_store_rails_31x/test/integration/.gitkeep +0 -0
  118. data/test_apps/book_store_rails_31x/test/performance/browsing_test.rb +12 -0
  119. data/test_apps/book_store_rails_31x/test/test_helper.rb +13 -0
  120. data/test_apps/book_store_rails_31x/test/unit/.gitkeep +0 -0
  121. data/test_apps/book_store_rails_31x/vendor/assets/stylesheets/.gitkeep +0 -0
  122. data/test_apps/book_store_rails_31x/vendor/plugins/.gitkeep +0 -0
  123. data/test_apps/book_store_rails_32x/.gitignore +15 -0
  124. data/test_apps/book_store_rails_32x/.rvmrc +1 -0
  125. data/test_apps/book_store_rails_32x/Gemfile +47 -0
  126. data/test_apps/book_store_rails_32x/README.rdoc +261 -0
  127. data/test_apps/book_store_rails_32x/Rakefile +14 -0
  128. data/test_apps/book_store_rails_32x/app/assets/images/rails.png +0 -0
  129. data/test_apps/book_store_rails_32x/app/assets/javascripts/application.js +15 -0
  130. data/test_apps/book_store_rails_32x/app/assets/stylesheets/application.css +13 -0
  131. data/test_apps/book_store_rails_32x/app/controllers/application_controller.rb +3 -0
  132. data/test_apps/book_store_rails_32x/app/helpers/application_helper.rb +2 -0
  133. data/test_apps/book_store_rails_32x/app/mailers/.gitkeep +0 -0
  134. data/test_apps/book_store_rails_32x/app/models/.gitkeep +0 -0
  135. data/test_apps/book_store_rails_32x/app/models/book.rb +4 -0
  136. data/test_apps/book_store_rails_32x/app/models/book_price.rb +3 -0
  137. data/test_apps/book_store_rails_32x/app/models/chapter.rb +4 -0
  138. data/test_apps/book_store_rails_32x/app/models/page.rb +3 -0
  139. data/test_apps/book_store_rails_32x/app/views/layouts/application.html.erb +14 -0
  140. data/test_apps/book_store_rails_32x/config.ru +4 -0
  141. data/test_apps/book_store_rails_32x/config/application.rb +59 -0
  142. data/test_apps/book_store_rails_32x/config/boot.rb +6 -0
  143. data/test_apps/book_store_rails_32x/config/cucumber.yml +8 -0
  144. data/test_apps/book_store_rails_32x/config/database.yml +28 -0
  145. data/test_apps/book_store_rails_32x/config/environment.rb +5 -0
  146. data/test_apps/book_store_rails_32x/config/environments/development.rb +37 -0
  147. data/test_apps/book_store_rails_32x/config/environments/production.rb +67 -0
  148. data/test_apps/book_store_rails_32x/config/environments/test.rb +37 -0
  149. data/test_apps/book_store_rails_32x/config/initializers/backtrace_silencers.rb +7 -0
  150. data/test_apps/book_store_rails_32x/config/initializers/inflections.rb +15 -0
  151. data/test_apps/book_store_rails_32x/config/initializers/mime_types.rb +5 -0
  152. data/test_apps/book_store_rails_32x/config/initializers/secret_token.rb +7 -0
  153. data/test_apps/book_store_rails_32x/config/initializers/session_store.rb +8 -0
  154. data/test_apps/book_store_rails_32x/config/initializers/wrap_parameters.rb +14 -0
  155. data/test_apps/book_store_rails_32x/config/locales/en.yml +5 -0
  156. data/test_apps/book_store_rails_32x/config/routes.rb +58 -0
  157. data/test_apps/book_store_rails_32x/db/migrate/20120422043253_create_books.rb +9 -0
  158. data/test_apps/book_store_rails_32x/db/migrate/20120422043434_create_chapters.rb +11 -0
  159. data/test_apps/book_store_rails_32x/db/migrate/20120422043553_create_pages.rb +11 -0
  160. data/test_apps/book_store_rails_32x/db/migrate/20120422043700_create_book_prices.rb +12 -0
  161. data/test_apps/book_store_rails_32x/db/schema.rb +47 -0
  162. data/test_apps/book_store_rails_32x/db/seeds.rb +7 -0
  163. data/test_apps/book_store_rails_32x/lib/assets/.gitkeep +0 -0
  164. data/test_apps/book_store_rails_32x/lib/tasks/.gitkeep +0 -0
  165. data/test_apps/book_store_rails_32x/lib/tasks/cucumber.rake +65 -0
  166. data/test_apps/book_store_rails_32x/log/.gitkeep +0 -0
  167. data/test_apps/book_store_rails_32x/public/404.html +26 -0
  168. data/test_apps/book_store_rails_32x/public/422.html +26 -0
  169. data/test_apps/book_store_rails_32x/public/500.html +25 -0
  170. data/test_apps/book_store_rails_32x/public/favicon.ico +0 -0
  171. data/test_apps/book_store_rails_32x/public/index.html +241 -0
  172. data/test_apps/book_store_rails_32x/public/robots.txt +5 -0
  173. data/test_apps/book_store_rails_32x/script/cucumber +10 -0
  174. data/test_apps/book_store_rails_32x/script/rails +6 -0
  175. data/test_apps/book_store_rails_32x/test/fixtures/.gitkeep +0 -0
  176. data/test_apps/book_store_rails_32x/test/functional/.gitkeep +0 -0
  177. data/test_apps/book_store_rails_32x/test/integration/.gitkeep +0 -0
  178. data/test_apps/book_store_rails_32x/test/performance/browsing_test.rb +12 -0
  179. data/test_apps/book_store_rails_32x/test/test_helper.rb +13 -0
  180. data/test_apps/book_store_rails_32x/test/unit/.gitkeep +0 -0
  181. data/test_apps/book_store_rails_32x/vendor/assets/javascripts/.gitkeep +0 -0
  182. data/test_apps/book_store_rails_32x/vendor/assets/stylesheets/.gitkeep +0 -0
  183. data/test_apps/book_store_rails_32x/vendor/plugins/.gitkeep +0 -0
  184. metadata +452 -0
data/.gitignore ADDED
@@ -0,0 +1,35 @@
1
+ *.gem
2
+ Gemfile.lock
3
+ pkg/*
4
+
5
+ ## MAC OS
6
+ .DS_Store
7
+
8
+ ## RubyMine
9
+ .idea
10
+
11
+ ## TEXTMATE
12
+ *.tmproj
13
+ tmtags
14
+
15
+ ## EMACS
16
+ *~
17
+ \#*
18
+ .\#*
19
+
20
+ ## VIM
21
+ *.swp
22
+
23
+ ## PROJECT::GENERAL
24
+ coverage
25
+ rdoc
26
+ pkg
27
+
28
+ ## PROJECT::SPECIFIC
29
+ tmp
30
+ .bundle
31
+ doc
32
+
33
+ ## Logging
34
+ *.log
35
+
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm ruby-1.9.3-p125@data_active --create
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in data_active.gemspec
4
+ gemspec
data/README.rdoc ADDED
@@ -0,0 +1,275 @@
1
+ = Data Active
2
+
3
+ Rails has some really fantastic features for serialisation of ActiveRecord to XML however going back the other way is not so fantastic. Data Active aims to provide the import of XML into your database while utilising the strengths of ActiveRecord. It extends ActiveRecord allowing it to seamlessly work in with your model. Here are the features provided by Data Active:
4
+
5
+ * Updating of records based on an XML document containing elements matching primary key
6
+ * Creation of records based on elements in an XML
7
+ * Removal of records based on an XML document containing elements matching primary key
8
+ * Choose to only update, only create or only remove records
9
+ * Choose to combine update, create and remove actions
10
+ * Synchronise your database with an XML Document
11
+
12
+ == The Background
13
+
14
+ == XML Documents
15
+
16
+ Data 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.
17
+
18
+ Active Record Style
19
+ <books type="array">
20
+ <book>
21
+ <id type="Integer">4</id>
22
+ <name>Book 1</name>
23
+ </book>
24
+ <book>
25
+ <id type="integer">5</id>
26
+ <name>Book 1</name>
27
+ <chapters type="array">
28
+ <chapter>
29
+ <id type="integer">1</id>
30
+ <title>Chapter 1</title>
31
+ <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>
32
+ <pages type="array">
33
+ <page>
34
+ <id type="integer">1</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>1</number>
37
+ </page>
38
+ <page>
39
+ <id type="integer">2</id>
40
+ <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>
41
+ <number>2</number>
42
+ </page>
43
+ <page>
44
+ <id type="integer">3</id>
45
+ <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>
46
+ <number>3</number>
47
+ </page>
48
+ </pages>
49
+ </chapter>
50
+ <chapter>
51
+ <id type="integer">2</id>
52
+ <title>Chapter 2</title>
53
+ <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>
54
+ <pages type="array">
55
+ <page>
56
+ <id type="integer">5</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>1</number>
59
+ </page>
60
+ <page>
61
+ <id type="integer">6</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>2</number>
64
+ </page>
65
+ <page>
66
+ <id type="integer">7</id>
67
+ <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>
68
+ <number>3</number>
69
+ </page>
70
+ <page>
71
+ <id type="integer">8</id>
72
+ <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>
73
+ <number>4</number>
74
+ </page>
75
+ </pages>
76
+ </chapter>
77
+ </chapters>
78
+ </book>
79
+ <book>
80
+ <id type="integer">6</id>
81
+ <name>Book 1</name>
82
+ </book>
83
+ </books>
84
+ </code>
85
+
86
+
87
+ Microsoft Style
88
+ <dataroot xmlns:od="urn:schemas-microsoft-com:officedata" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
89
+ xsi:noNamespaceSchemaLocation="book.xsd" generated="2012-07-11T16:01:54">
90
+ <book>
91
+ <id >1</id>
92
+ <name>Book 1</name>
93
+ <book_price>
94
+ <id >1</id>
95
+ <sell>50.00</sell>
96
+ <educational>35.00</educational>
97
+ <cost>20.00</cost>
98
+ </book_price>
99
+ <chapter>
100
+ <id >1</id>
101
+ <title>Chapter 1</title>
102
+ <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>
103
+ <page>
104
+ <id >1</id>
105
+ <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>
106
+ <number>1</number>
107
+ </page>
108
+ <page>
109
+ <id >2</id>
110
+ <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>
111
+ <number>2</number>
112
+ </page>
113
+ <page>
114
+ <id >3</id>
115
+ <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>
116
+ <number>3</number>
117
+ </page>
118
+ </chapter>
119
+ </book>
120
+ <book>
121
+ <id >2</id>
122
+ <name>Book 12</name>
123
+ <chapter>
124
+ <id >4</id>
125
+ <title>Chapter 1</title>
126
+ <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>
127
+ <page>
128
+ <id >12</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 >13</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 >14</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 >15</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
+ </chapter>
148
+ </book>
149
+ </dataroot>
150
+
151
+
152
+ === Database Schema
153
+
154
+ Data Active will detect the primary key of the tables in your schema even if is a name other than <b>id</b>. It will use the primary key to match records for update or deletion or creation when they don't exist. Given this it's very important that the tables that you wish to synchronise have a primary key with auto increment. The good news is by default Rails will create a primary key field called <b>id</b> that is auto-increment. At this time Data Active doesn't support compound primary keys.
155
+
156
+ === The Model
157
+
158
+ In order to identify the relationships Data Active it relies on associations so you have to make sure you define your associations in your model. Following are is a sample model.
159
+
160
+ book.rb
161
+ class Book < ActiveRecord::Base
162
+ has_many :chapters, :dependent => :destroy
163
+ end
164
+
165
+ chapter.rb
166
+ class Chapter < ActiveRecord::Base
167
+ has_many :pages, :dependent => :destroy
168
+ belongs_to :book
169
+ end
170
+
171
+ page.rb
172
+ class Page < ActiveRecord::Base
173
+ belongs_to :chapter
174
+ end
175
+
176
+ At this stage Data Active has been tested with a limited number of associations. It is hoped in the future that a more complete set of tests will exist.
177
+
178
+ == Examples
179
+
180
+ Now to the meaty part, the examples. Data Active currenly uses "Nokogiri"(http://nokogiri.org/) to do its XML parsing so you can provide either a <b>Nokogiri::XML::Element</b> or raw XML.
181
+
182
+ === New Functions
183
+
184
+ Data Active extends the ActiveRecord class giving you the following functions:
185
+
186
+ many_from_xml(xml, options)
187
+ Allows for the import of many records based on an XML document. This function expects an XML document similar to the following with many records in it:
188
+
189
+ books_changed.xml
190
+ <books type="array">
191
+ <book>
192
+ <id type="Integer">4</id>
193
+ <name>Book 1</name>
194
+ </book>
195
+ <book>
196
+ <id type="integer">5</id>
197
+ <name>Book 1</name>
198
+ </book>
199
+ <book>
200
+ <id type="integer">6</id>
201
+ <name>Book 1</name>
202
+ </book>
203
+ </books>
204
+
205
+ Following is an example:
206
+ Book.many_from_xml File.open("books_changed.xml").read, [:update]
207
+
208
+ one_from_xml(xml, options)
209
+ Allows for the import of one record based on an XML document. This function expects an XML document similar to the following with many records in it:
210
+
211
+ one_book_changed.xml
212
+ <book>
213
+ <id type="Integer">4</id>
214
+ <name>Book 1</name>
215
+ </book>
216
+
217
+ Following is an example:
218
+ Book.one_from_xml File.open("one_book_changed.xml").read, [:update]
219
+
220
+ === Options
221
+
222
+ You can combine any of the options (:create, :update or :destroy) and Data Active will only perform the actions provided in the options. However if you use the :sync option then all other options are ignored. Following are the current options:
223
+
224
+ * <b>:update</b> records in the database that match those in the provided XML document based on classes in the model, associations and the primary key.
225
+
226
+ * <b>:create</b> records in the database that exist in the provided XML document but not in the database. Matching is based on classes in the model, associations and the primary key.
227
+
228
+ * <b>:destroy</b> records in the database that don't exist in the provided XML document. Matching is based on classes in the model, associations and the primary key. This respects validation by using the ActiveRecord destroy rather than delete.
229
+
230
+ * <b>:sync</b> is really the combination of :create, :update and :destroy. Using this option will cause Data Active to ignore :create, :update and :destroy options and will proceed to make your database records match those in the XML document.
231
+
232
+ === One to One Associations
233
+ Data Active supports has_one association with all options and following arr the behaviours of Data Active with each option:
234
+
235
+ <b>:update</b>
236
+
237
+ * <i>Record exists in XML but not in DB</i>: No Action
238
+ * <i>Record exists in DB but not in XML</i>: No Action
239
+ * <i>Record exists in XML and in DB</i>: DB record is updated if identifying features (eg id) match
240
+
241
+ <b>:create</b>
242
+
243
+ * <i>Record exists in XML but not in DB</i>: A new record in the DB is created
244
+ * <i>Record exists in DB but not in XML</i>: No Action
245
+ * <i>Record exists in XML and in DB</i>: No Action
246
+
247
+ <b>:destroy</b>
248
+
249
+ * <i>Record exists in XML but not in DB</i>: No Action
250
+ * <i>Record exists in DB but not in XML</i>: The DB Record is Destroyed
251
+ * <i>Record exists in XML and in DB</i>: DB record is destroyed if identifying features (eg id) don't match
252
+
253
+ <b>:sync</b>
254
+
255
+ * <i>Record exists in XML but not in DB</i>: A new record in the DB is created
256
+ * <i>Record exists in DB but not in XML</i>: The DB Record is Destroyed
257
+ * <i>Record exists in XML and in DB</i>: DB record is replaced if identifying features (eg id) don't match but if they do then the DB record is updated
258
+
259
+ === Versions
260
+
261
+ * 0.0.1 First Release
262
+
263
+ === Future Features
264
+
265
+ * <b>Dry Run</b> allow data_active to do a dry run and report potentail changes rather than making them
266
+ * <b>Events</b> Event hooks in key locations
267
+ * <b>Selective Upates</b> Specify a set of active record objects to effect and leave all the rest alone
268
+ * <b>Many to Many</b> Tested support for has_and_belongs_to_many
269
+ * <b>Polymorphic Associations</b> Tested support for these associations
270
+
271
+ === Testing
272
+ In order to perform testing I created a number of test applications which can be found in the test_apps folder. These
273
+ applications target specific versions of rails utilising cucumber for testing
274
+
275
+ At this stage data_active has been tested with Rails 3.1.x and 3.2.x
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require 'rubygems'
2
+ require "bundler/gem_tasks"
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "data_active/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "data_active"
7
+ s.version = DataActive::VERSION
8
+ s.authors = ["Michael Harrison"]
9
+ s.email = %w(michael@focalpause.com)
10
+ s.homepage = "https://github.com/michael-harrison/data_active"
11
+ s.summary = "data_active #{s.version}"
12
+ s.description = %q{Data Active is an extension of ActiveRecord that provides features to synchronise an ActiveRecord Model with a supplied XML document}
13
+
14
+ s.rubyforge_project = "data_active"
15
+
16
+ s.add_dependency 'nokogiri'
17
+ s.add_dependency 'rails'
18
+ s.add_development_dependency 'rake'
19
+
20
+ s.files = `git ls-files`.split("\n")
21
+ s.test_files = `git ls-files -- {test_apps,test,spec,features}/*`.split("\n")
22
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
23
+ s.require_paths = %w(lib)
24
+
25
+ # specify any dependencies here; for example:
26
+ # s.add_development_dependency "rspec"
27
+ # s.add_runtime_dependency "rest-client"
28
+ end
@@ -0,0 +1,30 @@
1
+ Feature: Remove records missing in XML
2
+ As a Developer
3
+ I need to update the my database with an XML document
4
+ So that records in my database that do not share the same identify features as those in the XML are removed from my database
5
+
6
+ Scenario: Attempt removing missing records when records exist in the database with some matching records in the XML Document
7
+ Given I have a fresh set of books
8
+ And I have the "features/support/fixtures/xml/books_changed.xml" file
9
+ When I synchronise with "features/support/fixtures/xml/books_changed.xml" to only remove mismatching records
10
+ Then the books in the database that don't exist in "features/support/fixtures/xml/books_changed.xml" will no longer exist in the database
11
+ And the chapters in the database that don't exist in "features/support/fixtures/xml/books_changed.xml" will no longer exist in the database
12
+ And the pages in the database that don't exist in "features/support/fixtures/xml/books_changed.xml" will no longer exist in the database
13
+
14
+ Scenario: Attempt removing missing records when no records exist in the database
15
+ Given I have no books
16
+ And I have the "features/support/fixtures/xml/books_changed.xml" file
17
+ When I synchronise with "features/support/fixtures/xml/books_changed.xml" to only remove mismatching records
18
+ Then will have no books
19
+ And will have no chapters
20
+ And will have no pages
21
+ And will have no book prices
22
+
23
+ Scenario: Attempt removing missing records when records exist but there are no matching records in the database
24
+ Given I have no matching books
25
+ And I have the "features/support/fixtures/xml/books_changed.xml" file
26
+ When I synchronise with "features/support/fixtures/xml/books_changed.xml" to only remove mismatching records
27
+ Then will have no books
28
+ And will have no chapters
29
+ And will have no pages
30
+ And will have no book prices
@@ -0,0 +1,85 @@
1
+ require_relative "step_helper"
2
+
3
+ When /^I synchronise with "([^"]*)" to only remove mismatching records$/ do |xml_document_file|
4
+ Book.many_from_xml(File.open(Rails.root.join(xml_document_file)).read, [:destroy]) != nil
5
+ end
6
+
7
+ Then /^the books in the database that don't exist in "([^"]*)" will no longer exist in the database$/ do |xml_document_file|
8
+ xml_document = Nokogiri::XML(File.open(Rails.root.join(xml_document_file)).read)
9
+ books_in_database = Book.all
10
+
11
+ books_in_database.each do |book|
12
+ xml_book = xml_document.xpath("//book[id[text()='#{book.id}']]")
13
+ if (xml_book.count == 0)
14
+ fail "Found book (id=#{book.id} in database that doesn't exist in the XML and should have been destroyed"
15
+ end
16
+ end
17
+
18
+ end
19
+
20
+ Then /^the book_prices in the database that don't exist in "([^"]*)" will no longer exist in the database$/ do |xml_document_file|
21
+ xml_document = Nokogiri::XML(File.open(Rails.root.join(xml_document_file)).read)
22
+ book_prices_in_database = BookPrice.all
23
+
24
+ book_prices_in_database.each do |book_price|
25
+ xml_book_price = xml_document.xpath("//book_price[id[text()='#{book_price.id}']]")
26
+ if (xml_book_price.count == 0)
27
+ fail "Found book_price (id=#{book_price.id} in database that doesn't exist in the XML and should have been destroyed"
28
+ end
29
+ end
30
+
31
+ end
32
+
33
+ When /^the chapters in the database that don't exist in "([^"]*)" will no longer exist in the database$/ do |xml_document_file|
34
+ xml_document = Nokogiri::XML(File.open(Rails.root.join(xml_document_file)).read)
35
+ chapters_in_database = Chapter.all
36
+
37
+ chapters_in_database.each do |chapter|
38
+ xml_chapter = xml_document.xpath("//chapter[id[text()='#{chapter.id}']]")
39
+ if (xml_chapter.count == 0)
40
+ fail "Found chapter (id=#{chapter.id} in database that doesn't exist in the XML and should have been destroyed"
41
+ end
42
+ end
43
+
44
+ end
45
+
46
+ When /^the pages in the database that don't exist in "([^"]*)" will no longer exist in the database$/ do |xml_document_file|
47
+ xml_document = Nokogiri::XML(File.open(Rails.root.join(xml_document_file)).read)
48
+ pages_in_database = Page.all
49
+
50
+ pages_in_database.each do |page|
51
+ xml_page = xml_document.xpath("//page[id[text()='#{page.id}']]")
52
+ if (xml_page.count == 0)
53
+ fail "Found page (id=#{page.id} in database that doesn't exist in the XML and should have been destroyed"
54
+ end
55
+ end
56
+
57
+ end
58
+
59
+ Then /^will have no books$/ do
60
+ books = Book.all
61
+ if books.count > 0
62
+ fail "Books exist in the database"
63
+ end
64
+ end
65
+
66
+ When /^will have no chapters$/ do
67
+ chapters = Chapter.all
68
+ if chapters.count > 0
69
+ fail "Chapters exist in the database"
70
+ end
71
+ end
72
+
73
+ When /^will have no pages$/ do
74
+ pages = Page.all
75
+ if pages.count > 0
76
+ fail "Pages exist in the database"
77
+ end
78
+ end
79
+
80
+ When /^will have no book prices$/ do
81
+ book_prices = BookPrice.all
82
+ if book_prices.count > 0
83
+ fail "Book Prices exist in the database"
84
+ end
85
+ end