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,131 @@
1
+ require 'spec_helper'
2
+ require 'synchromesh/integration/test_components'
3
+
4
+ describe "regulate access allowed" do
5
+
6
+ before(:all) do
7
+ HyperMesh.configuration do |config|
8
+ config.transport = :none
9
+ end
10
+ end
11
+
12
+ before(:each) do
13
+ stub_const 'DummyModel', Class.new(ActiveRecord::Base)
14
+ DummyModel.class_eval do
15
+ self.table_name = 'test_models'
16
+ end
17
+ end
18
+
19
+ after(:each) do
20
+ load 'lib/synchromesh/reactive_record/permission_patches.rb'
21
+ end
22
+
23
+ HyperMesh::InternalClassPolicy::CHANGE_POLICIES.each do |policy|
24
+
25
+ it "will define a basic allow_#{policy} policy" do
26
+ stub_const 'DummyModelPolicy', Class.new
27
+ DummyModelPolicy.class_eval do
28
+ send("allow_#{policy}") { "called #{policy}" }
29
+ end
30
+ DummyModel.new.send("#{policy}_permitted?").should eq("called #{policy}")
31
+ end
32
+
33
+ it "will define allow_#{policy} policy with a class argument" do
34
+ stub_const 'ApplicationPolicy', Class.new
35
+ ApplicationPolicy.class_eval do
36
+ send("allow_#{policy}", DummyModel) { "called #{policy}" }
37
+ end
38
+ DummyModel.new.send("#{policy}_permitted?").should eq("called #{policy}")
39
+ end
40
+
41
+ it "will define allow_#{policy} policy with the to: :all option" do
42
+ stub_const 'ApplicationPolicy', Class.new
43
+ ApplicationPolicy.class_eval do
44
+ send("allow_#{policy}", to: :all) { "called #{policy} on #{self.class.name}" }
45
+ end
46
+ stub_const 'FooModel', Class.new(ActiveRecord::Base)
47
+ FooModel.class_eval do
48
+ self.table_name = 'test_models'
49
+ end
50
+ DummyModel.new.send("#{policy}_permitted?").should eq("called #{policy} on DummyModel")
51
+ FooModel.new.send("#{policy}_permitted?").should eq("called #{policy} on FooModel")
52
+ end
53
+
54
+ end
55
+
56
+ it "will define a basic allow_change policy" do
57
+ stub_const 'DummyModelPolicy', Class.new
58
+ DummyModelPolicy.class_eval do
59
+ send("allow_change") { "called change" }
60
+ end
61
+ HyperMesh::InternalClassPolicy::CHANGE_POLICIES.each do |policy|
62
+ DummyModel.new.send("#{policy}_permitted?").should eq("called change")
63
+ end
64
+ end
65
+
66
+ it "will define allow_change policy with a class argument" do
67
+ stub_const 'ApplicationPolicy', Class.new
68
+ ApplicationPolicy.class_eval do
69
+ send("allow_change", DummyModel) { "called change" }
70
+ end
71
+ HyperMesh::InternalClassPolicy::CHANGE_POLICIES.each do |policy|
72
+ DummyModel.new.send("#{policy}_permitted?").should eq("called change")
73
+ end
74
+ end
75
+
76
+ it "will define allow_change policy with the to: :all option" do
77
+ stub_const 'ApplicationPolicy', Class.new
78
+ ApplicationPolicy.class_eval do
79
+ send("allow_change", to: :all) { "called change on #{self.class.name}" }
80
+ end
81
+ stub_const 'FooModel', Class.new(ActiveRecord::Base)
82
+ FooModel.class_eval do
83
+ self.table_name = 'test_models'
84
+ end
85
+ HyperMesh::InternalClassPolicy::CHANGE_POLICIES.each do |policy|
86
+ DummyModel.new.send("#{policy}_permitted?").should eq("called change on DummyModel")
87
+ FooModel.new.send("#{policy}_permitted?").should eq("called change on FooModel")
88
+ end
89
+ end
90
+
91
+ it "will define allow_change policy with an :on option" do
92
+ stub_const 'ApplicationPolicy', Class.new
93
+ ApplicationPolicy.class_eval do
94
+ send("allow_change", DummyModel, on: [:create, :update]) { "called change" }
95
+ end
96
+ [:create, :update].each do |policy|
97
+ DummyModel.new.send("#{policy}_permitted?").should eq("called change")
98
+ end
99
+ DummyModel.new.send("destroy_permitted?").should be_falsy
100
+ end
101
+
102
+ it "will define a basic allow_read policy" do
103
+ stub_const 'DummyModelPolicy', Class.new
104
+ DummyModelPolicy.class_eval do
105
+ send("allow_read") { |attr| "called read #{attr}" }
106
+ end
107
+ DummyModel.new.send("view_permitted?", :foo).should eq("called read foo")
108
+ end
109
+
110
+ it "will define allow_read policy with a class argument" do
111
+ stub_const 'ApplicationPolicy', Class.new
112
+ ApplicationPolicy.class_eval do
113
+ send("allow_read", DummyModel) { |attr| "called read #{attr}" }
114
+ end
115
+ DummyModel.new.send("view_permitted?", :foo).should eq("called read foo")
116
+ end
117
+
118
+ it "will define allow_read policy with the to: :all option" do
119
+ stub_const 'ApplicationPolicy', Class.new
120
+ ApplicationPolicy.class_eval do
121
+ send("allow_read", to: :all) { |attr| "called read #{attr} on #{self.class.name}" }
122
+ end
123
+ stub_const 'FooModel', Class.new(ActiveRecord::Base)
124
+ FooModel.class_eval do
125
+ self.table_name = 'test_models'
126
+ end
127
+ DummyModel.new.send("view_permitted?", :foo).should eq("called read foo on DummyModel")
128
+ FooModel.new.send("view_permitted?", :foo).should eq("called read foo on FooModel")
129
+ end
130
+
131
+ end
@@ -0,0 +1,239 @@
1
+ require 'spec_helper'
2
+ require 'synchromesh/integration/test_components'
3
+
4
+ describe "dictionary examples", js: true do
5
+
6
+
7
+ HyperMesh.configuration do |config|
8
+ config.transport = :action_cable
9
+ config.channel_prefix = "synchromesh"
10
+ end
11
+
12
+ before(:all) do
13
+ class DictionaryEntry < ActiveRecord::Base
14
+ def self.build_tables
15
+ connection.create_table(:dictionary_entries, force: true) do |t|
16
+ t.string :word
17
+ t.string :pronunciation
18
+ end
19
+ end
20
+ end
21
+
22
+ class Definition < ActiveRecord::Base
23
+ def self.build_tables
24
+ connection.create_table(:definitions, force: true) do |t|
25
+ t.integer :dictionary_entry_id
26
+ t.text :definition
27
+ t.integer :votes
28
+ end
29
+ end
30
+ end
31
+
32
+ end
33
+
34
+ before(:each) do
35
+
36
+
37
+ stub_const 'ApplicationPolicy', Class.new
38
+ ApplicationPolicy.class_eval do
39
+ always_allow_connection
40
+ regulate_all_broadcasts { |policy| policy.send_all }
41
+ allow_change(to: :all, on: [:create, :update, :destroy]) { true }
42
+ end
43
+
44
+ size_window(:small, :portrait)
45
+
46
+ isomorphic do
47
+ class DictionaryEntry < ActiveRecord::Base
48
+ has_many :definitions
49
+ scope :defined, ->() {joins(:definitions).where('definitions.dictionary_entry_id IS NOT NULL')}
50
+ end
51
+ class Definition < ActiveRecord::Base
52
+ belongs_to :dictionary_entry
53
+ scope :order_by_votes, ->() { order('votes DESC') }
54
+ end
55
+ end
56
+
57
+ DictionaryEntry.build_tables
58
+ Definition.build_tables
59
+
60
+ foo = DictionaryEntry.create(word: 'foo', pronunciation: 'foo')
61
+ Definition.create(
62
+ definition: 'every programmers favorite symbol',
63
+ votes: 3, dictionary_entry: foo
64
+ )
65
+
66
+ DictionaryEntry.create(word: 'meaningless')
67
+
68
+ bar = DictionaryEntry.create(word: 'bar', pronunciation: 'bahr')
69
+ Definition.create(
70
+ definition: 'every programmers 2nd favorite symbol',
71
+ votes: 2, dictionary_entry: bar
72
+ )
73
+ Definition.create(
74
+ definition: 'every programmers favorite place',
75
+ votes: 4, dictionary_entry: bar
76
+ )
77
+
78
+ ruby = DictionaryEntry.create(word: 'ruby', pronunciation: 'roo-bee')
79
+ Definition.create(
80
+ definition: 'every programmers favorite language',
81
+ votes: 30, dictionary_entry: ruby
82
+ )
83
+ Definition.create(
84
+ definition: 'a red gem stone',
85
+ votes: 4, dictionary_entry: ruby
86
+ )
87
+
88
+ hyperloop = DictionaryEntry.create(word: 'hyperloop', pronunciation: 'hahy-per-loop')
89
+ Definition.create(
90
+ definition: 'every programmers favorite UI framework',
91
+ votes: 25, dictionary_entry: hyperloop
92
+ )
93
+ Definition.create(
94
+ definition: 'high speed rail for the 21st century',
95
+ votes: 24, dictionary_entry: hyperloop
96
+ )
97
+
98
+ end
99
+
100
+ it "displays a word" do
101
+ mount "WordOfTheDay" do
102
+
103
+ class WordOfTheDay < React::Component::Base
104
+
105
+ # The current word displayed is stored in the
106
+ # entry state variable. We use a state
107
+ # variable so that the display will refresh
108
+ # when the user chooses a new word
109
+
110
+ define_state :entry
111
+
112
+ def pick_entry!
113
+ # pick a random word and assign the selected record to entry
114
+ # the ! will notify react that the state is changing.
115
+ state.entry! DictionaryEntry.all[rand(DictionaryEntry.count)]
116
+ end
117
+
118
+ # before we mount our component pick the first entry to display
119
+
120
+ before_mount :pick_entry!
121
+
122
+ render(DIV) do
123
+ DIV { "Total definitions: #{DictionaryEntry.count}" }
124
+ DIV do
125
+ DIV { "word: #{state.entry.word}" }
126
+ DIV { "pronunciation: #{state.entry.pronunciation}" }
127
+ definitions
128
+ AddDefinition(entry: state.entry)
129
+ end
130
+ BUTTON { 'pick another' }.on(:click) { pick_entry! }
131
+ end
132
+
133
+ def definitions
134
+ # Display the ordered list of definitions
135
+ # note the use of relationships as scopes
136
+ # To keep things tidy we break out ShowDefinition
137
+ # as a separate component.
138
+ OL do
139
+ state.entry.definitions.order_by_votes.each do |definition|
140
+ LI { ShowDefinition definition: definition }
141
+ end
142
+ end
143
+ end
144
+ end
145
+
146
+ class ShowDefinition < React::Component::Base
147
+
148
+ # The ShowDefinition component has one param
149
+ # of type as "Definition". Typing is not
150
+ # necessary but will raise a warning if the
151
+ # type is wrong, so its helpful in debug.
152
+
153
+ param :definition, type: Definition
154
+
155
+ # display the number of votes and let the user
156
+ # up or down vote the definition
157
+
158
+ render(DIV) do
159
+ DIV do
160
+ SPAN { "votes: #{params.definition.votes}" }
161
+ BUTTON { '+' }.on(:click) { inc_vote(1) }
162
+ BUTTON { '-' }.on(:click) { inc_vote(-1) }
163
+ end
164
+ DIV { params.definition.definition }
165
+ end
166
+
167
+ def inc_vote(amt)
168
+ # update the votes attribute on the definition
169
+ # record. If votes goes to zero destroy the
170
+ # record, otherwise just save it.
171
+ params.definition.votes += amt
172
+ if params.definition.votes.zero?
173
+ params.definition.destroy
174
+ else
175
+ params.definition.save
176
+ end
177
+ end
178
+ end
179
+
180
+ class AddDefinition < React::Component::Base
181
+
182
+ # The AddDefinition component takes a
183
+ # DictionaryEntry, and will allow the user
184
+ # add a new definition.
185
+
186
+ param :entry, type: DictionaryEntry
187
+
188
+ # The definition state just holds the current
189
+ # text that the user has typed.
190
+
191
+ # More interesting is adding_definition which
192
+ # controls whether we diplay a editable text area,
193
+ # or the 'add new definition' button.
194
+
195
+ define_state definition: ''
196
+ define_state :adding_definition
197
+
198
+ def text_area
199
+ # display the text area and provide add& cancel buttons
200
+ DIV do
201
+ TEXTAREA(rows: 4, cols: 30, value: state.definition)
202
+ .on(:change) { |e| state.definition! e.target.value }
203
+
204
+ BUTTON { 'add' }
205
+ .on(:click) { add_definition }
206
+
207
+ BUTTON { 'cancel' }.on(:click) { state.adding_definition! false }
208
+ end
209
+ end
210
+
211
+ def add_definition
212
+ # Use ActiveRecord create to add a new
213
+ # record. Using the promise returned by
214
+ # create we will wait until the operation is
215
+ # finished to update the UI.
216
+ Definition.create(
217
+ definition: state.definition,
218
+ votes: 1, dictionary_entry: params.entry
219
+ ).then do
220
+ state.adding_definition! false
221
+ state.definition! ''
222
+ end
223
+ end
224
+
225
+ render do
226
+ if state.adding_definition
227
+ text_area
228
+ else
229
+ BUTTON { 'add new definition' }
230
+ .on(:click) { state.adding_definition! true }
231
+ end
232
+ end
233
+ end
234
+ end
235
+ #binding.pry
236
+ pause
237
+ end
238
+
239
+ end
@@ -0,0 +1,196 @@
1
+ require 'spec_helper'
2
+ require 'synchromesh/integration/test_components'
3
+
4
+ describe "dictionary examples", js: true do
5
+
6
+
7
+ HyperMesh.configuration do |config|
8
+ config.transport = :action_cable
9
+ config.channel_prefix = "synchromesh"
10
+ end
11
+
12
+ before(:all) do
13
+ class DictionaryEntry < ActiveRecord::Base
14
+ def self.build_tables
15
+ connection.create_table(:dictionary_entries, force: true) do |t|
16
+ t.string :word
17
+ t.string :pronunciation
18
+ end
19
+ end
20
+ end
21
+
22
+ class Definition < ActiveRecord::Base
23
+ def self.build_tables
24
+ connection.create_table(:definitions, force: true) do |t|
25
+ t.integer :dictionary_entry_id
26
+ t.text :definition
27
+ t.integer :votes
28
+ end
29
+ end
30
+ end
31
+
32
+ end
33
+
34
+ before(:each) do
35
+
36
+
37
+ stub_const 'ApplicationPolicy', Class.new
38
+ ApplicationPolicy.class_eval do
39
+ always_allow_connection
40
+ regulate_all_broadcasts { |policy| policy.send_all }
41
+ allow_change(to: :all, on: [:create, :update, :destroy]) { true }
42
+ end
43
+
44
+ size_window(:small, :portrait)
45
+
46
+ isomorphic do
47
+ class DictionaryEntry < ActiveRecord::Base
48
+ has_many :definitions
49
+ end
50
+ class Definition < ActiveRecord::Base
51
+ belongs_to :dictionary_entry
52
+ scope :order_by_votes,
53
+ server: ->() { order('votes DESC') },
54
+ select: ->() { sort { |a, b| b <=> a } }
55
+ end
56
+ end
57
+
58
+ DictionaryEntry.build_tables
59
+ Definition.build_tables
60
+
61
+ DictionaryEntry.create(word: 'meaningless')
62
+
63
+ end
64
+
65
+ it "displays a word" do
66
+ mount "WordOfTheDay" do
67
+ class WordOfTheDay < React::Component::Base
68
+
69
+ define_state :entry
70
+
71
+ before_mount do
72
+ state.entry! DictionaryEntry.all[0]
73
+ end
74
+
75
+ render(DIV) do
76
+ DIV { "Total definitions: #{DictionaryEntry.count}" }
77
+ DIV do
78
+ DIV { "word: #{state.entry.word}" }
79
+ DIV { "pronunciation: #{state.entry.pronunciation}" }
80
+ definitions
81
+ AddDefinition(entry: state.entry)
82
+ end
83
+ BUTTON { 'pick another' }.on(:click) { pick_entry! }
84
+ end
85
+
86
+ def definitions
87
+ # Display the ordered list of definitions
88
+ # note the use of relationships as scopes
89
+ # To keep things tidy we break out ShowDefinition
90
+ # as a separate component.
91
+ OL do
92
+ state.entry.definitions.order_by_votes.each do |definition|
93
+ LI { ShowDefinition definition: definition }
94
+ end
95
+ end
96
+ end
97
+ end
98
+
99
+ class ShowDefinition < React::Component::Base
100
+
101
+ # The ShowDefinition component has one param
102
+ # of type as "Definition". Typing is not
103
+ # necessary but will raise a warning if the
104
+ # type is wrong, so its helpful in debug.
105
+
106
+ param :definition, type: Definition
107
+
108
+ # display the number of votes and let the user
109
+ # up or down vote the definition
110
+
111
+ render(DIV) do
112
+ DIV do
113
+ SPAN { "votes: #{params.definition.votes}" }
114
+ BUTTON { '+' }.on(:click) { inc_vote(1) }
115
+ BUTTON { '-' }.on(:click) { inc_vote(-1) }
116
+ end
117
+ DIV { params.definition.definition }
118
+ end
119
+
120
+ def inc_vote(amt)
121
+ # update the votes attribute on the definition
122
+ # record. If votes goes to zero destroy the
123
+ # record, otherwise just save it.
124
+ params.definition.votes += amt
125
+ if params.definition.votes.zero?
126
+ params.definition.destroy
127
+ else
128
+ params.definition.save
129
+ end
130
+ end
131
+ end
132
+
133
+ class AddDefinition < React::Component::Base
134
+
135
+ # The AddDefinition component takes a
136
+ # DictionaryEntry, and will allow the user
137
+ # add a new definition.
138
+
139
+ param :entry, type: DictionaryEntry
140
+
141
+ # The definition state just holds the current
142
+ # text that the user has typed.
143
+
144
+ # More interesting is adding_definition which
145
+ # controls whether we diplay a editable text area,
146
+ # or the 'add new definition' button.
147
+
148
+ define_state definition: ''
149
+ define_state :adding_definition
150
+
151
+ def text_area
152
+ # display the text area and provide add& cancel buttons
153
+ DIV do
154
+ TEXTAREA(rows: 4, cols: 30, value: state.definition)
155
+ .on(:change) { |e| state.definition! e.target.value }
156
+
157
+ BUTTON { 'add' }
158
+ .on(:click) { add_definition }
159
+
160
+ BUTTON { 'cancel' }.on(:click) { state.adding_definition! false }
161
+ end
162
+ end
163
+
164
+ def add_definition
165
+ # Use ActiveRecord create to add a new
166
+ # record. Using the promise returned by
167
+ # create we will wait until the operation is
168
+ # finished to update the UI.
169
+ Definition.create(
170
+ definition: state.definition,
171
+ votes: 1, dictionary_entry: params.entry
172
+ ).then do
173
+ state.adding_definition! false
174
+ state.definition! ''
175
+ end
176
+ end
177
+
178
+ render do
179
+ if state.adding_definition
180
+ text_area
181
+ else
182
+ BUTTON { 'add new definition' }
183
+ .on(:click) { state.adding_definition! true }
184
+ end
185
+ end
186
+ end
187
+ end
188
+ pause
189
+ evaluate_ruby do
190
+ ReactiveRecord::Collection.hypertrace instrument: :all
191
+ Definition.create(definition: :foo, dictionary_entry: DictionaryEntry.find(1))
192
+ end
193
+ pause
194
+ end
195
+
196
+ end