sequencescape-client-api 0.3.5

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 (313) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +7 -0
  3. data/.rspec +1 -0
  4. data/.rvmrc +52 -0
  5. data/.yardoc/checksums +96 -0
  6. data/.yardoc/complete +0 -0
  7. data/.yardoc/object_types +0 -0
  8. data/.yardoc/objects/root.dat +0 -0
  9. data/.yardoc/proxy_types +0 -0
  10. data/Gemfile +4 -0
  11. data/README.markdown +4 -0
  12. data/Rakefile +2 -0
  13. data/doc/Array.html +192 -0
  14. data/doc/BaitLibrary.html +139 -0
  15. data/doc/Hash.html +242 -0
  16. data/doc/NilClass.html +214 -0
  17. data/doc/Sequencescape/Api/Actions/ClassActionHelpers.html +213 -0
  18. data/doc/Sequencescape/Api/Actions/InstanceActionHelpers.html +282 -0
  19. data/doc/Sequencescape/Api/Actions.html +197 -0
  20. data/doc/Sequencescape/Api/Associations/Base/InstanceMethods.html +352 -0
  21. data/doc/Sequencescape/Api/Associations/Base.html +237 -0
  22. data/doc/Sequencescape/Api/Associations/BelongsTo/AssociationProxy.html +332 -0
  23. data/doc/Sequencescape/Api/Associations/BelongsTo/CommonBehaviour/LoadHandler.html +279 -0
  24. data/doc/Sequencescape/Api/Associations/BelongsTo/CommonBehaviour.html +432 -0
  25. data/doc/Sequencescape/Api/Associations/BelongsTo/InlineAssociationProxy.html +173 -0
  26. data/doc/Sequencescape/Api/Associations/BelongsTo.html +212 -0
  27. data/doc/Sequencescape/Api/Associations/HasMany/AssociationProxy.html +366 -0
  28. data/doc/Sequencescape/Api/Associations/HasMany/InlineAssociationProxy.html +529 -0
  29. data/doc/Sequencescape/Api/Associations/HasMany/Json.html +250 -0
  30. data/doc/Sequencescape/Api/Associations/HasMany/Validation/CompositeErrors.html +493 -0
  31. data/doc/Sequencescape/Api/Associations/HasMany/Validation.html +245 -0
  32. data/doc/Sequencescape/Api/Associations/HasMany.html +212 -0
  33. data/doc/Sequencescape/Api/Associations/InstanceMethods/CompositeErrors.html +379 -0
  34. data/doc/Sequencescape/Api/Associations/InstanceMethods.html +496 -0
  35. data/doc/Sequencescape/Api/Associations.html +201 -0
  36. data/doc/Sequencescape/Api/BasicErrorHandling.html +371 -0
  37. data/doc/Sequencescape/Api/Composition/Target.html +262 -0
  38. data/doc/Sequencescape/Api/Composition.html +215 -0
  39. data/doc/Sequencescape/Api/ConnectionFactory/Actions.html +611 -0
  40. data/doc/Sequencescape/Api/ConnectionFactory/Helpers.html +233 -0
  41. data/doc/Sequencescape/Api/ConnectionFactory.html +321 -0
  42. data/doc/Sequencescape/Api/ErrorHandling/TurnOffValidationOfUuidOnlyRecords.html +189 -0
  43. data/doc/Sequencescape/Api/ErrorHandling.html +201 -0
  44. data/doc/Sequencescape/Api/FinderMethods/AllHandler.html +279 -0
  45. data/doc/Sequencescape/Api/FinderMethods/Caching.html +183 -0
  46. data/doc/Sequencescape/Api/FinderMethods/Delegation.html +189 -0
  47. data/doc/Sequencescape/Api/FinderMethods/FindByUuidHandler.html +279 -0
  48. data/doc/Sequencescape/Api/FinderMethods.html +364 -0
  49. data/doc/Sequencescape/Api/GeneralError.html +176 -0
  50. data/doc/Sequencescape/Api/JsonError.html +205 -0
  51. data/doc/Sequencescape/Api/ModifyingHandler.html +359 -0
  52. data/doc/Sequencescape/Api/PageOfResults/UpdateHandler.html +279 -0
  53. data/doc/Sequencescape/Api/PageOfResults.html +611 -0
  54. data/doc/Sequencescape/Api/Rails/ApplicationController.html +207 -0
  55. data/doc/Sequencescape/Api/Rails/Resourced.html +269 -0
  56. data/doc/Sequencescape/Api/Rails.html +117 -0
  57. data/doc/Sequencescape/Api/Resource/ActiveModel.html +287 -0
  58. data/doc/Sequencescape/Api/Resource/Attributes/InstanceMethods.html +272 -0
  59. data/doc/Sequencescape/Api/Resource/Attributes.html +199 -0
  60. data/doc/Sequencescape/Api/Resource/Groups/InstanceMethods.html +378 -0
  61. data/doc/Sequencescape/Api/Resource/Groups/Json.html +112 -0
  62. data/doc/Sequencescape/Api/Resource/Groups/Proxy/InstanceMethods.html +256 -0
  63. data/doc/Sequencescape/Api/Resource/Groups/Proxy.html +177 -0
  64. data/doc/Sequencescape/Api/Resource/Groups.html +270 -0
  65. data/doc/Sequencescape/Api/Resource/InstanceMethods.html +344 -0
  66. data/doc/Sequencescape/Api/Resource/Json/ClassMethods.html +243 -0
  67. data/doc/Sequencescape/Api/Resource/Json/CoercionHandler.html +279 -0
  68. data/doc/Sequencescape/Api/Resource/Json.html +405 -0
  69. data/doc/Sequencescape/Api/Resource/Modifications.html +295 -0
  70. data/doc/Sequencescape/Api/Resource.html +436 -0
  71. data/doc/Sequencescape/Api/ResourceInvalid.html +289 -0
  72. data/doc/Sequencescape/Api/ResourceModelProxy.html +444 -0
  73. data/doc/Sequencescape/Api/Version1.html +218 -0
  74. data/doc/Sequencescape/Api/Version2.html +222 -0
  75. data/doc/Sequencescape/Api.html +704 -0
  76. data/doc/Sequencescape/Asset.html +262 -0
  77. data/doc/Sequencescape/AssetAudit.html +258 -0
  78. data/doc/Sequencescape/AssetGroup.html +258 -0
  79. data/doc/Sequencescape/BaitLibraryLayout.html +258 -0
  80. data/doc/Sequencescape/BarcodePrinter.html +258 -0
  81. data/doc/Sequencescape/BarcodedAsset.html +293 -0
  82. data/doc/Sequencescape/Batch.html +327 -0
  83. data/doc/Sequencescape/Behaviour/Barcoded.html +195 -0
  84. data/doc/Sequencescape/Behaviour/Labeled.html +189 -0
  85. data/doc/Sequencescape/Behaviour/Qced/QcFile.html +244 -0
  86. data/doc/Sequencescape/Behaviour/Qced.html +199 -0
  87. data/doc/Sequencescape/Behaviour/Receptacle/Aliquot.html +258 -0
  88. data/doc/Sequencescape/Behaviour/Receptacle.html +195 -0
  89. data/doc/Sequencescape/Behaviour/StateDriven.html +205 -0
  90. data/doc/Sequencescape/Behaviour.html +130 -0
  91. data/doc/Sequencescape/BulkTransfer.html +258 -0
  92. data/doc/Sequencescape/Comment.html +258 -0
  93. data/doc/Sequencescape/Lane.html +282 -0
  94. data/doc/Sequencescape/LibraryEvent.html +258 -0
  95. data/doc/Sequencescape/LibraryTube.html +322 -0
  96. data/doc/Sequencescape/Lot.html +258 -0
  97. data/doc/Sequencescape/LotType/LotCreator.html +182 -0
  98. data/doc/Sequencescape/LotType.html +268 -0
  99. data/doc/Sequencescape/MultiplexedLibraryTube.html +326 -0
  100. data/doc/Sequencescape/Order.html +338 -0
  101. data/doc/Sequencescape/OrderTemplate/OrderCreator.html +182 -0
  102. data/doc/Sequencescape/OrderTemplate.html +268 -0
  103. data/doc/Sequencescape/Pipeline.html +258 -0
  104. data/doc/Sequencescape/Plate/CommentsCreation.html +184 -0
  105. data/doc/Sequencescape/Plate/CurrentVolumeSubstraction.html +236 -0
  106. data/doc/Sequencescape/Plate/Pooling.html +249 -0
  107. data/doc/Sequencescape/Plate/WellStructure.html +313 -0
  108. data/doc/Sequencescape/Plate.html +446 -0
  109. data/doc/Sequencescape/PlateConversion.html +258 -0
  110. data/doc/Sequencescape/PlateCreation.html +258 -0
  111. data/doc/Sequencescape/PlatePurpose/PlateCreation.html +186 -0
  112. data/doc/Sequencescape/PlatePurpose.html +268 -0
  113. data/doc/Sequencescape/PlateTemplate.html +282 -0
  114. data/doc/Sequencescape/PooledPlateCreation.html +258 -0
  115. data/doc/Sequencescape/Project.html +258 -0
  116. data/doc/Sequencescape/QcDecision.html +258 -0
  117. data/doc/Sequencescape/QcFile.html +327 -0
  118. data/doc/Sequencescape/Qcable.html +258 -0
  119. data/doc/Sequencescape/QcableCreator.html +258 -0
  120. data/doc/Sequencescape/Request.html +258 -0
  121. data/doc/Sequencescape/Robot.html +258 -0
  122. data/doc/Sequencescape/Sample.html +258 -0
  123. data/doc/Sequencescape/SampleTube.html +318 -0
  124. data/doc/Sequencescape/Search/BaseHandler.html +234 -0
  125. data/doc/Sequencescape/Search/MultipleResultHandler.html +276 -0
  126. data/doc/Sequencescape/Search/SingleResultHandler.html +303 -0
  127. data/doc/Sequencescape/Search.html +431 -0
  128. data/doc/Sequencescape/SpecificTubeCreation.html +258 -0
  129. data/doc/Sequencescape/Stamp.html +258 -0
  130. data/doc/Sequencescape/StateChange.html +258 -0
  131. data/doc/Sequencescape/Study.html +258 -0
  132. data/doc/Sequencescape/Submission.html +258 -0
  133. data/doc/Sequencescape/SubmissionPool.html +258 -0
  134. data/doc/Sequencescape/Tag/Group.html +219 -0
  135. data/doc/Sequencescape/Tag.html +149 -0
  136. data/doc/Sequencescape/Tag2Layout.html +258 -0
  137. data/doc/Sequencescape/Tag2LayoutTemplate.html +258 -0
  138. data/doc/Sequencescape/TagGroup.html +258 -0
  139. data/doc/Sequencescape/TagLayout.html +258 -0
  140. data/doc/Sequencescape/TagLayoutTemplate.html +258 -0
  141. data/doc/Sequencescape/Template.html +258 -0
  142. data/doc/Sequencescape/Transfer.html +258 -0
  143. data/doc/Sequencescape/TransferTemplate.html +258 -0
  144. data/doc/Sequencescape/Tube.html +319 -0
  145. data/doc/Sequencescape/TubeCreation.html +258 -0
  146. data/doc/Sequencescape/TubeFromTubeCreation.html +258 -0
  147. data/doc/Sequencescape/TubePurpose/TubeCreation.html +186 -0
  148. data/doc/Sequencescape/TubePurpose.html +268 -0
  149. data/doc/Sequencescape/User.html +258 -0
  150. data/doc/Sequencescape/VolumeUpdate.html +258 -0
  151. data/doc/Sequencescape/Well.html +362 -0
  152. data/doc/Sequencescape.html +121 -0
  153. data/doc/_index.html +1248 -0
  154. data/doc/class_list.html +51 -0
  155. data/doc/css/common.css +1 -0
  156. data/doc/css/full_list.css +58 -0
  157. data/doc/css/style.css +481 -0
  158. data/doc/file.README.html +76 -0
  159. data/doc/file_list.html +56 -0
  160. data/doc/frames.html +17 -0
  161. data/doc/index.html +76 -0
  162. data/doc/js/app.js +243 -0
  163. data/doc/js/full_list.js +216 -0
  164. data/doc/js/jquery.js +4 -0
  165. data/doc/method_list.html +1411 -0
  166. data/doc/top-level-namespace.html +114 -0
  167. data/lib/sequencescape/asset.rb +10 -0
  168. data/lib/sequencescape/asset_audit.rb +7 -0
  169. data/lib/sequencescape/asset_group.rb +9 -0
  170. data/lib/sequencescape/bait_library.rb +14 -0
  171. data/lib/sequencescape/bait_library_layout.rb +9 -0
  172. data/lib/sequencescape/barcode_printer.rb +13 -0
  173. data/lib/sequencescape/barcoded_asset.rb +12 -0
  174. data/lib/sequencescape/batch.rb +24 -0
  175. data/lib/sequencescape/behaviour/barcoded.rb +15 -0
  176. data/lib/sequencescape/behaviour/labeled.rb +14 -0
  177. data/lib/sequencescape/behaviour/qced.rb +31 -0
  178. data/lib/sequencescape/behaviour/receptacle.rb +21 -0
  179. data/lib/sequencescape/behaviour/state_driven.rb +19 -0
  180. data/lib/sequencescape/bulk_transfer.rb +8 -0
  181. data/lib/sequencescape/comment.rb +7 -0
  182. data/lib/sequencescape/custom_metadatum_collection.rb +8 -0
  183. data/lib/sequencescape/lane.rb +8 -0
  184. data/lib/sequencescape/library_event.rb +5 -0
  185. data/lib/sequencescape/library_tube.rb +7 -0
  186. data/lib/sequencescape/locale/en.yml +190 -0
  187. data/lib/sequencescape/lot.rb +11 -0
  188. data/lib/sequencescape/lot_type.rb +20 -0
  189. data/lib/sequencescape/multiplexed_library_tube.rb +6 -0
  190. data/lib/sequencescape/order.rb +13 -0
  191. data/lib/sequencescape/order_template.rb +19 -0
  192. data/lib/sequencescape/pipeline.rb +10 -0
  193. data/lib/sequencescape/plate/pooling.rb +23 -0
  194. data/lib/sequencescape/plate/well_structure.rb +27 -0
  195. data/lib/sequencescape/plate.rb +71 -0
  196. data/lib/sequencescape/plate_conversion.rb +8 -0
  197. data/lib/sequencescape/plate_creation.rb +8 -0
  198. data/lib/sequencescape/plate_purpose.rb +24 -0
  199. data/lib/sequencescape/plate_template.rb +13 -0
  200. data/lib/sequencescape/pooled_plate_creation.rb +8 -0
  201. data/lib/sequencescape/project.rb +11 -0
  202. data/lib/sequencescape/qc_decision.rb +9 -0
  203. data/lib/sequencescape/qc_file.rb +14 -0
  204. data/lib/sequencescape/qcable.rb +17 -0
  205. data/lib/sequencescape/qcable_creator.rb +13 -0
  206. data/lib/sequencescape/request.rb +16 -0
  207. data/lib/sequencescape/robot.rb +5 -0
  208. data/lib/sequencescape/sample.rb +51 -0
  209. data/lib/sequencescape/sample_tube.rb +7 -0
  210. data/lib/sequencescape/search.rb +80 -0
  211. data/lib/sequencescape/specific_tube_creation.rb +8 -0
  212. data/lib/sequencescape/stamp.rb +9 -0
  213. data/lib/sequencescape/state_change.rb +11 -0
  214. data/lib/sequencescape/study.rb +14 -0
  215. data/lib/sequencescape/submission.rb +16 -0
  216. data/lib/sequencescape/submission_pool.rb +7 -0
  217. data/lib/sequencescape/tag.rb +14 -0
  218. data/lib/sequencescape/tag2_layout.rb +10 -0
  219. data/lib/sequencescape/tag2_layout_template.rb +9 -0
  220. data/lib/sequencescape/tag_group.rb +4 -0
  221. data/lib/sequencescape/tag_layout.rb +10 -0
  222. data/lib/sequencescape/tag_layout_template.rb +15 -0
  223. data/lib/sequencescape/template.rb +6 -0
  224. data/lib/sequencescape/transfer.rb +9 -0
  225. data/lib/sequencescape/transfer_request.rb +12 -0
  226. data/lib/sequencescape/transfer_request_collection.rb +8 -0
  227. data/lib/sequencescape/transfer_template.rb +9 -0
  228. data/lib/sequencescape/tube.rb +26 -0
  229. data/lib/sequencescape/tube_creation.rb +8 -0
  230. data/lib/sequencescape/tube_from_tube_creation.rb +8 -0
  231. data/lib/sequencescape/tube_purpose.rb +24 -0
  232. data/lib/sequencescape/user.rb +13 -0
  233. data/lib/sequencescape/volume_update.rb +6 -0
  234. data/lib/sequencescape/well.rb +19 -0
  235. data/lib/sequencescape/work_completion.rb +10 -0
  236. data/lib/sequencescape-api/actions.rb +69 -0
  237. data/lib/sequencescape-api/associations/base/instance_methods.rb +57 -0
  238. data/lib/sequencescape-api/associations/base.rb +15 -0
  239. data/lib/sequencescape-api/associations/belongs_to.rb +127 -0
  240. data/lib/sequencescape-api/associations/has_many/json.rb +10 -0
  241. data/lib/sequencescape-api/associations/has_many/validation.rb +37 -0
  242. data/lib/sequencescape-api/associations/has_many.rb +123 -0
  243. data/lib/sequencescape-api/associations.rb +112 -0
  244. data/lib/sequencescape-api/capabilities.rb +11 -0
  245. data/lib/sequencescape-api/composition.rb +37 -0
  246. data/lib/sequencescape-api/connection_factory/actions.rb +154 -0
  247. data/lib/sequencescape-api/connection_factory/helpers.rb +9 -0
  248. data/lib/sequencescape-api/connection_factory.rb +40 -0
  249. data/lib/sequencescape-api/core.rb +63 -0
  250. data/lib/sequencescape-api/core_ext/array.rb +7 -0
  251. data/lib/sequencescape-api/core_ext/hash.rb +18 -0
  252. data/lib/sequencescape-api/core_ext.rb +2 -0
  253. data/lib/sequencescape-api/error_handling.rb +42 -0
  254. data/lib/sequencescape-api/errors.rb +38 -0
  255. data/lib/sequencescape-api/finder_methods.rb +184 -0
  256. data/lib/sequencescape-api/locale/en.yml +7 -0
  257. data/lib/sequencescape-api/rails.rb +88 -0
  258. data/lib/sequencescape-api/resource/active_model.rb +17 -0
  259. data/lib/sequencescape-api/resource/attribute_groups.rb +94 -0
  260. data/lib/sequencescape-api/resource/attributes.rb +87 -0
  261. data/lib/sequencescape-api/resource/instance_methods.rb +24 -0
  262. data/lib/sequencescape-api/resource/json.rb +114 -0
  263. data/lib/sequencescape-api/resource/modifications.rb +111 -0
  264. data/lib/sequencescape-api/resource.rb +47 -0
  265. data/lib/sequencescape-api/resource_model_proxy.rb +44 -0
  266. data/lib/sequencescape-api/version.rb +5 -0
  267. data/lib/sequencescape-api.rb +16 -0
  268. data/lib/sequencescape.rb +81 -0
  269. data/sequencescape-api.gemspec +32 -0
  270. data/spec/sequencescape-api/associations_spec.rb +95 -0
  271. data/spec/sequencescape-api/contracts/belongs-to-association.txt +14 -0
  272. data/spec/sequencescape-api/contracts/client-fails-authentication.txt +6 -0
  273. data/spec/sequencescape-api/contracts/create-invalid-resource.txt +10 -0
  274. data/spec/sequencescape-api/contracts/create-model-c-has-many-inline-nested.txt +10 -0
  275. data/spec/sequencescape-api/contracts/create-model-c-has-many-inline.txt +10 -0
  276. data/spec/sequencescape-api/contracts/create-model-c-has-many.txt +10 -0
  277. data/spec/sequencescape-api/contracts/create-model-c.txt +11 -0
  278. data/spec/sequencescape-api/contracts/create-via-has-many.txt +9 -0
  279. data/spec/sequencescape-api/contracts/model-a-instance.txt +21 -0
  280. data/spec/sequencescape-api/contracts/model-b-instance.txt +38 -0
  281. data/spec/sequencescape-api/contracts/model-c-instance-created.txt +17 -0
  282. data/spec/sequencescape-api/contracts/model-c-instance-updated.txt +17 -0
  283. data/spec/sequencescape-api/contracts/model-c-instance.txt +28 -0
  284. data/spec/sequencescape-api/contracts/model-c-invalid-attribute.txt +8 -0
  285. data/spec/sequencescape-api/contracts/resource-not-found.txt +6 -0
  286. data/spec/sequencescape-api/contracts/results-page-1.txt +17 -0
  287. data/spec/sequencescape-api/contracts/results-page-2.txt +18 -0
  288. data/spec/sequencescape-api/contracts/results-page-3.txt +17 -0
  289. data/spec/sequencescape-api/contracts/retrieve-belongs-to-association.txt +3 -0
  290. data/spec/sequencescape-api/contracts/retrieve-model.txt +4 -0
  291. data/spec/sequencescape-api/contracts/retrieve-results-page-1.txt +3 -0
  292. data/spec/sequencescape-api/contracts/retrieve-results-page-2.txt +3 -0
  293. data/spec/sequencescape-api/contracts/retrieve-results-page-3.txt +3 -0
  294. data/spec/sequencescape-api/contracts/retrieve-root-with-an-authorised-client.txt +5 -0
  295. data/spec/sequencescape-api/contracts/retrieve-root-with-an-unauthorised-client.txt +4 -0
  296. data/spec/sequencescape-api/contracts/retrieve-unauthorised-model-a-list.txt +4 -0
  297. data/spec/sequencescape-api/contracts/retrieve-unauthorised-model-b-list.txt +4 -0
  298. data/spec/sequencescape-api/contracts/retrieve-unauthorised-model-c-list.txt +4 -0
  299. data/spec/sequencescape-api/contracts/root-response-for-authorised-client.txt +20 -0
  300. data/spec/sequencescape-api/contracts/root-response-for-unauthorised-client.txt +28 -0
  301. data/spec/sequencescape-api/contracts/unauthorised-model-a-list.txt +15 -0
  302. data/spec/sequencescape-api/contracts/unauthorised-model-b-list.txt +14 -0
  303. data/spec/sequencescape-api/contracts/unauthorised-model-c-list.txt +15 -0
  304. data/spec/sequencescape-api/contracts/update-invalid-resource.txt +10 -0
  305. data/spec/sequencescape-api/contracts/update-model-c.txt +11 -0
  306. data/spec/sequencescape-api/finding_methods_spec.rb +34 -0
  307. data/spec/sequencescape-api/modifications_spec.rb +169 -0
  308. data/spec/sequencescape-api/root_spec.rb +58 -0
  309. data/spec/spec_helper.rb +13 -0
  310. data/spec/support/contract_helper.rb +112 -0
  311. data/spec/support/namespaces.rb +67 -0
  312. data/spec/support/shared_examples.rb +12 -0
  313. metadata +540 -0
