rhoconnect 3.0.0.beta1

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 (361) hide show
  1. data/CHANGELOG +208 -0
  2. data/CREDITS +38 -0
  3. data/Gemfile +18 -0
  4. data/Gemfile.lock +97 -0
  5. data/LICENSE +75 -0
  6. data/README.md +10 -0
  7. data/Rakefile +82 -0
  8. data/bench/bench +7 -0
  9. data/bench/bench_runner.rb +112 -0
  10. data/bench/benchapp/Gemfile +21 -0
  11. data/bench/benchapp/Gemfile.lock +76 -0
  12. data/bench/benchapp/Rakefile +22 -0
  13. data/bench/benchapp/application.rb +39 -0
  14. data/bench/benchapp/config.ru +36 -0
  15. data/bench/benchapp/settings/license.key +1 -0
  16. data/bench/benchapp/settings/settings.yml +20 -0
  17. data/bench/benchapp/sources/mock_adapter.rb +55 -0
  18. data/bench/benchapp/sources/queue_mock_adapter.rb +2 -0
  19. data/bench/benchapp/spec/sources/mock_adapter_spec.rb +25 -0
  20. data/bench/benchapp/spec/sources/queue_mock_adapter_spec.rb +25 -0
  21. data/bench/benchapp/spec/spec_helper.rb +42 -0
  22. data/bench/blobapp/Gemfile +31 -0
  23. data/bench/blobapp/Gemfile.lock +103 -0
  24. data/bench/blobapp/Rakefile +25 -0
  25. data/bench/blobapp/application.rb +44 -0
  26. data/bench/blobapp/config.ru +36 -0
  27. data/bench/blobapp/settings/license.key +1 -0
  28. data/bench/blobapp/settings/settings.yml +16 -0
  29. data/bench/blobapp/sources/blob_adapter.rb +71 -0
  30. data/bench/blobapp/spec/sources/blob_adapter_spec.rb +25 -0
  31. data/bench/blobapp/spec/spec_helper.rb +42 -0
  32. data/bench/lib/bench/cli.rb +16 -0
  33. data/bench/lib/bench/logging.rb +13 -0
  34. data/bench/lib/bench/mock_client.rb +41 -0
  35. data/bench/lib/bench/result.rb +50 -0
  36. data/bench/lib/bench/runner.rb +107 -0
  37. data/bench/lib/bench/session.rb +67 -0
  38. data/bench/lib/bench/statistics.rb +56 -0
  39. data/bench/lib/bench/test_data.rb +98 -0
  40. data/bench/lib/bench/timer.rb +10 -0
  41. data/bench/lib/bench/utils.rb +49 -0
  42. data/bench/lib/bench.rb +129 -0
  43. data/bench/lib/testdata/100-data.txt +148 -0
  44. data/bench/lib/testdata/5-data.txt +9 -0
  45. data/bench/lib/testdata/images/icon.ico +0 -0
  46. data/bench/lib/testdata/images/icon.png +0 -0
  47. data/bench/lib/testdata/images/loading-Landscape.png +0 -0
  48. data/bench/lib/testdata/images/loading-LandscapeLeft.png +0 -0
  49. data/bench/lib/testdata/images/loading-LandscapeRight.png +0 -0
  50. data/bench/lib/testdata/images/loading-Portrait.png +0 -0
  51. data/bench/lib/testdata/images/loading-PortraitUpsideDown.png +0 -0
  52. data/bench/lib/testdata/images/loading.png +0 -0
  53. data/bench/lib/testdata/images/loading@2x.png +0 -0
  54. data/bench/run_bench.sh +42 -0
  55. data/bench/run_blob_script.sh +3 -0
  56. data/bench/run_cud_script.sh +3 -0
  57. data/bench/run_query_md_script.sh +3 -0
  58. data/bench/run_query_only_script.sh +3 -0
  59. data/bench/run_query_script.sh +3 -0
  60. data/bench/scripts/blob_cud_script.rb +98 -0
  61. data/bench/scripts/cud_script.rb +92 -0
  62. data/bench/scripts/helpers.rb +101 -0
  63. data/bench/scripts/query_md_script.rb +45 -0
  64. data/bench/scripts/query_only_script.rb +51 -0
  65. data/bench/scripts/query_script.rb +45 -0
  66. data/bench/spec/bench_spec_helper.rb +33 -0
  67. data/bench/spec/logging_spec.rb +15 -0
  68. data/bench/spec/mock_adapter_spec.rb +61 -0
  69. data/bench/spec/mock_client_spec.rb +65 -0
  70. data/bench/spec/result_spec.rb +61 -0
  71. data/bench/spec/utils_spec.rb +36 -0
  72. data/bin/rhoconnect +34 -0
  73. data/bin/rhoconnect-setup +84 -0
  74. data/doc/async-jobs.txt +69 -0
  75. data/doc/authentication.txt +76 -0
  76. data/doc/benchmarks.txt +168 -0
  77. data/doc/blob-sync.txt +130 -0
  78. data/doc/bulk-sync.txt +102 -0
  79. data/doc/client.txt +432 -0
  80. data/doc/command-line.txt +210 -0
  81. data/doc/contributing.txt +60 -0
  82. data/doc/deploying.txt +82 -0
  83. data/doc/install.txt +28 -0
  84. data/doc/introduction.txt +20 -0
  85. data/doc/licensing.txt +18 -0
  86. data/doc/metadata.txt +458 -0
  87. data/doc/migration.txt +182 -0
  88. data/doc/public/css/print.css +29 -0
  89. data/doc/public/css/screen.css +257 -0
  90. data/doc/public/css/style.css +20 -0
  91. data/doc/push.txt +135 -0
  92. data/doc/release.txt +41 -0
  93. data/doc/rest-api.txt +367 -0
  94. data/doc/source-adapters.txt +325 -0
  95. data/doc/stats-middleware.txt +69 -0
  96. data/doc/testing.txt +222 -0
  97. data/doc/tutorial.txt +315 -0
  98. data/doc/web-console.txt +35 -0
  99. data/examples/simple/Rakefile +14 -0
  100. data/examples/simple/application.rb +27 -0
  101. data/examples/simple/config.ru +49 -0
  102. data/examples/simple/settings/license.key +1 -0
  103. data/examples/simple/settings/settings.yml +23 -0
  104. data/examples/simple/sources/sample_adapter.rb +5 -0
  105. data/examples/simple/sources/simple_adapter.rb +5 -0
  106. data/generators/rhoconnect.rb +119 -0
  107. data/generators/templates/application/Gemfile +21 -0
  108. data/generators/templates/application/Rakefile +22 -0
  109. data/generators/templates/application/application.rb +39 -0
  110. data/generators/templates/application/config.ru +36 -0
  111. data/generators/templates/application/settings/license.key +1 -0
  112. data/generators/templates/application/settings/settings.yml +14 -0
  113. data/generators/templates/application/spec/spec_helper.rb +42 -0
  114. data/generators/templates/source/source_adapter.rb +47 -0
  115. data/generators/templates/source/source_spec.rb +25 -0
  116. data/install.sh +408 -0
  117. data/installer/unix-like/rho_connect_install_checkers.rb +140 -0
  118. data/installer/unix-like/rho_connect_install_constants.rb +51 -0
  119. data/installer/unix-like/rho_connect_install_debian.rb +63 -0
  120. data/installer/unix-like/rho_connect_install_dnd.rb +58 -0
  121. data/installer/unix-like/rho_connect_install_get_params.rb +30 -0
  122. data/installer/unix-like/rho_connect_install_installers.rb +142 -0
  123. data/installer/unix-like/rho_connect_install_utilities.rb +85 -0
  124. data/installer/unix-like/rho_connect_install_yum.rb +63 -0
  125. data/installer/unix-like/rhoinstaller.rb +89 -0
  126. data/installer/utils/create_texts.rb +366 -0
  127. data/installer/utils/install_test.rb +140 -0
  128. data/installer/windows/EnvVarUpdate.nsh +328 -0
  129. data/installer/windows/ServiceLib.nsh +369 -0
  130. data/installer/windows/configUi.ini +44 -0
  131. data/installer/windows/icon.ico +0 -0
  132. data/installer/windows/rhosync.nsi +418 -0
  133. data/installer/windows/uninstall.bat +7 -0
  134. data/lib/rhoconnect/api/admin/get_api_token.rb +14 -0
  135. data/lib/rhoconnect/api/admin/get_license_info.rb +8 -0
  136. data/lib/rhoconnect/api/admin/login.rb +6 -0
  137. data/lib/rhoconnect/api/admin/reset.rb +10 -0
  138. data/lib/rhoconnect/api/admin/stats.rb +21 -0
  139. data/lib/rhoconnect/api/application/bulk_data.rb +7 -0
  140. data/lib/rhoconnect/api/application/clientcreate.rb +8 -0
  141. data/lib/rhoconnect/api/application/clientlogin.rb +6 -0
  142. data/lib/rhoconnect/api/application/clientregister.rb +6 -0
  143. data/lib/rhoconnect/api/application/clientreset.rb +6 -0
  144. data/lib/rhoconnect/api/application/query.rb +7 -0
  145. data/lib/rhoconnect/api/application/queue_updates.rb +6 -0
  146. data/lib/rhoconnect/api/application/search.rb +6 -0
  147. data/lib/rhoconnect/api/client/create_client.rb +3 -0
  148. data/lib/rhoconnect/api/client/delete_client.rb +5 -0
  149. data/lib/rhoconnect/api/client/get_client_params.rb +3 -0
  150. data/lib/rhoconnect/api/client/list_client_docs.rb +12 -0
  151. data/lib/rhoconnect/api/client/list_clients.rb +3 -0
  152. data/lib/rhoconnect/api/client/ping.rb +7 -0
  153. data/lib/rhoconnect/api/source/get_adapter.rb +3 -0
  154. data/lib/rhoconnect/api/source/get_db_doc.rb +7 -0
  155. data/lib/rhoconnect/api/source/get_source_params.rb +3 -0
  156. data/lib/rhoconnect/api/source/list_source_docs.rb +10 -0
  157. data/lib/rhoconnect/api/source/list_sources.rb +15 -0
  158. data/lib/rhoconnect/api/source/push_deletes.rb +8 -0
  159. data/lib/rhoconnect/api/source/push_objects.rb +8 -0
  160. data/lib/rhoconnect/api/source/save_adapter.rb +3 -0
  161. data/lib/rhoconnect/api/source/set_db_doc.rb +8 -0
  162. data/lib/rhoconnect/api/source/set_refresh_time.rb +8 -0
  163. data/lib/rhoconnect/api/source/upload_file.rb +4 -0
  164. data/lib/rhoconnect/api/user/create_user.rb +7 -0
  165. data/lib/rhoconnect/api/user/delete_user.rb +9 -0
  166. data/lib/rhoconnect/api/user/list_users.rb +3 -0
  167. data/lib/rhoconnect/api/user/update_user.rb +4 -0
  168. data/lib/rhoconnect/api_token.rb +19 -0
  169. data/lib/rhoconnect/app.rb +76 -0
  170. data/lib/rhoconnect/bulk_data/bulk_data.rb +88 -0
  171. data/lib/rhoconnect/bulk_data/syncdb.index.schema +4 -0
  172. data/lib/rhoconnect/bulk_data/syncdb.schema +41 -0
  173. data/lib/rhoconnect/bulk_data.rb +2 -0
  174. data/lib/rhoconnect/client.rb +96 -0
  175. data/lib/rhoconnect/client_sync.rb +412 -0
  176. data/lib/rhoconnect/console/app/helpers/auth_helper.rb +22 -0
  177. data/lib/rhoconnect/console/app/helpers/extensions.rb +19 -0
  178. data/lib/rhoconnect/console/app/helpers/helpers.rb +57 -0
  179. data/lib/rhoconnect/console/app/public/ThickBox.css +649 -0
  180. data/lib/rhoconnect/console/app/public/home.css +438 -0
  181. data/lib/rhoconnect/console/app/public/images/foot_logo_rhoconnect.png +0 -0
  182. data/lib/rhoconnect/console/app/public/images/header_halo.jpg +0 -0
  183. data/lib/rhoconnect/console/app/public/images/land_separator.gif +0 -0
  184. data/lib/rhoconnect/console/app/public/images/landing_header.jpg +0 -0
  185. data/lib/rhoconnect/console/app/public/images/logo_rhoconnect.png +0 -0
  186. data/lib/rhoconnect/console/app/public/images/rhomobile_rhohub_logo.png +0 -0
  187. data/lib/rhoconnect/console/app/public/images/tabs_separator.png +0 -0
  188. data/lib/rhoconnect/console/app/public/jqplot/excanvas.min.js +35 -0
  189. data/lib/rhoconnect/console/app/public/jqplot/jqplot.barRenderer.min.js +34 -0
  190. data/lib/rhoconnect/console/app/public/jqplot/jqplot.canvasAxisLabelRenderer.js +187 -0
  191. data/lib/rhoconnect/console/app/public/jqplot/jqplot.canvasAxisTickRenderer.js +226 -0
  192. data/lib/rhoconnect/console/app/public/jqplot/jqplot.canvasTextRenderer.js +408 -0
  193. data/lib/rhoconnect/console/app/public/jqplot/jqplot.categoryAxisRenderer.min.js +34 -0
  194. data/lib/rhoconnect/console/app/public/jqplot/jqplot.cursor.js +952 -0
  195. data/lib/rhoconnect/console/app/public/jqplot/jqplot.dateAxisRenderer.js +313 -0
  196. data/lib/rhoconnect/console/app/public/jqplot/jqplot.dateAxisRenderer.min.js +34 -0
  197. data/lib/rhoconnect/console/app/public/jqplot/jqplot.pointLabels.min.js +34 -0
  198. data/lib/rhoconnect/console/app/public/jqplot/jquery-1.4.2.min.js +154 -0
  199. data/lib/rhoconnect/console/app/public/jqplot/jquery.jqplot.min.css +1 -0
  200. data/lib/rhoconnect/console/app/public/jqplot/jquery.jqplot.min.js +34 -0
  201. data/lib/rhoconnect/console/app/public/main.css +7 -0
  202. data/lib/rhoconnect/console/app/public/reset.css +76 -0
  203. data/lib/rhoconnect/console/app/public/style.css +2201 -0
  204. data/lib/rhoconnect/console/app/public/text.txt +0 -0
  205. data/lib/rhoconnect/console/app/routes/adapter.rb +28 -0
  206. data/lib/rhoconnect/console/app/routes/auth.rb +29 -0
  207. data/lib/rhoconnect/console/app/routes/client.rb +31 -0
  208. data/lib/rhoconnect/console/app/routes/docs.rb +145 -0
  209. data/lib/rhoconnect/console/app/routes/heroku.rb +19 -0
  210. data/lib/rhoconnect/console/app/routes/home.rb +63 -0
  211. data/lib/rhoconnect/console/app/routes/timing.rb +242 -0
  212. data/lib/rhoconnect/console/app/routes/user.rb +117 -0
  213. data/lib/rhoconnect/console/app/views/adapter.erb +16 -0
  214. data/lib/rhoconnect/console/app/views/client.erb +30 -0
  215. data/lib/rhoconnect/console/app/views/content.erb +14 -0
  216. data/lib/rhoconnect/console/app/views/doc.erb +8 -0
  217. data/lib/rhoconnect/console/app/views/docdata.erb +28 -0
  218. data/lib/rhoconnect/console/app/views/docs.erb +30 -0
  219. data/lib/rhoconnect/console/app/views/edituser.erb +13 -0
  220. data/lib/rhoconnect/console/app/views/headermenu.erb +40 -0
  221. data/lib/rhoconnect/console/app/views/home.erb +24 -0
  222. data/lib/rhoconnect/console/app/views/index.erb +58 -0
  223. data/lib/rhoconnect/console/app/views/jqplot.erb +52 -0
  224. data/lib/rhoconnect/console/app/views/layout.erb +165 -0
  225. data/lib/rhoconnect/console/app/views/login.erb +26 -0
  226. data/lib/rhoconnect/console/app/views/newuser.erb +17 -0
  227. data/lib/rhoconnect/console/app/views/ping.erb +28 -0
  228. data/lib/rhoconnect/console/app/views/result.erb +11 -0
  229. data/lib/rhoconnect/console/app/views/rightboxlinks.erb +15 -0
  230. data/lib/rhoconnect/console/app/views/select_doc.erb +17 -0
  231. data/lib/rhoconnect/console/app/views/upload_doc.erb +23 -0
  232. data/lib/rhoconnect/console/app/views/user.erb +29 -0
  233. data/lib/rhoconnect/console/app/views/users.erb +12 -0
  234. data/lib/rhoconnect/console/rhoconnect_api.rb +245 -0
  235. data/lib/rhoconnect/console/server.rb +32 -0
  236. data/lib/rhoconnect/console.rb +3 -0
  237. data/lib/rhoconnect/cors.rb +229 -0
  238. data/lib/rhoconnect/credential.rb +9 -0
  239. data/lib/rhoconnect/db_adapter.rb +46 -0
  240. data/lib/rhoconnect/document.rb +49 -0
  241. data/lib/rhoconnect/dynamic_adapter.rb +91 -0
  242. data/lib/rhoconnect/generator.rb +1 -0
  243. data/lib/rhoconnect/jobs/bulk_data_job.rb +203 -0
  244. data/lib/rhoconnect/jobs/ping_job.rb +46 -0
  245. data/lib/rhoconnect/jobs/source_job.rb +16 -0
  246. data/lib/rhoconnect/license.rb +86 -0
  247. data/lib/rhoconnect/lock_ops.rb +11 -0
  248. data/lib/rhoconnect/model.rb +414 -0
  249. data/lib/rhoconnect/ping/android.rb +56 -0
  250. data/lib/rhoconnect/ping/apple.rb +52 -0
  251. data/lib/rhoconnect/ping/blackberry.rb +56 -0
  252. data/lib/rhoconnect/ping.rb +3 -0
  253. data/lib/rhoconnect/read_state.rb +31 -0
  254. data/lib/rhoconnect/rho_indifferent_access.rb +88 -0
  255. data/lib/rhoconnect/server/views/index.erb +13 -0
  256. data/lib/rhoconnect/server.rb +286 -0
  257. data/lib/rhoconnect/source.rb +289 -0
  258. data/lib/rhoconnect/source_adapter.rb +123 -0
  259. data/lib/rhoconnect/source_sync.rb +302 -0
  260. data/lib/rhoconnect/stats/middleware.rb +20 -0
  261. data/lib/rhoconnect/stats/record.rb +108 -0
  262. data/lib/rhoconnect/store.rb +232 -0
  263. data/lib/rhoconnect/tasks.rb +350 -0
  264. data/lib/rhoconnect/test_methods.rb +220 -0
  265. data/lib/rhoconnect/user.rb +95 -0
  266. data/lib/rhoconnect/version.rb +3 -0
  267. data/lib/rhoconnect/x_domain_session_wrapper.rb +53 -0
  268. data/lib/rhoconnect.rb +285 -0
  269. data/rhoconnect.gemspec +67 -0
  270. data/spec/api/admin/api_token_spec.rb +14 -0
  271. data/spec/api/admin/get_api_token_spec.rb +36 -0
  272. data/spec/api/admin/get_license_info_spec.rb +38 -0
  273. data/spec/api/admin/reset_spec.rb +22 -0
  274. data/spec/api/admin/stats_spec.rb +66 -0
  275. data/spec/api/api_helper.rb +21 -0
  276. data/spec/api/application/rhoconnect_api_spec.rb +548 -0
  277. data/spec/api/client/create_client_spec.rb +13 -0
  278. data/spec/api/client/delete_client_spec.rb +13 -0
  279. data/spec/api/client/get_client_params_spec.rb +18 -0
  280. data/spec/api/client/list_client_docs_spec.rb +32 -0
  281. data/spec/api/client/list_clients_spec.rb +22 -0
  282. data/spec/api/client/ping_spec.rb +23 -0
  283. data/spec/api/rhosync_api_spec.rb.orig +606 -0
  284. data/spec/api/source/adapter_spec.rb +29 -0
  285. data/spec/api/source/get_db_doc_spec.rb +21 -0
  286. data/spec/api/source/get_source_params_spec.rb +32 -0
  287. data/spec/api/source/list_source_docs_spec.rb +25 -0
  288. data/spec/api/source/list_sources_spec.rb +26 -0
  289. data/spec/api/source/push_deletes_spec.rb +18 -0
  290. data/spec/api/source/push_objects_spec.rb +27 -0
  291. data/spec/api/source/set_db_doc_spec.rb +19 -0
  292. data/spec/api/source/set_refresh_time_spec.rb +43 -0
  293. data/spec/api/source/upload_file_spec.rb +26 -0
  294. data/spec/api/user/create_user_spec.rb +16 -0
  295. data/spec/api/user/delete_user_spec.rb +36 -0
  296. data/spec/api/user/list_users_spec.rb +30 -0
  297. data/spec/api/user/update_user_spec.rb +31 -0
  298. data/spec/api_token_spec.rb +14 -0
  299. data/spec/app_spec.rb +18 -0
  300. data/spec/apps/emptyapp/application.rb +27 -0
  301. data/spec/apps/emptyapp/settings/license.key +1 -0
  302. data/spec/apps/emptyapp/settings/settings.yml +14 -0
  303. data/spec/apps/rhotestapp/Rakefile +1 -0
  304. data/spec/apps/rhotestapp/application.rb +19 -0
  305. data/spec/apps/rhotestapp/config.ru +1 -0
  306. data/spec/apps/rhotestapp/settings/apple_fake_cert.pem +1 -0
  307. data/spec/apps/rhotestapp/settings/license.key +1 -0
  308. data/spec/apps/rhotestapp/settings/settings.yml +36 -0
  309. data/spec/apps/rhotestapp/sources/base_adapter.rb +9 -0
  310. data/spec/apps/rhotestapp/sources/fixed_schema_adapter.rb +28 -0
  311. data/spec/apps/rhotestapp/sources/sample_adapter.rb +71 -0
  312. data/spec/apps/rhotestapp/sources/simple_adapter.rb +41 -0
  313. data/spec/apps/rhotestapp/sources/sub_adapter.rb +7 -0
  314. data/spec/apps/rhotestapp/vendor/mygem-0.1.0/lib/mygem/mygem.rb +8 -0
  315. data/spec/apps/rhotestapp/vendor/mygem-0.1.0/lib/mygem.rb +1 -0
  316. data/spec/bulk_data/bulk_data_spec.rb +97 -0
  317. data/spec/client_spec.rb +124 -0
  318. data/spec/client_sync_spec.rb +774 -0
  319. data/spec/doc/base.html +72 -0
  320. data/spec/doc/doc_spec.rb +376 -0
  321. data/spec/doc/footer.html +4 -0
  322. data/spec/doc/header.html +30 -0
  323. data/spec/document_spec.rb +31 -0
  324. data/spec/dynamic_adapter_spec.rb +43 -0
  325. data/spec/factories/factories.rb +17 -0
  326. data/spec/generator/generator_spec.rb +58 -0
  327. data/spec/generator/generator_spec_helper.rb +9 -0
  328. data/spec/jobs/bulk_data_job_spec.rb +125 -0
  329. data/spec/jobs/ping_job_spec.rb +102 -0
  330. data/spec/jobs/source_job_spec.rb +29 -0
  331. data/spec/license_spec.rb +66 -0
  332. data/spec/model_spec.rb +273 -0
  333. data/spec/perf/bulk_data_perf_spec.rb +32 -0
  334. data/spec/perf/perf_spec_helper.rb +50 -0
  335. data/spec/perf/store_perf_spec.rb +27 -0
  336. data/spec/ping/android_spec.rb +91 -0
  337. data/spec/ping/apple_spec.rb +70 -0
  338. data/spec/ping/blackberry_spec.rb +62 -0
  339. data/spec/read_state_spec.rb +35 -0
  340. data/spec/rhosync_spec.rb +75 -0
  341. data/spec/server/cors_spec.rb +287 -0
  342. data/spec/server/server_spec.rb +470 -0
  343. data/spec/server/x_domain_session_wrapper_spec.rb +150 -0
  344. data/spec/source_adapter_spec.rb +125 -0
  345. data/spec/source_spec.rb +114 -0
  346. data/spec/source_sync_spec.rb +328 -0
  347. data/spec/spec.opts +4 -0
  348. data/spec/spec_helper.rb +231 -0
  349. data/spec/stats/middleware_spec.rb +53 -0
  350. data/spec/stats/record_spec.rb +92 -0
  351. data/spec/store_spec.rb +251 -0
  352. data/spec/support/shared_examples.rb +168 -0
  353. data/spec/sync_states_spec.rb +72 -0
  354. data/spec/test_methods_spec.rb +134 -0
  355. data/spec/testdata/1000-data.txt +1414 -0
  356. data/spec/testdata/compressed/compress-data.txt +1 -0
  357. data/spec/testdata/upload1.txt +1 -0
  358. data/spec/testdata/upload2.txt +1 -0
  359. data/spec/user_spec.rb +139 -0
  360. data/tasks/redis.rake +206 -0
  361. metadata +706 -0
