sequenceserver 1.0.0.pre.2 → 1.0.0.pre.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (337) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -1
  3. data/README.md +18 -4
  4. data/bin/sequenceserver +219 -124
  5. data/lib/sequenceserver.rb +156 -153
  6. data/lib/sequenceserver/blast.rb +163 -432
  7. data/lib/sequenceserver/blast/exceptions.rb +27 -0
  8. data/lib/sequenceserver/blast/hit.rb +32 -0
  9. data/lib/sequenceserver/blast/hsp.rb +260 -0
  10. data/lib/sequenceserver/blast/query.rb +28 -0
  11. data/lib/sequenceserver/blast/report.rb +123 -0
  12. data/lib/sequenceserver/config.rb +94 -0
  13. data/lib/sequenceserver/database.rb +89 -49
  14. data/lib/sequenceserver/exceptions.rb +154 -0
  15. data/lib/sequenceserver/links.rb +1 -1
  16. data/lib/sequenceserver/logger.rb +5 -7
  17. data/lib/sequenceserver/sequence.rb +40 -39
  18. data/public/css/bootstrap.min.css +5 -7
  19. data/public/css/custom.css +28 -27
  20. data/public/dist/css/sequenceserver.min.css +1 -0
  21. data/public/dist/css/sequenceserver.min.css.gz +0 -0
  22. data/public/dist/fonts/FontAwesome.otf +0 -0
  23. data/public/dist/fonts/fontawesome-webfont.eot +0 -0
  24. data/public/dist/fonts/fontawesome-webfont.svg +565 -0
  25. data/public/dist/fonts/fontawesome-webfont.ttf +0 -0
  26. data/public/dist/fonts/fontawesome-webfont.woff +0 -0
  27. data/public/dist/fonts/fontawesome-webfont.woff2 +0 -0
  28. data/public/dist/js/sequenceserver.min.js +12 -0
  29. data/public/dist/js/sequenceserver.min.js.gz +0 -0
  30. data/public/dist/js/shims/FlashCanvas/canvas2png.js +1 -0
  31. data/public/dist/js/shims/FlashCanvas/flashcanvas.js +1 -0
  32. data/public/dist/js/shims/FlashCanvas/flashcanvas.swf +0 -0
  33. data/public/dist/js/shims/FlashCanvasPro/canvas2png.js +1 -0
  34. data/public/dist/js/shims/FlashCanvasPro/flash10canvas.swf +0 -0
  35. data/public/dist/js/shims/FlashCanvasPro/flash9canvas.swf +0 -0
  36. data/public/dist/js/shims/FlashCanvasPro/flashcanvas.js +1 -0
  37. data/public/dist/js/shims/canvas-blob.js +1 -0
  38. data/public/dist/js/shims/color-picker.js +2 -0
  39. data/public/dist/js/shims/combos/1.js +6 -0
  40. data/public/dist/js/shims/combos/10.js +2 -0
  41. data/public/dist/js/shims/combos/11.js +2 -0
  42. data/public/dist/js/shims/combos/12.js +6 -0
  43. data/public/dist/js/shims/combos/13.js +1 -0
  44. data/public/dist/js/shims/combos/14.js +1 -0
  45. data/public/dist/js/shims/combos/15.js +2 -0
  46. data/public/dist/js/shims/combos/16.js +7 -0
  47. data/public/dist/js/shims/combos/17.js +2 -0
  48. data/public/dist/js/shims/combos/18.js +3 -0
  49. data/public/dist/js/shims/combos/2.js +7 -0
  50. data/public/dist/js/shims/combos/21.js +2 -0
  51. data/public/dist/js/shims/combos/22.js +1 -0
  52. data/public/dist/js/shims/combos/23.js +6 -0
  53. data/public/dist/js/shims/combos/25.js +2 -0
  54. data/public/dist/js/shims/combos/27.js +1 -0
  55. data/public/dist/js/shims/combos/28.js +1 -0
  56. data/public/dist/js/shims/combos/29.js +1 -0
  57. data/public/dist/js/shims/combos/3.js +1 -0
  58. data/public/dist/js/shims/combos/30.js +2 -0
  59. data/public/dist/js/shims/combos/31.js +1 -0
  60. data/public/dist/js/shims/combos/33.js +1 -0
  61. data/public/dist/js/shims/combos/34.js +1 -0
  62. data/public/dist/js/shims/combos/4.js +1 -0
  63. data/public/dist/js/shims/combos/5.js +2 -0
  64. data/public/dist/js/shims/combos/6.js +2 -0
  65. data/public/dist/js/shims/combos/7.js +7 -0
  66. data/public/dist/js/shims/combos/8.js +7 -0
  67. data/public/dist/js/shims/combos/9.js +2 -0
  68. data/public/dist/js/shims/combos/97.js +1 -0
  69. data/public/dist/js/shims/combos/98.js +1 -0
  70. data/public/dist/js/shims/combos/99.js +1 -0
  71. data/public/dist/js/shims/details.js +1 -0
  72. data/public/dist/js/shims/dom-extend.js +1 -0
  73. data/public/dist/js/shims/es5.js +1 -0
  74. data/public/dist/js/shims/es6.js +1 -0
  75. data/public/dist/js/shims/excanvas.js +1 -0
  76. data/public/dist/js/shims/filereader-xhr.js +1 -0
  77. data/public/dist/js/shims/form-combat.js +1 -0
  78. data/public/dist/js/shims/form-core.js +1 -0
  79. data/public/dist/js/shims/form-datalist-lazy.js +1 -0
  80. data/public/dist/js/shims/form-datalist.js +1 -0
  81. data/public/dist/js/shims/form-fixrangechange.js +1 -0
  82. data/public/dist/js/shims/form-inputmode.js +1 -0
  83. data/public/dist/js/shims/form-message.js +1 -0
  84. data/public/dist/js/shims/form-native-extend.js +1 -0
  85. data/public/dist/js/shims/form-number-date-api.js +1 -0
  86. data/public/dist/js/shims/form-number-date-ui.js +1 -0
  87. data/public/dist/js/shims/form-shim-extend.js +1 -0
  88. data/public/dist/js/shims/form-shim-extend2.js +1 -0
  89. data/public/dist/js/shims/form-validation.js +1 -0
  90. data/public/dist/js/shims/form-validators.js +1 -0
  91. data/public/dist/js/shims/forms-picker.js +1 -0
  92. data/public/dist/js/shims/geolocation.js +1 -0
  93. data/public/dist/js/shims/i18n/formcfg-ar.js +1 -0
  94. data/public/dist/js/shims/i18n/formcfg-ch-CN.js +1 -0
  95. data/public/dist/js/shims/i18n/formcfg-cs.js +1 -0
  96. data/public/dist/js/shims/i18n/formcfg-de.js +1 -0
  97. data/public/dist/js/shims/i18n/formcfg-el.js +1 -0
  98. data/public/dist/js/shims/i18n/formcfg-en.js +1 -0
  99. data/public/dist/js/shims/i18n/formcfg-es.js +1 -0
  100. data/public/dist/js/shims/i18n/formcfg-fa.js +1 -0
  101. data/public/dist/js/shims/i18n/formcfg-fr.js +1 -0
  102. data/public/dist/js/shims/i18n/formcfg-he.js +1 -0
  103. data/public/dist/js/shims/i18n/formcfg-hi.js +1 -0
  104. data/public/dist/js/shims/i18n/formcfg-hu.js +1 -0
  105. data/public/dist/js/shims/i18n/formcfg-it.js +1 -0
  106. data/public/dist/js/shims/i18n/formcfg-ja.js +1 -0
  107. data/public/dist/js/shims/i18n/formcfg-lt.js +1 -0
  108. data/public/dist/js/shims/i18n/formcfg-nl.js +1 -0
  109. data/public/dist/js/shims/i18n/formcfg-pl.js +1 -0
  110. data/public/dist/js/shims/i18n/formcfg-pt-BR.js +1 -0
  111. data/public/dist/js/shims/i18n/formcfg-pt-PT.js +1 -0
  112. data/public/dist/js/shims/i18n/formcfg-pt.js +1 -0
  113. data/public/dist/js/shims/i18n/formcfg-ru.js +1 -0
  114. data/public/dist/js/shims/i18n/formcfg-sv.js +1 -0
  115. data/public/dist/js/shims/i18n/formcfg-zh-CN.js +1 -0
  116. data/public/dist/js/shims/i18n/formcfg-zh-TW.js +1 -0
  117. data/public/dist/js/shims/jme/alternate-media.js +1 -0
  118. data/public/dist/js/shims/jme/base.js +1 -0
  119. data/public/dist/js/shims/jme/controls.css +1 -0
  120. data/public/dist/js/shims/jme/jme.eot +0 -0
  121. data/public/dist/js/shims/jme/jme.svg +36 -0
  122. data/public/dist/js/shims/jme/jme.ttf +0 -0
  123. data/public/dist/js/shims/jme/jme.woff +0 -0
  124. data/public/dist/js/shims/jme/mediacontrols-lazy.js +1 -0
  125. data/public/dist/js/shims/jme/mediacontrols.js +1 -0
  126. data/public/dist/js/shims/jme/playlist.js +1 -0
  127. data/public/dist/js/shims/jpicker/images/AlphaBar.png +0 -0
  128. data/public/dist/js/shims/jpicker/images/Bars.png +0 -0
  129. data/public/dist/js/shims/jpicker/images/Maps.png +0 -0
  130. data/public/dist/js/shims/jpicker/images/NoColor.png +0 -0
  131. data/public/dist/js/shims/jpicker/images/bar-opacity.png +0 -0
  132. data/public/dist/js/shims/jpicker/images/map-opacity.png +0 -0
  133. data/public/dist/js/shims/jpicker/images/mappoint.gif +0 -0
  134. data/public/dist/js/shims/jpicker/images/picker.gif +0 -0
  135. data/public/dist/js/shims/jpicker/images/preview-opacity.png +0 -0
  136. data/public/dist/js/shims/jpicker/images/rangearrows.gif +0 -0
  137. data/public/dist/js/shims/jpicker/jpicker.css +1 -0
  138. data/public/dist/js/shims/matchMedia.js +3 -0
  139. data/public/dist/js/shims/mediacapture-picker.js +1 -0
  140. data/public/dist/js/shims/mediacapture.js +1 -0
  141. data/public/dist/js/shims/mediaelement-core.js +1 -0
  142. data/public/dist/js/shims/mediaelement-debug.js +1 -0
  143. data/public/dist/js/shims/mediaelement-jaris.js +1 -0
  144. data/public/dist/js/shims/mediaelement-native-fix.js +1 -0
  145. data/public/dist/js/shims/mediaelement-yt.js +1 -0
  146. data/public/dist/js/shims/moxie/flash/Moxie.cdn.swf +0 -0
  147. data/public/dist/js/shims/moxie/flash/Moxie.min.swf +0 -0
  148. data/public/dist/js/shims/moxie/js/moxie-html4.js +3 -0
  149. data/public/dist/js/shims/moxie/js/moxie-swf.js +2 -0
  150. data/public/dist/js/shims/picture.js +1 -0
  151. data/public/dist/js/shims/plugins/jquery.ui.position.js +11 -0
  152. data/public/dist/js/shims/range-ui.js +1 -0
  153. data/public/dist/js/shims/sizzle.js +11 -0
  154. data/public/dist/js/shims/sticky.js +1 -0
  155. data/public/dist/js/shims/styles/color-picker.png +0 -0
  156. data/public/dist/js/shims/styles/forms-ext.css +1 -0
  157. data/public/dist/js/shims/styles/forms-picker.css +1 -0
  158. data/public/dist/js/shims/styles/progress.gif +0 -0
  159. data/public/dist/js/shims/styles/progress.png +0 -0
  160. data/public/dist/js/shims/styles/shim-ext.css +1 -0
  161. data/public/dist/js/shims/styles/shim.css +1 -0
  162. data/public/dist/js/shims/styles/transparent.png +0 -0
  163. data/public/dist/js/shims/styles/widget.eot +0 -0
  164. data/public/dist/js/shims/styles/widget.svg +12 -0
  165. data/public/dist/js/shims/styles/widget.ttf +0 -0
  166. data/public/dist/js/shims/styles/widget.woff +0 -0
  167. data/public/dist/js/shims/swf/JarisFLVPlayer.swf +0 -0
  168. data/public/dist/js/shims/swfmini-embed.js +1 -0
  169. data/public/dist/js/shims/swfmini.js +6 -0
  170. data/public/dist/js/shims/track-ui.js +1 -0
  171. data/public/dist/js/shims/track.js +1 -0
  172. data/public/dist/js/shims/url.js +1 -0
  173. data/public/dist/js/shims/usermedia-core.js +1 -0
  174. data/public/dist/js/shims/usermedia-shim.js +1 -0
  175. data/public/dist/js/webshims/shims/FlashCanvas/canvas2png.js +1 -0
  176. data/public/dist/js/webshims/shims/FlashCanvas/flashcanvas.js +1 -0
  177. data/public/dist/js/webshims/shims/FlashCanvas/flashcanvas.swf +0 -0
  178. data/public/dist/js/webshims/shims/FlashCanvasPro/canvas2png.js +1 -0
  179. data/public/dist/js/webshims/shims/FlashCanvasPro/flash10canvas.swf +0 -0
  180. data/public/dist/js/webshims/shims/FlashCanvasPro/flash9canvas.swf +0 -0
  181. data/public/dist/js/webshims/shims/FlashCanvasPro/flashcanvas.js +1 -0
  182. data/public/dist/js/webshims/shims/canvas-blob.js +1 -0
  183. data/public/dist/js/webshims/shims/color-picker.js +2 -0
  184. data/public/dist/js/webshims/shims/combos/1.js +6 -0
  185. data/public/dist/js/webshims/shims/combos/10.js +2 -0
  186. data/public/dist/js/webshims/shims/combos/11.js +2 -0
  187. data/public/dist/js/webshims/shims/combos/12.js +6 -0
  188. data/public/dist/js/webshims/shims/combos/13.js +1 -0
  189. data/public/dist/js/webshims/shims/combos/14.js +1 -0
  190. data/public/dist/js/webshims/shims/combos/15.js +2 -0
  191. data/public/dist/js/webshims/shims/combos/16.js +7 -0
  192. data/public/dist/js/webshims/shims/combos/17.js +2 -0
  193. data/public/dist/js/webshims/shims/combos/18.js +3 -0
  194. data/public/dist/js/webshims/shims/combos/2.js +7 -0
  195. data/public/dist/js/webshims/shims/combos/21.js +2 -0
  196. data/public/dist/js/webshims/shims/combos/22.js +1 -0
  197. data/public/dist/js/webshims/shims/combos/23.js +6 -0
  198. data/public/dist/js/webshims/shims/combos/25.js +2 -0
  199. data/public/dist/js/webshims/shims/combos/27.js +1 -0
  200. data/public/dist/js/webshims/shims/combos/28.js +1 -0
  201. data/public/dist/js/webshims/shims/combos/29.js +1 -0
  202. data/public/dist/js/webshims/shims/combos/3.js +1 -0
  203. data/public/dist/js/webshims/shims/combos/30.js +2 -0
  204. data/public/dist/js/webshims/shims/combos/31.js +1 -0
  205. data/public/dist/js/webshims/shims/combos/33.js +1 -0
  206. data/public/dist/js/webshims/shims/combos/34.js +1 -0
  207. data/public/dist/js/webshims/shims/combos/4.js +1 -0
  208. data/public/dist/js/webshims/shims/combos/5.js +2 -0
  209. data/public/dist/js/webshims/shims/combos/6.js +2 -0
  210. data/public/dist/js/webshims/shims/combos/7.js +7 -0
  211. data/public/dist/js/webshims/shims/combos/8.js +7 -0
  212. data/public/dist/js/webshims/shims/combos/9.js +2 -0
  213. data/public/dist/js/webshims/shims/combos/97.js +1 -0
  214. data/public/dist/js/webshims/shims/combos/98.js +1 -0
  215. data/public/dist/js/webshims/shims/combos/99.js +1 -0
  216. data/public/dist/js/webshims/shims/details.js +1 -0
  217. data/public/dist/js/webshims/shims/dom-extend.js +1 -0
  218. data/public/dist/js/webshims/shims/es5.js +1 -0
  219. data/public/dist/js/webshims/shims/es6.js +1 -0
  220. data/public/dist/js/webshims/shims/excanvas.js +1 -0
  221. data/public/dist/js/webshims/shims/filereader-xhr.js +1 -0
  222. data/public/dist/js/webshims/shims/form-combat.js +1 -0
  223. data/public/dist/js/webshims/shims/form-core.js +1 -0
  224. data/public/dist/js/webshims/shims/form-datalist-lazy.js +1 -0
  225. data/public/dist/js/webshims/shims/form-datalist.js +1 -0
  226. data/public/dist/js/webshims/shims/form-fixrangechange.js +1 -0
  227. data/public/dist/js/webshims/shims/form-inputmode.js +1 -0
  228. data/public/dist/js/webshims/shims/form-message.js +1 -0
  229. data/public/dist/js/webshims/shims/form-native-extend.js +1 -0
  230. data/public/dist/js/webshims/shims/form-number-date-api.js +1 -0
  231. data/public/dist/js/webshims/shims/form-number-date-ui.js +1 -0
  232. data/public/dist/js/webshims/shims/form-shim-extend.js +1 -0
  233. data/public/dist/js/webshims/shims/form-shim-extend2.js +1 -0
  234. data/public/dist/js/webshims/shims/form-validation.js +1 -0
  235. data/public/dist/js/webshims/shims/form-validators.js +1 -0
  236. data/public/dist/js/webshims/shims/forms-picker.js +1 -0
  237. data/public/dist/js/webshims/shims/geolocation.js +1 -0
  238. data/public/dist/js/webshims/shims/i18n/formcfg-ar.js +1 -0
  239. data/public/dist/js/webshims/shims/i18n/formcfg-ch-CN.js +1 -0
  240. data/public/dist/js/webshims/shims/i18n/formcfg-cs.js +1 -0
  241. data/public/dist/js/webshims/shims/i18n/formcfg-de.js +1 -0
  242. data/public/dist/js/webshims/shims/i18n/formcfg-el.js +1 -0
  243. data/public/dist/js/webshims/shims/i18n/formcfg-en.js +1 -0
  244. data/public/dist/js/webshims/shims/i18n/formcfg-es.js +1 -0
  245. data/public/dist/js/webshims/shims/i18n/formcfg-fa.js +1 -0
  246. data/public/dist/js/webshims/shims/i18n/formcfg-fr.js +1 -0
  247. data/public/dist/js/webshims/shims/i18n/formcfg-he.js +1 -0
  248. data/public/dist/js/webshims/shims/i18n/formcfg-hi.js +1 -0
  249. data/public/dist/js/webshims/shims/i18n/formcfg-hu.js +1 -0
  250. data/public/dist/js/webshims/shims/i18n/formcfg-it.js +1 -0
  251. data/public/dist/js/webshims/shims/i18n/formcfg-ja.js +1 -0
  252. data/public/dist/js/webshims/shims/i18n/formcfg-lt.js +1 -0
  253. data/public/dist/js/webshims/shims/i18n/formcfg-nl.js +1 -0
  254. data/public/dist/js/webshims/shims/i18n/formcfg-pl.js +1 -0
  255. data/public/dist/js/webshims/shims/i18n/formcfg-pt-BR.js +1 -0
  256. data/public/dist/js/webshims/shims/i18n/formcfg-pt-PT.js +1 -0
  257. data/public/dist/js/webshims/shims/i18n/formcfg-pt.js +1 -0
  258. data/public/dist/js/webshims/shims/i18n/formcfg-ru.js +1 -0
  259. data/public/dist/js/webshims/shims/i18n/formcfg-sv.js +1 -0
  260. data/public/dist/js/webshims/shims/i18n/formcfg-zh-CN.js +1 -0
  261. data/public/dist/js/webshims/shims/i18n/formcfg-zh-TW.js +1 -0
  262. data/public/dist/js/webshims/shims/jme/alternate-media.js +1 -0
  263. data/public/dist/js/webshims/shims/jme/base.js +1 -0
  264. data/public/dist/js/webshims/shims/jme/controls.css +1 -0
  265. data/public/dist/js/webshims/shims/jme/jme.eot +0 -0
  266. data/public/dist/js/webshims/shims/jme/jme.svg +36 -0
  267. data/public/dist/js/webshims/shims/jme/jme.ttf +0 -0
  268. data/public/dist/js/webshims/shims/jme/jme.woff +0 -0
  269. data/public/dist/js/webshims/shims/jme/mediacontrols-lazy.js +1 -0
  270. data/public/dist/js/webshims/shims/jme/mediacontrols.js +1 -0
  271. data/public/dist/js/webshims/shims/jme/playlist.js +1 -0
  272. data/public/dist/js/webshims/shims/jpicker/images/AlphaBar.png +0 -0
  273. data/public/dist/js/webshims/shims/jpicker/images/Bars.png +0 -0
  274. data/public/dist/js/webshims/shims/jpicker/images/Maps.png +0 -0
  275. data/public/dist/js/webshims/shims/jpicker/images/NoColor.png +0 -0
  276. data/public/dist/js/webshims/shims/jpicker/images/bar-opacity.png +0 -0
  277. data/public/dist/js/webshims/shims/jpicker/images/map-opacity.png +0 -0
  278. data/public/dist/js/webshims/shims/jpicker/images/mappoint.gif +0 -0
  279. data/public/dist/js/webshims/shims/jpicker/images/picker.gif +0 -0
  280. data/public/dist/js/webshims/shims/jpicker/images/preview-opacity.png +0 -0
  281. data/public/dist/js/webshims/shims/jpicker/images/rangearrows.gif +0 -0
  282. data/public/dist/js/webshims/shims/jpicker/jpicker.css +1 -0
  283. data/public/dist/js/webshims/shims/matchMedia.js +3 -0
  284. data/public/dist/js/webshims/shims/mediacapture-picker.js +1 -0
  285. data/public/dist/js/webshims/shims/mediacapture.js +1 -0
  286. data/public/dist/js/webshims/shims/mediaelement-core.js +1 -0
  287. data/public/dist/js/webshims/shims/mediaelement-debug.js +1 -0
  288. data/public/dist/js/webshims/shims/mediaelement-jaris.js +1 -0
  289. data/public/dist/js/webshims/shims/mediaelement-native-fix.js +1 -0
  290. data/public/dist/js/webshims/shims/mediaelement-yt.js +1 -0
  291. data/public/dist/js/webshims/shims/moxie/flash/Moxie.cdn.swf +0 -0
  292. data/public/dist/js/webshims/shims/moxie/flash/Moxie.min.swf +0 -0
  293. data/public/dist/js/webshims/shims/moxie/js/moxie-html4.js +3 -0
  294. data/public/dist/js/webshims/shims/moxie/js/moxie-swf.js +2 -0
  295. data/public/dist/js/webshims/shims/picture.js +1 -0
  296. data/public/dist/js/webshims/shims/plugins/jquery.ui.position.js +11 -0
  297. data/public/dist/js/webshims/shims/range-ui.js +1 -0
  298. data/public/dist/js/webshims/shims/sizzle.js +11 -0
  299. data/public/dist/js/webshims/shims/sticky.js +1 -0
  300. data/public/dist/js/webshims/shims/styles/color-picker.png +0 -0
  301. data/public/dist/js/webshims/shims/styles/forms-ext.css +1 -0
  302. data/public/dist/js/webshims/shims/styles/forms-picker.css +1 -0
  303. data/public/dist/js/webshims/shims/styles/progress.gif +0 -0
  304. data/public/dist/js/webshims/shims/styles/progress.png +0 -0
  305. data/public/dist/js/webshims/shims/styles/shim-ext.css +1 -0
  306. data/public/dist/js/webshims/shims/styles/shim.css +1 -0
  307. data/public/dist/js/webshims/shims/styles/transparent.png +0 -0
  308. data/public/dist/js/webshims/shims/styles/widget.eot +0 -0
  309. data/public/dist/js/webshims/shims/styles/widget.svg +12 -0
  310. data/public/dist/js/webshims/shims/styles/widget.ttf +0 -0
  311. data/public/dist/js/webshims/shims/styles/widget.woff +0 -0
  312. data/public/dist/js/webshims/shims/swf/JarisFLVPlayer.swf +0 -0
  313. data/public/dist/js/webshims/shims/swfmini-embed.js +1 -0
  314. data/public/dist/js/webshims/shims/swfmini.js +6 -0
  315. data/public/dist/js/webshims/shims/track-ui.js +1 -0
  316. data/public/dist/js/webshims/shims/track.js +1 -0
  317. data/public/dist/js/webshims/shims/url.js +1 -0
  318. data/public/dist/js/webshims/shims/usermedia-core.js +1 -0
  319. data/public/dist/js/webshims/shims/usermedia-shim.js +1 -0
  320. data/public/js/bootstrap.min.js +3 -8
  321. data/public/js/jquery-ui.min.js +6 -0
  322. data/public/js/jquery.min.js +4 -0
  323. data/public/js/jquery.t.js +4 -4
  324. data/public/js/sequenceserver.blast.js +20 -18
  325. data/public/js/sequenceserver.js +116 -74
  326. data/sequenceserver.gemspec +20 -16
  327. data/views/400.erb +2 -1
  328. data/views/500.erb +6 -1
  329. data/views/result.erb +38 -18
  330. data/views/search.erb +49 -32
  331. metadata +389 -11
  332. data/public/img/glyphicons-halflings-white.png +0 -0
  333. data/public/img/glyphicons-halflings.png +0 -0
  334. data/public/js/jquery-ui.js +0 -14987
  335. data/public/js/jquery.js +0 -5
  336. data/public/js/jquery.scrollspy.js +0 -74
  337. data/public/sequence.min.js +0 -1