@@ -0,0 +1,11 @@
1
+ class Sequencescape::Api::Version1
2
+ def size_in_pages?
3
+ false
4
+ end
5
+ end
6
+
7
+ class Sequencescape::Api::Version2 < Sequencescape::Api::Version1
8
+ def size_in_pages?
9
+ true
10
+ end
11
+ end
@@ -0,0 +1,37 @@
1
+ module Sequencescape::Api::Composition
2
+ module Target
3
+ def self.included(base)
4
+ base.class_eval do
5
+ include Sequencescape::Api::Resource::ActiveModel
6
+ extend Sequencescape::Api::Resource::Attributes
7
+
8
+ extend Sequencescape::Api::Resource::Groups
9
+ include Sequencescape::Api::Resource::Groups::Json
10
+ end
11
+ end
12
+
13
+ def initialize(owner, attributes)
14
+ @owner, @attributes = owner, attributes
15
+ attributes.each { |k,v| send(:"#{k}=", v) }
16
+ end
17
+
18
+ attr_reader :attributes
19
+ private :attributes
20
+ end
21
+
22
+ def composed_of(name, options = {})
23
+ composed_class_name = options[:class_name] || name
24
+
25
+ line = __LINE__ + 1
26
+ class_eval(%Q{
27
+ def #{name}
28
+ return nil unless attributes_for?(#{name.to_s.inspect})
29
+ api.model(#{composed_class_name.inspect}).new(self, attributes_for(#{name.to_s.inspect}))
30
+ end
31
+
32
+ def #{name}=(attributes)
33
+ @attributes[#{name.to_s.inspect}] = attributes
34
+ end
35
+ }, __FILE__, line)
36
+ end
37
+ end
@@ -0,0 +1,154 @@
1
+ require 'net/http'
2
+ require 'yajl'
3
+
4
+ # PINCHED FROM https://gist.github.com/736721
5
+ BEGIN {
6
+ require 'net/http'
7
+
8
+ Net::HTTP.module_eval do
9
+ alias_method '__initialize__', 'initialize'
10
+
11
+ def initialize(*args,&block)
12
+ __initialize__(*args, &block)
13
+ ensure
14
+ @debug_output = $stderr if ENV['HTTP_DEBUG']
15
+ end
16
+ end
17
+ }
18
+
19
+ module Sequencescape::Api::ConnectionFactory::Actions
20
+ ServerError = Class.new(Sequencescape::Api::ConnectionFactory::ConnectionError)
21
+
22
+ def root(handler)
23
+ read(url, handler)
24
+ end
25
+
26
+ def retrieve(url, handler, content_type)
27
+ perform(:get, url, nil, content_type) do |response|
28
+ case response
29
+ when Net::HTTPSuccess then response
30
+ when Net::HTTPUnauthorized then handler.unauthenticated(parse_json_from(response))
31
+ when Net::HTTPNotFound then handler.missing(parse_json_from(response))
32
+ when Net::HTTPRedirection then handle_redirect(response, handler)
33
+ else raise ServerError, response.body
34
+ end
35
+ end
36
+ end
37
+
38
+ def read(url, handler)
39
+ perform(:get, url) do |response|
40
+ case response
41
+ when Net::HTTPSuccess then handler.success(parse_json_from(response))
42
+ when Net::HTTPUnauthorized then handler.unauthenticated(parse_json_from(response))
43
+ when Net::HTTPNotFound then handler.missing(parse_json_from(response))
44
+ when Net::HTTPRedirection then handle_redirect(response, handler)
45
+ else raise ServerError, response.body
46
+ end
47
+ end
48
+ end
49
+
50
+ def create(url, body, handler)
51
+ perform(:post, url, jsonify(body, :action => :create)) do |response|
52
+ case response
53
+ when Net::HTTPCreated then handler.success(parse_json_from(response))
54
+ when Net::HTTPSuccess then handler.success(parse_json_from(response)) # TODO: should be error!
55
+ when Net::HTTPUnauthorized then handler.unauthenticated(parse_json_from(response))
56
+ when Net::HTTPUnprocessableEntity then handler.failure(parse_json_from(response))
57
+ when Net::HTTPNotFound then handler.missing(parse_json_from(response))
58
+ when Net::HTTPRedirection then handle_redirect(response, handler)
59
+ else raise ServerError, response.body
60
+ end
61
+ end
62
+ end
63
+
64
+ def create_from_file(url, file, filename, content_type, handler)
65
+ perform_for_file(:post, url, file, filename, content_type) do |response|
66
+ case response
67
+ when Net::HTTPCreated then handler.success(parse_json_from(response))
68
+ when Net::HTTPSuccess then handler.success(parse_json_from(response)) # TODO: should be error!
69
+ when Net::HTTPUnauthorized then handler.unauthenticated(parse_json_from(response))
70
+ when Net::HTTPUnprocessableEntity then handler.failure(parse_json_from(response))
71
+ when Net::HTTPNotFound then handler.missing(parse_json_from(response))
72
+ when Net::HTTPRedirection then handle_redirect(response, handler)
73
+ else raise ServerError, response.body
74
+ end
75
+ end
76
+ end
77
+
78
+ def update(url, body, handler)
79
+ perform(:put, url, jsonify(body, :action => :update)) do |response|
80
+ case response
81
+ when Net::HTTPSuccess then handler.success(parse_json_from(response))
82
+ when Net::HTTPUnauthorized then handler.unauthenticated(parse_json_from(response))
83
+ when Net::HTTPUnprocessableEntity then handler.failure(parse_json_from(response))
84
+ when Net::HTTPNotFound then handler.missing(parse_json_from(response))
85
+ when Net::HTTPRedirection then handle_redirect(response, handler)
86
+ else raise ServerError, response.body
87
+ end
88
+ end
89
+ end
90
+
91
+ def handle_redirect(response, handler)
92
+ handler.redirection(parse_json_from(response)) do |read_handler|
93
+ read(response['Location'], read_handler)
94
+ end
95
+ end
96
+ private :handle_redirect
97
+
98
+ def perform(http_verb, url, body = nil, accepts = nil, &block)
99
+ begin
100
+ uri = URI.parse(url)
101
+ rescue URI::InvalidURIError => e
102
+ raise URI::InvalidURIError, "#{http_verb} failed: #{url.inspect} is not a valid uri"
103
+ end
104
+ Net::HTTP.start(uri.host, uri.port) do |connection|
105
+ connection.read_timeout = read_timeout
106
+ request_headers = headers
107
+ request_headers.merge!('Accept' => accepts) unless accepts.nil?
108
+ request = Net::HTTP.const_get(http_verb.to_s.classify).new(uri.request_uri, request_headers)
109
+ unless body.nil?
110
+ request.content_type = 'application/json'
111
+ #request.body = body.to_json
112
+ request.body = Yajl::Encoder.encode(body)
113
+ end
114
+ yield(connection.request(request))
115
+ end
116
+ end
117
+ private :perform
118
+
119
+ def perform_for_file(http_verb, url, file, filename, content_type, &block)
120
+ uri = URI.parse(url)
121
+ Net::HTTP.start(uri.host, uri.port) do |connection|
122
+ connection.read_timeout = read_timeout
123
+ file_headers = headers.merge!({'Content-Disposition'=> "form-data; filename=\"#{filename}\""})
124
+ request = Net::HTTP.const_get(http_verb.to_s.classify).new(uri.request_uri, file_headers)
125
+ request.content_type = content_type
126
+ #request.body = body.to_json
127
+ request.body = file.read
128
+ yield(connection.request(request))
129
+ end
130
+ end
131
+ private :perform
132
+
133
+ def jsonify(body, options)
134
+ case
135
+ when body.nil? then {}
136
+ when body.is_a?(Hash) then body
137
+ else body.as_json(options.merge(:root => true))
138
+ end
139
+ end
140
+ private :jsonify
141
+
142
+ def parse_json_from(response)
143
+ raise ServerError, 'server returned non-JSON content' unless response.content_type == 'application/json'
144
+ Yajl::Parser.parse(StringIO.new(response.body))
145
+ end
146
+ private :parse_json_from
147
+
148
+ def headers
149
+ { 'Accept' => 'application/json', 'Cookie' => "WTSISignOn=#{cookie}" }.tap do |standard|
150
+ standard.merge!('X-Sequencescape-Client-ID' => @authorisation) unless @authorisation.blank?
151
+ end
152
+ end
153
+ private :headers
154
+ end
@@ -0,0 +1,9 @@
1
+ module Sequencescape::Api::ConnectionFactory::Helpers
2
+ def connection_factory=(connection_factory)
3
+ @connection_factory = connection_factory
4
+ end
5
+
6
+ def connection_factory
7
+ @connection_factory ||= Sequencescape::Api::ConnectionFactory
8
+ end
9
+ end
@@ -0,0 +1,40 @@
1
+ require 'uri'
2
+
3
+ class Sequencescape::Api::ConnectionFactory
4
+ ConnectionError = Class.new(::Sequencescape::Api::Error)
5
+
6
+ class_attribute :default_url
7
+
8
+ def self.create(options)
9
+ required_options = []
10
+ required_options.push(:cookie) if options[:authorisation].blank?
11
+ required_options.push(:url) if self.default_url.blank?
12
+
13
+ required_options.push(:allow_blank => false)
14
+ options.required!(*required_options) do |missing|
15
+ raise ::Sequencescape::Api::Error, "No #{missing.or_sentence} set"
16
+ end
17
+
18
+ options[:url] ||= self.default_url
19
+ new(options)
20
+ end
21
+
22
+ attr_reader :url, :cookie, :read_timeout
23
+ private :url, :cookie, :read_timeout
24
+
25
+ def initialize(options)
26
+ @url, @cookie, @authorisation = options[:url], options[:cookie], options[:authorisation]
27
+ @read_timeout = options[:read_timeout] || 120
28
+ end
29
+ private_class_method :initialize
30
+
31
+ def url_for_uuid(uuid)
32
+ URI.join(url, uuid).to_s
33
+ end
34
+
35
+ require 'sequencescape-api/connection_factory/helpers'
36
+ require 'sequencescape-api/connection_factory/actions'
37
+
38
+ include Actions
39
+ end
40
+
@@ -0,0 +1,63 @@
1
+ require 'sequencescape-api/core_ext'
2
+ require 'sequencescape-api/resource_model_proxy'
3
+ require 'sequencescape-api/connection_factory'
4
+ require 'sequencescape-api/capabilities'
5
+
6
+ require 'active_support'
7
+ require 'active_support/core_ext/class'
8
+
9
+ class Sequencescape::Api
10
+ extend Sequencescape::Api::ConnectionFactory::Helpers
11
+
12
+ def initialize(options = {})
13
+ @models, @model_namespace = {}, options.delete(:namespace) || Sequencescape
14
+ @model_namespace = @model_namespace.constantize if @model_namespace.is_a?(String)
15
+ @connection = self.class.connection_factory.create(options).tap do |connection|
16
+ connection.root(self)
17
+ end
18
+ end
19
+
20
+ attr_reader :capabilities
21
+ delegate :read, :create, :create_from_file, :update, :retrieve, :to => :@connection
22
+
23
+ def read_uuid(uuid, handler)
24
+ read(@connection.url_for_uuid(uuid), handler)
25
+ end
26
+
27
+ def respond_to?(name, include_private = false)
28
+ super || @models.keys.include?(name.to_s)
29
+ end
30
+
31
+ def method_missing(name, *args, &block)
32
+ return super unless @models.keys.include?(name.to_s)
33
+ ResourceModelProxy.new(self, model(name), @models[name.to_s])
34
+ end
35
+ protected :method_missing
36
+
37
+ def model(name)
38
+ parts = name.to_s.split('::').map(&:classify)
39
+ raise StandardError, "#{name.inspect} is rooted and that is not supported" if parts.first.blank?
40
+ parts.inject(@model_namespace) { |context, part| context.const_get(part) }
41
+ rescue NameError => missing_constant_in_user_specified_namespace_fallback
42
+ raise if @model_namespace == ::Sequencescape
43
+ parts.inject([ ::Sequencescape, @model_namespace ]) do |(source, dest), part|
44
+ const_from_source = source.const_get(part)
45
+ if dest.const_defined?(part)
46
+ [ const_from_source, dest.const_get(part) ]
47
+ else
48
+ [ const_from_source, dest.const_set(part, const_from_source) ]
49
+ end
50
+ end.last
51
+ end
52
+
53
+ include BasicErrorHandling
54
+
55
+ def success(json)
56
+ @capabilities = Sequencescape::Api.const_get("Version#{json.delete('revision') || 1}").new
57
+ @models = json.each_with_object({}) { |(k, v), models| models[k.to_s.singularize] = v['actions'] }
58
+ end
59
+
60
+ def inspect
61
+ "#<Sequencescape::Api @connection=#{@connection.inspect}>"
62
+ end
63
+ end
@@ -0,0 +1,7 @@
1
+ require 'active_support/core_ext/array/conversions'
2
+
3
+ class Array
4
+ def or_sentence
5
+ to_sentence(:two_words_connector => ' or ', :last_word_connector => ', or ')
6
+ end
7
+ end
@@ -0,0 +1,18 @@
1
+ class Hash
2
+ # Yields all of the missing keys if there are any so that you can do what you like, like
3
+ # error maybe?
4
+ def required!(*keys, &block)
5
+ options = keys.extract_options!
6
+ return if keys.empty?
7
+
8
+ allowance_method = (options[:allow_blank] == false) ? :blank? : :nil?
9
+
10
+ missing = keys.inject([]) do |missing, next_key|
11
+ missing.tap do
12
+ value = self[next_key]
13
+ missing << next_key if value.send(allowance_method)
14
+ end
15
+ end
16
+ yield(missing) unless missing.empty?
17
+ end
18
+ end
@@ -0,0 +1,2 @@
1
+ require 'sequencescape-api/core_ext/hash'
2
+ require 'sequencescape-api/core_ext/array'
@@ -0,0 +1,42 @@
1
+ require 'active_model/naming'
2
+ require 'active_model/translation'
3
+
4
+ # Uh, ok, so why do I have to include these if I'm not going to use them!?!!
5
+ require 'active_model/errors'
6
+
7
+ # Uh, ok, so why do I have to include these when I've kind of said I want everything!?!!
8
+ require 'active_model/validator'
9
+ require 'active_model/validations'
10
+ require 'active_model/callbacks'
11
+
12
+ module Sequencescape::Api::ErrorHandling
13
+ def self.included(base)
14
+ base.class_eval do
15
+ extend ActiveModel::Translation
16
+ include ActiveModel::Validations
17
+ include ActiveModel::Validations::Callbacks
18
+ include TurnOffValidationOfUuidOnlyRecords
19
+ end
20
+ end
21
+
22
+ #--
23
+ # A bit of a fiddle this but any records that are UUID only are typically coming from the user
24
+ # having selected a load. If this has happened then there is no data and so none of the
25
+ # validations should be run.
26
+ #++
27
+ module TurnOffValidationOfUuidOnlyRecords
28
+ def run_validations!
29
+ uuid_only? ? true : super
30
+ end
31
+
32
+ def uuid_only=(value)
33
+ @uuid_only = value
34
+ end
35
+ private :uuid_only=
36
+
37
+ def uuid_only?
38
+ @uuid_only
39
+ end
40
+ private :uuid_only?
41
+ end
42
+ end
@@ -0,0 +1,38 @@
1
+ module Sequencescape
2
+ class Api
3
+ Error = Class.new(StandardError)
4
+
5
+ module GeneralError
6
+ def initialize(json)
7
+ super(json['general'])
8
+ end
9
+ end
10
+
11
+ [ :UnauthenticatedError, :ResourceNotFound ].each do |name|
12
+ const_set(name, Class.new(Error) { |c| c.send(:include, GeneralError) })
13
+ end
14
+
15
+ class ResourceInvalid < Error
16
+ def initialize(resource)
17
+ super('Resource is reported as invalid by the server')
18
+ @resource = resource
19
+ end
20
+
21
+ attr_reader :resource
22
+ end
23
+
24
+ module BasicErrorHandling
25
+ def unauthenticated(json)
26
+ raise Sequencescape::Api::UnauthenticatedError, json
27
+ end
28
+
29
+ def missing(json)
30
+ raise Sequencescape::Api::ResourceNotFound, json
31
+ end
32
+
33
+ def redirection(json, &block)
34
+ yield(self)
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,184 @@
1
+ require 'ostruct'
2
+
3
+ module Sequencescape::Api::FinderMethods
4
+ module Delegation
5
+ def self.included(base)
6
+ base.with_options(:to => :all) do |all|
7
+ all.delegate :each, :first, :last, :to_a, :size
8
+ all.delegate :empty?, :blank?
9
+ all.delegate :each_page, :first_page, :last_page
10
+ end
11
+ end
12
+ end
13
+
14
+ class << self
15
+ def extended(base)
16
+ base.singleton_class.send(:include, Delegation)
17
+ end
18
+
19
+ def included(base)
20
+ base.send(:include, Delegation)
21
+ end
22
+ end
23
+
24
+ class FindByUuidHandler
25
+ include Sequencescape::Api::BasicErrorHandling
26
+
27
+ def initialize(owner)
28
+ @owner = owner
29
+ end
30
+
31
+ def new(*args, &block)
32
+ # TODO: Consider updating
33
+ @owner.__send__(:new, *args, &block)
34
+ end
35
+ private :new
36
+
37
+ def success(json)
38
+ new(json, true)
39
+ end
40
+ end
41
+
42
+ def find(uuid)
43
+ api.read_uuid(uuid, FindByUuidHandler.new(self))
44
+ end
45
+
46
+ class AllHandler
47
+ include Sequencescape::Api::BasicErrorHandling
48
+
49
+ def initialize(owner)
50
+ @owner = owner
51
+ end
52
+
53
+ def api(*args, &block)
54
+ # TODO: Consider updating
55
+ @owner.__send__(:api, *args, &block)
56
+ end
57
+ private :api
58
+
59
+ def new(*args, &block)
60
+ # TODO: Consider updating
61
+ @owner.__send__(:new, *args, &block)
62
+ end
63
+ private :new
64
+
65
+ def success(json)
66
+ ::Sequencescape::Api::PageOfResults.new(api, json, &method(:new))
67
+ end
68
+ end
69
+
70
+ def all
71
+ api.read(actions.read, AllHandler.new(self))
72
+ end
73
+ end
74
+
75
+ module Sequencescape::Api::FinderMethods::Caching
76
+ def all(reload = false)
77
+ @cached_all = super() if @cached_all.nil? or reload
78
+ @cached_all
79
+ end
80
+ end
81
+
82
+ class Sequencescape::Api::PageOfResults
83
+ include Enumerable
84
+
85
+ attr_reader :api, :actions
86
+ private :api, :actions
87
+
88
+ attr_reader :size
89
+
90
+ def initialize(api, json, &block)
91
+ @api, @ctor = api, block
92
+ update_from_json(json)
93
+ end
94
+
95
+ def empty?
96
+ return @size.zero? if api.capabilities.size_in_pages?
97
+
98
+ first_page
99
+ @objects.empty?
100
+ end
101
+
102
+ def present?
103
+ !empty?
104
+ end
105
+
106
+ def each(&block)
107
+ walk_pages do
108
+ @objects.each(&block)
109
+ end
110
+ end
111
+
112
+ def each_page(&block)
113
+ walk_pages do
114
+ yield(@objects.dup)
115
+ end
116
+ end
117
+
118
+ def first
119
+ first_page
120
+ @objects.first
121
+ end
122
+
123
+ def last
124
+ last_page
125
+ @objects.last
126
+ end
127
+
128
+ def walk_pages
129
+ first_page
130
+ while true
131
+ yield
132
+ break if actions.next.blank?
133
+ next_page
134
+ end
135
+ end
136
+ private :walk_pages
137
+
138
+ class UpdateHandler
139
+ include Sequencescape::Api::BasicErrorHandling
140
+
141
+ def initialize(owner)
142
+ @owner = owner
143
+ end
144
+
145
+ def update_from_json(*args, &block)
146
+ # TODO: Consider updating
147
+ @owner.__send__(:update_from_json, *args, &block)
148
+ end
149
+ private :update_from_json
150
+
151
+ def success(json)
152
+ update_from_json(json)
153
+ end
154
+ end
155
+
156
+ [ :first, :last, :previous, :next ].each do |page|
157
+ line = __LINE__ + 1
158
+ class_eval(%Q{
159
+ def #{page}_page
160
+ self.tap do
161
+ api.read(actions.#{page}, UpdateHandler.new(self)) unless actions.read == actions.#{page}
162
+ yield(@objects.dup) if block_given?
163
+ end
164
+ end
165
+ }, __FILE__, line)
166
+ end
167
+ private :last_page, :next_page
168
+
169
+ def update_from_json(json)
170
+ json.delete('uuids_to_ids') # Discard unwanted rubbish
171
+ actions = json.delete('actions')
172
+ raise Sequencescape::Api::Error, 'No actions for page!' if actions.blank?
173
+
174
+ if api.capabilities.size_in_pages?
175
+ size = json.delete('size')
176
+ raise Sequencescape::Api::Error, 'No size for page!' if size.blank?
177
+ @size = size.to_i
178
+ end
179
+
180
+ raise Sequencescape::Api::Error, 'No object json in page!' if json.keys.empty?
181
+ @actions, @objects = OpenStruct.new(actions), json[json.keys.first].map(&@ctor)
182
+ end
183
+ private :update_from_json
184
+ end