xeroizer 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (292) hide show
  1. data/.bundle/config +2 -0
  2. data/Gemfile +12 -0
  3. data/Gemfile.lock +22 -0
  4. data/LICENSE.txt +20 -0
  5. data/README.md +416 -0
  6. data/Rakefile +39 -0
  7. data/VERSION +1 -0
  8. data/lib/.DS_Store +0 -0
  9. data/lib/big_decimal_to_s.rb +9 -0
  10. data/lib/class_level_inheritable_attributes.rb +25 -0
  11. data/lib/nokogiri_utils.rb +58 -0
  12. data/lib/xeroizer.rb +55 -0
  13. data/lib/xeroizer/ca-certificates.crt +2642 -0
  14. data/lib/xeroizer/exceptions.rb +94 -0
  15. data/lib/xeroizer/generic_application.rb +40 -0
  16. data/lib/xeroizer/http.rb +148 -0
  17. data/lib/xeroizer/http_encoding_helper.rb +49 -0
  18. data/lib/xeroizer/models/account.rb +60 -0
  19. data/lib/xeroizer/models/address.rb +31 -0
  20. data/lib/xeroizer/models/branding_theme.rb +22 -0
  21. data/lib/xeroizer/models/contact.rb +46 -0
  22. data/lib/xeroizer/models/credit_note.rb +113 -0
  23. data/lib/xeroizer/models/currency.rb +18 -0
  24. data/lib/xeroizer/models/invoice.rb +138 -0
  25. data/lib/xeroizer/models/item.rb +27 -0
  26. data/lib/xeroizer/models/item_purchase_sale_details.rb +17 -0
  27. data/lib/xeroizer/models/journal.rb +25 -0
  28. data/lib/xeroizer/models/journal_line.rb +28 -0
  29. data/lib/xeroizer/models/line_item.rb +43 -0
  30. data/lib/xeroizer/models/manual_journal.rb +42 -0
  31. data/lib/xeroizer/models/manual_journal_line.rb +24 -0
  32. data/lib/xeroizer/models/option.rb +15 -0
  33. data/lib/xeroizer/models/organisation.rb +23 -0
  34. data/lib/xeroizer/models/payment.rb +30 -0
  35. data/lib/xeroizer/models/phone.rb +25 -0
  36. data/lib/xeroizer/models/tax_rate.rb +25 -0
  37. data/lib/xeroizer/models/tracking_category.rb +23 -0
  38. data/lib/xeroizer/models/tracking_category_child.rb +17 -0
  39. data/lib/xeroizer/oauth.rb +130 -0
  40. data/lib/xeroizer/partner_application.rb +44 -0
  41. data/lib/xeroizer/private_application.rb +27 -0
  42. data/lib/xeroizer/public_application.rb +21 -0
  43. data/lib/xeroizer/record/application_helper.rb +22 -0
  44. data/lib/xeroizer/record/base.rb +116 -0
  45. data/lib/xeroizer/record/base_model.rb +138 -0
  46. data/lib/xeroizer/record/base_model_http_proxy.rb +134 -0
  47. data/lib/xeroizer/record/model_definition_helper.rb +94 -0
  48. data/lib/xeroizer/record/record_association_helper.rb +94 -0
  49. data/lib/xeroizer/record/validation_helper.rb +59 -0
  50. data/lib/xeroizer/record/validators/associated_validator.rb +33 -0
  51. data/lib/xeroizer/record/validators/inclusion_of_validator.rb +22 -0
  52. data/lib/xeroizer/record/validators/presence_of_validator.rb +19 -0
  53. data/lib/xeroizer/record/validators/validator.rb +24 -0
  54. data/lib/xeroizer/record/xml_helper.rb +113 -0
  55. data/lib/xeroizer/response.rb +17 -0
  56. data/test/stub_responses/accounts.xml +589 -0
  57. data/test/stub_responses/api_exception.xml +153 -0
  58. data/test/stub_responses/bogus_oauth_error +1 -0
  59. data/test/stub_responses/branding_themes.xml +20 -0
  60. data/test/stub_responses/contact.xml +40 -0
  61. data/test/stub_responses/contacts.xml +3349 -0
  62. data/test/stub_responses/create_credit_note.xml +104 -0
  63. data/test/stub_responses/create_invoice.xml +64 -0
  64. data/test/stub_responses/credit_note.xml +69 -0
  65. data/test/stub_responses/credit_note_not_found_error.xml +1 -0
  66. data/test/stub_responses/credit_notes.xml +149 -0
  67. data/test/stub_responses/currencies.xml +12 -0
  68. data/test/stub_responses/invalid_api_key_error.xml +1 -0
  69. data/test/stub_responses/invalid_consumer_key +1 -0
  70. data/test/stub_responses/invalid_request_token +1 -0
  71. data/test/stub_responses/invoice.xml +91 -0
  72. data/test/stub_responses/invoice_not_found_error.xml +1 -0
  73. data/test/stub_responses/invoices.xml +1728 -0
  74. data/test/stub_responses/items.xml +112 -0
  75. data/test/stub_responses/manual_journal.xml +45 -0
  76. data/test/stub_responses/manual_journals.xml +40 -0
  77. data/test/stub_responses/organisation.xml +16 -0
  78. data/test/stub_responses/organisations.xml +16 -0
  79. data/test/stub_responses/rate_limit_exceeded +1 -0
  80. data/test/stub_responses/records/contact-043892a1-aef1-4c18-88d8-b8ccb6d31466.xml +38 -0
  81. data/test/stub_responses/records/contact-09664078-efe2-4a88-89a5-67eac9b0047b.xml +40 -0
  82. data/test/stub_responses/records/contact-0a4cf37b-a1a8-4753-9ee2-f9207f63a8ff.xml +48 -0
  83. data/test/stub_responses/records/contact-0e74f929-11b9-4255-a035-1fdfe573e676.xml +40 -0
  84. data/test/stub_responses/records/contact-0f471ca5-15c9-405e-a1b9-7cc35194b673.xml +38 -0
  85. data/test/stub_responses/records/contact-13cd4c47-baa6-4f07-93f6-6442310df4bf.xml +47 -0
  86. data/test/stub_responses/records/contact-158a2667-82ee-43bf-8f33-a6cc9524092d.xml +38 -0
  87. data/test/stub_responses/records/contact-17465072-6fa3-40bf-bc42-97765d9e1bea.xml +38 -0
  88. data/test/stub_responses/records/contact-1975b0ed-b7ba-4c61-bae8-2aa6d78b0dee.xml +39 -0
  89. data/test/stub_responses/records/contact-1b2be6e9-8d58-4da9-aaf8-4fe5471b653c.xml +53 -0
  90. data/test/stub_responses/records/contact-1c40da58-fe1d-4e97-b729-b2abdae94d9e.xml +38 -0
  91. data/test/stub_responses/records/contact-258176a5-c622-4394-9c94-6f88c3ea12e5.xml +40 -0
  92. data/test/stub_responses/records/contact-299dd3a0-a417-4a37-8a04-2f55e91963e5.xml +40 -0
  93. data/test/stub_responses/records/contact-2be39278-5154-4ed1-8eb0-676f25acfc66.xml +40 -0
  94. data/test/stub_responses/records/contact-2e58cff6-488c-4a32-884b-baf848010229.xml +40 -0
  95. data/test/stub_responses/records/contact-2faccd41-935e-40aa-b74e-e2fc28ac34c3.xml +38 -0
  96. data/test/stub_responses/records/contact-31af01e7-2ca7-45b9-a500-b02db996568e.xml +38 -0
  97. data/test/stub_responses/records/contact-344f1113-a25b-4344-b82e-bedeacc17c8e.xml +40 -0
  98. data/test/stub_responses/records/contact-3e776c4b-ea9e-4bb1-96be-6b0c7a71a37f.xml +39 -0
  99. data/test/stub_responses/records/contact-3fc1fc6c-e5ff-4e40-b6f3-7eb535637d87.xml +38 -0
  100. data/test/stub_responses/records/contact-416ab20c-5357-4beb-a740-e8d175d71efb.xml +38 -0
  101. data/test/stub_responses/records/contact-41a42865-f15a-4fa1-b643-47877608f557.xml +54 -0
  102. data/test/stub_responses/records/contact-42771b60-19a7-4692-af81-dd9f9b9362d4.xml +43 -0
  103. data/test/stub_responses/records/contact-451ceb28-9610-44c9-8f35-3225482f2413.xml +40 -0
  104. data/test/stub_responses/records/contact-4ab343ad-1ebb-4afe-9d48-1814a93c2081.xml +38 -0
  105. data/test/stub_responses/records/contact-4bb77692-42d4-4565-85a0-8849eb85e039.xml +39 -0
  106. data/test/stub_responses/records/contact-4dec292f-3ab7-46a8-83e4-5fb5eac42c7f.xml +40 -0
  107. data/test/stub_responses/records/contact-4e2f192e-8397-4d4d-97ca-a4fc5ac531bf.xml +38 -0
  108. data/test/stub_responses/records/contact-5188c17c-7786-4436-ad6e-9da2997386d0.xml +40 -0
  109. data/test/stub_responses/records/contact-52442753-b1c4-40b7-9b79-c33997de5837.xml +40 -0
  110. data/test/stub_responses/records/contact-565acaa9-e7f3-4fbf-80c3-16b081ddae10.xml +38 -0
  111. data/test/stub_responses/records/contact-571a2414-81ff-4f8f-8498-d91d83793131.xml +44 -0
  112. data/test/stub_responses/records/contact-58697449-85ef-46ae-83fc-6a9446f037fb.xml +40 -0
  113. data/test/stub_responses/records/contact-58bf2ae3-5144-4628-8de2-e165ac2bcdc6.xml +40 -0
  114. data/test/stub_responses/records/contact-5d41dafd-eb7e-42c1-bd5a-ba3be1da0960.xml +38 -0
  115. data/test/stub_responses/records/contact-5f005a09-5ce4-4fb4-8096-e69c18be636e.xml +38 -0
  116. data/test/stub_responses/records/contact-60d578d9-3e10-4aef-b5dc-9d9fd60a3633.xml +47 -0
  117. data/test/stub_responses/records/contact-62392126-dba4-4a75-b907-5875ebf75259.xml +40 -0
  118. data/test/stub_responses/records/contact-642c7fb5-e8e5-48e1-a710-39a18c6c3217.xml +40 -0
  119. data/test/stub_responses/records/contact-64aebf9c-bb89-4b38-b99b-405bd1ece6fd.xml +40 -0
  120. data/test/stub_responses/records/contact-64eedbc9-1fa0-485a-837f-705f23188161.xml +38 -0
  121. data/test/stub_responses/records/contact-65e96c9f-1595-4653-9a8a-2a36d49223c2.xml +40 -0
  122. data/test/stub_responses/records/contact-67d26b93-ccb4-4890-9bf1-284b70ea755d.xml +38 -0
  123. data/test/stub_responses/records/contact-69d3e538-44b3-4e00-a5f6-7dddcb6e0656.xml +40 -0
  124. data/test/stub_responses/records/contact-6a8450bc-f81a-4bb0-a8f6-aa4afe9497c7.xml +40 -0
  125. data/test/stub_responses/records/contact-6c70e424-41d6-4b9b-af3e-b3a9f3589106.xml +40 -0
  126. data/test/stub_responses/records/contact-6de0b0cf-560c-4503-aab3-e1543c329deb.xml +54 -0
  127. data/test/stub_responses/records/contact-72dd6a02-396e-42a2-a4d6-cc3fa75dfece.xml +41 -0
  128. data/test/stub_responses/records/contact-755f1475-d255-43a8-bedc-5ea7fd26c71f.xml +51 -0
  129. data/test/stub_responses/records/contact-78a9d0a0-3d8c-4f84-af3e-f260bf4a9dc0.xml +38 -0
  130. data/test/stub_responses/records/contact-79aa39ca-22b0-42c2-9026-78757a29d665.xml +42 -0
  131. data/test/stub_responses/records/contact-804f4140-5978-48fe-ba20-b56e5b834b18.xml +40 -0
  132. data/test/stub_responses/records/contact-812d4f28-1681-4241-8e34-d15c5520ba35.xml +38 -0
  133. data/test/stub_responses/records/contact-860b99a9-0958-4c8d-a98f-bb1f092b16bb.xml +60 -0
  134. data/test/stub_responses/records/contact-87c8da45-97cc-46be-b170-398da0eacfb8.xml +40 -0
  135. data/test/stub_responses/records/contact-8a154a19-6c6c-404b-bbc9-6deae2d18251.xml +38 -0
  136. data/test/stub_responses/records/contact-8bb6931d-2865-44e9-9a23-ed1fb9c7a46c.xml +40 -0
  137. data/test/stub_responses/records/contact-936c9759-01da-4063-b472-424ab9f48212.xml +38 -0
  138. data/test/stub_responses/records/contact-9d12a994-9640-4b75-95cc-3de1e9d0ef09.xml +38 -0
  139. data/test/stub_responses/records/contact-9fe59245-1fbb-4157-93c3-dc97388f3746.xml +40 -0
  140. data/test/stub_responses/records/contact-a06a7225-6f8a-4522-8400-c534dd43a16e.xml +40 -0
  141. data/test/stub_responses/records/contact-a76a85fe-73a2-46fa-aba7-791f36103cdb.xml +40 -0
  142. data/test/stub_responses/records/contact-a93b5f40-0346-4d21-9181-431e129911c0.xml +40 -0
  143. data/test/stub_responses/records/contact-abf272dd-6b1d-4829-af88-c57bf55855e3.xml +38 -0
  144. data/test/stub_responses/records/contact-ad24c33b-256b-4157-ad56-cbcf0e8db7b1.xml +47 -0
  145. data/test/stub_responses/records/contact-b107129d-f4c9-438e-9573-64b778527f4a.xml +40 -0
  146. data/test/stub_responses/records/contact-b233288a-aa26-4b26-9fc7-779d797dd56f.xml +40 -0
  147. data/test/stub_responses/records/contact-b2b5333a-2546-4975-891f-d71a8a640d23.xml +38 -0
  148. data/test/stub_responses/records/contact-b4d149bf-1823-4bd2-96da-9032388c9686.xml +40 -0
  149. data/test/stub_responses/records/contact-b78d4fd1-4306-4d83-a0b9-61458d1c53a2.xml +40 -0
  150. data/test/stub_responses/records/contact-b7d108a8-d5f7-4f16-a7c9-26eaed98e8de.xml +40 -0
  151. data/test/stub_responses/records/contact-baeed0f3-7989-4874-99b3-59f23032cb73.xml +38 -0
  152. data/test/stub_responses/records/contact-bc51a3a1-b7f6-46ca-ac9e-19b87e6ca100.xml +40 -0
  153. data/test/stub_responses/records/contact-be9f3aab-52f5-4d9c-94b4-87f7d9e5ee8b.xml +38 -0
  154. data/test/stub_responses/records/contact-c135f994-01e4-427b-9e15-acfe8a477c16.xml +49 -0
  155. data/test/stub_responses/records/contact-c14edf75-15e4-4a9c-86e4-f52e2fe7cfa4.xml +40 -0
  156. data/test/stub_responses/records/contact-ca9b9abc-c2dc-4221-8101-31f464d314cc.xml +44 -0
  157. data/test/stub_responses/records/contact-cc4db604-9ed8-4eef-8a29-51b5b70496a0.xml +38 -0
  158. data/test/stub_responses/records/contact-cce9b044-be4a-43b3-9dc7-c027d8dd35b2.xml +38 -0
  159. data/test/stub_responses/records/contact-d0cd2c4f-18a0-4f7c-a32a-2db00f29d298.xml +43 -0
  160. data/test/stub_responses/records/contact-d6851dc2-9ed9-4515-bc0b-810b09c06a6a.xml +38 -0
  161. data/test/stub_responses/records/contact-d6a384fb-f46f-41a3-8ac7-b7bc9e0b5efa.xml +46 -0
  162. data/test/stub_responses/records/contact-d74e61cf-2ad0-4f0d-b9d1-6a808e3f70cf.xml +40 -0
  163. data/test/stub_responses/records/contact-d9ab0f61-3b56-4e2b-be39-f33c11bd99e3.xml +40 -0
  164. data/test/stub_responses/records/contact-dbb1f0b5-a71b-4458-8462-104acd0fec6b.xml +38 -0
  165. data/test/stub_responses/records/contact-dd981bd6-40dd-496d-a282-bf7d3391b8b9.xml +40 -0
  166. data/test/stub_responses/records/contact-e1826204-cc0a-42a5-a6d0-4b352d9d5953.xml +40 -0
  167. data/test/stub_responses/records/contact-e2d955db-f366-42dd-87f7-fbdb4da2306f.xml +40 -0
  168. data/test/stub_responses/records/contact-e32e2130-3d27-443a-8313-48fffa03cf53.xml +40 -0
  169. data/test/stub_responses/records/contact-e3a68332-d322-4816-8678-73a537c8cd33.xml +38 -0
  170. data/test/stub_responses/records/contact-e6ac76a3-ca32-4fa1-8ef9-6a4bf8b0ec2a.xml +40 -0
  171. data/test/stub_responses/records/contact-e6ca965d-7c48-480e-be39-e847307f474a.xml +38 -0
  172. data/test/stub_responses/records/contact-e77d1f20-2e8e-46ec-9a10-50335a216724.xml +40 -0
  173. data/test/stub_responses/records/contact-e8b98c13-a424-41d2-ba0e-7b7621411e7a.xml +38 -0
  174. data/test/stub_responses/records/contact-e8e9a2c2-3e7e-48ed-8528-c3d61b28f276.xml +39 -0
  175. data/test/stub_responses/records/contact-eb43fcc6-87ec-4a0a-b243-d718bee4e2cb.xml +38 -0
  176. data/test/stub_responses/records/contact-ef6f54c1-eb45-4956-b8cd-1be82ad665f2.xml +43 -0
  177. data/test/stub_responses/records/contact-efdb3600-f233-42e2-8f18-ce7e2a95e4b1.xml +38 -0
  178. data/test/stub_responses/records/contact-f7eca431-5c97-4d24-93fd-004bb8a6c644.xml +40 -0
  179. data/test/stub_responses/records/contact-fb078879-5d6d-474f-825f-61dc90689349.xml +38 -0
  180. data/test/stub_responses/records/contact-fc39b273-4aa2-4785-99ca-24672f6c0000.xml +38 -0
  181. data/test/stub_responses/records/contact-fc9ec3a6-a2fe-4300-a8cb-ca8a0b3662e0.xml +40 -0
  182. data/test/stub_responses/records/contact-fdf96102-7491-44b6-bf4d-7a77ff25f890.xml +40 -0
  183. data/test/stub_responses/records/contact-fe61ead1-8afc-4f0b-beda-066620227aad.xml +38 -0
  184. data/test/stub_responses/records/credit_note-371cd138-1e5c-4ec1-a8c6-a1c10e8bdab1.xml +69 -0
  185. data/test/stub_responses/records/credit_note-3bffc09b-79f2-490d-b91b-c59b700b43a4.xml +91 -0
  186. data/test/stub_responses/records/credit_note-43c678ee-f357-48e2-b192-b6e3634762f9.xml +90 -0
  187. data/test/stub_responses/records/credit_note-482c018b-d329-4e05-9b4f-7a4cfc695aa0.xml +73 -0
  188. data/test/stub_responses/records/credit_note-4f67130a-749a-4ee6-98b2-743adbc11245.xml +60 -0
  189. data/test/stub_responses/records/credit_note-50e98404-2fba-4031-af67-8ba4bb227c44.xml +74 -0
  190. data/test/stub_responses/records/credit_note-7df8949c-b71f-40c0-bbcf-39f2f450f286.xml +73 -0
  191. data/test/stub_responses/records/credit_note-b356e488-2678-4be4-ad4b-d294df2d48d6.xml +76 -0
  192. data/test/stub_responses/records/invoice-0032f627-3156-4d30-9b1c-4d3b994dc921.xml +82 -0
  193. data/test/stub_responses/records/invoice-00c9511b-24b9-4190-a90a-8abf2fe9f4a0.xml +74 -0
  194. data/test/stub_responses/records/invoice-024d7994-a26c-4c20-9894-13934840fc31.xml +73 -0
  195. data/test/stub_responses/records/invoice-0e64a623-c2a1-446a-93ed-eb897f118cbc.xml +96 -0
  196. data/test/stub_responses/records/invoice-15e88e57-2554-4496-a18e-eb3f5c622345.xml +73 -0
  197. data/test/stub_responses/records/invoice-166f0588-d0ba-458c-b28a-8edd4c8fc463.xml +73 -0
  198. data/test/stub_responses/records/invoice-1d1ba340-afa2-4f4c-8ff7-a147bda9a47b.xml +91 -0
  199. data/test/stub_responses/records/invoice-290ef4c4-baec-492b-b4dd-c102826470ae.xml +85 -0
  200. data/test/stub_responses/records/invoice-30a87092-31b5-4a2c-831e-327486533dd2.xml +77 -0
  201. data/test/stub_responses/records/invoice-30dbd181-72a8-43df-b392-4241bf43d5fc.xml +68 -0
  202. data/test/stub_responses/records/invoice-33e4123e-7cdd-4f05-9a0a-eb8adeb2b868.xml +92 -0
  203. data/test/stub_responses/records/invoice-387db692-26ac-47e6-b6cc-015343809bda.xml +73 -0
  204. data/test/stub_responses/records/invoice-3b28bf11-ed2f-4cf4-8e9e-fcae730cc292.xml +89 -0
  205. data/test/stub_responses/records/invoice-3fcb9847-b350-412e-ab90-7d9d774ad881.xml +89 -0
  206. data/test/stub_responses/records/invoice-4602eda6-abe9-448e-b65f-ae6bea21f0eb.xml +96 -0
  207. data/test/stub_responses/records/invoice-46441f63-873f-4cdc-a278-b8fe516f3abb.xml +91 -0
  208. data/test/stub_responses/records/invoice-4ad1ec01-f4a3-41d7-bbb4-d2ab2fec8e65.xml +73 -0
  209. data/test/stub_responses/records/invoice-4b9afceb-f7c7-4e64-8aac-7b009971fd52.xml +84 -0
  210. data/test/stub_responses/records/invoice-4edbf6d5-4e92-43af-bedd-7effc0b86833.xml +68 -0
  211. data/test/stub_responses/records/invoice-4fad1af2-b871-4ac5-a15a-3c5e32d2e2c4.xml +83 -0
  212. data/test/stub_responses/records/invoice-52ee4d67-cae4-462c-adb2-182c39017f3d.xml +81 -0
  213. data/test/stub_responses/records/invoice-54585f46-c1a0-4432-bd4f-c1fae2fba59b.xml +66 -0
  214. data/test/stub_responses/records/invoice-5613938b-9e27-472e-92ae-3b038b669d10.xml +85 -0
  215. data/test/stub_responses/records/invoice-5aa9451d-95d1-4f95-a966-bbab2573f71c.xml +73 -0
  216. data/test/stub_responses/records/invoice-5aadcd34-01a9-4b8d-a2bb-d7cc1de9fa45.xml +87 -0
  217. data/test/stub_responses/records/invoice-5f6deadf-36a2-495a-9980-ceb11e8af9a9.xml +83 -0
  218. data/test/stub_responses/records/invoice-625ffe1b-f5d8-438e-a376-981de5f5a733.xml +75 -0
  219. data/test/stub_responses/records/invoice-64cd559e-8e03-46af-b461-8555285cee71.xml +84 -0
  220. data/test/stub_responses/records/invoice-666f8dbb-bc9a-476c-8ec4-4665d7f83190.xml +63 -0
  221. data/test/stub_responses/records/invoice-66fbe37f-49b1-43fd-97ed-85114022cd2f.xml +77 -0
  222. data/test/stub_responses/records/invoice-673dd7cc-beb7-4697-83d4-0c47cb400cc2.xml +85 -0
  223. data/test/stub_responses/records/invoice-69fc971e-9b37-41c5-9c87-174330f22343.xml +65 -0
  224. data/test/stub_responses/records/invoice-70e6db69-e5a4-42c7-a397-aa3212c2945f.xml +73 -0
  225. data/test/stub_responses/records/invoice-766d1289-b440-4675-a656-1a0612ecac77.xml +74 -0
  226. data/test/stub_responses/records/invoice-76bcb361-f93b-4513-b312-5a4af306d276.xml +66 -0
  227. data/test/stub_responses/records/invoice-76e3f056-479f-417c-a72b-f3d767899b87.xml +89 -0
  228. data/test/stub_responses/records/invoice-77b338ef-ecc0-4b95-a0d7-2617b0054611.xml +103 -0
  229. data/test/stub_responses/records/invoice-7be9956d-5316-4f6b-a66a-d355b3f159b2.xml +82 -0
  230. data/test/stub_responses/records/invoice-7dae876a-b424-436b-a4e6-17b3fdeec80c.xml +82 -0
  231. data/test/stub_responses/records/invoice-7e862d93-8dab-4856-8b0c-d844e09d750f.xml +66 -0
  232. data/test/stub_responses/records/invoice-803f70b0-56d9-4157-9787-41df271777a0.xml +82 -0
  233. data/test/stub_responses/records/invoice-86102312-aa3f-438c-9938-6840f4d8dda6.xml +74 -0
  234. data/test/stub_responses/records/invoice-8694c9c5-7097-4449-a708-b8c1982921a4.xml +68 -0
  235. data/test/stub_responses/records/invoice-86d6e00f-ef56-49f7-9a54-796ccd5ca057.xml +83 -0
  236. data/test/stub_responses/records/invoice-88e77f0f-54a5-4efc-a979-7e22223cc4d7.xml +65 -0
  237. data/test/stub_responses/records/invoice-8b0ccb6a-d9b7-4da5-8360-ef7fb157b5aa.xml +65 -0
  238. data/test/stub_responses/records/invoice-935fc854-8037-4111-8d91-993010c331cc.xml +73 -0
  239. data/test/stub_responses/records/invoice-95ef3000-c764-4ba9-a66a-b6e2d161f839.xml +62 -0
  240. data/test/stub_responses/records/invoice-962ef33f-c9d2-4602-9b9f-93a02bea23b3.xml +90 -0
  241. data/test/stub_responses/records/invoice-9868b472-1983-48e9-8edf-7e81ddf2c03a.xml +83 -0
  242. data/test/stub_responses/records/invoice-9a448e9b-a9fa-4a8b-98f5-6dc892a37374.xml +62 -0
  243. data/test/stub_responses/records/invoice-a1d04a14-96a8-4067-a0ff-8136990a354f.xml +91 -0
  244. data/test/stub_responses/records/invoice-a3bc62ef-f11b-4a9c-a4f9-a342bda371b5.xml +65 -0
  245. data/test/stub_responses/records/invoice-a6894ca0-60ee-4d45-9dd4-b44fcba46ec5.xml +88 -0
  246. data/test/stub_responses/records/invoice-a9f765e6-b9bc-4505-a47b-fb3ecb327e7b.xml +85 -0
  247. data/test/stub_responses/records/invoice-aa0173af-8707-4e7f-8dde-4c7a357bd312.xml +92 -0
  248. data/test/stub_responses/records/invoice-ab63738a-370a-43a5-bfa3-620d684e66d0.xml +81 -0
  249. data/test/stub_responses/records/invoice-b0344791-5a8a-40dd-a208-d99a461a6c10.xml +82 -0
  250. data/test/stub_responses/records/invoice-b1e53910-473c-46a3-b3cb-38ece571220e.xml +66 -0
  251. data/test/stub_responses/records/invoice-b2c02d0b-41a8-4d4d-97d7-014c78b3547d.xml +91 -0
  252. data/test/stub_responses/records/invoice-b75b3928-ab72-4424-8b93-9cdbbde4cd72.xml +80 -0
  253. data/test/stub_responses/records/invoice-bcd8a71f-aa31-4d0f-8a01-13ea26363ddf.xml +92 -0
  254. data/test/stub_responses/records/invoice-bfbb7c45-de02-45e7-b065-d9863ecfb0d8.xml +65 -0
  255. data/test/stub_responses/records/invoice-c12aff7e-12bf-4185-8702-460929f19674.xml +76 -0
  256. data/test/stub_responses/records/invoice-c3380b96-976d-4b3e-8b26-8d01eb6a3742.xml +85 -0
  257. data/test/stub_responses/records/invoice-c963f2b0-cbe1-4abd-9ccc-7e512c942068.xml +66 -0
  258. data/test/stub_responses/records/invoice-d62646b9-d0a9-4fdb-9561-756a8b7eba45.xml +63 -0
  259. data/test/stub_responses/records/invoice-dba2f021-f149-4191-a126-5351d587ab0e.xml +74 -0
  260. data/test/stub_responses/records/invoice-de5d9c29-21b3-4342-958b-ed72c4bd7ab0.xml +106 -0
  261. data/test/stub_responses/records/invoice-e3d96555-2876-4364-a46a-7551a4f52611.xml +119 -0
  262. data/test/stub_responses/records/invoice-e4a0afbd-aea0-450b-ae23-0ce921e84a77.xml +99 -0
  263. data/test/stub_responses/records/invoice-e9cb9ecb-58ef-43a8-bd20-69a85338142d.xml +74 -0
  264. data/test/stub_responses/records/invoice-ec9a6f67-7128-4a63-8ba3-5e516f455f9b.xml +92 -0
  265. data/test/stub_responses/records/invoice-ed0f2587-84fc-4aef-bc4b-b1a262e24484.xml +78 -0
  266. data/test/stub_responses/records/invoice-f362ca53-8ade-4047-865a-bb64bee5863d.xml +73 -0
  267. data/test/stub_responses/records/invoice-f571c38b-5be1-41e1-ad5a-ff6184284beb.xml +104 -0
  268. data/test/stub_responses/records/invoice-f5832195-5cd3-4660-ad3f-b73d9c64f263.xml +83 -0
  269. data/test/stub_responses/records/invoice-f9c857eb-64cd-4235-a078-d04b52c77ea7.xml +74 -0
  270. data/test/stub_responses/records/manual_journal-4765d07b-aa03-4e56-9166-50661958c864.xml +38 -0
  271. data/test/stub_responses/records/manual_journal-53fc5558-5b76-4ecd-ae5c-c4af3ccde87c.xml +31 -0
  272. data/test/stub_responses/records/manual_journal-bb6cfcfc-4500-4475-bd3a-93ee512428e0.xml +31 -0
  273. data/test/stub_responses/records/manual_journal-f00a355b-7374-445c-886b-0437bea4095c.xml +45 -0
  274. data/test/stub_responses/refresh_responses.rb +29 -0
  275. data/test/stub_responses/tax_rates.xml +118 -0
  276. data/test/stub_responses/token_expired +1 -0
  277. data/test/stub_responses/tracking_categories.xml +27 -0
  278. data/test/stub_responses/unknown_error.xml +1 -0
  279. data/test/test_helper.rb +49 -0
  280. data/test/unit/models/contact_test.rb +26 -0
  281. data/test/unit/models/credit_note_test.rb +37 -0
  282. data/test/unit/models/invoice_test.rb +51 -0
  283. data/test/unit/oauth_test.rb +72 -0
  284. data/test/unit/private_application_test.rb +20 -0
  285. data/test/unit/record/base_model_test.rb +50 -0
  286. data/test/unit/record/base_test.rb +57 -0
  287. data/test/unit/record/model_definition_test.rb +159 -0
  288. data/test/unit/record/parse_where_hash_test.rb +63 -0
  289. data/test/unit/record/record_association_test.rb +36 -0
  290. data/test/unit/record/validators_test.rb +180 -0
  291. data/xeroizer.gemspec +375 -0
  292. metadata +533 -0