@@ -0,0 +1,44 @@
1
+ ; Ini file generated by the HM NIS Edit IO designer.
2
+ [Settings]
3
+ NumFields=9
4
+
5
+ [Field 1]
6
+ Type=Groupbox
7
+ Text=Apache
8
+ Left=5
9
+ Right=148
10
+ Top=11
11
+ Bottom=59
12
+
13
+ [Field 2]
14
+ Type=Label
15
+ Text=Port:
16
+ Left=8
17
+ Right=42
18
+ Top=40
19
+ Bottom=49
20
+
21
+ [Field 3]
22
+ Type=Text
23
+ State=example@example.com
24
+ Left=54
25
+ Right=144
26
+ Top=25
27
+ Bottom=36
28
+
29
+ [Field 4]
30
+ Type=Text
31
+ State=80
32
+ Left=54
33
+ Right=88
34
+ Top=38
35
+ Bottom=49
36
+
37
+ [Field 5]
38
+ Type=Label
39
+ Text=Admin Email:
40
+ Left=8
41
+ Right=48
42
+ Top=25
43
+ Bottom=36
44
+
Binary file
@@ -0,0 +1,418 @@
1
+ ;======================================================
2
+ ; Include
3
+
4
+ !include "MUI.nsh"
5
+ !include "EnvVarUpdate.nsh"
6
+
7
+ ;======================================================
8
+ ; Installer Information
9
+
10
+ Name "RhoSync"
11
+ OutFile "RhoSync_setup.exe"
12
+ InstallDir C:\RhoSync
13
+ BrandingText " "
14
+ ;======================================================
15
+ ; Modern Interface Configuration
16
+
17
+ !define MUI_ICON "icon.ico"
18
+ !define MUI_UNICON "icon.ico"
19
+ !define MUI_HEADERIMAGE
20
+ !define MUI_ABORTWARNING
21
+ !define MUI_COMPONENTSPAGE_SMALLDESC
22
+ !define MUI_HEADERIMAGE_BITMAP_NOSTRETCH
23
+ !define MUI_FINISHPAGE
24
+ !define MUI_FINISHPAGE_TEXT "Thank you for installing RhoSync. \r\n\n\n"
25
+
26
+
27
+ ;======================================================
28
+ ; Pages
29
+
30
+ !insertmacro MUI_PAGE_WELCOME
31
+ !insertmacro MUI_PAGE_COMPONENTS
32
+ !insertmacro MUI_PAGE_DIRECTORY
33
+ !insertmacro MUI_PAGE_INSTFILES
34
+ Page custom customerConfig
35
+ !insertmacro MUI_PAGE_FINISH
36
+
37
+ ;======================================================
38
+ ; Languages
39
+
40
+ !insertmacro MUI_LANGUAGE "English"
41
+
42
+ ;======================================================
43
+ ; Reserve Files
44
+
45
+ ReserveFile "configUi.ini"
46
+ !insertmacro MUI_RESERVEFILE_INSTALLOPTIONS
47
+
48
+
49
+ ;======================================================
50
+ ; Variables
51
+ var varApacheEmail
52
+ var varApachePort
53
+ var varDbPass
54
+
55
+ ;======================================================
56
+ ; Sections
57
+
58
+ # start default section
59
+ section
60
+
61
+ # set the installation directory as the destination for the following actions
62
+ setOutPath $INSTDIR
63
+
64
+ # create the uninstaller
65
+ writeUninstaller "$INSTDIR\uninstall.exe"
66
+
67
+ # create a shortcut named "new shortcut" in the start menu programs directory
68
+ # point the new shortcut at the program uninstaller
69
+ createShortCut "$SMPROGRAMS\Uninstall RhoSync.lnk" "$INSTDIR\uninstall.exe"
70
+ sectionEnd
71
+
72
+ # uninstaller section start
73
+ section "uninstall"
74
+
75
+ # first, delete the uninstaller
76
+ delete "$INSTDIR\uninstall.exe"
77
+
78
+ # second, remove the link from the start menu
79
+ delete "$SMPROGRAMS\Uninstall RhoSync.lnk"
80
+
81
+ ExecWait 'net stop apache2.2'
82
+ ExecWait 'net stop redis'
83
+ ExecWait 'net stop rhosync-1'
84
+ ExecWait 'net stop rhosync-2'
85
+ ExecWait 'net stop rhosync-3'
86
+ ExecWait 'sc delete apache2.2'
87
+ ExecWait 'sc delete redis'
88
+ ExecWait 'sc delete rhosync-1'
89
+ ExecWait 'sc delete rhosync-2'
90
+ ExecWait 'sc delete rhosync-3'
91
+
92
+ Push "PATH"
93
+ Push "R"
94
+ Push "HKLM"
95
+ Push "$INSTDIR\ruby\bin"
96
+ Call un.EnvVarUpdate
97
+ Pop $R0
98
+
99
+ Push "PATH"
100
+ Push "R"
101
+ Push "HKLM"
102
+ Push "$INSTDIR\redis-2.2.12"
103
+ Call un.EnvVarUpdate
104
+ Pop $R0
105
+
106
+ DeleteRegValue HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment" REDIS_HOME
107
+
108
+ RMDir /r /REBOOTOK $INSTDIR
109
+
110
+
111
+ # uninstaller section end
112
+ sectionEnd
113
+
114
+
115
+ Section "Apache" apache2Section
116
+
117
+ SetOutPath $INSTDIR
118
+
119
+ File /r "apache2"
120
+
121
+ Push $INSTDIR
122
+ Push "\"
123
+ Call StrSlash
124
+ Pop $R0
125
+
126
+
127
+ Push SERVERROOT
128
+ Push $R0/apache2
129
+ Push all
130
+ Push all
131
+ Push $INSTDIR\apache2\conf\httpd.conf
132
+ Call AdvReplaceInFile
133
+
134
+ Push DOCROOT
135
+ Push $R0/apache2/htdocs
136
+ Push all
137
+ Push all
138
+ Push $INSTDIR\apache2\conf\httpd.conf
139
+ Call AdvReplaceInFile
140
+
141
+ Push CGIBIN
142
+ Push $R0/apache2/cgi-bin
143
+ Push all
144
+ Push all
145
+ Push $INSTDIR\apache2\conf\httpd.conf
146
+ Call AdvReplaceInFile
147
+
148
+
149
+ SectionEnd
150
+
151
+ Section "Ruby" rubySection
152
+
153
+ SetOutPath $INSTDIR
154
+
155
+ File /r "ruby"
156
+
157
+ ;add to path here
158
+
159
+ Push "PATH"
160
+ Push "P"
161
+ Push "HKLM"
162
+ Push "$INSTDIR\ruby\bin"
163
+ Call EnvVarUpdate
164
+ Pop $R0
165
+
166
+
167
+ SectionEnd
168
+
169
+ Section "Redis" redisSection
170
+
171
+ SetOutPath $INSTDIR
172
+
173
+ File /r "redis-2.2.12"
174
+
175
+ ;add to path here
176
+
177
+ Push "PATH"
178
+ Push "P"
179
+ Push "HKLM"
180
+ Push "$INSTDIR\redis-2.2.12"
181
+ Call EnvVarUpdate
182
+ Pop $R0
183
+
184
+ Push "REDIS_HOME"
185
+ Push "P"
186
+ Push "HKLM"
187
+ Push "$INSTDIR\redis-2.2.12"
188
+ Call EnvVarUpdate
189
+ Pop $R0
190
+
191
+ ExecWait '$INSTDIR\redis-2.2.12\redis-service.exe install' $0
192
+ StrCmp $0 "0" continue wrong
193
+
194
+ wrong:
195
+ MessageBox MB_OK "Error installing service"
196
+
197
+ continue:
198
+
199
+ ExecWait 'net start redis'
200
+
201
+ SectionEnd
202
+
203
+ Section "rhosync" rhosyncSection
204
+
205
+ SetOutPath $INSTDIR
206
+
207
+ File /r "rhosync"
208
+
209
+ ExecWait '$INSTDIR\rhosync\services\rhosync-service1.exe install' $0
210
+ ExecWait '$INSTDIR\rhosync\services\rhosync-service2.exe install' $0
211
+ ExecWait '$INSTDIR\rhosync\services\rhosync-service3.exe install' $0
212
+
213
+ ExecWait 'net start rhosync-1'
214
+ ExecWait 'net start rhosync-2'
215
+ ExecWait 'net start rhosync-3'
216
+ SectionEnd
217
+
218
+
219
+ ;======================================================
220
+ ;Descriptions
221
+
222
+ ;Language strings
223
+ LangString DESC_InstallApache ${LANG_ENGLISH} "This installs the Apache 2.2 webserver"
224
+ LangString DESC_InstallRuby ${LANG_ENGLISH} "This installs ruby 1.8.7 with preinstalled gems for rhosync"
225
+ LangString DESC_InstallRedis ${LANG_ENGLISH} "This installs redis 1.2.6"
226
+
227
+ ;Assign language strings to sections
228
+ !insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
229
+ !insertmacro MUI_DESCRIPTION_TEXT ${apache2Section} $(DESC_InstallApache)
230
+ !insertmacro MUI_DESCRIPTION_TEXT ${redisSection} $(DESC_InstallRedis)
231
+ !insertmacro MUI_DESCRIPTION_TEXT ${rubySection} $(DESC_InstallRuby)
232
+ !insertmacro MUI_FUNCTION_DESCRIPTION_END
233
+
234
+ ;======================================================
235
+ ; Functions
236
+
237
+ Function .onInit
238
+ !insertmacro MUI_INSTALLOPTIONS_EXTRACT "configUi.ini"
239
+ FunctionEnd
240
+ ;==============================================================
241
+ ; Custom functions
242
+
243
+ LangString TEXT_IO_TITLE ${LANG_ENGLISH} "Configuration page"
244
+ LangString TEXT_IO_SUBTITLE ${LANG_ENGLISH} "This page will update application files based on your system configuration."
245
+
246
+
247
+ Function customerConfig
248
+ !insertmacro MUI_HEADER_TEXT "$(TEXT_IO_TITLE)" "$(TEXT_IO_SUBTITLE)"
249
+ !insertmacro MUI_INSTALLOPTIONS_DISPLAY "configUi.ini"
250
+ !insertmacro MUI_INSTALLOPTIONS_READ $varApacheEmail "configUi.ini" "Field 3" "State"
251
+ !insertmacro MUI_INSTALLOPTIONS_READ $varApachePort "configUi.ini" "Field 4" "State"
252
+
253
+ Push SERVERADMIN
254
+ Push $varApacheEmail
255
+ Push all
256
+ Push all
257
+ Push $INSTDIR\apache2\conf\httpd.conf
258
+ Call AdvReplaceInFile
259
+
260
+ Push SERVERPORT
261
+ Push $varApachePort
262
+ Push all
263
+ Push all
264
+ Push $INSTDIR\apache2\conf\httpd.conf
265
+ Call AdvReplaceInFile
266
+
267
+ ExecWait '"$INSTDIR\apache2\bin\httpd.exe" -k install'
268
+ ExecWait 'net start Apache2.2'
269
+
270
+ FunctionEnd
271
+
272
+
273
+ Function AdvReplaceInFile
274
+ Exch $0 ;file to replace in
275
+ Exch
276
+ Exch $1 ;number to replace after
277
+ Exch
278
+ Exch 2
279
+ Exch $2 ;replace and onwards
280
+ Exch 2
281
+ Exch 3
282
+ Exch $3 ;replace with
283
+ Exch 3
284
+ Exch 4
285
+ Exch $4 ;to replace
286
+ Exch 4
287
+ Push $5 ;minus count
288
+ Push $6 ;universal
289
+ Push $7 ;end string
290
+ Push $8 ;left string
291
+ Push $9 ;right string
292
+ Push $R0 ;file1
293
+ Push $R1 ;file2
294
+ Push $R2 ;read
295
+ Push $R3 ;universal
296
+ Push $R4 ;count (onwards)
297
+ Push $R5 ;count (after)
298
+ Push $R6 ;temp file name
299
+
300
+ GetTempFileName $R6
301
+ FileOpen $R1 $0 r ;file to search in
302
+ FileOpen $R0 $R6 w ;temp file
303
+ StrLen $R3 $4
304
+ StrCpy $R4 -1
305
+ StrCpy $R5 -1
306
+
307
+ loop_read:
308
+ ClearErrors
309
+ FileRead $R1 $R2 ;read line
310
+ IfErrors exit
311
+
312
+ StrCpy $5 0
313
+ StrCpy $7 $R2
314
+
315
+ loop_filter:
316
+ IntOp $5 $5 - 1
317
+ StrCpy $6 $7 $R3 $5 ;search
318
+ StrCmp $6 "" file_write2
319
+ StrCmp $6 $4 0 loop_filter
320
+
321
+ StrCpy $8 $7 $5 ;left part
322
+ IntOp $6 $5 + $R3
323
+ IntCmp $6 0 is0 not0
324
+ is0:
325
+ StrCpy $9 ""
326
+ Goto done
327
+ not0:
328
+ StrCpy $9 $7 "" $6 ;right part
329
+ done:
330
+ StrCpy $7 $8$3$9 ;re-join
331
+
332
+ IntOp $R4 $R4 + 1
333
+ StrCmp $2 all file_write1
334
+ StrCmp $R4 $2 0 file_write2
335
+ IntOp $R4 $R4 - 1
336
+
337
+ IntOp $R5 $R5 + 1
338
+ StrCmp $1 all file_write1
339
+ StrCmp $R5 $1 0 file_write1
340
+ IntOp $R5 $R5 - 1
341
+ Goto file_write2
342
+
343
+ file_write1:
344
+ FileWrite $R0 $7 ;write modified line
345
+ Goto loop_read
346
+
347
+ file_write2:
348
+ FileWrite $R0 $R2 ;write unmodified line
349
+ Goto loop_read
350
+
351
+ exit:
352
+ FileClose $R0
353
+ FileClose $R1
354
+
355
+ SetDetailsPrint none
356
+ Delete $0
357
+ Rename $R6 $0
358
+ Delete $R6
359
+ SetDetailsPrint both
360
+
361
+ Pop $R6
362
+ Pop $R5
363
+ Pop $R4
364
+ Pop $R3
365
+ Pop $R2
366
+ Pop $R1
367
+ Pop $R0
368
+ Pop $9
369
+ Pop $8
370
+ Pop $7
371
+ Pop $6
372
+ Pop $5
373
+ Pop $0
374
+ Pop $1
375
+ Pop $2
376
+ Pop $3
377
+ Pop $4
378
+ FunctionEnd
379
+
380
+
381
+
382
+ ; Push $filenamestring (e.g. 'c:\this\and\that\filename.htm')
383
+ ; Push "\"
384
+ ; Call StrSlash
385
+ ; Pop $R0
386
+ ; ;Now $R0 contains 'c:/this/and/that/filename.htm'
387
+ Function StrSlash
388
+ Exch $R3 ; $R3 = needle ("\" or "/")
389
+ Exch
390
+ Exch $R1 ; $R1 = String to replacement in (haystack)
391
+ Push $R2 ; Replaced haystack
392
+ Push $R4 ; $R4 = not $R3 ("/" or "\")
393
+ Push $R6
394
+ Push $R7 ; Scratch reg
395
+ StrCpy $R2 ""
396
+ StrLen $R6 $R1
397
+ StrCpy $R4 "\"
398
+ StrCmp $R3 "/" loop
399
+ StrCpy $R4 "/"
400
+ loop:
401
+ StrCpy $R7 $R1 1
402
+ StrCpy $R1 $R1 $R6 1
403
+ StrCmp $R7 $R3 found
404
+ StrCpy $R2 "$R2$R7"
405
+ StrCmp $R1 "" done loop
406
+ found:
407
+ StrCpy $R2 "$R2$R4"
408
+ StrCmp $R1 "" done loop
409
+ done:
410
+ StrCpy $R3 $R2
411
+ Pop $R7
412
+ Pop $R6
413
+ Pop $R4
414
+ Pop $R2
415
+ Pop $R1
416
+ Exch $R3
417
+ FunctionEnd
418
+
@@ -0,0 +1,7 @@
1
+ net stop apache2.2
2
+ net stop mysql
3
+ net stop rhosync
4
+ sc delete apache2.2
5
+ sc delete mysql
6
+ sc delete rhosync
7
+
@@ -0,0 +1,14 @@
1
+ Server.api :get_api_token, :admin do |params,user|
2
+ warning_message = "API method 'get_api_token' is deprecated.
3
+ Use 'login' method only, it returns api token in response's body"
4
+ params[:warning] = warning_message unless params.nil?
5
+ do_get_api_token(params, user)
6
+ end
7
+
8
+ def do_get_api_token(params, user)
9
+ if user and user.admin == 1 and user.token
10
+ user.token.value
11
+ else
12
+ raise ApiException.new(422, "Invalid/missing API user")
13
+ end
14
+ end
@@ -0,0 +1,8 @@
1
+ Server.api :get_license_info, :admin do |params,user|
2
+ {:rhoconnect_version => Rhoconnect.license.rhoconnect_version,
3
+ :licensee => Rhoconnect.license.licensee,
4
+ :seats => Rhoconnect.license.seats,
5
+ :issued => Rhoconnect.license.issued,
6
+ :available => Rhoconnect.license.available }.to_json
7
+ end
8
+
@@ -0,0 +1,6 @@
1
+ Server.api :login, :admin do |params,user,server|
2
+ token = ''
3
+ server.logout
4
+ server.do_login
5
+ token = do_get_api_token(params, server.current_user)
6
+ end
@@ -0,0 +1,10 @@
1
+ Server.api :reset, :admin do |params,user|
2
+ Store.db.flushdb
3
+ app_klass = Object.const_get(camelize(APP_NAME))
4
+ if app_klass.singleton_methods.map(&:to_sym).include?(:initializer)
5
+ app_klass.send :initializer, Rhoconnect.base_directory
6
+ end
7
+ # restoring previous token value after flushdb
8
+ user.token = params[:api_token]
9
+ "DB reset"
10
+ end
@@ -0,0 +1,21 @@
1
+ Server.api :stats, :admin do |params,user|
2
+ if Rhoconnect.stats == true
3
+ names = params[:names]
4
+ if names
5
+ Rhoconnect::Stats::Record.keys(names).to_json
6
+ else
7
+ metric = params[:metric]
8
+ rtype = Rhoconnect::Stats::Record.rtype(metric)
9
+ if rtype == 'zset'
10
+ # returns [] if no results
11
+ Rhoconnect::Stats::Record.range(metric,params[:start],params[:finish]).to_json
12
+ elsif rtype == 'string'
13
+ Rhoconnect::Stats::Record.get_value(metric) || ''
14
+ else
15
+ raise ApiException.new(404, "Unknown metric")
16
+ end
17
+ end
18
+ else
19
+ raise ApiException.new(500, "Stats not enabled")
20
+ end
21
+ end
@@ -0,0 +1,7 @@
1
+ Server.api :bulk_data, :application, :get do |params,user,server|
2
+ server.catch_all do
3
+ server.content_type :json
4
+ data = ClientSync.bulk_data(params[:partition].to_sym,server.current_client)
5
+ data.to_json
6
+ end
7
+ end
@@ -0,0 +1,8 @@
1
+ Server.api :clientcreate, :application, :get do |params,user,server|
2
+ server.catch_all do
3
+ server.content_type :json
4
+ client = Client.create(:user_id => server.current_user.id,:app_id => server.current_app.id)
5
+ client.update_fields(params)
6
+ { "client" => { "client_id" => client.id.to_s } }.merge!(server.source_config).to_json
7
+ end
8
+ end
@@ -0,0 +1,6 @@
1
+ Server.api :clientlogin, :application, :post do |params,user,server|
2
+ server.catch_all do
3
+ server.logout
4
+ server.do_login
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ Server.api :clientregister, :application, :post do |params,user,server|
2
+ server.catch_all do
3
+ server.current_client.update_fields(params)
4
+ server.source_config.to_json
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ Server.api :clientreset, :application, :get do |params,user,server|
2
+ server.catch_all do
3
+ ClientSync.reset(server.current_client, params)
4
+ server.source_config.to_json
5
+ end
6
+ end
@@ -0,0 +1,7 @@
1
+ Server.api :query, :application, :get do |params,user,server|
2
+ server.catch_all do
3
+ server.content_type :json
4
+ res = server.current_client_sync.send_cud(params[:token],params[:query]).to_json
5
+ res
6
+ end
7
+ end
@@ -0,0 +1,6 @@
1
+ Server.api :queue_updates, :application, :post do |params,user,server|
2
+ server.catch_all do
3
+ server.current_client_sync.receive_cud(params)
4
+ server.status 200
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ Server.api :search, :application, :get do |params,user,server|
2
+ server.catch_all do
3
+ server.content_type :json
4
+ ClientSync.search_all(server.current_client,params).to_json
5
+ end
6
+ end
@@ -0,0 +1,3 @@
1
+ Server.api :create_client, :client do |params,user|
2
+ Client.create(:user_id => params[:user_id],:app_id => APP_NAME).id
3
+ end
@@ -0,0 +1,5 @@
1
+ Server.api :delete_client, :client do |params,user|
2
+ Client.load(params[:client_id],{:source_name => '*'}).delete
3
+ User.load(params[:user_id]).clients.delete(params[:client_id])
4
+ "Client deleted"
5
+ end
@@ -0,0 +1,3 @@
1
+ Server.api :get_client_params, :client do |params,user|
2
+ Client.load(params[:client_id],{:source_name => '*'}).to_array.to_json
3
+ end
@@ -0,0 +1,12 @@
1
+ Server.api :list_client_docs, :client do |params,user|
2
+ c = Client.load(params[:client_id],{:source_name => params[:source_id]})
3
+ res = {}
4
+ [:cd,:cd_size,:create,:update,:delete,
5
+ :page,:delete_page,:create_links,:create_links_page,:page_token,
6
+ :create_errors,:update_errors,:delete_errors,:login_error,:logoff_error,
7
+ :search,:search_token,:search_errors].each do |doc|
8
+ db_key = c.docname(doc)
9
+ res.merge!(doc => db_key)
10
+ end
11
+ res.to_json
12
+ end
@@ -0,0 +1,3 @@
1
+ Server.api :list_clients, :client do |params,user|
2
+ User.load(params[:user_id]).clients.members.to_json
3
+ end
@@ -0,0 +1,7 @@
1
+ Server.api :ping, :client do |params,user|
2
+ if params['async']
3
+ PingJob.enqueue(params)
4
+ else
5
+ PingJob.perform(params)
6
+ end
7
+ end
@@ -0,0 +1,3 @@
1
+ Server.api :get_adapter, :source do |params,user|
2
+ {:adapter_url => Rhoconnect.appserver}.to_json
3
+ end
@@ -0,0 +1,7 @@
1
+ Server.api :get_db_doc, :source do |params,user|
2
+ if params[:data_type] and params[:data_type] == 'string'
3
+ Store.get_value(params[:doc])
4
+ else
5
+ Store.get_data(params[:doc]).to_json
6
+ end
7
+ end
@@ -0,0 +1,3 @@
1
+ Server.api :get_source_params, :source do |params,user|
2
+ Source.load(params[:source_id],{:app_id => APP_NAME,:user_id => '*'}).to_array.to_json
3
+ end
@@ -0,0 +1,10 @@
1
+ Server.api :list_source_docs, :source do |params,user|
2
+ res = {}
3
+ s = Source.load(params[:source_id], {:app_id => APP_NAME,:user_id => params[:user_id]})
4
+ [:md,:md_size,:md_copy,:errors].each do |doc|
5
+ db_key = s.docname(doc)
6
+ res.merge!(doc => db_key)
7
+ end
8
+ res.to_json
9
+ end
10
+
@@ -0,0 +1,15 @@
1
+ Server.api :list_sources, :source do |params,user|
2
+ sources = App.load(APP_NAME).sources
3
+ if params[:partition_type].nil? or params[:partition_type] == 'all'
4
+ sources.to_json
5
+ else
6
+ res = []
7
+ sources.each do |name|
8
+ s = Source.load(name,{:app_id => APP_NAME,:user_id => '*'})
9
+ if s.partition_type and s.partition_type == params[:partition_type].to_sym
10
+ res << name
11
+ end
12
+ end
13
+ res.to_json
14
+ end
15
+ end