@@ -1,8 +1,14 @@
1
1
  require 'forwardable'
2
2
  require 'tempfile'
3
+ require 'English'
3
4
  require 'ox'
4
5
 
5
6
  require 'sequenceserver/links'
7
+ require 'sequenceserver/blast/exceptions'
8
+ require 'sequenceserver/blast/report'
9
+ require 'sequenceserver/blast/query'
10
+ require 'sequenceserver/blast/hit'
11
+ require 'sequenceserver/blast/hsp'
6
12
 
7
13
  module SequenceServer
8
14
  # Simple wrapper around BLAST+ search algorithms.
@@ -10,478 +16,203 @@ module SequenceServer
10
16
  # `BLAST::ArgumentError` and `BLAST::RuntimeError` signal errors encountered
11
17
  # when attempting a BLAST search.
12
18
  module BLAST
13
- # To signal error in query sequence or options.
14
- #
15
- # ArgumentError is raised when BLAST+'s exit status is 1; see [1].
16
- class ArgumentError < ArgumentError
17
- end
18
-
19
- # To signal internal errors.
20
- #
21
- # RuntimeError is raised when BLAST+'s exits status is one of 2, 3, 4, or
22
- # 255; see [1]. These are rare, infrastructure errors, used internally,
23
- # and of concern only to the admins/developers.
24
- class RuntimeError < RuntimeError
25
- def initialize(status, message)
26
- @status = status
27
- @message = message
28
- end
29
-
30
- attr_reader :status, :message
31
-
32
- def to_s
33
- "#{status}, #{message}"
34
- end
35
- end
36
-
37
- # Capture results per query of a BLAST search.
38
- # @member [String] number
39
- # @member [String] def
40
- # @member [Fixnum] len
41
- # @member [Array(Hit)] hits
42
- Query = Struct.new(:number, :def, :len, :hits) do
43
- def initialize(*args)
44
- args[0] = args[0].to_i
45
- args[1] = "Query_#{args[0]}" if args[1] == 'No definition line'
46
- args[2] = args[2].to_i
47
- @id, *rest = args[1].split
48
- @title = rest.join(' ')
49
- super
50
- end
51
-
52
- def sort_hits_by_evalue!
53
- @hits = hits.sort_by(&:evalue)
54
- end
55
-
56
- attr_reader :id, :title
57
-
58
- alias_method :length, :len
59
- end
60
-
61
- # Hit Object to store all the hits per Query.
62
- # @member [Fixnum] number
63
- # @member [String] id
64
- # @member [String] def
65
- # @member [String] accession
66
- # @member [Fixnum] len
67
- # @member [HSP] hsp
68
- Hit = Struct.new(:number, :id, :title, :accession, :len, :hsps) do
69
- def initialize(*args)
70
- args[0] = args[0].to_i
71
- args[2] = '' if args[2] == 'No definition line'
72
- args[4] = args[4].to_i
73
- super
74
- end
75
-
76
- alias_method :length, :len
77
-
78
- # Hit evalue is the minimum evalue of all HSP(s).
79
- def evalue
80
- hsps.map(&:evalue).min
81
- end
82
-
83
- # Hit score is the sum of bit scores of all HSP(s).
84
- def score
85
- hsps.map(&:bit_score).reduce(:+)
86
- end
87
- end
88
-
89
- # Structure to hold the HSP information about each hit. For more
90
- # information, check the link contained in the references section at the
91
- # end of the file.
92
- HSP = Struct.new(:number, :bit_score, :score, :evalue, :qstart, :qend,
93
- :sstart, :send, :qframe, :sframe, :identity, :positives,
94
- :gaps, :len, :qseq, :sseq, :midline) do
19
+ ERROR_LINE = /\(CArgException.*\)\s(.*)/
95
20
 
