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,19 @@
1
+ module ActiveRecord
2
+ # adds method to get the HyperMesh public column types
3
+ # for now we are just getting all models column types, but we should
4
+ # look through the public folder, and just get those models.
5
+ # this works because the public folder is currently required to be eaer loaded.
6
+ class Base
7
+ def self.public_columns_hash
8
+ return @public_columns_hash if @public_columns_hash
9
+ Dir.glob(Rails.root.join('app/models/public/*.rb')).each do |file|
10
+ require_dependency(file) rescue nil
11
+ end
12
+ @public_columns_hash = {}
13
+ descendants.each do |model|
14
+ @public_columns_hash[model.name] = model.columns_hash
15
+ end
16
+ @public_columns_hash
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,520 @@
1
+ module ReactiveRecord
2
+ class Base
3
+
4
+ # Its all about lazy loading. This prevents us from grabbing enormous association collections, or large attributes
5
+ # unless they are explicitly requested.
6
+
7
+ # During prerendering we get each attribute as its requested and fill it in both on the javascript side, as well as
8
+ # remember that the attribute needs to be part of the download to client.
9
+
10
+ # On the client we fill in the record data with empty values (nil, or one element collections) but only as the attribute
11
+ # is requested. Each request queues up a request to get the real data from the server.
12
+
13
+ # The ReactiveRecord class serves two purposes. First it is the unique data corresponding to the last known state of a
14
+ # database record. This means All records matching a specific database record are unique. This is unlike AR but is
15
+ # important both for the lazy loading and also so that when values change react can be informed of the change.
16
+
17
+ # Secondly it serves as name space for all the ReactiveRecord specific methods, so every AR Instance has a ReactiveRecord
18
+
19
+ # Because there is no point in generating a new ar_instance everytime a search is made we cache the first ar_instance created.
20
+ # Its possible however during loading to create a new ar_instances that will in the end point to the same record.
21
+
22
+ # VECTORS... are an important concept. They are the substitute for a primary key before a record is loaded.
23
+ # Vectors have the form [ModelClass, method_call, method_call, method_call...]
24
+
25
+ # Each method call is either a simple method name or an array in the form [method_name, param, param ...]
26
+ # Example [User, [find, 123], todos, active, [due, "1/1/2016"], title]
27
+ # Roughly corresponds to this query: User.find(123).todos.active.due("1/1/2016").select(:title)
28
+
29
+ attr_accessor :ar_instance
30
+ attr_accessor :vector
31
+ attr_accessor :model
32
+ attr_accessor :changed_attributes
33
+ attr_accessor :aggregate_owner
34
+ attr_accessor :aggregate_attribute
35
+ attr_accessor :destroyed
36
+ attr_accessor :updated_during
37
+ attr_accessor :synced_attributes
38
+ attr_accessor :virgin
39
+
40
+ # While data is being loaded from the server certain internal behaviors need to change
41
+ # for example records all record changes are synced as they happen.
42
+ # This is implemented this way so that the ServerDataCache class can use pure active
43
+ # record methods in its implementation
44
+
45
+ def self.data_loading?
46
+ @data_loading
47
+ end
48
+
49
+ def data_loading?
50
+ self.class.data_loading?
51
+ end
52
+
53
+ def self.load_data(&block)
54
+ current_data_loading, @data_loading = [@data_loading, true]
55
+ yield
56
+ ensure
57
+ @data_loading = current_data_loading
58
+ end
59
+
60
+ def self.load_from_json(json, target = nil)
61
+ load_data { ServerDataCache.load_from_json(json, target) }
62
+ end
63
+
64
+ def self.class_scopes(model)
65
+ @class_scopes[model.base_class]
66
+ end
67
+
68
+ # def self.sync_blocks
69
+ # # @sync_blocks[watch_model][sync_model][scope_name][...array of blocks...]
70
+ # @sync_blocks ||= Hash.new { |hash, key| hash[key] = Hash.new { |hash, key| hash[key] = Hash.new { |hash, key| hash[key] = [] } } }
71
+ # end
72
+
73
+
74
+ def self.find(model, attribute, value)
75
+ # will return the unique record with this attribute-value pair
76
+ # value cannot be an association or aggregation
77
+
78
+ model = model.base_class
79
+ # already have a record with this attribute-value pair?
80
+ record = @records[model].detect { |record| record.attributes[attribute] == value}
81
+ unless record
82
+ # if not, and then the record may be loaded, but not have this attribute set yet,
83
+ # so find the id of of record with the attribute-value pair, and see if that is loaded.
84
+ # find_in_db returns nil if we are not prerendering which will force us to create a new record
85
+ # because there is no way of knowing the id.
86
+ if attribute != model.primary_key and id = find_in_db(model, attribute, value)
87
+ record = @records[model].detect { |record| record.id == id}
88
+ end
89
+ # if we don't have a record then create one
90
+ (record = new(model)).vector = [model, ["find_by_#{attribute}", value]] unless record
91
+ # and set the value
92
+ record.sync_attribute(attribute, value)
93
+ # and set the primary if we have one
94
+ record.sync_attribute(model.primary_key, id) if id
95
+ end
96
+ # finally initialize and return the ar_instance
97
+ record.ar_instance ||= infer_type_from_hash(model, record.attributes).new(record)
98
+ end
99
+
100
+ def self.find_by_object_id(model, object_id)
101
+ @records[model].detect { |record| record.object_id == object_id }.ar_instance
102
+ end
103
+
104
+ def self.new_from_vector(model, aggregate_owner, *vector)
105
+ # this is the equivilent of find but for associations and aggregations
106
+ # because we are not fetching a specific attribute yet, there is NO communication with the
107
+ # server. That only happens during find.
108
+ model = model.base_class
109
+
110
+ # do we already have a record with this vector? If so return it, otherwise make a new one.
111
+
112
+ record = @records[model].detect { |record| record.vector == vector }
113
+ unless record
114
+ record = new model
115
+ record.vector = vector
116
+ end
117
+
118
+ record.ar_instance ||= infer_type_from_hash(model, record.attributes).new(record)
119
+
120
+ if aggregate_owner
121
+ record.aggregate_owner = aggregate_owner
122
+ record.aggregate_attribute = vector.last
123
+ aggregate_owner.attributes[vector.last] = record.ar_instance
124
+ end
125
+
126
+ record.ar_instance
127
+
128
+ end
129
+
130
+ def initialize(model, hash = {}, ar_instance = nil)
131
+ @model = model
132
+ @ar_instance = ar_instance
133
+ @synced_attributes = {}
134
+ @attributes = {}
135
+ @changed_attributes = []
136
+ @virgin = true
137
+ records[model] << self
138
+ end
139
+
140
+ def find(*args)
141
+ self.class.find(*args)
142
+ end
143
+
144
+ def new_from_vector(*args)
145
+ self.class.new_from_vector(*args)
146
+ end
147
+
148
+ def primary_key
149
+ @model.primary_key
150
+ end
151
+
152
+ def id
153
+ attributes[primary_key]
154
+ end
155
+
156
+ def id=(value)
157
+ # value can be nil if we are loading an aggregate otherwise check if it already exists
158
+ if !(value and existing_record = records[@model].detect { |record| record.attributes[primary_key] == value})
159
+ attributes[primary_key] = value
160
+ else
161
+ @ar_instance.instance_variable_set(:@backing_record, existing_record)
162
+ existing_record.attributes.merge!(attributes) { |key, v1, v2| v1 }
163
+ end
164
+ value
165
+ end
166
+
167
+ def attributes
168
+ @last_access_at = Time.now
169
+ @attributes
170
+ end
171
+
172
+ def reactive_get!(attribute, reload = nil)
173
+ @virgin = false unless data_loading?
174
+ unless @destroyed
175
+ if @attributes.has_key? attribute
176
+ attributes[attribute].notify if @attributes[attribute].is_a? DummyValue
177
+ apply_method(attribute) if reload
178
+ else
179
+ apply_method(attribute)
180
+ end
181
+ React::State.get_state(self, attribute) unless data_loading?
182
+ attributes[attribute]
183
+ end
184
+ end
185
+
186
+ def reactive_set!(attribute, value)
187
+ @virgin = false unless data_loading?
188
+ return value if @destroyed || dont_update_attribute?(attribute, value)
189
+ return attributes[attribute] if update_aggregate(attribute, value)
190
+ value = update_relationships(attribute, value)
191
+ update_attribute(attribute, value)
192
+ value
193
+ end
194
+
195
+ def dont_update_attribute?(attribute, value)
196
+ return false if attributes[attribute].is_a?(DummyValue)
197
+ return false unless attributes.key?(attribute)
198
+ return false if attributes[attribute] != value
199
+ true
200
+ end
201
+
202
+ def update_attribute(attribute, *args)
203
+ value = args[0]
204
+ if args.count != 0 and data_loading?
205
+ if (aggregation = model.reflect_on_aggregation(attribute)) and !(aggregation.klass < ActiveRecord::Base)
206
+ @synced_attributes[attribute] = aggregation.deserialize(aggregation.serialize(value))
207
+ else
208
+ @synced_attributes[attribute] = value
209
+ end
210
+ end
211
+ if @virgin
212
+ attributes[attribute] = value if args.count != 0
213
+ return
214
+ end
215
+ changed = if args.count == 0
216
+ if (association = @model.reflect_on_association(attribute)) and association.collection?
217
+ attributes[attribute] != @synced_attributes[attribute]
218
+ else
219
+ !attributes[attribute].backing_record.changed_attributes.empty?
220
+ end
221
+ elsif (association = @model.reflect_on_association(attribute)) and association.collection?
222
+ value != @synced_attributes[attribute]
223
+ else
224
+ !@synced_attributes.has_key?(attribute) or @synced_attributes[attribute] != value
225
+ end
226
+ empty_before = changed_attributes.empty?
227
+ if !changed
228
+ changed_attributes.delete(attribute)
229
+ elsif !changed_attributes.include?(attribute)
230
+ changed_attributes << attribute
231
+ end
232
+ had_key = attributes.has_key? attribute
233
+ current_value = attributes[attribute]
234
+ attributes[attribute] = value if args.count != 0
235
+ if !data_loading?
236
+ React::State.set_state(self, attribute, value)
237
+ elsif on_opal_client? and had_key and current_value.loaded? and current_value != value and args.count > 0 # this is to handle changes in already loaded server side methods
238
+ React::State.set_state(self, attribute, value, true)
239
+ end
240
+ if empty_before != changed_attributes.empty?
241
+ React::State.set_state(self, "!CHANGED!", !changed_attributes.empty?, true) unless on_opal_server? or data_loading?
242
+ aggregate_owner.update_attribute(aggregate_attribute) if aggregate_owner
243
+ end
244
+ end
245
+
246
+ def changed?(*args)
247
+ if args.count == 0
248
+ React::State.get_state(self, "!CHANGED!")
249
+ !changed_attributes.empty?
250
+ else
251
+ React::State.get_state(self, args[0])
252
+ changed_attributes.include? args[0]
253
+ end
254
+ end
255
+
256
+ def errors
257
+ @errors ||= ActiveModel::Error.new
258
+ end
259
+
260
+ # called when we have a newly created record, to initialize
261
+ # any nil collections to empty arrays. We can do this because
262
+ # if its a brand new record, then any collections that are still
263
+ # nil must not have any children.
264
+ def initialize_collections
265
+ if (!vector || vector.empty?) && id && id != ''
266
+ @vector = [@model, ["find_by_#{@model.primary_key}", id]]
267
+ end
268
+ @model.reflect_on_all_associations.each do |assoc|
269
+ if assoc.collection? && attributes[assoc.attribute].nil?
270
+ ar_instance.send("#{assoc.attribute}=", [])
271
+ end
272
+ end
273
+ end
274
+
275
+ # sync! now will also initialize any nil collections
276
+ def sync!(hash = {}) # does NOT notify (see saved! for notification)
277
+ hash.each do |attr, value|
278
+ @attributes[attr] = convert(attr, value)
279
+ end
280
+ @synced_attributes = {}
281
+ @synced_attributes.each { |attribute, value| sync_attribute(key, value) }
282
+ @changed_attributes = []
283
+ @saving = false
284
+ @errors = nil
285
+ # set the vector and clear collections - this only happens when a new record is saved
286
+ initialize_collections if (!vector || vector.empty?) && id && id != ''
287
+ self
288
+ end
289
+
290
+ # this keeps the unscoped collection up to date.
291
+ # @destroy_sync and @create_sync prevent multiple insertions
292
+ # to collections that just have a count
293
+ def sync_unscoped_collection!
294
+ if destroyed
295
+ return if @destroy_sync
296
+ @destroy_sync = true
297
+ else
298
+ return if @create_sync
299
+ @create_sync = true
300
+ end
301
+ model.unscoped << ar_instance
302
+ @synced_with_unscoped = !@synced_with_unscoped
303
+ end
304
+
305
+ def sync_attribute(attribute, value)
306
+
307
+ @synced_attributes[attribute] = attributes[attribute] = value
308
+
309
+ #@synced_attributes[attribute] = value.dup if value.is_a? ReactiveRecord::Collection
310
+
311
+ if value.is_a? Collection
312
+ @synced_attributes[attribute] = value.dup_for_sync
313
+ elsif aggregation = model.reflect_on_aggregation(attribute) and (aggregation.klass < ActiveRecord::Base)
314
+ value.backing_record.sync!
315
+ elsif aggregation
316
+ @synced_attributes[attribute] = aggregation.deserialize(aggregation.serialize(value))
317
+ elsif !model.reflect_on_association(attribute)
318
+ @synced_attributes[attribute] = JSON.parse(value.to_json)
319
+ end
320
+
321
+ @changed_attributes.delete(attribute)
322
+ value
323
+ end
324
+
325
+ # helper so we can tell if model exists. We need this so we can detect
326
+ # if a record has local changes that are out of sync.
327
+ def self.exists?(model, id)
328
+ @records[model].detect { |record| record.attributes[model.primary_key] == id }
329
+ end
330
+
331
+ def revert
332
+ @changed_attributes.dup.each do |attribute|
333
+ @ar_instance.send("#{attribute}=", @synced_attributes[attribute])
334
+ @attributes.delete(attribute) unless @synced_attributes.key?(attribute)
335
+ end
336
+ @changed_attributes = []
337
+ @errors = nil
338
+ end
339
+
340
+ def saving!
341
+ React::State.set_state(self, self, :saving) unless data_loading?
342
+ @saving = true
343
+ end
344
+
345
+ def errors!(errors)
346
+ @saving = false
347
+ @errors = errors and ActiveModel::Error.new(errors)
348
+ end
349
+
350
+ def saved! # sets saving to false AND notifies
351
+ @saving = false
352
+ if !@errors or @errors.empty?
353
+ React::State.set_state(self, self, :saved)
354
+ elsif !data_loading?
355
+ React::State.set_state(self, self, :error)
356
+ end
357
+ self
358
+ end
359
+
360
+ def saving?
361
+ React::State.get_state(self, self)
362
+ @saving
363
+ end
364
+
365
+ def new?
366
+ !id and !vector
367
+ end
368
+
369
+ def find_association(association, id)
370
+ inverse_of = association.inverse_of
371
+ instance = if id
372
+ find(association.klass, association.klass.primary_key, id)
373
+ else
374
+ new_from_vector(association.klass, nil, *vector, association.attribute)
375
+ end
376
+ instance_backing_record_attributes = instance.backing_record.attributes
377
+ inverse_association = association.klass.reflect_on_association(inverse_of)
378
+ if inverse_association.collection?
379
+ instance_backing_record_attributes[inverse_of] = if id and id != ""
380
+ Collection.new(@model, instance, inverse_association, association.klass, ["find", id], inverse_of)
381
+ else
382
+ Collection.new(@model, instance, inverse_association, *vector, association.attribute, inverse_of)
383
+ end unless instance_backing_record_attributes[inverse_of]
384
+ instance_backing_record_attributes[inverse_of].replace [@ar_instance]
385
+ else
386
+ instance_backing_record_attributes[inverse_of] = @ar_instance
387
+ end unless association.through_association? || instance_backing_record_attributes.key?(inverse_of)
388
+ instance
389
+ end
390
+
391
+ def apply_method(method)
392
+ # Fills in the value returned by sending "method" to the corresponding server side db instance
393
+ if on_opal_server? and changed?
394
+ log("Warning fetching virtual attributes (#{model.name}.#{method}) during prerendering on a changed or new model is not implemented.", :warning)
395
+ # to implement this we would have to sync up any changes during prererendering with a set the cached models (see server_data_cache)
396
+ # right now server_data cache is read only, BUT we could change this. However it seems like a tails case. Why would we create or update
397
+ # a model during prerendering???
398
+ end
399
+ if !new?
400
+ new_value = if association = @model.reflect_on_association(method)
401
+ if association.collection?
402
+ Collection.new(association.klass, @ar_instance, association, *vector, method)
403
+ else
404
+ find_association(association, (id and id != "" and self.class.fetch_from_db([@model, [:find, id], method, @model.primary_key])))
405
+ end
406
+ elsif aggregation = @model.reflect_on_aggregation(method) and (aggregation.klass < ActiveRecord::Base)
407
+ new_from_vector(aggregation.klass, self, *vector, method)
408
+ elsif id and id != ''
409
+ self.class.fetch_from_db([@model, [:find, id], *method]) || self.class.load_from_db(self, *(vector ? vector : [nil]), method)
410
+ else # its a attribute in an aggregate or we are on the client and don't know the id
411
+ self.class.fetch_from_db([*vector, *method]) || self.class.load_from_db(self, *(vector ? vector : [nil]), method)
412
+ end
413
+ new_value = @attributes[method] if new_value.is_a? DummyValue and @attributes.has_key?(method)
414
+ sync_attribute(method, new_value)
415
+ elsif association = @model.reflect_on_association(method) and association.collection?
416
+ @attributes[method] = Collection.new(association.klass, @ar_instance, association)
417
+ elsif aggregation = @model.reflect_on_aggregation(method) and (aggregation.klass < ActiveRecord::Base)
418
+ @attributes[method] = aggregation.klass.new.tap do |aggregate|
419
+ backing_record = aggregate.backing_record
420
+ backing_record.aggregate_owner = self
421
+ backing_record.aggregate_attribute = method
422
+ end
423
+ elsif !aggregation and method != model.primary_key
424
+ if model.columns_hash[method]
425
+ new_value = convert(method, model.columns_hash[method][:default])
426
+ else
427
+ unless @attributes.key?(method)
428
+ log("Warning: reading from new #{model.name}.#{method} before assignment. Will fetch value from server. This may not be what you expected!!", :warning)
429
+ end
430
+ new_value = self.class.load_from_db(self, *(vector ? vector : [nil]), method)
431
+ new_value = @attributes[method] if new_value.is_a?(DummyValue) && @attributes.key?(method)
432
+ end
433
+ sync_attribute(method, new_value)
434
+ end
435
+ end
436
+
437
+ def self.infer_type_from_hash(klass, hash)
438
+ klass = klass.base_class
439
+ return klass unless hash
440
+ type = hash[klass.inheritance_column]
441
+ begin
442
+ return Object.const_get(type)
443
+ rescue Exception => e
444
+ message = "Could not subclass #{@model_klass.model_name} as #{type}. Perhaps #{type} class has not been required. Exception: #{e}"
445
+ `console.error(#{message})`
446
+ end if type
447
+ klass
448
+ end
449
+
450
+ class << self
451
+ attr_reader :outer_scopes
452
+
453
+ def default_scope
454
+ @class_scopes[:default_scope]
455
+ end
456
+
457
+ def unscoped
458
+ @class_scopes[:unscoped]
459
+ end
460
+
461
+ def add_to_outer_scopes(item)
462
+ @outer_scopes << item
463
+ end
464
+ end
465
+
466
+ # when_not_saving will wait until reactive-record is not saving a model.
467
+ # Currently there is no easy way to do this without polling.
468
+ def self.when_not_saving(model)
469
+ if @records[model].detect(&:saving?)
470
+ poller = every(0.1) do
471
+ unless @records[model].detect(&:saving?)
472
+ poller.stop
473
+ yield model
474
+ end
475
+ end
476
+ else
477
+ yield model
478
+ end
479
+ end
480
+
481
+ # While evaluating scopes we want to catch any requests
482
+ # to the server. Once we catch any requests to the server
483
+ # then all the further scopes in that chain will be made
484
+ # at the server.
485
+
486
+ class << self
487
+ class DbRequestMade < RuntimeError; end
488
+
489
+ def catch_db_requests(return_val = nil)
490
+ @catch_db_requests = true
491
+ yield
492
+ rescue DbRequestMade => e
493
+ puts "Warning request for server side data during scope evaluation: #{e.message}"
494
+ return_val
495
+ ensure
496
+ @catch_db_requests = false
497
+ end
498
+
499
+ alias pre_synchromesh_load_from_db load_from_db
500
+
501
+ def load_from_db(*args)
502
+ raise DbRequestMade, args if @catch_db_requests
503
+ pre_synchromesh_load_from_db(*args)
504
+ end
505
+ end
506
+
507
+ def destroy_associations
508
+ @destroyed = false
509
+ model.reflect_on_all_associations.each do |association|
510
+ if association.collection?
511
+ attributes[association.attribute].replace([]) if attributes[association.attribute]
512
+ else
513
+ @ar_instance.send("#{association.attribute}=", nil)
514
+ end
515
+ end
516
+ @destroyed = true
517
+ end
518
+
519
+ end
520
+ end