hyper-mesh 0.5.2 → 0.5.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (203) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +34 -0
  3. data/README.md +10 -11
  4. data/docs/activerecord_api.md +55 -39
  5. data/docs/authorization-policies.md +62 -19
  6. data/docs/client_side_scoping.md +1 -1
  7. data/docs/word_game.md +26 -0
  8. data/examples/action-cable-production-mode/.gitignore +21 -0
  9. data/examples/action-cable-production-mode/Gemfile +57 -0
  10. data/examples/action-cable-production-mode/Gemfile.lock +231 -0
  11. data/examples/action-cable-production-mode/README.md +24 -0
  12. data/examples/action-cable-production-mode/Rakefile +6 -0
  13. data/examples/action-cable-production-mode/app/assets/config/manifest.js +3 -0
  14. data/examples/action-cable-production-mode/app/assets/images/.keep +0 -0
  15. data/examples/action-cable-production-mode/app/assets/javascripts/application.js +20 -0
  16. data/examples/action-cable-production-mode/app/assets/javascripts/cable.js +13 -0
  17. data/examples/action-cable-production-mode/app/assets/javascripts/channels/.keep +0 -0
  18. data/examples/action-cable-production-mode/app/assets/stylesheets/application.css +15 -0
  19. data/examples/action-cable-production-mode/app/channels/application_cable/channel.rb +4 -0
  20. data/examples/action-cable-production-mode/app/channels/application_cable/connection.rb +4 -0
  21. data/examples/action-cable-production-mode/app/controllers/application_controller.rb +3 -0
  22. data/examples/action-cable-production-mode/app/controllers/test_controller.rb +5 -0
  23. data/examples/action-cable-production-mode/app/models/models.rb +2 -0
  24. data/examples/action-cable-production-mode/app/models/public/application_record.rb +3 -0
  25. data/examples/action-cable-production-mode/app/models/public/word.rb +2 -0
  26. data/examples/action-cable-production-mode/app/policies/application_policy.rb +14 -0
  27. data/examples/action-cable-production-mode/app/views/components.rb +16 -0
  28. data/examples/action-cable-production-mode/app/views/components/app.rb +18 -0
  29. data/examples/action-cable-production-mode/app/views/layouts/application.html.erb +14 -0
  30. data/examples/action-cable-production-mode/bin/bundle +3 -0
  31. data/examples/action-cable-production-mode/bin/rails +9 -0
  32. data/examples/action-cable-production-mode/bin/rake +9 -0
  33. data/examples/action-cable-production-mode/bin/setup +34 -0
  34. data/examples/action-cable-production-mode/bin/spring +16 -0
  35. data/examples/action-cable-production-mode/bin/update +29 -0
  36. data/examples/action-cable-production-mode/config.ru +5 -0
  37. data/examples/action-cable-production-mode/config/application.rb +18 -0
  38. data/examples/action-cable-production-mode/config/boot.rb +3 -0
  39. data/examples/action-cable-production-mode/config/cable.yml +9 -0
  40. data/examples/action-cable-production-mode/config/database.yml +25 -0
  41. data/examples/action-cable-production-mode/config/environment.rb +5 -0
  42. data/examples/action-cable-production-mode/config/environments/development.rb +56 -0
  43. data/examples/action-cable-production-mode/config/environments/production.rb +89 -0
  44. data/examples/action-cable-production-mode/config/environments/test.rb +42 -0
  45. data/examples/action-cable-production-mode/config/initializers/application_controller_renderer.rb +6 -0
  46. data/examples/action-cable-production-mode/config/initializers/assets.rb +11 -0
  47. data/examples/action-cable-production-mode/config/initializers/backtrace_silencers.rb +7 -0
  48. data/examples/action-cable-production-mode/config/initializers/cookies_serializer.rb +5 -0
  49. data/examples/action-cable-production-mode/config/initializers/filter_parameter_logging.rb +4 -0
  50. data/examples/action-cable-production-mode/config/initializers/hyper_mesh.rb +4 -0
  51. data/examples/action-cable-production-mode/config/initializers/inflections.rb +16 -0
  52. data/examples/action-cable-production-mode/config/initializers/mime_types.rb +4 -0
  53. data/examples/action-cable-production-mode/config/initializers/new_framework_defaults.rb +24 -0
  54. data/examples/action-cable-production-mode/config/initializers/session_store.rb +3 -0
  55. data/examples/action-cable-production-mode/config/initializers/wrap_parameters.rb +14 -0
  56. data/examples/action-cable-production-mode/config/locales/en.yml +23 -0
  57. data/examples/action-cable-production-mode/config/puma.rb +47 -0
  58. data/examples/action-cable-production-mode/config/routes.rb +5 -0
  59. data/examples/action-cable-production-mode/config/secrets.yml +22 -0
  60. data/examples/action-cable-production-mode/config/spring.rb +6 -0
  61. data/examples/action-cable-production-mode/db/migrate/20161114213840_create_words.rb +9 -0
  62. data/examples/action-cable-production-mode/db/schema.rb +34 -0
  63. data/examples/action-cable-production-mode/db/seeds.rb +7 -0
  64. data/examples/action-cable-production-mode/lib/assets/.keep +0 -0
  65. data/examples/action-cable-production-mode/lib/tasks/.keep +0 -0
  66. data/examples/action-cable-production-mode/log/.keep +0 -0
  67. data/examples/action-cable-production-mode/public/404.html +67 -0
  68. data/examples/action-cable-production-mode/public/422.html +67 -0
  69. data/examples/action-cable-production-mode/public/500.html +66 -0
  70. data/examples/action-cable-production-mode/public/apple-touch-icon-precomposed.png +0 -0
  71. data/examples/action-cable-production-mode/public/apple-touch-icon.png +0 -0
  72. data/examples/action-cable-production-mode/public/assets/.sprockets-manifest-3e9abd3ee8ba47c39a55b61ae37ed9e1.json +1 -0
  73. data/examples/action-cable-production-mode/public/assets/application-90043e04e9e784054fd08159fa7aafe5e23d3ffb31584b1bea1e47043c9cfb5a.js +50 -0
  74. data/examples/action-cable-production-mode/public/assets/application-90043e04e9e784054fd08159fa7aafe5e23d3ffb31584b1bea1e47043c9cfb5a.js.gz +0 -0
  75. data/examples/action-cable-production-mode/public/assets/application-e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855.css +0 -0
  76. data/examples/action-cable-production-mode/public/assets/application-e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855.css.gz +0 -0
  77. data/examples/action-cable-production-mode/public/favicon.ico +0 -0
  78. data/examples/action-cable-production-mode/public/robots.txt +5 -0
  79. data/examples/action-cable-production-mode/test/controllers/.keep +0 -0
  80. data/examples/action-cable-production-mode/test/fixtures/.keep +0 -0
  81. data/examples/action-cable-production-mode/test/fixtures/files/.keep +0 -0
  82. data/examples/action-cable-production-mode/test/fixtures/words.yml +7 -0
  83. data/examples/action-cable-production-mode/test/helpers/.keep +0 -0
  84. data/examples/action-cable-production-mode/test/integration/.keep +0 -0
  85. data/examples/action-cable-production-mode/test/mailers/.keep +0 -0
  86. data/examples/action-cable-production-mode/test/models/.keep +0 -0
  87. data/examples/action-cable-production-mode/test/models/word_test.rb +7 -0
  88. data/examples/action-cable-production-mode/test/test_helper.rb +10 -0
  89. data/examples/action-cable-production-mode/tmp/.keep +0 -0
  90. data/examples/action-cable-production-mode/vendor/assets/javascripts/.keep +0 -0
  91. data/examples/action-cable-production-mode/vendor/assets/stylesheets/.keep +0 -0
  92. data/examples/action-cable/app/views/layouts/application.html.erb +1 -1
  93. data/examples/pusher-fake/app/views/layouts/application.html.erb +1 -1
  94. data/examples/pusher/app/views/layouts/application.html.erb +1 -1
  95. data/examples/simple-poller/app/views/layouts/application.html.erb +1 -1
  96. data/examples/word-game/.gitignore +21 -0
  97. data/examples/word-game/Gemfile +57 -0
  98. data/examples/word-game/Gemfile.lock +241 -0
  99. data/examples/word-game/README.md +24 -0
  100. data/examples/word-game/Rakefile +6 -0
  101. data/examples/word-game/app/assets/config/manifest.js +3 -0
  102. data/examples/word-game/app/assets/images/.keep +0 -0
  103. data/examples/word-game/app/assets/javascripts/application.js +20 -0
  104. data/examples/word-game/app/assets/javascripts/cable.js +13 -0
  105. data/examples/word-game/app/assets/javascripts/channels/.keep +0 -0
  106. data/examples/word-game/app/assets/stylesheets/application.css +15 -0
  107. data/examples/word-game/app/channels/application_cable/channel.rb +4 -0
  108. data/examples/word-game/app/channels/application_cable/connection.rb +4 -0
  109. data/examples/word-game/app/controllers/application_controller.rb +3 -0
  110. data/examples/word-game/app/controllers/concerns/.keep +0 -0
  111. data/examples/word-game/app/helpers/application_helper.rb +2 -0
  112. data/examples/word-game/app/jobs/application_job.rb +2 -0
  113. data/examples/word-game/app/mailers/application_mailer.rb +4 -0
  114. data/examples/word-game/app/models/application_record.rb +3 -0
  115. data/examples/word-game/app/models/concerns/.keep +0 -0
  116. data/examples/word-game/app/models/models.rb +2 -0
  117. data/examples/word-game/app/models/public/.keep +0 -0
  118. data/examples/word-game/app/policies/application_policy.rb +14 -0
  119. data/examples/word-game/app/views/components.rb +16 -0
  120. data/examples/word-game/app/views/components/.keep +0 -0
  121. data/examples/word-game/app/views/layouts/application.html.erb +14 -0
  122. data/examples/{action-cable → word-game}/app/views/layouts/mailer.html.erb +0 -0
  123. data/examples/{action-cable → word-game}/app/views/layouts/mailer.text.erb +0 -0
  124. data/examples/word-game/bin/bundle +3 -0
  125. data/examples/word-game/bin/rails +9 -0
  126. data/examples/word-game/bin/rake +9 -0
  127. data/examples/word-game/bin/setup +34 -0
  128. data/examples/word-game/bin/spring +16 -0
  129. data/examples/word-game/bin/update +29 -0
  130. data/examples/word-game/config.ru +5 -0
  131. data/examples/word-game/config/application.rb +20 -0
  132. data/examples/word-game/config/boot.rb +3 -0
  133. data/examples/word-game/config/cable.yml +9 -0
  134. data/examples/word-game/config/database.yml +25 -0
  135. data/examples/word-game/config/environment.rb +5 -0
  136. data/examples/word-game/config/environments/development.rb +56 -0
  137. data/examples/word-game/config/environments/production.rb +86 -0
  138. data/examples/word-game/config/environments/test.rb +42 -0
  139. data/examples/word-game/config/initializers/application_controller_renderer.rb +6 -0
  140. data/examples/word-game/config/initializers/assets.rb +11 -0
  141. data/examples/word-game/config/initializers/backtrace_silencers.rb +7 -0
  142. data/examples/word-game/config/initializers/cookies_serializer.rb +5 -0
  143. data/examples/word-game/config/initializers/filter_parameter_logging.rb +4 -0
  144. data/examples/word-game/config/initializers/hyper_mesh.rb +4 -0
  145. data/examples/word-game/config/initializers/inflections.rb +16 -0
  146. data/examples/word-game/config/initializers/mime_types.rb +4 -0
  147. data/examples/word-game/config/initializers/new_framework_defaults.rb +24 -0
  148. data/examples/word-game/config/initializers/session_store.rb +3 -0
  149. data/examples/word-game/config/initializers/wrap_parameters.rb +14 -0
  150. data/examples/word-game/config/locales/en.yml +23 -0
  151. data/examples/word-game/config/puma.rb +47 -0
  152. data/examples/word-game/config/routes.rb +4 -0
  153. data/examples/word-game/config/secrets.yml +22 -0
  154. data/examples/word-game/config/spring.rb +6 -0
  155. data/examples/word-game/db/seeds.rb +7 -0
  156. data/examples/word-game/lib/assets/.keep +0 -0
  157. data/examples/word-game/lib/tasks/.keep +0 -0
  158. data/examples/word-game/log/.keep +0 -0
  159. data/examples/word-game/public/404.html +67 -0
  160. data/examples/word-game/public/422.html +67 -0
  161. data/examples/word-game/public/500.html +66 -0
  162. data/examples/word-game/public/apple-touch-icon-precomposed.png +0 -0
  163. data/examples/word-game/public/apple-touch-icon.png +0 -0
  164. data/examples/word-game/public/favicon.ico +0 -0
  165. data/examples/word-game/public/robots.txt +5 -0
  166. data/examples/word-game/test/controllers/.keep +0 -0
  167. data/examples/word-game/test/fixtures/.keep +0 -0
  168. data/examples/word-game/test/fixtures/files/.keep +0 -0
  169. data/examples/word-game/test/helpers/.keep +0 -0
  170. data/examples/word-game/test/integration/.keep +0 -0
  171. data/examples/word-game/test/mailers/.keep +0 -0
  172. data/examples/word-game/test/models/.keep +0 -0
  173. data/examples/word-game/test/test_helper.rb +10 -0
  174. data/examples/word-game/tmp/.keep +0 -0
  175. data/examples/word-game/vendor/assets/javascripts/.keep +0 -0
  176. data/examples/word-game/vendor/assets/stylesheets/.keep +0 -0
  177. data/lib/active_record_base.rb +20 -1
  178. data/lib/hyper-mesh.rb +1 -0
  179. data/lib/hypermesh/version.rb +1 -1
  180. data/lib/kernel/itself.rb +5 -0
  181. data/lib/reactive_record/active_record/class_methods.rb +87 -11
  182. data/lib/reactive_record/active_record/instance_methods.rb +4 -2
  183. data/lib/reactive_record/active_record/reactive_record/collection.rb +9 -12
  184. data/lib/reactive_record/active_record/reactive_record/isomorphic_base.rb +52 -52
  185. data/lib/reactive_record/active_record/reactive_record/while_loading.rb +2 -3
  186. data/lib/reactive_record/scope_description.rb +8 -3
  187. data/lib/synchromesh/client_drivers.rb +1 -9
  188. data/lib/synchromesh/connection.rb +2 -2
  189. data/lib/synchromesh/synchromesh.rb +4 -4
  190. data/spec/reactive_record/auto_load_itself_spec.rb +24 -0
  191. data/spec/reactive_record/finder_method_spec.rb +67 -0
  192. data/spec/reactive_record/save_while_loading_spec.rb +44 -0
  193. data/spec/reactive_record/update_associations_spec.rb +0 -1
  194. data/spec/reactive_record/update_scopes_spec.rb +1 -1
  195. data/spec/spec_helper.rb +5 -0
  196. data/spec/synchromesh/examples/random_examples.rb +33 -0
  197. metadata +176 -10
  198. data/examples/pusher-fake/app/views/layouts/mailer.html.erb +0 -13
  199. data/examples/pusher-fake/app/views/layouts/mailer.text.erb +0 -1
  200. data/examples/pusher/app/views/layouts/mailer.html.erb +0 -13
  201. data/examples/pusher/app/views/layouts/mailer.text.erb +0 -1
  202. data/examples/simple-poller/app/views/layouts/mailer.html.erb +0 -13
  203. data/examples/simple-poller/app/views/layouts/mailer.text.erb +0 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 48e04a10e1ad295fd3b0d59e4bf55f376c4b7776