96
- INTEGER_ARGS = [0, 2].concat((4..13).to_a)
97
- FLOAT_ARGS = [1, 3]
21
+ ALGORITHMS = %w(blastn blastp blastx tblastn tblastx)
98
22
 
99
- def initialize(*args)
100
- INTEGER_ARGS.each do |i|
101
- args[i] = args[i].to_i
102
- end
23
+ OUTFMT = {
24
+ 'pairwise' => [0, 'txt'],
25
+ 'qa_identity' => [1, 'txt'],
26
+ 'qa_no_identity' => [2, 'txt'],
27
+ 'fqa_identity' => [3, 'txt'],
28
+ 'fqa_no_identity' => [4, 'txt'],
29
+ 'xml' => [5, 'xml'],
30
+ 'tsv' => [6, 'tsv'],
31
+ 'tsv_commented' => [7, 'tsv'],
32
+ 'asn_text' => [8, 'asn'],
33
+ 'asn_binary' => [9, 'asn'],
34
+ 'csv' => [10, 'csv'],
35
+ 'archive' => [11, 'txt']
36
+ } # See [1]
37
+
38
+ class << self
39
+ extend Forwardable
40
+
41
+ def_delegators SequenceServer, :config, :logger
42
+
43
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
44
+ # rubocop:disable Metrics/MethodLength
45
+ def run(params)
46
+ pre_process params
47
+ validate_blast_params params
48
+
49
+ # Compile parameters for BLAST search into a shell executable command.
50
+ #
51
+ # BLAST method to use.
52
+ method = params[:method]
53
+ #
54
+ # BLAST+ expects query sequence as a file.
55
+ qfile = Tempfile.new('sequenceserver_query')
56
+ qfile.puts(params[:sequence])
57
+ qfile.close
58
+ #
59
+ # Retrieve database objects from database id.
60
+ databases = Database[params[:databases]]
61
+ #
62
+ # Concatenate other blast options.
63
+ options = params[:advanced].to_s.strip + defaults
64
+ #
65
+ # blastn implies blastn, not megablast; but let's not interfere if a
66
+ # user specifies `task` herself.
67
+ options << ' -task blastn' if method == 'blastn' && !(options =~ /task/)
68
+
69
+ # Run BLAST search.
70
+ #
71
+ # Command to execute.
72
+ command = "#{method} -db '#{databases.map(&:name).join(' ')}'" \
73
+ " -query '#{qfile.path}' #{options}"
74
+ #
75
+ # Debugging log.
76
+ logger.debug("Executing: #{command}")
77
+ #
78
+ # Temporary files to capture stdout and stderr.
79
+ rfile = Tempfile.new('sequenceserver_blast_result')
80
+ efile = Tempfile.new('sequenceserver_blast_error')
81
+ [rfile, efile].each(&:close)
82
+ #
83
+ # Execute.
84
+ system("#{command} > #{rfile.path} 2> #{efile.path}")
85
+
86
+ # Capture error.
87
+ status = $CHILD_STATUS.exitstatus
88
+ case status
89
+ when 1 # error in query sequence or options; see [1]
90
+ efile.open
91
+
92
+ # Most of the time BLAST+ generates a verbose error message with
93
+ # details we don't require. So we parse out the relevant lines.
94
+ error = efile.each_line do |l|
95
+ break Regexp.last_match[1] if l.match(ERROR_LINE)
96
+ end
103
97
 
