hyper-mesh 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (469) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +27 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +11 -0
  5. data/.travis.yml +4 -0
  6. data/CODE_OF_CONDUCT.md +13 -0
  7. data/Gemfile +5 -0
  8. data/LICENSE.txt +21 -0
  9. data/README.md +396 -0
  10. data/Rakefile +6 -0
  11. data/app/controllers/reactive_record/application_controller.rb +4 -0
  12. data/app/controllers/reactive_record/reactive_record_controller.rb +49 -0
  13. data/bin/console +14 -0
  14. data/bin/setup +7 -0
  15. data/codeship.database.yml +18 -0
  16. data/config/routes.rb +7 -0
  17. data/docs/action_cable_quickstart.md +151 -0
  18. data/docs/authorization-policies.md +449 -0
  19. data/docs/client_side_scoping.md +103 -0
  20. data/docs/pusher_quickstart.md +0 -0
  21. data/docs/simple_poller_quickstart.md +121 -0
  22. data/docs/todo-example.md +116 -0
  23. data/docs/words-example.md +65 -0
  24. data/examples/action-cable/.gitignore +21 -0
  25. data/examples/action-cable/Gemfile +58 -0
  26. data/examples/action-cable/Gemfile.lock +247 -0
  27. data/examples/action-cable/README.md +24 -0
  28. data/examples/action-cable/Rakefile +6 -0
  29. data/examples/action-cable/app/assets/config/manifest.js +3 -0
  30. data/examples/action-cable/app/assets/javascripts/application.js +18 -0
  31. data/examples/action-cable/app/assets/stylesheets/application.css +15 -0
  32. data/examples/action-cable/app/controllers/application_controller.rb +3 -0
  33. data/examples/action-cable/app/controllers/test_controller.rb +6 -0
  34. data/examples/action-cable/app/models/models.rb +2 -0
  35. data/examples/action-cable/app/models/public/application_record.rb +3 -0
  36. data/examples/action-cable/app/models/public/word.rb +2 -0
  37. data/examples/action-cable/app/policies/application_policy.rb +6 -0
  38. data/examples/action-cable/app/views/components.rb +17 -0
  39. data/examples/action-cable/app/views/components/app.rb +18 -0
  40. data/examples/action-cable/app/views/layouts/application.html.erb +14 -0
  41. data/examples/action-cable/app/views/layouts/mailer.html.erb +13 -0
  42. data/examples/action-cable/app/views/layouts/mailer.text.erb +1 -0
  43. data/examples/action-cable/bin/bundle +3 -0
  44. data/examples/action-cable/bin/rails +9 -0
  45. data/examples/action-cable/bin/rake +9 -0
  46. data/examples/action-cable/bin/setup +34 -0
  47. data/examples/action-cable/bin/spring +15 -0
  48. data/examples/action-cable/bin/update +29 -0
  49. data/examples/action-cable/config.ru +5 -0
  50. data/examples/action-cable/config/application.rb +30 -0
  51. data/examples/action-cable/config/boot.rb +3 -0
  52. data/examples/action-cable/config/cable.yml +9 -0
  53. data/examples/action-cable/config/database.yml +25 -0
  54. data/examples/action-cable/config/environment.rb +5 -0
  55. data/examples/action-cable/config/environments/development.rb +45 -0
  56. data/examples/action-cable/config/environments/production.rb +86 -0
  57. data/examples/action-cable/config/environments/test.rb +42 -0
  58. data/examples/action-cable/config/initializers/application_controller_renderer.rb +6 -0
  59. data/examples/action-cable/config/initializers/assets.rb +11 -0
  60. data/examples/action-cable/config/initializers/backtrace_silencers.rb +7 -0
  61. data/examples/action-cable/config/initializers/cookies_serializer.rb +5 -0
  62. data/examples/action-cable/config/initializers/filter_parameter_logging.rb +4 -0
  63. data/examples/action-cable/config/initializers/inflections.rb +16 -0
  64. data/examples/action-cable/config/initializers/mime_types.rb +4 -0
  65. data/examples/action-cable/config/initializers/new_framework_defaults.rb +24 -0
  66. data/examples/action-cable/config/initializers/session_store.rb +3 -0
  67. data/examples/action-cable/config/initializers/synchromesh.rb +5 -0
  68. data/examples/action-cable/config/initializers/wrap_parameters.rb +14 -0
  69. data/examples/action-cable/config/locales/en.yml +23 -0
  70. data/examples/action-cable/config/puma.rb +47 -0
  71. data/examples/action-cable/config/routes.rb +5 -0
  72. data/examples/action-cable/config/secrets.yml +22 -0
  73. data/examples/action-cable/config/spring.rb +6 -0
  74. data/examples/action-cable/db/migrate/20160921223808_create_words.rb +9 -0
  75. data/examples/action-cable/db/schema.rb +21 -0
  76. data/examples/action-cable/log/.keep +0 -0
  77. data/examples/action-cable/public/404.html +67 -0
  78. data/examples/action-cable/public/422.html +67 -0
  79. data/examples/action-cable/public/500.html +66 -0
  80. data/examples/action-cable/public/apple-touch-icon-precomposed.png +0 -0
  81. data/examples/action-cable/public/apple-touch-icon.png +0 -0
  82. data/examples/action-cable/public/favicon.ico +0 -0
  83. data/examples/action-cable/public/robots.txt +5 -0
  84. data/examples/action-cable/rails_cache_dir2/C91/480/synchromesh_active_connections +0 -0
  85. data/examples/action-cable/tmp/.keep +0 -0
  86. data/examples/action-cable/vendor/assets/javascripts/.keep +0 -0
  87. data/examples/action-cable/vendor/assets/stylesheets/.keep +0 -0
  88. data/examples/simple-poller/.gitignore +21 -0
  89. data/examples/simple-poller/Gemfile +64 -0
  90. data/examples/simple-poller/Gemfile.lock +272 -0
  91. data/examples/simple-poller/README.md +24 -0
  92. data/examples/simple-poller/Rakefile +6 -0
  93. data/examples/simple-poller/app/assets/config/manifest.js +3 -0
  94. data/examples/simple-poller/app/assets/images/.keep +0 -0
  95. data/examples/simple-poller/app/assets/javascripts/application.js +20 -0
  96. data/examples/simple-poller/app/assets/javascripts/channels/.keep +0 -0
  97. data/examples/simple-poller/app/assets/stylesheets/application.css +15 -0
  98. data/examples/simple-poller/app/channels/application_cable/channel.rb +4 -0
  99. data/examples/simple-poller/app/channels/application_cable/connection.rb +4 -0
  100. data/examples/simple-poller/app/controllers/application_controller.rb +3 -0
  101. data/examples/simple-poller/app/controllers/concerns/.keep +0 -0
  102. data/examples/simple-poller/app/controllers/test_controller.rb +6 -0
  103. data/examples/simple-poller/app/helpers/application_helper.rb +2 -0
  104. data/examples/simple-poller/app/jobs/application_job.rb +2 -0
  105. data/examples/simple-poller/app/mailers/application_mailer.rb +4 -0
  106. data/examples/simple-poller/app/models/concerns/.keep +0 -0
  107. data/examples/simple-poller/app/models/models.rb +2 -0
  108. data/examples/simple-poller/app/models/public/.keep +0 -0
  109. data/examples/simple-poller/app/models/public/application_record.rb +3 -0
  110. data/examples/simple-poller/app/models/public/word.rb +2 -0
  111. data/examples/simple-poller/app/policies/application_policy.rb +5 -0
  112. data/examples/simple-poller/app/views/components.rb +18 -0
  113. data/examples/simple-poller/app/views/components/.keep +0 -0
  114. data/examples/simple-poller/app/views/components/app.rb +40 -0
  115. data/examples/simple-poller/app/views/layouts/application.html.erb +14 -0
  116. data/examples/simple-poller/app/views/layouts/mailer.html.erb +13 -0
  117. data/examples/simple-poller/app/views/layouts/mailer.text.erb +1 -0
  118. data/examples/simple-poller/bin/bundle +3 -0
  119. data/examples/simple-poller/bin/rails +9 -0
  120. data/examples/simple-poller/bin/rake +9 -0
  121. data/examples/simple-poller/bin/setup +34 -0
  122. data/examples/simple-poller/bin/spring +16 -0
  123. data/examples/simple-poller/bin/update +29 -0
  124. data/examples/simple-poller/config.ru +5 -0
  125. data/examples/simple-poller/config/application.rb +20 -0
  126. data/examples/simple-poller/config/boot.rb +3 -0
  127. data/examples/simple-poller/config/cable.yml +9 -0
  128. data/examples/simple-poller/config/database.yml +25 -0
  129. data/examples/simple-poller/config/environment.rb +5 -0
  130. data/examples/simple-poller/config/environments/development.rb +44 -0
  131. data/examples/simple-poller/config/environments/production.rb +86 -0
  132. data/examples/simple-poller/config/environments/test.rb +42 -0
  133. data/examples/simple-poller/config/initializers/application_controller_renderer.rb +6 -0
  134. data/examples/simple-poller/config/initializers/assets.rb +11 -0
  135. data/examples/simple-poller/config/initializers/backtrace_silencers.rb +7 -0
  136. data/examples/simple-poller/config/initializers/cookies_serializer.rb +5 -0
  137. data/examples/simple-poller/config/initializers/filter_parameter_logging.rb +4 -0
  138. data/examples/simple-poller/config/initializers/inflections.rb +16 -0
  139. data/examples/simple-poller/config/initializers/mime_types.rb +4 -0
  140. data/examples/simple-poller/config/initializers/new_framework_defaults.rb +24 -0
  141. data/examples/simple-poller/config/initializers/session_store.rb +3 -0
  142. data/examples/simple-poller/config/initializers/synchromesh.rb +15 -0
  143. data/examples/simple-poller/config/initializers/wrap_parameters.rb +14 -0
  144. data/examples/simple-poller/config/locales/en.yml +23 -0
  145. data/examples/simple-poller/config/puma.rb +47 -0
  146. data/examples/simple-poller/config/routes.rb +5 -0
  147. data/examples/simple-poller/config/secrets.yml +22 -0
  148. data/examples/simple-poller/config/spring.rb +6 -0
  149. data/examples/simple-poller/db/migrate/20161013220135_create_words.rb +9 -0
  150. data/examples/simple-poller/db/schema.rb +21 -0
  151. data/examples/simple-poller/db/seeds.rb +7 -0
  152. data/examples/simple-poller/lib/assets/.keep +0 -0
  153. data/examples/simple-poller/lib/tasks/.keep +0 -0
  154. data/examples/simple-poller/log/.keep +0 -0
  155. data/examples/simple-poller/public/404.html +67 -0
  156. data/examples/simple-poller/public/422.html +67 -0
  157. data/examples/simple-poller/public/500.html +66 -0
  158. data/examples/simple-poller/public/apple-touch-icon-precomposed.png +0 -0
  159. data/examples/simple-poller/public/apple-touch-icon.png +0 -0
  160. data/examples/simple-poller/public/favicon.ico +0 -0
  161. data/examples/simple-poller/public/robots.txt +5 -0
  162. data/examples/simple-poller/test/controllers/.keep +0 -0
  163. data/examples/simple-poller/test/fixtures/.keep +0 -0
  164. data/examples/simple-poller/test/fixtures/files/.keep +0 -0
  165. data/examples/simple-poller/test/fixtures/words.yml +7 -0
  166. data/examples/simple-poller/test/helpers/.keep +0 -0
  167. data/examples/simple-poller/test/integration/.keep +0 -0
  168. data/examples/simple-poller/test/mailers/.keep +0 -0
  169. data/examples/simple-poller/test/models/.keep +0 -0
  170. data/examples/simple-poller/test/models/word_test.rb +7 -0
  171. data/examples/simple-poller/test/test_helper.rb +10 -0
  172. data/examples/simple-poller/tmp/.keep +0 -0
  173. data/examples/simple-poller/vendor/assets/javascripts/.keep +0 -0
  174. data/examples/simple-poller/vendor/assets/stylesheets/.keep +0 -0
  175. data/examples/words/.gitignore +21 -0
  176. data/examples/words/Gemfile +58 -0
  177. data/examples/words/Gemfile.lock +247 -0
  178. data/examples/words/README.md +24 -0
  179. data/examples/words/Rakefile +6 -0
  180. data/examples/words/app/assets/config/manifest.js +3 -0
  181. data/examples/words/app/assets/javascripts/application.js +18 -0
  182. data/examples/words/app/assets/stylesheets/application.css +15 -0
  183. data/examples/words/app/controllers/application_controller.rb +3 -0
  184. data/examples/words/app/controllers/test_controller.rb +6 -0
  185. data/examples/words/app/models/models.rb +2 -0
  186. data/examples/words/app/models/public/application_record.rb +3 -0
  187. data/examples/words/app/models/public/word.rb +10 -0
  188. data/examples/words/app/policies/application_policy.rb +6 -0
  189. data/examples/words/app/views/components.rb +16 -0
  190. data/examples/words/app/views/components/app.rb +31 -0
  191. data/examples/words/app/views/layouts/application.html.erb +14 -0
  192. data/examples/words/app/views/layouts/mailer.html.erb +13 -0
  193. data/examples/words/app/views/layouts/mailer.text.erb +1 -0
  194. data/examples/words/bin/bundle +3 -0
  195. data/examples/words/bin/rails +9 -0
  196. data/examples/words/bin/rake +9 -0
  197. data/examples/words/bin/setup +34 -0
  198. data/examples/words/bin/spring +15 -0
  199. data/examples/words/bin/update +29 -0
  200. data/examples/words/config.ru +5 -0
  201. data/examples/words/config/application.rb +30 -0
  202. data/examples/words/config/boot.rb +3 -0
  203. data/examples/words/config/cable.yml +9 -0
  204. data/examples/words/config/database.yml +25 -0
  205. data/examples/words/config/environment.rb +5 -0
  206. data/examples/words/config/environments/development.rb +45 -0
  207. data/examples/words/config/environments/production.rb +86 -0
  208. data/examples/words/config/environments/test.rb +42 -0
  209. data/examples/words/config/initializers/application_controller_renderer.rb +6 -0
  210. data/examples/words/config/initializers/assets.rb +11 -0
  211. data/examples/words/config/initializers/backtrace_silencers.rb +7 -0
  212. data/examples/words/config/initializers/cookies_serializer.rb +5 -0
  213. data/examples/words/config/initializers/filter_parameter_logging.rb +4 -0
  214. data/examples/words/config/initializers/inflections.rb +16 -0
  215. data/examples/words/config/initializers/mime_types.rb +4 -0
  216. data/examples/words/config/initializers/new_framework_defaults.rb +24 -0
  217. data/examples/words/config/initializers/session_store.rb +3 -0
  218. data/examples/words/config/initializers/synchromesh.rb +5 -0
  219. data/examples/words/config/initializers/wrap_parameters.rb +14 -0
  220. data/examples/words/config/locales/en.yml +23 -0
  221. data/examples/words/config/puma.rb +47 -0
  222. data/examples/words/config/routes.rb +5 -0
  223. data/examples/words/config/secrets.yml +22 -0
  224. data/examples/words/config/spring.rb +6 -0
  225. data/examples/words/db/migrate/20160921223808_create_words.rb +9 -0
  226. data/examples/words/db/schema.rb +28 -0
  227. data/examples/words/log/.keep +0 -0
  228. data/examples/words/public/404.html +67 -0
  229. data/examples/words/public/422.html +67 -0
  230. data/examples/words/public/500.html +66 -0
  231. data/examples/words/public/apple-touch-icon-precomposed.png +0 -0
  232. data/examples/words/public/apple-touch-icon.png +0 -0
  233. data/examples/words/public/favicon.ico +0 -0
  234. data/examples/words/public/robots.txt +5 -0
  235. data/examples/words/tmp/.keep +0 -0
  236. data/examples/words/vendor/assets/javascripts/.keep +0 -0
  237. data/examples/words/vendor/assets/stylesheets/.keep +0 -0
  238. data/hyper-mesh.gemspec +82 -0
  239. data/lib/active_record_base.rb +152 -0
  240. data/lib/enumerable/pluck.rb +6 -0
  241. data/lib/hyper-mesh.rb +62 -0
  242. data/lib/hypermesh/version.rb +3 -0
  243. data/lib/opal/equality_patches.rb +15 -0
  244. data/lib/opal/parse_patch.rb +14 -0
  245. data/lib/opal/set_patches.rb +8 -0
  246. data/lib/reactive_record/active_record/aggregations.rb +69 -0
  247. data/lib/reactive_record/active_record/associations.rb +111 -0
  248. data/lib/reactive_record/active_record/base.rb +9 -0
  249. data/lib/reactive_record/active_record/class_methods.rb +177 -0
  250. data/lib/reactive_record/active_record/error.rb +26 -0
  251. data/lib/reactive_record/active_record/instance_methods.rb +129 -0
  252. data/lib/reactive_record/active_record/public_columns_hash.rb +19 -0
  253. data/lib/reactive_record/active_record/reactive_record/base.rb +520 -0
  254. data/lib/reactive_record/active_record/reactive_record/collection.rb +517 -0
  255. data/lib/reactive_record/active_record/reactive_record/column_types.rb +67 -0
  256. data/lib/reactive_record/active_record/reactive_record/dummy_value.rb +218 -0
  257. data/lib/reactive_record/active_record/reactive_record/isomorphic_base.rb +532 -0
  258. data/lib/reactive_record/active_record/reactive_record/reactive_set_relationship_helpers.rb +189 -0
  259. data/lib/reactive_record/active_record/reactive_record/scoped_collection.rb +62 -0
  260. data/lib/reactive_record/active_record/reactive_record/unscoped_collection.rb +16 -0
  261. data/lib/reactive_record/active_record/reactive_record/while_loading.rb +299 -0
  262. data/lib/reactive_record/engine.rb +13 -0
  263. data/lib/reactive_record/interval.rb +190 -0
  264. data/lib/reactive_record/permissions.rb +102 -0
  265. data/lib/reactive_record/pry.rb +13 -0
  266. data/lib/reactive_record/reactive_scope.rb +18 -0
  267. data/lib/reactive_record/scope_description.rb +108 -0
  268. data/lib/reactive_record/serializers.rb +7 -0
  269. data/lib/reactive_record/server_data_cache.rb +347 -0
  270. data/lib/reactive_record/version.rb +3 -0
  271. data/lib/sources/hyper-mesh/pusher.js +98 -0
  272. data/lib/synchromesh/action_cable.rb +39 -0
  273. data/lib/synchromesh/client_drivers.rb +357 -0
  274. data/lib/synchromesh/configuration.rb +40 -0
  275. data/lib/synchromesh/connection.rb +144 -0
  276. data/lib/synchromesh/policy.rb +504 -0
  277. data/lib/synchromesh/reactive_record/permission_patches.rb +43 -0
  278. data/lib/synchromesh/synchromesh.rb +155 -0
  279. data/lib/synchromesh/synchromesh_controller.rb +154 -0
  280. data/logo.jpg +0 -0
  281. data/path_release_steps.md +0 -0
  282. data/reactive_record_test_app/Gemfile +15 -0
  283. data/reactive_record_test_app/Gemfile.lock +209 -0
  284. data/reactive_record_test_app/README.rdoc +261 -0
  285. data/reactive_record_test_app/Rakefile +7 -0
  286. data/reactive_record_test_app/app/assets/javascripts/application.rb +5 -0
  287. data/reactive_record_test_app/app/assets/javascripts/components/another_component.rb +24 -0
  288. data/reactive_record_test_app/app/assets/javascripts/components/empty_component.rb +6 -0
  289. data/reactive_record_test_app/app/assets/javascripts/components/todo_item_component.js.rb +16 -0
  290. data/reactive_record_test_app/app/assets/javascripts/components/todos_component.js.rb +42 -0
  291. data/reactive_record_test_app/app/assets/javascripts/components/todos_main_component.rb +49 -0
  292. data/reactive_record_test_app/app/assets/javascripts/react_js_test_only.js +21618 -0
  293. data/reactive_record_test_app/app/assets/javascripts/reactive_record_config.js +2 -0
  294. data/reactive_record_test_app/app/assets/javascripts/spec/reactive_record_xspec.js.rb +42 -0
  295. data/reactive_record_test_app/app/assets/stylesheets/application.css +13 -0
  296. data/reactive_record_test_app/app/controllers/application_controller.rb +8 -0
  297. data/reactive_record_test_app/app/controllers/home_controller.rb +7 -0
  298. data/reactive_record_test_app/app/controllers/test_controller.rb +7 -0
  299. data/reactive_record_test_app/app/helpers/application_helper.rb +2 -0
  300. data/reactive_record_test_app/app/mailers/.gitkeep +0 -0
  301. data/reactive_record_test_app/app/models/.gitkeep +0 -0
  302. data/reactive_record_test_app/app/policies/application_policy.rb +5 -0
  303. data/reactive_record_test_app/app/views/components.rb +4 -0
  304. data/reactive_record_test_app/app/views/components/test.rb +18 -0
  305. data/reactive_record_test_app/app/views/home/index.html.erb +1 -0
  306. data/reactive_record_test_app/app/views/layouts/application.html.erb +17 -0
  307. data/reactive_record_test_app/app/views/models.rb +1 -0
  308. data/reactive_record_test_app/app/views/models/address.rb +13 -0
  309. data/reactive_record_test_app/app/views/models/comment.rb +19 -0
  310. data/reactive_record_test_app/app/views/models/todo_item.rb +36 -0
  311. data/reactive_record_test_app/app/views/models/user.rb +78 -0
  312. data/reactive_record_test_app/config.ru +33 -0
  313. data/reactive_record_test_app/config/application.rb +73 -0
  314. data/reactive_record_test_app/config/boot.rb +10 -0
  315. data/reactive_record_test_app/config/database.yml +25 -0
  316. data/reactive_record_test_app/config/environment.rb +5 -0
  317. data/reactive_record_test_app/config/environments/development.rb +43 -0
  318. data/reactive_record_test_app/config/environments/production.rb +70 -0
  319. data/reactive_record_test_app/config/environments/test.rb +41 -0
  320. data/reactive_record_test_app/config/initializers/backtrace_silencers.rb +7 -0
  321. data/reactive_record_test_app/config/initializers/inflections.rb +15 -0
  322. data/reactive_record_test_app/config/initializers/mime_types.rb +5 -0
  323. data/reactive_record_test_app/config/initializers/secret_token.rb +7 -0
  324. data/reactive_record_test_app/config/initializers/session_store.rb +8 -0
  325. data/reactive_record_test_app/config/initializers/wrap_parameters.rb +14 -0
  326. data/reactive_record_test_app/config/locales/en.yml +5 -0
  327. data/reactive_record_test_app/config/routes.rb +7 -0
  328. data/reactive_record_test_app/db/migrate/20150617002932_create_todo_items.rb +11 -0
  329. data/reactive_record_test_app/db/migrate/20150617134028_create_users.rb +14 -0
  330. data/reactive_record_test_app/db/migrate/20150729195556_add_address_to_user.rb +20 -0
  331. data/reactive_record_test_app/db/migrate/20150826142045_create_comments.rb +10 -0
  332. data/reactive_record_test_app/db/migrate/20150828172008_add_single_comment_to_todo_item.rb +5 -0
  333. data/reactive_record_test_app/db/migrate/20150908184118_add_address_id_to_user.rb +5 -0
  334. data/reactive_record_test_app/db/migrate/20150917220236_add_second_address_to_user.rb +10 -0
  335. data/reactive_record_test_app/db/migrate/20151009000111_add_test_data_attributes_to_user.rb +6 -0
  336. data/reactive_record_test_app/db/migrate/20160129182544_add_test_enum_to_user.rb +5 -0
  337. data/reactive_record_test_app/db/schema.rb +63 -0
  338. data/reactive_record_test_app/db/seeds.rb +60 -0
  339. data/reactive_record_test_app/public/404.html +26 -0
  340. data/reactive_record_test_app/public/422.html +26 -0
  341. data/reactive_record_test_app/public/500.html +25 -0
  342. data/reactive_record_test_app/public/favicon.ico +0 -0
  343. data/reactive_record_test_app/script/rails +6 -0
  344. data/reactive_record_test_app/spec-opal/active-record/aggregations_spec.rb +41 -0
  345. data/reactive_record_test_app/spec-opal/active-record/associations_spec.rb +75 -0
  346. data/reactive_record_test_app/spec-opal/active-record/base_spec.rb +126 -0
  347. data/reactive_record_test_app/spec-opal/active-record/dummy_value_spec.rb +27 -0
  348. data/reactive_record_test_app/spec-opal/active-record/edge_cases_spec.rb +116 -0
  349. data/reactive_record_test_app/spec-opal/active-record/enum_spec.rb +43 -0
  350. data/reactive_record_test_app/spec-opal/active-record/instance_methods_spec.rb +53 -0
  351. data/reactive_record_test_app/spec-opal/active-record/non_ar_aggregations_spec.rb +74 -0
  352. data/reactive_record_test_app/spec-opal/active-record/permissions_spec.rb +170 -0
  353. data/reactive_record_test_app/spec-opal/active-record/prerendering_spec.rb +49 -0
  354. data/reactive_record_test_app/spec-opal/active-record/reactive_record_load_spec.rb +23 -0
  355. data/reactive_record_test_app/spec-opal/active-record/rendering_spec.rb +221 -0
  356. data/reactive_record_test_app/spec-opal/active-record/save_spec.rb +125 -0
  357. data/reactive_record_test_app/spec-opal/active-record/scope_spec.rb +85 -0
  358. data/reactive_record_test_app/spec-opal/active-record/update_aggregations_spec.rb +76 -0
  359. data/reactive_record_test_app/spec-opal/active-record/update_attributes_spec.rb +186 -0
  360. data/reactive_record_test_app/spec-opal/active-record/virtual_methods_spec.rb +71 -0
  361. data/reactive_record_test_app/spec-opal/index.html.erb +11 -0
  362. data/reactive_record_test_app/spec-opal/spec_helper.js.rb +268 -0
  363. data/reactive_record_test_app/spec-opal/vendor/es5-shim.min.js +6 -0
  364. data/reactive_record_test_app/spec_dont_run/README.md +26 -0
  365. data/reactive_record_test_app/spec_dont_run/moved_to_main_spec_dir/zzzmany_to_many_spec_moved_to_main_suite.rb +30 -0
  366. data/reactive_record_test_app/spec_dont_run/moved_to_main_spec_dir/zzzrevert_record_spec_moved.rb +78 -0
  367. data/reactive_record_test_app/spec_dont_run/moved_to_main_spec_dir/zzzupdate_associations_spec_moved.rb +142 -0
  368. data/reactive_record_test_app/spec_dont_run/moved_to_main_spec_dir/zzzupdate_scopes_movedspec.rb +48 -0
  369. data/remote.md +115 -0
  370. data/spec/bin/firebug-2.0.13-fx.xpi +0 -0
  371. data/spec/component_helpers_xspec.rb +49 -0
  372. data/spec/factories/child_model.rb +5 -0
  373. data/spec/factories/comment.rb +5 -0
  374. data/spec/factories/test_models.rb +5 -0
  375. data/spec/factories/todo.rb +5 -0
  376. data/spec/factories/user.rb +5 -0
  377. data/spec/reactive_record/edge_cases_spec.rb +31 -0
  378. data/spec/reactive_record/factory.rb +62 -0
  379. data/spec/reactive_record/many_to_many_spec.rb +50 -0
  380. data/spec/reactive_record/play.rb +64 -0
  381. data/spec/reactive_record/pry_rescue_xspec.rb +48 -0
  382. data/spec/reactive_record/revert_spec.rb +112 -0
  383. data/spec/reactive_record/update_associations_spec.rb +189 -0
  384. data/spec/reactive_record/update_scopes_spec.rb +53 -0
  385. data/spec/spec_helper.rb +366 -0
  386. data/spec/support/component_helpers.rb +351 -0
  387. data/spec/synchromesh/column_types/column_type_spec.rb +302 -0
  388. data/spec/synchromesh/connection_spec.rb +144 -0
  389. data/spec/synchromesh/crud_access_regulation/broadcast_controls_access_spec.rb +105 -0
  390. data/spec/synchromesh/crud_access_regulation/model_policies_spec.rb +131 -0
  391. data/spec/synchromesh/examples/dictionary.rb +239 -0
  392. data/spec/synchromesh/examples/dictionary_with_client_scopes.rb +196 -0
  393. data/spec/synchromesh/examples/random_examples.rb +100 -0
  394. data/spec/synchromesh/examples/scoped_todos_spec.rb +167 -0
  395. data/spec/synchromesh/integration/authorization_spec.rb +111 -0
  396. data/spec/synchromesh/integration/default_scope_spec.rb +121 -0
  397. data/spec/synchromesh/integration/has_many_through_spec.rb +173 -0
  398. data/spec/synchromesh/integration/relationships_spec.rb +263 -0
  399. data/spec/synchromesh/integration/scope_spec.rb +553 -0
  400. data/spec/synchromesh/integration/synchromesh_spec.rb +80 -0
  401. data/spec/synchromesh/integration/test_components.rb +18 -0
  402. data/spec/synchromesh/integration/transports_spec.rb +308 -0
  403. data/spec/synchromesh/policies/auto_connect_spec.rb +60 -0
  404. data/spec/synchromesh/policies/auto_loader_spec.rb +34 -0
  405. data/spec/synchromesh/policies/policy_methods_spec.rb +85 -0
  406. data/spec/synchromesh/policies/regulate_all_broadcasts_spec.rb +315 -0
  407. data/spec/synchromesh/policies/regulate_broadcast_spec.rb +370 -0
  408. data/spec/synchromesh/policies/regulate_class_connection_spec.rb +50 -0
  409. data/spec/synchromesh/policies/regulate_instance_connection_spec.rb +66 -0
  410. data/spec/test_app/Gemfile +61 -0
  411. data/spec/test_app/Gemfile.lock +253 -0
  412. data/spec/test_app/Rakefile +6 -0
  413. data/spec/test_app/app/assets/javascripts/application.js +6 -0
  414. data/spec/test_app/app/assets/stylesheets/application.css +15 -0
  415. data/spec/test_app/app/controllers/application_controller.rb +13 -0
  416. data/spec/test_app/app/controllers/test_controller.rb +5 -0
  417. data/spec/test_app/app/models/_react_public_models.rb +2 -0
  418. data/spec/test_app/app/models/public/address.rb +13 -0
  419. data/spec/test_app/app/models/public/child_model.rb +3 -0
  420. data/spec/test_app/app/models/public/comment.rb +25 -0
  421. data/spec/test_app/app/models/public/test_model.rb +5 -0
  422. data/spec/test_app/app/models/public/todo.rb +6 -0
  423. data/spec/test_app/app/models/public/todo_item.rb +36 -0
  424. data/spec/test_app/app/models/public/user.rb +88 -0
  425. data/spec/test_app/app/policies/auto_loader_test_classa_policy.rb +3 -0
  426. data/spec/test_app/app/policies/auto_loader_test_classb_policy.rb +3 -0
  427. data/spec/test_app/app/policies/auto_loader_test_classc_policy.rb +3 -0
  428. data/spec/test_app/app/policies/auto_loader_test_classd_policy.rb +3 -0
  429. data/spec/test_app/app/views/components.rb +14 -0
  430. data/spec/test_app/app/views/components/show.rb +5 -0
  431. data/spec/test_app/app/views/layouts/application.html.erb +14 -0
  432. data/spec/test_app/bin/bundle +3 -0
  433. data/spec/test_app/bin/rails +4 -0
  434. data/spec/test_app/bin/rake +4 -0
  435. data/spec/test_app/bin/setup +29 -0
  436. data/spec/test_app/config.ru +4 -0
  437. data/spec/test_app/config/application.rb +42 -0
  438. data/spec/test_app/config/boot.rb +6 -0
  439. data/spec/test_app/config/cable.yml +10 -0
  440. data/spec/test_app/config/database.yml +47 -0
  441. data/spec/test_app/config/environment.rb +5 -0
  442. data/spec/test_app/config/environments/development.rb +41 -0
  443. data/spec/test_app/config/environments/production.rb +79 -0
  444. data/spec/test_app/config/environments/test.rb +42 -0
  445. data/spec/test_app/config/initializers/assets.rb +11 -0
  446. data/spec/test_app/config/initializers/backtrace_silencers.rb +7 -0
  447. data/spec/test_app/config/initializers/cookies_serializer.rb +3 -0
  448. data/spec/test_app/config/initializers/filter_parameter_logging.rb +4 -0
  449. data/spec/test_app/config/initializers/inflections.rb +16 -0
  450. data/spec/test_app/config/initializers/mime_types.rb +4 -0
  451. data/spec/test_app/config/initializers/session_store.rb +3 -0
  452. data/spec/test_app/config/initializers/synchromesh.rb +11 -0
  453. data/spec/test_app/config/initializers/wrap_parameters.rb +14 -0
  454. data/spec/test_app/config/locales/en.yml +23 -0
  455. data/spec/test_app/config/routes.rb +61 -0
  456. data/spec/test_app/config/secrets.yml +22 -0
  457. data/spec/test_app/db/development.sqlite3 +0 -0
  458. data/spec/test_app/db/migrate/20160731182106_create_test_models.rb +75 -0
  459. data/spec/test_app/db/schema.rb +88 -0
  460. data/spec/test_app/db/seeds.rb +7 -0
  461. data/spec/test_app/lib/assets/.keep +0 -0
  462. data/spec/test_app/log/.keep +0 -0
  463. data/spec/test_app/public/404.html +67 -0
  464. data/spec/test_app/public/422.html +67 -0
  465. data/spec/test_app/public/500.html +66 -0
  466. data/spec/test_app/public/favicon.ico +0 -0
  467. data/spec/vendor/es5-shim.min.js +6 -0
  468. data/terminal.md +66 -0
  469. metadata +1175 -0
