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,19 @@
|
|
|
1
|
+
module ActiveRecord
|
|
2
|
+
# adds method to get the HyperMesh public column types
|
|
3
|
+
# for now we are just getting all models column types, but we should
|
|
4
|
+
# look through the public folder, and just get those models.
|
|
5
|
+
# this works because the public folder is currently required to be eaer loaded.
|
|
6
|
+
class Base
|
|
7
|
+
def self.public_columns_hash
|
|
8
|
+
return @public_columns_hash if @public_columns_hash
|
|
9
|
+
Dir.glob(Rails.root.join('app/models/public/*.rb')).each do |file|
|
|
10
|
+
require_dependency(file) rescue nil
|
|
11
|
+
end
|
|
12
|
+
@public_columns_hash = {}
|
|
13
|
+
descendants.each do |model|
|
|
14
|
+
@public_columns_hash[model.name] = model.columns_hash
|
|
15
|
+
end
|
|
16
|
+
@public_columns_hash
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,520 @@
|
|
|
1
|
+
module ReactiveRecord
|
|
2
|
+
class Base
|
|
3
|
+
|
|
4
|
+
# Its all about lazy loading. This prevents us from grabbing enormous association collections, or large attributes
|
|
5
|
+
# unless they are explicitly requested.
|
|
6
|
+
|
|
7
|
+
# During prerendering we get each attribute as its requested and fill it in both on the javascript side, as well as
|
|
8
|
+
# remember that the attribute needs to be part of the download to client.
|
|
9
|
+
|
|
10
|
+
# On the client we fill in the record data with empty values (nil, or one element collections) but only as the attribute
|
|
11
|
+
# is requested. Each request queues up a request to get the real data from the server.
|
|
12
|
+
|
|
13
|
+
# The ReactiveRecord class serves two purposes. First it is the unique data corresponding to the last known state of a
|
|
14
|
+
# database record. This means All records matching a specific database record are unique. This is unlike AR but is
|
|
15
|
+
# important both for the lazy loading and also so that when values change react can be informed of the change.
|
|
16
|
+
|
|
17
|
+
# Secondly it serves as name space for all the ReactiveRecord specific methods, so every AR Instance has a ReactiveRecord
|
|
18
|
+
|
|
19
|
+
# Because there is no point in generating a new ar_instance everytime a search is made we cache the first ar_instance created.
|
|
20
|
+
# Its possible however during loading to create a new ar_instances that will in the end point to the same record.
|
|
21
|
+
|
|
22
|
+
# VECTORS... are an important concept. They are the substitute for a primary key before a record is loaded.
|
|
23
|
+
# Vectors have the form [ModelClass, method_call, method_call, method_call...]
|
|
24
|
+
|
|
25
|
+
# Each method call is either a simple method name or an array in the form [method_name, param, param ...]
|
|
26
|
+
# Example [User, [find, 123], todos, active, [due, "1/1/2016"], title]
|
|
27
|
+
# Roughly corresponds to this query: User.find(123).todos.active.due("1/1/2016").select(:title)
|
|
28
|
+
|
|
29
|
+
attr_accessor :ar_instance
|
|
30
|
+
attr_accessor :vector
|
|
31
|
+
attr_accessor :model
|
|
32
|
+
attr_accessor :changed_attributes
|
|
33
|
+
attr_accessor :aggregate_owner
|
|
34
|
+
attr_accessor :aggregate_attribute
|
|
35
|
+
attr_accessor :destroyed
|
|
36
|
+
attr_accessor :updated_during
|
|
37
|
+
attr_accessor :synced_attributes
|
|
38
|
+
attr_accessor :virgin
|
|
39
|
+
|
|
40
|
+
# While data is being loaded from the server certain internal behaviors need to change
|
|
41
|
+
# for example records all record changes are synced as they happen.
|
|
42
|
+
# This is implemented this way so that the ServerDataCache class can use pure active
|
|
43
|
+
# record methods in its implementation
|
|
44
|
+
|
|
45
|
+
def self.data_loading?
|
|
46
|
+
@data_loading
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def data_loading?
|
|
50
|
+
self.class.data_loading?
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def self.load_data(&block)
|
|
54
|
+
current_data_loading, @data_loading = [@data_loading, true]
|
|
55
|
+
yield
|
|
56
|
+
ensure
|
|
57
|
+
@data_loading = current_data_loading
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def self.load_from_json(json, target = nil)
|
|
61
|
+
load_data { ServerDataCache.load_from_json(json, target) }
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def self.class_scopes(model)
|
|
65
|
+
@class_scopes[model.base_class]
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# def self.sync_blocks
|
|
69
|
+
# # @sync_blocks[watch_model][sync_model][scope_name][...array of blocks...]
|
|
70
|
+
# @sync_blocks ||= Hash.new { |hash, key| hash[key] = Hash.new { |hash, key| hash[key] = Hash.new { |hash, key| hash[key] = [] } } }
|
|
71
|
+
# end
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def self.find(model, attribute, value)
|
|
75
|
+
# will return the unique record with this attribute-value pair
|
|
76
|
+
# value cannot be an association or aggregation
|
|
77
|
+
|
|
78
|
+
model = model.base_class
|
|
79
|
+
# already have a record with this attribute-value pair?
|
|
80
|
+
record = @records[model].detect { |record| record.attributes[attribute] == value}
|
|
81
|
+
unless record
|
|
82
|
+
# if not, and then the record may be loaded, but not have this attribute set yet,
|
|
83
|
+
# so find the id of of record with the attribute-value pair, and see if that is loaded.
|
|
84
|
+
# find_in_db returns nil if we are not prerendering which will force us to create a new record
|
|
85
|
+
# because there is no way of knowing the id.
|
|
86
|
+
if attribute != model.primary_key and id = find_in_db(model, attribute, value)
|
|
87
|
+
record = @records[model].detect { |record| record.id == id}
|
|
88
|
+
end
|
|
89
|
+
# if we don't have a record then create one
|
|
90
|
+
(record = new(model)).vector = [model, ["find_by_#{attribute}", value]] unless record
|
|
91
|
+
# and set the value
|
|
92
|
+
record.sync_attribute(attribute, value)
|
|
93
|
+
# and set the primary if we have one
|
|
94
|
+
record.sync_attribute(model.primary_key, id) if id
|
|
95
|
+
end
|
|
96
|
+
# finally initialize and return the ar_instance
|
|
97
|
+
record.ar_instance ||= infer_type_from_hash(model, record.attributes).new(record)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def self.find_by_object_id(model, object_id)
|
|
101
|
+
@records[model].detect { |record| record.object_id == object_id }.ar_instance
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def self.new_from_vector(model, aggregate_owner, *vector)
|
|
105
|
+
# this is the equivilent of find but for associations and aggregations
|
|
106
|
+
# because we are not fetching a specific attribute yet, there is NO communication with the
|
|
107
|
+
# server. That only happens during find.
|
|
108
|
+
model = model.base_class
|
|
109
|
+
|
|
110
|
+
# do we already have a record with this vector? If so return it, otherwise make a new one.
|
|
111
|
+
|
|
112
|
+
record = @records[model].detect { |record| record.vector == vector }
|
|
113
|
+
unless record
|
|
114
|
+
record = new model
|
|
115
|
+
record.vector = vector
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
record.ar_instance ||= infer_type_from_hash(model, record.attributes).new(record)
|
|
119
|
+
|
|
120
|
+
if aggregate_owner
|
|
121
|
+
record.aggregate_owner = aggregate_owner
|
|
122
|
+
record.aggregate_attribute = vector.last
|
|
123
|
+
aggregate_owner.attributes[vector.last] = record.ar_instance
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
record.ar_instance
|
|
127
|
+
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def initialize(model, hash = {}, ar_instance = nil)
|
|
131
|
+
@model = model
|
|
132
|
+
@ar_instance = ar_instance
|
|
133
|
+
@synced_attributes = {}
|
|
134
|
+
@attributes = {}
|
|
135
|
+
@changed_attributes = []
|
|
136
|
+
@virgin = true
|
|
137
|
+
records[model] << self
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def find(*args)
|
|
141
|
+
self.class.find(*args)
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def new_from_vector(*args)
|
|
145
|
+
self.class.new_from_vector(*args)
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def primary_key
|
|
149
|
+
@model.primary_key
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def id
|
|
153
|
+
attributes[primary_key]
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def id=(value)
|
|
157
|
+
# value can be nil if we are loading an aggregate otherwise check if it already exists
|
|
158
|
+
if !(value and existing_record = records[@model].detect { |record| record.attributes[primary_key] == value})
|
|
159
|
+
attributes[primary_key] = value
|
|
160
|
+
else
|
|
161
|
+
@ar_instance.instance_variable_set(:@backing_record, existing_record)
|
|
162
|
+
existing_record.attributes.merge!(attributes) { |key, v1, v2| v1 }
|
|
163
|
+
end
|
|
164
|
+
value
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def attributes
|
|
168
|
+
@last_access_at = Time.now
|
|
169
|
+
@attributes
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def reactive_get!(attribute, reload = nil)
|
|
173
|
+
@virgin = false unless data_loading?
|
|
174
|
+
unless @destroyed
|
|
175
|
+
if @attributes.has_key? attribute
|
|
176
|
+
attributes[attribute].notify if @attributes[attribute].is_a? DummyValue
|
|
177
|
+
apply_method(attribute) if reload
|
|
178
|
+
else
|
|
179
|
+
apply_method(attribute)
|
|
180
|
+
end
|
|
181
|
+
React::State.get_state(self, attribute) unless data_loading?
|
|
182
|
+
attributes[attribute]
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def reactive_set!(attribute, value)
|
|
187
|
+
@virgin = false unless data_loading?
|
|
188
|
+
return value if @destroyed || dont_update_attribute?(attribute, value)
|
|
189
|
+
return attributes[attribute] if update_aggregate(attribute, value)
|
|
190
|
+
value = update_relationships(attribute, value)
|
|
191
|
+
update_attribute(attribute, value)
|
|
192
|
+
value
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def dont_update_attribute?(attribute, value)
|
|
196
|
+
return false if attributes[attribute].is_a?(DummyValue)
|
|
197
|
+
return false unless attributes.key?(attribute)
|
|
198
|
+
return false if attributes[attribute] != value
|
|
199
|
+
true
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def update_attribute(attribute, *args)
|
|
203
|
+
value = args[0]
|
|
204
|
+
if args.count != 0 and data_loading?
|
|
205
|
+
if (aggregation = model.reflect_on_aggregation(attribute)) and !(aggregation.klass < ActiveRecord::Base)
|
|
206
|
+
@synced_attributes[attribute] = aggregation.deserialize(aggregation.serialize(value))
|
|
207
|
+
else
|
|
208
|
+
@synced_attributes[attribute] = value
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
if @virgin
|
|
212
|
+
attributes[attribute] = value if args.count != 0
|
|
213
|
+
return
|
|
214
|
+
end
|
|
215
|
+
changed = if args.count == 0
|
|
216
|
+
if (association = @model.reflect_on_association(attribute)) and association.collection?
|
|
217
|
+
attributes[attribute] != @synced_attributes[attribute]
|
|
218
|
+
else
|
|
219
|
+
!attributes[attribute].backing_record.changed_attributes.empty?
|
|
220
|
+
end
|
|
221
|
+
elsif (association = @model.reflect_on_association(attribute)) and association.collection?
|
|
222
|
+
value != @synced_attributes[attribute]
|
|
223
|
+
else
|
|
224
|
+
!@synced_attributes.has_key?(attribute) or @synced_attributes[attribute] != value
|
|
225
|
+
end
|
|
226
|
+
empty_before = changed_attributes.empty?
|
|
227
|
+
if !changed
|
|
228
|
+
changed_attributes.delete(attribute)
|
|
229
|
+
elsif !changed_attributes.include?(attribute)
|
|
230
|
+
changed_attributes << attribute
|
|
231
|
+
end
|
|
232
|
+
had_key = attributes.has_key? attribute
|
|
233
|
+
current_value = attributes[attribute]
|
|
234
|
+
attributes[attribute] = value if args.count != 0
|
|
235
|
+
if !data_loading?
|
|
236
|
+
React::State.set_state(self, attribute, value)
|
|
237
|
+
elsif on_opal_client? and had_key and current_value.loaded? and current_value != value and args.count > 0 # this is to handle changes in already loaded server side methods
|
|
238
|
+
React::State.set_state(self, attribute, value, true)
|
|
239
|
+
end
|
|
240
|
+
if empty_before != changed_attributes.empty?
|
|
241
|
+
React::State.set_state(self, "!CHANGED!", !changed_attributes.empty?, true) unless on_opal_server? or data_loading?
|
|
242
|
+
aggregate_owner.update_attribute(aggregate_attribute) if aggregate_owner
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
def changed?(*args)
|
|
247
|
+
if args.count == 0
|
|
248
|
+
React::State.get_state(self, "!CHANGED!")
|
|
249
|
+
!changed_attributes.empty?
|
|
250
|
+
else
|
|
251
|
+
React::State.get_state(self, args[0])
|
|
252
|
+
changed_attributes.include? args[0]
|
|
253
|
+
end
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
def errors
|
|
257
|
+
@errors ||= ActiveModel::Error.new
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
# called when we have a newly created record, to initialize
|
|
261
|
+
# any nil collections to empty arrays. We can do this because
|
|
262
|
+
# if its a brand new record, then any collections that are still
|
|
263
|
+
# nil must not have any children.
|
|
264
|
+
def initialize_collections
|
|
265
|
+
if (!vector || vector.empty?) && id && id != ''
|
|
266
|
+
@vector = [@model, ["find_by_#{@model.primary_key}", id]]
|
|
267
|
+
end
|
|
268
|
+
@model.reflect_on_all_associations.each do |assoc|
|
|
269
|
+
if assoc.collection? && attributes[assoc.attribute].nil?
|
|
270
|
+
ar_instance.send("#{assoc.attribute}=", [])
|
|
271
|
+
end
|
|
272
|
+
end
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
# sync! now will also initialize any nil collections
|
|
276
|
+
def sync!(hash = {}) # does NOT notify (see saved! for notification)
|
|
277
|
+
hash.each do |attr, value|
|
|
278
|
+
@attributes[attr] = convert(attr, value)
|
|
279
|
+
end
|
|
280
|
+
@synced_attributes = {}
|
|
281
|
+
@synced_attributes.each { |attribute, value| sync_attribute(key, value) }
|
|
282
|
+
@changed_attributes = []
|
|
283
|
+
@saving = false
|
|
284
|
+
@errors = nil
|
|
285
|
+
# set the vector and clear collections - this only happens when a new record is saved
|
|
286
|
+
initialize_collections if (!vector || vector.empty?) && id && id != ''
|
|
287
|
+
self
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
# this keeps the unscoped collection up to date.
|
|
291
|
+
# @destroy_sync and @create_sync prevent multiple insertions
|
|
292
|
+
# to collections that just have a count
|
|
293
|
+
def sync_unscoped_collection!
|
|
294
|
+
if destroyed
|
|
295
|
+
return if @destroy_sync
|
|
296
|
+
@destroy_sync = true
|
|
297
|
+
else
|
|
298
|
+
return if @create_sync
|
|
299
|
+
@create_sync = true
|
|
300
|
+
end
|
|
301
|
+
model.unscoped << ar_instance
|
|
302
|
+
@synced_with_unscoped = !@synced_with_unscoped
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
def sync_attribute(attribute, value)
|
|
306
|
+
|
|
307
|
+
@synced_attributes[attribute] = attributes[attribute] = value
|
|
308
|
+
|
|
309
|
+
#@synced_attributes[attribute] = value.dup if value.is_a? ReactiveRecord::Collection
|
|
310
|
+
|
|
311
|
+
if value.is_a? Collection
|
|
312
|
+
@synced_attributes[attribute] = value.dup_for_sync
|
|
313
|
+
elsif aggregation = model.reflect_on_aggregation(attribute) and (aggregation.klass < ActiveRecord::Base)
|
|
314
|
+
value.backing_record.sync!
|
|
315
|
+
elsif aggregation
|
|
316
|
+
@synced_attributes[attribute] = aggregation.deserialize(aggregation.serialize(value))
|
|
317
|
+
elsif !model.reflect_on_association(attribute)
|
|
318
|
+
@synced_attributes[attribute] = JSON.parse(value.to_json)
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
@changed_attributes.delete(attribute)
|
|
322
|
+
value
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
# helper so we can tell if model exists. We need this so we can detect
|
|
326
|
+
# if a record has local changes that are out of sync.
|
|
327
|
+
def self.exists?(model, id)
|
|
328
|
+
@records[model].detect { |record| record.attributes[model.primary_key] == id }
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
def revert
|
|
332
|
+
@changed_attributes.dup.each do |attribute|
|
|
333
|
+
@ar_instance.send("#{attribute}=", @synced_attributes[attribute])
|
|
334
|
+
@attributes.delete(attribute) unless @synced_attributes.key?(attribute)
|
|
335
|
+
end
|
|
336
|
+
@changed_attributes = []
|
|
337
|
+
@errors = nil
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
def saving!
|
|
341
|
+
React::State.set_state(self, self, :saving) unless data_loading?
|
|
342
|
+
@saving = true
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
def errors!(errors)
|
|
346
|
+
@saving = false
|
|
347
|
+
@errors = errors and ActiveModel::Error.new(errors)
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
def saved! # sets saving to false AND notifies
|
|
351
|
+
@saving = false
|
|
352
|
+
if !@errors or @errors.empty?
|
|
353
|
+
React::State.set_state(self, self, :saved)
|
|
354
|
+
elsif !data_loading?
|
|
355
|
+
React::State.set_state(self, self, :error)
|
|
356
|
+
end
|
|
357
|
+
self
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
def saving?
|
|
361
|
+
React::State.get_state(self, self)
|
|
362
|
+
@saving
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
def new?
|
|
366
|
+
!id and !vector
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
def find_association(association, id)
|
|
370
|
+
inverse_of = association.inverse_of
|
|
371
|
+
instance = if id
|
|
372
|
+
find(association.klass, association.klass.primary_key, id)
|
|
373
|
+
else
|
|
374
|
+
new_from_vector(association.klass, nil, *vector, association.attribute)
|
|
375
|
+
end
|
|
376
|
+
instance_backing_record_attributes = instance.backing_record.attributes
|
|
377
|
+
inverse_association = association.klass.reflect_on_association(inverse_of)
|
|
378
|
+
if inverse_association.collection?
|
|
379
|
+
instance_backing_record_attributes[inverse_of] = if id and id != ""
|
|
380
|
+
Collection.new(@model, instance, inverse_association, association.klass, ["find", id], inverse_of)
|
|
381
|
+
else
|
|
382
|
+
Collection.new(@model, instance, inverse_association, *vector, association.attribute, inverse_of)
|
|
383
|
+
end unless instance_backing_record_attributes[inverse_of]
|
|
384
|
+
instance_backing_record_attributes[inverse_of].replace [@ar_instance]
|
|
385
|
+
else
|
|
386
|
+
instance_backing_record_attributes[inverse_of] = @ar_instance
|
|
387
|
+
end unless association.through_association? || instance_backing_record_attributes.key?(inverse_of)
|
|
388
|
+
instance
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
def apply_method(method)
|
|
392
|
+
# Fills in the value returned by sending "method" to the corresponding server side db instance
|
|
393
|
+
if on_opal_server? and changed?
|
|
394
|
+
log("Warning fetching virtual attributes (#{model.name}.#{method}) during prerendering on a changed or new model is not implemented.", :warning)
|
|
395
|
+
# to implement this we would have to sync up any changes during prererendering with a set the cached models (see server_data_cache)
|
|
396
|
+
# right now server_data cache is read only, BUT we could change this. However it seems like a tails case. Why would we create or update
|
|
397
|
+
# a model during prerendering???
|
|
398
|
+
end
|
|
399
|
+
if !new?
|
|
400
|
+
new_value = if association = @model.reflect_on_association(method)
|
|
401
|
+
if association.collection?
|
|
402
|
+
Collection.new(association.klass, @ar_instance, association, *vector, method)
|
|
403
|
+
else
|
|
404
|
+
find_association(association, (id and id != "" and self.class.fetch_from_db([@model, [:find, id], method, @model.primary_key])))
|
|
405
|
+
end
|
|
406
|
+
elsif aggregation = @model.reflect_on_aggregation(method) and (aggregation.klass < ActiveRecord::Base)
|
|
407
|
+
new_from_vector(aggregation.klass, self, *vector, method)
|
|
408
|
+
elsif id and id != ''
|
|
409
|
+
self.class.fetch_from_db([@model, [:find, id], *method]) || self.class.load_from_db(self, *(vector ? vector : [nil]), method)
|
|
410
|
+
else # its a attribute in an aggregate or we are on the client and don't know the id
|
|
411
|
+
self.class.fetch_from_db([*vector, *method]) || self.class.load_from_db(self, *(vector ? vector : [nil]), method)
|
|
412
|
+
end
|
|
413
|
+
new_value = @attributes[method] if new_value.is_a? DummyValue and @attributes.has_key?(method)
|
|
414
|
+
sync_attribute(method, new_value)
|
|
415
|
+
elsif association = @model.reflect_on_association(method) and association.collection?
|
|
416
|
+
@attributes[method] = Collection.new(association.klass, @ar_instance, association)
|
|
417
|
+
elsif aggregation = @model.reflect_on_aggregation(method) and (aggregation.klass < ActiveRecord::Base)
|
|
418
|
+
@attributes[method] = aggregation.klass.new.tap do |aggregate|
|
|
419
|
+
backing_record = aggregate.backing_record
|
|
420
|
+
backing_record.aggregate_owner = self
|
|
421
|
+
backing_record.aggregate_attribute = method
|
|
422
|
+
end
|
|
423
|
+
elsif !aggregation and method != model.primary_key
|
|
424
|
+
if model.columns_hash[method]
|
|
425
|
+
new_value = convert(method, model.columns_hash[method][:default])
|
|
426
|
+
else
|
|
427
|
+
unless @attributes.key?(method)
|
|
428
|
+
log("Warning: reading from new #{model.name}.#{method} before assignment. Will fetch value from server. This may not be what you expected!!", :warning)
|
|
429
|
+
end
|
|
430
|
+
new_value = self.class.load_from_db(self, *(vector ? vector : [nil]), method)
|
|
431
|
+
new_value = @attributes[method] if new_value.is_a?(DummyValue) && @attributes.key?(method)
|
|
432
|
+
end
|
|
433
|
+
sync_attribute(method, new_value)
|
|
434
|
+
end
|
|
435
|
+
end
|
|
436
|
+
|
|
437
|
+
def self.infer_type_from_hash(klass, hash)
|
|
438
|
+
klass = klass.base_class
|
|
439
|
+
return klass unless hash
|
|
440
|
+
type = hash[klass.inheritance_column]
|
|
441
|
+
begin
|
|
442
|
+
return Object.const_get(type)
|
|
443
|
+
rescue Exception => e
|
|
444
|
+
message = "Could not subclass #{@model_klass.model_name} as #{type}. Perhaps #{type} class has not been required. Exception: #{e}"
|
|
445
|
+
`console.error(#{message})`
|
|
446
|
+
end if type
|
|
447
|
+
klass
|
|
448
|
+
end
|
|
449
|
+
|
|
450
|
+
class << self
|
|
451
|
+
attr_reader :outer_scopes
|
|
452
|
+
|
|
453
|
+
def default_scope
|
|
454
|
+
@class_scopes[:default_scope]
|
|
455
|
+
end
|
|
456
|
+
|
|
457
|
+
def unscoped
|
|
458
|
+
@class_scopes[:unscoped]
|
|
459
|
+
end
|
|
460
|
+
|
|
461
|
+
def add_to_outer_scopes(item)
|
|
462
|
+
@outer_scopes << item
|
|
463
|
+
end
|
|
464
|
+
end
|
|
465
|
+
|
|
466
|
+
# when_not_saving will wait until reactive-record is not saving a model.
|
|
467
|
+
# Currently there is no easy way to do this without polling.
|
|
468
|
+
def self.when_not_saving(model)
|
|
469
|
+
if @records[model].detect(&:saving?)
|
|
470
|
+
poller = every(0.1) do
|
|
471
|
+
unless @records[model].detect(&:saving?)
|
|
472
|
+
poller.stop
|
|
473
|
+
yield model
|
|
474
|
+
end
|
|
475
|
+
end
|
|
476
|
+
else
|
|
477
|
+
yield model
|
|
478
|
+
end
|
|
479
|
+
end
|
|
480
|
+
|
|
481
|
+
# While evaluating scopes we want to catch any requests
|
|
482
|
+
# to the server. Once we catch any requests to the server
|
|
483
|
+
# then all the further scopes in that chain will be made
|
|
484
|
+
# at the server.
|
|
485
|
+
|
|
486
|
+
class << self
|
|
487
|
+
class DbRequestMade < RuntimeError; end
|
|
488
|
+
|
|
489
|
+
def catch_db_requests(return_val = nil)
|
|
490
|
+
@catch_db_requests = true
|
|
491
|
+
yield
|
|
492
|
+
rescue DbRequestMade => e
|
|
493
|
+
puts "Warning request for server side data during scope evaluation: #{e.message}"
|
|
494
|
+
return_val
|
|
495
|
+
ensure
|
|
496
|
+
@catch_db_requests = false
|
|
497
|
+
end
|
|
498
|
+
|
|
499
|
+
alias pre_synchromesh_load_from_db load_from_db
|
|
500
|
+
|
|
501
|
+
def load_from_db(*args)
|
|
502
|
+
raise DbRequestMade, args if @catch_db_requests
|
|
503
|
+
pre_synchromesh_load_from_db(*args)
|
|
504
|
+
end
|
|
505
|
+
end
|
|
506
|
+
|
|
507
|
+
def destroy_associations
|
|
508
|
+
@destroyed = false
|
|
509
|
+
model.reflect_on_all_associations.each do |association|
|
|
510
|
+
if association.collection?
|
|
511
|
+
attributes[association.attribute].replace([]) if attributes[association.attribute]
|
|
512
|
+
else
|
|
513
|
+
@ar_instance.send("#{association.attribute}=", nil)
|
|
514
|
+
end
|
|
515
|
+
end
|
|
516
|
+
@destroyed = true
|
|
517
|
+
end
|
|
518
|
+
|
|
519
|
+
end
|
|
520
|
+
end
|