104
- FLOAT_ARGS.each do |i|
105
- args[i] = args[i].to_f
98
+ # But sometimes BLAST+ returns the exact/relevant error message.
99
+ # Trying to parse such messages returns nil, and we use the error
100
+ # message from BLAST+ as it is.
101
+ error = efile.rewind && efile.read unless error.is_a? String
102
+
103
+ efile.close
104
+ fail ArgumentError, error
105
+ when 2, 3, 4, 255 # see [1]
106
+ efile.open
107
+ error = efile.read
108
+ efile.close
109
+ fail RuntimeError.new(status, error)
106
110
  end
107
111
 
108
- super
109
- end
110
-
111
- alias_method :length, :len
112
-
113
- end
114
-
115
- # Captures BLAST results from BLAST+'s XML output.
116
- class Report
117
-
118
- include Links
119
-
120
- # Expects a File object and Database objects used to BLAST against.
121
- #
122
- # Parses the XML file into an intermediate representation (ir) and
123
- # constructs an object model from that.
124
- #
125
- # NOTE:
126
- # Databases param is optional for test suite.
127
- def initialize(rfile, databases = nil)
128
- ir = node_to_array Ox.parse(rfile.read).root
129
-
130
- @program = ir[0]
131
- @program_version = ir[1]
132
- @querydb = Array databases
133
- @parameters = {
134
- :matrix => ir[7][0],
135
- :evalue => ir[7][1],
136
- :gapopen => ir[7][2],
137
- :gapextend => ir[7][3],
138
- :filters => ir[7][4]
139
- }
140
-
141
- ir[8].each_with_index do |n, i|
142
- @stats ||= n[5][0]
143
- @queries ||= []
144
- @queries.push(Query.new(n[0], n[2], n[3], []))
145
-
146
- # Ensure a hit object is received. No hits, returns a newline. Note
147
- # that checking to "\n" doesn't work since n[4] = ["\n"]
148
- if n[4] == ["\n"]
149
- @queries[i][:hits] = []
150
- else
151
- n[4].each_with_index do |hits, j|
152
- @queries[i][:hits].push(Hit.new(hits[0], hits[1], hits[2],
153
- hits[3], hits[4], []))
154
- hits[5].each do |hsp|
155
- @queries[i][:hits][j][:hsps].push(HSP.new(*hsp))
156
- end
157
- end
158
- @queries[i].sort_hits_by_evalue!
159
- end
160
- end
112
+ Report.new(File.basename(rfile), databases)
161
113
  end