@@ -0,0 +1,39 @@
1
+ module ApplicationCable
2
+ class Channel < ActionCable::Channel::Base; end
3
+ class Connection < ActionCable::Connection::Base; end
4
+ end
5
+
6
+ module HyperMesh
7
+ class ActionCableChannel < ApplicationCable::Channel
8
+ class << self
9
+ def subscriptions
10
+ @subscriptions ||= Hash.new { |h, k| h[k] = 0 }
11
+ end
12
+ end
13
+
14
+ def inc_subscription
15
+ self.class.subscriptions[params[:synchromesh_channel]] =
16
+ self.class.subscriptions[params[:synchromesh_channel]] + 1
17
+ end
18
+
19
+ def dec_subscription
20
+ self.class.subscriptions[params[:synchromesh_channel]] =
21
+ self.class.subscriptions[params[:synchromesh_channel]] - 1
22
+ end
23
+
24
+ def subscribed
25
+ session_id = params["client_id"]
26
+ authorization = HyperMesh.authorization(params["salt"], params["synchromesh_channel"], session_id)
27
+ if params["authorization"] == authorization
28
+ inc_subscription
29
+ stream_from "synchromesh-#{params[:synchromesh_channel]}"
30
+ else
31
+ reject
32
+ end
33
+ end
34
+
35
+ def unsubscribed
36
+ HyperMesh::Connection.disconnect(params[:synchromesh_channel]) if dec_subscription == 0
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,357 @@
1
+ module HyperMesh
2
+ # Client side handling of synchronization messages
3
+ # When a synchronization message comes in, the client will call
4
+ # either the sync_change or sync_destroy methods.
5
+ # Everything is setup during hyper-react before_first_mount callback
6
+ # We use ERB to determine the configuration and implement the appropriate
7
+ # client interface to sync_change or sync_destroy
8
+
9
+ if RUBY_ENGINE == 'opal'
10
+ def self.connect(*channels)
11
+ channels.each do |channel|
12
+ if channel.is_a? Class
13
+ IncomingBroadcast.connect_to(channel.name)
14
+ elsif channel.is_a?(String) || channel.is_a?(Array)
15
+ IncomingBroadcast.connect_to(*channel)
16
+ elsif channel.id
17
+ IncomingBroadcast.connect_to(channel.class.name, channel.id)
18
+ else
19
+ raise "cannot connect to model before it has been saved"
20
+ end
21
+ end
22
+ end
23
+
24
+ def self.action_cable_consumer
25
+ ClientDrivers.opts[:action_cable_consumer]
26
+ end
27
+
28
+ class IncomingBroadcast
29
+ def self.add_connection(channel_name, id = nil)
30
+ channel_string = "#{channel_name}#{'-'+id.to_s if id}"
31
+ open_channels << channel_string
32
+ channel_string
33
+ end
34
+
35
+ def self.connect_to(channel_name, id = nil)
36
+ channel_string = add_connection(channel_name, id)
37
+ if ClientDrivers.opts[:transport] == :pusher
38
+ channel = "#{ClientDrivers.opts[:channel]}-#{channel_string}"
39
+ %x{
40
+ var channel = #{ClientDrivers.opts[:pusher_api]}.subscribe(#{channel});
41
+ channel.bind('create', #{ClientDrivers.opts[:create]});
42
+ channel.bind('change', #{ClientDrivers.opts[:change]});
43
+ channel.bind('destroy', #{ClientDrivers.opts[:destroy]});
44
+ channel.bind('pusher:subscription_succeeded', #{lambda {ClientDrivers.get_queued_data("connect-to-transport", channel_string)}})
45
+ }
46
+ elsif ClientDrivers.opts[:transport] == :action_cable
47
+ channel = "#{ClientDrivers.opts[:channel]}-#{channel_string}"
48
+ HTTP.post(ClientDrivers.polling_path('action-cable-auth', channel)).then do |response|
49
+ %x{
50
+ #{HyperMesh.action_cable_consumer}.subscriptions.create(
51
+ {
52
+ channel: "HyperMesh::ActionCableChannel",
53
+ client_id: #{ClientDrivers.opts[:id]},
54
+ synchromesh_channel: #{channel_string},
55
+ authorization: #{response.json[:authorization]},
56
+ salt: #{response.json[:salt]}
57
+ },
58
+ {
59
+ connected: function() {
60
+ #{ClientDrivers.get_queued_data("connect-to-transport", channel_string)}
61
+ },
62
+ received: function(data) {
63
+ var data = #{JSON.parse(`JSON.stringify(data)`)}
64
+ #{ClientDrivers.send("sync_#{`data`['message']}", `data`['data'])}
65
+ return true
66
+ }
67
+ }
68
+ )
69
+ }
70
+ end
71
+ else
72
+ HTTP.get(ClientDrivers.polling_path(:subscribe, channel_string))
73
+ end
74
+ end
75
+
76
+ def self.receive(data, operation, &block)
77
+ in_transit[data[:broadcast_id]].receive(data, operation, &block)
78
+ end
79
+
80
+ def record_with_current_values
81
+ ReactiveRecord::Base.load_data do
82
+
83
+ backing_record = @backing_record || klass.find(record[:id]).backing_record
84
+ if destroyed?
85
+ backing_record.ar_instance
86
+ else
87
+ merge_current_values(backing_record)
88
+ end
89
+ end
90
+ end
91
+
92
+ def record_with_new_values
93
+ klass._react_param_conversion(record).tap do |ar_instance|
94
+ if destroyed?
95
+ ar_instance.backing_record.destroy_associations
96
+ elsif new?
97
+ ar_instance.backing_record.initialize_collections
98
+ end
99
+ end
100
+ end
101
+
102
+ def new?
103
+ @is_new
104
+ end
105
+
106
+ def destroyed?
107
+ @destroyed
108
+ end
109
+
110
+ def klass
111
+ Object.const_get(@klass)
112
+ end
113
+
114
+ def to_s
115
+ "klass: #{klass} record: #{record} new?: #{new?} destroyed?: #{destroyed?}"
116
+ end
117
+
118
+ # private
119
+
120
+ attr_reader :record
121
+
122
+ def self.open_channels
123
+ @open_channels ||= Set.new
124
+ end
125
+
126
+ def self.in_transit
127
+ @in_transit ||= Hash.new { |h, k| h[k] = new(k) }
128
+ end
129
+
130
+ def initialize(id)
131
+ @id = id
132
+ @received = Set.new
133
+ @record = {}
134
+ @previous_changes = {}
135
+ end
136
+
137
+ def local(operation, record, data)
138
+ @destroyed = operation == :destroy
139
+ @is_new = operation == :create
140
+ @klass = record.class.name
141
+ @record = data
142
+ record.backing_record.destroyed = false
143
+ @record.merge!(id: record.id) if record.id
144
+ record.backing_record.destroyed = @destroyed
145
+ @backing_record = record.backing_record
146
+ attributes = record.backing_record.attributes
147
+ data.each do |k, v|
148
+ next if klass.reflect_on_association(k) || attributes[k] == v
149
+ @previous_changes[k] = [attributes[k], v]
150
+ end
151
+ self
152
+ end
153
+
154
+ def receive(data, operation)
155
+ @destroyed = operation == :destroy
156
+ @is_new = operation == :create
157
+ @channels ||= self.class.open_channels.intersection data[:channels]
158
+ raise 'synchromesh security violation' unless @channels.include? data[:channel]
159
+ @received << data[:channel]
160
+ @klass ||= data[:klass]
161
+ @record.merge! data[:record]
162
+ @previous_changes.merge! data[:previous_changes]
163
+ @backing_record = ReactiveRecord::Base.exists?(klass, record[:id])
164
+ yield complete! if @channels == @received
165
+ end
166
+
167
+ def complete!
168
+ self.class.in_transit.delete @id
169
+ end
170
+
171
+ def merge_current_values(br)
172
+ current_values = Hash[*@previous_changes.collect do |attr, values|
173
+ value = attr == :id ? record[:id] : values.first
174
+ begin
175
+ if br.attributes.key?(attr) &&
176
+ br.attributes[attr] != br.convert(attr, value) &&
177
+ br.attributes[attr] != br.convert(attr, values.last)
178
+ puts "warning #{attr} has changed locally - will force a reload.\n"\
179
+ "local value: #{br.attributes[attr]} remote value: #{br.convert(attr, value)}->#{br.convert(attr, values.last)}"
180
+ return nil
181
+ end
182
+ rescue Exception => e
183
+ debugger
184
+ nil
185
+ end
186
+ [attr, value]
187
+ end.compact.flatten].merge(br.attributes)
188
+ klass._react_param_conversion(current_values)
189
+ rescue Exception => e
190
+ debugger
191
+ nil
192
+ end
193
+ end
194
+ end
195
+
196
+ class LocalSync
197
+ def self.after_save(record, data = {})
198
+ operation = record.new? ? :create : (record.destroyed? ? :destroy : :change)
199
+ dummy_broadcast = IncomingBroadcast.new.local(operation, record, data)
200
+ record.backing_record.sync! data unless operation == :destroy
201
+ ReactiveRecord::Collection.sync_scopes dummy_broadcast
202
+ end
203
+ end
204
+
205
+ class ClientDrivers
206
+ include React::IsomorphicHelpers
207
+
208
+ # sync_changes: Wait till we are done with any concurrent model saves, then
209
+ # hydrate the data (which will update any attributes) and sync the scopes.
210
+
211
+ def self.sync_create(data)
212
+ IncomingBroadcast.receive(data, :create) do |broadcast|
213
+ ReactiveRecord::Base.when_not_saving(broadcast.klass) do
214
+ ReactiveRecord::Collection.sync_scopes broadcast
215
+ end
216
+ end
217
+ end
218
+
219
+ def self.sync_change(data)
220
+ IncomingBroadcast.receive(data, :change) do |broadcast|
221
+ ReactiveRecord::Base.when_not_saving(broadcast.klass) do
222
+ ReactiveRecord::Collection.sync_scopes broadcast
223
+ end
224
+ end
225
+ end
226
+
227
+ # sync_destroy: Hydrate the data, then destroy the record, cleanup
228
+ # and syncronize the scopes.
229
+
230
+ def self.sync_destroy(data)
231
+ IncomingBroadcast.receive(data, :destroy) do |broadcast|
232
+ ReactiveRecord::Collection.sync_scopes broadcast
233
+ end
234
+ end
235
+
236
+ # save the configuration info needed in window.HyperMeshOpts
237
+
238
+ # we keep a list of all channels by session with connections in progress
239
+ # for each broadcast we check the list, and add the message to a queue for that
240
+ # session. When the client is informed that the connection has completed
241
+ # we call the server, this will return ALL the broadcasts (cool) and
242
+ # will remove the session from the list.
243
+
244
+ prerender_footer do |controller|
245
+ if defined?(PusherFake)
246
+ path = ::Rails.application.routes.routes.detect do |route|
247
+ route.app == ReactiveRecord::Engine ||
248
+ (route.app.respond_to?(:app) && route.app.app == ReactiveRecord::Engine)
249
+ end.path.spec
250
+ pusher_fake_js = PusherFake.javascript(
251
+ auth: { headers: { 'X-CSRF-Token' => controller.send(:form_authenticity_token) } },
252
+ authEndpoint: "#{path}/synchromesh-pusher-auth"
253
+ )
254
+ end
255
+ controller.session.delete 'synchromesh-dummy-init' unless controller.session.id
256
+ id = "#{SecureRandom.uuid}-#{controller.session.id}"
257
+ config_hash = {
258
+ public_columns_hash: ActiveRecord::Base.public_columns_hash,
259
+ transport: HyperMesh.transport,
260
+ id: id,
261
+ client_logging: HyperMesh.client_logging,
262
+ pusher_fake_js: pusher_fake_js,
263
+ key: HyperMesh.key,
264
+ encrypted: HyperMesh.encrypted,
265
+ channel: HyperMesh.channel,
266
+ form_authenticity_token: controller.send(:form_authenticity_token),
267
+ seconds_between_poll: HyperMesh.seconds_between_poll,
268
+ auto_connect: HyperMesh::AutoConnect.channels(id, controller.acting_user)
269
+ }
270
+ "<script type='text/javascript'>\n"\
271
+ "window.HyperMeshOpts = #{config_hash.to_json}\n"\
272
+ "</script>\n"
273
+ end if RUBY_ENGINE != 'opal'
274
+
275
+ def self.opts
276
+ @opts ||= Hash.new(`window.HyperMeshOpts`)
277
+ end
278
+
279
+ isomorphic_method(:get_public_columns_hash) do |f|
280
+ f.when_on_client { @opts[:public_columns_hash] || {} }
281
+ f.send_to_server
282
+ f.when_on_server { ActiveRecord::Base.public_columns_hash }
283
+ end
284
+
285
+ def self.public_columns_hash
286
+ # return {} unless @opts && @opts[:public_columns_hash]
287
+ # @opts[:public_columns_hash]
288
+ @public_columns_hash ||= get_public_columns_hash
289
+ end
290
+
291
+ def self.get_queued_data(operation, channel = nil, opts = {})
292
+ HTTP.get(polling_path(operation, channel), opts).then do |response|
293
+ response.json.each do |update|
294
+ send "sync_#{update[0]}", update[1]
295
+ end
296
+ end
297
+ end
298
+
299
+ # Before first mount, hook up callbacks depending on what kind of transport
300
+ # we are using.
301
+
302
+ before_first_mount do
303
+ if on_opal_client?
304
+
305
+ if opts[:transport] == :pusher
306
+
307
+ opts[:create] = lambda do |data|
308
+ sync_create JSON.parse(`JSON.stringify(#{data})`)
309
+ end
310
+
311
+ opts[:change] = lambda do |data|
312
+ sync_change JSON.parse(`JSON.stringify(#{data})`)
313
+ end
314
+
315
+ opts[:destroy] = lambda do |data|
316
+ sync_destroy JSON.parse(`JSON.stringify(#{data})`)
317
+ end
318
+
319
+ if opts[:client_logging] && `window.console && window.console.log`
320
+ `Pusher.log = function(message) {window.console.log(message);}`
321
+ end
322
+ if opts[:pusher_fake_js]
323
+ opts[:pusher_api] = `eval(#{opts[:pusher_fake_js]})`
324
+ else
325
+ h = nil
326
+ pusher_api = nil
327
+ %x{
328
+ h = {
329
+ encrypted: #{opts[:encrypted]},
330
+ authEndpoint: window.ReactiveRecordEnginePath+'/synchromesh-pusher-auth',
331
+ auth: {headers: {'X-CSRF-Token': #{opts[:form_authenticity_token]}}}
332
+ };
333
+ pusher_api = new Pusher(#{opts[:key]}, h)
334
+ }
335
+ opts[:pusher_api] = pusher_api
336
+ end
337
+ HyperMesh.connect(*opts[:auto_connect])
338
+ elsif opts[:transport] == :action_cable
339
+ opts[:action_cable_consumer] =
340
+ `ActionCable.createConsumer.apply(ActionCable, #{[*opts[:action_cable_consumer_url]]})`
341
+ HyperMesh.connect(*opts[:auto_connect])
342
+ elsif opts[:transport] == :simple_poller
343
+ opts[:auto_connect].each { |channel| IncomingBroadcast.add_connection(*channel) }
344
+ every(opts[:seconds_between_poll]) do
345
+ get_queued_data(:read, nil, headers: {'X-SYNCHROMESH-SILENT-REQUEST': true })
346
+ end
347
+ end
348
+ end
349
+ end
350
+
351
+ def self.polling_path(to, id = nil)
352
+ s = "#{`window.ReactiveRecordEnginePath`}/synchromesh-#{to}/#{opts[:id]}"
353
+ s = "#{s}/#{id}" if id
354
+ s
355
+ end
356
+ end
357
+ end
@@ -0,0 +1,40 @@
1
+ module HyperMesh
2
+ # configuration utility
3
+ module Configuration
4
+
5
+ def configuration
6
+ config_reset
7
+ yield self
8
+ config_initialized
9
+ end
10
+
11
+ def define_setting(name, default = nil, &block)
12
+ class_variable_set("@@#{name}", default)
13
+
14
+ define_class_method "#{name}=" do |value|
15
+ class_variable_set("@@#{name}", value)
16
+ block.call value if block
17
+ value
18
+ end
19
+
20
+ define_class_method name do
21
+ class_variable_get("@@#{name}")
22
+ end
23
+ end
24
+
25
+ def config_reset
26
+ raise "must implement"
27
+ end
28
+
29
+ def config_initialized
30
+ end
31
+
32
+ private
33
+
34
+ def define_class_method(name, &block)
35
+ (class << self; self; end).instance_eval do
36
+ define_method name, &block
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,144 @@
1
+ module HyperMesh
2
+ class Connection < ActiveRecord::Base
3
+ class QueuedMessage < ActiveRecord::Base
4
+ self.table_name = 'synchromesh_queued_messages'
5
+
6
+ do_not_synchronize
7
+
8
+ serialize :data
9
+
10
+ belongs_to :synchromesh_connection,
11
+ class_name: 'HyperMesh::Connection',
12
+ foreign_key: 'connection_id'
13
+
14
+ scope :for_session,
15
+ ->(session) { joins(:synchromesh_connection).where('session = ?', session) }
16
+
17
+ # For simplicity we use QueuedMessage with connection_id 0
18
+ # to store the current path which is used by consoles to
19
+ # communicate back to the server
20
+
21
+ default_scope { where('connection_id IS NULL OR connection_id != 0') }
22
+
23
+ def self.root_path=(path)
24
+ unscoped.find_or_create_by(connection_id: 0).update(data: path)
25
+ end
26
+
27
+ def self.root_path
28
+ unscoped.find_or_create_by(connection_id: 0).data
29
+ end
30
+ end
31
+
32
+ def self.build_tables
33
+ # unless connection.tables.include? 'synchromesh_connections'
34
+ connection.create_table(:synchromesh_connections, force: true) do |t|
35
+ t.string :channel
36
+ t.string :session
37
+ t.datetime :created_at
38
+ t.datetime :expires_at
39
+ t.datetime :refresh_at
40
+ end
41
+ # unless connection.tables.include? 'synchromesh_queued_messages'
42
+ connection.create_table(:synchromesh_queued_messages, force: true) do |t|
43
+ t.text :data
44
+ t.integer :connection_id
45
+ end
46
+ end
47
+
48
+ do_not_synchronize
49
+
50
+ self.table_name = 'synchromesh_connections'
51
+
52
+ has_many :messages,
53
+ foreign_key: 'connection_id',
54
+ class_name: 'HyperMesh::Connection::QueuedMessage',
55
+ dependent: :destroy
56
+ scope :expired,
57
+ -> { where('expires_at IS NOT NULL AND expires_at < ?', Time.zone.now) }
58
+ scope :pending_for,
59
+ ->(channel) { where(channel: channel).where('session IS NOT NULL') }
60
+ scope :inactive,
61
+ -> { where('session IS NULL AND refresh_at < ?', Time.zone.now) }
62
+
63
+ def self.needs_refresh?
64
+ exists?(['refresh_at IS NOT NULL AND refresh_at < ?', Time.zone.now])
65
+ end
66
+
67
+ def transport
68
+ self.class.transport
69
+ end
70
+
71
+ before_create do
72
+ if session
73
+ self.expires_at = Time.now + transport.expire_new_connection_in
74
+ elsif transport.refresh_channels_every != :never
75
+ self.refresh_at = Time.now + transport.refresh_channels_every
76
+ end
77
+ end
78
+
79
+ class << self
80
+ attr_accessor :transport
81
+
82
+ def active
83
+ expired.delete_all
84
+ refresh_connections if needs_refresh?
85
+ all.pluck(:channel).uniq
86
+ end
87
+
88
+ def open(channel, session = nil, root_path = nil)
89
+ self.root_path = root_path
90
+ find_or_create_by(channel: channel, session: session)
91
+ rescue Exception => e
92
+ binding.pry
93
+ end
94
+
95
+ def send_to_channel(channel, data)
96
+ pending_for(channel).each do |connection|
97
+ QueuedMessage.create(data: data, synchromesh_connection: connection)
98
+ end
99
+ transport.send(channel, data) if exists?(channel: channel, session: nil)
100
+ end
101
+
102
+ def read(session, root_path)
103
+ self.root_path = root_path
104
+ where(session: session)
105
+ .update_all(expires_at: Time.now + transport.expire_polled_connection_in)
106
+ QueuedMessage.for_session(session).destroy_all.pluck(:data)
107
+ end
108
+
109
+ def connect_to_transport(channel, session, root_path)
110
+ self.root_path = root_path
111
+ if (connection = find_by(channel: channel, session: session))
112
+ messages = connection.messages.pluck(:data)
113
+ connection.destroy
114
+ else
115
+ messages = []
116
+ end
117
+ open(channel)
118
+ messages
119
+ end
120
+
121
+ def disconnect(channel)
122
+ find_by(channel: channel, session: nil).destroy
123
+ end
124
+
125
+ def root_path=(path)
126
+ QueuedMessage.root_path = path if path
127
+ end
128
+
129
+ def root_path
130
+ QueuedMessage.root_path
131
+ end
132
+
133
+ def refresh_connections
134
+ refresh_started_at = Time.zone.now
135
+ channels = transport.refresh_channels
136
+ next_refresh = refresh_started_at + transport.refresh_channels_every
137
+ channels.each do |channel|
138
+ find_by(channel: channel, session: nil).update(refresh_at: next_refresh)
139
+ end
140
+ inactive.delete_all
141
+ end
142
+ end
143
+ end
144
+ end