4
- data.tar.gz: eef9862a88351836cc5a0bccfec7e3d7899d0315
3
+ metadata.gz: b8982a6629467339b2a91000f327e77e5d1b08f9
4
+ data.tar.gz: ff8f97442cfc6de5a5902c424e19d89e490a2f99
5
5
  SHA512:
6
- metadata.gz: c7458271b5d897075e1769db7017ce01e4cbe87d2c756605051f42e9131ddf8ca5816d81538b12c8f04e852902e4890b86cd3fabc944f6c1c2a484ddff91572e
7
- data.tar.gz: b636a901cfe8eb0434c54cd3baa61534c34f758243531a2946b2d9dd3504e21d93f3154defbdf2542a6b31e78b7044377d05fe4b6a5c2ef82415f931ba655f91
6
+ metadata.gz: ac64d306366dcf4eca7034e60c04ec423766d93f76d2dabef0c123d31e9447654b29046e0055daf86ef5c6b35609c9d02fec01e523f8da5fc90326e781edaabe
7
+ data.tar.gz: 61ae00d5f4ecf921d4acc9037808841c5539fb4ed59dbbfac712bef62e1c2512d049a89217de410719d9fb0cd89de2c04b3162fa3a19d1423f472ca3b59810af
@@ -0,0 +1,34 @@
1
+ # Change Log
2
+
3
+ All notable changes to this project will be documented in this file starting with v0.8.4.
4
+ This project *tries* to adhere to [Semantic Versioning](http://semver.org/), even before v1.0.
5
+
6
+ Changes are grouped as follows:
7
+ - **Added** for new features.
8
+ - **Changed** for changes in existing functionality.
9
+ - **Deprecated** for once-stable features to be removed in upcoming releases.
10
+ - **Removed** for deprecated features removed in this release.
11
+ - **Fixed** for any bug fixes.
12
+ - **Security** to invite users to upgrade in case of vulnerabilities.
13
+
14
+ <!--
15
+ Whitespace conventions:
16
+ - 4 spaces before ## titles
17
+ - 2 spaces before ### titles
18
+ - 1 spaces before normal text
19
+ -->
20
+
21
+ ## [0.5.3] - 2017-01-03
22
+
23
+
24
+ ### Added
25
+
26
+ - Fixed problem with synchronizing multiple requests to same record within one render cycle (#20)
27
+ - Add *finder* methods. I.e. server side methods that return a single record using a custom query. (#12)
28
+ - Allow `save` even if records are loading (#10)
29
+ - `ReactiveRecord.Load` will automatically apply `.itself` to the final value of the load block (#9)
30
+
31
+
32
+ ### Fixed
33
+
34
+ - Can't create AR records from within Rake Tasks. (#20)
data/README.md CHANGED
@@ -1,10 +1,9 @@
1
1
  # ![](https://avatars3.githubusercontent.com/u/15810526?v=3&s=40&raw=true)HyperMesh
2
2
 
3
-
4
3
  [![Join the chat at https://gitter.im/reactrb/chat](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/reactrb/chat?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
5
4
  [![Gem Version](https://badge.fury.io/rb/hyper-mesh.svg)](https://badge.fury.io/rb/hyper-mesh)
6
5
 
7
- HyperMesh gives your HyperReact components CRUD access to your server side ActiveRecord models, using the the standard ActiveRecord API.
6
+ HyperMesh gives your HyperReact components CRUD access to your server side ActiveRecord models, using the standard ActiveRecord API.
8
7
  In addition HyperMesh implements push notifications (via a number of possible
9
8
  technologies) so changes to records on the server are dynamically pushed to all authorised clients.
10
9
 
@@ -27,7 +26,7 @@ For example consider a simple model called `Dictionary` which might be part of W
27
26
  class Dictionary < ActiveRecord::Base
28
27
 
29
28
  # attributes
30
- # word: string
29
+ # word: string
31
30
  # definition: text
32
31
  # pronunciation: string
33
32
 
@@ -69,7 +68,7 @@ For more complete examples with *push* updates checkout any of the apps in the `
69
68
 
70
69
  + [Rails 5 with ActionCable](/docs/action_cable_quickstart.md)
71
70
  + [Using Pusher.com](/docs/pusher_quickstart.md)
72
- + [Using Pusher-Faker](/docs/pusher_quickstart.md)
71
+ + [Using Pusher-Faker](/docs/pusher_faker_quickstart.md)
73
72
  + [Using Simple Polling](/docs/simple_poller_quickstart.md)
74
73
 
75
74
  ## Basic Installation and Setup
@@ -93,7 +92,7 @@ To have changes to your models on the server broadcast to authorized clients, a
93
92
 
94
93
  ```ruby
95
94
  # config/initializers/hyper_mesh.rb
96
- HyperMesh.configuration |config|
95
+ HyperMesh.configuration do |config|
97
96
  config.transport = :simple_poller
98
97
  end
99
98
  ```
@@ -104,7 +103,7 @@ For setting up the other possible transports following one of these guides:
104
103
 
105
104
  + [Rails 5 with ActionCable](/docs/action_cable_quickstart.md)
106
105
  + [Using Pusher.com](/docs/pusher_quickstart.md)
107
- + [Using Pusher-Faker](/docs/pusher_quickstart.md)
106
+ + [Using Pusher-Faker](/docs/pusher_faker_quickstart.md)
108
107
  + [Using Simple Polling](/docs/simple_poller_quickstart.md)
109
108
 
110
109
  ## Advanced Configuration
@@ -142,29 +141,29 @@ By default HyperMesh will look for a `ApplicationPolicy` class.
142
141
  See the Pusher-Fake gem repo for details.
143
142
 
144
143
  - Forgetting to add `require pusher` in application.js file
145
- this results in an error like this:
144
+ this results in an error like this:
146
145
  ```text
147
146
  Exception raised while rendering #<TopLevelRailsComponent:0x53e>
148
147
  ReferenceError: Pusher is not defined
149
148
  ```
150
149
  To resolve make sure you `require 'pusher'` in your application.js file if using pusher. **DO NOT** require pusher from your components manifest as this will cause prerendering to fail.
151
150
 
152
- - No create/update/destroy policies
151
+ - No create/update/destroy policies
153
152
  You must explicitly allow changes to the models to be made by the client. If you don't you will
154
153
  see 500 responses from the server when you try to update. To open all access do this in
155
154
  your application policy: `allow_change(to: :all, on: [:create, :update, :destroy]) { true }`
156
155
 
157
156
  - Cannot connect to real pusher account:
158
157
  If you are trying to use a real pusher account (not pusher-fake) but see errors like this
159
- ```text
158
+ ```text
160
159
  pusher.self.js?body=1:62 WebSocket connection to
161
160
  'wss://127.0.0.1/app/PUSHER_API_KEY?protocol=7&client=js&version=3.0.0&flash=false'
162
161
  failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED
163
- ```
162
+ ```
164
163
  Check to see if you are including the pusher-fake gem.
165
164
  HyperMesh will always try to use pusher-fake if it sees the gem included. Remove it and you should be good to go. See [issue #5](https://github.com/hyper-react/HyperMesh/issues/5) for more details.
166
165
 
167
- - Cannot connect with ActionCable. Make sure that `config.action_cable.allowed_request_origins` includes the url you use for development (including the port) and that you are useing `Puma`.
166
+ - Cannot connect with ActionCable. Make sure that `config.action_cable.allowed_request_origins` includes the url you use for development (including the port) and that you are using `Puma`.
168
167
 
169
168
  - Attributes are not being converted from strings, or do not have their default values
170
169
  Eager loading is probably turned off. HyperMesh needs to eager load `models/public` so it can find all the column information for all public models.
@@ -1,12 +1,12 @@
1
1
  ## ActiveRecord API
2
2
 
3
- HyperMesh uses a subset of the standard ActiveRecord API to give your client side HyperReact components access to your server side models. As much as possible HyperMesh follows the syntax as semantics of ActiveRecord.
3
+ HyperMesh uses a subset of the standard ActiveRecord API to give your client side HyperReact components access to your server side models. As much as possible HyperMesh follows the syntax and semantics of ActiveRecord.
4
4
 
5
5
  ### Interfacing to React
6
6
 
7
- HyperMesh uses the React to deliver your model data to the client without you having to create extra APIs or specialized controllers. The key idea of React is that when state (or params) change, the portions of the display effected by this data will be updated.
7
+ HyperMesh integrates with React to deliver your model data to the client without you having to create extra APIs or specialized controllers. The key idea of React is that when state (or params) change, the portions of the display effected by this data will be updated.
8
8
 
9
- HyperMesh automatically creates state objects that will be updated as server side data is loaded, or changes.
9
+ HyperMesh automatically creates react state objects that will be updated as server side data is loaded or changes. When these states change the associated parts of the display will be updated.
10
10
 
11
11
  A brief overview of how this works will help you understand the how HyperMesh gets the job done.
12
12
 
@@ -14,23 +14,27 @@ A brief overview of how this works will help you understand the how HyperMesh ge
14
14
 
15
15
  On the UI you will be reading models in order to display data.
16
16
 
17
- If during the rendering of the display the model data is not yet loaded placeholder values (the default values from the `columns_hash`) will be returned by HyperMesh.
17
+ If during the rendering of the display the model data is not yet loaded, placeholder values (the default values from the `columns_hash`) will be returned by HyperMesh.
18
18
 
19
19
  HyperMesh then keeps track of where these placeholders (or `DummyValue`s) are displayed, and when they do get loaded, those parts of the display will re-render.
20
20
 
21
21
  If later the data changes (either due to local user actions, or receiving push updates) then again any parts of the display that were dependent on the current values will be re-rendered.
22
22
 
23
- You normally do not have to be aware of this. Just access your models using the normal scopes and finders, to compute values and display attributes as you would on the server. Initially the display will show the placeholder values and then will be replaced with the real values.
24
-
23
+ You normally do not have to be aware of this. Just access your models using the normal scopes and finders, then compute values and display attributes as you would on the server. Initially the display will show the placeholder values and then will be replaced with the real values.
25
24
 
26
25
  #### Prerendering
27
26
 
28
- During server-side pre-rendering, we do know all the values immediately so on initial page load all the values will be loaded and present.
27
+ During server-side pre-rendering, HyperMesh has direct access to the server so on initial page load all the values will be loaded and present.
29
28
 
30
- #### Load Cycle Methods
29
+ #### Lazy Loading
31
30
 
32
- There are a number of methods that allow you to interact with this load cycle when needed. These are documented [below].
31
+ HyperMesh lazy loads values, and does not load any thing until an explicit displayable value is requested. For example `Todo.all` will have no action, but `Todo.all.pluck[:title]` will return an array of titles.
33
32
 
33
+ At the end of the rendering cycle the set of all values requested will be merged into a tree structure and sent to the server, returning the minimum amount of data needed.
34
+
35
+ #### Load Cycle Methods
36
+
37
+ There are a number of methods that allow you to interact with this load cycle when needed. These are documented [below](#other-methods-for-interacting-with-the-load-and-render-cycle).
34
38
 
35
39
  ### Class Methods
36
40
 
@@ -38,16 +42,16 @@ There are a number of methods that allow you to interact with this load cycle wh
38
42
 
39
43
  `new`: Takes a hash of attributes and initializes a new unsaved record. The values of any attributes not specified in the hash will be taken from the models default values specified in the `columns_hash`.
40
44
 
41
- If new is passed a native javascript object it will be treated as a hash and converted accordingly.
45
+ If `new` is passed a native javascript object it will be treated as a hash and converted accordingly.
42
46
 
43
47
  `create`: Short hand for `new(...).save`. See the `save` instance method for details on how saving is done.
44
48
 
45
49
  #### Scoping and Finding
46
50
 
47
- `scope` and `default_scope`: HyperMesh adds four new options to these methods: `joins`, `client`, `select` and `server`. The `joins` option provides information on how the scope will be joined with other models. The `client` and `select` options allow scoping to be done on the client side to offload this from the server, and the `server` option is there just for symmetry with the othe options. See the [Client Side Scoping](/docs/client_side_scoping.md) page for more details.
51
+ `scope` and `default_scope`: HyperMesh adds four new options to these methods: `joins`, `client`, `select` and `server`. The `joins` option provides information on how the scope will be joined with other models. The `client` and `select` options allow scoping to be done on the client side to offload this from the server, and the `server` option is there just for symmetry with the other options. See the [Client Side Scoping](/docs/client_side_scoping.md) page for more details.
48
52
 
49
53
  ```ruby
50
- # the scope proc is executed on the server
54
+ # the active scope proc is executed on the server
51
55
  scope :active, -> () { where(completed: true) }
52
56
 
53
57
  # if the scope does a join (or include) this must be indicated
@@ -61,7 +65,7 @@ scope :with_recent_comments,
61
65
  # locally at the client
62
66
  scope :completed,
63
67
  server: -> { where(complete: true) }
64
- client: -> { complete }
68
+ client: -> { complete } # return true if the record should be included
65
69
  ```
66
70
 
67
71
  `unscoped` and `all`: These builtin scopes work just like standard ActiveRecord.
@@ -70,25 +74,27 @@ scope :completed,
70
74
  Word.all.each { |word| LI { word.text }}
71
75
  ```
72
76
 
73
- `limit` and `offset`: These builtin scopes behave as they do on the server:
74
-
75
- ```ruby
76
- Word.offset(500).limit(20) # get words 500-519
77
- ```
77
+ BTW: to save typing you can skip the `all`: Models will respond like enumerators
78
78
 
79
- `find`: takes an id and returns the corresponding record.
79
+ `find`: takes an id and delivers the corresponding record.
80
80
 
81
- `find_by`: takes single item hash indicating an attribute value pair to find.
81
+ `find_by`: takes a single item hash indicating an attribute value pair to find.
82
82
 
83
- `find_by_...`: i.e. `find_by_first_name` these will find the first record with a matching attribute.
83
+ `find_by_...`: i.e. `find_by_first_name` these methods will find the first record with a matching attribute.
84
84
 
85
85
  ```ruby
86
86
  Word.find_by_text('hello') # short for Word.find_by(text: 'hello')
87
87
  ```
88
88
 
89
+ `limit` and `offset`: These builtin scopes behave as they do on the server:
90
+
91
+ ```ruby
92
+ Word.offset(500).limit(20) # get words 500-519
93
+ ```
94
+
89
95
  #### Relationships and Aggregations
90
96
 
91
- `belongs_to, has_many, has_one`: These all work as on the server. However it is important that you fully specify both sides of the relationship. This is not always necessary on the server because ActiveRecord uses the table schema to work things out.
97
+ `belongs_to, has_many, has_one`: These all work as on the server. However it is important that you fully specify both sides of the relationship.
92
98
 
93
99
  ```ruby
94
100
  class Todo < ActiveRecord::Base
@@ -100,8 +106,22 @@ class User < ActiveRecord::Base
100
106
  end
101
107
  ```
102
108
 
109
+ Note that on the client the linkages between relationships are live and direct. In the above example this works:
110
+
111
+ ```ruby
112
+ Todo.create(assigned_to: some_user)
113
+ ```
114
+
115
+ but this may not:
116
+
117
+ ```ruby
118
+ Todo.create(assigned_to_id: some_user.id)
119
+ ```
120
+
103
121
  `composed_of`: You can create aggregate models like ActiveRecord.
104
122
 
123
+ Similar to the linkages in relationships, aggregate records are represented on the client as actual independent objects.
124
+
105
125
  #### Defining server methods
106
126
 
107
127
  Normally an application defined instance method will run on the client and the server:
@@ -114,7 +134,7 @@ class User < ActiveRecord::Base
114
134
  end
115
135
  ```
116
136
 
117
- Sometimes it is desirable to only run method on the server. This can be done using the `server_method` macro:
137
+ Sometimes it is desirable to only run the method on the server. This can be done using the `server_method` macro:
118
138
 
119
139
  ```ruby
120
140
  class User < ActiveRecord::Base
@@ -124,7 +144,7 @@ class User < ActiveRecord::Base
124
144
  end
125
145
  ```
126
146
 
127
- When the method is first called on the client the default value will be returned, and there will be a reactive update when the true value is returned from the server.
147
+ When the method is first called on the client the default value will be returned, and there will be a reactive update when the true value is returned from the server.
128
148
 
129
149
  To force the value to be recomputed at the server append a `!` to the end of the name, otherwise the last value returned from the server will continue to be returned.
130
150
 
@@ -140,11 +160,11 @@ To force the value to be recomputed at the server append a `!` to the end of th
140
160
 
141
161
  #### Attribute and Relationship Getter and Setters
142
162
 
143
- All attributes have an associated getter and setter. All relationships have a getter. All belongs_to relationships have a setter. `has_many` relationships can be updated using the push (`<<`) operator or using the `delete` method.
163
+ All attributes have an associated getter and setter. All relationships have a getter. All `belongs_to` relationships also have a setter. `has_many` relationships can be updated using the push (`<<`) operator or using the `delete` method.
144
164
 
145
165
  ```ruby
146
- puts my_todo.name
147
- my_todo.name = "neuname"
166
+ puts my_todo.title
167
+ my_todo.title = "neutitle"
148
168
  my_todo.comments << a_new_comment
149
169
  a_new_comment.todo == my_todo # true!
150
170
  ```
@@ -161,7 +181,7 @@ my_todo.save(validate: false).then do |result|
161
181
  end
162
182
  ```
163
183
 
164
- After saving the models will have an `errors` hash with any validation problems.
184
+ After a save operation completes the models will have an `errors` hash (just like on the server) with any validation problems.
165
185
 
166
186
  During the save operation the method `saving?` will return `true`. This can be used to instead of (or with) the promise to update the screen:
167
187
 
@@ -183,7 +203,7 @@ end
183
203
 
184
204
  Like `save` destroy returns a promise that is resolved when the destroy completes.
185
205
 
186
- After the destroy completes the records `destroyed?` method will return true.
206
+ After the destroy completes the record's `destroyed?` method will return true.
187
207
 
188
208
  #### Other Instance Methods
189
209
 
@@ -201,17 +221,17 @@ After the destroy completes the records `destroyed?` method will return true.
201
221
 
202
222
  `dup` duplicate the instance.
203
223
 
204
- `==` two instances are the same if they reference the same underlying table row.
224
+ `==` two instances are the same if it is known that they reference the same underlying table row.
205
225
 
206
226
  `..._changed?` (i.e. name_changed?) returns true if the specific attribute has changed.
207
227
 
208
- `itself` returns the record, but will also force a load of at least the model's id.
228
+ `itself` returns the record, but will override lazy loading and force a load of at least the model's id.
209
229
 
210
- #### Other Methods for Interacting with the Load & Render Cycle
230
+ ### Other Methods for Interacting with the Load and Render Cycle
211
231
 
212
232
  #### `loading?` and `loaded?`
213
233
 
214
- All Ruby objects will respond to these methods. If you want to put up a "Please Wait" message, spinner, etc, you can use the `loaded?` or `loading?` method to determine if the object represents a real loaded value or not.
234
+ All Ruby objects will respond to these methods. If you want to put up a "Please Wait" message, spinner, etc, you can use the `loaded?` or `loading?` method to determine if the object represents a real loaded value or not. Any value for which `loaded?` returns `false` (or `loading?` returns `true`) will eventually load and cause a re-render
215
235
 
216
236
  #### The `HyperMesh.load` Method
217
237
 
@@ -229,17 +249,13 @@ end.then |result|
229
249
  end
230
250
  ```
231
251
 
232
-
233
-
234
252
  #### Force Loading Attributes
235
253
 
236
- Like
237
-
238
254
  Normally you will simply display attributes as part of the render method, and when the values are loaded from the server the component will re-render.
239
255
 
240
256
  Sometimes outside of the render method you may need to insure an attribute (or a server side method) is loaded before proceeding. This is typically when you are building some kind of higher level store.
241
257
 
242
- The `load` takes a list of attributes (symbols) and will insure these are loaded. Load returns a promise that is resolved when the load completes, or can be passed a block that will execute when the load completes.
258
+ The `load` method takes a list of attributes (symbols) and will insure these are loaded. Load returns a promise that is resolved when the load completes, or can be passed a block that will execute when the load completes.
243
259
 
244
260
  ```ruby
245
261
  before_mount do
@@ -250,4 +266,4 @@ before_mount do
250
266
  end
251
267
  ```
252
268
 
253
- Think hard about how you are using this, as HyperMesh already acts as flux store, and is managing state for you.
269
+ Think hard about how you are using this, as HyperMesh already acts as flux store, and is managing state for you. It may be you are just creating a redundant store!
@@ -1,8 +1,12 @@
1
1
  ### HyperMesh Authorization Policies
2
2
 
3
- Each time an ActiveRecord model changes, HyperMesh broadcasts the changed attributes over *channels*.
3
+ Access to your models is controlled by *Policies* that describe how the current *acting_user* and *channels* may access your models.
4
4
 
5
- An application can have several channels and each channel and each active record model can have different *policies* to determine which attributes are sent when a record changes.
5
+ Each browser session has an *acting_user* (which may be nil) and you will define `create`, `update`, and `destroy` policies giving (or denying) the `acting_user` the ability to do these operations.
6
+
7
+ Read and *broadcast* access is defined based on *channels* which are connected based again on the current `acting_user`. Read access is initiated when a specific browser tries to read a record attribute, and broadcasts are initiated whenever a model changes.
8
+
9
+ An application can have several channels and each channel and each active record model can have different policies to determine which attributes are sent when a record changes.
6
10
 
7
11
  For example a Todo application might have an *instance* of a channel for each currently logged in user; an instance of a channel for each team if that team has one or more logged in users; and a general `AdminUser` channel shared by all administrators that are logged in.
8
12
 
@@ -20,8 +24,8 @@ class UserPolicy # defines policies for the User class
20
24
  # The policy is defined by a block that is executed in the context of the
21
25
  # current acting_user.
22
26
 
23
- # For our User instance connection the policy is that there must be logged in
24
- # user, and the connection is made to that user:
27
+ # For our User instance connection the policy is that there must be a
28
+ # logged-in user, and the connection is made to that user:
25
29
  regulate_instance_connections { self }
26
30
  # If there is no logged in user self will be nil, and no connection will be
27
31
  # made.
@@ -206,7 +210,7 @@ Typically connections are made to ActiveRecord models, and if those are in the `
206
210
 
207
211
  #### Acting User
208
212
 
209
- HyperMesh uses the same `acting_user` method that reactive-record permissions uses. This method is typically defined in the ApplicationController and would normally pick up the current session user, and return an appropriate object.
213
+ HyperMesh looks for an `acting_user` method typically defined in the ApplicationController and would normally pick up the current session user, and return an appropriate object.
210
214
 
211
215
  ```ruby
212
216
  class ApplicationController < ActiveController::Base
@@ -405,6 +409,59 @@ class MessagePolicy
405
409
  end
406
410
  ```
407
411
 
412
+ #### Browser Initiated Change policies
413
+
414
+ To allow code in the browser to create, update or destroy a model, there must be a change access policy defined for that operation.
415
+
416
+ Each change access policy executes a block in the context of the record that will be accessed. The current value of `acting_user` is also defined for the life of the block.
417
+
418
+ If the block returns a truthy value access will be allowed, otherwise if the block returns a falsy value or raises an exception, access will be denied.
419
+
420
+ In the below examples we assume that your user model responds to `admin?` but this is not built into HyperMesh.
421
+
422
+ ```ruby
423
+ class TodoPolicy
424
+ # allow creation to any logged in user
425
+ allow_create { acting_user }
426
+ # only allow the owner, author any any admin to update a todo
427
+ allow_update { acting_user == owner || acting_user == author || acting_user.admin? }
428
+ # don't allow Todo's to be destroyed
429
+ # this is the default behavior so its not actually needed
430
+ allow_destroy { false }
431
+ end
432
+ ```
433
+
434
+ There are several variants of the access policy method:
435
+
436
+ ```ruby
437
+ class ConfigDataPolicy
438
+ allow_change(on: [:create, :update, :destroy]) { acting_user.admin? }
439
+ # which can be shortened to:
440
+ allow_change { acting_user.admin? }
441
+ end
442
+ ```
443
+
444
+ ```ruby
445
+ class ApplicationPolicy
446
+ # do any thing to all models unless we are in production! Be careful!
447
+ allow_change(to: :all) { true } unless Rails.env.production?
448
+ # and always allow admins to destroy models globally:
449
+ allow_change(to: :all, on: :destroy) { acting_user.admin? }
450
+ # which is the same as saying:
451
+ allow_destroy(to: :all) { acting_user.admin? }
452
+ # you can create model specific policies in the Application Policy as well.
453
+ # Here we allow the author of a message to destroy the message within 5
454
+ # minutes of creation.
455
+ allow_destroy(to: Message) do
456
+ return true if acting_user == author && created_at > 5.minutes.ago
457
+ return true if acting_user.admin?
458
+ end
459
+ end
460
+ ```
461
+
462
+ Note that there is no `allow_read` method. Read access is granted if this browser would have the attribute broadcast to it.
463
+
464
+
408
465
  #### Method Summary and Name Space Conflicts
409
466
 
410
467
  Policy classes (and the HyperMesh::PolicyMethods module) define the following class methods:
@@ -433,17 +490,3 @@ class ProductionCenterPolicy < MyPolicyClass
433
490
  ...
434
491
  end
435
492
  ```
436
-
437
- #### Setting the policy directory
438
-
439
- *HyperMesh auto-connect needs to know about all policies ahead of time so cannot rely on rails auto loading. Sorry about that!*
440
-
441
- By default HyperMesh will load all the files in the `app/policies` directory. To change the directory set the policy_directory in the synchromesh initializer.
442
-
443
- ```ruby
444
- HyperMesh.configuration do |config|
445
- ...
446
- config.policy_directory = File.join(Rails.root, 'app', 'synchromesh-authorization')
447
- # can also be set to nil if you want to manually require your files
448
- end
449
- ```