162
114
 
163
- attr_reader :program, :program_version
164
-
165
- # :nodoc:
166
- # params are defaults provided by BLAST or user input to tweak the
167
- # result. stats are computed metrics provided by BLAST.
168
- #
169
- # BLAST+ doesn't list all input params (like word_size) in the XML
170
- # output. Only matrix, evalue, gapopen, gapextend, and filters.
171
- attr_reader :params, :stats
115
+ def format(params)
116
+ validate_format_params params
172
117
 
173
- attr_reader :querydb
118
+ rfile = create_file_path params['report']
119
+ ofmt = OUTFMT[params['format']]
120
+ type = params['type']
174
121
 
175
- attr_reader :queries
122
+ oname = "seqserv_#{type}_#{Time.now.strftime('%H%M')}.#{ofmt[1]}"
123
+ ofile = Tempfile.new oname
176
124
 
177
- # Helper methods for pretty printing results
125
+ command = "blast_formatter -archive '#{rfile}' -out #{ofile.path}" \
126
+ " -outfmt '#{ofmt[0]} #{params['specifiers']}' 2> /dev/null"
178
127
 
179
- # FIXME: HTML!!
180
- def pretty_evalue(hsp)
181
- hsp.evalue.to_s.sub(/(\d*\.\d*)e?([+-]\d*)?/) do
182
- s = '%.3f' % Regexp.last_match[1]
183
- s << " &times; 10<sup>#{Regexp.last_match[2]}</sup>" if Regexp.last_match[2]
184
- s
185
- end
186
- end
187
-
188
- def identity_fraction(hsp)
189
- "#{hsp.identity}/#{hsp.length}"
190
- end
128
+ logger.debug("Executing: #{command}")
129
+ system command
191
130
 