@@ -0,0 +1,22 @@
1
+ module Xeroizer
2
+ module Record
3
+ module ApplicationHelper
4
+
5
+ # Factory for new BaseModel instances with the class name `record_type`.
6
+ # Only creates the instance if one doesn't already exist.
7
+ #
8
+ # @param [Symbol] record_type Symbol of the record type (e.g. :Invoice)
9
+ # @return [BaseModel] instance of BaseModel subclass matching `record_type`
10
+ def record(record_type)
11
+ define_method record_type do
12
+ var_name = "@#{record_type}_cache".to_sym
13
+ unless instance_variable_defined?(var_name)
14
+ instance_variable_set(var_name, Xeroizer::Record.const_get("#{record_type}Model".to_sym).new(self, record_type.to_s))
15
+ end
16
+ instance_variable_get(var_name)
17
+ end
18
+ end
19
+
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,116 @@
1
+ require 'xeroizer/record/model_definition_helper'
2
+ require 'xeroizer/record/record_association_helper'
3
+ require 'xeroizer/record/validation_helper'
4
+ require 'xeroizer/record/xml_helper'
5
+
6
+ module Xeroizer
7
+ module Record
8
+
9
+ class Base
10
+
11
+ include ClassLevelInheritableAttributes
12
+ class_inheritable_attributes :fields, :possible_primary_keys, :primary_key_name, :summary_only, :validators
13
+
14
+ attr_reader :attributes
15
+ attr_reader :parent
16
+ attr_accessor :errors
17
+ attr_accessor :complete_record_downloaded
18
+
19
+ include ModelDefinitionHelper
20
+ include RecordAssociationHelper
21
+ include ValidationHelper
22
+ include XmlHelper
23
+
24
+ class << self
25
+
26
+ # Build a record with attributes set to the value of attributes.
27
+ def build(attributes, parent)
28
+ record = new(parent)
29
+ attributes.each do | key, value |
30
+ record.send("#{key}=".to_sym, value)
31
+ end
32
+ record
33
+ end
34
+
35
+ end
36
+
37
+ public
38
+
39
+ def initialize(parent)
40
+ @parent = parent
41
+ @attributes = {}
42
+ end
43
+
44
+ def new_model_class(model_name)
45
+ Xeroizer::Record.const_get("#{model_name}Model".to_sym).new(parent.application, model_name.to_s)
46
+ end
47
+
48
+ def [](attribute)
49
+ self.send(attribute)
50
+ end
51
+
52
+ def []=(attribute, value)
53
+ self.send("#{attribute}=", value)
54
+ end
55
+
56
+ def new_record?
57
+ self.class.possible_primary_keys.all? { | key | @attributes[key].nil? }
58
+ end
59
+
60
+ # Check to see if the complete record is downloaded.
61
+ def complete_record_downloaded?
62
+ if !!self.class.list_contains_summary_only?
63
+ !!complete_record_downloaded
64
+ else
65
+ true
66
+ end
67
+ end
68
+
69
+ # Downloads the complete record if we only have a summary of the record.
70
+ def download_complete_record!
71
+ record = self.parent.find(self.id)
72
+ @attributes = record.attributes if record
73
+ @complete_record_downloaded = true
74
+ self
75
+ end
76
+
77
+ def save
78
+ return false unless valid?
79
+ if new_record?
80
+ create
81
+ else
82
+ update
83
+ end
84
+ true
85
+ end
86
+
87
+ protected
88
+
89
+ # Attempt to create a new record.
90
+ def create
91
+ parse_save_response(parent.http_put(to_xml))
92
+ end
93
+
94
+ # Attempt to update an existing record.
95
+ def update
96
+ if self.class.possible_primary_keys && self.class.possible_primary_keys.all? { | possible_key | self[possible_key].nil? }
97
+ raise RecordKeyMustBeDefined.new(self.class.possible_primary_keys)
98
+ end
99
+
100
+ parse_save_response(parent.http_post(to_xml))
101
+ end
102
+
103
+ # Parse the response from a create/update request.
104
+ def parse_save_response(response_xml)
105
+ record = parent.parse_response(response_xml)
106
+ record = record.first if record.is_a?(Array)
107
+ if record && record.is_a?(self.class)
108
+ @attributes = record.attributes
109
+ end
110
+ self
111
+ end
112
+
113
+ end
114
+
115
+ end
116
+ end
@@ -0,0 +1,138 @@
1
+ require 'xeroizer/record/base_model_http_proxy'
2
+
3
+ module Xeroizer
4
+ module Record
5
+
6
+ class BaseModel
7
+
8
+ include ClassLevelInheritableAttributes
9
+ class_inheritable_attributes :api_controller_name
10
+
11
+ class InvaidPermissionError < StandardError; end
12
+ ALLOWED_PERMISSIONS = [:read, :write, :update]
13
+ class_inheritable_attributes :permissions
14
+
15
+ include BaseModelHttpProxy
16
+
17
+ attr_reader :application
18
+ attr_reader :model_name
19
+ attr :model_class
20
+ attr_reader :response
21
+
22
+ class << self
23
+
24
+ # Method to allow override of the default controller name used
25
+ # in the API URLs.
26
+ #
27
+ # Default: pluaralized model name (e.g. if the controller name is
28
+ # Invoice then the default is Invoices.
29
+ def set_api_controller_name(controller_name)
30
+ self.api_controller_name = controller_name
31
+ end
32
+
33
+ # Set the permissions allowed for this class type.
34
+ # There are no permissions set by default.
35
+ # Valid permissions are :read, :write, :update.
36
+ def set_permissions(*args)
37
+ self.permissions = {}
38
+ args.each do | permission |
39
+ raise InvaidPermissionError.new("Permission #{permission} is invalid.") unless ALLOWED_PERMISSIONS.include?(permission)
40
+ self.permissions[permission] = true
41
+ end
42
+ end
43
+
44
+ end
45
+
46
+ public
47
+
48
+ def initialize(application, model_name)
49
+ @application = application
50
+ @model_name = model_name
51
+ end
52
+
53
+ # Retrieve the controller name.
54
+ #
55
+ # Default: pluaralized model name (e.g. if the controller name is
56
+ # Invoice then the default is Invoices.
57
+ def api_controller_name
58
+ self.class.api_controller_name || model_name.pluralize
59
+ end
60
+
61
+ def model_class
62
+ @model_class ||= Xeroizer::Record.const_get(model_name.to_sym)
63
+ end
64
+
65
+ # Build a record with attributes set to the value of attributes.
66
+ def build(attributes = {})
67
+ model_class.build(attributes, self)
68
+ end
69
+
70
+ # Retreive full record list for this model.
71
+ def all(options = {})
72
+ raise MethodNotAllowed.new(self, :all) unless self.class.permissions[:read]
73
+ response_xml = http_get(parse_params(options))
74
+ parse_response(response_xml, options) || []
75
+ end
76
+
77
+ # Helper method to retrieve just the first element from
78
+ # the full record list.
79
+ def first(options = {})
80
+ raise MethodNotAllowed.new(self, :all) unless self.class.permissions[:read]
81
+ result = all(options)
82
+ result.first if result.is_a?(Array)
83
+ end
84
+
85
+ # Retrieve record matching the passed in ID.
86
+ def find(id, options = {})
87
+ raise MethodNotAllowed.new(self, :all) unless self.class.permissions[:read]
88
+ response_xml = @application.http_get(@application.client, "#{url}/#{CGI.escape(id)}", options)
89
+ result = parse_response(response_xml, options)
90
+ result = result.first if result.is_a?(Array)
91
+ result.complete_record_downloaded = true if result
92
+ result
93
+ end
94
+
95
+ # Parse the response retreived during any request.
96
+ def parse_response(raw_response, request = {}, options = {})
97
+ @response = Xeroizer::Response.new
98
+ @response.response_xml = raw_response
99
+
100
+ doc = Nokogiri::XML(raw_response) { | cfg | cfg.noblanks }
101
+
102
+ # check for responses we don't understand
103
+ raise Xeroizer::UnparseableResponse.new(doc.root.name) unless doc.root.name == 'Response'
104
+
105
+ doc.root.elements.each do | element |
106
+
107
+ # Text element
108
+ if element.children && element.children.size == 1 && element.children.first.text?
109
+ case element.name
110
+ when 'Id' then @response.id = element.text
111
+ when 'Status' then @response.status = element.text
112
+ when 'ProviderName' then @response.provider = element.text
113
+ when 'DateTimeUTC' then @response.date_time = Time.parse(element.text)
114
+ end
115
+
116
+ # Records in response
117
+ elsif element.children && element.children.size > 0 && element.children.first.name == model_name
118
+ parse_records(element.children)
119
+ end
120
+ end
121
+
122
+ @response.response_items
123
+ end
124
+
125
+ protected
126
+
127
+ # Parse the records part of the XML response and builds model instances as necessary.
128
+ def parse_records(elements)
129
+ @response.response_items = []
130
+ elements.each do | element |
131
+ @response.response_items << model_class.build_from_node(element, self)
132
+ end
133
+ end
134
+
135
+ end
136
+
137
+ end
138
+ end
@@ -0,0 +1,134 @@
1
+ module Xeroizer
2
+ module Record
3
+ module BaseModelHttpProxy
4
+
5
+ def self.included(base)
6
+ base.send :include, InstanceMethods
7
+ end
8
+
9
+ module InstanceMethods
10
+
11
+ public
12
+
13
+ # URL end-point for this model.
14
+ def url
15
+ @application.xero_url + '/' + api_controller_name
16
+ end
17
+
18
+ def http_get(extra_params = {})
19
+ application.http_get(application.client, url, extra_params)
20
+ end
21
+
22
+ def http_put(xml, extra_params = {})
23
+ application.http_put(application.client, url, xml, extra_params)
24
+ end
25
+
26
+ def http_post(xml, extra_params = {})
27
+ application.http_post(application.client, url, xml, extra_params)
28
+ end
29
+
30
+ protected
31
+
32
+ # Parse parameters for GET requests.
33
+ def parse_params(options)
34
+ params = {}
35
+ params[:ModifiedAfter] = options[:modified_since] if options[:modified_since]
36
+ params[:OrderBy] = options[:order] if options[:order]
37
+ if options[:where]
38
+ params[:where] = case options[:where]
39
+ when String then options[:where]
40
+ when Hash then parse_where_hash(options[:where])
41
+ end
42
+ end
43
+ params[:offset] = options[:offset] if options[:offset]
44
+ params
45
+ end
46
+
47
+ # Parse the :where part of the options for GET parameters and construct a valid
48
+ # .Net version of the criteria to pass to Xero.
49
+ #
50
+ # Attribute names can be modified as follows to change the expression used:
51
+ # {attribute_name}_is_greater_than or {attribute_name}> uses '>'
52
+ # {attribute_name}_is_greater_than_or_equal_to or {attribute_name}>= uses '>='
53
+ # {attribute_name}_is_less_than or {attribute_name}< uses '<'
54
+ # {attribute_name}_is_less_than_or_equal_to or {attribute_name}< uses '<='
55
+ # DEFAULT: '=='
56
+ def parse_where_hash(where)
57
+ conditions = []
58
+ where.each do | key, value |
59
+ (attribute_name, expression) = extract_expression_from_attribute_name(key)
60
+ (field_name, field) = model_class.fields.find { | k, v | v[:internal_name] == attribute_name }
61
+ if field
62
+ conditions << where_condition_part(field, expression, value)
63
+ else
64
+ raise InvalidAttributeInWhere.new(model_name, attribute_name)
65
+ end
66
+ end
67
+ escaped_where = CGI.escape(conditions.map { | (attr, expression, value) | "#{attr}#{expression}#{value}"}.join('&&'))
68
+ escaped_where
69
+ end
70
+
71
+ # Extract the attribute name and expression from the attribute.
72
+ #
73
+ # @return [Array] containing [actual_attribute_name, expression]
74
+ def extract_expression_from_attribute_name(key)
75
+ case key.to_s
76
+ when /(_is_not|\<\>)$/
77
+ [
78
+ key.to_s.gsub(/(_is_not|\<\>)$/, '').to_sym,
79
+ '<>'
80
+ ]
81
+
82
+ when /(_is_greater_than|\>)$/
83
+ [
84
+ key.to_s.gsub(/(_is_greater_than|\>)$/, '').to_sym,
85
+ '>'
86
+ ]
87
+
88
+ when /(_is_greater_than_or_equal_to|\>\=)$/
89
+ [
90
+ key.to_s.gsub(/(_is_greater_than_or_equal_to|\>\=)$/, '').to_sym,
91
+ '>='
92
+ ]
93
+
94
+ when /(_is_less_than|\<)$/
95
+ [
96
+ key.to_s.gsub(/(_is_less_than|\<)$/, '').to_sym,
97
+ '<'
98
+ ]
99
+
100
+ when /(_is_less_than_or_equal_to|\<\=)$/
101
+ [
102
+ key.to_s.gsub(/(_is_less_than_or_equal_to|\<\=)$/, '').to_sym,
103
+ '<='
104
+ ]
105
+
106
+ else
107
+ [key, '==']
108
+
109
+ end
110
+ end
111
+
112
+ # Creates a condition part array containing the:
113
+ # * Field's API name
114
+ # * Expression
115
+ # * .Net formatted value.
116
+ def where_condition_part(field, expression, value)
117
+ case field[:type]
118
+ when :guid then ["#{field[:api_name]}.ToString()", expression, "\"#{value}\""]
119
+ when :string then [field[:api_name], expression, "\"#{value}\""]
120
+ when :boolean then [field[:api_name], expression, value ? 'true' : 'false']
121
+ when :integer then [field[:api_name], expression, value.to_s]
122
+ when :decimal then [field[:api_name], expression, value.to_s]
123
+ when :date then [field[:api_name], expression, "DateTime.Parse(\"#{value.strftime("%Y-%m-%d")}\")"]
124
+ when :datetime then [field[:api_name], expression, "DateTime.Parse(\"#{value.utc.strftime("%Y-%m-%dT%H:%M:%S")}\")"]
125
+ when :belongs_to then
126
+ when :has_many then
127
+ end
128
+ end
129
+
130
+ end
131
+
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,94 @@
1
+ module Xeroizer
2
+ module Record
3
+ module ModelDefinitionHelper
4
+
5
+ def self.included(base)
6
+ base.extend(ClassMethods)
7
+ base.send :include, InstanceMethods
8
+ end
9
+
10
+ module ClassMethods
11
+
12
+ # Possible primary keys. At least one of these must exist before attempting to update a record.
13
+ def set_possible_primary_keys(*args)
14
+ args = [args] unless args.is_a?(Array)
15
+ self.possible_primary_keys = args
16
+ end
17
+
18
+ # Set the actual Xero primary key for this record.
19
+ def set_primary_key(primary_key_name)
20
+ self.primary_key_name = primary_key_name
21
+ end
22
+
23
+ # Whether this record type's list results contain summary data only.
24
+ #
25
+ # Records like invoices, when returning a list, only show summary data for
26
+ # certain types of associations (like the contact record) and do not return
27
+ # any data for line items.
28
+ #
29
+ # Default: false
30
+ def list_contains_summary_only(status)
31
+ self.summary_only = status
32
+ end
33
+
34
+ def list_contains_summary_only?
35
+ !!summary_only
36
+ end
37
+
38
+ # Helper methods used to define the fields this model has.
39
+ def string(field_name, options = {}); define_simple_attribute(field_name, :string, options); end
40
+ def boolean(field_name, options = {}); define_simple_attribute(field_name, :boolean, options); end
41
+ def integer(field_name, options = {}); define_simple_attribute(field_name, :integer, options, 0); end
42
+ def decimal(field_name, options = {}); define_simple_attribute(field_name, :decimal, options, 0.0); end
43
+ def date(field_name, options = {}); define_simple_attribute(field_name, :date, options); end
44
+ def datetime(field_name, options = {}); define_simple_attribute(field_name, :datetime, options); end
45
+
46
+ def guid(field_name, options = {})
47
+ # Ensure all automated Id conversions are changed to ID.
48
+ options[:api_name] ||= field_name.to_s.camelize.gsub(/Id/, 'ID')
49
+ define_simple_attribute(field_name, :guid, options)
50
+ end
51
+
52
+ # Helper method to simplify field definition.
53
+ # Creates an accessor and reader for the field.
54
+ # Options:
55
+ # :internal_name => allows the specification of an internal field name differing from the API's field name.
56
+ # :api_name => allows the API name to be specified if it can't be properly converted from camelize.
57
+ # :model_name => allows class used for children to be different from it's ndoe name in the XML.
58
+ # :type => type of field
59
+ def define_simple_attribute(field_name, field_type, options, value_if_nil = nil)
60
+ self.fields ||= {}
61
+
62
+ internal_field_name = options[:internal_name] || field_name
63
+ self.fields[field_name] = options.merge({
64
+ :internal_name => internal_field_name,
65
+ :api_name => options[:api_name] || field_name.to_s.camelize,
66
+ :type => field_type
67
+ })
68
+ define_method internal_field_name do
69
+ @attributes[field_name] || value_if_nil
70
+ end
71
+ define_method "#{internal_field_name}=".to_sym do | value |
72
+ @attributes[field_name] = value
73
+ end
74
+ end
75
+
76
+ end
77
+
78
+ module InstanceMethods
79
+
80
+ # Returns the value of the Xero primary key for this record if it exists.
81
+ def id
82
+ self[self.class.primary_key_name]
83
+ end
84
+
85
+ # Sets the value of the Xero primary key for this record if it exists.
86
+ def id=(new_id)
87
+ self[self.class.primary_key_name] = new_id
88
+ end
89
+
90
+ end
91
+
92
+ end
93
+ end
94
+ end