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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +34 -0
- data/README.md +10 -11
- data/docs/activerecord_api.md +55 -39
- data/docs/authorization-policies.md +62 -19
- data/docs/client_side_scoping.md +1 -1
- data/docs/word_game.md +26 -0
- data/examples/action-cable-production-mode/.gitignore +21 -0
- data/examples/action-cable-production-mode/Gemfile +57 -0
- data/examples/action-cable-production-mode/Gemfile.lock +231 -0
- data/examples/action-cable-production-mode/README.md +24 -0
- data/examples/action-cable-production-mode/Rakefile +6 -0
- data/examples/action-cable-production-mode/app/assets/config/manifest.js +3 -0
- data/examples/action-cable-production-mode/app/assets/images/.keep +0 -0
- data/examples/action-cable-production-mode/app/assets/javascripts/application.js +20 -0
- data/examples/action-cable-production-mode/app/assets/javascripts/cable.js +13 -0
- data/examples/action-cable-production-mode/app/assets/javascripts/channels/.keep +0 -0
- data/examples/action-cable-production-mode/app/assets/stylesheets/application.css +15 -0
- data/examples/action-cable-production-mode/app/channels/application_cable/channel.rb +4 -0
- data/examples/action-cable-production-mode/app/channels/application_cable/connection.rb +4 -0
- data/examples/action-cable-production-mode/app/controllers/application_controller.rb +3 -0
- data/examples/action-cable-production-mode/app/controllers/test_controller.rb +5 -0
- data/examples/action-cable-production-mode/app/models/models.rb +2 -0
- data/examples/action-cable-production-mode/app/models/public/application_record.rb +3 -0
- data/examples/action-cable-production-mode/app/models/public/word.rb +2 -0
- data/examples/action-cable-production-mode/app/policies/application_policy.rb +14 -0
- data/examples/action-cable-production-mode/app/views/components.rb +16 -0
- data/examples/action-cable-production-mode/app/views/components/app.rb +18 -0
- data/examples/action-cable-production-mode/app/views/layouts/application.html.erb +14 -0
- data/examples/action-cable-production-mode/bin/bundle +3 -0
- data/examples/action-cable-production-mode/bin/rails +9 -0
- data/examples/action-cable-production-mode/bin/rake +9 -0
- data/examples/action-cable-production-mode/bin/setup +34 -0
- data/examples/action-cable-production-mode/bin/spring +16 -0
- data/examples/action-cable-production-mode/bin/update +29 -0
- data/examples/action-cable-production-mode/config.ru +5 -0
- data/examples/action-cable-production-mode/config/application.rb +18 -0
- data/examples/action-cable-production-mode/config/boot.rb +3 -0
- data/examples/action-cable-production-mode/config/cable.yml +9 -0
- data/examples/action-cable-production-mode/config/database.yml +25 -0
- data/examples/action-cable-production-mode/config/environment.rb +5 -0
- data/examples/action-cable-production-mode/config/environments/development.rb +56 -0
- data/examples/action-cable-production-mode/config/environments/production.rb +89 -0
- data/examples/action-cable-production-mode/config/environments/test.rb +42 -0
- data/examples/action-cable-production-mode/config/initializers/application_controller_renderer.rb +6 -0
- data/examples/action-cable-production-mode/config/initializers/assets.rb +11 -0
- data/examples/action-cable-production-mode/config/initializers/backtrace_silencers.rb +7 -0
- data/examples/action-cable-production-mode/config/initializers/cookies_serializer.rb +5 -0
- data/examples/action-cable-production-mode/config/initializers/filter_parameter_logging.rb +4 -0
- data/examples/action-cable-production-mode/config/initializers/hyper_mesh.rb +4 -0
- data/examples/action-cable-production-mode/config/initializers/inflections.rb +16 -0
- data/examples/action-cable-production-mode/config/initializers/mime_types.rb +4 -0
- data/examples/action-cable-production-mode/config/initializers/new_framework_defaults.rb +24 -0
- data/examples/action-cable-production-mode/config/initializers/session_store.rb +3 -0
- data/examples/action-cable-production-mode/config/initializers/wrap_parameters.rb +14 -0
- data/examples/action-cable-production-mode/config/locales/en.yml +23 -0
- data/examples/action-cable-production-mode/config/puma.rb +47 -0
- data/examples/action-cable-production-mode/config/routes.rb +5 -0
- data/examples/action-cable-production-mode/config/secrets.yml +22 -0
- data/examples/action-cable-production-mode/config/spring.rb +6 -0
- data/examples/action-cable-production-mode/db/migrate/20161114213840_create_words.rb +9 -0
- data/examples/action-cable-production-mode/db/schema.rb +34 -0
- data/examples/action-cable-production-mode/db/seeds.rb +7 -0
- data/examples/action-cable-production-mode/lib/assets/.keep +0 -0
- data/examples/action-cable-production-mode/lib/tasks/.keep +0 -0
- data/examples/action-cable-production-mode/log/.keep +0 -0
- data/examples/action-cable-production-mode/public/404.html +67 -0
- data/examples/action-cable-production-mode/public/422.html +67 -0
- data/examples/action-cable-production-mode/public/500.html +66 -0
- data/examples/action-cable-production-mode/public/apple-touch-icon-precomposed.png +0 -0
- data/examples/action-cable-production-mode/public/apple-touch-icon.png +0 -0
- data/examples/action-cable-production-mode/public/assets/.sprockets-manifest-3e9abd3ee8ba47c39a55b61ae37ed9e1.json +1 -0
- data/examples/action-cable-production-mode/public/assets/application-90043e04e9e784054fd08159fa7aafe5e23d3ffb31584b1bea1e47043c9cfb5a.js +50 -0
- data/examples/action-cable-production-mode/public/assets/application-90043e04e9e784054fd08159fa7aafe5e23d3ffb31584b1bea1e47043c9cfb5a.js.gz +0 -0
- data/examples/action-cable-production-mode/public/assets/application-e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855.css +0 -0
- data/examples/action-cable-production-mode/public/assets/application-e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855.css.gz +0 -0
- data/examples/action-cable-production-mode/public/favicon.ico +0 -0
- data/examples/action-cable-production-mode/public/robots.txt +5 -0
- data/examples/action-cable-production-mode/test/controllers/.keep +0 -0
- data/examples/action-cable-production-mode/test/fixtures/.keep +0 -0
- data/examples/action-cable-production-mode/test/fixtures/files/.keep +0 -0
- data/examples/action-cable-production-mode/test/fixtures/words.yml +7 -0
- data/examples/action-cable-production-mode/test/helpers/.keep +0 -0
- data/examples/action-cable-production-mode/test/integration/.keep +0 -0
- data/examples/action-cable-production-mode/test/mailers/.keep +0 -0
- data/examples/action-cable-production-mode/test/models/.keep +0 -0
- data/examples/action-cable-production-mode/test/models/word_test.rb +7 -0
- data/examples/action-cable-production-mode/test/test_helper.rb +10 -0
- data/examples/action-cable-production-mode/tmp/.keep +0 -0
- data/examples/action-cable-production-mode/vendor/assets/javascripts/.keep +0 -0
- data/examples/action-cable-production-mode/vendor/assets/stylesheets/.keep +0 -0
- data/examples/action-cable/app/views/layouts/application.html.erb +1 -1
- data/examples/pusher-fake/app/views/layouts/application.html.erb +1 -1
- data/examples/pusher/app/views/layouts/application.html.erb +1 -1
- data/examples/simple-poller/app/views/layouts/application.html.erb +1 -1
- data/examples/word-game/.gitignore +21 -0
- data/examples/word-game/Gemfile +57 -0
- data/examples/word-game/Gemfile.lock +241 -0
- data/examples/word-game/README.md +24 -0
- data/examples/word-game/Rakefile +6 -0
- data/examples/word-game/app/assets/config/manifest.js +3 -0
- data/examples/word-game/app/assets/images/.keep +0 -0
- data/examples/word-game/app/assets/javascripts/application.js +20 -0
- data/examples/word-game/app/assets/javascripts/cable.js +13 -0
- data/examples/word-game/app/assets/javascripts/channels/.keep +0 -0
- data/examples/word-game/app/assets/stylesheets/application.css +15 -0
- data/examples/word-game/app/channels/application_cable/channel.rb +4 -0
- data/examples/word-game/app/channels/application_cable/connection.rb +4 -0
- data/examples/word-game/app/controllers/application_controller.rb +3 -0
- data/examples/word-game/app/controllers/concerns/.keep +0 -0
- data/examples/word-game/app/helpers/application_helper.rb +2 -0
- data/examples/word-game/app/jobs/application_job.rb +2 -0
- data/examples/word-game/app/mailers/application_mailer.rb +4 -0
- data/examples/word-game/app/models/application_record.rb +3 -0
- data/examples/word-game/app/models/concerns/.keep +0 -0
- data/examples/word-game/app/models/models.rb +2 -0
- data/examples/word-game/app/models/public/.keep +0 -0
- data/examples/word-game/app/policies/application_policy.rb +14 -0
- data/examples/word-game/app/views/components.rb +16 -0
- data/examples/word-game/app/views/components/.keep +0 -0
- data/examples/word-game/app/views/layouts/application.html.erb +14 -0
- data/examples/{action-cable → word-game}/app/views/layouts/mailer.html.erb +0 -0
- data/examples/{action-cable → word-game}/app/views/layouts/mailer.text.erb +0 -0
- data/examples/word-game/bin/bundle +3 -0
- data/examples/word-game/bin/rails +9 -0
- data/examples/word-game/bin/rake +9 -0
- data/examples/word-game/bin/setup +34 -0
- data/examples/word-game/bin/spring +16 -0
- data/examples/word-game/bin/update +29 -0
- data/examples/word-game/config.ru +5 -0
- data/examples/word-game/config/application.rb +20 -0
- data/examples/word-game/config/boot.rb +3 -0
- data/examples/word-game/config/cable.yml +9 -0
- data/examples/word-game/config/database.yml +25 -0
- data/examples/word-game/config/environment.rb +5 -0
- data/examples/word-game/config/environments/development.rb +56 -0
- data/examples/word-game/config/environments/production.rb +86 -0
- data/examples/word-game/config/environments/test.rb +42 -0
- data/examples/word-game/config/initializers/application_controller_renderer.rb +6 -0
- data/examples/word-game/config/initializers/assets.rb +11 -0
- data/examples/word-game/config/initializers/backtrace_silencers.rb +7 -0
- data/examples/word-game/config/initializers/cookies_serializer.rb +5 -0
- data/examples/word-game/config/initializers/filter_parameter_logging.rb +4 -0
- data/examples/word-game/config/initializers/hyper_mesh.rb +4 -0
- data/examples/word-game/config/initializers/inflections.rb +16 -0
- data/examples/word-game/config/initializers/mime_types.rb +4 -0
- data/examples/word-game/config/initializers/new_framework_defaults.rb +24 -0
- data/examples/word-game/config/initializers/session_store.rb +3 -0
- data/examples/word-game/config/initializers/wrap_parameters.rb +14 -0
- data/examples/word-game/config/locales/en.yml +23 -0
- data/examples/word-game/config/puma.rb +47 -0
- data/examples/word-game/config/routes.rb +4 -0
- data/examples/word-game/config/secrets.yml +22 -0
- data/examples/word-game/config/spring.rb +6 -0
- data/examples/word-game/db/seeds.rb +7 -0
- data/examples/word-game/lib/assets/.keep +0 -0
- data/examples/word-game/lib/tasks/.keep +0 -0
- data/examples/word-game/log/.keep +0 -0
- data/examples/word-game/public/404.html +67 -0
- data/examples/word-game/public/422.html +67 -0
- data/examples/word-game/public/500.html +66 -0
- data/examples/word-game/public/apple-touch-icon-precomposed.png +0 -0
- data/examples/word-game/public/apple-touch-icon.png +0 -0
- data/examples/word-game/public/favicon.ico +0 -0
- data/examples/word-game/public/robots.txt +5 -0
- data/examples/word-game/test/controllers/.keep +0 -0
- data/examples/word-game/test/fixtures/.keep +0 -0
- data/examples/word-game/test/fixtures/files/.keep +0 -0
- data/examples/word-game/test/helpers/.keep +0 -0
- data/examples/word-game/test/integration/.keep +0 -0
- data/examples/word-game/test/mailers/.keep +0 -0
- data/examples/word-game/test/models/.keep +0 -0
- data/examples/word-game/test/test_helper.rb +10 -0
- data/examples/word-game/tmp/.keep +0 -0
- data/examples/word-game/vendor/assets/javascripts/.keep +0 -0
- data/examples/word-game/vendor/assets/stylesheets/.keep +0 -0
- data/lib/active_record_base.rb +20 -1
- data/lib/hyper-mesh.rb +1 -0
- data/lib/hypermesh/version.rb +1 -1
- data/lib/kernel/itself.rb +5 -0
- data/lib/reactive_record/active_record/class_methods.rb +87 -11
- data/lib/reactive_record/active_record/instance_methods.rb +4 -2
- data/lib/reactive_record/active_record/reactive_record/collection.rb +9 -12
- data/lib/reactive_record/active_record/reactive_record/isomorphic_base.rb +52 -52
- data/lib/reactive_record/active_record/reactive_record/while_loading.rb +2 -3
- data/lib/reactive_record/scope_description.rb +8 -3
- data/lib/synchromesh/client_drivers.rb +1 -9
- data/lib/synchromesh/connection.rb +2 -2
- data/lib/synchromesh/synchromesh.rb +4 -4
- data/spec/reactive_record/auto_load_itself_spec.rb +24 -0
- data/spec/reactive_record/finder_method_spec.rb +67 -0
- data/spec/reactive_record/save_while_loading_spec.rb +44 -0
- data/spec/reactive_record/update_associations_spec.rb +0 -1
- data/spec/reactive_record/update_scopes_spec.rb +1 -1
- data/spec/spec_helper.rb +5 -0
- data/spec/synchromesh/examples/random_examples.rb +33 -0
- metadata +176 -10
- data/examples/pusher-fake/app/views/layouts/mailer.html.erb +0 -13
- data/examples/pusher-fake/app/views/layouts/mailer.text.erb +0 -1
- data/examples/pusher/app/views/layouts/mailer.html.erb +0 -13
- data/examples/pusher/app/views/layouts/mailer.text.erb +0 -1
- data/examples/simple-poller/app/views/layouts/mailer.html.erb +0 -13
- 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:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b8982a6629467339b2a91000f327e77e5d1b08f9
|
4
|
+
data.tar.gz: ff8f97442cfc6de5a5902c424e19d89e490a2f99
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ac64d306366dcf4eca7034e60c04ec423766d93f76d2dabef0c123d31e9447654b29046e0055daf86ef5c6b35609c9d02fec01e523f8da5fc90326e781edaabe
|
7
|
+
data.tar.gz: 61ae00d5f4ecf921d4acc9037808841c5539fb4ed59dbbfac712bef62e1c2512d049a89217de410719d9fb0cd89de2c04b3162fa3a19d1423f472ca3b59810af
|
data/CHANGELOG.md
ADDED
@@ -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
|
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/
|
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/
|
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
|
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.
|
data/docs/activerecord_api.md
CHANGED
@@ -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
|
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
|
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
|
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,
|
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,
|
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
|
-
####
|
29
|
+
#### Lazy Loading
|
31
30
|
|
32
|
-
|
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
|
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
|
-
|
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
|
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.
|
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
|
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.
|
147
|
-
my_todo.
|
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
|
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
|
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
|
228
|
+
`itself` returns the record, but will override lazy loading and force a load of at least the model's id.
|
209
229
|
|
210
|
-
|
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
|
-
|
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
|
-
|
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
|
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
|
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
|
-
```
|