192
- def positives_fraction(hsp)
193
- "#{hsp.positives}/#{hsp.length}"
131
+ {
132
+ :filepath => ofile.path,
133
+ :filename => oname,
134
+ :type => ofmt[1]
135
+ }
194
136
  end
137
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity
138
+ # rubocop:enable Metrics/MethodLength
195
139
 
196
- def gaps_fraction(hsp)
197
- "#{hsp.gaps}/#{hsp.length}"
140
+ def pre_process(params)
141
+ params[:sequence].strip! unless params[:sequence].nil?
198
142
  end
199
143
 
200
- def identity_percentage(hsp)
201
- "#{'%.2f' % (hsp.identity * 100.0 / hsp.length)}"
144
+ def validate_format_params(params)
145
+ return true if params.include?('report') &&
146
+ params.include?('format') &&
147
+ File.exist?(create_file_path params['report'])
148
+ fail ArgumentError, <<MSG
149
+ Incorrect request parameters. Please ensure that requested file name is
150
+ correct and the file type is either xml or tsv.
151
+ MSG
202
152
  end
203
153
 
204
- def positives_percentage(hsp)
205
- "#{'%.2f' % (hsp.positives * 100.0 / hsp.length)}"
154
+ def validate_blast_params(params)
155
+ validate_blast_method params[:method]
156
+ validate_blast_sequences params[:sequence]
157
+ validate_blast_databases params[:databases]
158
+ validate_blast_options params[:advanced]
206
159
  end
