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.
- checksums.yaml +7 -0
- data/.gitignore +27 -0
- data/.rspec +2 -0
- data/.rubocop.yml +11 -0
- data/.travis.yml +4 -0
- data/CODE_OF_CONDUCT.md +13 -0
- data/Gemfile +5 -0
- data/LICENSE.txt +21 -0
- data/README.md +396 -0
- data/Rakefile +6 -0
- data/app/controllers/reactive_record/application_controller.rb +4 -0
- data/app/controllers/reactive_record/reactive_record_controller.rb +49 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/codeship.database.yml +18 -0
- data/config/routes.rb +7 -0
- data/docs/action_cable_quickstart.md +151 -0
- data/docs/authorization-policies.md +449 -0
- data/docs/client_side_scoping.md +103 -0
- data/docs/pusher_quickstart.md +0 -0
- data/docs/simple_poller_quickstart.md +121 -0
- data/docs/todo-example.md +116 -0
- data/docs/words-example.md +65 -0
- data/examples/action-cable/.gitignore +21 -0
- data/examples/action-cable/Gemfile +58 -0
- data/examples/action-cable/Gemfile.lock +247 -0
- data/examples/action-cable/README.md +24 -0
- data/examples/action-cable/Rakefile +6 -0
- data/examples/action-cable/app/assets/config/manifest.js +3 -0
- data/examples/action-cable/app/assets/javascripts/application.js +18 -0
- data/examples/action-cable/app/assets/stylesheets/application.css +15 -0
- data/examples/action-cable/app/controllers/application_controller.rb +3 -0
- data/examples/action-cable/app/controllers/test_controller.rb +6 -0
- data/examples/action-cable/app/models/models.rb +2 -0
- data/examples/action-cable/app/models/public/application_record.rb +3 -0
- data/examples/action-cable/app/models/public/word.rb +2 -0
- data/examples/action-cable/app/policies/application_policy.rb +6 -0
- data/examples/action-cable/app/views/components.rb +17 -0
- data/examples/action-cable/app/views/components/app.rb +18 -0
- data/examples/action-cable/app/views/layouts/application.html.erb +14 -0
- data/examples/action-cable/app/views/layouts/mailer.html.erb +13 -0
- data/examples/action-cable/app/views/layouts/mailer.text.erb +1 -0
- data/examples/action-cable/bin/bundle +3 -0
- data/examples/action-cable/bin/rails +9 -0
- data/examples/action-cable/bin/rake +9 -0
- data/examples/action-cable/bin/setup +34 -0
- data/examples/action-cable/bin/spring +15 -0
- data/examples/action-cable/bin/update +29 -0
- data/examples/action-cable/config.ru +5 -0
- data/examples/action-cable/config/application.rb +30 -0
- data/examples/action-cable/config/boot.rb +3 -0
- data/examples/action-cable/config/cable.yml +9 -0
- data/examples/action-cable/config/database.yml +25 -0
- data/examples/action-cable/config/environment.rb +5 -0
- data/examples/action-cable/config/environments/development.rb +45 -0
- data/examples/action-cable/config/environments/production.rb +86 -0
- data/examples/action-cable/config/environments/test.rb +42 -0
- data/examples/action-cable/config/initializers/application_controller_renderer.rb +6 -0
- data/examples/action-cable/config/initializers/assets.rb +11 -0
- data/examples/action-cable/config/initializers/backtrace_silencers.rb +7 -0
- data/examples/action-cable/config/initializers/cookies_serializer.rb +5 -0
- data/examples/action-cable/config/initializers/filter_parameter_logging.rb +4 -0
- data/examples/action-cable/config/initializers/inflections.rb +16 -0
- data/examples/action-cable/config/initializers/mime_types.rb +4 -0
- data/examples/action-cable/config/initializers/new_framework_defaults.rb +24 -0
- data/examples/action-cable/config/initializers/session_store.rb +3 -0
- data/examples/action-cable/config/initializers/synchromesh.rb +5 -0
- data/examples/action-cable/config/initializers/wrap_parameters.rb +14 -0
- data/examples/action-cable/config/locales/en.yml +23 -0
- data/examples/action-cable/config/puma.rb +47 -0
- data/examples/action-cable/config/routes.rb +5 -0
- data/examples/action-cable/config/secrets.yml +22 -0
- data/examples/action-cable/config/spring.rb +6 -0
- data/examples/action-cable/db/migrate/20160921223808_create_words.rb +9 -0
- data/examples/action-cable/db/schema.rb +21 -0
- data/examples/action-cable/log/.keep +0 -0
- data/examples/action-cable/public/404.html +67 -0
- data/examples/action-cable/public/422.html +67 -0
- data/examples/action-cable/public/500.html +66 -0
- data/examples/action-cable/public/apple-touch-icon-precomposed.png +0 -0
- data/examples/action-cable/public/apple-touch-icon.png +0 -0
- data/examples/action-cable/public/favicon.ico +0 -0
- data/examples/action-cable/public/robots.txt +5 -0
- data/examples/action-cable/rails_cache_dir2/C91/480/synchromesh_active_connections +0 -0
- data/examples/action-cable/tmp/.keep +0 -0
- data/examples/action-cable/vendor/assets/javascripts/.keep +0 -0
- data/examples/action-cable/vendor/assets/stylesheets/.keep +0 -0
- data/examples/simple-poller/.gitignore +21 -0
- data/examples/simple-poller/Gemfile +64 -0
- data/examples/simple-poller/Gemfile.lock +272 -0
- data/examples/simple-poller/README.md +24 -0
- data/examples/simple-poller/Rakefile +6 -0
- data/examples/simple-poller/app/assets/config/manifest.js +3 -0
- data/examples/simple-poller/app/assets/images/.keep +0 -0
- data/examples/simple-poller/app/assets/javascripts/application.js +20 -0
- data/examples/simple-poller/app/assets/javascripts/channels/.keep +0 -0
- data/examples/simple-poller/app/assets/stylesheets/application.css +15 -0
- data/examples/simple-poller/app/channels/application_cable/channel.rb +4 -0
- data/examples/simple-poller/app/channels/application_cable/connection.rb +4 -0
- data/examples/simple-poller/app/controllers/application_controller.rb +3 -0
- data/examples/simple-poller/app/controllers/concerns/.keep +0 -0
- data/examples/simple-poller/app/controllers/test_controller.rb +6 -0
- data/examples/simple-poller/app/helpers/application_helper.rb +2 -0
- data/examples/simple-poller/app/jobs/application_job.rb +2 -0
- data/examples/simple-poller/app/mailers/application_mailer.rb +4 -0
- data/examples/simple-poller/app/models/concerns/.keep +0 -0
- data/examples/simple-poller/app/models/models.rb +2 -0
- data/examples/simple-poller/app/models/public/.keep +0 -0
- data/examples/simple-poller/app/models/public/application_record.rb +3 -0
- data/examples/simple-poller/app/models/public/word.rb +2 -0
- data/examples/simple-poller/app/policies/application_policy.rb +5 -0
- data/examples/simple-poller/app/views/components.rb +18 -0
- data/examples/simple-poller/app/views/components/.keep +0 -0
- data/examples/simple-poller/app/views/components/app.rb +40 -0
- data/examples/simple-poller/app/views/layouts/application.html.erb +14 -0
- data/examples/simple-poller/app/views/layouts/mailer.html.erb +13 -0
- data/examples/simple-poller/app/views/layouts/mailer.text.erb +1 -0
- data/examples/simple-poller/bin/bundle +3 -0
- data/examples/simple-poller/bin/rails +9 -0
- data/examples/simple-poller/bin/rake +9 -0
- data/examples/simple-poller/bin/setup +34 -0
- data/examples/simple-poller/bin/spring +16 -0
- data/examples/simple-poller/bin/update +29 -0
- data/examples/simple-poller/config.ru +5 -0
- data/examples/simple-poller/config/application.rb +20 -0
- data/examples/simple-poller/config/boot.rb +3 -0
- data/examples/simple-poller/config/cable.yml +9 -0
- data/examples/simple-poller/config/database.yml +25 -0
- data/examples/simple-poller/config/environment.rb +5 -0
- data/examples/simple-poller/config/environments/development.rb +44 -0
- data/examples/simple-poller/config/environments/production.rb +86 -0
- data/examples/simple-poller/config/environments/test.rb +42 -0
- data/examples/simple-poller/config/initializers/application_controller_renderer.rb +6 -0
- data/examples/simple-poller/config/initializers/assets.rb +11 -0
- data/examples/simple-poller/config/initializers/backtrace_silencers.rb +7 -0
- data/examples/simple-poller/config/initializers/cookies_serializer.rb +5 -0
- data/examples/simple-poller/config/initializers/filter_parameter_logging.rb +4 -0
- data/examples/simple-poller/config/initializers/inflections.rb +16 -0
- data/examples/simple-poller/config/initializers/mime_types.rb +4 -0
- data/examples/simple-poller/config/initializers/new_framework_defaults.rb +24 -0
- data/examples/simple-poller/config/initializers/session_store.rb +3 -0
- data/examples/simple-poller/config/initializers/synchromesh.rb +15 -0
- data/examples/simple-poller/config/initializers/wrap_parameters.rb +14 -0
- data/examples/simple-poller/config/locales/en.yml +23 -0
- data/examples/simple-poller/config/puma.rb +47 -0
- data/examples/simple-poller/config/routes.rb +5 -0
- data/examples/simple-poller/config/secrets.yml +22 -0
- data/examples/simple-poller/config/spring.rb +6 -0
- data/examples/simple-poller/db/migrate/20161013220135_create_words.rb +9 -0
- data/examples/simple-poller/db/schema.rb +21 -0
- data/examples/simple-poller/db/seeds.rb +7 -0
- data/examples/simple-poller/lib/assets/.keep +0 -0
- data/examples/simple-poller/lib/tasks/.keep +0 -0
- data/examples/simple-poller/log/.keep +0 -0
- data/examples/simple-poller/public/404.html +67 -0
- data/examples/simple-poller/public/422.html +67 -0
- data/examples/simple-poller/public/500.html +66 -0
- data/examples/simple-poller/public/apple-touch-icon-precomposed.png +0 -0
- data/examples/simple-poller/public/apple-touch-icon.png +0 -0
- data/examples/simple-poller/public/favicon.ico +0 -0
- data/examples/simple-poller/public/robots.txt +5 -0
- data/examples/simple-poller/test/controllers/.keep +0 -0
- data/examples/simple-poller/test/fixtures/.keep +0 -0
- data/examples/simple-poller/test/fixtures/files/.keep +0 -0
- data/examples/simple-poller/test/fixtures/words.yml +7 -0
- data/examples/simple-poller/test/helpers/.keep +0 -0
- data/examples/simple-poller/test/integration/.keep +0 -0
- data/examples/simple-poller/test/mailers/.keep +0 -0
- data/examples/simple-poller/test/models/.keep +0 -0
- data/examples/simple-poller/test/models/word_test.rb +7 -0
- data/examples/simple-poller/test/test_helper.rb +10 -0
- data/examples/simple-poller/tmp/.keep +0 -0
- data/examples/simple-poller/vendor/assets/javascripts/.keep +0 -0
- data/examples/simple-poller/vendor/assets/stylesheets/.keep +0 -0
- data/examples/words/.gitignore +21 -0
- data/examples/words/Gemfile +58 -0
- data/examples/words/Gemfile.lock +247 -0
- data/examples/words/README.md +24 -0
- data/examples/words/Rakefile +6 -0
- data/examples/words/app/assets/config/manifest.js +3 -0
- data/examples/words/app/assets/javascripts/application.js +18 -0
- data/examples/words/app/assets/stylesheets/application.css +15 -0
- data/examples/words/app/controllers/application_controller.rb +3 -0
- data/examples/words/app/controllers/test_controller.rb +6 -0
- data/examples/words/app/models/models.rb +2 -0
- data/examples/words/app/models/public/application_record.rb +3 -0
- data/examples/words/app/models/public/word.rb +10 -0
- data/examples/words/app/policies/application_policy.rb +6 -0
- data/examples/words/app/views/components.rb +16 -0
- data/examples/words/app/views/components/app.rb +31 -0
- data/examples/words/app/views/layouts/application.html.erb +14 -0
- data/examples/words/app/views/layouts/mailer.html.erb +13 -0
- data/examples/words/app/views/layouts/mailer.text.erb +1 -0
- data/examples/words/bin/bundle +3 -0
- data/examples/words/bin/rails +9 -0
- data/examples/words/bin/rake +9 -0
- data/examples/words/bin/setup +34 -0
- data/examples/words/bin/spring +15 -0
- data/examples/words/bin/update +29 -0
- data/examples/words/config.ru +5 -0
- data/examples/words/config/application.rb +30 -0
- data/examples/words/config/boot.rb +3 -0
- data/examples/words/config/cable.yml +9 -0
- data/examples/words/config/database.yml +25 -0
- data/examples/words/config/environment.rb +5 -0
- data/examples/words/config/environments/development.rb +45 -0
- data/examples/words/config/environments/production.rb +86 -0
- data/examples/words/config/environments/test.rb +42 -0
- data/examples/words/config/initializers/application_controller_renderer.rb +6 -0
- data/examples/words/config/initializers/assets.rb +11 -0
- data/examples/words/config/initializers/backtrace_silencers.rb +7 -0
- data/examples/words/config/initializers/cookies_serializer.rb +5 -0
- data/examples/words/config/initializers/filter_parameter_logging.rb +4 -0
- data/examples/words/config/initializers/inflections.rb +16 -0
- data/examples/words/config/initializers/mime_types.rb +4 -0
- data/examples/words/config/initializers/new_framework_defaults.rb +24 -0
- data/examples/words/config/initializers/session_store.rb +3 -0
- data/examples/words/config/initializers/synchromesh.rb +5 -0
- data/examples/words/config/initializers/wrap_parameters.rb +14 -0
- data/examples/words/config/locales/en.yml +23 -0
- data/examples/words/config/puma.rb +47 -0
- data/examples/words/config/routes.rb +5 -0
- data/examples/words/config/secrets.yml +22 -0
- data/examples/words/config/spring.rb +6 -0
- data/examples/words/db/migrate/20160921223808_create_words.rb +9 -0
- data/examples/words/db/schema.rb +28 -0
- data/examples/words/log/.keep +0 -0
- data/examples/words/public/404.html +67 -0
- data/examples/words/public/422.html +67 -0
- data/examples/words/public/500.html +66 -0
- data/examples/words/public/apple-touch-icon-precomposed.png +0 -0
- data/examples/words/public/apple-touch-icon.png +0 -0
- data/examples/words/public/favicon.ico +0 -0
- data/examples/words/public/robots.txt +5 -0
- data/examples/words/tmp/.keep +0 -0
- data/examples/words/vendor/assets/javascripts/.keep +0 -0
- data/examples/words/vendor/assets/stylesheets/.keep +0 -0
- data/hyper-mesh.gemspec +82 -0
- data/lib/active_record_base.rb +152 -0
- data/lib/enumerable/pluck.rb +6 -0
- data/lib/hyper-mesh.rb +62 -0
- data/lib/hypermesh/version.rb +3 -0
- data/lib/opal/equality_patches.rb +15 -0
- data/lib/opal/parse_patch.rb +14 -0
- data/lib/opal/set_patches.rb +8 -0
- data/lib/reactive_record/active_record/aggregations.rb +69 -0
- data/lib/reactive_record/active_record/associations.rb +111 -0
- data/lib/reactive_record/active_record/base.rb +9 -0
- data/lib/reactive_record/active_record/class_methods.rb +177 -0
- data/lib/reactive_record/active_record/error.rb +26 -0
- data/lib/reactive_record/active_record/instance_methods.rb +129 -0
- data/lib/reactive_record/active_record/public_columns_hash.rb +19 -0
- data/lib/reactive_record/active_record/reactive_record/base.rb +520 -0
- data/lib/reactive_record/active_record/reactive_record/collection.rb +517 -0
- data/lib/reactive_record/active_record/reactive_record/column_types.rb +67 -0
- data/lib/reactive_record/active_record/reactive_record/dummy_value.rb +218 -0
- data/lib/reactive_record/active_record/reactive_record/isomorphic_base.rb +532 -0
- data/lib/reactive_record/active_record/reactive_record/reactive_set_relationship_helpers.rb +189 -0
- data/lib/reactive_record/active_record/reactive_record/scoped_collection.rb +62 -0
- data/lib/reactive_record/active_record/reactive_record/unscoped_collection.rb +16 -0
- data/lib/reactive_record/active_record/reactive_record/while_loading.rb +299 -0
- data/lib/reactive_record/engine.rb +13 -0
- data/lib/reactive_record/interval.rb +190 -0
- data/lib/reactive_record/permissions.rb +102 -0
- data/lib/reactive_record/pry.rb +13 -0
- data/lib/reactive_record/reactive_scope.rb +18 -0
- data/lib/reactive_record/scope_description.rb +108 -0
- data/lib/reactive_record/serializers.rb +7 -0
- data/lib/reactive_record/server_data_cache.rb +347 -0
- data/lib/reactive_record/version.rb +3 -0
- data/lib/sources/hyper-mesh/pusher.js +98 -0
- data/lib/synchromesh/action_cable.rb +39 -0
- data/lib/synchromesh/client_drivers.rb +357 -0
- data/lib/synchromesh/configuration.rb +40 -0
- data/lib/synchromesh/connection.rb +144 -0
- data/lib/synchromesh/policy.rb +504 -0
- data/lib/synchromesh/reactive_record/permission_patches.rb +43 -0
- data/lib/synchromesh/synchromesh.rb +155 -0
- data/lib/synchromesh/synchromesh_controller.rb +154 -0
- data/logo.jpg +0 -0
- data/path_release_steps.md +0 -0
- data/reactive_record_test_app/Gemfile +15 -0
- data/reactive_record_test_app/Gemfile.lock +209 -0
- data/reactive_record_test_app/README.rdoc +261 -0
- data/reactive_record_test_app/Rakefile +7 -0
- data/reactive_record_test_app/app/assets/javascripts/application.rb +5 -0
- data/reactive_record_test_app/app/assets/javascripts/components/another_component.rb +24 -0
- data/reactive_record_test_app/app/assets/javascripts/components/empty_component.rb +6 -0
- data/reactive_record_test_app/app/assets/javascripts/components/todo_item_component.js.rb +16 -0
- data/reactive_record_test_app/app/assets/javascripts/components/todos_component.js.rb +42 -0
- data/reactive_record_test_app/app/assets/javascripts/components/todos_main_component.rb +49 -0
- data/reactive_record_test_app/app/assets/javascripts/react_js_test_only.js +21618 -0
- data/reactive_record_test_app/app/assets/javascripts/reactive_record_config.js +2 -0
- data/reactive_record_test_app/app/assets/javascripts/spec/reactive_record_xspec.js.rb +42 -0
- data/reactive_record_test_app/app/assets/stylesheets/application.css +13 -0
- data/reactive_record_test_app/app/controllers/application_controller.rb +8 -0
- data/reactive_record_test_app/app/controllers/home_controller.rb +7 -0
- data/reactive_record_test_app/app/controllers/test_controller.rb +7 -0
- data/reactive_record_test_app/app/helpers/application_helper.rb +2 -0
- data/reactive_record_test_app/app/mailers/.gitkeep +0 -0
- data/reactive_record_test_app/app/models/.gitkeep +0 -0
- data/reactive_record_test_app/app/policies/application_policy.rb +5 -0
- data/reactive_record_test_app/app/views/components.rb +4 -0
- data/reactive_record_test_app/app/views/components/test.rb +18 -0
- data/reactive_record_test_app/app/views/home/index.html.erb +1 -0
- data/reactive_record_test_app/app/views/layouts/application.html.erb +17 -0
- data/reactive_record_test_app/app/views/models.rb +1 -0
- data/reactive_record_test_app/app/views/models/address.rb +13 -0
- data/reactive_record_test_app/app/views/models/comment.rb +19 -0
- data/reactive_record_test_app/app/views/models/todo_item.rb +36 -0
- data/reactive_record_test_app/app/views/models/user.rb +78 -0
- data/reactive_record_test_app/config.ru +33 -0
- data/reactive_record_test_app/config/application.rb +73 -0
- data/reactive_record_test_app/config/boot.rb +10 -0
- data/reactive_record_test_app/config/database.yml +25 -0
- data/reactive_record_test_app/config/environment.rb +5 -0
- data/reactive_record_test_app/config/environments/development.rb +43 -0
- data/reactive_record_test_app/config/environments/production.rb +70 -0
- data/reactive_record_test_app/config/environments/test.rb +41 -0
- data/reactive_record_test_app/config/initializers/backtrace_silencers.rb +7 -0
- data/reactive_record_test_app/config/initializers/inflections.rb +15 -0
- data/reactive_record_test_app/config/initializers/mime_types.rb +5 -0
- data/reactive_record_test_app/config/initializers/secret_token.rb +7 -0
- data/reactive_record_test_app/config/initializers/session_store.rb +8 -0
- data/reactive_record_test_app/config/initializers/wrap_parameters.rb +14 -0
- data/reactive_record_test_app/config/locales/en.yml +5 -0
- data/reactive_record_test_app/config/routes.rb +7 -0
- data/reactive_record_test_app/db/migrate/20150617002932_create_todo_items.rb +11 -0
- data/reactive_record_test_app/db/migrate/20150617134028_create_users.rb +14 -0
- data/reactive_record_test_app/db/migrate/20150729195556_add_address_to_user.rb +20 -0
- data/reactive_record_test_app/db/migrate/20150826142045_create_comments.rb +10 -0
- data/reactive_record_test_app/db/migrate/20150828172008_add_single_comment_to_todo_item.rb +5 -0
- data/reactive_record_test_app/db/migrate/20150908184118_add_address_id_to_user.rb +5 -0
- data/reactive_record_test_app/db/migrate/20150917220236_add_second_address_to_user.rb +10 -0
- data/reactive_record_test_app/db/migrate/20151009000111_add_test_data_attributes_to_user.rb +6 -0
- data/reactive_record_test_app/db/migrate/20160129182544_add_test_enum_to_user.rb +5 -0
- data/reactive_record_test_app/db/schema.rb +63 -0
- data/reactive_record_test_app/db/seeds.rb +60 -0
- data/reactive_record_test_app/public/404.html +26 -0
- data/reactive_record_test_app/public/422.html +26 -0
- data/reactive_record_test_app/public/500.html +25 -0
- data/reactive_record_test_app/public/favicon.ico +0 -0
- data/reactive_record_test_app/script/rails +6 -0
- data/reactive_record_test_app/spec-opal/active-record/aggregations_spec.rb +41 -0
- data/reactive_record_test_app/spec-opal/active-record/associations_spec.rb +75 -0
- data/reactive_record_test_app/spec-opal/active-record/base_spec.rb +126 -0
- data/reactive_record_test_app/spec-opal/active-record/dummy_value_spec.rb +27 -0
- data/reactive_record_test_app/spec-opal/active-record/edge_cases_spec.rb +116 -0
- data/reactive_record_test_app/spec-opal/active-record/enum_spec.rb +43 -0
- data/reactive_record_test_app/spec-opal/active-record/instance_methods_spec.rb +53 -0
- data/reactive_record_test_app/spec-opal/active-record/non_ar_aggregations_spec.rb +74 -0
- data/reactive_record_test_app/spec-opal/active-record/permissions_spec.rb +170 -0
- data/reactive_record_test_app/spec-opal/active-record/prerendering_spec.rb +49 -0
- data/reactive_record_test_app/spec-opal/active-record/reactive_record_load_spec.rb +23 -0
- data/reactive_record_test_app/spec-opal/active-record/rendering_spec.rb +221 -0
- data/reactive_record_test_app/spec-opal/active-record/save_spec.rb +125 -0
- data/reactive_record_test_app/spec-opal/active-record/scope_spec.rb +85 -0
- data/reactive_record_test_app/spec-opal/active-record/update_aggregations_spec.rb +76 -0
- data/reactive_record_test_app/spec-opal/active-record/update_attributes_spec.rb +186 -0
- data/reactive_record_test_app/spec-opal/active-record/virtual_methods_spec.rb +71 -0
- data/reactive_record_test_app/spec-opal/index.html.erb +11 -0
- data/reactive_record_test_app/spec-opal/spec_helper.js.rb +268 -0
- data/reactive_record_test_app/spec-opal/vendor/es5-shim.min.js +6 -0
- data/reactive_record_test_app/spec_dont_run/README.md +26 -0
- data/reactive_record_test_app/spec_dont_run/moved_to_main_spec_dir/zzzmany_to_many_spec_moved_to_main_suite.rb +30 -0
- data/reactive_record_test_app/spec_dont_run/moved_to_main_spec_dir/zzzrevert_record_spec_moved.rb +78 -0
- data/reactive_record_test_app/spec_dont_run/moved_to_main_spec_dir/zzzupdate_associations_spec_moved.rb +142 -0
- data/reactive_record_test_app/spec_dont_run/moved_to_main_spec_dir/zzzupdate_scopes_movedspec.rb +48 -0
- data/remote.md +115 -0
- data/spec/bin/firebug-2.0.13-fx.xpi +0 -0
- data/spec/component_helpers_xspec.rb +49 -0
- data/spec/factories/child_model.rb +5 -0
- data/spec/factories/comment.rb +5 -0
- data/spec/factories/test_models.rb +5 -0
- data/spec/factories/todo.rb +5 -0
- data/spec/factories/user.rb +5 -0
- data/spec/reactive_record/edge_cases_spec.rb +31 -0
- data/spec/reactive_record/factory.rb +62 -0
- data/spec/reactive_record/many_to_many_spec.rb +50 -0
- data/spec/reactive_record/play.rb +64 -0
- data/spec/reactive_record/pry_rescue_xspec.rb +48 -0
- data/spec/reactive_record/revert_spec.rb +112 -0
- data/spec/reactive_record/update_associations_spec.rb +189 -0
- data/spec/reactive_record/update_scopes_spec.rb +53 -0
- data/spec/spec_helper.rb +366 -0
- data/spec/support/component_helpers.rb +351 -0
- data/spec/synchromesh/column_types/column_type_spec.rb +302 -0
- data/spec/synchromesh/connection_spec.rb +144 -0
- data/spec/synchromesh/crud_access_regulation/broadcast_controls_access_spec.rb +105 -0
- data/spec/synchromesh/crud_access_regulation/model_policies_spec.rb +131 -0
- data/spec/synchromesh/examples/dictionary.rb +239 -0
- data/spec/synchromesh/examples/dictionary_with_client_scopes.rb +196 -0
- data/spec/synchromesh/examples/random_examples.rb +100 -0
- data/spec/synchromesh/examples/scoped_todos_spec.rb +167 -0
- data/spec/synchromesh/integration/authorization_spec.rb +111 -0
- data/spec/synchromesh/integration/default_scope_spec.rb +121 -0
- data/spec/synchromesh/integration/has_many_through_spec.rb +173 -0
- data/spec/synchromesh/integration/relationships_spec.rb +263 -0
- data/spec/synchromesh/integration/scope_spec.rb +553 -0
- data/spec/synchromesh/integration/synchromesh_spec.rb +80 -0
- data/spec/synchromesh/integration/test_components.rb +18 -0
- data/spec/synchromesh/integration/transports_spec.rb +308 -0
- data/spec/synchromesh/policies/auto_connect_spec.rb +60 -0
- data/spec/synchromesh/policies/auto_loader_spec.rb +34 -0
- data/spec/synchromesh/policies/policy_methods_spec.rb +85 -0
- data/spec/synchromesh/policies/regulate_all_broadcasts_spec.rb +315 -0
- data/spec/synchromesh/policies/regulate_broadcast_spec.rb +370 -0
- data/spec/synchromesh/policies/regulate_class_connection_spec.rb +50 -0
- data/spec/synchromesh/policies/regulate_instance_connection_spec.rb +66 -0
- data/spec/test_app/Gemfile +61 -0
- data/spec/test_app/Gemfile.lock +253 -0
- data/spec/test_app/Rakefile +6 -0
- data/spec/test_app/app/assets/javascripts/application.js +6 -0
- data/spec/test_app/app/assets/stylesheets/application.css +15 -0
- data/spec/test_app/app/controllers/application_controller.rb +13 -0
- data/spec/test_app/app/controllers/test_controller.rb +5 -0
- data/spec/test_app/app/models/_react_public_models.rb +2 -0
- data/spec/test_app/app/models/public/address.rb +13 -0
- data/spec/test_app/app/models/public/child_model.rb +3 -0
- data/spec/test_app/app/models/public/comment.rb +25 -0
- data/spec/test_app/app/models/public/test_model.rb +5 -0
- data/spec/test_app/app/models/public/todo.rb +6 -0
- data/spec/test_app/app/models/public/todo_item.rb +36 -0
- data/spec/test_app/app/models/public/user.rb +88 -0
- data/spec/test_app/app/policies/auto_loader_test_classa_policy.rb +3 -0
- data/spec/test_app/app/policies/auto_loader_test_classb_policy.rb +3 -0
- data/spec/test_app/app/policies/auto_loader_test_classc_policy.rb +3 -0
- data/spec/test_app/app/policies/auto_loader_test_classd_policy.rb +3 -0
- data/spec/test_app/app/views/components.rb +14 -0
- data/spec/test_app/app/views/components/show.rb +5 -0
- data/spec/test_app/app/views/layouts/application.html.erb +14 -0
- data/spec/test_app/bin/bundle +3 -0
- data/spec/test_app/bin/rails +4 -0
- data/spec/test_app/bin/rake +4 -0
- data/spec/test_app/bin/setup +29 -0
- data/spec/test_app/config.ru +4 -0
- data/spec/test_app/config/application.rb +42 -0
- data/spec/test_app/config/boot.rb +6 -0
- data/spec/test_app/config/cable.yml +10 -0
- data/spec/test_app/config/database.yml +47 -0
- data/spec/test_app/config/environment.rb +5 -0
- data/spec/test_app/config/environments/development.rb +41 -0
- data/spec/test_app/config/environments/production.rb +79 -0
- data/spec/test_app/config/environments/test.rb +42 -0
- data/spec/test_app/config/initializers/assets.rb +11 -0
- data/spec/test_app/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/test_app/config/initializers/cookies_serializer.rb +3 -0
- data/spec/test_app/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/test_app/config/initializers/inflections.rb +16 -0
- data/spec/test_app/config/initializers/mime_types.rb +4 -0
- data/spec/test_app/config/initializers/session_store.rb +3 -0
- data/spec/test_app/config/initializers/synchromesh.rb +11 -0
- data/spec/test_app/config/initializers/wrap_parameters.rb +14 -0
- data/spec/test_app/config/locales/en.yml +23 -0
- data/spec/test_app/config/routes.rb +61 -0
- data/spec/test_app/config/secrets.yml +22 -0
- data/spec/test_app/db/development.sqlite3 +0 -0
- data/spec/test_app/db/migrate/20160731182106_create_test_models.rb +75 -0
- data/spec/test_app/db/schema.rb +88 -0
- data/spec/test_app/db/seeds.rb +7 -0
- data/spec/test_app/lib/assets/.keep +0 -0
- data/spec/test_app/log/.keep +0 -0
- data/spec/test_app/public/404.html +67 -0
- data/spec/test_app/public/422.html +67 -0
- data/spec/test_app/public/500.html +66 -0
- data/spec/test_app/public/favicon.ico +0 -0
- data/spec/vendor/es5-shim.min.js +6 -0
- data/terminal.md +66 -0
- metadata +1175 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
module ReactiveRecord
|
|
2
|
+
# ActiveRecord column access and conversion helpers
|
|
3
|
+
class Base
|
|
4
|
+
def columns_hash
|
|
5
|
+
model.columns_hash
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def column_type(attr)
|
|
9
|
+
column_hash = columns_hash[attr]
|
|
10
|
+
return nil unless column_hash
|
|
11
|
+
column_hash[:sql_type_metadata] && column_hash[:sql_type_metadata][:type]
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def convert_datetime(val)
|
|
15
|
+
if val.is_a?(Numeric)
|
|
16
|
+
Time.at(val)
|
|
17
|
+
elsif val.is_a?(Time)
|
|
18
|
+
val
|
|
19
|
+
else
|
|
20
|
+
Time.parse(val)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
alias convert_time convert_datetime
|
|
25
|
+
alias convert_timestamp convert_datetime
|
|
26
|
+
|
|
27
|
+
def convert_date(val)
|
|
28
|
+
if val.is_a?(Time)
|
|
29
|
+
Date.parse(val.strftime('%d/%m/%Y'))
|
|
30
|
+
elsif val.is_a?(Date)
|
|
31
|
+
val
|
|
32
|
+
else
|
|
33
|
+
Date.parse(val)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def convert_boolean(val)
|
|
38
|
+
!['false', false, nil, 0].include?(val)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def convert_integer(val)
|
|
42
|
+
Integer(`parseInt(#{val})`)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
alias convert_bigint convert_integer
|
|
46
|
+
|
|
47
|
+
def convert_float(val)
|
|
48
|
+
Float(val)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
alias convert_decimal convert_float
|
|
52
|
+
|
|
53
|
+
def convert_text(val)
|
|
54
|
+
val.to_s
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
alias convert_string convert_text
|
|
58
|
+
|
|
59
|
+
def convert(attr, val)
|
|
60
|
+
column_type = column_type(attr)
|
|
61
|
+
return val if !column_type || val.loading? || (!val && column_type != :boolean)
|
|
62
|
+
conversion_method = "convert_#{column_type}"
|
|
63
|
+
return send(conversion_method, val) if respond_to? conversion_method
|
|
64
|
+
val
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
# add mehods to Object to determine if this is a dummy object or not
|
|
2
|
+
class Object
|
|
3
|
+
def loaded?
|
|
4
|
+
!loading?
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def loading?
|
|
8
|
+
false
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def present?
|
|
12
|
+
!!self
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
module ReactiveRecord
|
|
17
|
+
class Base
|
|
18
|
+
# A DummyValue stands in for actual value while waiting for load from
|
|
19
|
+
# server. when value is accessed by most methods it notifies hyper-react
|
|
20
|
+
# so when the actual value loads react will update.
|
|
21
|
+
|
|
22
|
+
# DummyValue uses the ActiveRecord type info to act like an appropriate
|
|
23
|
+
# loaded value.
|
|
24
|
+
class DummyValue < BasicObject
|
|
25
|
+
def initialize(column_hash = nil)
|
|
26
|
+
column_hash ||= {}
|
|
27
|
+
notify
|
|
28
|
+
@column_hash = column_hash
|
|
29
|
+
column_type = (
|
|
30
|
+
@column_hash[:sql_type_metadata] &&
|
|
31
|
+
@column_hash[:sql_type_metadata][:type]
|
|
32
|
+
) || 'nil'
|
|
33
|
+
default_value_method = "build_default_value_for_#{column_type}"
|
|
34
|
+
@object = __send__ default_value_method
|
|
35
|
+
rescue Exception
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def build_default_value_for_nil
|
|
39
|
+
nil
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def build_default_value_for_datetime
|
|
43
|
+
if @column_hash[:default]
|
|
44
|
+
Time.parse(@column_hash[:default].gsub(' ','T')+'+00:00')
|
|
45
|
+
else
|
|
46
|
+
::ReactiveRecord::Base::DummyValue.dummy_time
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
alias build_default_value_for_time build_default_value_for_datetime
|
|
51
|
+
alias build_default_value_for_timestamp build_default_value_for_datetime
|
|
52
|
+
|
|
53
|
+
def build_default_value_for_date
|
|
54
|
+
if @column_hash[:default]
|
|
55
|
+
Date.parse(@column_hash[:default])
|
|
56
|
+
else
|
|
57
|
+
::ReactiveRecord::Base::DummyValue.dummy_date
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def build_default_value_for_boolean
|
|
62
|
+
@column_hash[:default] || false
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def build_default_value_for_float
|
|
66
|
+
@column_hash[:default] || Float(0.0)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
alias build_default_value_for_decimal build_default_value_for_float
|
|
70
|
+
|
|
71
|
+
def build_default_value_for_integer
|
|
72
|
+
@column_hash[:default] || Integer(0)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
alias build_default_value_for_bigint build_default_value_for_integer
|
|
76
|
+
|
|
77
|
+
def build_default_value_for_string
|
|
78
|
+
@column_hash[:default] || ''
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
alias build_default_value_for_text build_default_value_for_string
|
|
82
|
+
|
|
83
|
+
def notify
|
|
84
|
+
return if ReactiveRecord::Base.data_loading?
|
|
85
|
+
ReactiveRecord.loads_pending!
|
|
86
|
+
ReactiveRecord::WhileLoading.loading!
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def loading?
|
|
90
|
+
true
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def loaded?
|
|
94
|
+
false
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def present?
|
|
98
|
+
false
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def nil?
|
|
102
|
+
true
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def !
|
|
106
|
+
true
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def method_missing(method, *args, &block)
|
|
110
|
+
if method =~ /^build_default_value_for_/
|
|
111
|
+
nil
|
|
112
|
+
elsif @object || @object.respond_to?(method)
|
|
113
|
+
notify
|
|
114
|
+
@object.send method, *args, &block
|
|
115
|
+
elsif 0.respond_to? method
|
|
116
|
+
notify
|
|
117
|
+
0.send(method, *args, &block)
|
|
118
|
+
elsif ''.respond_to? method
|
|
119
|
+
notify
|
|
120
|
+
''.send(method, *args, &block)
|
|
121
|
+
else
|
|
122
|
+
super
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def coerce(s)
|
|
127
|
+
# notify # why are we not notifying here
|
|
128
|
+
return @object.coerce(s) if @object
|
|
129
|
+
[__send__("to_#{s.class.name.downcase}"), s]
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def ==(other)
|
|
133
|
+
# notify # why are we not notifying here
|
|
134
|
+
other.object_id == object_id
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def object_id
|
|
138
|
+
`self.$$id`
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def is_a?(klass)
|
|
142
|
+
klass == ::ReactiveRecord::Base::DummyValue
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def zero?
|
|
146
|
+
return @object.zero? if @object
|
|
147
|
+
false
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def to_s
|
|
151
|
+
notify
|
|
152
|
+
return @object.to_s if @object
|
|
153
|
+
''
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
alias inspect to_s
|
|
157
|
+
|
|
158
|
+
def to_f
|
|
159
|
+
notify
|
|
160
|
+
return @object.to_f if @object
|
|
161
|
+
0.0
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def to_i
|
|
165
|
+
notify
|
|
166
|
+
return @object.to_i if @object
|
|
167
|
+
0
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def to_numeric
|
|
171
|
+
notify
|
|
172
|
+
return @object.to_numeric if @object
|
|
173
|
+
0
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def to_number
|
|
177
|
+
notify
|
|
178
|
+
return @object.to_number if @object
|
|
179
|
+
0
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def self.dummy_time
|
|
183
|
+
@dummy_time ||= Time.parse('2001-01-01T00:00:00.000-00:00')
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def self.dummy_date
|
|
187
|
+
@dummy_date ||= Date.parse('1/1/2001')
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def to_date
|
|
191
|
+
notify
|
|
192
|
+
return @object.to_date if @object
|
|
193
|
+
ReactiveRecord::Base::DummyValue.dummy_date
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def to_time
|
|
197
|
+
notify
|
|
198
|
+
return @object.to_time if @object
|
|
199
|
+
ReactiveRecord::Base::DummyValue.dummy_time
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def acts_as_string?
|
|
203
|
+
return @object.acts_as_string if @object
|
|
204
|
+
true
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
def try(*args, &b)
|
|
208
|
+
if args.empty? && block_given?
|
|
209
|
+
yield self
|
|
210
|
+
else
|
|
211
|
+
__send__(*args, &b)
|
|
212
|
+
end
|
|
213
|
+
rescue
|
|
214
|
+
nil
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
end
|
|
@@ -0,0 +1,532 @@
|
|
|
1
|
+
require 'json'
|
|
2
|
+
|
|
3
|
+
module ReactiveRecord
|
|
4
|
+
|
|
5
|
+
class Base
|
|
6
|
+
|
|
7
|
+
include React::IsomorphicHelpers
|
|
8
|
+
|
|
9
|
+
before_first_mount do |context|
|
|
10
|
+
if RUBY_ENGINE != 'opal'
|
|
11
|
+
@server_data_cache = ReactiveRecord::ServerDataCache.new(context.controller.acting_user, {})
|
|
12
|
+
else
|
|
13
|
+
@outer_scopes = Set.new
|
|
14
|
+
@fetch_scheduled = nil
|
|
15
|
+
@records = Hash.new { |hash, key| hash[key] = [] }
|
|
16
|
+
@class_scopes = Hash.new { |hash, key| hash[key] = {} }
|
|
17
|
+
if on_opal_client?
|
|
18
|
+
@pending_fetches = []
|
|
19
|
+
@pending_records = []
|
|
20
|
+
@last_fetch_at = Time.now
|
|
21
|
+
unless `typeof window.ReactiveRecordInitialData === 'undefined'`
|
|
22
|
+
log(["Reactive record prerendered data being loaded: %o", `window.ReactiveRecordInitialData`])
|
|
23
|
+
JSON.from_object(`window.ReactiveRecordInitialData`).each do |hash|
|
|
24
|
+
load_from_json hash
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def records
|
|
32
|
+
self.class.instance_variable_get(:@records)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Prerendering db access (returns nil if on client):
|
|
36
|
+
# at end of prerendering dumps all accessed records in the footer
|
|
37
|
+
|
|
38
|
+
isomorphic_method(:fetch_from_db) do |f, vector|
|
|
39
|
+
# vector must end with either "*all", or be a simple attribute
|
|
40
|
+
f.send_to_server [vector.shift.name, *vector] if RUBY_ENGINE == 'opal'
|
|
41
|
+
f.when_on_server { @server_data_cache[*vector] }
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
isomorphic_method(:find_in_db) do |f, klass, attribute, value|
|
|
45
|
+
f.send_to_server klass.name, attribute, value if RUBY_ENGINE == 'opal'
|
|
46
|
+
f.when_on_server { @server_data_cache[klass, ["find_by_#{attribute}", value], :id] }
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
prerender_footer do
|
|
50
|
+
if @server_data_cache
|
|
51
|
+
json = @server_data_cache.as_json.to_json # can this just be to_json?
|
|
52
|
+
@server_data_cache.clear_requests
|
|
53
|
+
else
|
|
54
|
+
json = {}.to_json
|
|
55
|
+
end
|
|
56
|
+
path = ::Rails.application.routes.routes.detect do |route|
|
|
57
|
+
# not sure why the second check is needed. It happens in the test app
|
|
58
|
+
route.app == ReactiveRecord::Engine or (route.app.respond_to?(:app) and route.app.app == ReactiveRecord::Engine)
|
|
59
|
+
end.path.spec
|
|
60
|
+
"<script type='text/javascript'>\n"+
|
|
61
|
+
"window.ReactiveRecordEnginePath = '#{path}';\n"+
|
|
62
|
+
"if (typeof window.ReactiveRecordInitialData === 'undefined') { window.ReactiveRecordInitialData = [] }\n" +
|
|
63
|
+
"window.ReactiveRecordInitialData.push(#{json})\n"+
|
|
64
|
+
"</script>\n"
|
|
65
|
+
end if RUBY_ENGINE != 'opal'
|
|
66
|
+
|
|
67
|
+
# Client side db access (never called during prerendering):
|
|
68
|
+
|
|
69
|
+
# Always returns an object of class DummyValue which will act like most standard AR field types
|
|
70
|
+
# Whenever a dummy value is accessed it notify React that there are loads pending so appropriate rerenders
|
|
71
|
+
# will occur when the value is eventually loaded.
|
|
72
|
+
|
|
73
|
+
# queue up fetches, and at the end of each rendering cycle fetch the records
|
|
74
|
+
# notify that loads are pending
|
|
75
|
+
|
|
76
|
+
def self.load_from_db(record, *vector)
|
|
77
|
+
return nil unless on_opal_client? # this can happen when we are on the server and a nil value is returned for an attribute
|
|
78
|
+
# only called from the client side
|
|
79
|
+
# pushes the value of vector onto the a list of vectors that will be loaded from the server when the next
|
|
80
|
+
# rendering cycle completes.
|
|
81
|
+
# takes care of informing react that there are things to load, and schedules the loader to run
|
|
82
|
+
# Note there is no equivilent to find_in_db, because each vector implicitly does a find.
|
|
83
|
+
raise "attempt to do a find_by_id of nil. This will return all records, and is not allowed" if vector[1] == ["find_by_id", nil]
|
|
84
|
+
vector = [record.model.model_name, ["new", record.object_id]]+vector[1..-1] if vector[0].nil?
|
|
85
|
+
unless data_loading?
|
|
86
|
+
@pending_fetches << vector
|
|
87
|
+
@pending_records << record if record
|
|
88
|
+
schedule_fetch
|
|
89
|
+
end
|
|
90
|
+
DummyValue.new(record && record.model.columns_hash[vector.last])
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
class << self
|
|
94
|
+
|
|
95
|
+
attr_reader :pending_fetches
|
|
96
|
+
attr_reader :last_fetch_at
|
|
97
|
+
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def self.schedule_fetch
|
|
101
|
+
@fetch_scheduled ||= after(0) do
|
|
102
|
+
if @pending_fetches.count > 0 # during testing we might reset the context while there are pending fetches otherwise this would never normally happen
|
|
103
|
+
last_fetch_at = @last_fetch_at
|
|
104
|
+
@last_fetch_at = Time.now
|
|
105
|
+
pending_fetches = @pending_fetches.uniq
|
|
106
|
+
models, associations = gather_records(@pending_records, false, nil)
|
|
107
|
+
log(["Server Fetching: %o", pending_fetches.to_n])
|
|
108
|
+
start_time = Time.now
|
|
109
|
+
HTTP.post(`window.ReactiveRecordEnginePath`,
|
|
110
|
+
payload: {
|
|
111
|
+
json: {
|
|
112
|
+
models: models,
|
|
113
|
+
associations: associations,
|
|
114
|
+
pending_fetches: pending_fetches
|
|
115
|
+
}.to_json
|
|
116
|
+
}
|
|
117
|
+
).then do |response|
|
|
118
|
+
fetch_time = Time.now
|
|
119
|
+
log(" Fetched in: #{(fetch_time-start_time).to_i}s")
|
|
120
|
+
begin
|
|
121
|
+
ReactiveRecord::Base.load_from_json(response.json)
|
|
122
|
+
rescue Exception => e
|
|
123
|
+
log("Unexpected exception raised while loading json from server: #{e}", :error)
|
|
124
|
+
end
|
|
125
|
+
log(" Processed in: #{(Time.now-fetch_time).to_i}s")
|
|
126
|
+
log([" Returned: %o", response.json.to_n])
|
|
127
|
+
ReactiveRecord.run_blocks_to_load last_fetch_at
|
|
128
|
+
ReactiveRecord::WhileLoading.loaded_at last_fetch_at
|
|
129
|
+
ReactiveRecord::WhileLoading.quiet! if @pending_fetches.empty?
|
|
130
|
+
end.fail do |response|
|
|
131
|
+
log("Fetch failed", :error)
|
|
132
|
+
ReactiveRecord.run_blocks_to_load(last_fetch_at, response.body)
|
|
133
|
+
end
|
|
134
|
+
@pending_fetches = []
|
|
135
|
+
@pending_records = []
|
|
136
|
+
@fetch_scheduled = nil
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def self.get_type_hash(record)
|
|
142
|
+
{record.class.inheritance_column => record[record.class.inheritance_column]}
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
if RUBY_ENGINE == 'opal'
|
|
146
|
+
|
|
147
|
+
def self.gather_records(records_to_process, force, record_being_saved)
|
|
148
|
+
# we want to pass not just the model data to save, but also enough information so that on return from the server
|
|
149
|
+
# we can update the models on the client
|
|
150
|
+
|
|
151
|
+
# input
|
|
152
|
+
# list of records to process, will grow as we chase associations
|
|
153
|
+
# outputs
|
|
154
|
+
models = [] # the actual data to save {id: record.object_id, model: record.model.model_name, attributes: changed_attributes}
|
|
155
|
+
associations = [] # {parent_id: record.object_id, attribute: attribute, child_id: assoc_record.object_id}
|
|
156
|
+
|
|
157
|
+
# used to keep track of records that have been processed for effeciency
|
|
158
|
+
# for quick lookup of records that have been or will be processed [record.object_id] => record
|
|
159
|
+
records_to_process = records_to_process.uniq
|
|
160
|
+
backing_records = Hash[*records_to_process.collect { |record| [record.object_id, record] }.flatten(1)]
|
|
161
|
+
|
|
162
|
+
add_new_association = lambda do |record, attribute, assoc_record|
|
|
163
|
+
unless backing_records[assoc_record.object_id]
|
|
164
|
+
records_to_process << assoc_record
|
|
165
|
+
backing_records[assoc_record.object_id] = assoc_record
|
|
166
|
+
end
|
|
167
|
+
associations << {parent_id: record.object_id, attribute: attribute, child_id: assoc_record.object_id}
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
record_index = 0
|
|
171
|
+
while(record_index < records_to_process.count)
|
|
172
|
+
record = records_to_process[record_index]
|
|
173
|
+
if record.id.loading? and record_being_saved
|
|
174
|
+
raise "Attempt to save a model while it or an associated model is still loading: model being saved: #{record_being_saved.model}:#{record_being_saved.id}#{', associated model: '+record.model.to_s if record != record_being_saved}"
|
|
175
|
+
end
|
|
176
|
+
output_attributes = {record.model.primary_key => record.id.loading? ? nil : record.id}
|
|
177
|
+
vector = record.vector || [record.model.model_name, ["new", record.object_id]]
|
|
178
|
+
models << {id: record.object_id, model: record.model.model_name, attributes: output_attributes, vector: vector}
|
|
179
|
+
record.attributes.each do |attribute, value|
|
|
180
|
+
if association = record.model.reflect_on_association(attribute)
|
|
181
|
+
if association.collection?
|
|
182
|
+
# following line changed from .all to .collection on 10/28
|
|
183
|
+
[*value.collection, *value.unsaved_children].each do |assoc|
|
|
184
|
+
add_new_association.call(record, attribute, assoc.backing_record) if assoc.changed?(association.inverse_of) or assoc.new?
|
|
185
|
+
end
|
|
186
|
+
elsif record.new? || record.changed?(attribute) || (record == record_being_saved && force)
|
|
187
|
+
if value.nil?
|
|
188
|
+
output_attributes[attribute] = nil
|
|
189
|
+
else
|
|
190
|
+
add_new_association.call record, attribute, value.backing_record
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
elsif aggregation = record.model.reflect_on_aggregation(attribute) and (aggregation.klass < ActiveRecord::Base)
|
|
194
|
+
add_new_association.call record, attribute, value.backing_record unless value.nil?
|
|
195
|
+
elsif aggregation
|
|
196
|
+
new_value = aggregation.serialize(value)
|
|
197
|
+
output_attributes[attribute] = new_value if record.changed?(attribute) or new_value != aggregation.serialize(record.synced_attributes[attribute])
|
|
198
|
+
elsif record.new? or record.changed?(attribute)
|
|
199
|
+
output_attributes[attribute] = value
|
|
200
|
+
end
|
|
201
|
+
end if record.new? || record.changed? || (record == record_being_saved && force)
|
|
202
|
+
record_index += 1
|
|
203
|
+
end
|
|
204
|
+
[models, associations, backing_records]
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
def save(validate, force, &block)
|
|
208
|
+
|
|
209
|
+
if data_loading?
|
|
210
|
+
|
|
211
|
+
sync!
|
|
212
|
+
|
|
213
|
+
elsif force or changed?
|
|
214
|
+
|
|
215
|
+
begin
|
|
216
|
+
|
|
217
|
+
models, associations, backing_records = self.class.gather_records([self], force, self)
|
|
218
|
+
|
|
219
|
+
backing_records.each { |id, record| record.saving! }
|
|
220
|
+
|
|
221
|
+
promise = Promise.new
|
|
222
|
+
|
|
223
|
+
HTTP.post(`window.ReactiveRecordEnginePath`+"/save",
|
|
224
|
+
payload: {
|
|
225
|
+
json: {
|
|
226
|
+
models: models,
|
|
227
|
+
associations: associations,
|
|
228
|
+
validate: validate
|
|
229
|
+
}.to_json
|
|
230
|
+
}
|
|
231
|
+
).then do |response|
|
|
232
|
+
begin
|
|
233
|
+
response.json[:models] = response.json[:saved_models].collect do |item|
|
|
234
|
+
backing_records[item[0]].ar_instance
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
if response.json[:success]
|
|
238
|
+
response.json[:saved_models].each do | item |
|
|
239
|
+
# was backing_records[item[0]].sync!(item[2])
|
|
240
|
+
HyperMesh::LocalSync.after_save backing_records[item[0]].ar_instance, item[2]
|
|
241
|
+
end
|
|
242
|
+
else
|
|
243
|
+
log("Reactive Record Save Failed: #{response.json[:message]}", :error)
|
|
244
|
+
response.json[:saved_models].each do | item |
|
|
245
|
+
log(" Model: #{item[1]}[#{item[0]}] Attributes: #{item[2]} Errors: #{item[3]}", :error) if item[3]
|
|
246
|
+
end
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
response.json[:saved_models].each do | item |
|
|
250
|
+
backing_records[item[0]].sync_unscoped_collection!
|
|
251
|
+
backing_records[item[0]].errors! item[3]
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
yield response.json[:success], response.json[:message], response.json[:models] if block
|
|
255
|
+
promise.resolve response.json
|
|
256
|
+
|
|
257
|
+
backing_records.each { |id, record| record.saved! }
|
|
258
|
+
|
|
259
|
+
rescue Exception => e
|
|
260
|
+
log("Exception raised while saving - #{e}", :error)
|
|
261
|
+
end
|
|
262
|
+
end
|
|
263
|
+
promise
|
|
264
|
+
rescue Exception => e
|
|
265
|
+
log("Exception raised while saving - #{e}", :error)
|
|
266
|
+
yield false, e.message, [] if block
|
|
267
|
+
promise.resolve({success: false, message: e.message, models: []})
|
|
268
|
+
promise
|
|
269
|
+
end
|
|
270
|
+
else
|
|
271
|
+
promise = Promise.new
|
|
272
|
+
yield true, nil, [] if block
|
|
273
|
+
promise.resolve({success: true})
|
|
274
|
+
promise
|
|
275
|
+
end
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
else
|
|
279
|
+
|
|
280
|
+
def self.find_record(model, id, vector, save)
|
|
281
|
+
if !save
|
|
282
|
+
found = vector[1..-1].inject(vector[0]) do |object, method|
|
|
283
|
+
if object.nil? # happens if you try to do an all on empty scope followed by more scopes
|
|
284
|
+
object
|
|
285
|
+
elsif method.is_a? Array
|
|
286
|
+
if method[0] == 'new'
|
|
287
|
+
object.new
|
|
288
|
+
else
|
|
289
|
+
object.send(*method)
|
|
290
|
+
end
|
|
291
|
+
elsif method.is_a? String and method[0] == '*'
|
|
292
|
+
object[method.gsub(/^\*/,'').to_i]
|
|
293
|
+
else
|
|
294
|
+
object.send(method)
|
|
295
|
+
end
|
|
296
|
+
end
|
|
297
|
+
if id and (found.nil? or !(found.class <= model) or (found.id and found.id.to_s != id.to_s))
|
|
298
|
+
raise "Inconsistent data sent to server - #{model.name}.find(#{id}) != [#{vector}]"
|
|
299
|
+
end
|
|
300
|
+
found
|
|
301
|
+
elsif id
|
|
302
|
+
model.find(id)
|
|
303
|
+
else
|
|
304
|
+
model.new
|
|
305
|
+
end
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
def self.is_enum?(record, key)
|
|
310
|
+
record.class.respond_to?(:defined_enums) && record.class.defined_enums[key]
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
def self.save_records(models, associations, acting_user, validate, save)
|
|
314
|
+
reactive_records = {}
|
|
315
|
+
vectors = {}
|
|
316
|
+
new_models = []
|
|
317
|
+
saved_models = []
|
|
318
|
+
dont_save_list = []
|
|
319
|
+
|
|
320
|
+
models.each do |model_to_save|
|
|
321
|
+
attributes = model_to_save[:attributes]
|
|
322
|
+
model = Object.const_get(model_to_save[:model])
|
|
323
|
+
id = attributes.delete(model.primary_key) if model.respond_to? :primary_key # if we are saving existing model primary key value will be present
|
|
324
|
+
vector = model_to_save[:vector]
|
|
325
|
+
vector = [vector[0].constantize] + vector[1..-1].collect do |method|
|
|
326
|
+
if method.is_a?(Array) and method.first == "find_by_id"
|
|
327
|
+
["find", method.last]
|
|
328
|
+
else
|
|
329
|
+
method
|
|
330
|
+
end
|
|
331
|
+
end
|
|
332
|
+
reactive_records[model_to_save[:id]] = vectors[vector] = record = find_record(model, id, vector, save)
|
|
333
|
+
if record and record.respond_to?(:id) and record.id
|
|
334
|
+
# we have an already exising activerecord model
|
|
335
|
+
keys = record.attributes.keys
|
|
336
|
+
attributes.each do |key, value|
|
|
337
|
+
if is_enum?(record, key)
|
|
338
|
+
record.send("#{key}=",value)
|
|
339
|
+
elsif keys.include? key
|
|
340
|
+
record[key] = value
|
|
341
|
+
elsif !value.nil? and aggregation = record.class.reflect_on_aggregation(key.to_sym) and !(aggregation.klass < ActiveRecord::Base)
|
|
342
|
+
aggregation.mapping.each_with_index do |pair, i|
|
|
343
|
+
record[pair.first] = value[i]
|
|
344
|
+
end
|
|
345
|
+
elsif record.respond_to? "#{key}="
|
|
346
|
+
record.send("#{key}=",value)
|
|
347
|
+
else
|
|
348
|
+
# TODO once reading schema.rb on client is implemented throw an error here
|
|
349
|
+
end
|
|
350
|
+
end
|
|
351
|
+
elsif record
|
|
352
|
+
# either the model is new, or its not even an active record model
|
|
353
|
+
dont_save_list << record unless save
|
|
354
|
+
keys = record.attributes.keys
|
|
355
|
+
attributes.each do |key, value|
|
|
356
|
+
if is_enum?(record, key)
|
|
357
|
+
record.send("#{key}=",value)
|
|
358
|
+
elsif keys.include? key
|
|
359
|
+
record[key] = value
|
|
360
|
+
elsif !value.nil? and aggregation = record.class.reflect_on_aggregation(key) and !(aggregation.klass < ActiveRecord::Base)
|
|
361
|
+
aggregation.mapping.each_with_index do |pair, i|
|
|
362
|
+
record[pair.first] = value[i]
|
|
363
|
+
end
|
|
364
|
+
elsif key.to_s != "id" and record.respond_to?("#{key}=") # server side methods can get included and we won't be able to write them...
|
|
365
|
+
# for example if you have a server side method foo, that you "get" on a new record, then later that value will get sent to the server
|
|
366
|
+
# we should track better these server side methods so this does not happen
|
|
367
|
+
record.send("#{key}=",value)
|
|
368
|
+
end
|
|
369
|
+
end
|
|
370
|
+
new_models << record
|
|
371
|
+
end
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
#puts "!!!!!!!!!!!!!!attributes updated"
|
|
375
|
+
ActiveRecord::Base.transaction do
|
|
376
|
+
associations.each do |association|
|
|
377
|
+
parent = reactive_records[association[:parent_id]]
|
|
378
|
+
next unless parent
|
|
379
|
+
#parent.instance_variable_set("@reactive_record_#{association[:attribute]}_changed", true) remove this????
|
|
380
|
+
if parent.class.reflect_on_aggregation(association[:attribute].to_sym)
|
|
381
|
+
#puts ">>>>>>AGGREGATE>>>> #{parent.class.name}.send('#{association[:attribute]}=', #{reactive_records[association[:child_id]]})"
|
|
382
|
+
aggregate = reactive_records[association[:child_id]]
|
|
383
|
+
dont_save_list << aggregate
|
|
384
|
+
current_attributes = parent.send(association[:attribute]).attributes
|
|
385
|
+
#puts "current parent attributes = #{current_attributes}"
|
|
386
|
+
new_attributes = aggregate.attributes
|
|
387
|
+
#puts "current child attributes = #{new_attributes}"
|
|
388
|
+
merged_attributes = current_attributes.merge(new_attributes) { |k, current_attr, new_attr| aggregate.send("#{k}_changed?") ? new_attr : current_attr}
|
|
389
|
+
#puts "merged attributes = #{merged_attributes}"
|
|
390
|
+
aggregate.assign_attributes(merged_attributes)
|
|
391
|
+
#puts "aggregate attributes after merge = #{aggregate.attributes}"
|
|
392
|
+
parent.send("#{association[:attribute]}=", aggregate)
|
|
393
|
+
#puts "updated is frozen? #{aggregate.frozen?}, parent attributes = #{parent.send(association[:attribute]).attributes}"
|
|
394
|
+
elsif parent.class.reflect_on_association(association[:attribute].to_sym).nil?
|
|
395
|
+
raise "Missing association :#{association[:attribute]} for #{parent.class.name}. Was association defined on opal side only?"
|
|
396
|
+
elsif parent.class.reflect_on_association(association[:attribute].to_sym).collection?
|
|
397
|
+
#puts ">>>>>>>>>> #{parent.class.name}.send('#{association[:attribute]}') << #{reactive_records[association[:child_id]]})"
|
|
398
|
+
dont_save_list.delete(parent)
|
|
399
|
+
if false and parent.new?
|
|
400
|
+
parent.send("#{association[:attribute]}") << reactive_records[association[:child_id]] if parent.new?
|
|
401
|
+
#puts "updated"
|
|
402
|
+
else
|
|
403
|
+
#puts "skipped"
|
|
404
|
+
end
|
|
405
|
+
else
|
|
406
|
+
#puts ">>>>ASSOCIATION>>>> #{parent.class.name}.send('#{association[:attribute]}=', #{reactive_records[association[:child_id]]})"
|
|
407
|
+
parent.send("#{association[:attribute]}=", reactive_records[association[:child_id]])
|
|
408
|
+
dont_save_list.delete(parent)
|
|
409
|
+
#puts "updated"
|
|
410
|
+
end
|
|
411
|
+
end if associations
|
|
412
|
+
|
|
413
|
+
#puts "!!!!!!!!!!!!associations updated"
|
|
414
|
+
|
|
415
|
+
has_errors = false
|
|
416
|
+
|
|
417
|
+
#puts "ready to start saving... dont_save_list = #{dont_save_list}"
|
|
418
|
+
|
|
419
|
+
saved_models = reactive_records.collect do |reactive_record_id, model|
|
|
420
|
+
#puts "saving rr_id: #{reactive_record_id} model.object_id: #{model.object_id} frozen? <#{model.frozen?}>"
|
|
421
|
+
if model and (model.frozen? or dont_save_list.include?(model) or model.changed.include?(model.class.primary_key))
|
|
422
|
+
# the above check for changed including the private key happens if you have an aggregate that includes its own id
|
|
423
|
+
#puts "validating frozen model #{model.class.name} #{model} (reactive_record_id = #{reactive_record_id})"
|
|
424
|
+
valid = model.valid?
|
|
425
|
+
#puts "has_errors before = #{has_errors}, validate= #{validate}, !valid= #{!valid} (validate and !valid) #{validate and !valid}"
|
|
426
|
+
has_errors ||= (validate and !valid)
|
|
427
|
+
#puts "validation complete errors = <#{!valid}>, #{model.errors.messages} has_errors #{has_errors}"
|
|
428
|
+
[reactive_record_id, model.class.name, model.attributes, (valid ? nil : model.errors.messages)]
|
|
429
|
+
elsif model and (!model.id or model.changed?)
|
|
430
|
+
#puts "saving #{model.class.name} #{model} (reactive_record_id = #{reactive_record_id})"
|
|
431
|
+
saved = model.check_permission_with_acting_user(acting_user, new_models.include?(model) ? :create_permitted? : :update_permitted?).save(validate: validate)
|
|
432
|
+
has_errors ||= !saved
|
|
433
|
+
messages = model.errors.messages if (validate and !saved) or (!validate and !model.valid?)
|
|
434
|
+
#puts "saved complete errors = <#{!saved}>, #{messages} has_errors #{has_errors}"
|
|
435
|
+
[reactive_record_id, model.class.name, model.attributes, messages]
|
|
436
|
+
end
|
|
437
|
+
end.compact
|
|
438
|
+
|
|
439
|
+
raise "Could not save all models" if has_errors
|
|
440
|
+
|
|
441
|
+
if save
|
|
442
|
+
|
|
443
|
+
{success: true, saved_models: saved_models }
|
|
444
|
+
|
|
445
|
+
else
|
|
446
|
+
|
|
447
|
+
vectors.each { |vector, model| model.reload unless model.nil? or model.new_record? or model.frozen? }
|
|
448
|
+
vectors
|
|
449
|
+
|
|
450
|
+
end
|
|
451
|
+
|
|
452
|
+
end
|
|
453
|
+
|
|
454
|
+
rescue Exception => e
|
|
455
|
+
ReactiveRecord::Pry.rescued(e)
|
|
456
|
+
if save
|
|
457
|
+
{success: false, saved_models: saved_models, message: e}
|
|
458
|
+
else
|
|
459
|
+
{}
|
|
460
|
+
end
|
|
461
|
+
end
|
|
462
|
+
|
|
463
|
+
end
|
|
464
|
+
|
|
465
|
+
# destroy records
|
|
466
|
+
|
|
467
|
+
if RUBY_ENGINE == 'opal'
|
|
468
|
+
|
|
469
|
+
def destroy(&block)
|
|
470
|
+
|
|
471
|
+
return if @destroyed
|
|
472
|
+
|
|
473
|
+
#destroy_associations
|
|
474
|
+
|
|
475
|
+
promise = Promise.new
|
|
476
|
+
|
|
477
|
+
if !data_loading? and (id or vector)
|
|
478
|
+
HTTP.post(`window.ReactiveRecordEnginePath`+"/destroy",
|
|
479
|
+
payload: {
|
|
480
|
+
json: {
|
|
481
|
+
model: ar_instance.model_name,
|
|
482
|
+
id: id,
|
|
483
|
+
vector: vector
|
|
484
|
+
}.to_json
|
|
485
|
+
}
|
|
486
|
+
).then do |response|
|
|
487
|
+
#sync_unscoped_collection!
|
|
488
|
+
HyperMesh::LocalSync.after_save ar_instance
|
|
489
|
+
yield response.json[:success], response.json[:message] if block
|
|
490
|
+
promise.resolve response.json
|
|
491
|
+
end
|
|
492
|
+
else
|
|
493
|
+
destroy_associations
|
|
494
|
+
# sync_unscoped_collection! # ? should we do this here was NOT being done before hypermesh integration
|
|
495
|
+
yield true, nil if block
|
|
496
|
+
promise.resolve({success: true})
|
|
497
|
+
end
|
|
498
|
+
|
|
499
|
+
# DO NOT CLEAR ATTRIBUTES. Records that are not found, are destroyed, and if they are searched for again, we want to make
|
|
500
|
+
# sure to find them. We may want to change this, and provide a separate flag called not_found. In this case you
|
|
501
|
+
# would put these lines here:
|
|
502
|
+
# @attributes = {}
|
|
503
|
+
# sync!
|
|
504
|
+
# and modify server_data_cache so that it does NOT call destroy
|
|
505
|
+
|
|
506
|
+
@destroyed = true
|
|
507
|
+
|
|
508
|
+
promise
|
|
509
|
+
end
|
|
510
|
+
|
|
511
|
+
else
|
|
512
|
+
|
|
513
|
+
def self.destroy_record(model, id, vector, acting_user)
|
|
514
|
+
model = Object.const_get(model)
|
|
515
|
+
record = if id
|
|
516
|
+
model.find(id)
|
|
517
|
+
else
|
|
518
|
+
ServerDataCache.new(acting_user, {})[*vector]
|
|
519
|
+
end
|
|
520
|
+
|
|
521
|
+
|
|
522
|
+
record.check_permission_with_acting_user(acting_user, :destroy_permitted?).destroy
|
|
523
|
+
{success: true, attributes: {}}
|
|
524
|
+
|
|
525
|
+
rescue Exception => e
|
|
526
|
+
ReactiveRecord::Pry.rescued(e)
|
|
527
|
+
{success: false, record: record, message: e}
|
|
528
|
+
end
|
|
529
|
+
end
|
|
530
|
+
end
|
|
531
|
+
|
|
532
|
+
end
|