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,189 @@
1
+ module ReactiveRecord
2
+ # methods to update aggregrations and relations, called from reactive_set!
3
+ class Base
4
+ def update_aggregate(attribute, value)
5
+ # if attribute is an aggregate then
6
+ # match and update all fields in the aggregate from value and return true
7
+ # otherwise return false
8
+ aggregation = @model.reflect_on_aggregation(attribute)
9
+ return false unless aggregation && (aggregation.klass < ActiveRecord::Base)
10
+ if value
11
+ value_attributes = value.backing_record.attributes
12
+ update_mapped_attributes(aggregation) { |attr| value_attributes[attr] }
13
+ else
14
+ update_mapped_attributes(aggregation) { nil }
15
+ end
16
+ true
17
+ end
18
+
19
+ def update_mapped_attributes(aggregation)
20
+ # insure the aggregate attr is initialized, clear the virt flag, the caller
21
+ # will yield each of the matching attribute values
22
+ attr = aggregation.attribute
23
+ attributes[attr] ||= aggregation.klass.new if new?
24
+ aggregate_record = attributes[attr]
25
+ raise 'uninitialized aggregate attribute - should never happen' unless aggregate_record
26
+ aggregate_backing_record = aggregate_record.backing_record
27
+ aggregate_backing_record.virgin = false
28
+ aggregation.mapped_attributes.each do |mapped_attribute|
29
+ aggregate_backing_record.update_attribute(mapped_attribute, yield(mapped_attribute))
30
+ end
31
+ end
32
+
33
+ def update_relationships(attr, value)
34
+ # update the inverse relationship, and any through relationships
35
+ # return either the value, or in the case of updating a collection
36
+ # return the new collection after value is overwritten into it.
37
+ association = @model.reflect_on_association(attr)
38
+ return value unless association
39
+ if association.collection?
40
+ overwrite_has_many_collection(association, value)
41
+ else
42
+ update_belongs_to_association(association, value)
43
+ value
44
+ end
45
+ end
46
+
47
+ def overwrite_has_many_collection(association, value)
48
+ # create a new collection to hold value, shove it in, and return the new collection
49
+ # the replace method will take care of updating the inverse belongs_to links as
50
+ # the collection is overwritten
51
+ Collection.new(association.klass, @ar_instance, association).tap do |collection|
52
+ collection.replace(value || [])
53
+ end
54
+ end
55
+
56
+ def update_belongs_to_association(association, value)
57
+ # either update update the inverse has_many collection or individual belongs_to
58
+ # inverse values
59
+ if association.inverse.collection?
60
+ update_has_many_through_associations(association, value)
61
+ update_inverse_collections(association, value)
62
+ else
63
+ update_inverse_attribute(association, value)
64
+ end
65
+ end
66
+
67
+ def update_inverse_attribute(association, value)
68
+ # when updating the inverse attribute of a belongs_to that is itself a belongs_to
69
+ # (i.e. 1-1 relationship) we clear the existing inverse value and then
70
+ # write the current record to the new value
71
+ current_value = attributes[association.attribute]
72
+ inverse_attr = association.inverse.attribute
73
+ current_value.attributes[inverse_attr] = nil unless current_value.nil?
74
+ return if value.nil?
75
+ value.attributes[inverse_attr] = @ar_instance
76
+ return if data_loading?
77
+ React::State.set_state(value.backing_record, inverse_attr, @ar_instance)
78
+ end
79
+
80
+ def update_inverse_collections(association, value)
81
+ # when updating an inverse attribute of a belongs_to that is a has_many (i.e. a collection)
82
+ # we need to first remove the current associated value (if non-nil), then add the new
83
+ # value to the collection. If the inverse collection is not yet initialized we do it here.
84
+ current_value = attributes[association.attribute]
85
+ inverse_attr = association.inverse.attribute
86
+ if value.nil?
87
+ current_value.attributes[inverse_attr].delete(@ar_instance) unless current_value.nil?
88
+ else
89
+ value.backing_record.push_onto_collection(association.inverse, @ar_instance)
90
+ end
91
+ end
92
+
93
+ def push_onto_collection(association, ar_instance)
94
+ attributes[association.attribute] ||= Collection.new(@model, @ar_instance, association)
95
+ attributes[association.attribute] << ar_instance
96
+ end
97
+
98
+ def update_has_many_through_associations(association, value)
99
+ association.through_associations.each { |ta| update_through_association(ta, value) }
100
+ association.source_associations.each { |sa| update_source_association(sa, value) }
101
+ end
102
+
103
+ def update_through_association(ta, new_belongs_to_value)
104
+ # appointment.doctor = doctor_new_value (i.e. through association is changing)
105
+ # means appointment.doctor_new_value.patients << appointment.patient
106
+ # and we have to appointment.doctor_current_value.patients.delete(appointment.patient)
107
+ source_value = attributes[ta.source]
108
+ current_belongs_to_value = attributes[ta.inverse.attribute]
109
+ return unless source_value
110
+ unless current_belongs_to_value.nil? || current_belongs_to_value.attributes[ta.attribute].nil?
111
+ current_belongs_to_value.attributes[ta.attribute].delete(source_value)
112
+ end
113
+ return unless new_belongs_to_value
114
+ new_belongs_to_value.attributes[ta.attribute] ||= Collection.new(ta.klass, new_belongs_to_value, ta)
115
+ new_belongs_to_value.attributes[ta.attribute] << source_value
116
+ end
117
+
118
+ def update_source_association(sa, new_source_value)
119
+ # appointment.patient = patient_value (i.e. source is changing)
120
+ # means appointment.doctor.patients.delete(appointment.patient)
121
+ # means appointment.doctor.patients << patient_value
122
+ belongs_to_value = attributes[sa.inverse.attribute]
123
+ current_source_value = attributes[sa.source]
124
+ return unless belongs_to_value
125
+ unless belongs_to_value.attributes[sa.attribute].nil? || current_source_value.nil?
126
+ belongs_to_value.attributes[sa.attribute].delete(current_source_value)
127
+ end
128
+ return unless new_source_value
129
+ belongs_to_value.attributes[sa.attribute] ||= Collection.new(sa.klass, belongs_to_value, sa)
130
+ belongs_to_value.attributes[sa.attribute] << new_source_value
131
+ end
132
+ end
133
+ end
134
+
135
+ # def reactive_set!(attribute, value)
136
+ # @virgin = false unless data_loading?
137
+ # unless @destroyed or (!(attributes[attribute].is_a? DummyValue) and attributes.has_key?(attribute) and attributes[attribute] == value)
138
+ # if association = @model.reflect_on_association(attribute)
139
+ # if association.collection?
140
+ # collection = Collection.new(association.klass, @ar_instance, association)
141
+ # collection.replace(value || [])
142
+ # value = collection
143
+ # else
144
+ # inverse_of = association.inverse_of
145
+ # inverse_association = association.klass.reflect_on_association(inverse_of)
146
+ # if inverse_association.collection?
147
+ # if value.nil?
148
+ # attributes[attribute].attributes[inverse_of].delete(@ar_instance) unless attributes[attribute].nil?
149
+ # elsif value.attributes[inverse_of]
150
+ # value.attributes[inverse_of] << @ar_instance
151
+ # else
152
+ # value.attributes[inverse_of] = Collection.new(@model, value, inverse_association)
153
+ # # value.attributes[inverse_of].replace [@ar_instance]
154
+ # # why was the above not just the below???? fixed 10/28/2016
155
+ # value.attributes[inverse_of] << @ar_instance
156
+ # end
157
+ # elsif !value.nil?
158
+ # attributes[attribute].attributes[inverse_of] = nil unless attributes[attribute].nil?
159
+ # value.attributes[inverse_of] = @ar_instance
160
+ # React::State.set_state(value.backing_record, inverse_of, @ar_instance) unless data_loading?
161
+ # elsif attributes[attribute]
162
+ # attributes[attribute].attributes[inverse_of] = nil
163
+ # end
164
+ # end
165
+ # elsif aggregation = @model.reflect_on_aggregation(attribute) and (aggregation.klass < ActiveRecord::Base)
166
+ #
167
+ # if new?
168
+ # attributes[attribute] ||= aggregation.klass.new
169
+ # elsif !attributes[attribute]
170
+ # raise "uninitialized aggregate attribute - should never happen"
171
+ # end
172
+ #
173
+ # aggregate_record = attributes[attribute].backing_record
174
+ # aggregate_record.virgin = false
175
+ #
176
+ # if value
177
+ # value_attributes = value.backing_record.attributes
178
+ # aggregation.mapped_attributes.each { |mapped_attribute| aggregate_record.update_attribute(mapped_attribute, value_attributes[mapped_attribute])}
179
+ # else
180
+ # aggregation.mapped_attributes.each { |mapped_attribute| aggregate_record.update_attribute(mapped_attribute, nil) }
181
+ # end
182
+ #
183
+ # return attributes[attribute]
184
+ #
185
+ # end
186
+ # update_attribute(attribute, value)
187
+ # end
188
+ # value
189
+ # end
@@ -0,0 +1,62 @@
1
+ module ReactiveRecord
2
+ # The base collection class works with relationships
3
+ # method overrides for scoped collections
4
+ module ScopedCollection
5
+ [:filter?, :collector?, :joins_with?, :related_records_for].each do |method|
6
+ define_method(method) { |*args| @scope_description.send method, *args }
7
+ end
8
+
9
+ def set_pre_sync_related_records(related_records, _record = nil)
10
+ @pre_sync_related_records = nil
11
+ ReactiveRecord::Base.catch_db_requests do
12
+ @pre_sync_related_records = filter_records(related_records)
13
+ live_scopes.each do |scope|
14
+ scope.set_pre_sync_related_records(@pre_sync_related_records)
15
+ end
16
+ end if filter?
17
+ end
18
+
19
+ def sync_scopes(related_records, record, filtering = true)
20
+ filtering =
21
+ @pre_sync_related_records && filtering &&
22
+ ReactiveRecord::Base.catch_db_requests do
23
+ related_records = update_collection(related_records)
24
+ end
25
+ reload_from_db if !filtering && joins_with?(record)
26
+ live_scopes.each { |scope| scope.sync_scopes(related_records, record, filtering) }
27
+ ensure
28
+ @pre_sync_related_records = nil
29
+ end
30
+
31
+ def update_collection(related_records)
32
+ if collector?
33
+ update_collector_scope(related_records)
34
+ else
35
+ related_records = filter_records(related_records)
36
+ update_filter_scope(@pre_sync_related_records, related_records)
37
+ end
38
+ end
39
+
40
+ def update_collector_scope(related_records)
41
+ current = Set.new([*@collection])
42
+ (related_records - @pre_sync_related_records).each { |r| current << r }
43
+ (@pre_sync_related_records - related_records).each { |r| current.delete(r) }
44
+ replace(filter_records(current))
45
+ Set.new([*@collection])
46
+ end
47
+
48
+ def update_filter_scope(before, after)
49
+ if (collection || !@count.nil?) && before != after
50
+ if collection
51
+ (after - before).each { |r| push r }
52
+ (before - after).each { |r| delete r }
53
+ else
54
+ @count += (after - before).count
55
+ @count -= (before - after).count
56
+ notify_of_change self # TODO remove self .... and retest
57
+ end
58
+ end
59
+ after
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,16 @@
1
+ module ReactiveRecord
2
+ # The base collection class works with relationships
3
+ # method overrides for the unscoped collection
4
+ module UnscopedCollection
5
+ def set_pre_sync_related_records(related_records, _record = nil)
6
+ @pre_sync_related_records = related_records
7
+ live_scopes.each { |scope| scope.set_pre_sync_related_records(@pre_sync_related_records) }
8
+ end
9
+
10
+ def sync_scopes(related_records, record, filtering = true)
11
+ live_scopes.each { |scope| scope.sync_scopes(related_records, record, filtering) }
12
+ ensure
13
+ @pre_sync_related_records = nil
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,299 @@
1
+ module ReactiveRecord
2
+
3
+ # will repeatedly execute the block until it is loaded
4
+ # immediately returns a promise that will resolve once the block is loaded
5
+
6
+ def self.load(&block)
7
+ promise = Promise.new
8
+ @load_stack ||= []
9
+ @load_stack << @loads_pending
10
+ @loads_pending = nil
11
+ result = block.call
12
+ if @loads_pending
13
+ @blocks_to_load ||= []
14
+ @blocks_to_load << [Base.last_fetch_at, promise, block]
15
+ else
16
+ promise.resolve result
17
+ end
18
+ @loads_pending = @load_stack.pop
19
+ promise
20
+ rescue Exception => e
21
+ debugger
22
+ React::IsomorphicHelpers.log "ReactiveRecord.load exception raised during initial load: #{e}", :error
23
+ end
24
+
25
+ def self.loads_pending!
26
+ @loads_pending = true
27
+ end
28
+
29
+ def self.check_loads_pending
30
+ if @loads_pending
31
+ if Base.pending_fetches.count > 0
32
+ true
33
+ else # this happens when for example loading foo.x results in somebody looking at foo.y while foo.y is still being loaded
34
+ ReactiveRecord::WhileLoading.loaded_at Base.last_fetch_at
35
+ ReactiveRecord::WhileLoading.quiet!
36
+ false
37
+ end
38
+ end
39
+ end
40
+
41
+ def self.run_blocks_to_load(fetch_id, failure = nil)
42
+ if @blocks_to_load
43
+ blocks_to_load_now = @blocks_to_load.select { |data| data.first == fetch_id }
44
+ @blocks_to_load = @blocks_to_load.reject { |data| data.first == fetch_id }
45
+ @load_stack ||= []
46
+ blocks_to_load_now.each do |data|
47
+ id, promise, block = data
48
+ @load_stack << @loads_pending
49
+ @loads_pending = nil
50
+ result = block.call(failure)
51
+ if check_loads_pending and !failure
52
+ @blocks_to_load << [Base.last_fetch_at, promise, block]
53
+ else
54
+ promise.resolve result
55
+ end
56
+ @loads_pending = @load_stack.pop
57
+ end
58
+ end
59
+ rescue Exception => e
60
+ React::IsomorphicHelpers.log "ReactiveRecord.load exception raised during retry: #{e}", :error
61
+ end
62
+
63
+
64
+ # Adds while_loading feature to React
65
+ # to use attach a .while_loading handler to any element for example
66
+ # div { "displayed if everything is loaded" }.while_loading { "displayed while I'm loading" }
67
+ # the contents of the div will be switched (using jQuery.show/hide) depending on the state of contents of the first block
68
+
69
+ # To notify React that something is loading use React::WhileLoading.loading!
70
+ # once everything is loaded then do React::WhileLoading.loaded_at message (typically a time stamp just for debug purposes)
71
+
72
+ class WhileLoading
73
+
74
+ include React::IsomorphicHelpers
75
+
76
+ before_first_mount do
77
+ @css_to_preload = ""
78
+ @while_loading_counter = 0
79
+ end
80
+
81
+ def get_next_while_loading_counter
82
+ @while_loading_counter += 1
83
+ end
84
+
85
+ def preload_css(css)
86
+ @css_to_preload << css << "\n"
87
+ end
88
+
89
+ def self.has_observers?
90
+ React::State.has_observers?(self, :loaded_at)
91
+ end
92
+
93
+ prerender_footer do
94
+ "<style>\n#{@css_to_preload}\n</style>".tap { @css_to_preload = ""}
95
+ end
96
+
97
+ if RUBY_ENGINE == 'opal'
98
+
99
+ # I DONT THINK WE USE opal-jquery in this module anymore - require 'opal-jquery' if opal_client?
100
+
101
+ include React::Component
102
+
103
+ param :loading
104
+ param :loaded_children
105
+ param :loading_children
106
+ param :element_type
107
+ param :element_props
108
+ param :display, default: ""
109
+
110
+ class << self
111
+
112
+ def loading?
113
+ @is_loading
114
+ end
115
+
116
+ def loading!
117
+ React::RenderingContext.waiting_on_resources = true
118
+ React::State.get_state(self, :loaded_at)
119
+ React::State.set_state(self, :quiet, false)
120
+ @is_loading = true
121
+ end
122
+
123
+ def loaded_at(loaded_at)
124
+ React::State.set_state(self, :loaded_at, loaded_at)
125
+ @is_loading = false
126
+ end
127
+
128
+ def quiet?
129
+ React::State.get_state(self, :quiet)
130
+ end
131
+
132
+ def page_loaded?
133
+ React::State.get_state(self, :page_loaded)
134
+ end
135
+
136
+ def quiet!
137
+ React::State.set_state(self, :quiet, true)
138
+ after(1) { React::State.set_state(self, :page_loaded, true) } unless on_opal_server? or @page_loaded
139
+ @page_loaded = true
140
+ end
141
+
142
+ def add_style_sheet
143
+ @style_sheet ||= %x{
144
+ $('<style type="text/css">'+
145
+ ' .reactive_record_is_loading > .reactive_record_show_when_loaded { display: none; }'+
146
+ ' .reactive_record_is_loaded > .reactive_record_show_while_loading { display: none; }'+
147
+ '</style>').appendTo("head")
148
+ }
149
+ end
150
+
151
+ end
152
+
153
+ before_mount do
154
+ @uniq_id = WhileLoading.get_next_while_loading_counter
155
+ WhileLoading.preload_css(
156
+ ".reactive_record_while_loading_container_#{@uniq_id} > :nth-child(1n+#{loaded_children.count+1}) {\n"+
157
+ " display: none;\n"+
158
+ "}\n"
159
+ )
160
+ end
161
+
162
+ after_mount do
163
+ @waiting_on_resources = loading
164
+ WhileLoading.add_style_sheet
165
+ %x{
166
+ var node = #{dom_node};
167
+ $(node).children(':nth-child(-1n+'+#{loaded_children.count}+')').addClass('reactive_record_show_when_loaded');
168
+ $(node).children(':nth-child(1n+'+#{loaded_children.count+1}+')').addClass('reactive_record_show_while_loading');
169
+ }
170
+ end
171
+
172
+ after_update do
173
+ @waiting_on_resources = loading
174
+ end
175
+
176
+ def render
177
+ props = element_props.dup
178
+ classes = [props[:class], props[:className], "reactive_record_while_loading_container_#{@uniq_id}"].compact.join(" ")
179
+ props.merge!({
180
+ "data-reactive_record_while_loading_container_id" => @uniq_id,
181
+ "data-reactive_record_enclosing_while_loading_container_id" => @uniq_id,
182
+ class: classes
183
+ })
184
+ React.create_element(element_type, props) { loaded_children + loading_children }
185
+ end
186
+
187
+ end
188
+
189
+ end
190
+
191
+ end
192
+
193
+ module React
194
+
195
+ class Element
196
+
197
+ def while_loading(display = "", &loading_display_block)
198
+
199
+ loaded_children = []
200
+ loaded_children = block.call.dup if block
201
+
202
+ loading_children = [display]
203
+ loading_children = RenderingContext.build do |buffer|
204
+ result = loading_display_block.call
205
+ buffer << result.to_s if result.is_a? String
206
+ buffer.dup
207
+ end if loading_display_block
208
+ RenderingContext.replace(
209
+ self,
210
+ React.create_element(
211
+ ReactiveRecord::WhileLoading,
212
+ loading: waiting_on_resources,
213
+ loading_children: loading_children,
214
+ loaded_children: loaded_children,
215
+ element_type: type,
216
+ element_props: properties)
217
+ )
218
+ end
219
+
220
+ def hide_while_loading
221
+ while_loading
222
+ end
223
+
224
+ end
225
+
226
+ module Component
227
+
228
+ alias_method :original_component_did_mount, :component_did_mount
229
+
230
+ def component_did_mount(*args)
231
+ original_component_did_mount(*args)
232
+ reactive_record_link_to_enclosing_while_loading_container
233
+ reactive_record_link_set_while_loading_container_class
234
+ end
235
+
236
+ alias_method :original_component_did_update, :component_did_update
237
+
238
+ def component_did_update(*args)
239
+ original_component_did_update(*args)
240
+ reactive_record_link_set_while_loading_container_class
241
+ end
242
+
243
+ def reactive_record_link_to_enclosing_while_loading_container
244
+ # Call after any component mounts - attaches the containers loading id to this component
245
+ # Fyi, the while_loading container is responsible for setting its own link to itself
246
+
247
+ %x{
248
+ var node = #{dom_node};
249
+ if (!$(node).is('[data-reactive_record_enclosing_while_loading_container_id]')) {
250
+ var while_loading_container = $(node).closest('[data-reactive_record_while_loading_container_id]')
251
+ if (while_loading_container.length > 0) {
252
+ var container_id = $(while_loading_container).attr('data-reactive_record_while_loading_container_id')
253
+ $(node).attr('data-reactive_record_enclosing_while_loading_container_id', container_id)
254
+ }
255
+ }
256
+ }
257
+
258
+ end
259
+
260
+ def reactive_record_link_set_while_loading_container_class
261
+
262
+ %x{
263
+
264
+ var node = #{dom_node};
265
+ var while_loading_container_id = $(node).attr('data-reactive_record_enclosing_while_loading_container_id');
266
+ if (while_loading_container_id) {
267
+ var while_loading_container = $('[data-reactive_record_while_loading_container_id='+while_loading_container_id+']');
268
+ var loading = (#{waiting_on_resources} == true);
269
+ if (loading) {
270
+ $(node).addClass('reactive_record_is_loading');
271
+ $(node).removeClass('reactive_record_is_loaded');
272
+ $(while_loading_container).addClass('reactive_record_is_loading');
273
+ $(while_loading_container).removeClass('reactive_record_is_loaded');
274
+
275
+ } else if (!$(node).hasClass('reactive_record_is_loaded')) {
276
+
277
+ if (!$(node).attr('data-reactive_record_while_loading_container_id')) {
278
+ $(node).removeClass('reactive_record_is_loading');
279
+ $(node).addClass('reactive_record_is_loaded');
280
+ }
281
+ if (!$(while_loading_container).hasClass('reactive_record_is_loaded')) {
282
+ var loading_children = $(while_loading_container).
283
+ find('[data-reactive_record_enclosing_while_loading_container_id='+while_loading_container_id+'].reactive_record_is_loading')
284
+ if (loading_children.length == 0) {
285
+ $(while_loading_container).removeClass('reactive_record_is_loading')
286
+ $(while_loading_container).addClass('reactive_record_is_loaded')
287
+ }
288
+ }
289
+
290
+ }
291
+
292
+ }
293
+ }
294
+
295
+ end
296
+
297
+ end if RUBY_ENGINE == 'opal'
298
+
299
+ end