207
160
 
208
- def gaps_percentage(hsp)
209
- "#{'%.2f' % (hsp.gaps * 100.0 / hsp.length)}"
161
+ def defaults
162
+ " -outfmt 11 -num_threads #{config[:num_threads]}"
210
163
  end
211
164
 
212
- # FIXME: Test me.
213
- def pp_hsp(hsp)
214
- # In many of the BLAST algorithms, translated queries are performed
215
- # which has to be taken care while determining end co ordinates of
216
- # query and subject sequences. Since each amino acid is encoded using
217
- # three nucl. referred to as codons, necessary value is multiplied
218
- # to determine the coordinates.
219
-
220
- # blastn and blastp search the nucleotide and protein databases using
221
- # nucleotide and protein queries respectively.
222
- qframe_unit = 1
223
- sframe_unit = 1
224
- # tblastn searches translated nucleotide database using a protein query
225
- if @program == 'tblastn'
226
- sframe_unit = 3
227
- # blastx searches protein database using a translated nucleotide query,
228
- elsif @program == 'blastx'
229
- qframe_unit = 3
230
- # tblastx searches translated nucleotide database using a translated
231
- # nucleotide query.
232
- elsif @program == 'tblastx'
233
- qframe_unit = 3
234
- sframe_unit = 3
235
- end
236
-
237
- qframe_sign = hsp.qframe >= 0 ? 1 : -1
238
- sframe_sign = hsp.sframe >= 0 ? 1 : -1
239
-
240
- chars = 60
241
- lines = (hsp.length / chars.to_f).ceil
242
- width = [hsp.qend, hsp.send, hsp.qstart,
243
- hsp.sstart].map(&:to_s).map(&:length).max
244
-
245
- # blastn results are inconsistent with the other methods as it
246
- # automatically reverse the start and end coordinates (based on
247
- # frame), while for others it has to be inferred.
248
- if @program != 'blastn'
249
- nqseq = hsp.qframe >= 0 ? hsp.qstart : hsp.qend
250
- nsseq = hsp.sframe >= 0 ? hsp.sstart : hsp.send
251
- else
252
- nqseq = hsp.qstart
253
- nsseq = hsp.sstart
254
- end
255
-
256
- s = ''
257
- (1..lines).each do |i|
258
- lqstart = nqseq
259
- lqseq = hsp.qseq[chars * (i - 1), chars]
260
- nqseq += (lqseq.length - lqseq.count('-')) * qframe_unit * qframe_sign
261
- lqend = nqseq - qframe_sign
262
- s << "Query %#{width}d #{lqseq} #{lqend}\n" % lqstart
263
-
264
- lmseq = hsp.midline[chars * (i - 1), chars]
265
- s << "#{' ' * (width + 8)} #{lmseq}\n"
266
-
267
- lsstart = nsseq
268
- lsseq = hsp.sseq[chars * (i - 1), chars]
269
- nsseq += (lsseq.length - lsseq.count('-')) * sframe_unit * sframe_sign
270
- lsend = nsseq - sframe_sign
271
- s << "Subject %#{width}d #{lsseq} #{lsend}\n" % lsstart
272
-
273
- s << "\n" unless i == lines
274
- end
275
- s
165
+ def validate_blast_method(method)
166
+ return true if ALGORITHMS.include? method
167
+ fail ArgumentError, 'BLAST algorithm should be one of:' \
168
+ " #{ALGORITHMS.join(', ')}."
276
169
  end
277
170
 
278
- # FIXME: Document me.
279
- def filter_hsp_stats(hsp)
280
- hsp_stats = {
281
- 'Score' => "#{'%.2f' % hsp[:bit_score]} (#{hsp[:score]})",
282
- 'E value' => "#{pretty_evalue hsp}",
283
- 'Identities' => "#{identity_fraction hsp} " \
284
- "(#{identity_percentage hsp}%)",
285
- 'Gaps' => "#{gaps_fraction hsp} (#{gaps_percentage hsp}%)"
286
- }
287
-
288
- if @program == 'blastp'
289
- hsp_stats['Positives'] = "#{positives_fraction hsp}" \
290
- "(#{positives_percentage hsp}%)"
291
- elsif @program == 'blastx'
292
- hsp_stats['Query frame'] = "#{hsp[:qframe]}"
293
- elsif @program == 'tblastn'
294
- hsp_stats['Hit frame'] = "#{hsp[:sframe]}"
295
- elsif @program == 'tblastx'
296
- hsp_stats['Positives'] = "#{positives_fraction hsp}" \
297
- "(#{positives_percentage hsp}%)"
298
- hsp_stats['Frame'] = "#{hsp[:qframe]}/#{hsp[:sframe]}"
299
- elsif @program == 'blastn'
300
- hsp_stats['Strand'] = "#{hsp[:qframe] > 0 ? '+' : '-'}/" \
301
- "#{hsp[:sframe] > 0 ? '+' : '-'}"
302
- end
303
-
304
- hsp_stats
171
+ def validate_blast_sequences(sequences)
172
+ return true if sequences.is_a?(String) && !sequences.empty?
173
+ fail ArgumentError, 'Sequences should be a non-empty string.'
305
174
  end
306
175
 
307
- def link_per_hit(sequence_id)
308
- links = Links.instance_methods.map {|m| send(m, sequence_id)}
309
-
310
- # Sort links based on :order key (ascending)
311
- links.compact!.sort_by! {|link| link[:order]}
176
+ def validate_blast_databases(database_ids)
177
+ ids = Database.ids
178
+ return true if database_ids.is_a?(Array) && !database_ids.empty? &&
179
+ (ids & database_ids).length == database_ids.length
180
+ fail ArgumentError, 'Database id should be one of:' \
181
+ " #{ids.join("\n")}."
312
182
  end
313
183
 
314
- # Returns an array of database objects which contain the queried
315
- # sequence id.
316
- # NOTE: This function may return more than one database object for
317
- # a single sequence id.
184
+ # Advanced options are specified by the user. Here they are checked for
185
+ # interference with SequenceServer operations.
318
186
  #
319
- # e.g., which_blastdb('SI_2.2.23') => [<Database: ...>, ...]
320
- def which_blastdb(sequence_id)
321
- querydb.select {|db| db.include? sequence_id}
322
- end
323
-
324
- private
325
-
326
- PARSEABLE_AS_ARRAY = %w(Parameters BlastOutput_param Iteration_stat
327
- Statistics Iteration_hits BlastOutput_iterations
328
- Iteration Hit Hit_hsps Hsp)
329
-
330
- def node_to_array(element)
331
- element.nodes.map {|n| node_to_value n}
332
- end
187
+ # Raise ArgumentError if an error has occurred.
188
+ def validate_blast_options(options)
189
+ return true if !options || (options.is_a?(String) &&
190
+ options.strip.empty?)
333
191
 
334
- def node_to_value(node)
335
- # Ensure that the recursion doesn't fails when String value is received.
336
- return node if node.is_a?(String)
337
-
338
- if PARSEABLE_AS_ARRAY.include? node.name
339
- value = node_to_array(node)
340
- else
341
- value = first_text(node)
192
+ unless allowed_chars.match(options)
193
+ fail ArgumentError, 'Invalid characters detected in options.'
342
194
  end
343
- value
344
- end
345
-
346
- def first_text(node)
347
- node.nodes.find {|n| n.is_a? String}
348
- end
349
- end
350
-
351
- ERROR_LINE = /\(CArgException.*\)\s(.*)/
352
-
353
- ALGORITHMS = %w(blastn blastp blastx tblastn tblastx)
354
-
355
- extend self
356
-
357
- extend Forwardable
358
-
359
- def_delegators SequenceServer, :config, :logger
360
-
361
- def run(params)
362
- pre_process params
363
- validate_blast_params params
364
195
 
365
- # Compile parameters for BLAST search into a shell executable command.
366
- #
367
- # BLAST method to use.
368
- method = params[:method]
369
- #
370
- # BLAST+ expects query sequence as a file.
371
- qfile = Tempfile.new('sequenceserver_query')
372
- qfile.puts(params[:sequence])
373
- qfile.close
374
- #
375
- # Retrieve database objects from database id.
376
- databases = Database[params[:databases]]
377
- #
378
- # Concatenate other blast options.
379
- options = params[:advanced].to_s.strip + defaults
380
- #
381
- # blastn implies blastn, not megablast; but let's not interfere if a user
382
- # specifies `task` herself.
383
- if method == 'blastn' and not options =~ /task/
384
- options << ' -task blastn'
385
- end
386
-
387
- # Run BLAST search.
388
- #
389
- # Command to execute.
390
- command = "#{method} -db '#{databases.map(&:name).join(' ')}'" \
391
- " -query '#{qfile.path}' #{options}"
392
- #
393
- # Debugging log.
394
- logger.debug("Executing: #{command}")
395
- #
396
- # Temporary files to capture stdout and stderr.
397
- rfile = Tempfile.new('sequenceserver_blast_result')
398
- efile = Tempfile.new('sequenceserver_blast_error')
399
- [rfile, efile].each(&:close)
400
- #
401
- # Execute.
402
- system("#{command} > #{rfile.path} 2> #{efile.path}")
403
-
404
- # Capture error.
405
- status = $?.exitstatus
406
- case status
407
- when 1 # error in query sequence or options; see [1]
408
- efile.open
409
-
410
- # Most of the time BLAST+ generates a verbose error message with
411
- # details we don't require. So we parse out the relevant lines.
412
- error = efile.each_line do |l|
413
- break Regexp.last_match[1] if l.match(ERROR_LINE)
196
+ if disallowed_options.match(options)
197
+ failedopt = Regexp.last_match[0]
198
+ fail ArgumentError, "Option \"#{failedopt}\" is prohibited."
414
199
  end
415
200
 
416
- # But sometimes BLAST+ returns the exact/relevant error message.
417
- # Trying to parse such messages returns nil, and we use the error
418
- # message from BLAST+ as it is.
419
- error = efile.rewind && efile.read unless error.is_a? String
420
-
421
- efile.close
422
- raise ArgumentError, error
423
- when 2, 3, 4, 255 # see [1]
424
- efile.open
425
- error = efile.read
426
- efile.close
427
- raise RuntimeError.new(status, error)
201
+ true
428
202
  end
429
203
 
430
- # Report the results, ensures that file is closed after execution.
431
- File.open(rfile.path) {|f| Report.new(f, databases)}
432
- end
433
-
434
- def pre_process(params)
435
- unless params[:sequence].nil?
436
- params[:sequence].strip!
204
+ # Returns filename if path exists otherwise returns a path to tmp dir.
205
+ def create_file_path(filename)
206
+ return File.join(Dir.tmpdir, filename) unless File.exist? filename
207
+ filename
437
208
  end
438
- end
439
-
440
- def validate_blast_params(params)
441
- validate_blast_method params[:method]
442
- validate_blast_sequences params[:sequence]
443
- validate_blast_databases params[:databases]
444
- validate_blast_options params[:advanced]
445
- end
446
-
447
- def defaults
448
- " -outfmt 5 -num_threads #{config[:num_threads]}"
449
- end
450
-
451
- def validate_blast_method(method)
452
- return true if ALGORITHMS.include? method
453
- raise ArgumentError, "BLAST algorithm should be one of:" \
454
- " #{ALGORITHMS.join(', ')}."
455
- end
456
209
 
457
- def validate_blast_sequences(sequences)
458
- return true if sequences.is_a? String and not sequences.empty?
459
- raise ArgumentError, 'Sequences should be a non-empty string.'
460
- end
461
-
462
- def validate_blast_databases(database_ids)
463
- ids = Database.ids
464
- return true if database_ids.is_a?(Array) && !database_ids.empty? &&
465
- (ids & database_ids).length == database_ids.length
466
- raise ArgumentError, "Database id should be one of:" \
467
- " #{ids.join("\n")}."
468
- end
469
-
470
- # Advanced options are specified by the user. Here they are checked for
471
- # interference with SequenceServer operations.
472
- # raise ArgumentError if an error has occurred, else return without value
473
- def validate_blast_options(options)
474
- return true if !options || (options.is_a?(String) && options.strip.empty?)
475
-
476
- unless options =~ /\A[a-z0-9\-_\. ']*\Z/i
477
- raise ArgumentError, 'Invalid characters detected in options.'
210
+ def allowed_chars
211
+ /\A[a-z0-9\-_\. ']*\Z/i
478
212
  end
479
213
 
480
- disallowed_options = %w(-out -html -outfmt -db -query)
481
- disallowed_options.each do |o|
482
- if options =~ /#{o}/i
483
- raise ArgumentError, "Option \"#{o}\" is prohibited."
484
- end
214
+ def disallowed_options
215
+ /-out|-html|-outfmt|-db|-query/i
485
216
  end
486
217
  end
487
218
  end