├── .gitignore
├── .rspec
├── .rubocop.yml
├── .travis.yml
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── DOCS.md
├── Gemfile
├── Gemfile.lock
├── LICENSE.txt
├── README.md
├── Rakefile
├── bin
├── console
└── setup
├── codeship.database.yml
├── dciy.toml
├── gemfiles
└── hyper-mesh.gemfile
├── hyper-mesh.gemspec
├── lib
├── active_record_base.rb
├── acts_as_string.rb
├── enumerable
│ └── pluck.rb
├── hyper-mesh.rb
├── hyper_react
│ └── input_tags.rb
├── hypermesh
│ └── version.rb
├── kernel
│ └── itself.rb
├── object
│ └── tap.rb
├── opal
│ ├── equality_patches.rb
│ ├── parse_patch.rb
│ └── set_patches.rb
└── reactive_record
│ ├── active_record
│ ├── aggregations.rb
│ ├── associations.rb
│ ├── base.rb
│ ├── class_methods.rb
│ ├── error.rb
│ ├── errors.rb
│ ├── instance_methods.rb
│ ├── public_columns_hash.rb
│ └── reactive_record
│ │ ├── backing_record_inspector.rb
│ │ ├── base.rb
│ │ ├── collection.rb
│ │ ├── column_types.rb
│ │ ├── dummy_value.rb
│ │ ├── getters.rb
│ │ ├── isomorphic_base.rb
│ │ ├── lookup_tables.rb
│ │ ├── operations.rb
│ │ ├── scoped_collection.rb
│ │ ├── setters.rb
│ │ ├── unscoped_collection.rb
│ │ └── while_loading.rb
│ ├── active_record_error.rb
│ ├── broadcast.rb
│ ├── engine.rb
│ ├── interval.rb
│ ├── permissions.rb
│ ├── pry.rb
│ ├── reactive_scope.rb
│ ├── scope_description.rb
│ ├── serializers.rb
│ └── server_data_cache.rb
├── logo.jpg
├── path_release_steps.md
├── remote.md
├── spec
├── batch1
│ ├── column_types
│ │ └── column_type_spec.rb
│ ├── crud_access_regulation
│ │ ├── broadcast_controls_access_spec.rb
│ │ └── model_policies_spec.rb
│ ├── misc
│ │ ├── access_like_hash_spec.rb
│ │ ├── errors_spec.rb
│ │ ├── validate_spec.rb
│ │ └── while_loading_spec.rb
│ └── policies
│ │ ├── regulate_all_broadcasts_spec.rb
│ │ ├── regulate_broadcast_spec.rb
│ │ └── send_access_xspec.rb
├── batch2
│ ├── alias_attribute_spec.rb
│ ├── default_scope_spec.rb
│ ├── enum_xspec.rb
│ ├── has_many_through_spec.rb
│ ├── non_ar_aggregations_tbdspec.rb
│ └── relationships_spec.rb
├── batch3
│ ├── auto_load_itself_spec.rb
│ ├── edge_cases_spec.rb
│ ├── finder_method_spec.rb
│ ├── many_to_many_spec.rb
│ ├── pry_rescue_xspec.rb
│ └── revert_spec.rb
├── batch4
│ ├── default_value_spec.rb
│ ├── scope_spec.rb
│ ├── scoped_todos_spec.rb
│ ├── synchromesh_spec.rb
│ └── zzz_saving_during_commit_spec.rb
├── batch5
│ ├── authorization_spec.rb
│ ├── get_model_spec.rb
│ ├── load_from_json_xspec.rb
│ ├── save_while_loading_spec.rb
│ └── zzz_must_be_last_relationship_permissions_spec.rb
├── batch6
│ ├── aaa_update_scopes_spec.rb
│ ├── inspect_spec.rb
│ ├── on_fetch_error_spec.rb
│ ├── server_method_spec.rb
│ ├── speed_improvement.rb
│ └── update_associations_spec.rb
├── batch7
│ ├── aaa-unit_tests
│ │ ├── aggregation_experiments.rb
│ │ ├── aggregations_spec.rb
│ │ ├── ar_basics_tbdspec.rb
│ │ ├── association_reflection_tbdspec.rb
│ │ └── dummy_value_spec.rb
│ └── sti_spec.rb
├── bin
│ └── firebug-2.0.13-fx.xpi
├── examples
│ ├── dictionary.rb
│ ├── dictionary_with_client_scopes.rb
│ └── random_examples.rb
├── factories
│ ├── child_model.rb
│ ├── comment.rb
│ ├── test_models.rb
│ ├── todo.rb
│ └── user.rb
├── failing_tests.txt
├── play_ground.rb
├── reactive_record_factory.rb
├── spec_helper.rb
├── support
│ └── component_helpers.rb
├── test_app
│ ├── Gemfile
│ ├── Rakefile
│ ├── app
│ │ ├── assets
│ │ │ ├── javascripts
│ │ │ │ ├── application.js
│ │ │ │ └── server_rendering.js
│ │ │ └── stylesheets
│ │ │ │ └── application.css
│ │ ├── controllers
│ │ │ ├── application_controller.rb
│ │ │ └── test_controller.rb
│ │ ├── models
│ │ │ ├── _react_public_models.rb
│ │ │ └── public
│ │ │ │ ├── address.rb
│ │ │ │ ├── application_record.rb
│ │ │ │ ├── child_model.rb
│ │ │ │ ├── comment.rb
│ │ │ │ ├── default_test.rb
│ │ │ │ ├── test_model.rb
│ │ │ │ ├── todo.rb
│ │ │ │ ├── todo_item.rb
│ │ │ │ ├── type_test.rb
│ │ │ │ └── user.rb
│ │ ├── other_classes
│ │ │ └── unloaded_class.rb
│ │ ├── policies
│ │ │ ├── auto_loader_test_classa_policy.rb
│ │ │ ├── auto_loader_test_classb_policy.rb
│ │ │ ├── auto_loader_test_classc_policy.rb
│ │ │ └── auto_loader_test_classd_policy.rb
│ │ └── views
│ │ │ ├── components.rb
│ │ │ ├── components
│ │ │ └── show.rb
│ │ │ └── layouts
│ │ │ └── application.html.erb
│ ├── bin
│ │ ├── bundle
│ │ ├── rails
│ │ ├── rake
│ │ └── setup
│ ├── config.ru
│ ├── config
│ │ ├── application.rb
│ │ ├── boot.rb
│ │ ├── cable.yml
│ │ ├── database.yml
│ │ ├── environment.rb
│ │ ├── environments
│ │ │ ├── development.rb
│ │ │ ├── production.rb
│ │ │ └── test.rb
│ │ ├── initializers
│ │ │ ├── assets.rb
│ │ │ ├── backtrace_silencers.rb
│ │ │ ├── cookies_serializer.rb
│ │ │ ├── filter_parameter_logging.rb
│ │ │ ├── inflections.rb
│ │ │ ├── mime_types.rb
│ │ │ ├── session_store.rb
│ │ │ ├── synchromesh.rb
│ │ │ └── wrap_parameters.rb
│ │ ├── locales
│ │ │ └── en.yml
│ │ ├── routes.rb
│ │ └── secrets.yml
│ ├── db
│ │ ├── development.sqlite3
│ │ ├── migrate
│ │ │ └── 20160731182106_create_test_models.rb
│ │ ├── schema.rb
│ │ └── seeds.rb
│ ├── lib
│ │ └── assets
│ │ │ └── .keep
│ └── public
│ │ ├── 404.html
│ │ ├── 422.html
│ │ ├── 500.html
│ │ └── favicon.ico
├── test_components.rb
└── vendor
│ └── es5-shim.min.js
├── terminal.md
└── work-in-progress-drinking.png
/.gitignore:
--------------------------------------------------------------------------------
1 | /.bundle/
2 | /.yardoc
3 | /_yardoc/
4 | /.byebug_history
5 | /coverage/
6 | /doc/
7 | /pkg/
8 | /spec/reports/
9 | tmp/
10 | spec/test_app/tmp/
11 | spec/test_app/db/test.sqlite3
12 | spec/test_app/log/test.log
13 | spec/test_app/log/development.log
14 | spec/test_app/Gemfile.lock
15 | /synchromesh-simple-poller-store
16 | /synchromesh-pusher-channel-store
17 | /examples/action-cable/rails_cache_dir/
18 | rails_cache_dir/
19 | react_prerendering_src.js
20 |
21 | .bundle/
22 | log/*.log
23 | pkg/
24 | reactive_record_test_app/db/*.sqlite3
25 | reactive_record_test_app/log/
26 | reactive_record_test_app/tmp/
27 | reactive_record_test_app/.sass-cache
28 | .DS_Store
29 | public/assets/*
30 |
31 | # ignore gems
32 | *.gem
33 |
34 | # ingore Idea
35 | .idea
36 | .vscode
37 |
--------------------------------------------------------------------------------
/.rspec:
--------------------------------------------------------------------------------
1 | --format documentation
2 | --color
3 |
--------------------------------------------------------------------------------
/.rubocop.yml:
--------------------------------------------------------------------------------
1 |
2 | Metrics/LineLength:
3 | Max: 100
4 |
5 | Style/MutableConstant:
6 | Enabled: false
7 |
8 | # Lint/LiteralInCondition:
9 | # Enabled: false
10 |
11 | Style/CommandLiteral:
12 | EnforcedStyle: mixed
13 |
14 | AllCops:
15 | TargetRubyVersion: 2.4.1
16 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | dist: trusty
2 | language: ruby
3 | cache: bundler
4 | rvm:
5 | - 2.4.4
6 | - 2.5.1
7 | - ruby-head
8 | services:
9 | - mysql
10 | env:
11 | - DRIVER=google-chrome TZ=Europe/Berlin
12 | before_install:
13 | - if [[ "$DRIVER" == "google-chrome" ]]; then wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | sudo apt-key add -; fi
14 | - if [[ "$DRIVER" == "google-chrome" ]]; then echo "deb http://dl.google.com/linux/chrome/deb/ stable main" | sudo tee /etc/apt/sources.list.d/google-chrome.list; fi
15 | - if [[ "$DRIVER" == "google-chrome" ]]; then sudo apt-get update -qq && sudo apt-get install -qq -y google-chrome-stable; fi
16 | - sudo apt-get install -qq -y fonts-liberation
17 | - gem install bundler
18 | before_script:
19 | - cd spec/test_app
20 | - bundle install --jobs=3 --retry=3
21 | - bundle exec rails db:setup
22 | - cd ../../
23 | - if [[ "$DRIVER" == "google-chrome" ]]; then chromedriver-update; fi
24 | - if [[ "$DRIVER" == "google-chrome" ]]; then ls -lR ~/.chromedriver-helper/; fi
25 | - if [[ "$DRIVER" == "google-chrome" ]]; then chromedriver --version; fi
26 | - if [[ "$DRIVER" == "google-chrome" ]]; then google-chrome --version; fi
27 | - if [[ "$DRIVER" == "google-chrome" ]]; then which chromedriver; fi
28 | - if [[ "$DRIVER" == "google-chrome" ]]; then which google-chrome; fi
29 | script: bundle exec rspec
30 | gemfile:
31 | - gemfiles/hyper-mesh.gemfile
32 | matrix:
33 | fast_finish: true
34 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 |
3 | All notable changes to this project will be documented in this file starting with v0.8.4.
4 | This project *tries* to adhere to [Semantic Versioning](http://semver.org/), even before v1.0.
5 |
6 | Changes are grouped as follows:
7 | - **Added** for new features.
8 | - **Changed** for changes in existing functionality.
9 | - **Deprecated** for once-stable features to be removed in upcoming releases.
10 | - **Removed** for deprecated features removed in this release.
11 | - **Fixed** for any bug fixes.
12 | - **Security** to invite users to upgrade in case of vulnerabilities.
13 |
14 |
20 |
21 | ## [0.5.3] - 2017-01-03
22 |
23 |
24 | ### Added
25 |
26 | - Fixed problem with synchronizing multiple requests to same record within one render cycle (#20)
27 | - Add *finder* methods. I.e. server side methods that return a single record using a custom query. (#12)
28 | - Allow `save` even if records are loading (#10)
29 | - `ReactiveRecord.Load` will automatically apply `.itself` to the final value of the load block (#9)
30 |
31 |
32 | ### Fixed
33 |
34 | - Can't create AR records from within Rake Tasks. (#20)
35 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Code of Conduct
2 |
3 | As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
4 |
5 | We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion.
6 |
7 | Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
8 |
9 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
10 |
11 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
12 |
13 | This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)
14 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 | gem "opal-jquery", git: "https://github.com/opal/opal-jquery.git", branch: "master"
3 | gem 'hyperloop-config', path: '../hyperloop-config'
4 | gem 'hyper-operation', path: '../hyper-operation'
5 | gemspec
6 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Mitch VanDuyn
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
32 |
33 | ## Hyper-Mesh GEM is part of Hyperloop GEMS family
34 |
35 | Hyper-Mesh GEM comes with the Hyperloop GEM.
36 |
37 | But if you want to install it separately, please install the [Hyper-model GEM](https://github.com/ruby-hyperloop/hyper-model).
38 |
39 | ## Community
40 |
41 | #### Getting Help
42 | Please **do not post** usage questions to GitHub Issues. For these types of questions use our [Gitter chatroom](https://gitter.im/ruby-hyperloop/chat) or [StackOverflow](http://stackoverflow.com/questions/tagged/hyperloop).
43 |
44 | #### Submitting Bugs and Enhancements
45 | [GitHub Issues](https://github.com/ruby-hyperloop/hyperloop/issues) is for suggesting enhancements and reporting bugs. Before submiting a bug make sure you do the following:
46 | * Check out our [contributing guide](https://github.com/ruby-hyperloop/hyperloop/blob/master/CONTRIBUTING.md) for info on our release cycle.
47 |
48 | ## License
49 |
50 | Hyperloop is released under the [MIT License](http://www.opensource.org/licenses/MIT).
51 |
52 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | require "bundler/gem_tasks"
2 | require "rspec/core/rake_task"
3 |
4 |
5 |
6 | task :spec do
7 | (1..7).each { |batch| Rake::Task["spec:batch#{batch}"].invoke rescue nil }
8 | end
9 |
10 | namespace :spec do
11 | task :prepare do
12 | sh %{bundle update}
13 | sh %{cd spec/test_app; bundle update; bundle exec rails db:setup} # may need ;bundle exec rails db:setup as well
14 | end
15 | (1..7).each do |batch|
16 | RSpec::Core::RakeTask.new(:"batch#{batch}") do |t|
17 | t.pattern = "spec/batch#{batch}/**/*_spec.rb"
18 | end
19 | end
20 | end
21 |
22 | task :default => :spec
23 |
--------------------------------------------------------------------------------
/bin/console:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | require "bundler/setup"
4 | require "hyper-mesh"
5 |
6 | # You can add fixtures and/or initialization code here to make experimenting
7 | # with your gem easier. You can also use a different console, if you like.
8 |
9 | # (If you use this, don't forget to add pry to your Gemfile!)
10 | # require "pry"
11 | # Pry.start
12 |
13 | require "irb"
14 | IRB.start
15 |
--------------------------------------------------------------------------------
/bin/setup:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -euo pipefail
3 | IFS=$'\n\t'
4 |
5 | bundle install
6 |
7 | # Do any other automated setup that you need to do here
8 |
--------------------------------------------------------------------------------
/codeship.database.yml:
--------------------------------------------------------------------------------
1 | development:
2 | adapter: mysql2
3 | host: localhost
4 | encoding: utf8
5 | pool: 10
6 | username: <%= ENV['MYSQL_USER'] %>
7 | password: <%= ENV['MYSQL_PASSWORD'] %>
8 | database: development<%= ENV['TEST_ENV_NUMBER'] %>
9 | socket: /var/run/mysqld/mysqld.sock
10 | test:
11 | adapter: mysql2
12 | host: localhost
13 | encoding: utf8
14 | pool: 10
15 | username: <%= ENV['MYSQL_USER'] %>
16 | password: <%= ENV['MYSQL_PASSWORD'] %>
17 | database: test<%= ENV['TEST_ENV_NUMBER'] %>
18 | socket: /var/run/mysqld/mysqld.sock
19 |
--------------------------------------------------------------------------------
/dciy.toml:
--------------------------------------------------------------------------------
1 | [dciy.commands]
2 | prepare = ["rake spec:prepare"]
3 | cibuild = ["bundle exec rake"]
4 |
--------------------------------------------------------------------------------
/gemfiles/hyper-mesh.gemfile:
--------------------------------------------------------------------------------
1 | # This file was generated by Appraisal
2 |
3 | source "https://rubygems.org"
4 | gem "opal-jquery", git: "https://github.com/opal/opal-jquery.git", branch: "master"
5 | gemspec :path => "../"
6 |
--------------------------------------------------------------------------------
/hyper-mesh.gemspec:
--------------------------------------------------------------------------------
1 | # -*- encoding: utf-8 -*-
2 | $:.push File.expand_path('../lib/', __FILE__)
3 | require 'hypermesh/version'
4 |
5 | Gem::Specification.new do |spec|
6 | spec.name = 'hyper-mesh'
7 | spec.version = Hypermesh::VERSION
8 | spec.authors = ['Mitch VanDuyn', 'Jan Biedermann']
9 | spec.email = ['mitch@catprint.com', 'jan@kursator.com']
10 | spec.summary = 'React based CRUD access and Synchronization of active record models across multiple clients'
11 | spec.description = 'HyperMesh is the base for HyperModel. HyperModel gives your HyperComponents CRUD access to your '\
12 | 'ActiveRecord models on the client, using the the standard ActiveRecord '\
13 | 'API. HyperModel also implements push notifications (via a number of '\
14 | 'possible technologies) so changes to records on the server are '\
15 | 'dynamically updated on all authorised clients.'
16 | spec.homepage = 'http://ruby-hyperloop.org'
17 | spec.license = 'MIT'
18 | # spec.metadata = {
19 | # "homepage_uri" => 'http://ruby-hyperloop.org',
20 | # "source_code_uri" => 'https://github.com/ruby-hyperloop/hyper-component'
21 | # }
22 |
23 | spec.files = `git ls-files`.split("\n").reject { |f| f.match(%r{^(examples|gemfiles|pkg|reactive_record_test_app|spec)/}) }
24 | # spec.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
25 | spec.test_files = `git ls-files -- {spec}/*`.split("\n")
26 | spec.require_paths = ['lib']
27 |
28 | spec.add_dependency 'activerecord', '>= 4.0.0'
29 | spec.add_dependency 'hyper-component', Hypermesh::VERSION
30 | spec.add_dependency 'hyper-operation', Hypermesh::VERSION
31 | spec.add_development_dependency 'bundler'
32 | spec.add_development_dependency 'capybara'
33 | spec.add_development_dependency 'chromedriver-helper'
34 | spec.add_development_dependency 'database_cleaner'
35 | spec.add_development_dependency 'factory_bot_rails'
36 | spec.add_development_dependency 'hyper-spec', Hypermesh::VERSION
37 | spec.add_development_dependency 'hyper-trace'
38 | spec.add_development_dependency 'mysql2'
39 | spec.add_development_dependency 'opal-activesupport', '~> 0.3.1'
40 | spec.add_development_dependency 'opal-browser', '~> 0.2.0'
41 | spec.add_development_dependency 'opal-rails', '~> 0.9.4'
42 | spec.add_development_dependency 'parser'
43 | spec.add_development_dependency 'puma'
44 | spec.add_development_dependency 'pusher'
45 | spec.add_development_dependency 'pusher-fake'
46 | spec.add_development_dependency 'rails', '>= 4.0.0'
47 | spec.add_development_dependency 'rake'
48 | spec.add_development_dependency 'react-rails', '>= 2.4.0', '< 2.5.0'
49 | spec.add_development_dependency 'reactrb-rails-generator'
50 | spec.add_development_dependency 'rspec-collection_matchers'
51 | spec.add_development_dependency 'rspec-expectations'
52 | spec.add_development_dependency 'rspec-its'
53 | spec.add_development_dependency 'rspec-mocks'
54 | spec.add_development_dependency 'rspec-rails'
55 | spec.add_development_dependency 'rspec-steps', '~> 2.1.1'
56 | spec.add_development_dependency 'rspec-wait'
57 | spec.add_development_dependency 'rubocop', '~> 0.51.0'
58 | spec.add_development_dependency 'shoulda'
59 | spec.add_development_dependency 'shoulda-matchers'
60 | spec.add_development_dependency 'spring-commands-rspec'
61 | spec.add_development_dependency 'sqlite3'
62 | spec.add_development_dependency 'mini_racer', '~> 0.1.15'
63 | # https://github.com/discourse/mini_racer/issues/92
64 | spec.add_development_dependency 'libv8', '~> 6.3.0'
65 | spec.add_development_dependency 'timecop', '~> 0.8.1'
66 | spec.add_development_dependency 'unparser'
67 | spec.add_development_dependency 'pry'
68 | spec.add_development_dependency 'pry-rescue'
69 | end
70 |
--------------------------------------------------------------------------------
/lib/acts_as_string.rb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ruby-hyperloop/hyper-mesh/d29f00b16efbdd25419f07c7590505de4d3ba42a/lib/acts_as_string.rb
--------------------------------------------------------------------------------
/lib/enumerable/pluck.rb:
--------------------------------------------------------------------------------
1 | # Add pluck to enumerable... its already done for us in rails 5+
2 | module Enumerable
3 | def pluck(key)
4 | map { |element| element[key] }
5 | end
6 | end unless Enumerable.method_defined? :pluck
7 |
--------------------------------------------------------------------------------
/lib/hyper-mesh.rb:
--------------------------------------------------------------------------------
1 | require 'set'
2 | require 'hyperloop-config'
3 | require 'hyper-component'
4 | if RUBY_ENGINE == 'opal'
5 | require 'hyper-operation'
6 | require 'active_support'
7 | require 'time'
8 | require 'date'
9 | require 'kernel/itself' unless Object.instance_methods.include?(:itself)
10 | require 'object/tap'
11 | require "reactive_record/active_record_error"
12 | require "reactive_record/active_record/errors"
13 | require "reactive_record/active_record/error"
14 | require "reactive_record/server_data_cache"
15 | require "reactive_record/active_record/reactive_record/while_loading"
16 | require "reactive_record/active_record/reactive_record/operations"
17 | require 'reactive_record/broadcast'
18 | require "reactive_record/active_record/reactive_record/isomorphic_base"
19 | require 'reactive_record/active_record/reactive_record/dummy_value'
20 | require 'reactive_record/active_record/reactive_record/column_types'
21 | require "reactive_record/active_record/aggregations"
22 | require "reactive_record/active_record/associations"
23 | require "reactive_record/active_record/reactive_record/backing_record_inspector"
24 | require "reactive_record/active_record/reactive_record/getters"
25 | require "reactive_record/active_record/reactive_record/setters"
26 | require "reactive_record/active_record/reactive_record/lookup_tables"
27 | require "reactive_record/active_record/reactive_record/base"
28 | require "reactive_record/active_record/reactive_record/collection"
29 | require "reactive_record/active_record/reactive_record/scoped_collection"
30 | require "reactive_record/active_record/reactive_record/unscoped_collection"
31 | require "reactive_record/interval"
32 | require_relative 'active_record_base'
33 | require_relative 'reactive_record/scope_description'
34 | require "reactive_record/active_record/class_methods"
35 | require "reactive_record/active_record/instance_methods"
36 | require "reactive_record/active_record/base"
37 | require 'hyper_react/input_tags'
38 | require_relative 'hypermesh/version'
39 | require_relative 'opal/parse_patch'
40 | require_relative 'opal/set_patches'
41 | require_relative 'opal/equality_patches'
42 | React::IsomorphicHelpers.log(
43 | "The gem 'hyper-mesh' is deprecated. Use gem 'hyper-model' instead.", :warning
44 | ) unless defined? Hyperloop::Model
45 | else
46 | require 'opal'
47 | require 'hyper-operation'
48 | require "reactive_record/permissions"
49 | require "reactive_record/server_data_cache"
50 | require "reactive_record/active_record/reactive_record/operations"
51 | require 'reactive_record/broadcast'
52 | require "reactive_record/active_record/reactive_record/isomorphic_base"
53 | require "reactive_record/active_record/public_columns_hash"
54 | require "reactive_record/serializers"
55 | require "reactive_record/pry"
56 | require_relative 'active_record_base'
57 | require 'hypermesh/version'
58 |
59 | Opal.append_path File.expand_path('../sources/', __FILE__).untaint
60 | Opal.append_path File.expand_path('../', __FILE__).untaint
61 | Opal.append_path File.expand_path('../../vendor', __FILE__).untaint
62 | end
63 | require 'enumerable/pluck'
64 |
--------------------------------------------------------------------------------
/lib/hyper_react/input_tags.rb:
--------------------------------------------------------------------------------
1 | # Special handling of input tags so they ignore defaultValue (and defaultChecked) values while loading.
2 | # This is accomplished by adding a react 'key prop' that tracks whether the default value is loading.
3 | # When the default value transitions from loading to loaded the key will be updated causing react to
4 | # remount the component with the new default value.
5 | # To handle cases where defaultValue (or defaultChecked) is an expression, a proc (or lambda) can be
6 | # provided for the default value. The proc will be called, and if it raises the waiting_on_resources
7 | # flag then we know that within that expression there is a value still being loaded, and the react
8 | # key will be set accordingly.
9 |
10 | module React
11 | module Component
12 | module Tags
13 | %i[INPUT SELECT TEXTAREA].each do |component|
14 | remove_method component
15 | send(:remove_const, component)
16 | tag = component.downcase
17 | klass = Class.new(Hyperloop::Component) do
18 | collect_other_params_as :opts
19 | render do
20 | opts = props.dup # should be opts = params.opts.dup but requires next release candiate of hyper-react
21 | default_value = opts[:defaultValue] || opts[:defaultChecked]
22 | if default_value.respond_to? :call
23 | begin
24 | saved_waiting_on_resources = React::RenderingContext.waiting_on_resources
25 | React::RenderingContext.waiting_on_resources = false
26 | default_value = default_value.call
27 | opts[:key] = React::RenderingContext.waiting_on_resources
28 | if opts[:defaultValue]
29 | opts[:defaultValue] = default_value
30 | else
31 | opts[:defaultChecked] = default_value
32 | end
33 | ensure
34 | React::RenderingContext.waiting_on_resources = !!saved_waiting_on_resources
35 | end
36 | else
37 | opts[:key] = !!default_value.loading?
38 | end
39 | opts[:value] = opts[:value].to_s if opts.key? :value # this may not be needed
40 | React::RenderingContext.render(tag, opts) { children.each(&:render) }
41 | end
42 | end
43 | const_set component, klass
44 | end
45 | end
46 | end
47 | end
48 |
--------------------------------------------------------------------------------
/lib/hypermesh/version.rb:
--------------------------------------------------------------------------------
1 | module Hypermesh
2 | VERSION = '1.0.0.lap28'
3 | end
4 |
--------------------------------------------------------------------------------
/lib/kernel/itself.rb:
--------------------------------------------------------------------------------
1 | module Kernel
2 | def itself
3 | self
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/lib/object/tap.rb:
--------------------------------------------------------------------------------
1 | class Object
2 | def tap
3 | val = `self.$$is_boolean` ? self==true : self
4 | yield val
5 | val
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/lib/opal/equality_patches.rb:
--------------------------------------------------------------------------------
1 | class Date
2 | alias broken_equals ==
3 | def ==(other)
4 | return false unless other.is_a?(Date)
5 | broken_equals(other)
6 | end
7 | end
8 |
9 | class Time
10 | alias broken_equals ==
11 | def ==(other)
12 | return false unless other.is_a?(Time)
13 | broken_equals(other)
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/lib/opal/parse_patch.rb:
--------------------------------------------------------------------------------
1 | begin
2 | JSON.parse("test")
3 | rescue Exception => e
4 | JSON.class_eval do
5 | class << self
6 | alias old_parse parse
7 | end
8 | def self.parse(*args, &block)
9 | old_parse(*args, &block)
10 | rescue Exception => e
11 | raise StandardError.new e.message
12 | end
13 | end unless e.is_a? StandardError
14 | end
15 |
--------------------------------------------------------------------------------
/lib/opal/set_patches.rb:
--------------------------------------------------------------------------------
1 | class Set
2 | def &(enum)
3 | n = self.class.new
4 | enum.each { |o| n.add(o) if include?(o) }
5 | n
6 | end
7 | alias intersection &
8 | end unless Set.method_defined? :intersection
9 |
--------------------------------------------------------------------------------
/lib/reactive_record/active_record/aggregations.rb:
--------------------------------------------------------------------------------
1 | module ActiveRecord
2 |
3 | class Base
4 |
5 | def self.reflect_on_all_aggregations
6 | base_class.instance_eval { @aggregations ||= [] }
7 | end
8 |
9 | def self.reflect_on_aggregation(attribute)
10 | reflect_on_all_aggregations.detect { |aggregation| aggregation.attribute == attribute }
11 | end
12 |
13 | end
14 |
15 | module Aggregations
16 |
17 | class AggregationReflection
18 |
19 | attr_reader :klass_name
20 | attr_reader :attribute
21 | attr_reader :mapped_attributes
22 | attr_reader :constructor
23 |
24 | def construct(args)
25 |
26 | end
27 |
28 | def initialize(owner_class, macro, name, options = {})
29 | owner_class.reflect_on_all_aggregations << self
30 | @owner_class = owner_class
31 | @constructor = options[:constructor] || :new
32 | @klass_name = options[:class_name] || name.camelize
33 | @attribute = name
34 | if options[:mapping].respond_to? :collect
35 | @mapped_attributes = options[:mapping].collect(&:last)
36 | else
37 | ReactiveRecord::Base.log("improper aggregate definition #{@owner_class}, :#{name}, class_name: #{@klass_name} - missing mapping", :error)
38 | @mapped_attributes = []
39 | end
40 | end
41 |
42 | def klass
43 | @klass ||= Object.const_get(@klass_name)
44 | end
45 |
46 | def serialize(object)
47 | if object.nil?
48 | object # return dummy value if that is what we got
49 | else
50 | @mapped_attributes.collect { |attr| object.send(attr) }
51 | end
52 | end
53 |
54 | def deserialize(array)
55 | if array.nil?
56 | array # return dummy value if that is what we got
57 | elsif @constructor.respond_to?(:call)
58 | @constructor.call(*array)
59 | else
60 | klass.send(@constructor, *array)
61 | end
62 | end
63 |
64 | end
65 |
66 | end
67 |
68 |
69 | end
70 |
--------------------------------------------------------------------------------
/lib/reactive_record/active_record/associations.rb:
--------------------------------------------------------------------------------
1 | module ActiveRecord
2 |
3 | class Base
4 |
5 | def self.reflect_on_all_associations
6 | base_class.instance_eval { @associations ||= superclass.instance_eval { (@associations && @associations.dup) || [] } }
7 | end
8 |
9 | def self.reflect_on_association(attr)
10 | reflection_finder { |assoc| assoc.attribute == attr }
11 | end
12 |
13 | def self.reflect_on_association_by_foreign_key(key)
14 | reflection_finder { |assoc| assoc.association_foreign_key == key }
15 | end
16 |
17 | def self.reflection_finder(&block)
18 | found = reflect_on_all_associations.detect do |assoc|
19 | assoc.owner_class == self && yield(assoc)
20 | end
21 | if found
22 | found
23 | elsif superclass == Base
24 | nil
25 | else
26 | superclass.reflection_finder(&block)
27 | end
28 | end
29 |
30 | end
31 |
32 | module Associations
33 |
34 | class AssociationReflection
35 |
36 | attr_reader :association_foreign_key
37 | attr_reader :attribute
38 | attr_reader :macro
39 | attr_reader :owner_class
40 | attr_reader :source
41 |
42 | def initialize(owner_class, macro, name, options = {})
43 | owner_class.reflect_on_all_associations << self
44 | @owner_class = owner_class
45 | @macro = macro
46 | @options = options
47 | @klass_name = options[:class_name] || (collection? && name.camelize.sub(/s$/, '')) || name.camelize
48 | @association_foreign_key = options[:foreign_key] || (macro == :belongs_to && "#{name}_id") || "#{@owner_class.name.underscore}_id"
49 | @source = options[:source] || @klass_name.underscore if options[:through]
50 | @attribute = name
51 | end
52 |
53 | def through_association
54 | return unless @options[:through]
55 | @through_association ||= @owner_class.reflect_on_all_associations.detect do |association|
56 | association.attribute == @options[:through]
57 | end
58 | raise "Through association #{@options[:through]} for "\
59 | "#{@owner_class}.#{attribute} not found." unless @through_association
60 | @through_association
61 | end
62 |
63 | alias through_association? through_association
64 |
65 | def through_associations
66 | # find all associations that use the inverse association as the through association
67 | # that is find all associations that are using this association in a through relationship
68 | @through_associations ||= klass.reflect_on_all_associations.select do |assoc|
69 | assoc.through_association && assoc.inverse == self
70 | end
71 | end
72 |
73 | def source_associations
74 | # find all associations that use this association as the source
75 | # that is final all associations that are using this association as the source in a
76 | # through relationship
77 | @source_associations ||= owner_class.reflect_on_all_associations.collect do |sibling|
78 | sibling.klass.reflect_on_all_associations.select do |assoc|
79 | assoc.source == attribute
80 | end
81 | end.flatten
82 | end
83 |
84 | def inverse
85 | @inverse ||=
86 | through_association ? through_association.inverse : find_inverse
87 | end
88 |
89 | def inverse_of
90 | @inverse_of ||= inverse.attribute
91 | end
92 |
93 | def find_inverse
94 | klass.reflect_on_all_associations.each do |association|
95 | next if association.association_foreign_key != @association_foreign_key
96 | next if association.klass != @owner_class
97 | next if association.attribute == attribute
98 | return association if klass == association.owner_class
99 | end
100 | raise "Association #{@owner_class}.#{attribute} "\
101 | "(foreign_key: #{@association_foreign_key}) "\
102 | "has no inverse in #{@klass_name}"
103 | end
104 |
105 | def klass
106 | @klass ||= Object.const_get(@klass_name)
107 | end
108 |
109 | def collection?
110 | [:has_many].include? @macro
111 | end
112 |
113 | end
114 |
115 | end
116 |
117 |
118 | end
119 |
--------------------------------------------------------------------------------
/lib/reactive_record/active_record/base.rb:
--------------------------------------------------------------------------------
1 | module ActiveRecord
2 | # client side ActiveRecord::Base proxy
3 | class Base
4 | include InstanceMethods
5 | extend ClassMethods
6 |
7 | scope :limit, ->() {}
8 | scope :offset, ->() {}
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/lib/reactive_record/active_record/error.rb:
--------------------------------------------------------------------------------
1 | module ActiveModel
2 | class Error
3 |
4 | attr_reader :messages
5 |
6 | def initialize(initial_msgs = {})
7 | @messages = Hash.new { |h, k| h[k] = [] }
8 | initial_msgs.each { |attr, msgs| @messages[attr] = msgs.uniq }
9 | end
10 |
11 | def [](attribute)
12 | messages[attribute]
13 | end
14 |
15 | def delete(attribute)
16 | messages.delete(attribute)
17 | end
18 |
19 | def empty?
20 | messages.empty?
21 | end
22 |
23 | def clear
24 | @messages.clear
25 | end
26 |
27 | def add(attribute, message = :invalid, _options = {})
28 | @messages[attribute] << message unless @messages[attribute].include? message
29 | end
30 | end
31 | end
32 |
--------------------------------------------------------------------------------
/lib/reactive_record/active_record/instance_methods.rb:
--------------------------------------------------------------------------------
1 | module ActiveRecord
2 | module InstanceMethods
3 | def inspect
4 | "<#{model_name}:#{ReactiveRecord::Operations::Base::FORMAT % to_key} "\
5 | "(#{ReactiveRecord::Operations::Base::FORMAT % object_id}) "\
6 | "#{backing_record.inspection_details} >"
7 | end
8 |
9 | attr_reader :backing_record
10 |
11 | def attributes
12 | @backing_record.attributes
13 | end
14 |
15 | def changed_attributes
16 | backing_record.changed_attributes_and_values
17 | end
18 |
19 | def changes
20 | backing_record.changes
21 | end
22 |
23 | def initialize(hash = {})
24 | if hash.is_a? ReactiveRecord::Base
25 | @backing_record = hash
26 | else
27 | # standard active_record new -> creates a new instance, primary key is ignored if present
28 | # we have to build the backing record first then initialize it so associations work correctly
29 | @backing_record = ReactiveRecord::Base.new(self.class, {}, self)
30 | if self.class.inheritance_column && !hash.key?(self.class.inheritance_column)
31 | hash[self.class.inheritance_column] = self.class.name
32 | end
33 | @backing_record.instance_eval do
34 | h = {}
35 | hash.each do |a, v|
36 | a = model._dealias_attribute(a)
37 | h[a] = convert(a, v).itself
38 | end
39 | self.class.load_data do
40 | h.each do |attribute, value|
41 | next if attribute == primary_key
42 | @ar_instance[attribute] = value
43 | changed_attributes << attribute
44 | end
45 | end
46 | end
47 | end
48 | end
49 |
50 | def primary_key
51 | self.class.primary_key
52 | end
53 |
54 | def id
55 | @backing_record.get_primary_key_value
56 | end
57 |
58 | def id=(value)
59 | @backing_record.id = value
60 | end
61 |
62 | def id?
63 | id.present?
64 | end
65 |
66 | def model_name
67 | # in reality should return ActiveModel::Name object, blah blah
68 | self.class.model_name
69 | end
70 |
71 | def revert
72 | @backing_record.revert
73 | end
74 |
75 | def changed?
76 | @backing_record.changed?
77 | end
78 |
79 | def dup
80 | self.class.new(self.attributes)
81 | end
82 |
83 | def ==(ar_instance)
84 | @backing_record == ar_instance.instance_eval { @backing_record }
85 | end
86 |
87 | def [](attr)
88 | send(attr)
89 | end
90 |
91 | def []=(attr, val)
92 | send("#{attr}=", val)
93 | end
94 |
95 | def itself
96 | # this is useful when you just want to get a handle on record instance
97 | # in the ReactiveRecord.load method
98 | id # force load of id...
99 | # if self.class.columns_hash.keys.include?(self.class.inheritance_column) &&
100 | # (klass = self[self.class.inheritance_column]).loaded?
101 | # Object.const_get(klass).new(attributes)
102 | # else
103 | self
104 | # end
105 | end
106 |
107 | def load(*attributes, &block)
108 | first_time = true
109 | ReactiveRecord.load do
110 | results = attributes.collect { |attr| send("#{attr}#{'!' if first_time}") }
111 | results = yield(*results) if block
112 | first_time = false
113 | block.nil? && results.count == 1 ? results.first : results
114 | end
115 | end
116 |
117 | def save(opts = {}, &block)
118 | @backing_record.save_or_validate(true, opts.has_key?(:validate) ? opts[:validate] : true, opts[:force], &block)
119 | end
120 |
121 | def validate(opts = {}, &block)
122 | @backing_record.save_or_validate(false, true, opts[:force]).then do
123 | if block
124 | yield @backing_record.ar_instance
125 | else
126 | @backing_record.ar_instance
127 | end
128 | end
129 | end
130 |
131 | def valid?
132 | errors.reactive_empty?
133 | end
134 |
135 | def saving?
136 | @backing_record.saving?
137 | end
138 |
139 | def destroy(&block)
140 | @backing_record.destroy(&block)
141 | end
142 |
143 | def destroyed?
144 | @backing_record.destroyed
145 | end
146 |
147 | def new?
148 | @backing_record.new?
149 | end
150 |
151 | def errors
152 | React::State.get_state(@backing_record, @backing_record)
153 | @backing_record.errors
154 | end
155 |
156 | def to_key
157 | @backing_record.object_id
158 | end
159 |
160 | def update_attribute(attr, value, &block)
161 | send("#{attr}=", value)
162 | save(validate: false, &block)
163 | end
164 |
165 | def update(attrs = {}, &block)
166 | attrs.each { |attr, value| send("#{attr}=", value) }
167 | save(&block)
168 | end
169 |
170 | def <=>(other)
171 | id.to_i <=> other.id.to_i
172 | end
173 |
174 | def becomes(klass)
175 | klass._new_without_sti_type_cast(backing_record)
176 | end
177 |
178 | def becomes!(klass)
179 | self[self.class.inheritance_column] = klass.name
180 | becomes(klass)
181 | end
182 |
183 | def cast_to_current_sti_type
184 | @backing_record.set_ar_instance!
185 | end
186 | end
187 | end
188 |
--------------------------------------------------------------------------------
/lib/reactive_record/active_record/public_columns_hash.rb:
--------------------------------------------------------------------------------
1 | module Hyperloop
2 | define_setting :public_model_directories, [File.join('app','hyperloop','models'), File.join('app','models','public')]
3 | end
4 |
5 | module ActiveRecord
6 | # adds method to get the HyperMesh public column types
7 | # this works because the public folder is currently required to be eager loaded.
8 | class Base
9 | def self.public_columns_hash
10 | return @public_columns_hash if @public_columns_hash && Rails.env.production?
11 | files = []
12 | Hyperloop.public_model_directories.each do |dir|
13 | dir_length = Rails.root.join(dir).to_s.length + 1
14 | Dir.glob(Rails.root.join(dir, '**', '*.rb')).each do |file|
15 | require_dependency(file) # still the file is loaded to make sure for development and test env
16 | files << file[dir_length..-4]
17 | end
18 | end
19 | @public_columns_hash = {}
20 | # descendants only works for already loaded models!
21 | descendants.each do |model|
22 | if files.include?(model.name.underscore) && model.name.underscore != 'application_record'
23 | @public_columns_hash[model.name] = model.columns_hash rescue nil # why rescue?
24 | end
25 | # begin
26 | # @public_columns_hash[model.name] = model.columns_hash if model.table_name
27 | # rescue Exception => e
28 | # binding.pry
29 | # @public_columns_hash = nil
30 | # raise $!, "Could not read 'columns_hash' for #{model}: #{$!}", $!.backtrace
31 | # end if files.include?(model.name.underscore) && model.name.underscore != 'application_record'
32 | end
33 | @public_columns_hash
34 | end
35 |
36 | def self.public_columns_hash_as_json
37 | return @public_columns_hash_json if @public_columns_hash_json && Rails.env.production?
38 | pch = public_columns_hash
39 | return @public_columns_hash_json if @prev_public_columns_hash == pch
40 | @prev_public_columns_hash = pch
41 | @public_columns_hash_json = pch.to_json
42 | end
43 | end
44 | end
45 |
--------------------------------------------------------------------------------
/lib/reactive_record/active_record/reactive_record/backing_record_inspector.rb:
--------------------------------------------------------------------------------
1 | module ReactiveRecord
2 | # inspection_details is used by client side ActiveRecord::Base
3 | # runs down the possible states of a backing record and returns
4 | # the appropriate string. The order of execution is important!
5 | module BackingRecordInspector
6 | def inspection_details
7 | return error_details unless errors.empty?
8 | return new_details if new?
9 | return destroyed_details if destroyed
10 | return loading_details unless @attributes.key? primary_key
11 | return dirty_details unless changed_attributes.empty?
12 | "[loaded id: #{id}]"
13 | end
14 |
15 | def error_details
16 | id_str = "id: #{id} " unless new?
17 | "[errors #{id_str}#{errors.messages}]"
18 | end
19 |
20 | def new_details
21 | "[new #{attributes.select { |attr| column_type(attr) }}]"
22 | end
23 |
24 | def destroyed_details
25 | "[destroyed id: #{id}]"
26 | end
27 |
28 | def loading_details
29 | "[loading #{vector}]"
30 | end
31 |
32 | def dirty_details
33 | "[changed id: #{id} #{changes}]"
34 | end
35 | end
36 | end
37 |
--------------------------------------------------------------------------------
/lib/reactive_record/active_record/reactive_record/column_types.rb:
--------------------------------------------------------------------------------
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 self.column_type(column_hash)
9 | column_hash && column_hash[:sql_type_metadata] && column_hash[:sql_type_metadata][:type]
10 | end
11 |
12 | def column_type(attr)
13 | Base.column_type(columns_hash[attr])
14 | end
15 |
16 | def convert_datetime(val)
17 | if val.is_a?(Numeric)
18 | Time.at(val)
19 | elsif val.is_a?(Time)
20 | val
21 | else
22 | Time.parse(val)
23 | end
24 | end
25 |
26 | alias convert_time convert_datetime
27 | alias convert_timestamp convert_datetime
28 |
29 | def convert_date(val)
30 | if val.is_a?(Time)
31 | Date.parse(val.strftime('%d/%m/%Y'))
32 | elsif val.is_a?(Date)
33 | val
34 | else
35 | Date.parse(val)
36 | end
37 | end
38 |
39 | def convert_boolean(val)
40 | !['false', false, nil, 0].include?(val)
41 | end
42 |
43 | def convert_integer(val)
44 | Integer(`parseInt(#{val})`)
45 | end
46 |
47 | alias convert_bigint convert_integer
48 |
49 | def convert_float(val)
50 | Float(val)
51 | end
52 |
53 | alias convert_decimal convert_float
54 |
55 | def convert_text(val)
56 | val.to_s
57 | end
58 |
59 | alias convert_string convert_text
60 |
61 | def self.serialized?
62 | @serialized_attrs ||= Hash.new { |h, k| h[k] = Hash.new }
63 | end
64 |
65 | def convert(attr, val)
66 | column_type = column_type(attr)
67 | return val if self.class.serialized?[model][attr] ||
68 | !column_type || val.loading? ||
69 | (!val && column_type != :boolean)
70 | conversion_method = "convert_#{column_type}"
71 | return send(conversion_method, val) if respond_to? conversion_method
72 | val
73 | end
74 | end
75 | end
76 |
--------------------------------------------------------------------------------
/lib/reactive_record/active_record/reactive_record/lookup_tables.rb:
--------------------------------------------------------------------------------
1 | module ReactiveRecord
2 | module LookupTables
3 | def initialize_lookup_tables
4 | @records = Hash.new { |hash, key| hash[key] = [] }
5 | @records_by_id = `{}`
6 | @records_by_vector = `{}`
7 | @records_by_object_id = `{}`
8 | @class_scopes = Hash.new { |hash, key| hash[key] = {} }
9 | @waiting_for_save = Hash.new { |hash, key| hash[key] = [] }
10 | end
11 |
12 | def class_scopes(model)
13 | @class_scopes[model.base_class]
14 | end
15 |
16 | def waiting_for_save(model)
17 | @waiting_for_save[model]
18 | end
19 |
20 | def wait_for_save(model, &block)
21 | @waiting_for_save[model] << block
22 | end
23 |
24 | def clear_waiting_for_save(model)
25 | @waiting_for_save[model] = []
26 | end
27 |
28 | def lookup_by_object_id(object_id)
29 | `#{@records_by_object_id}[#{object_id}]`.ar_instance
30 | end
31 |
32 | def set_object_id_lookup(record)
33 | `#{@records_by_object_id}[#{record.object_id}] = #{record}`
34 | end
35 |
36 | def lookup_by_id(*args) # model and id
37 | `#{@records_by_id}[#{args}]` || nil
38 | end
39 |
40 | def set_id_lookup(record)
41 | `#{@records_by_id}[#{[record.model, record.id]}] = #{record}`
42 | end
43 |
44 | def lookup_by_vector(vector)
45 | `#{@records_by_vector}[#{vector}]` || nil
46 | end
47 |
48 | def set_vector_lookup(record, vector)
49 | record.vector = vector
50 | `delete #{@records_by_vector}[#{record.vector}]`
51 | `#{@records_by_vector}[#{vector}] = record`
52 | end
53 | end
54 | end
55 |
--------------------------------------------------------------------------------
/lib/reactive_record/active_record/reactive_record/operations.rb:
--------------------------------------------------------------------------------
1 | module ReactiveRecord
2 | # redefine if you want to process errors (i.e. logging, rollbar, etc)
3 | def self.on_fetch_error(e, params); end
4 |
5 | # associations: {parent_id: record.object_id, attribute: attribute, child_id: assoc_record.object_id}
6 | # models: {id: record.object_id, model: record.model.model_name, attributes: changed_attributes}
7 |
8 | module Operations
9 | # to make debug easier we convert all the object_id strings to be hex representation
10 | class Base < Hyperloop::ControllerOp
11 | param :acting_user, nils: true
12 |
13 | FORMAT = '0x%x'
14 |
15 | def self.serialize_params(hash)
16 | hash['associations'].each do |assoc|
17 | assoc['parent_id'] = FORMAT % assoc['parent_id']
18 | assoc['child_id'] = FORMAT % assoc['child_id']
19 | end if hash['associations']
20 | hash['models'].each do |assoc|
21 | assoc['id'] = FORMAT % assoc[:id]
22 | end if hash['models']
23 | hash
24 | end
25 |
26 | def self.deserialize_params(hash)
27 | hash['associations'].each do |assoc|
28 | assoc['parent_id'] = assoc['parent_id'].to_i(16)
29 | assoc['child_id'] = assoc['child_id'].to_i(16)
30 | end if hash['associations']
31 | hash['models'].each do |assoc|
32 | assoc['id'] = assoc['id'].to_i(16)
33 | end if hash['models']
34 | hash
35 | end
36 |
37 | def self.serialize_response(response)
38 | response[:saved_models].each do |saved_model|
39 | saved_model[0] = FORMAT % saved_model[0]
40 | end if response.is_a?(Hash) && response[:saved_models]
41 | response
42 | end
43 |
44 | def self.deserialize_response(response)
45 | response[:saved_models].each do |saved_model|
46 | saved_model[0] = saved_model[0].to_i(16)
47 | end if response.is_a?(Hash) && response[:saved_models]
48 | response
49 | end
50 | end
51 | # fetch queued up records from the server
52 | # subclass of ControllerOp so we can pass the controller
53 | # along to on_error
54 | class Fetch < Base
55 | param :acting_user, nils: true
56 | param models: []
57 | param associations: []
58 | param :pending_fetches
59 | step do
60 | ReactiveRecord::ServerDataCache[
61 | params.models.map(&:with_indifferent_access),
62 | params.associations.map(&:with_indifferent_access),
63 | params.pending_fetches,
64 | params.acting_user
65 | ]
66 | end
67 | failed do |e|
68 | # AccessViolations are already sent to on_error
69 | Hyperloop.on_error(e, :fetch_error, params.to_h) unless e.is_a? Hyperloop::AccessViolation
70 | raise e
71 | end
72 | end
73 |
74 | class Save < Base
75 | param :acting_user, nils: true
76 | param models: []
77 | param associations: []
78 | param :save, type: :boolean
79 | param :validate, type: :boolean
80 |
81 | step do
82 | ReactiveRecord::Base.save_records(
83 | params.models.map(&:with_indifferent_access),
84 | params.associations.map(&:with_indifferent_access),
85 | params.acting_user,
86 | params.validate,
87 | params.save
88 | )
89 | end
90 | end
91 |
92 | class Destroy < Base
93 | param :acting_user, nils: true
94 | param :model
95 | param :id
96 | param :vector
97 | step do
98 | ReactiveRecord::Base.destroy_record(
99 | params.model,
100 | params.id,
101 | params.vector,
102 | params.acting_user
103 | )
104 | end
105 | end
106 | end
107 | end
108 |
--------------------------------------------------------------------------------
/lib/reactive_record/active_record/reactive_record/scoped_collection.rb:
--------------------------------------------------------------------------------
1 | module ReactiveRecord
2 | # The base collection class works with relationships
3 | # method overrides for scoped collections
4 | module ScopedCollection
5 | [:filter?, :collector?, :joins_with?, :related_records_for].each do |method|
6 | define_method(method) { |*args| @scope_description.send method, *args }
7 | end
8 |
9 | def set_pre_sync_related_records(related_records, _record = nil)
10 | @pre_sync_related_records = nil
11 | ReactiveRecord::Base.catch_db_requests do
12 | @pre_sync_related_records = filter_records(related_records)
13 | live_scopes.each do |scope|
14 | scope.set_pre_sync_related_records(@pre_sync_related_records)
15 | end
16 | end if filter?
17 | end
18 |
19 | def sync_scopes(related_records, record, filtering = true)
20 | filtering =
21 | @pre_sync_related_records && filtering &&
22 | ReactiveRecord::Base.catch_db_requests do
23 | related_records = update_collection(related_records)
24 | end
25 | reload_from_db if !filtering && joins_with?(record)
26 | live_scopes.each { |scope| scope.sync_scopes(related_records, record, filtering) }
27 | ensure
28 | @pre_sync_related_records = nil
29 | end
30 |
31 | def update_collection(related_records)
32 | if collector?
33 | update_collector_scope(related_records)
34 | else
35 | related_records = filter_records(related_records)
36 | update_filter_scope(@pre_sync_related_records, related_records)
37 | end
38 | end
39 |
40 | def update_collector_scope(related_records)
41 | current = Set.new([*@collection])
42 | (related_records - @pre_sync_related_records).each { |r| current << r }
43 | (@pre_sync_related_records - related_records).each { |r| current.delete(r) }
44 | replace(filter_records(current))
45 | Set.new([*@collection])
46 | end
47 |
48 | def update_filter_scope(before, after)
49 | if (collection || !@count.nil?) && before != after
50 | if collection
51 | (after - before).each { |r| push r }
52 | (before - after).each { |r| delete r }
53 | else
54 | @count += (after - before).count
55 | @count -= (before - after).count
56 | notify_of_change self # TODO: remove self .... and retest
57 | end
58 | end
59 | after
60 | end
61 | end
62 | end
63 |
--------------------------------------------------------------------------------
/lib/reactive_record/active_record/reactive_record/unscoped_collection.rb:
--------------------------------------------------------------------------------
1 | module ReactiveRecord
2 | # The base collection class works with relationships
3 | # method overrides for the unscoped collection
4 | module UnscopedCollection
5 | def set_pre_sync_related_records(related_records, _record = nil)
6 | @pre_sync_related_records = related_records
7 | live_scopes.each { |scope| scope.set_pre_sync_related_records(@pre_sync_related_records) }
8 | end
9 |
10 | def sync_scopes(related_records, record, filtering = true)
11 | live_scopes.each { |scope| scope.sync_scopes(related_records, record, filtering) }
12 | ensure
13 | @pre_sync_related_records = nil
14 | end
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/lib/reactive_record/active_record_error.rb:
--------------------------------------------------------------------------------
1 | module ActiveRecord
2 | class ActiveRecordError < StandardError
3 | end
4 | end
5 |
--------------------------------------------------------------------------------
/lib/reactive_record/engine.rb:
--------------------------------------------------------------------------------
1 | module HyperMesh
2 | class Engine < ::Rails::Engine
3 | isolate_namespace ReactiveRecord
4 | config.generators do |g|
5 | g.test_framework :rspec, :fixture => false
6 | g.fixture_replacement :factory_bot, :dir => 'spec/factories'
7 | g.assets false
8 | g.helper false
9 | end
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/lib/reactive_record/interval.rb:
--------------------------------------------------------------------------------
1 | module Browser
2 |
3 | # Allows you to create an interval that executes the function every given
4 | # seconds.
5 | #
6 | # @see https://developer.mozilla.org/en-US/docs/Web/API/Window.setInterval
7 | class Interval
8 | # @!attribute [r] every
9 | # @return [Float] the seconds every which the block is called
10 | attr_reader :every
11 |
12 | # Create and start an interval.
13 | #
14 | # @param window [Window] the window to start the interval on
15 | # @param time [Float] seconds every which to call the block
16 | def initialize(window, time, &block)
17 | @window = Native.convert(window)
18 | @every = time
19 | @block = block
20 |
21 | @aborted = false
22 | end
23 |
24 | # Check if the interval has been stopped.
25 | def stopped?
26 | @id.nil?
27 | end
28 |
29 | # Check if the interval has been aborted.
30 | def aborted?
31 | @aborted
32 | end
33 |
34 | # Abort the interval, it won't be possible to start it again.
35 | def abort
36 | `#@window.clearInterval(#@id)`
37 |
38 | @aborted = true
39 | @id = nil
40 | end
41 |
42 | # Stop the interval, it will be possible to start it again.
43 | def stop
44 | return if stopped?
45 |
46 | `#@window.clearInterval(#@id)`
47 |
48 | @stopped = true
49 | @id = nil
50 | end
51 |
52 | # Start the interval if it has been stopped.
53 | def start
54 | raise "the interval has been aborted" if aborted?
55 | return unless stopped?
56 |
57 | @id = `#@window.setInterval(#@block, #@every * 1000)`
58 | end
59 |
60 | # Call the [Interval] block.
61 | def call
62 | @block.call
63 | end
64 | end
65 |
66 | class Window
67 | # Execute the block every given seconds.
68 | #
69 | # @param time [Float] the seconds between every call
70 | #
71 | # @return [Interval] the object representing the interval
72 | def every(time, &block)
73 | Interval.new(@native, time, &block).tap(&:start)
74 | end
75 |
76 | # Execute the block every given seconds, you have to call [#start] on it
77 | # yourself.
78 | #
79 | # @param time [Float] the seconds between every call
80 | #
81 | # @return [Interval] the object representing the interval
82 | def every!(time, &block)
83 | Interval.new(@native, time, &block)
84 | end
85 | end
86 |
87 | end
88 |
89 | module Kernel
90 | # (see Browser::Window#every)
91 | def every(time, &block)
92 | $window.every(time, &block)
93 | end
94 |
95 | # (see Browser::Window#every!)
96 | def every!(time, &block)
97 | $window.every!(time, &block)
98 | end
99 | end
100 |
101 | class Proc
102 | # (see Browser::Window#every)
103 | def every(time)
104 | $window.every(time, &self)
105 | end
106 |
107 | # (see Browser::Window#every!)
108 | def every!(time)
109 | $window.every!(time, &self)
110 | end
111 | end
112 |
113 | module Browser
114 |
115 | # Allows you to delay the call to a function which gets called after the
116 | # given time.
117 | #
118 | # @see https://developer.mozilla.org/en-US/docs/Web/API/Window.setTimeout
119 | class Delay
120 | # @!attribute [r] after
121 | # @return [Float] the seconds after which the block is called
122 | attr_reader :after
123 |
124 | # Create and start a timeout.
125 | #
126 | # @param window [Window] the window to start the timeout on
127 | # @param time [Float] seconds after which the block is called
128 | def initialize(window, time, &block)
129 | @window = Native.convert(window)
130 | @after = time
131 | @block = block
132 | end
133 |
134 | # Abort the timeout.
135 | def abort
136 | `#@window.clearTimeout(#@id)`
137 | end
138 |
139 | # Start the delay.
140 | def start
141 | @id = `#@window.setTimeout(#{@block.to_n}, #@after * 1000)`
142 | end
143 | end
144 |
145 | class Window
146 | # Execute a block after the given seconds.
147 | #
148 | # @param time [Float] the seconds after it gets called
149 | #
150 | # @return [Delay] the object representing the timeout
151 | def after(time, &block)
152 | Delay.new(@native, time, &block).tap(&:start)
153 | end
154 |
155 | # Execute a block after the given seconds, you have to call [#start] on it
156 | # yourself.
157 | #
158 | # @param time [Float] the seconds after it gets called
159 | #
160 | # @return [Delay] the object representing the timeout
161 | def after!(time, &block)
162 | Delay.new(@native, time, &block)
163 | end
164 | end
165 |
166 | end
167 |
168 | module Kernel
169 | # (see Browser::Window#after)
170 | def after(time, &block)
171 | `setTimeout(#{block.to_n}, time * 1000)`
172 | end
173 |
174 | # (see Browser::Window#after!)
175 | def after!(time, &block)
176 | `setTimeout(#{block.to_n}, time * 1000)`
177 | end
178 | end
179 |
180 | class Proc
181 | # (see Browser::Window#after)
182 | def after(time)
183 | $window.after(time, &self)
184 | end
185 |
186 | # (see Browser::Window#after!)
187 | def after!(time)
188 | $window.after!(time, &self)
189 | end
190 | end
--------------------------------------------------------------------------------
/lib/reactive_record/permissions.rb:
--------------------------------------------------------------------------------
1 | module Hyperloop
2 | class InternalPolicy
3 |
4 | def self.accessible_attributes_for(model, acting_user)
5 | user_channels = ClassConnectionRegulation.connections_for(acting_user, false) +
6 | InstanceConnectionRegulation.connections_for(acting_user, false)
7 | internal_policy = InternalPolicy.new(model, model.attribute_names, user_channels)
8 | ChannelBroadcastRegulation.broadcast(internal_policy)
9 | InstanceBroadcastRegulation.broadcast(model, internal_policy)
10 | internal_policy.accessible_attributes_for
11 | end
12 |
13 | def accessible_attributes_for
14 | accessible_attributes = Set.new
15 | @channel_sets.each do |channel, attribute_set|
16 | accessible_attributes.merge attribute_set
17 | end
18 | accessible_attributes << :id unless accessible_attributes.empty?
19 | accessible_attributes
20 | end
21 | end
22 | end
23 |
24 | class ActiveRecord::Base
25 |
26 | attr_accessor :acting_user
27 |
28 | def view_permitted?(attribute)
29 | Hyperloop::InternalPolicy.accessible_attributes_for(self, acting_user).include? attribute.to_sym
30 | end
31 |
32 | def create_permitted?
33 | false
34 | end
35 |
36 | def update_permitted?
37 | false
38 | end
39 |
40 | def destroy_permitted?
41 | false
42 | end
43 |
44 | def only_changed?(*attributes)
45 | (self.attributes.keys + self.class.reactive_record_association_keys).each do |key|
46 | return false if self.send("#{key}_changed?") and !attributes.include? key
47 | end
48 | true
49 | end
50 |
51 | def none_changed?(*attributes)
52 | attributes.each do |key|
53 | return false if self.send("#{key}_changed?")
54 | end
55 | true
56 | end
57 |
58 | def any_changed?(*attributes)
59 | attributes.each do |key|
60 | return true if self.send("#{key}_changed?")
61 | end
62 | false
63 | end
64 |
65 | def all_changed?(*attributes)
66 | attributes.each do |key|
67 | return false unless self.send("#{key}_changed?")
68 | end
69 | true
70 | end
71 |
72 | class << self
73 |
74 | attr_reader :reactive_record_association_keys
75 |
76 | [:has_many, :belongs_to, :composed_of].each do |macro|
77 | define_method "#{macro}_with_reactive_record_add_changed_method".to_sym do |attr_name, *args, &block|
78 | define_method "#{attr_name}_changed?".to_sym do
79 | instance_variable_get "@reactive_record_#{attr_name}_changed".to_sym
80 | end
81 | (@reactive_record_association_keys ||= []) << attr_name
82 | send "#{macro}_without_reactive_record_add_changed_method".to_sym, attr_name, *args, &block
83 | end
84 | alias_method "#{macro}_without_reactive_record_add_changed_method".to_sym, macro
85 | alias_method macro, "#{macro}_with_reactive_record_add_changed_method".to_sym
86 | end
87 |
88 | alias belongs_to_without_reactive_record_add_is_method belongs_to
89 |
90 | def belongs_to(attr_name, *args)
91 | belongs_to_without_reactive_record_add_is_method(attr_name, *args).tap do
92 | define_method "#{attr_name}_is?".to_sym do |model|
93 | self.class.reflections[attr_name].foreign_key == model.id
94 | end
95 | end
96 | end
97 | end
98 |
99 | def check_permission_with_acting_user(user, permission, *args)
100 | old = acting_user
101 | self.acting_user = user
102 | if self.send(permission, *args)
103 | self.acting_user = old
104 | self
105 | else
106 | Hyperloop::InternalPolicy.raise_operation_access_violation(:crud_access_violation, "for #{self} - #{permission}(#{args}) acting_user: #{user}")
107 | end
108 | end
109 |
110 | end
111 |
112 | class ActionController::Base
113 |
114 | def acting_user
115 | end
116 |
117 | end
118 |
--------------------------------------------------------------------------------
/lib/reactive_record/pry.rb:
--------------------------------------------------------------------------------
1 | module ReactiveRecord
2 |
3 | module Pry
4 |
5 | def self.rescued(e)
6 | if defined?(PryRescue) && e.instance_variable_defined?(:@rescue_bindings) && !e.is_a?(Hyperloop::AccessViolation)
7 | ::Pry::rescued(e)
8 | end
9 | end
10 |
11 | end
12 |
13 | end
14 |
--------------------------------------------------------------------------------
/lib/reactive_record/reactive_scope.rb:
--------------------------------------------------------------------------------
1 | # class ActiveRecord::Base
2 | #
3 | # def self.to_sync(scope_name, opts={}, &block)
4 | # watch_list = if opts[:watch]
5 | # [*opts.delete[:watch]]
6 | # else
7 | # [self]
8 | # end
9 | # if RUBY_ENGINE=='opal'
10 | # watch_list.each do |klass_to_watch|
11 | # ReactiveRecord::Base.sync_blocks[klass_to_watch][self][scope_name] << block
12 | # end
13 | # else
14 | # # this is where we put server side watchers in place to sync all clients!
15 | # end
16 | # end
17 | #
18 | # end
19 |
--------------------------------------------------------------------------------
/lib/reactive_record/scope_description.rb:
--------------------------------------------------------------------------------
1 | module ReactiveRecord
2 | # Keeps track of the details (client side) of a scope.
3 | # The main point is to provide knowledge of what models
4 | # the scope is joined with, and the client side
5 | # filter proc
6 | class ScopeDescription
7 | def initialize(model, name, opts)
8 | sself = self
9 | @filter_proc = filter_proc(opts)
10 | @name = name
11 | model.singleton_class.send(:define_method, "_#{@name}_synchromesh_scope_description_") do
12 | sself
13 | end
14 | @model = model
15 | build_joins opts[:joins]
16 | end
17 |
18 | attr_reader :name
19 |
20 | def self.find(target_model, name)
21 | name = name.gsub(/!$/, '')
22 | target_model.send "_#{name}_synchromesh_scope_description_"
23 | rescue
24 | nil
25 | end
26 |
27 | def filter?
28 | @filter_proc.respond_to?(:call)
29 | end
30 |
31 | def collector?
32 | @is_collector
33 | end
34 |
35 | def joins_with?(record)
36 | @joins.detect do |klass, vector|
37 | # added klass < record.class to handle STI case... should check to see if this could ever
38 | # cause a problem. Probably not a problem.
39 | next unless vector.any?
40 | (klass == :all || record.class == klass || record.class < klass || klass < record.class)
41 | end
42 | end
43 |
44 | def get_joins(klass)
45 | joins = @joins[klass] if @joins.key? klass
46 | joins ||= @joins[klass.base_class] if @joins.key?(klass.base_class)
47 | joins || @joins[:all]
48 | end
49 |
50 | def related_records_for(record)
51 | ReactiveRecord::Base.catch_db_requests([]) do
52 | get_joins(record.class).collect do |vector|
53 | crawl(record, *vector)
54 | end.flatten.compact
55 | end
56 | end
57 |
58 | def filter_records(related_records, args)
59 | if collector?
60 | Set.new(related_records.to_a.instance_exec(*args, &@filter_proc))
61 | else
62 | Set.new(related_records.select { |r| r.instance_exec(*args, &@filter_proc) })
63 | end
64 | end
65 |
66 | # private methods
67 |
68 | def filter_proc(opts)
69 | return true unless opts.key?(:client) || opts.key?(:select)
70 | client_opt = opts[:client] || opts[:select]
71 | @is_collector = opts.key?(:select)
72 | return client_opt if !client_opt || client_opt.respond_to?(:call)
73 | raise 'Scope option :client or :select must be a proc, false, or nil'
74 | end
75 |
76 | def build_joins(joins_list)
77 | if !@filter_proc || joins_list == []
78 | @joins = { all: [] }
79 | elsif joins_list.nil?
80 | klass = @model < ActiveRecord::Base ? @model.base_class : @model
81 | @joins = { klass => [[]], all: [] }
82 | elsif joins_list == :all
83 | @joins = { all: [[]] }
84 | else
85 | joins_list = [joins_list] unless joins_list.is_a? Array
86 | map_joins_path joins_list
87 | end
88 | end
89 |
90 | def map_joins_path(paths)
91 | @joins = Hash.new { |h, k| h[k] = Array.new }.merge(@model => [[]])
92 | paths.each do |path|
93 | vector = []
94 | path.split('.').inject(@model) do |model, attribute|
95 | association = model.reflect_on_association(attribute)
96 | raise build_error(path, model, attribute) unless association
97 | vector = [association.inverse_of, *vector]
98 | @joins[association.klass] << vector
99 | association.klass
100 | end
101 | end
102 | end
103 |
104 | def build_error(path, model, attribute)
105 | "Could not find joins association '#{model.name}.#{attribute}' "\
106 | "for '#{path}' while processing scope #{@model.name}.#{@name}."
107 | end
108 |
109 | def crawl(item, method = nil, *vector)
110 | if !method && item.is_a?(Collection)
111 | item.all
112 | elsif !method
113 | item
114 | elsif item.respond_to? :collect
115 | item.collect { |record| crawl(record.send(method), *vector) }
116 | else
117 | crawl(item.send(method), *vector)
118 | end
119 | end
120 | end
121 | end
122 |
--------------------------------------------------------------------------------
/lib/reactive_record/serializers.rb:
--------------------------------------------------------------------------------
1 | ActiveRecord::Base.send(:define_method, :react_serializer) do
2 | serializable_hash.merge(ReactiveRecord::Base.get_type_hash(self))
3 | end
4 |
5 | ActiveRecord::Relation.send(:define_method, :react_serializer) do
6 | all.to_a.react_serializer
7 | end
8 |
--------------------------------------------------------------------------------
/logo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ruby-hyperloop/hyper-mesh/d29f00b16efbdd25419f07c7590505de4d3ba42a/logo.jpg
--------------------------------------------------------------------------------
/path_release_steps.md:
--------------------------------------------------------------------------------
1 |
2 | For example assuming you are releasing fix to 0.8.18
3 |
4 | 1. Checkout 0-8-stable
5 | 2. Update tests, fix the bug and commit the changes.
6 | 3. Build & Release to RubyGems (Remember the version in version.rb should already be 0.8.19)
7 | 4. Create a tag 'v0.8.19' pointing to that commit.
8 | 5. Bump the version in 0-8-stable to 0.8.20 so it will be ready for the next patch level release.
9 | 6. Commit the version bump, and do a `git push --tags` so the new tag goes up
10 |
--------------------------------------------------------------------------------
/remote.md:
--------------------------------------------------------------------------------
1 | ```ruby
2 | class Model < ActiveRecord::Base
3 |
4 | def <=>(other)
5 | self.text.downcase <=> other.text.downcase
6 | end
7 |
8 | scope :sorted, -> { order('lower(text) ASC')}, client: -> { sort }
9 |
10 | scope :proper_nouns, -> { where('lower(text) <> text ')}, client: -> (r) { r.text.downcase != r.text }
11 | end
12 | ```
13 |
14 | if the remote proc takes a param it is given each record to check (i.e. its wrapped in a select)
15 |
16 | if there is no param the proc is executed in context of a collection which it can return a modified version of.
17 |
18 | How:
19 |
20 | three kinds of scopes:
21 |
22 | Base collections (i.e. has_many, all, and unscoped)
23 |
24 | outer scopes (i.e. directly following a base collection Model.sorted === Model.all.sorted parent.children.proper_nouns )
25 |
26 | inner scopes (i.e. other scopes that follow outer scopes)
27 |
28 |
29 | First we need to update the base collections based on the changed models. By definition everything we need to know to update the base scopes is available in the changed record.
30 |
31 | + has_many collections get updated by the load_from_json method
32 | + all, and unscoped get updated by a new method on the class: update_base_scopes (maybe not needed? )
33 |
34 | Then we need to find all the outer scopes in the world.
35 |
36 | To do this whenever an scope is applied, if it is being applied to a base collection (or to a Model itself which is equivilent to applying it Model.all) then it is added to a list, along with its parent base scope.
37 |
38 | We can then iterate through this list.
39 |
40 | During the iteration we do this:
41 |
42 | ```ruby
43 | def update_collections(updated_record, base_collection, local_updates)
44 | if this scope is joined with the updated_record
45 | if local_updates && updated_record.class = self.class && my_client_proc
46 | local_updates << self
47 | else
48 | update_from_server
49 | client_procs = nil
50 | end
51 | end
52 | if children_collections
53 | children_collections.each { |child| child.update_collections(udpated_record, base_collection, client_procs)}
54 | elsif client_procs
55 | client_procs.inject(base_collection) do |collection, scope|
56 | dup collection into scope.all
57 | either call scope.proc with each record in a select
58 | or just call the proc in the context of scope
59 | end
60 | end
61 | end
62 | ```
63 |
64 | how about initial load?
65 |
66 |
67 | 1) if there is client_proc then run it on first load...
68 |
69 | filter_most_out_on_server.filter_a_few_out_on_client <- good
70 | filter_a_few_out_on_client.filter_most_out_on_server <- blah
71 |
72 | fix later...
73 | or
74 | as each scope is applied... of course we go back through all parent scopes and clear a filter flag (proc?)
75 |
76 |
77 | then on a load as we add each scope in, we can do the initial filter right then... which is okay because
78 | we don't have that data yet!!!
79 |
80 | or don't even bother, just run the filter as things are replaced from the server...
81 |
82 | its static... can always know if something should be filtered
83 |
84 |
85 | arghhh...
86 |
87 | Model.filter1.filter2...
88 |
89 | its still true that to filter, all scopes in chain must be client filters.
90 |
91 | but what is the diff between
92 |
93 | Model.inc_filter1.coll_filter1.inc_filter2.coll_filter2
94 |
95 | vs.
96 |
97 | Model.coll_filter
98 |
99 | right because this implies:
100 |
101 | Model.all.coll_filter, which means we have to do Model.all.each
102 |
103 | but Model.all.inc_filter should just pass any new/changed entries to inc_filter, and then update the collection
104 |
105 | Model.inc_filter1.inc_filter2
106 |
107 | is okay because we should not have inc_filter1's collection all updated.
108 |
109 | but really? what if we are doing this:
110 |
111 | Model.inc_minor_filter.inc_major_filter.count
112 |
113 | We do not have to fetch all inc_minor_filter's collection ever.
114 |
115 | We do not have to
116 |
--------------------------------------------------------------------------------
/spec/batch1/crud_access_regulation/broadcast_controls_access_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'test_components'
3 |
4 | describe "regulate access allowed" do
5 |
6 | context "basic tests" do
7 | before(:each) do
8 | # spec_helper resets the policy system after each test so we have to setup
9 | # before each test
10 | stub_const 'TestApplication', Class.new
11 | stub_const 'C2', Class.new
12 | stub_const 'TestApplicationPolicy', Class.new
13 | TestApplicationPolicy.class_eval do
14 | regulate_class_connection { self }
15 | regulate_class_connection(C2) { self }
16 | regulate_instance_connections(TestModel) { self if self.is_a? TestModel }
17 | regulate_all_broadcasts(C2) { |policy| policy.send_all_but(:created_at) }
18 | regulate_broadcast(TestModel) do |policy|
19 | policy.send_all.to(TestApplication) unless test_attribute == "bogus"
20 | policy.send_all.to(self)
21 | end
22 | end
23 | end
24 |
25 | it "will allow access if the broadcast policy allows access" do
26 | m = FactoryBot.create(:test_model, test_attribute: "hello")
27 | expect { m.check_permission_with_acting_user("user", :view_permitted?, :test_attribute) }.
28 | not_to raise_error
29 | expect { m.check_permission_with_acting_user("user", :view_permitted?, :created_at) }.
30 | not_to raise_error
31 | end
32 |
33 | it "will disallow access if acting_user is not allowed to connect" do
34 | m = FactoryBot.create(:test_model, test_attribute: "hello")
35 | expect { m.check_permission_with_acting_user(nil, :view_permitted?, :test_attribute) }.
36 | to raise_error(Hyperloop::AccessViolation)
37 | expect { m.check_permission_with_acting_user(nil, :view_permitted?, :created_at) }.
38 | to raise_error(Hyperloop::AccessViolation)
39 | end
40 |
41 | it "will disallow access to attributes not broadcast by the model" do
42 | m = FactoryBot.create(:test_model, test_attribute: "bogus")
43 | expect { m.check_permission_with_acting_user("user", :view_permitted?, :test_attribute) }.
44 | not_to raise_error
45 | expect { m.check_permission_with_acting_user("user", :view_permitted?, :created_at) }.
46 | to raise_error(Hyperloop::AccessViolation)
47 | end
48 |
49 | it "will allow access to attributes broadcast over an instance channel" do
50 | m = FactoryBot.create(:test_model, test_attribute: "bogus")
51 | expect { m.check_permission_with_acting_user(m, :view_permitted?, :test_attribute) }.
52 | not_to raise_error
53 | expect { m.check_permission_with_acting_user(m, :view_permitted?, :created_at) }.
54 | not_to raise_error
55 | end
56 | end
57 |
58 | it "will prevent access to specific attributes" do
59 | stub_const 'TestApplication', Class.new
60 | stub_const 'TestApplicationPolicy', Class.new
61 | TestApplicationPolicy.class_eval do
62 | always_allow_connection
63 | regulate_broadcast(TestModel) do |policy|
64 | policy.send_all_but(:created_at).to(TestApplication)
65 | end
66 | end
67 | m = FactoryBot.create(:test_model)
68 | expect { m.check_permission_with_acting_user(nil, :view_permitted?, :test_attribute) }.
69 | not_to raise_error
70 | expect { m.check_permission_with_acting_user(nil, :view_permitted?, :created_at) }.
71 | to raise_error(Hyperloop::AccessViolation)
72 | end
73 |
74 | it "will include :id as read attribute as long as any other attribute is readable" do
75 | stub_const 'TestApplication', Class.new
76 | stub_const 'TestApplicationPolicy', Class.new
77 | TestApplicationPolicy.class_eval do
78 | always_allow_connection
79 | regulate_all_broadcasts { |policy| policy.send_only(:test_attribute) }
80 | end
81 | m = FactoryBot.create(:test_model)
82 | expect { m.check_permission_with_acting_user(nil, :view_permitted?, :id) }.
83 | not_to raise_error
84 | end
85 |
86 | it "will not include :id as read attribute if no other attributes are readable" do
87 | stub_const 'TestApplication', Class.new
88 | stub_const 'TestApplicationPolicy', Class.new
89 | TestApplicationPolicy.class_eval do
90 | always_allow_connection
91 | end
92 | m = FactoryBot.create(:test_model)
93 | expect { m.check_permission_with_acting_user(nil, :view_permitted?, :id) }.
94 | to raise_error(Hyperloop::AccessViolation)
95 | end
96 |
97 | it "will ignore auto_connect: false " do
98 | stub_const 'TestApplication', Class.new
99 | stub_const 'TestApplicationPolicy', Class.new
100 | TestApplicationPolicy.class_eval do
101 | regulate_class_connection(auto_connect: false) { true }
102 | regulate_instance_connections(TestModel, auto_connect: false) { self }
103 | regulate_all_broadcasts { |policy| policy.send_only(:test_attribute) }
104 | regulate_broadcast(TestModel) { |policy| policy.send_only(:created_at).to(self) }
105 | end
106 | m = FactoryBot.create(:test_model)
107 | expect { m.check_permission_with_acting_user(m, :view_permitted?, :id) }.
108 | not_to raise_error
109 | expect { m.check_permission_with_acting_user(m, :view_permitted?, :test_attribute) }.
110 | not_to raise_error
111 | expect { m.check_permission_with_acting_user(m, :view_permitted?, :created_at) }.
112 | not_to raise_error
113 | end
114 |
115 | end
116 |
--------------------------------------------------------------------------------
/spec/batch1/crud_access_regulation/model_policies_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'test_components'
3 |
4 | describe "regulate access allowed" do
5 |
6 |
7 | before(:each) do
8 | stub_const 'DummyModel', Class.new(ActiveRecord::Base)
9 | DummyModel.class_eval do
10 | self.table_name = 'test_models'
11 | end
12 | end
13 |
14 | after(:each) do
15 | class ActiveRecord::Base
16 | def view_permitted?(attribute)
17 | Hyperloop::InternalPolicy.accessible_attributes_for(self, acting_user).include? attribute.to_sym
18 | end
19 | [:create, :update, :destroy].each do |access|
20 | define_method("#{access}_permitted?".to_sym) { false }
21 | end
22 | end
23 | end
24 |
25 | Hyperloop::InternalClassPolicy::CHANGE_POLICIES.each do |policy|
26 |
27 | it "will define a basic allow_#{policy} policy" do
28 | stub_const 'DummyModelPolicy', Class.new
29 | DummyModelPolicy.class_eval do
30 | send("allow_#{policy}") { "called #{policy}" }
31 | end
32 | DummyModel.new.send("#{policy}_permitted?").should eq("called #{policy}")
33 | end
34 |
35 | it "will define allow_#{policy} policy with a class argument" do
36 | stub_const 'ApplicationPolicy', Class.new
37 | ApplicationPolicy.class_eval do
38 | send("allow_#{policy}", DummyModel) { "called #{policy}" }
39 | end
40 | DummyModel.new.send("#{policy}_permitted?").should eq("called #{policy}")
41 | end
42 |
43 | it "will define allow_#{policy} policy with the to: :all option" do
44 | stub_const 'ApplicationPolicy', Class.new
45 | ApplicationPolicy.class_eval do
46 | send("allow_#{policy}", to: :all) { "called #{policy} on #{self.class.name}" }
47 | end
48 | stub_const 'FooModel', Class.new(ActiveRecord::Base)
49 | FooModel.class_eval do
50 | self.table_name = 'test_models'
51 | end
52 | DummyModel.new.send("#{policy}_permitted?").should eq("called #{policy} on DummyModel")
53 | FooModel.new.send("#{policy}_permitted?").should eq("called #{policy} on FooModel")
54 | end
55 |
56 | end
57 |
58 | it "will define a basic allow_change policy" do
59 | stub_const 'DummyModelPolicy', Class.new
60 | DummyModelPolicy.class_eval do
61 | send("allow_change") { "called change" }
62 | end
63 | Hyperloop::InternalClassPolicy::CHANGE_POLICIES.each do |policy|
64 | DummyModel.new.send("#{policy}_permitted?").should eq("called change")
65 | end
66 | end
67 |
68 | it "will define allow_change policy with a class argument" do
69 | stub_const 'ApplicationPolicy', Class.new
70 | ApplicationPolicy.class_eval do
71 | send("allow_change", DummyModel) { "called change" }
72 | end
73 | Hyperloop::InternalClassPolicy::CHANGE_POLICIES.each do |policy|
74 | DummyModel.new.send("#{policy}_permitted?").should eq("called change")
75 | end
76 | end
77 |
78 | it "will define allow_change policy with the to: :all option" do
79 | stub_const 'ApplicationPolicy', Class.new
80 | ApplicationPolicy.class_eval do
81 | send("allow_change", to: :all) { "called change on #{self.class.name}" }
82 | end
83 | stub_const 'FooModel', Class.new(ActiveRecord::Base)
84 | FooModel.class_eval do
85 | self.table_name = 'test_models'
86 | end
87 | Hyperloop::InternalClassPolicy::CHANGE_POLICIES.each do |policy|
88 | DummyModel.new.send("#{policy}_permitted?").should eq("called change on DummyModel")
89 | FooModel.new.send("#{policy}_permitted?").should eq("called change on FooModel")
90 | end
91 | end
92 |
93 | it "will define allow_change policy with an :on option" do
94 | stub_const 'ApplicationPolicy', Class.new
95 | ApplicationPolicy.class_eval do
96 | send("allow_change", DummyModel, on: [:create, :update]) { "called change" }
97 | end
98 | [:create, :update].each do |policy|
99 | DummyModel.new.send("#{policy}_permitted?").should eq("called change")
100 | end
101 | DummyModel.new.send("destroy_permitted?").should be_falsy
102 | end
103 |
104 | it "will define a basic allow_read policy" do
105 | stub_const 'DummyModelPolicy', Class.new
106 | DummyModelPolicy.class_eval do
107 | send("allow_read") { |attr| "called read #{attr}" }
108 | end
109 | DummyModel.new.send("view_permitted?", :foo).should eq("called read foo")
110 | end
111 |
112 | it "will define allow_read policy with a class argument" do
113 | stub_const 'ApplicationPolicy', Class.new
114 | ApplicationPolicy.class_eval do
115 | send("allow_read", DummyModel) { |attr| "called read #{attr}" }
116 | end
117 | DummyModel.new.send("view_permitted?", :foo).should eq("called read foo")
118 | end
119 |
120 | it "will define allow_read policy with the to: :all option" do
121 | stub_const 'ApplicationPolicy', Class.new
122 | ApplicationPolicy.class_eval do
123 | send("allow_read", to: :all) { |attr| "called read #{attr} on #{self.class.name}" }
124 | end
125 | stub_const 'FooModel', Class.new(ActiveRecord::Base)
126 | FooModel.class_eval do
127 | self.table_name = 'test_models'
128 | end
129 | DummyModel.new.send("view_permitted?", :foo).should eq("called read foo on DummyModel")
130 | FooModel.new.send("view_permitted?", :foo).should eq("called read foo on FooModel")
131 | end
132 |
133 | end
134 |
--------------------------------------------------------------------------------
/spec/batch1/misc/access_like_hash_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'test_components'
3 | require 'rspec-steps'
4 |
5 |
6 | RSpec::Steps.steps "access like a hash", js: true do
7 |
8 | before(:step) do
9 | # spec_helper resets the policy system after each test so we have to setup
10 | # before each test
11 | stub_const 'TestApplication', Class.new
12 | stub_const 'TestApplicationPolicy', Class.new
13 | TestApplicationPolicy.class_eval do
14 | always_allow_connection
15 | regulate_all_broadcasts { |policy| policy.send_all }
16 | allow_change(to: :all, on: [:create, :update, :destroy]) { true }
17 | end
18 | size_window(:small, :portrait)
19 | FactoryBot.create(:user, first_name: 'Lily', last_name: 'DaDog')
20 | end
21 |
22 | it "can access attributes using the [] operator" do
23 | expect_promise do
24 | HyperMesh.load do
25 | User.find_by_first_name('Lily')
26 | end.then do |lily|
27 | lily[:first_name]
28 | end
29 | end.to eq('Lily')
30 | end
31 |
32 | it "can update attributes using the []= operator" do
33 | expect_promise do
34 | HyperMesh.load do
35 | User.find_by_first_name('Lily')
36 | end.then do |lily|
37 | lily[:last_name] = 'DerDog'
38 | lily.save
39 | end
40 | end.to be_truthy
41 | expect(User.find_by_first_name('Lily')[:last_name]).to eq('DerDog')
42 | end
43 | end
44 |
--------------------------------------------------------------------------------
/spec/batch1/misc/validate_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'test_components'
3 | require 'rspec-steps'
4 |
5 |
6 | RSpec::Steps.steps "validate and valid? methods", js: true do
7 |
8 | before(:step) do
9 | # spec_helper resets the policy system after each test so we have to setup
10 | # before each test
11 | stub_const 'TestApplication', Class.new
12 | stub_const 'TestApplicationPolicy', Class.new
13 | TestApplicationPolicy.class_eval do
14 | #always_allow_connection TURN OFF BROADCAST SO TESTS DON"T EFFECT EACH OTHER
15 | regulate_all_broadcasts { |policy| policy.send_all }
16 | allow_change(to: :all, on: [:create, :update, :destroy]) { true }
17 | end
18 | #size_window(:large, :landscape)
19 | User.validates :last_name, exclusion: { in: %w[f**k], message: 'no swear words allowed' }
20 | TestModel.validates_presence_of :child_models
21 | client_option raise_on_js_errors: :off
22 | end
23 |
24 | it "can validate the presence of an association" do
25 | expect_promise do
26 | @test_model = TestModel.new
27 | @test_model.validate.then { |test_model| test_model.errors.messages }
28 | end.not_to be_empty
29 | expect_promise do
30 | @test_model.child_models << ChildModel.new
31 | @test_model.validate.then { |test_model| test_model.errors.messages }
32 | end.to be_empty
33 | expect(TestModel.count).to be_zero
34 | expect(ChildModel.count).to be_zero
35 | end
36 |
37 | it "can validate only using the validate method" do
38 | expect_promise do
39 | User.new(last_name: 'f**k').validate.then do |new_user|
40 | new_user.errors.messages
41 | end
42 | end.to eq("last_name"=>["no swear words allowed"])
43 | end
44 |
45 | it "the valid? method will return true if the model has no errors" do
46 | mount "Validator" do
47 | class Validator < Hyperloop::Component
48 | include React::IsomorphicHelpers
49 | class << self
50 | attr_reader :model
51 | end
52 | before_first_mount { @model = User.new }
53 | render(DIV) { "#{Validator.model}.valid? #{!!Validator.model.valid?}" }
54 | end
55 | end
56 | expect_promise do
57 | user = User.new(last_name: 'dog')
58 | user.save.then { user.valid? }
59 | end.to be_truthy
60 | end
61 |
62 | it "the valid? method will return false if the model has errors" do
63 | expect_promise do
64 | user = User.new(last_name: 'f**k')
65 | user.save.then { user.valid? }
66 | end.to be_falsy
67 | end
68 |
69 | it "the valid? method reacts to the model being saved" do
70 | evaluate_ruby do
71 | Validator.model.last_name = 'f**k'
72 | end
73 | expect(page).to have_content('.valid? true')
74 | evaluate_ruby do
75 | Validator.model.save
76 | end
77 | expect(page).to have_content('.valid? false')
78 | evaluate_ruby do
79 | Validator.model.update(last_name: 'nice doggy')
80 | end
81 | expect(page).to have_content('.valid? true')
82 | end
83 |
84 | it "the valid? method reacts to the model being validated" do
85 | evaluate_ruby do
86 | Validator.model.last_name = 'f**k'
87 | end
88 | evaluate_ruby do
89 | Validator.model.validate
90 | end
91 | expect(page).to have_content('.valid? false')
92 | evaluate_ruby do
93 | Validator.model.last_name = 'nice doggy'
94 | Validator.model.validate
95 | end
96 | expect(page).to have_content('.valid? true')
97 | end
98 |
99 | it "the valid? method reacts to the error object changing state" do
100 | expect(page).to have_content('.valid? true')
101 | evaluate_ruby do
102 | Validator.model.errors.add(:bite)
103 | end
104 | expect(page).to have_content('.valid? false')
105 | evaluate_ruby do
106 | Validator.model.errors.clear
107 | end
108 | expect(page).to have_content('.valid? true')
109 | end
110 |
111 | end
112 |
--------------------------------------------------------------------------------
/spec/batch2/alias_attribute_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'rspec-steps'
3 |
4 | RSpec::Steps.steps 'alias_attribute', js: true do
5 |
6 | before(:each) do
7 | require 'pusher'
8 | require 'pusher-fake'
9 | Pusher.app_id = "MY_TEST_ID"
10 | Pusher.key = "MY_TEST_KEY"
11 | Pusher.secret = "MY_TEST_SECRET"
12 | require "pusher-fake/support/base"
13 |
14 | Hyperloop.configuration do |config|
15 | config.transport = :pusher
16 | config.channel_prefix = "synchromesh"
17 | config.opts = {app_id: Pusher.app_id, key: Pusher.key, secret: Pusher.secret}.merge(PusherFake.configuration.web_options)
18 | end
19 |
20 | end
21 |
22 | before(:step) do
23 | stub_const 'TestApplicationPolicy', Class.new
24 | TestApplicationPolicy.class_eval do
25 | always_allow_connection
26 | regulate_all_broadcasts { |policy| policy.send_all }
27 | allow_change(to: :all, on: [:create, :update, :destroy]) { true }
28 | end
29 | ApplicationController.acting_user = nil
30 | isomorphic do
31 | User.alias_attribute :surname, :last_name
32 | class SubUser < User
33 | end
34 | end
35 | on_client do
36 | # Aliases are implemented as method aliases. However these will not
37 | # work with class methods like create, so we also keep a hash of aliases
38 | # associated with the class.
39 | # In order to test alias inheritence we will just add this alias on the
40 | # client. Thus if during any access we DID not inherit the alias
41 | # it will remain as client_name, which will break the server side store
42 | User.alias_attribute :client_name, :last_name
43 | end
44 | end
45 |
46 | it "implements find_by" do
47 | @user = User.create(first_name: "Mitch", last_name: "VanDuyn")
48 | expect_promise do
49 | ReactiveRecord.load { User.find_by(first_name: "Mitch", surname: "VanDuyn").id }
50 | end.to eq(@user.id)
51 | end
52 |
53 | it "implements the finder" do
54 | @user = User.create(first_name: "M.", last_name: "Pantel")
55 | expect_promise do
56 | ReactiveRecord.load { User.find_by_surname('Pantel').id }
57 | end.to eq(@user.id)
58 | end
59 |
60 | it "works with find_by without fetching from the DB" do
61 | expect_evaluate_ruby do
62 | User.find_by(first_name: 'M.', last_name: 'Pantel').id
63 | end.to eq(@user.id)
64 | end
65 |
66 | it "implements the getter" do
67 | expect_promise do
68 | ReactiveRecord.load { User.find_by_first_name('M.').surname }
69 | end.to eq('Pantel')
70 | end
71 |
72 | it "implements the setter" do
73 | evaluate_promise do
74 | user = User.find_by_first_name('M.')
75 | user.surname = "Someoneelse"
76 | user.save
77 | end
78 | expect(@user.reload.surname).to eq('Someoneelse')
79 | end
80 |
81 | it "implements the _changed? method" do
82 | expect_evaluate_ruby do
83 | user = User.find_by_first_name('M.')
84 | user.last_name = "Pantel"
85 | user.surname_changed?
86 | end.to be_truthy
87 | end
88 |
89 | it "can inherit the aliases" do
90 | evaluate_promise do
91 | SubUser.create(client_name: 'Fred')
92 | end
93 | expect(SubUser.find_by_surname('Fred')).to be_truthy
94 | end
95 |
96 | end
97 |
--------------------------------------------------------------------------------
/spec/batch2/default_scope_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'test_components'
3 |
4 | describe "default_scope" do
5 |
6 | context "client tests", js: true do
7 |
8 | before(:all) do
9 | require 'pusher'
10 | require 'pusher-fake'
11 | Pusher.app_id = "MY_TEST_ID"
12 | Pusher.key = "MY_TEST_KEY"
13 | Pusher.secret = "MY_TEST_SECRET"
14 | require "pusher-fake/support/base"
15 |
16 | Hyperloop.configuration do |config|
17 | config.transport = :pusher
18 | config.channel_prefix = "synchromesh"
19 | config.opts = {app_id: Pusher.app_id, key: Pusher.key, secret: Pusher.secret}.merge(PusherFake.configuration.web_options)
20 | end
21 | end
22 |
23 | before(:each) do
24 | # spec_helper resets the policy system after each test so we have to setup
25 | # before each test
26 | stub_const 'TestApplication', Class.new
27 | stub_const 'TestApplicationPolicy', Class.new
28 | TestApplicationPolicy.class_eval do
29 | always_allow_connection
30 | regulate_all_broadcasts { |policy| policy.send_all }
31 | allow_change(to: :all, on: [:create, :update, :destroy]) { true }
32 | end
33 | size_window(:small, :portrait)
34 | end
35 |
36 | after(:each) do
37 | TestModel.default_scopes = []
38 | end
39 |
40 | it "a default scope can be added server side using either a block or proc" do
41 | isomorphic do
42 | TestModel.class_eval do
43 | default_scope -> { where(completed: true) }
44 | default_scope { where(test_attribute: 'foo') }
45 | end
46 | end
47 | mount "TestComponent2" do
48 | class TestComponent2 < React::Component::Base
49 | render(:div) do
50 | "#{TestModel.count} items".br
51 | "#{TestModel.unscoped.count} unscoped items"
52 | end
53 | end
54 | end
55 | wait_for_ajax
56 | page.should have_content("0 items")
57 | page.should have_content("0 unscoped items")
58 | m1 = FactoryBot.create(:test_model, completed: false, test_attribute: nil)
59 | wait_for_ajax
60 | page.should have_content("0 items")
61 | page.should have_content("1 unscoped items")
62 | m2 = FactoryBot.create(:test_model, completed: true, test_attribute: nil)
63 | wait_for_ajax
64 | page.should have_content("0 items")
65 | page.should have_content("2 unscoped items")
66 | m2.update(test_attribute: 'foo')
67 | wait_for_ajax
68 | page.should have_content("1 items")
69 | page.should have_content("2 unscoped items")
70 | m3 = FactoryBot.create(:test_model)
71 | wait_for_ajax
72 | page.should have_content("2 items")
73 | page.should have_content("3 unscoped items")
74 | m3.update_attribute(:completed, false)
75 | wait_for_ajax
76 | page.should have_content("1 items")
77 | page.should have_content("3 unscoped items")
78 | m2.destroy
79 | wait_for_ajax
80 | page.should have_content("0 items")
81 | page.should have_content("2 unscoped items")
82 | end
83 |
84 | it "a default scope can be added client side" do
85 | isomorphic do
86 | TestModel.class_eval do
87 | default_scope server: -> { where(completed: true) },
88 | client: -> { completed }
89 | default_scope server: -> { where(test_attribute: 'foo') },
90 | client: -> { test_attribute == 'foo' }
91 | end
92 | end
93 | mount "TestComponent2" do
94 | class TestComponent2 < React::Component::Base
95 | render(:div) do
96 | "#{TestModel.count} items".br
97 | "#{TestModel.unscoped.count} unscoped items"
98 | end
99 | end
100 | end
101 | wait_for_ajax
102 | starting_fetch_time = evaluate_ruby("ReactiveRecord::Base.current_fetch_id")
103 | page.should have_content("0 items")
104 | page.should have_content("0 unscoped items")
105 | m1 = FactoryBot.create(:test_model, completed: false, test_attribute: nil)
106 | wait_for_ajax
107 | page.should have_content("0 items")
108 | page.should have_content("1 unscoped items")
109 | m2 = FactoryBot.create(:test_model, completed: true, test_attribute: nil)
110 | wait_for_ajax
111 | page.should have_content("0 items")
112 | page.should have_content("2 unscoped items")
113 | m2.update(test_attribute: 'foo')
114 | wait_for_ajax
115 | page.should have_content("1 items")
116 | page.should have_content("2 unscoped items")
117 | m3 = FactoryBot.create(:test_model)
118 | wait_for_ajax
119 | page.should have_content("2 items")
120 | page.should have_content("3 unscoped items")
121 | m3.update_attribute(:completed, false)
122 | wait_for_ajax
123 | page.should have_content("1 items")
124 | page.should have_content("3 unscoped items")
125 | m2.destroy
126 | wait_for_ajax
127 | page.should have_content("0 items")
128 | page.should have_content("2 unscoped items")
129 | # there should be no client fetches should replace this with a double of
130 | # ServerDataCache[] which should not be called
131 | wait_for_ajax
132 | starting_fetch_time.should eq(evaluate_ruby("ReactiveRecord::Base.current_fetch_id"))
133 | end
134 | end
135 | end
136 |
--------------------------------------------------------------------------------
/spec/batch2/enum_xspec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'rspec-steps'
3 |
4 | RSpec::Steps.steps 'Reading and Writing Enums', js: true do
5 |
6 | before(:each) do
7 |
8 | require 'pusher'
9 | require 'pusher-fake'
10 | Pusher.app_id = "MY_TEST_ID"
11 | Pusher.key = "MY_TEST_KEY"
12 | Pusher.secret = "MY_TEST_SECRET"
13 | require "pusher-fake/support/base"
14 |
15 | Hyperloop.configuration do |config|
16 | config.transport = :pusher
17 | config.channel_prefix = "synchromesh"
18 | config.opts = {app_id: Pusher.app_id, key: Pusher.key, secret: Pusher.secret}.merge(PusherFake.configuration.web_options)
19 | end
20 | end
21 | before(:step) do
22 | stub_const 'TestApplicationPolicy', Class.new
23 | TestApplicationPolicy.class_eval do
24 | always_allow_connection
25 | regulate_all_broadcasts { |policy| policy.send_all }
26 | end
27 | ApplicationController.acting_user = nil
28 | end
29 |
30 | it "can change the enum and read it back" do
31 | User.create(name: 'test user')
32 | evaluate_ruby do
33 | ReactiveRecord.load { User.find(1).itself }.then do |user|
34 | user.test_enum = :no
35 | user.save.then do
36 | React::IsomorphicHelpers.load_context
37 | ReactiveRecord.load do
38 | User.find(1).test_enum
39 | end
40 | end
41 | end
42 | end.to eq('no')
43 | end
44 |
45 | # async "can set it back" do
46 | # React::IsomorphicHelpers.load_context
47 | # set_acting_user "super-user"
48 | # user = User.find(1)
49 | # user.test_enum = :yes
50 | # user.save.then do
51 | # React::IsomorphicHelpers.load_context
52 | # ReactiveRecord.load do
53 | # User.find(1).test_enum
54 | # end.then do |test_enum|
55 | # async { expect(test_enum).to eq(:yes) }
56 | # end
57 | # end
58 | # end
59 | #
60 | # it "can change it back" do
61 | # user = User.find(1)
62 | # user.test_enum = :yes
63 | # user.save.then do |success|
64 | # expect(success).to be_truthy
65 | # end
66 | # end
67 |
68 | end
69 |
--------------------------------------------------------------------------------
/spec/batch2/non_ar_aggregations_tbdspec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'rspec-steps'
3 |
4 | RSpec::Steps.steps 'using non-ar aggregations', js: true do
5 |
6 | before(:each) do
7 | require 'pusher'
8 | require 'pusher-fake'
9 | Pusher.app_id = "MY_TEST_ID"
10 | Pusher.key = "MY_TEST_KEY"
11 | Pusher.secret = "MY_TEST_SECRET"
12 | require "pusher-fake/support/base"
13 |
14 | Hyperloop.configuration do |config|
15 | config.transport = :pusher
16 | config.channel_prefix = "synchromesh"
17 | config.opts = {app_id: Pusher.app_id, key: Pusher.key, secret: Pusher.secret}.merge(PusherFake.configuration.web_options)
18 | end
19 |
20 | User.do_not_synchronize
21 | end
22 |
23 | before(:step) do
24 | stub_const 'TestApplicationPolicy', Class.new
25 | TestApplicationPolicy.class_eval do
26 | always_allow_connection
27 | regulate_all_broadcasts { |policy| policy.send_all }
28 | allow_change(to: :all, on: [:create, :update, :destroy]) { true }
29 | end
30 | ApplicationController.acting_user = nil
31 | end
32 |
33 | it "create an aggregation" do
34 | expect_evaluate_ruby do
35 | User.new(first_name: "Data", data: TestData.new("hello", 3)).data.big_string
36 | end.to eq("hellohellohello")
37 | end
38 |
39 | it "save it" do
40 | evaluate_promise do
41 | User.find_by_first_name("Data").save
42 | end
43 | expect(User.find_by_first_name("Data").data.big_string).to eq("hellohellohello")
44 | binding.pry
45 | end
46 |
47 | it "read it" do
48 | User.create(first_name: 'User2', data: TestData.new('goodby', 3))
49 | expect_promise do
50 | ReactiveRecord.load { User.find_by_first_name("User2").data.big_string }
51 | end.to eq('goodbygoodbygoodby')
52 | end
53 |
54 | # and restored ...
55 | #
56 | # async "is time to change it, and force the save" do
57 | # user = User.find_by_first_name("Data")
58 | # user.data.string = "goodby"
59 | # user.save(force: true).then do
60 | # React::IsomorphicHelpers.load_context
61 | # ReactiveRecord.load do
62 | # User.find_by_first_name("Data").data
63 | # end.then do |data|
64 | # async { expect(data.big_string).to eq("goodbygoodbygoodby") }
65 | # end
66 | # end
67 | # end
68 | #
69 | # async "is time to change the value completely and save it (no force needed)" do
70 | # user = User.find_by_first_name("Data")
71 | # user.data = TestData.new("the end", 1)
72 | # user.save.then do
73 | # React::IsomorphicHelpers.load_context
74 | # ReactiveRecord.load do
75 | # User.find_by_first_name("Data").data
76 | # end.then do |data|
77 | # async { expect(data.big_string).to eq("the end") }
78 | # end
79 | # end
80 | # end
81 | #
82 | # async "is time to delete the value and see if returns nil after saving" do
83 | # user = User.find_by_first_name("Data")
84 | # user.data = nil
85 | # user.save.then do
86 | # React::IsomorphicHelpers.load_context
87 | # ReactiveRecord.load do
88 | # User.find_by_first_name("Data").data
89 | # end.then do |data|
90 | # async { expect(data).to be_nil }
91 | # end
92 | # end
93 | # end
94 | #
95 | # it "is time to delete our user" do
96 | # User.find_by_first_name("Data").destroy.then do
97 | # expect(User.find_by_first_name("Data")).to be_destroyed
98 | # end
99 | # end
100 | #
101 | # it "is time to see to make sure a nil aggregate that has never had a value returns nil" do
102 | # ReactiveRecord.load do
103 | # User.find_by_email("mitch@catprint.com").data
104 | # end.then do |data|
105 | # expect(data).to be_nil
106 | # end
107 | # end
108 |
109 | end
110 |
--------------------------------------------------------------------------------
/spec/batch3/auto_load_itself_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'test_components'
3 |
4 | describe "HyperMesh.load", js: true do
5 |
6 | before(:each) do
7 | # spec_helper resets the policy system after each test so we have to setup
8 | # before each test
9 | stub_const 'TestApplication', Class.new
10 | stub_const 'TestApplicationPolicy', Class.new
11 | TestApplicationPolicy.class_eval do
12 | always_allow_connection
13 | regulate_all_broadcasts { |policy| policy.send_all }
14 | end
15 | size_window(:small, :portrait)
16 | end
17 |
18 | it "uses itself to force loading" do
19 | user = FactoryBot.create(:user, first_name: 'Ima')
20 | expect_promise do
21 | HyperMesh.load { User.find_by_first_name('Ima') }.then { |user| user.id }
22 | end.to eq(user.id)
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/spec/batch3/edge_cases_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'test_components'
3 |
4 | describe "reactive-record edge cases", js: true do
5 |
6 | before(:all) do
7 | # Hyperloop.configuration do |config|
8 | # config.transport = :simple_poller
9 | # # slow down the polling so wait_for_ajax works
10 | # config.opts = { seconds_between_poll: 2 }
11 | # end
12 |
13 | require 'pusher'
14 | require 'pusher-fake'
15 | Pusher.app_id = "MY_TEST_ID"
16 | Pusher.key = "MY_TEST_KEY"
17 | Pusher.secret = "MY_TEST_SECRET"
18 | require "pusher-fake/support/base"
19 |
20 | Hyperloop.configuration do |config|
21 | config.transport = :pusher
22 | config.channel_prefix = "synchromesh"
23 | config.opts = {app_id: Pusher.app_id, key: Pusher.key, secret: Pusher.secret}.merge(PusherFake.configuration.web_options)
24 | end
25 |
26 | end
27 |
28 | before(:each) do
29 | # spec_helper resets the policy system after each test so we have to setup
30 | # before each test
31 | stub_const 'TestApplication', Class.new
32 | stub_const 'TestApplicationPolicy', Class.new
33 | TestApplicationPolicy.class_eval do
34 | always_allow_connection
35 | regulate_all_broadcasts { |policy| policy.send_all }
36 | allow_change(to: :all, on: [:create, :update, :destroy]) { true }
37 | end
38 | size_window(:small, :portrait)
39 | end
40 |
41 | it "trims the association tree" do
42 | 5.times do |i|
43 | user = FactoryBot.create(:user, first_name: i) unless i == 3
44 | FactoryBot.create(:todo, title: "User #{i}'s todo", owner: user)
45 | end
46 | expect_promise do
47 | HyperMesh.load do
48 | Todo.all.collect do |todo|
49 | todo.owner && todo.owner.first_name
50 | end.compact
51 | end
52 | end.to contain_exactly('0', '1', '2', '4')
53 | end
54 |
55 | it "does not double count local saves" do
56 | expect_promise do
57 | HyperMesh.load do
58 | Todo.count
59 | end.then do |count|
60 | Todo.create(title: 'test todo')
61 | end.then do
62 | Todo.count
63 | end
64 | end.to eq(1)
65 | end
66 |
67 | it "fetches data during prerendering" do
68 | 5.times do |i|
69 | FactoryBot.create(:todo, title: "Todo #{i}")
70 | end
71 | # cause spec to fail if there are attempts to fetch data after prerendering
72 | hide_const 'ReactiveRecord::Operations::Fetch'
73 | mount "TestComponent77", {}, render_on: :both do
74 | class TestComponent77 < Hyperloop::Component
75 | render(UL) do
76 | Todo.each do |todo|
77 | LI { todo.title }
78 | end
79 | end
80 | end
81 | end
82 | Todo.all.each do |todo|
83 | page.should have_content(todo.title)
84 | end
85 | end
86 |
87 | it "prerenders a belongs to relationship" do
88 | user_item = User.create(name: 'Fred')
89 | todo_item = TodoItem.create(title: 'test-todo', user: user_item)
90 | mount "PrerenderTest", {}, render_on: :server_only do
91 | class PrerenderTest < Hyperloop::Component
92 | render(DIV) do
93 | TodoItem.first.user.name
94 | end
95 | end
96 | end
97 | page.should have_content("Fred")
98 | end
99 |
100 | it "the limit and offset predefined scopes work" do
101 | 5.times do |i|
102 | FactoryBot.create(:todo, title: "Todo #{i}")
103 | end
104 | mount "TestComponent77" do
105 | class TestComponent77 < Hyperloop::Component
106 | render(UL) do
107 | Todo.limit(2).offset(3).each do |todo|
108 | LI { todo.title }
109 | end
110 | end
111 | end
112 | end
113 | Todo.limit(2).offset(3).each do |todo|
114 | page.should have_content(todo.title)
115 | end
116 | end
117 | end
118 |
--------------------------------------------------------------------------------
/spec/batch3/finder_method_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'test_components'
3 | require 'rspec-steps'
4 |
5 |
6 | RSpec::Steps.steps "finder_method", js: true do
7 |
8 | before(:step) do
9 | # spec_helper resets the policy system after each test so we have to setup
10 | # before each test
11 | stub_const 'TestApplication', Class.new
12 | stub_const 'TestApplicationPolicy', Class.new
13 | TestApplicationPolicy.class_eval do
14 | always_allow_connection
15 | regulate_all_broadcasts { |policy| policy.send_all }
16 | end
17 | isomorphic do
18 | Todo.class_eval do
19 | class << self
20 | attr_accessor :current_random_value
21 | end
22 | finder_method :random_item do |i|
23 | find(current_random_value + i.to_i)
24 | end
25 | scope :test_scope, -> { all }
26 | end
27 | end
28 | size_window(:small, :portrait)
29 | 5.times { FactoryBot.create(:todo) }
30 | end
31 |
32 | it "returns the correct value" do
33 | Todo.current_random_value = 1
34 | expect_promise do
35 | HyperMesh.load { Todo.random_item(2) }.then { |todo| todo.id }
36 | end.to eq(3)
37 | end
38 |
39 | it "returns the correct value on the server too" do
40 | Todo.current_random_value = 1
41 | expect(Todo.random_item(2).id).to eq(3)
42 | end
43 |
44 | it "will not reload the value unless forced" do
45 | Todo.current_random_value = 2
46 | expect_promise do
47 | HyperMesh.load { Todo.random_item(2) }.then { |todo| todo.id }
48 | end.to eq(3)
49 | end
50 |
51 | it "can be forced to reload the value" do
52 | expect_promise do
53 | current_value = Todo.random_item(2).id
54 | HyperMesh.load do
55 | new_value = Todo.random_item(2).id
56 | Todo.random_item!(2) if current_value == new_value
57 | new_value
58 | end
59 | end.to eq(4)
60 | end
61 |
62 | it "can apply to a nested scope" do
63 | expect_promise do
64 | HyperMesh.load { Todo.test_scope.random_item(2) }.then { |todo| todo.id }
65 | end.to eq(4)
66 | end
67 | end
68 |
--------------------------------------------------------------------------------
/spec/batch3/many_to_many_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'test_components'
3 | require 'reactive_record_factory'
4 |
5 | describe "many to many associations", js: true do
6 |
7 | before(:each) do
8 | seed_database
9 | end
10 |
11 | before(:each) do
12 | # spec_helper resets the policy system after each test so we have to setup
13 | # before each test
14 | stub_const 'TestApplication', Class.new
15 | stub_const 'TestApplicationPolicy', Class.new
16 | TestApplicationPolicy.class_eval do
17 | always_allow_connection
18 | regulate_all_broadcasts { |policy| policy.send_all }
19 | end
20 | size_window(:small, :portrait)
21 | end
22 |
23 | it "does not effect the base relationship count" do
24 | expect_promise do
25 | ReactiveRecord.load do
26 | TodoItem.find_by_title("a todo for mitch").comments.count
27 | end
28 | end.to be(1)
29 | end
30 |
31 | it "does not effect access to attributes in the base relationship" do
32 | expect_promise do
33 | ReactiveRecord.load do
34 | TodoItem.find_by_title("a todo for mitch").comments.count
35 | end.then do
36 | ReactiveRecord.load do
37 | TodoItem.find_by_title("a todo for mitch").comments.first.user.email
38 | end
39 | end
40 | end.to eq("adamg@catprint.com")
41 | end
42 |
43 | it "can be followed directly" do
44 | expect_promise do
45 | ReactiveRecord.load do
46 | TodoItem.find_by_title("a todo for mitch").commenters.first.email
47 | end
48 | end.to eq("adamg@catprint.com")
49 | end
50 | end
51 |
--------------------------------------------------------------------------------
/spec/batch3/pry_rescue_xspec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe "ServerDataCache" do
4 |
5 | before(:all) do
6 | @current_pry_definition = Object.const_get("Pry") if defined? Pry
7 | @current_pry_rescue_definition = Object.const_get("PryRescue") if defined? PryRescue
8 | end
9 |
10 | after(:all) do
11 | Object.const_set("Pry", @current_pry_definition) if @current_pry_definition
12 | Object.const_set("PryRescue", @current_pry_definition) if @current_pry_rescue_definition
13 | end
14 |
15 | it "behaves normally if there is no pry rescue" do
16 | expect { ReactiveRecord::ServerDataCache[[],[], [["User", ["find", 1], "fake_attribute"]], nil] }.to raise_error(ActiveRecord::RecordNotFound)
17 | end
18 |
19 | context "will use pry rescue if it is defined" do
20 |
21 | before(:all) do
22 | pry = Class.new do
23 | def self.rescue
24 | yield
25 | end
26 | def self.rescued(e)
27 | @last_exception = e
28 | end
29 | def self.last_exception
30 | @last_exception
31 | end
32 | end
33 | Object.const_set("PryRescue", true)
34 | Object.const_set("Pry", pry)
35 | end
36 |
37 | it "and it will still raise an error" do
38 | expect { ReactiveRecord::ServerDataCache[[],[], [["User", ["find", 1], "fake_attribute"]], nil] }.to raise_error(ActiveRecord::RecordNotFound)
39 | end
40 |
41 | it "but it will call Pry.rescued first" do
42 | ReactiveRecord::ServerDataCache[[],[], ["User", ["new", 10852], "fake_attribute"], nil] rescue nil
43 | expect(Pry.last_exception).to be_a(Exception)
44 | end
45 |
46 | end
47 |
48 | end
49 |
--------------------------------------------------------------------------------
/spec/batch3/revert_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'test_components'
3 | require 'reactive_record_factory'
4 | require 'rspec-steps'
5 |
6 | RSpec::Steps.steps "reverting records", js: true do
7 |
8 | before(:all) do
9 | seed_database
10 | end
11 |
12 | before(:step) do
13 | # spec_helper resets the policy system after each test so we have to setup
14 | # before each test
15 | stub_const 'TestApplication', Class.new
16 | stub_const 'TestApplicationPolicy', Class.new
17 | TestApplicationPolicy.class_eval do
18 | always_allow_connection
19 | regulate_all_broadcasts { |policy| policy.send_all }
20 | allow_change(to: :all, on: [:create, :update, :destroy]) { true }
21 | end
22 | size_window(:small, :portrait)
23 | end
24 |
25 | it "finds that the user Adam has not changed yet" do
26 | expect_promise do
27 | ReactiveRecord.load do
28 | User.find_by_first_name("Adam")
29 | end.then { |u| u.changed? }
30 | end.to be_falsy
31 | end
32 |
33 | it "creates a new todo which should be changed (because its new)" do
34 | expect_evaluate_ruby do
35 | TodoItem.new({title: "Adam is not getting this todo"}).changed?
36 | end.to be_truthy
37 | end
38 |
39 | it "adds the todo to adam's todos and expects adam to change" do
40 | expect_evaluate_ruby do
41 | adam = User.find_by_first_name("Adam")
42 | adam.todo_items << TodoItem.find_by_title("Adam is not getting this todo")
43 | adam.changed?
44 | end.to be_truthy
45 | end
46 |
47 | it "will show that the new todo is still changed" do
48 | expect_evaluate_ruby do
49 | TodoItem.find_by_title("Adam is not getting this todo").changed?
50 | end.to be_truthy
51 | end
52 |
53 | it "the todo now has an owner" do
54 | expect_evaluate_ruby do
55 | TodoItem.find_by_title("Adam is not getting this todo").user
56 | end.not_to be_nil
57 | end
58 |
59 | it "can be reverted and the todo will not be changed" do
60 | expect_evaluate_ruby do
61 | todo = TodoItem.find_by_title("Adam is not getting this todo")
62 | todo.revert
63 | todo.changed?
64 | end.not_to be_truthy
65 | end
66 |
67 | it "will not have changed adam" do
68 | expect_evaluate_ruby do
69 | User.find_by_first_name("Adam").changed?
70 | end.not_to be_truthy
71 | end
72 |
73 | it "is time to test going the other way, lets give adam a todo again" do
74 | expect_evaluate_ruby do
75 | new_todo = TodoItem.new({title: "Adam is still not getting this todo"})
76 | adam = User.find_by_first_name("Adam")
77 | adam.todo_items << new_todo
78 | adam.changed?
79 | end.to be_truthy
80 | end
81 |
82 | it "can be reverted" do
83 | expect_evaluate_ruby do
84 | adam = User.find_by_first_name("Adam")
85 | adam.revert
86 | adam.changed?
87 | end.not_to be_truthy
88 | end
89 |
90 | it "finds the todo is still changed" do
91 | expect_evaluate_ruby do
92 | TodoItem.find_by_title("Adam is still not getting this todo").changed?
93 | end.to be_truthy
94 | end
95 |
96 | it "can change an attribute, revert, and make sure nothing else changes" do
97 | original_count = User.find_by_email("mitch@catprint.com").todo_items.count
98 | expect_promise do
99 | mitch = nil
100 | ReactiveRecord.load do
101 | mitch = User.find_by_email("mitch@catprint.com")
102 | mitch.last_name
103 | mitch.todo_items.count
104 | end.then do
105 | mitch.last_name = "xxxx"
106 | mitch.save
107 | end.then do
108 | mitch.revert
109 | mitch.todo_items.count
110 | end
111 | end.to eq(original_count)
112 | end
113 | end
114 |
--------------------------------------------------------------------------------
/spec/batch4/synchromesh_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'test_components'
3 |
4 | describe "HyperMesh", js: true do
5 |
6 | before(:all) do
7 | require 'pusher'
8 | require 'pusher-fake'
9 | Pusher.app_id = "MY_TEST_ID"
10 | Pusher.key = "MY_TEST_KEY"
11 | Pusher.secret = "MY_TEST_SECRET"
12 | require "pusher-fake/support/base"
13 |
14 | Hyperloop.configuration do |config|
15 | config.transport = :pusher
16 | config.channel_prefix = "synchromesh"
17 | config.opts = {app_id: Pusher.app_id, key: Pusher.key, secret: Pusher.secret}.merge(PusherFake.configuration.web_options)
18 | end
19 | end
20 |
21 | before(:each) do
22 | # spec_helper resets the policy system after each test so we have to setup
23 | # before each test
24 | stub_const 'TestApplicationPolicy', Class.new
25 | TestApplicationPolicy.class_eval do
26 | always_allow_connection
27 | regulate_all_broadcasts { |policy| policy.send_all }
28 | end
29 | size_window(:small, :portrait)
30 | end
31 |
32 | it "will synchronize on an attribute update" do
33 | mount "TestComponent"
34 | FactoryBot.create(:test_model, test_attribute: "hello")
35 | page.should have_content("hello")
36 | TestModel.first.update_attribute(:test_attribute, 'goodby')
37 | page.should have_content("goodby")
38 | end
39 |
40 | describe "the .all method" do
41 | before(:each) do
42 | mount "TestComponent"
43 | 5.times { |i| FactoryBot.create(:test_model, test_attribute: "I am item #{i}") }
44 | page.should have_content("5 items")
45 | end
46 |
47 | it "will synchronize on create" do
48 | TestModel.new(test_attribute: "I'm new here!").save
49 | page.should have_content("6 items")
50 | end
51 |
52 | it "will synchronize on destroy" do
53 | TestModel.first.destroy
54 | page.should have_content("4 items")
55 | end
56 | end
57 |
58 | describe "scopes" do
59 | before(:each) do
60 | mount "TestComponent", scope: :active
61 | 5.times { |i| FactoryBot.create(:test_model, test_attribute: "I am item #{i}", completed: false) }
62 | page.should have_content("5 items")
63 | end
64 |
65 | it "will synchronize on create" do
66 | TestModel.new(test_attribute: "I'm new here!", completed: false).save
67 | page.should have_content("6 items")
68 | end
69 |
70 | it "will synchronize on destroy" do
71 | TestModel.first.destroy
72 | page.should have_content("4 items")
73 | end
74 |
75 | it "will syncronize on an update" do
76 | TestModel.first.update_attribute(:completed, true)
77 | page.should have_content("4 items")
78 | end
79 | end
80 | end
81 |
--------------------------------------------------------------------------------
/spec/batch4/zzz_saving_during_commit_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'test_components'
3 | require 'rspec-steps'
4 |
5 | describe "saving during commit", js: true do
6 |
7 | before(:each) do
8 | require 'pusher'
9 | require 'pusher-fake'
10 | Pusher.app_id = "MY_TEST_ID"
11 | Pusher.key = "MY_TEST_KEY"
12 | Pusher.secret = "MY_TEST_SECRET"
13 | require "pusher-fake/support/base"
14 |
15 | Hyperloop.configuration do |config|
16 | config.transport = :pusher
17 | config.channel_prefix = "synchromesh"
18 | config.opts = {app_id: Pusher.app_id, key: Pusher.key, secret: Pusher.secret}.merge(PusherFake.configuration.web_options)
19 | end
20 |
21 | class ActiveRecord::Base
22 | class << self
23 | def public_columns_hash
24 | @public_columns_hash ||= {}
25 | end
26 | end
27 | end
28 |
29 | class CommitIssue < ActiveRecord::Base
30 | def self.build_tables
31 | connection.create_table :commit_issues, force: true do |t|
32 | t.string :name
33 | t.timestamps
34 | end
35 | end
36 | ActiveRecord::Base.public_columns_hash[name] = columns_hash
37 | end
38 |
39 | isomorphic do
40 | class CommitIssue < ActiveRecord::Base
41 | after_create :save_again
42 | def save_again
43 | save
44 | end
45 | end
46 | end
47 |
48 | CommitIssue.build_tables rescue nil
49 |
50 | stub_const 'ApplicationPolicy', Class.new
51 | ApplicationPolicy.class_eval do
52 | always_allow_connection
53 | regulate_all_broadcasts { |policy| policy.send_all }
54 | allow_change(to: :all, on: [:create, :update, :destroy]) { true }
55 | end
56 | size_window(:small, :portrait)
57 | end
58 |
59 | it "broadcast even if saving during after_save" do
60 | CommitIssue.create(name: 1)
61 | mount "CommitIssueTest" do
62 | class CommitIssueTest < React::Component::Base
63 | render do
64 | "all: [#{CommitIssue.all.pluck(:name)}]"
65 | end
66 | end
67 | end
68 | page.should have_content('all: [1]')
69 | CommitIssue.create(name: 2)
70 | page.should have_content('all: [1,2]')
71 | end
72 |
73 | end
74 |
--------------------------------------------------------------------------------
/spec/batch5/get_model_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'test_components'
3 |
4 | describe "ReactiveRecord::ServerDataCache.get_model" do
5 |
6 | before(:each) do
7 | ActiveRecord::Base.public_columns_hash
8 | end
9 |
10 | it "will raise an access violation for an unloaded class" do
11 | expect { ReactiveRecord::ServerDataCache.get_model('UnloadedClass') }.to raise_exception
12 | end
13 |
14 | it "will not raise an access violation for an AR model in the Models folder" do
15 | expect(ReactiveRecord::ServerDataCache.get_model('Comment')).to eq Comment
16 | end
17 |
18 | it "will not raise an access violation if the class is already loaded" do
19 | expect(UnloadedClass).to eq ReactiveRecord::ServerDataCache.get_model('UnloadedClass')
20 | end
21 |
22 | end
23 |
--------------------------------------------------------------------------------
/spec/batch5/load_from_json_xspec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'test_components'
3 | require 'reactive_record_factory'
4 | require 'rspec-steps'
5 |
6 | RSpec::Steps.steps 'Load From Json', js: true do
7 |
8 | before(:all) do
9 | Hyperloop.configuration do |config|
10 | config.transport = :crud_only
11 | end
12 | seed_database
13 | end
14 |
15 | before(:step) do
16 | # spec_helper resets the policy system after each test so we have to setup
17 | # before each test
18 | stub_const 'TestApplication', Class.new
19 | stub_const 'TestApplicationPolicy', Class.new
20 | TestApplicationPolicy.class_eval do
21 | always_allow_connection
22 | regulate_all_broadcasts { |policy| policy.send_all }
23 | allow_change(to: :all, on: [:create, :update, :destroy]) { true }
24 | end
25 | size_window(:small, :portrait)
26 | end
27 |
28 | it '*all key' do
29 | binding.pry
30 | evaluate_ruby do
31 | User.all.count
32 | end
33 | # ["User", "all", "*count"] -> {'User': {'unscoped': {'*count': [4]}}}
34 | evaluate_ruby do
35 | User.collect { |user| user.id }
36 | end
37 | # ["User", "all", "*all"], ["User", "all", "*0", "id"] ->
38 | # {
39 | # 'User': {
40 | # 'all': {
41 | # 1: {id: [1]}},
42 | # 2: {id: [2]}},
43 | # 3: {id: [3]}},
44 | # 4: {id: [4]}}
45 | # '*all': [1, 2, 3, 4]
46 | # }
47 | # }
48 | # }
49 | evaluate_ruby('User.collect { |x| x.first_name }')
50 | # [["User", ["find_by", {"id":1}], "first_name"], ["User", ["find_by", {"id":2}], "first_name"], ["User", ["find_by", {"id":3}], "first_name"], ["User", ["find_by", {"id":4}], "first_name"]]
51 | # {"User":{"[\"find_by\",{\"id\":1}]":{"first_name":["Mitch"],"id":[1],"type":[null]},"[\"find_by\",{\"id\":2}]":{"first_name":["Todd"],"id":[2],"type":[null]},"[\"find_by\",{\"id\":3}]":{"first_name":["Adam"],"id":[3],"type":[null]},"[\"find_by\",{\"id\":4}]":{"first_name":["Test1"],"id":[4],"type":[null]}}}}
52 |
53 | # [["User", "all", "*all"], ["User", "all", "*0", "first_name"]] ->
54 | # {
55 | # 'User': {
56 | # 'all': {
57 | # 1: {first_name: ['Mitch']}},
58 | # 2: {first_name: ['Todd']}},
59 | # 3: {first_name: ['Adam']}},
60 | # 4: {first_name: ['Test1']}}
61 | # '*all': [1, 2, 3, 4]
62 | # }
63 | # }
64 | # }
65 | evaluate_ruby('User.all[2].first_name')
66 | # works like User.all.each...
67 | # but User.all[2] will insert dummy records for items [0], [1] and [2]
68 | # but then we only look up `first_name` for [2], so only
69 | # the [2] (or *2) vector gets pushed to server
70 | # but strangely this results in the whole array being returned anyway
71 | # same as above.
72 | evaluate_ruby('User.all[1].first_name; User.all[3].first_name')
73 | # actually contains 2 requests, but works the same as above, *1, *3
74 | # resolve to just * on server. so *1, and *3 on client are just way to
75 | # identify the records (via vectors) so on return we correctly match up
76 | # and update record [1] and record[3] for example. Point being is that each
77 | # record has a unique vector... Not sure why that is important...
78 |
79 | end
80 |
81 | end
82 |
83 | # require 'spec_helper'
84 | # require 'test_components'
85 | # require 'reactive_record_factory'
86 | # require 'rspec-steps'
87 | #
88 | # RSpec::Steps.steps 'Load From Json', js: true do
89 | #
90 | # before(:all) do
91 | # seed_database
92 | # end
93 | #
94 | # before(:step) do
95 | # # spec_helper resets the policy system after each test so we have to setup
96 | # # before each test
97 | # stub_const 'TestApplication', Class.new
98 | # stub_const 'TestApplicationPolicy', Class.new
99 | # TestApplicationPolicy.class_eval do
100 | # always_allow_connection
101 | # regulate_all_broadcasts { |policy| policy.send_all }
102 | # allow_change(to: :all, on: [:create, :update, :destroy]) { true }
103 | # end
104 | # size_window(:small, :portrait)
105 | # end
106 | #
107 | # it '*all key' do
108 | # #mount "TestComponent2"
109 | # x = evaluate_ruby do
110 | # User.all
111 | # end
112 | # binding.pry
113 | # end
114 | #
115 | # end
116 |
--------------------------------------------------------------------------------
/spec/batch5/save_while_loading_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'test_components'
3 |
4 | describe "save while loading", js: true do
5 |
6 | before(:each) do
7 | # spec_helper resets the policy system after each test so we have to setup
8 | # before each test
9 | stub_const 'TestApplication', Class.new
10 | stub_const 'TestApplicationPolicy', Class.new
11 | TestApplicationPolicy.class_eval do
12 | always_allow_connection
13 | regulate_all_broadcasts { |policy| policy.send_all }
14 | #allow_change(to: User, on: [:update]) { true }
15 | allow_change(to: :all, on: [:create, :update, :destroy]) { true }
16 | end
17 | size_window(:small, :portrait)
18 | end
19 |
20 | it "with new and create" do
21 | user = FactoryBot.create(:user, first_name: 'Ima')
22 | expect_promise do
23 | TodoItem.create(user: User.find_by_first_name('Ima'))
24 | end.to include('success' => true)
25 | expect(user.todo_items.to_a).to match_array([TodoItem.first])
26 | end
27 |
28 | it "with push" do
29 | user = FactoryBot.create(:user, first_name: 'Ima')
30 | expect_promise do
31 | User.find(1).todo_items << TodoItem.new
32 | User.find(1).save
33 | end.to include('success' => true)
34 | expect(user.todo_items).to match_array([TodoItem.first])
35 | end
36 |
37 | it "with assignment" do
38 | user = FactoryBot.create(:user, first_name: 'Ima')
39 | expect_promise do
40 | todo = TodoItem.new
41 | todo.user = User.find_by_first_name('Ima')
42 | todo.save
43 | end.to include('success' => true)
44 | expect(user.todo_items).to match_array([TodoItem.first])
45 | end
46 | end
47 |
--------------------------------------------------------------------------------
/spec/batch6/aaa_update_scopes_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'test_components'
3 | require 'reactive_record_factory'
4 | require 'rspec-steps'
5 |
6 | RSpec::Steps.steps "updating scopes", js: true do
7 |
8 | before(:all) do
9 | seed_database
10 | end
11 |
12 | before(:step) do
13 | # spec_helper resets the policy system after each test so we have to setup
14 | # before each test
15 | stub_const 'TestApplication', Class.new
16 | stub_const 'TestApplicationPolicy', Class.new
17 | TestApplicationPolicy.class_eval do
18 | always_allow_connection
19 | regulate_all_broadcasts { |policy| policy.send_all }
20 | allow_change(to: :all, on: [:create, :update, :destroy]) { true }
21 | end
22 | size_window(:small, :portrait)
23 | end
24 |
25 | it "will update .all and rerender after saving a record" do
26 | mount "TestComponent" do
27 | class TestComponent < React::Component::Base
28 | def render
29 | #div do
30 | "TodoItem.count = #{TodoItem.all.count}".span
31 | #ul { TodoItem.each { |todo| li { todo.id.to_s } }}
32 | #end
33 | end
34 | end
35 | end
36 | starting_count = TodoItem.count
37 | expect(page).to have_content("TodoItem.count = #{starting_count}")
38 | evaluate_ruby { TodoItem.new(title: "play it again sam").save }
39 | expect(page).to have_content("TodoItem.count = #{starting_count+1}")
40 | end
41 |
42 | it "destroying records causes a rerender" do
43 | count = TodoItem.count
44 | while count > 0
45 | expect(page).to have_content("TodoItem.count = #{count}")
46 | evaluate_ruby do
47 | ReactiveRecord.load { TodoItem.last }.then { |todo| todo.destroy }
48 | end
49 | count -= 1
50 | end
51 | expect(page).to have_content("TodoItem.count = 0")
52 | end
53 |
54 | end
55 |
--------------------------------------------------------------------------------
/spec/batch6/inspect_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'test_components'
3 | require 'reactive_record_factory'
4 | require 'rspec-steps'
5 |
6 | RSpec::Steps.steps 'ActiveRecord::Base.inspect displays', js: true do
7 | before(:all) do
8 | require 'pusher'
9 | require 'pusher-fake'
10 | Pusher.app_id = "MY_TEST_ID"
11 | Pusher.key = "MY_TEST_KEY"
12 | Pusher.secret = "MY_TEST_SECRET"
13 | require "pusher-fake/support/base"
14 |
15 | Hyperloop.configuration do |config|
16 | config.transport = :pusher
17 | config.channel_prefix = "synchromesh"
18 | config.opts = {app_id: Pusher.app_id, key: Pusher.key, secret: Pusher.secret}.merge(PusherFake.configuration.web_options)
19 | end
20 | TodoItem.do_not_synchronize
21 | end
22 |
23 | after(:all) do
24 | ['TodoItem'].each do |klass|
25 | Object.send(:remove_const, klass.to_sym) && load("#{klass.underscore}.rb") rescue nil
26 | end
27 | end
28 |
29 | before(:step) do
30 | stub_const 'ApplicationPolicy', Class.new
31 | ApplicationPolicy.class_eval do
32 | always_allow_connection
33 | regulate_all_broadcasts { |policy| policy.send_all }
34 | allow_change(to: :all, on: [:create, :update, :destroy]) { true }
35 | end
36 | size_window(:small, :portrait)
37 | client_option raise_on_js_errors: :off
38 | end
39 |
40 | it 'shows the backing record id and actual record id' do
41 | backing_record_id = evaluate_ruby(
42 | 'ReactiveRecord::Operations::Base::FORMAT % TodoItem.find(999).backing_record.object_id'
43 | )
44 | record_id = evaluate_ruby(
45 | 'ReactiveRecord::Operations::Base::FORMAT % TodoItem.find(999).object_id'
46 | )
47 | expect_evaluate_ruby('TodoItem.find_by_id(999).inspect')
48 | .to match(/\"test\"}\] >/
55 | end
56 |
57 | it 'loading records with the vector' do
58 | expect_evaluate_ruby do
59 | TodoItem.find_by_title('test2').inspect
60 | end.to match(/\"test2\"}\] >/)
61 | end
62 |
63 | it 'loaded records with the primary key value' do
64 | TodoItem.create(title: 'test3')
65 | expect_promise do
66 | ReactiveRecord.load do
67 | TodoItem.find_by_title('test3').itself
68 | end.then do |loaded_item|
69 | loaded_item.inspect
70 | end
71 | end.to match //
72 | end
73 |
74 | it 'changed records with the new attributes' do
75 | expect_evaluate_ruby do
76 | TodoItem.find_by_title('test3').tap do |todo|
77 | todo.title = 'new title'
78 | todo.user = User.new
79 | end.inspect
80 | end.to match /\[\"test3\", \"new title\"\]}\] >/
81 | end
82 |
83 | it 'destroyed records with the primary key value' do
84 | expect_promise do
85 | todo = TodoItem.find_by_title('test3')
86 | todo.destroy.then do
87 | todo.inspect
88 | end
89 | end.to match //
90 | end
91 |
92 | it 'new records with the errors after attempting to save' do
93 | TodoItem.validates :title, presence: true
94 | expect_promise do
95 | todo = TodoItem.new(description: 'this has no title')
96 | todo.save.then do
97 | todo.inspect
98 | end
99 | end.to match /\[\"can't be blank\"\]}\] >/
100 | end
101 |
102 | it 'updated records with the errors after attempting to save' do
103 | expect_promise do
104 | todo = TodoItem.new(title: 'test4')
105 | todo.save.then do
106 | todo.title = nil
107 | todo.save
108 | end.then do
109 | todo.inspect
110 | end
111 | end.to match /\[\"can't be blank\"\]}\] >/
112 | end
113 |
114 | it 'new records with the errors after attempting to save (deprecated error handler)' do
115 |
116 | evaluate_ruby do
117 | class ReactiveRecord::Base
118 | def errors
119 | @errors ||= ActiveModel::Error.new
120 | end
121 | end
122 | end
123 |
124 | TodoItem.validates :title, presence: true
125 | expect_promise do
126 | todo = TodoItem.new(description: 'this has no title')
127 | todo.save.then do
128 | todo.inspect
129 | end
130 | end.to match /\[\"can't be blank\"\]}\] >/
131 | end
132 |
133 | it 'updated records with the errors after attempting to save (deprecated error handler)' do
134 | expect_promise do
135 | todo = TodoItem.new(title: 'test5')
136 | todo.save.then do
137 | todo.title = nil
138 | todo.save
139 | end.then do
140 | todo.inspect
141 | end
142 | end.to match /\[\"can't be blank\"\]}\] >/
143 | end
144 | end
145 |
--------------------------------------------------------------------------------
/spec/batch6/on_fetch_error_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'test_components'
3 |
4 | describe "Hyperloop.on_error (for fetches) ", js: true do
5 |
6 | before(:all) do
7 | require 'pusher'
8 | require 'pusher-fake'
9 | Pusher.app_id = "MY_TEST_ID"
10 | Pusher.key = "MY_TEST_KEY"
11 | Pusher.secret = "MY_TEST_SECRET"
12 | require "pusher-fake/support/base"
13 |
14 | Hyperloop.configuration do |config|
15 | config.transport = :pusher
16 | config.channel_prefix = "synchromesh"
17 | config.opts = {app_id: Pusher.app_id, key: Pusher.key, secret: Pusher.secret}.merge(PusherFake.configuration.web_options)
18 | end
19 |
20 | end
21 |
22 | before(:each) do
23 | # spec_helper resets the policy system after each test so we have to setup
24 | # before each test
25 | stub_const 'TestApplicationPolicy', Class.new
26 | TestApplicationPolicy.class_eval do
27 | regulate_class_connection { self }
28 | end
29 | ActiveRecord::Base.regulate_scope unscoped: nil
30 | ApplicationController.acting_user = User.new(first_name: 'fred')
31 | size_window(:large, :landscape)
32 | client_option raise_on_js_errors: :off
33 | end
34 |
35 | after(:each) do
36 | ['ApplicationRecord', 'TodoItem', 'Comment'].each do |klass|
37 | Object.send(:remove_const, klass.to_sym) && load("#{klass.underscore}.rb") rescue nil
38 | end
39 | ApplicationController.acting_user = nil
40 | end
41 |
42 | it 'call Hyperloop.on_error for access violations' do
43 | TodoItem.class_eval do
44 | TodoItem.regulate_relationship(:comments) { acting_user == user }
45 | end
46 | todo_item1 = TodoItem.create(user: ApplicationController.acting_user)
47 | todo_item2 = TodoItem.create(user: nil)
48 | Comment.create(todo_item: todo_item1)
49 | Comment.create(todo_item: todo_item1)
50 | # expect(Hyperloop).to receive(:on_error).once.with(
51 | # Hyperloop::AccessViolation,
52 | # :fetch_error,
53 | # 'acting_user' => ApplicationController.acting_user,
54 | # 'controller' => kind_of(ActionController::Base),
55 | # 'pending_fetches' => [['TodoItem', ['find_by', { 'id' => 2 }], 'comments', '*count']],
56 | # 'models' => [],
57 | # 'associations' => []
58 | # )
59 | expect(Hyperloop).to receive(:on_error).once.with(
60 | Hyperloop::AccessViolation,
61 | :scoped_permission_not_granted,
62 | anything
63 | )
64 | expect_promise("ReactiveRecord.load { TodoItem.find(#{todo_item1.id}).comments.count }")
65 | .to eq(2)
66 | expect_promise("ReactiveRecord.load { TodoItem.find(#{todo_item2.id}).comments.count }")
67 | .not_to eq(0)
68 | end
69 |
70 | it 'call ReactiveRecord.on_fetch_error for errors raised by models' do
71 | TodoItem.class_eval do
72 | def title
73 | raise 'Bogus'
74 | end
75 | end
76 | TodoItem.create(user: nil)
77 | expect(Hyperloop).to receive(:on_error).once.with(
78 | Exception,
79 | :fetch_error,
80 | hash_including(:acting_user, :controller, :pending_fetches, :models, :associations)
81 | )
82 | evaluate_ruby('TodoItem.find(1).title')
83 | wait_for_ajax
84 | end
85 | end
86 |
--------------------------------------------------------------------------------
/spec/batch6/server_method_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'rspec-steps'
3 |
4 | RSpec::Steps.steps 'server_method', js: true do
5 |
6 | before(:each) do
7 | require 'pusher'
8 | require 'pusher-fake'
9 | Pusher.app_id = "MY_TEST_ID"
10 | Pusher.key = "MY_TEST_KEY"
11 | Pusher.secret = "MY_TEST_SECRET"
12 | require "pusher-fake/support/base"
13 |
14 | Hyperloop.configuration do |config|
15 | config.transport = :pusher
16 | config.channel_prefix = "synchromesh"
17 | config.opts = {app_id: Pusher.app_id, key: Pusher.key, secret: Pusher.secret}.merge(PusherFake.configuration.web_options)
18 | end
19 |
20 | #User.do_not_synchronize
21 | end
22 |
23 | before(:step) do
24 | stub_const 'TestApplicationPolicy', Class.new
25 | TestApplicationPolicy.class_eval do
26 | always_allow_connection
27 | regulate_all_broadcasts { |policy| policy.send_all }
28 | allow_change(to: :all, on: [:create, :update, :destroy]) { true }
29 | end
30 | ApplicationController.acting_user = nil
31 | end
32 |
33 | it "can call a server method" do
34 | isomorphic do
35 | TodoItem.class_eval do
36 | class << self
37 | attr_writer :server_method_count
38 |
39 | def server_method_count
40 | @server_method_count ||= 0
41 | end
42 | end
43 | server_method(:test, default: 0) { TodoItem.server_method_count += 1 }
44 | end
45 | TestModel.server_method(:test) { child_models.count }
46 | end
47 | TodoItem.create
48 | mount 'ServerMethodTester' do
49 | class ServerMethodTester < Hyperloop::Component
50 | render(DIV) do
51 | "test = #{TodoItem.first.test}"
52 | end
53 | end
54 | end
55 | expect(page).to have_content('test = 1')
56 | end
57 |
58 | it "can update the server method" do
59 | evaluate_ruby("TodoItem.first.test!")
60 | expect(page).to have_content('test = 2')
61 | end
62 |
63 | it "when updating the server method it returns the current value while waiting for the promise" do
64 | expect_evaluate_ruby("TodoItem.first.test!").to eq(2)
65 | end
66 |
67 | it "returns the default value on the first call while waiting for the promise" do
68 | expect_evaluate_ruby("TodoItem.new.test").to eq(0)
69 | end
70 |
71 | it "works with the load method" do
72 | expect_promise do
73 | new_todo = TodoItem.new
74 | ReactiveRecord.load do
75 | new_todo.test
76 | end
77 | end.to eq(5)
78 | end
79 |
80 | it "the server method can access any unsaved associations" do
81 | expect_promise do
82 | test_model = TestModel.new
83 | ChildModel.new(test_model: test_model)
84 | ReactiveRecord.load do
85 | test_model.test
86 | end
87 | end.to eq(1)
88 | expect(TestModel.count).to be_zero
89 | expect(ChildModel.count).to be_zero
90 | end
91 | end
92 |
--------------------------------------------------------------------------------
/spec/batch6/speed_improvement.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'test_components'
3 | #require 'reactive_record_factory'
4 | #require 'rspec-steps'
5 |
6 | #RSpec::Steps.steps 'Load From Json', js: true do
7 | describe "speed tests", js: true do
8 |
9 | def build_records(users, todos_per_user, comments_per_user)
10 | User.destroy_all
11 | Comment.destroy_all
12 | TodoItem.destroy_all
13 | users.times do |u|
14 | user = User.create(name: "User#{u}")
15 | todos_per_user.times do |t|
16 | todo = TodoItem.create(title: "Todo #{u} - #{t}", user: user)
17 | comments_per_user.times do |c|
18 | Comment.create(comment: "Comment #{c} #{u} - #{t}", user: user, todo_item: todo)
19 | end
20 | end
21 | end
22 | end
23 |
24 | def measure(test, users, todos_per_user, comments_per_user)
25 | build_records(users, todos_per_user, comments_per_user)
26 | evaluate_promise("SpeedTester.load_all(#{test})")
27 | end
28 |
29 | before(:all) do
30 | Hyperloop.configuration do |config|
31 | config.transport = :crud_only
32 | end
33 | end
34 |
35 | before(:each) do
36 | stub_const 'TestApplication', Class.new
37 | stub_const 'TestApplicationPolicy', Class.new
38 | TestApplicationPolicy.class_eval do
39 | always_allow_connection
40 | regulate_all_broadcasts { |policy| policy.send_all }
41 | allow_change(to: :all, on: [:create, :update, :destroy]) { true }
42 | end
43 | on_client do
44 | class SpeedTester < Hyperloop::Component
45 | def self.load_all(id)
46 | React::IsomorphicHelpers.load_context
47 | start_time = Time.now
48 | timer_promise = Promise.new
49 | case id
50 | when 1
51 | ReactiveRecord.load do
52 | Comment.all.collect { |todo| todo.id }
53 | end.then do |ids|
54 | ReactiveRecord.load do
55 | ids.each do |id|
56 | Comment.find(id).todo_item.user.name
57 | end
58 | end
59 | end
60 | when 2
61 | ReactiveRecord.load do
62 | User.each do |user|
63 | user.name
64 | user.todo_items.each do |todo|
65 | todo.title
66 | todo.comments.each do |comment|
67 | comment.comment
68 | end
69 | end
70 | end
71 | end
72 | when 3
73 | ReactiveRecord.load do
74 | Comment.all.collect { |todo| todo.id }
75 | end.then do |ids|
76 | ReactiveRecord.load do
77 | ids.each do |id|
78 | Comment.find(id).comment
79 | end
80 | end
81 | end
82 | # puts gets clock to sync otherwise its slightly inaccurate
83 | end.then do
84 | after(0) { timer_promise.resolve(Time.now-start_time) }
85 | end
86 | timer_promise
87 | end
88 |
89 | after_mount do
90 | @start_time = Time.now
91 | end
92 |
93 | render(DIV) do
94 | DIV { "fetched in #{Time.now-@start_time} seconds"} if @start_time
95 | User.each do |user|
96 | LI do
97 | DIV do
98 | user.name.span
99 | UL do
100 | user.todo_items.each do |todo|
101 | LI do
102 | DIV do
103 | todo.title.span
104 | UL do
105 | todo.comments.each do |comment|
106 | LI { comment.comment }
107 | end
108 | end
109 | end
110 | end
111 | end
112 | end
113 | end
114 | end
115 | end
116 | end
117 | end
118 | end
119 | size_window(:large, :landscape)
120 | end
121 |
122 | it "can display 9 items" do
123 | binding.pry
124 | end
125 | end
126 |
127 | =begin
128 | results:
129 | with improvements:
130 | measure(1, 9, 9, 9)
131 | ********* Total Time 10.760204 ***********************
132 | process_vectors: 7.729447 (71)%
133 | building cache_items: 7.713941 (71)%
134 | save_records: 2.569092 (23)%
135 | as_json: 0.457144 (4)%
136 | active_record: 0.44621399999999695 (4)%
137 | apply_method lookup: 0.06301599999999652 (0)%
138 | root_lookup: 0.009902000000000168 (0)%
139 | public_columns_hash: 0.003181 (0)%
140 | ********* Other Time ***********************
141 | measure(1, 7, 7, 7) (1372 data points fetched) vs 107 seconds w/o fixes
142 | ********* Total Time 3.230448 ***********************
143 | process_vectors: 1.854479 (57)%
144 | building cache_items: 1.847171 (57)%
145 | save_records: 1.197451 (37)%
146 | active_record: 0.19789300000000268 (6)%
147 | as_json: 0.175386 (5)%
148 | apply_method lookup: 0.030857000000000908 (0)%
149 | root_lookup: 0.004622000000000015 (0)%
150 | public_columns_hash: 0.001442 (0)%
151 | ********* Other Time ***********************
152 | processed in 2.449s with getters fixed
153 | processed in 2.420s with setters fixed
154 | processed in 2.392s without React set or get state
155 | processed in 1.551s with hashing used instead of detects
156 | =end
157 |
--------------------------------------------------------------------------------
/spec/batch7/aaa-unit_tests/aggregation_experiments.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'rspec-steps'
3 |
4 | describe "aggregation experiments" do
5 | it "even AR models (that are aggregates) are immutable" do
6 | user = User.new
7 | address = user.address
8 | address.save
9 | address.reload
10 | user.save
11 | user.reload
12 | expect(user.address).not_to eq(address)
13 | user.address = address
14 | user.save
15 | user.reload
16 | expect(user.address).to eq(address)
17 | end
18 |
19 | it "updating an aggregate does NOT change the container" do
20 | user = User.new
21 | expect { user.address.state = "philly" }.to raise_error
22 | address = user.address
23 | user.address.save
24 | expect(user).not_to be_changed
25 | user.address = address
26 | expect(user).to be_changed
27 | end
28 |
29 | end
30 |
--------------------------------------------------------------------------------
/spec/batch7/aaa-unit_tests/aggregations_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'rspec-steps'
3 |
4 | RSpec::Steps.steps 'Aggregation Reflection', js: true do
5 |
6 | it "knows the aggregates class" do
7 | expect_evaluate_ruby do
8 | User.reflect_on_aggregation(:address).klass
9 | end.to eq('Address')
10 | end
11 |
12 | it "knows the aggregates attribute" do
13 | expect_evaluate_ruby do
14 | User.reflect_on_aggregation(:address).attribute
15 | end.to eq('address')
16 | end
17 |
18 | it "knows all the Aggregates" do
19 | expect_evaluate_ruby do
20 | User.reflect_on_all_aggregations.count
21 | end.to eq(3)
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/spec/batch7/aaa-unit_tests/ar_basics_tbdspec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | #Opal::RSpec::Runner.autorun
3 |
4 | class BaseClass < ActiveRecord::Base
5 | end
6 |
7 | class SubClass < BaseClass
8 | end
9 |
10 | class Funky < ActiveRecord::Base
11 | self.primary_key = :funky_id
12 | self.inheritance_column = :funky_type
13 | end
14 |
15 | class BelongsTo < ActiveRecord::Base
16 | belongs_to :has_many
17 | belongs_to :has_one
18 | belongs_to :best_friend, class_name: "HasMany", foreign_key: :bf_id
19 | end
20 |
21 | class HasMany < ActiveRecord::Base
22 | has_many :belongs_to
23 | has_many :best_friends, class_name: "BelongsTo", foreign_key: :bf_id
24 | end
25 |
26 | class HasOne < ActiveRecord::Base
27 | has_one :belongs_to
28 | end
29 |
30 | class Scoped < ActiveRecord::Base
31 | scope :only_those_guys, -> () {}
32 | end
33 |
34 | describe "ActiveRecord" do
35 |
36 | before(:all) { React::IsomorphicHelpers.load_context }
37 |
38 | after(:each) { React::API.clear_component_class_cache }
39 |
40 | # uncomment if you are having trouble with tests failing. One non-async test must pass for things to work
41 |
42 | # describe "a passing dummy test" do
43 | # it "passes" do
44 | # expect(true).to be(true)
45 | # end
46 | # end
47 |
48 | describe "reactive_record active_record base methods" do
49 |
50 | it "will find the base class" do
51 | expect(SubClass.base_class).to eq(BaseClass)
52 | end
53 |
54 | it "knows the primary key" do
55 | expect(BaseClass.primary_key).to eq(:id)
56 | end
57 |
58 | it "can override the primary key" do
59 | expect(Funky.primary_key).to eq(:funky_id)
60 | end
61 |
62 | it "knows the inheritance column" do
63 | expect(BaseClass.inheritance_column).to eq(:type)
64 | end
65 |
66 | it "can override the inheritance column" do
67 | expect(Funky.inheritance_column).to eq(:funky_type)
68 | end
69 |
70 | it "knows the model name" do
71 | expect(BaseClass.model_name).to eq("BaseClass")
72 | end
73 |
74 | it "can find a record by id" do
75 | expect(BaseClass.find(12).id).to eq(12)
76 | end
77 |
78 | it "has a find_by_xxx method" do
79 | expect(BaseClass.find_by_xxx("beer").xxx).to eq("beer")
80 | end
81 |
82 | it "will correctly infer the model type from the inheritance column" do
83 | expect(BaseClass.find_by_type("SubClass").class).to eq(SubClass)
84 | expect(BaseClass.find_by_type(nil).class).to eq(BaseClass)
85 | end
86 |
87 | it "can have a has_many association" do
88 | expect(HasMany.reflect_on_association(:belongs_to).klass.reflect_on_association(:has_many).klass).to eq(HasMany)
89 | end
90 |
91 | it "can have a has_one association" do
92 | expect(HasOne.reflect_on_association(:belongs_to).klass.reflect_on_association(:has_one).klass).to eq(HasOne)
93 | end
94 |
95 | it "can override the class and foreign_key values when creating an association" do
96 | reflection = HasMany.reflect_on_association(:best_friends)
97 | expect(reflection.klass).to eq(BelongsTo)
98 | expect(reflection.association_foreign_key).to eq(:bf_id)
99 | end
100 |
101 | it "can have a scoping method" do
102 | expect(Scoped.only_those_guys.respond_to? :all).to be_truthy
103 | end
104 |
105 | it "can type check parameters" do
106 | expect(SubClass._react_param_conversion({attr1: 1, attr2: 2, type: "SubClass", id: 123}.to_n, :validate_only)).to be(true)
107 | end
108 |
109 | it "can type check parameters with native wrappers" do
110 | expect(SubClass._react_param_conversion(Native({attr1: 1, attr2: 2, type: "SubClass", id: 123}.to_n), :validate_only)).to be(true)
111 | end
112 |
113 | it "will fail type checking if type does not match" do
114 | expect(SubClass._react_param_conversion({attr1: 1, attr2: 2, type: nil, id: 123}.to_n, :validate_only)).to be_falsy
115 | end
116 |
117 | it "will convert a hash to an instance" do
118 | ar = SubClass._react_param_conversion({attr1: 1, attr2: 2, type: "SubClass", id: 123}.to_n)
119 | expect(ar.attr1).to eq(1)
120 | expect(ar.attr2).to eq(2)
121 | expect(ar.id).to eq(123)
122 | end
123 |
124 | end
125 |
126 | end
127 |
--------------------------------------------------------------------------------
/spec/batch7/aaa-unit_tests/association_reflection_tbdspec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | #require 'user'
3 | #require 'todo_item'
4 |
5 |
6 | class Thing < ActiveRecord::Base
7 | belongs_to :bucket
8 | end
9 |
10 | class Bucket < ActiveRecord::Base
11 | has_many :things
12 | end
13 |
14 | class OtherThing < ActiveRecord::Base
15 | has_many :things, through: :thing_group
16 | end
17 |
18 | describe "ActiveRecord" do
19 |
20 | after(:each) { React::API.clear_component_class_cache }
21 |
22 | # uncomment if you are having trouble with tests failing. One non-async test must pass for things to work
23 |
24 | # describe "a passing dummy test" do
25 | # it "passes" do
26 | # expect(true).to be(true)
27 | # end
28 | # end
29 |
30 |
31 | describe "Association Reflection" do
32 |
33 | it "knows the foreign key of a belongs_to relationship" do
34 | expect(Thing.reflect_on_association(:bucket).association_foreign_key).to eq(:bucket_id)
35 | end
36 |
37 | it "knows the foreign key of a has_many relationship" do
38 | expect(Bucket.reflect_on_association(:things).association_foreign_key).to eq(:bucket_id)
39 | end
40 |
41 | it "knows the attribute name" do
42 | expect(Bucket.reflect_on_association(:things).attribute).to eq(:things)
43 | end
44 |
45 | it "knows the associated klass" do
46 | expect(Bucket.reflect_on_association(:things).klass).to eq(Thing)
47 | end
48 |
49 | it "knows the macro" do
50 | expect(Bucket.reflect_on_association(:things).macro).to eq(:has_many)
51 | end
52 |
53 | it "knows the inverse" do
54 | expect(Bucket.reflect_on_association(:things).inverse_of).to eq(:bucket)
55 | end
56 |
57 | it "knows if the association is a collection" do
58 | expect(Bucket.reflect_on_association(:things).collection?).to be_truthy
59 | end
60 |
61 | it "knows if the association is not a collection" do
62 | expect(Thing.reflect_on_association(:bucket).collection?).to be_falsy
63 | end
64 |
65 | it "knows the associated klass of a has_many_through relationship" do
66 | expect(OtherThing.reflect_on_association(:things).klass).to eq(Thing)
67 | end
68 |
69 | it "knows a has_many_through is a collection" do
70 | expect(OtherThing.reflect_on_association(:things).collection?).to be_truthy
71 | end
72 |
73 | end
74 |
75 | end
76 |
--------------------------------------------------------------------------------
/spec/batch7/aaa-unit_tests/dummy_value_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'test_components'
3 | require 'reactive_record_factory'
4 | require 'rspec-steps'
5 |
6 | RSpec::Steps.steps 'DummyValue', js: true do
7 | before(:step) do
8 | # spec_helper resets the policy system after each test so we have to setup
9 | # before each test
10 | # stub_const 'TestApplication', Class.new
11 | # stub_const 'TestApplicationPolicy', Class.new
12 | # TestApplicationPolicy.class_eval do
13 | # always_allow_connection
14 | # regulate_all_broadcasts { |policy| policy.send_all }
15 | # allow_change(to: :all, on: [:create, :update, :destroy]) { true }
16 | # end
17 | size_window(:small, :portrait)
18 | end
19 |
20 | it 'works with string interpolation (defines a JS .toString method)' do
21 | expect_evaluate_ruby do
22 | column_hash = { default: 'foo', sql_type_metadata: { type: 'text' } }
23 | "value = #{ReactiveRecord::Base::DummyValue.new(column_hash)}"
24 | end.to eq('value = foo')
25 | end
26 | end
27 |
--------------------------------------------------------------------------------
/spec/bin/firebug-2.0.13-fx.xpi:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ruby-hyperloop/hyper-mesh/d29f00b16efbdd25419f07c7590505de4d3ba42a/spec/bin/firebug-2.0.13-fx.xpi
--------------------------------------------------------------------------------
/spec/examples/random_examples.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'test_components'
3 |
4 | describe "random examples", js: true do
5 |
6 | it "can pass an array subclass as a param" do
7 | mount "Tester" do
8 | class SubArray < Array
9 | end
10 |
11 | class HelloWorld < React::Component::Base
12 | param :array, type: SubArray
13 | render do
14 | i = 10
15 | div {
16 | div { "params.array.is_a? #{params.array.class}" }
17 | params.array.each {|i| h1 {i.to_s}}
18 | }
19 | end
20 | end
21 |
22 | class Tester < React::Component::Base
23 | def render
24 | normal_array = [1, 2]
25 | sub_array = SubArray.new
26 | sub_array << 1; sub_array << 2
27 | DIV do
28 | # this works
29 | HelloWorld(array: normal_array)
30 | # this doesn't
31 | DIV { "out here a sub_array is a #{sub_array.class}" }
32 | HelloWorld(array: sub_array)
33 | end
34 | end
35 | end
36 | end
37 | pause
38 | end
39 |
40 | it "pass a native hash as a param" do
41 | mount "Tester" do
42 |
43 | class React::RenderingContext
44 | def self.remove_nodes_from_args(args)
45 | args[0].each do |key, value|
46 | begin
47 | value.as_node if value.is_a?(Element)
48 | rescue Exception
49 | end
50 | end if args[0] && args[0].is_a?(Hash)
51 | end
52 | end
53 |
54 | class HelloWorld < React::Component::Base
55 | param :hash
56 | render do
57 | debugger
58 | "hash[key] = #{`#{params.hash['key']}`}"
59 | end
60 | end
61 |
62 | class Tester < React::Component::Base
63 | def render
64 | HelloWorld(hash: `{key: 'the key'}`)
65 | end
66 | end
67 | end
68 | page.should have_content("hash[key] = 'the key'")
69 | pause
70 | end
71 |
72 |
73 | it "can destroy on the fly" do
74 |
75 | 5.times do |i|
76 | FactoryBot.create(:test_model, test_attribute: "I am model #{i}")
77 | end
78 |
79 | mount "RecordsComp" do
80 | class RecordsComp < React::Component::Base
81 | # you had state.credits as an expression... not sure that is what you wanted
82 | render(:div, class: "state.credits") do
83 | h2.title { 'Records' }
84 | #RecordFormComp()
85 | hr { nil }
86 | table.table.table_bordered do
87 | thead { tr { th { 'Date' }
88 | th { 'Title' }
89 | #th { 'Amount' }
90 | th { 'Actions' } } }
91 | tbody do
92 | TestModel.each do |record|
93 | RecordComp key: record[:id], record: record
94 | end
95 | end
96 | end
97 | end
98 | end
99 |
100 | class RecordComp < React::Component::Base
101 | param :key, type: String
102 | param :record, type: TestModel # type is optional here
103 |
104 | def handle_delete
105 | params.record.destroy do |result|
106 | alert 'unable to delete record' unless result
107 | end
108 | end
109 |
110 | def render
111 | tr do
112 | # currently you should access attributes using
113 | # dot notation only. Currently the [] operator
114 | # accesses record values directly without setting up
115 | # any reactive response. This will probably change
116 | # with release of Hypermesh
117 | td { params.record.created_at }
118 | td { params.record.test_attribute }
119 | #td { amount_format(params.record[:amount]) }
120 | td { a.btn.btn_danger { 'Delete' }.on(:click) { handle_delete } }
121 | end
122 | end
123 |
124 | def amount_format(amount)
125 | '$ ' + amount.to_s.reverse.gsub(/...(?!-)(?=.)/,'\&,').reverse
126 | end
127 | end
128 | end
129 |
130 | pause
131 |
132 | end
133 | end
134 |
--------------------------------------------------------------------------------
/spec/factories/child_model.rb:
--------------------------------------------------------------------------------
1 | FactoryBot.define do
2 |
3 | factory :child_model
4 |
5 | end
6 |
--------------------------------------------------------------------------------
/spec/factories/comment.rb:
--------------------------------------------------------------------------------
1 | FactoryBot.define do
2 |
3 | factory :comment
4 |
5 | end
6 |
--------------------------------------------------------------------------------
/spec/factories/test_models.rb:
--------------------------------------------------------------------------------
1 | FactoryBot.define do
2 |
3 | factory :test_model
4 |
5 | end
6 |
--------------------------------------------------------------------------------
/spec/factories/todo.rb:
--------------------------------------------------------------------------------
1 | FactoryBot.define do
2 |
3 | factory :todo
4 |
5 | end
6 |
--------------------------------------------------------------------------------
/spec/factories/user.rb:
--------------------------------------------------------------------------------
1 | FactoryBot.define do
2 |
3 | factory :user
4 |
5 | end
6 |
--------------------------------------------------------------------------------
/spec/failing_tests.txt:
--------------------------------------------------------------------------------
1 | all while_loading specs
2 |
--------------------------------------------------------------------------------
/spec/play_ground.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe "playground", js: true do
4 |
5 | it "works!" do
6 | mount "SuperTest::Derp" do
7 | module SuperTest
8 | end
9 |
10 | SuperTest.const_set :Derp, Class.new(React::Component::Base)
11 | SuperTest::Derp.class_eval do
12 | inherited(self)
13 | def render
14 | p {"hi"}
15 | end
16 | end
17 | SuperTest::Derp.hypertrace instrument: :all
18 | end
19 | page.should have_content('hi')
20 | pause
21 | end
22 |
23 | it "works! as well" do
24 |
25 | mount "SuperTest::Derp" do
26 | module SuperTest
27 | end
28 |
29 | # YOU HAVE TO have the parens otherwise ruby rules say that
30 | # the block will be sent to const_set (where it is ignored)
31 |
32 | SuperTest.const_set(:Derp, Class.new(React::Component::Base) do
33 | inherited(self)
34 | def render
35 | p {"hi"}
36 | end
37 | end)
38 | SuperTest::Derp.hypertrace instrument: :all
39 | end
40 | page.should have_content('hi')
41 | pause
42 | end
43 |
44 | it "works!?" do
45 |
46 | mount "SuperTest::Derp" do
47 | module SuperTest
48 | end
49 |
50 | SuperTest.const_set(:Derp, Class.new do
51 | #include React::Component
52 | #inherited(self)
53 | def render
54 | p {"hi"}
55 | end
56 | end)
57 | SuperTest::Derp.hypertrace instrument: :all
58 | end
59 | pause
60 | page.should have_content('hi')
61 | pause
62 | end
63 |
64 | end
65 |
--------------------------------------------------------------------------------
/spec/reactive_record_factory.rb:
--------------------------------------------------------------------------------
1 | def seed_database
2 | users = [
3 | ["Mitch", "VanDuyn", "mitch@catprint.com"],
4 | ["Todd", "Russell", "todd@catprint.com"],
5 | ["Adam", "George", "adamg@catprint.com"],
6 | ["Test1", "Test1", "test1@catprint.com"]
7 | ]
8 |
9 | users.each do |first_name, last_name, email|
10 | User.create({
11 | first_name: first_name, last_name: last_name, email: email,
12 | address_street: "4348 Culver Road", address_city: "Rochester", address_state: "NY", address_zip: "14617"
13 | }
14 | #without_protection: true
15 | )
16 | end
17 |
18 | todo_items = [
19 | {
20 | title: "a todo for mitch",
21 | description: "mitch has a big fat todo to do!",
22 | user: User.find_by_email("mitch@catprint.com"),
23 | comments: [{user: User.find_by_email("adamg@catprint.com"), comment: "get it done mitch"}]
24 | },
25 | {
26 | title: "another todo for mitch",
27 | description: "mitch has too many todos",
28 | user: User.find_by_email("mitch@catprint.com")
29 | },
30 | {
31 | title: "do it again Todd",
32 | description: "Todd please do that great thing you did again",
33 | user: User.find_by_email("todd@catprint.com")
34 | },
35 | {
36 | title: "no user todo",
37 | description: "the description"
38 | },
39 | {
40 | title: "test 1 todo 1", description: "test 1 todo 1", user: User.find_by_email("test1@catprint.com"),
41 | comments: [
42 | {user: User.find_by_email("mitch@catprint.com"), comment: "test 1 todo 1 comment 1"},
43 | {user: User.find_by_email("mitch@catprint.com"), comment: "test 1 todo 1 comment 2"}
44 | ]
45 | },
46 | {
47 | title: "test 1 todo 2", description: "test 1 todo 2", user: User.find_by_email("test1@catprint.com"),
48 | comments: [
49 | {user: User.find_by_email("mitch@catprint.com"), comment: "test 1 todo 2 comment 1"},
50 | {user: User.find_by_email("mitch@catprint.com"), comment: "test 1 todo 2 comment 2"}
51 | ]
52 | }
53 | ]
54 |
55 | todo_items.each do |attributes|
56 | comments = attributes.delete(:comments) || []
57 | todo = TodoItem.create(attributes) #, without_protection: true)
58 | comments.each do |attributes|
59 | Comment.create(attributes.merge(todo_item: todo))
60 | end
61 | end
62 | end
63 |
--------------------------------------------------------------------------------
/spec/test_app/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 |
3 | # Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
4 | gem 'rails'
5 | # Use sqlite3 as the database for Active Record
6 | # gem 'sqlite3'
7 | gem 'mysql2'
8 | # Use SCSS for stylesheets
9 | # gem 'sass-rails'
10 | # Use Uglifier as compressor for JavaScript assets
11 | # gem 'uglifier'
12 | # Use CoffeeScript for .coffee assets and views
13 | # gem 'coffee-rails'
14 | # See https://github.com/rails/execjs#readme for more supported runtimes
15 | # gem 'mini_racer', platforms: :ruby
16 |
17 | # Turbolinks makes following links in your web application faster. Read more: https://github.com/rails/turbolinks
18 | gem 'turbolinks'
19 | # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
20 | gem 'jbuilder'
21 |
22 | # Use ActiveModel has_secure_password
23 | # gem 'bcrypt', '~> 3.1.7'
24 |
25 | # Use Unicorn as the app server
26 | # gem 'unicorn'
27 |
28 | # Use Capistrano for deployment
29 | # gem 'capistrano-rails', group: :development
30 |
31 | group :development, :test do
32 | # Call 'byebug' anywhere in the code to stop execution and get a debugger console
33 | gem 'byebug'
34 | gem 'pry'
35 | gem 'hyper-trace'
36 | end
37 |
38 | group :development do
39 | # Access an IRB console on exception pages or by using <%= console %> in views
40 | gem 'web-console'
41 | # gem 'reactrb-rails-generator'
42 | gem 'puma'
43 | # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
44 | gem 'spring'
45 | end
46 |
47 | # gem 'opal'
48 | gem "opal", '>= 0.11.0'
49 | gem "opal-jquery", git: "https://github.com/opal/opal-jquery.git", branch: "master"
50 | gem "opal-rails", '~> 0.9.4'
51 | gem "opal-activesupport", '~> 0.3.1'
52 | gem 'react-rails', '>= 2.4.0', '< 2.5.0'
53 | gem 'mini_racer', platforms: :ruby
54 | gem 'hyper-router'
55 | gem 'hyper-mesh', path: '../..'
56 | gem 'hyperloop-config' #, path: '../../../hyperloop-config'
57 | gem 'hyper-operation' #, path: '../../../hyper-operation'
58 | gem 'hyper-component'#, path: '../../../hyper-component'
59 | gem 'hyper-react' #, path: '../../../hyper-react'
60 | gem 'hyper-store'#, path: '../../../hyper-store'
61 | gem 'opal-browser'
62 | gem 'rspec-rails'
63 |
--------------------------------------------------------------------------------
/spec/test_app/Rakefile:
--------------------------------------------------------------------------------
1 | # Add your own tasks in files placed in lib/tasks ending in .rake,
2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3 |
4 | require File.expand_path('../config/application', __FILE__)
5 |
6 | Rails.application.load_tasks
7 |
--------------------------------------------------------------------------------
/spec/test_app/app/assets/javascripts/application.js:
--------------------------------------------------------------------------------
1 | //= require 'react'
2 | //= require 'react_ujs'
3 | //= require 'components'
4 | //= require action_cable
5 | //= require 'hyperloop/pusher'
6 | Opal.load('components');
7 |
--------------------------------------------------------------------------------
/spec/test_app/app/assets/javascripts/server_rendering.js:
--------------------------------------------------------------------------------
1 | //= require 'react-server'
2 | //= require 'react_ujs'
3 | //= require 'components'
4 | Opal.load('components')
--------------------------------------------------------------------------------
/spec/test_app/app/assets/stylesheets/application.css:
--------------------------------------------------------------------------------
1 | /*
2 | * This is a manifest file that'll be compiled into application.css, which will include all the files
3 | * listed below.
4 | *
5 | * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6 | * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
7 | *
8 | * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9 | * compiled file so the styles you add here take precedence over styles defined in any styles
10 | * defined in the other CSS/SCSS files in this directory. It is generally better to create a new
11 | * file per style scope.
12 | *
13 | *= require_tree .
14 | *= require_self
15 | */
16 |
--------------------------------------------------------------------------------
/spec/test_app/app/controllers/application_controller.rb:
--------------------------------------------------------------------------------
1 | class ApplicationController < ActionController::Base
2 | # Prevent CSRF attacks by raising an exception.
3 | # For APIs, you may want to use :null_session instead.
4 | protect_from_forgery with: :exception
5 |
6 | class << self
7 | attr_accessor :acting_user
8 | end
9 |
10 | def acting_user
11 | ApplicationController.acting_user
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/spec/test_app/app/controllers/test_controller.rb:
--------------------------------------------------------------------------------
1 | class TestController < ApplicationController
2 | def show
3 | render_component
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/spec/test_app/app/models/_react_public_models.rb:
--------------------------------------------------------------------------------
1 | # app/models/_react_public_models.rb
2 | require_tree './public' if RUBY_ENGINE == 'opal'
3 |
--------------------------------------------------------------------------------
/spec/test_app/app/models/public/address.rb:
--------------------------------------------------------------------------------
1 | class Address < ActiveRecord::Base
2 |
3 | MAPPED_FIELDS = %w(id street city state zip)
4 |
5 | def self.compose(*args)
6 | new.tap do |address|
7 | MAPPED_FIELDS.each_with_index do |field_name, i|
8 | address.send("#{field_name}=", args[i])
9 | end
10 | end
11 | end
12 |
13 | end
--------------------------------------------------------------------------------
/spec/test_app/app/models/public/application_record.rb:
--------------------------------------------------------------------------------
1 | class ApplicationRecord < ActiveRecord::Base
2 | self.abstract_class = true
3 | end
4 |
--------------------------------------------------------------------------------
/spec/test_app/app/models/public/child_model.rb:
--------------------------------------------------------------------------------
1 | class ChildModel < ActiveRecord::Base
2 | belongs_to :test_model
3 | end
4 |
--------------------------------------------------------------------------------
/spec/test_app/app/models/public/comment.rb:
--------------------------------------------------------------------------------
1 | class Comment < ActiveRecord::Base
2 | # see spec/examples folder for typically setup
3 | belongs_to :author, class_name: "User"
4 | belongs_to :todoz, class_name: "Todo", foreign_key: :todo_id
5 | end
6 |
7 | class Comment < ActiveRecord::Base
8 |
9 | def create_permitted?
10 | # for testing we allow anything if there is no acting_user
11 | # in the real world you would have something like this:
12 | # acting_user and (acting_user.admin? or user_is? acting_user)
13 | !acting_user or user_is? acting_user
14 | end
15 |
16 | def destroy_permitted?
17 | !acting_user or user_is? acting_user
18 | end
19 |
20 | belongs_to :user
21 | belongs_to :todo_item
22 |
23 | has_one :todo, -> {}, class_name: "TodoItem" # this is just so we can test scopes params and null belongs_to relations
24 |
25 | end
26 |
--------------------------------------------------------------------------------
/spec/test_app/app/models/public/default_test.rb:
--------------------------------------------------------------------------------
1 | class DefaultTest < ActiveRecord::Base
2 | def self.build_tables
3 | connection.create_table :default_tests, force: true do |t|
4 | t.string :string, default: "I'm a string!"
5 | t.date :date, default: Date.today
6 | t.datetime :datetime, default: Time.now
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/spec/test_app/app/models/public/test_model.rb:
--------------------------------------------------------------------------------
1 | class TestModel < ActiveRecord::Base
2 | has_many :child_models
3 | scope :completed, -> () { where(completed: true) }
4 | scope :active, -> () { where(completed: false) }
5 | end
6 |
--------------------------------------------------------------------------------
/spec/test_app/app/models/public/todo.rb:
--------------------------------------------------------------------------------
1 | class Todo < ActiveRecord::Base
2 | # see spec/examples folder for typically setup
3 | belongs_to :owner, class_name: "User"
4 | belongs_to :created_by, class_name: "User"
5 | has_many :comments
6 | end
7 |
--------------------------------------------------------------------------------
/spec/test_app/app/models/public/todo_item.rb:
--------------------------------------------------------------------------------
1 | class TodoItem < ApplicationRecord
2 |
3 | def view_permitted?(attribute)
4 | !acting_user or user_is? acting_user
5 | end
6 |
7 | def update_permitted?
8 | return true unless acting_user
9 | return only_changed? :comments unless user_is? acting_user
10 | true
11 | end
12 |
13 | belongs_to :user
14 | has_many :comments
15 | has_many :commenters, class_name: "User", through: :comments, source: :user
16 | belongs_to :comment # just so we can test an empty belongs_to relationship
17 |
18 | scope :find_string, ->(s) { where("title LIKE ? OR description LIKE ?", "%#{s}%", "%#{s}%") }
19 |
20 | scope :active, -> { where("title LIKE '%mitch%' OR description LIKE '%mitch%'")}
21 | # to_sync :active do |scope, record|
22 | # if record.title =~ /mitch/ || record.description =~ /mitch/
23 | # scope << record
24 | # else
25 | # scope.delete(record)
26 | # end
27 | # end
28 |
29 | scope :important, -> { where("title LIKE '%another%' OR description LIKE '%another%'")}
30 | # to_sync(:important) {title =~ /another/ || description =~ /another/ }
31 |
32 | def virtual_user_first_name
33 | user.first_name
34 | end unless RUBY_ENGINE == 'opal'
35 |
36 | end
37 |
--------------------------------------------------------------------------------
/spec/test_app/app/models/public/type_test.rb:
--------------------------------------------------------------------------------
1 | class TypeTest < ActiveRecord::Base
2 | def self.build_tables
3 | connection.create_table(:type_tests, force: true) do |t|
4 | t.binary(:binary)
5 | t.boolean(:boolean)
6 | t.date(:date)
7 | t.datetime(:datetime)
8 | t.decimal(:decimal, precision: 5, scale: 2)
9 | t.float(:float)
10 | t.integer(:integer)
11 | t.bigint(:bigint)
12 | t.string(:string)
13 | t.text(:text)
14 | t.time(:time)
15 | t.timestamp(:timestamp)
16 | end
17 | end
18 | end
--------------------------------------------------------------------------------
/spec/test_app/app/models/public/user.rb:
--------------------------------------------------------------------------------
1 | class User < ActiveRecord::Base
2 | # see spec/examples folder for typically setup
3 | has_many :assigned_todos, class_name: "Todo", foreign_key: :owner_id
4 | has_many :authored_todos, class_name: "Todo", foreign_key: :created_by_id
5 | has_many :commentz, class_name: "Comment", foreign_key: :author_id
6 | belongs_to :manager, class_name: "User"
7 | has_many :employees, class_name: "User", foreign_key: :manager_id
8 | end
9 |
10 |
11 | class TestData
12 |
13 | def initialize(string, times)
14 | @string = string
15 | @times = times
16 | end
17 |
18 | attr_accessor :string
19 | attr_accessor :times
20 |
21 | def big_string
22 | puts "calling big_string #{string} * #{times}"
23 | string * times
24 | end
25 |
26 | end
27 |
28 | class User < ActiveRecord::Base
29 |
30 | def view_permitted?(attribute)
31 | return self == acting_user if acting_user
32 | super # we call super to test if its there (just for the spec) not really the right way to do it, see comments or todo_items
33 | end
34 |
35 | has_many :todo_items
36 | has_many :comments
37 | has_many :commented_on_items, class_name: "TodoItem", through: :comments, source: :todo_item
38 |
39 | composed_of :address, :class_name => 'Address', :constructor => :compose, :mapping => Address::MAPPED_FIELDS.map {|f| ["address_#{f}", f] }
40 | composed_of :address2, :class_name => 'Address', :constructor => :compose, :mapping => Address::MAPPED_FIELDS.map {|f| ["address2_#{f}", f] }
41 |
42 | composed_of :data, :class_name => 'TestData', :allow_nil => true, :mapping => [['data_string', 'string'], ['data_times', 'times']]
43 |
44 | enum test_enum: [:yes, :no]
45 |
46 | def name
47 | "#{first_name} #{last_name}"
48 | end
49 |
50 | # two examples of server side calculated attributes. The second takes a parameter.
51 | # the first does not rely on an id, so can be used before the record is saved.
52 |
53 | def detailed_name
54 | s = "#{first_name[0]}. #{last_name}" rescue ""
55 | s += " - #{email}" if email
56 | s += " (#{todo_items.size} todo#{'s' if todo_items.size > 1})" if todo_items.size > 0
57 | s
58 | end unless RUBY_ENGINE == 'opal'
59 |
60 | def expensive_math(n)
61 | n*n
62 | end unless RUBY_ENGINE == 'opal'
63 |
64 | # this is also used for remote calculation in the aggregate test
65 |
66 | def verify_zip
67 | if address.zip =~ /^\d{5}$/
68 | address.zip
69 | end
70 | end unless RUBY_ENGINE == 'opal'
71 |
72 | end
73 |
74 | class User < ActiveRecord::Base
75 |
76 | def as_json(*args)
77 | {name: "bozo"}
78 | end
79 |
80 | validates :email, format: {with: /\@.+\./}, :allow_nil => true
81 |
82 | def name=(val) # this is here to test ability to save changes to this type of psuedo attribute
83 | val = val.to_s.split(" ")
84 | self.first_name = val[0]
85 | self.last_name = val[1]
86 | end
87 |
88 | end unless RUBY_ENGINE == 'opal'
89 |
--------------------------------------------------------------------------------
/spec/test_app/app/other_classes/unloaded_class.rb:
--------------------------------------------------------------------------------
1 | class UnloadedClass
2 | def am_i_loaded?
3 | true
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/spec/test_app/app/policies/auto_loader_test_classa_policy.rb:
--------------------------------------------------------------------------------
1 | class AutoLoaderTestClassaPolicy
2 | always_allow_connection
3 | end
4 |
--------------------------------------------------------------------------------
/spec/test_app/app/policies/auto_loader_test_classb_policy.rb:
--------------------------------------------------------------------------------
1 | class AutoLoaderTestClassbPolicy
2 | always_allow_connection
3 | end
4 |
--------------------------------------------------------------------------------
/spec/test_app/app/policies/auto_loader_test_classc_policy.rb:
--------------------------------------------------------------------------------
1 | class AutoLoaderTestClassxPolicy
2 | always_allow_connection
3 | end
4 |
--------------------------------------------------------------------------------
/spec/test_app/app/policies/auto_loader_test_classd_policy.rb:
--------------------------------------------------------------------------------
1 | class AutoLoaderTestClassyPolicy
2 | always_allow_connection
3 | end
4 |
--------------------------------------------------------------------------------
/spec/test_app/app/views/components.rb:
--------------------------------------------------------------------------------
1 | require 'opal'
2 | require 'hyper-component'
3 | if React::IsomorphicHelpers.on_opal_client?
4 | require 'browser'
5 | require 'browser/delay'
6 | require 'browser/interval'
7 | #require 'hyperloop/pusher'
8 | end
9 | require 'hyper-mesh'
10 | require '_react_public_models'
11 | require_tree './components'
12 |
13 |
14 | # require 'opal'
15 | # require 'promise'
16 | # require 'hyper-react'
17 | # if React::IsomorphicHelpers.on_opal_client?
18 | # require 'browser'
19 | # require 'browser/delay'
20 | # end
21 | # require_tree './components'
22 |
--------------------------------------------------------------------------------
/spec/test_app/app/views/components/show.rb:
--------------------------------------------------------------------------------
1 | class Show < React::Component::Base
2 | render do
3 | "hello"
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/spec/test_app/app/views/layouts/application.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | HyperMesh Test App
5 | <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %>
6 | <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
7 | <%= csrf_meta_tags %>
8 |
9 |
10 |
11 | <%= yield %>
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/spec/test_app/bin/bundle:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
3 | load Gem.bin_path('bundler', 'bundle')
4 |
--------------------------------------------------------------------------------
/spec/test_app/bin/rails:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | APP_PATH = File.expand_path('../../config/application', __FILE__)
3 | require_relative '../config/boot'
4 | require 'rails/commands'
5 |
--------------------------------------------------------------------------------
/spec/test_app/bin/rake:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require_relative '../config/boot'
3 | require 'rake'
4 | Rake.application.run
5 |
--------------------------------------------------------------------------------
/spec/test_app/bin/setup:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require 'pathname'
3 |
4 | # path to your application root.
5 | APP_ROOT = Pathname.new File.expand_path('../../', __FILE__)
6 |
7 | Dir.chdir APP_ROOT do
8 | # This script is a starting point to setup your application.
9 | # Add necessary setup steps to this file:
10 |
11 | puts "== Installing dependencies =="
12 | system "gem install bundler --conservative"
13 | system "bundle check || bundle install"
14 |
15 | # puts "\n== Copying sample files =="
16 | # unless File.exist?("config/database.yml")
17 | # system "cp config/database.yml.sample config/database.yml"
18 | # end
19 |
20 | puts "\n== Preparing database =="
21 | system "bin/rake db:setup"
22 |
23 | puts "\n== Removing old logs and tempfiles =="
24 | system "rm -f log/*"
25 | system "rm -rf tmp/cache"
26 |
27 | puts "\n== Restarting application server =="
28 | system "touch tmp/restart.txt"
29 | end
30 |
--------------------------------------------------------------------------------
/spec/test_app/config.ru:
--------------------------------------------------------------------------------
1 | # This file is used by Rack-based servers to start the application.
2 |
3 | require ::File.expand_path('../config/environment', __FILE__)
4 | run Rails.application
5 |
--------------------------------------------------------------------------------
/spec/test_app/config/application.rb:
--------------------------------------------------------------------------------
1 |
2 | require 'rails/all'
3 | require File.expand_path('../boot', __FILE__)
4 |
5 | # Require the gems listed in Gemfile, including any gems
6 | # you've limited to :test, :development, or :production.
7 | Bundler.require(*Rails.groups(assets: %w(development test)))
8 |
9 | require 'opal-rails'
10 | require 'hyper-react'
11 |
12 | module TestApp
13 | class Application < Rails::Application
14 | config.action_cable.allowed_request_origins = [/http\:\/\/127\.0\.0\.1\:[0-9]*/]
15 | config.eager_load_paths += %W(#{config.root}/app/models/public)
16 | config.autoload_paths += %W(#{config.root}/app/models/public)
17 | config.assets.paths << ::Rails.root.join('app', 'models').to_s
18 | config.hyperloop.auto_config = false
19 | config.opal.method_missing = true
20 | config.opal.optimized_operators = true
21 | config.opal.arity_check = false
22 | config.opal.const_missing = true
23 | config.opal.dynamic_require_severity = :ignore
24 | config.opal.enable_specs = true
25 | config.opal.spec_location = 'spec-opal'
26 |
27 | config.assets.cache_store = :null_store
28 | config.hyperloop.auto_config = false
29 |
30 | config.react.server_renderer_options = {
31 | files: ['server_rendering.js']
32 | }
33 | config.react.server_renderer_directories = ['/app/assets/javascripts']
34 |
35 | # Settings in config/environments/* take precedence over those specified here.
36 | # Application configuration should go into files in config/initializers
37 | # -- all .rb files in that directory are automatically loaded.
38 |
39 | # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
40 | # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
41 | # config.time_zone = 'Central Time (US & Canada)'
42 |
43 | # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
44 | # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
45 | # config.i18n.default_locale = :de
46 |
47 | # Do not swallow errors in after_commit/after_rollback callbacks.
48 | #config.active_record.raise_in_transactional_callbacks = true
49 | end
50 | end
51 |
--------------------------------------------------------------------------------
/spec/test_app/config/boot.rb:
--------------------------------------------------------------------------------
1 | require 'rubygems'
2 | gemfile = File.expand_path("../../../../Gemfile", __FILE__)
3 |
4 | ENV['BUNDLE_GEMFILE'] = gemfile
5 | require 'bundler'
6 | Bundler.setup
7 |
--------------------------------------------------------------------------------
/spec/test_app/config/cable.yml:
--------------------------------------------------------------------------------
1 |
2 | development:
3 | adapter: async
4 |
5 | test:
6 | adapter: async
7 |
8 | production:
9 | adapter: redis
10 | url: redis://10.10.3.153:6381
11 |
--------------------------------------------------------------------------------
/spec/test_app/config/database.yml:
--------------------------------------------------------------------------------
1 | # SQLite version 3.x
2 | # gem install sqlite3
3 | #
4 | # Ensure the SQLite 3 gem is defined in your Gemfile
5 | # gem 'sqlite3'
6 | #
7 | default: &default
8 | adapter: mysql2
9 | encoding: utf8
10 | username: root
11 |
12 | development:
13 | <<: *default
14 | database: hyper_mesh_development_db
15 |
16 | test:
17 | <<: *default
18 | database: hyper_mesh_test_db
19 |
20 | production:
21 | <<: *default
22 | database: hyper_mesh_production_db
23 |
24 | # default: &default
25 | # adapter: sqlite3
26 | # pool: 5
27 | # timeout: 10000
28 | #
29 | # development:
30 | # <<: *default
31 | # database: db/development.sqlite3
32 | #
33 | # # Warning: The database defined as "test" will be erased and
34 | # # re-generated from your development database when you run "rake".
35 | # # Do not set this db to the same as development or production.
36 | # test:
37 | # <<: *default
38 | # database: db/test.sqlite3
39 | #
40 | # production:
41 | # <<: *default
42 | # database: db/production.sqlite3
43 |
--------------------------------------------------------------------------------
/spec/test_app/config/environment.rb:
--------------------------------------------------------------------------------
1 | # Load the Rails application.
2 | require File.expand_path('../application', __FILE__)
3 |
4 | # Initialize the Rails application.
5 | Rails.application.initialize!
6 |
--------------------------------------------------------------------------------
/spec/test_app/config/environments/development.rb:
--------------------------------------------------------------------------------
1 | Rails.application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb.
3 |
4 | # In the development environment your application's code is reloaded on
5 | # every request. This slows down response time but is perfect for development
6 | # since you don't have to restart the web server when you make code changes.
7 | config.cache_classes = false
8 |
9 | # Do not eager load code on boot.
10 | config.eager_load = false
11 |
12 | # Show full error reports and disable caching.
13 | config.consider_all_requests_local = true
14 | config.action_controller.perform_caching = false
15 |
16 | # Don't care if the mailer can't send.
17 | config.action_mailer.raise_delivery_errors = false
18 |
19 | # Print deprecation notices to the Rails logger.
20 | config.active_support.deprecation = :log
21 |
22 | # Raise an error on page load if there are pending migrations.
23 | config.active_record.migration_error = :page_load
24 |
25 | # Debug mode disables concatenation and preprocessing of assets.
26 | # This option may cause significant delays in view rendering with a large
27 | # number of complex assets.
28 | config.assets.debug = false
29 |
30 | # Asset digests allow you to set far-future HTTP expiration dates on all assets,
31 | # yet still be able to expire them through the digest params.
32 | config.assets.digest = true
33 |
34 | # Adds additional error checking when serving assets at runtime.
35 | # Checks for improperly declared sprockets dependencies.
36 | # Raises helpful error messages.
37 | config.assets.raise_runtime_errors = true
38 |
39 | # Raises error for missing translations
40 | # config.action_view.raise_on_missing_translations = true
41 | end
42 |
--------------------------------------------------------------------------------
/spec/test_app/config/environments/production.rb:
--------------------------------------------------------------------------------
1 | Rails.application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb.
3 |
4 | # Code is not reloaded between requests.
5 | config.cache_classes = true
6 |
7 | # Eager load code on boot. This eager loads most of Rails and
8 | # your application in memory, allowing both threaded web servers
9 | # and those relying on copy on write to perform better.
10 | # Rake tasks automatically ignore this option for performance.
11 | config.eager_load = true
12 |
13 | # Full error reports are disabled and caching is turned on.
14 | config.consider_all_requests_local = false
15 | config.action_controller.perform_caching = true
16 |
17 | # Enable Rack::Cache to put a simple HTTP cache in front of your application
18 | # Add `rack-cache` to your Gemfile before enabling this.
19 | # For large-scale production use, consider using a caching reverse proxy like
20 | # NGINX, varnish or squid.
21 | # config.action_dispatch.rack_cache = true
22 |
23 | # Disable serving static files from the `/public` folder by default since
24 | # Apache or NGINX already handles this.
25 | config.serve_static_files = ENV['RAILS_SERVE_STATIC_FILES'].present?
26 |
27 | # Compress JavaScripts and CSS.
28 | config.assets.js_compressor = :uglifier
29 | # config.assets.css_compressor = :sass
30 |
31 | # Do not fallback to assets pipeline if a precompiled asset is missed.
32 | config.assets.compile = false
33 |
34 | # Asset digests allow you to set far-future HTTP expiration dates on all assets,
35 | # yet still be able to expire them through the digest params.
36 | config.assets.digest = true
37 |
38 | # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb
39 |
40 | # Specifies the header that your server uses for sending files.
41 | # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache
42 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX
43 |
44 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
45 | # config.force_ssl = true
46 |
47 | # Use the lowest log level to ensure availability of diagnostic information
48 | # when problems arise.
49 | config.log_level = :debug
50 |
51 | # Prepend all log lines with the following tags.
52 | # config.log_tags = [ :subdomain, :uuid ]
53 |
54 | # Use a different logger for distributed setups.
55 | # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new)
56 |
57 | # Use a different cache store in production.
58 | # config.cache_store = :mem_cache_store
59 |
60 | # Enable serving of images, stylesheets, and JavaScripts from an asset server.
61 | # config.action_controller.asset_host = 'http://assets.example.com'
62 |
63 | # Ignore bad email addresses and do not raise email delivery errors.
64 | # Set this to true and configure the email server for immediate delivery to raise delivery errors.
65 | # config.action_mailer.raise_delivery_errors = false
66 |
67 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
68 | # the I18n.default_locale when a translation cannot be found).
69 | config.i18n.fallbacks = true
70 |
71 | # Send deprecation notices to registered listeners.
72 | config.active_support.deprecation = :notify
73 |
74 | # Use default logging formatter so that PID and timestamp are not suppressed.
75 | config.log_formatter = ::Logger::Formatter.new
76 |
77 | # Do not dump schema after migrations.
78 | config.active_record.dump_schema_after_migration = false
79 | end
80 |
--------------------------------------------------------------------------------
/spec/test_app/config/environments/test.rb:
--------------------------------------------------------------------------------
1 | Rails.application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb.
3 |
4 | # The test environment is used exclusively to run your application's
5 | # test suite. You never need to work with it otherwise. Remember that
6 | # your test database is "scratch space" for the test suite and is wiped
7 | # and recreated between test runs. Don't rely on the data there!
8 | config.cache_classes = false
9 |
10 | # Do not eager load code on boot. This avoids loading your whole application
11 | # just for the purpose of running a single test. If you are using a tool that
12 | # preloads Rails for running tests, you may have to set it to true.
13 | config.eager_load = false
14 |
15 | # Configure static file server for tests with Cache-Control for performance.
16 | config.public_file_server.enabled = true
17 | config.public_file_server.headers = { 'Cache-Control' => 'public, max-age=3600' }
18 |
19 | # Show full error reports and disable caching.
20 | config.consider_all_requests_local = true
21 | config.action_controller.perform_caching = false
22 |
23 | # Raise exceptions instead of rendering exception templates.
24 | config.action_dispatch.show_exceptions = false
25 |
26 | # Disable request forgery protection in test environment.
27 | config.action_controller.allow_forgery_protection = false
28 |
29 | # Tell Action Mailer not to deliver emails to the real world.
30 | # The :test delivery method accumulates sent emails in the
31 | # ActionMailer::Base.deliveries array.
32 | config.action_mailer.delivery_method = :test
33 |
34 | # Randomize the order test cases are executed.
35 | config.active_support.test_order = :random
36 |
37 | # Print deprecation notices to the stderr.
38 | config.active_support.deprecation = :stderr
39 |
40 | # Raises error for missing translations
41 | # config.action_view.raise_on_missing_translations = true
42 | end
43 |
--------------------------------------------------------------------------------
/spec/test_app/config/initializers/assets.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Version of your assets, change this if you want to expire all your assets.
4 | Rails.application.config.assets.version = '1.0'
5 |
6 | # Add additional assets to the asset load path
7 | # Rails.application.config.assets.paths << Emoji.images_path
8 |
9 | # Precompile additional assets.
10 | # application.js, application.css, and all non-JS/CSS in app/assets folder are already added.
11 | Rails.application.config.assets.precompile += %w[time_cop.js]
12 |
--------------------------------------------------------------------------------
/spec/test_app/config/initializers/backtrace_silencers.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }
5 |
6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.
7 | # Rails.backtrace_cleaner.remove_silencers!
8 |
--------------------------------------------------------------------------------
/spec/test_app/config/initializers/cookies_serializer.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | Rails.application.config.action_dispatch.cookies_serializer = :json
4 |
--------------------------------------------------------------------------------
/spec/test_app/config/initializers/filter_parameter_logging.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Configure sensitive parameters which will be filtered from the log file.
4 | Rails.application.config.filter_parameters += [:password]
5 |
--------------------------------------------------------------------------------
/spec/test_app/config/initializers/inflections.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Add new inflection rules using the following format. Inflections
4 | # are locale specific, and you may define rules for as many different
5 | # locales as you wish. All of these examples are active by default:
6 | # ActiveSupport::Inflector.inflections(:en) do |inflect|
7 | # inflect.plural /^(ox)$/i, '\1en'
8 | # inflect.singular /^(ox)en/i, '\1'
9 | # inflect.irregular 'person', 'people'
10 | # inflect.uncountable %w( fish sheep )
11 | # end
12 |
13 | # These inflection rules are supported but not enabled by default:
14 | # ActiveSupport::Inflector.inflections(:en) do |inflect|
15 | # inflect.acronym 'RESTful'
16 | # end
17 |
--------------------------------------------------------------------------------
/spec/test_app/config/initializers/mime_types.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Add new mime types for use in respond_to blocks:
4 | # Mime::Type.register "text/richtext", :rtf
5 |
--------------------------------------------------------------------------------
/spec/test_app/config/initializers/session_store.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | Rails.application.config.session_store :cookie_store, key: '_test_app_session'
4 |
--------------------------------------------------------------------------------
/spec/test_app/config/initializers/synchromesh.rb:
--------------------------------------------------------------------------------
1 | # require 'pusher'
2 | # Pusher.app_id = "MY_TEST_ID"
3 | # Pusher.key = "MY_TEST_KEY"
4 | # Pusher.secret = "MY_TEST_SECRET"
5 | # require 'pusher-fake'
6 | #
7 | # HyperMesh.configuration do |config|
8 | # config.transport = :pusher
9 | # config.channel_prefix = "synchromesh"
10 | # config.opts = {app_id: Pusher.app_id, key: Pusher.key, secret: Pusher.secret}.merge(PusherFake.configuration.web_options)
11 | # end
12 | class MiniRacer::Context
13 | alias original_eval eval
14 | def eval(str, options=nil)
15 | original_eval str, options
16 | rescue Exception => e
17 | File.write('react_prerendering_src.js', str) rescue nil
18 | raise e
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/spec/test_app/config/initializers/wrap_parameters.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # This file contains settings for ActionController::ParamsWrapper which
4 | # is enabled by default.
5 |
6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
7 | ActiveSupport.on_load(:action_controller) do
8 | wrap_parameters format: [:json] if respond_to?(:wrap_parameters)
9 | end
10 |
11 | # To enable root element in JSON for ActiveRecord objects.
12 | # ActiveSupport.on_load(:active_record) do
13 | # self.include_root_in_json = true
14 | # end
15 |
--------------------------------------------------------------------------------
/spec/test_app/config/locales/en.yml:
--------------------------------------------------------------------------------
1 | # Files in the config/locales directory are used for internationalization
2 | # and are automatically loaded by Rails. If you want to use locales other
3 | # than English, add the necessary files in this directory.
4 | #
5 | # To use the locales, use `I18n.t`:
6 | #
7 | # I18n.t 'hello'
8 | #
9 | # In views, this is aliased to just `t`:
10 | #
11 | # <%= t('hello') %>
12 | #
13 | # To use a different locale, set it with `I18n.locale`:
14 | #
15 | # I18n.locale = :es
16 | #
17 | # This would use the information in config/locales/es.yml.
18 | #
19 | # To learn more, please read the Rails Internationalization guide
20 | # available at http://guides.rubyonrails.org/i18n.html.
21 |
22 | en:
23 | hello: "Hello world"
24 |
--------------------------------------------------------------------------------
/spec/test_app/config/routes.rb:
--------------------------------------------------------------------------------
1 | require 'hyper-mesh'
2 |
3 | Rails.application.routes.draw do
4 | mount Hyperloop::Engine => "/rr"
5 | # The priority is based upon order of creation: first created -> highest priority.
6 | # See how all your routes lay out with "rake routes".
7 |
8 | # You can have the root of your site routed with "root"
9 | # root 'welcome#index'
10 |
11 | get 'test' => 'test#show'
12 |
13 | # Example of regular route:
14 | # get 'products/:id' => 'catalog#view'
15 |
16 | # Example of named route that can be invoked with purchase_url(id: product.id)
17 | # get 'products/:id/purchase' => 'catalog#purchase', as: :purchase
18 |
19 | # Example resource route (maps HTTP verbs to controller actions automatically):
20 | # resources :products
21 |
22 | # Example resource route with options:
23 | # resources :products do
24 | # member do
25 | # get 'short'
26 | # post 'toggle'
27 | # end
28 | #
29 | # collection do
30 | # get 'sold'
31 | # end
32 | # end
33 |
34 | # Example resource route with sub-resources:
35 | # resources :products do
36 | # resources :comments, :sales
37 | # resource :seller
38 | # end
39 |
40 | # Example resource route with more complex sub-resources:
41 | # resources :products do
42 | # resources :comments
43 | # resources :sales do
44 | # get 'recent', on: :collection
45 | # end
46 | # end
47 |
48 | # Example resource route with concerns:
49 | # concern :toggleable do
50 | # post 'toggle'
51 | # end
52 | # resources :posts, concerns: :toggleable
53 | # resources :photos, concerns: :toggleable
54 |
55 | # Example resource route within a namespace:
56 | # namespace :admin do
57 | # # Directs /admin/products/* to Admin::ProductsController
58 | # # (app/controllers/admin/products_controller.rb)
59 | # resources :products
60 | # end
61 | end
62 |
--------------------------------------------------------------------------------
/spec/test_app/config/secrets.yml:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Your secret key is used for verifying the integrity of signed cookies.
4 | # If you change this key, all old signed cookies will become invalid!
5 |
6 | # Make sure the secret is at least 30 characters and all random,
7 | # no regular words or you'll be exposed to dictionary attacks.
8 | # You can use `rake secret` to generate a secure secret key.
9 |
10 | # Make sure the secrets in this file are kept private
11 | # if you're sharing your code publicly.
12 |
13 | development:
14 | secret_key_base: ad4964ee73093a0573860466d1e714c8e7b1103d56bbae9e7c7b9843163e51aa9b85c2fef3979a29dbeb0d77a8ade834d77b04890042a45b8121068c2a0e8cba
15 |
16 | test:
17 | secret_key_base: b1cc8e3d36a79eed70d5aa66b05ff6aa8872a43adbeaa8c9f6d21a5bd1b65f231c56a4efadc73c975ccfc5bbd7f238c0db0fe4b5a116353b646cb6de369f8063
18 |
19 | # Do not keep production secrets in the repository,
20 | # instead read values from the environment.
21 | production:
22 | secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
23 |
--------------------------------------------------------------------------------
/spec/test_app/db/development.sqlite3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ruby-hyperloop/hyper-mesh/d29f00b16efbdd25419f07c7590505de4d3ba42a/spec/test_app/db/development.sqlite3
--------------------------------------------------------------------------------
/spec/test_app/db/migrate/20160731182106_create_test_models.rb:
--------------------------------------------------------------------------------
1 | class CreateTestModels < ActiveRecord::Migration
2 | def change
3 | create_table :test_models do |t|
4 | t.string :test_attribute
5 | t.boolean :completed
6 | t.timestamps null: false
7 | end
8 |
9 | create_table :child_models do |t|
10 | t.string :child_attribute
11 | t.belongs_to :test_model
12 | end
13 |
14 | create_table :users do |t|
15 | t.string :role
16 | t.references :manager
17 | t.string "first_name"
18 | t.string "last_name"
19 | t.string "email"
20 | t.datetime "created_at"
21 | t.datetime "updated_at"
22 | t.string "address_street"
23 | t.string "address_city"
24 | t.string "address_state"
25 | t.string "address_zip"
26 | t.integer "address_id"
27 | t.string "address2_street"
28 | t.string "address2_city"
29 | t.string "address2_state"
30 | t.string "address2_zip"
31 | t.string "data_string"
32 | t.integer "data_times"
33 | t.integer "test_enum"
34 | end
35 |
36 | create_table :todos do |t|
37 | t.string :title
38 | t.text :description
39 | t.timestamps null: false
40 | t.boolean :completed, default: false, null: false
41 | t.references :created_by
42 | t.references :owner
43 | end
44 |
45 | create_table :comments do |t|
46 | t.text :comment
47 | t.timestamps null: false
48 | t.belongs_to :todo
49 | t.references :author
50 | t.integer "user_id"
51 | t.integer "todo_item_id"
52 | t.datetime "created_at"
53 | t.datetime "updated_at"
54 | end
55 |
56 | create_table "addresses" do |t|
57 | t.string "street"
58 | t.string "city"
59 | t.string "state"
60 | t.string "zip"
61 | t.datetime "created_at"
62 | t.datetime "updated_at"
63 | end
64 |
65 | create_table "todo_items" do |t|
66 | t.string "title"
67 | t.text "description"
68 | t.boolean "complete"
69 | t.datetime "created_at"
70 | t.datetime "updated_at"
71 | t.integer "user_id"
72 | t.integer "comment_id"
73 | end
74 | end
75 | end
76 |
--------------------------------------------------------------------------------
/spec/test_app/db/schema.rb:
--------------------------------------------------------------------------------
1 | # This file is auto-generated from the current state of the database. Instead
2 | # of editing this file, please use the migrations feature of Active Record to
3 | # incrementally modify your database, and then regenerate this schema definition.
4 | #
5 | # Note that this schema.rb definition is the authoritative source for your
6 | # database schema. If you need to create the application database on another
7 | # system, you should be using db:schema:load, not running all the migrations
8 | # from scratch. The latter is a flawed and unsustainable approach (the more migrations
9 | # you'll amass, the slower it'll run and the greater likelihood for issues).
10 | #
11 | # It's strongly recommended that you check this file into your version control system.
12 |
13 | ActiveRecord::Schema.define(version: 20160731182106) do
14 |
15 | create_table "addresses", force: :cascade do |t|
16 | t.string "street"
17 | t.string "city"
18 | t.string "state"
19 | t.string "zip"
20 | t.datetime "created_at"
21 | t.datetime "updated_at"
22 | end
23 |
24 | create_table "child_models", force: :cascade do |t|
25 | t.string "child_attribute"
26 | t.integer "test_model_id"
27 | end
28 |
29 | create_table "comments", force: :cascade do |t|
30 | t.text "comment"
31 | t.datetime "created_at"
32 | t.datetime "updated_at"
33 | t.integer "todo_id"
34 | t.integer "author_id"
35 | t.integer "user_id"
36 | t.integer "todo_item_id"
37 | end
38 |
39 | create_table "hyperloop_connections", force: :cascade do |t|
40 | t.string "channel"
41 | t.string "session"
42 | t.datetime "created_at"
43 | t.datetime "expires_at"
44 | t.datetime "refresh_at"
45 | end
46 |
47 | create_table "hyperloop_queued_messages", force: :cascade do |t|
48 | t.text "data"
49 | t.integer "connection_id"
50 | end
51 |
52 | create_table "test_models", force: :cascade do |t|
53 | t.string "test_attribute"
54 | t.boolean "completed"
55 | t.datetime "created_at", null: false
56 | t.datetime "updated_at", null: false
57 | end
58 |
59 | create_table "todo_items", force: :cascade do |t|
60 | t.string "title"
61 | t.text "description"
62 | t.boolean "complete"
63 | t.datetime "created_at"
64 | t.datetime "updated_at"
65 | t.integer "user_id"
66 | t.integer "comment_id"
67 | end
68 |
69 | create_table "todos", force: :cascade do |t|
70 | t.string "title"
71 | t.text "description"
72 | t.datetime "created_at", null: false
73 | t.datetime "updated_at", null: false
74 | t.boolean "completed", default: false, null: false
75 | t.integer "created_by_id"
76 | t.integer "owner_id"
77 | end
78 |
79 | create_table "users", force: :cascade do |t|
80 | t.string "role"
81 | t.integer "manager_id"
82 | t.string "first_name"
83 | t.string "last_name"
84 | t.string "email"
85 | t.datetime "created_at"
86 | t.datetime "updated_at"
87 | t.string "address_street"
88 | t.string "address_city"
89 | t.string "address_state"
90 | t.string "address_zip"
91 | t.integer "address_id"
92 | t.string "address2_street"
93 | t.string "address2_city"
94 | t.string "address2_state"
95 | t.string "address2_zip"
96 | t.string "data_string"
97 | t.integer "data_times"
98 | t.integer "test_enum"
99 | end
100 |
101 | end
102 |
--------------------------------------------------------------------------------
/spec/test_app/db/seeds.rb:
--------------------------------------------------------------------------------
1 | # This file should contain all the record creation needed to seed the database with its default values.
2 | # The data can then be loaded with the rake db:seed (or created alongside the db with db:setup).
3 | #
4 | # Examples:
5 | #
6 | # cities = City.create([{ name: 'Chicago' }, { name: 'Copenhagen' }])
7 | # Mayor.create(name: 'Emanuel', city: cities.first)
8 |
--------------------------------------------------------------------------------
/spec/test_app/lib/assets/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ruby-hyperloop/hyper-mesh/d29f00b16efbdd25419f07c7590505de4d3ba42a/spec/test_app/lib/assets/.keep
--------------------------------------------------------------------------------
/spec/test_app/public/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | The page you were looking for doesn't exist (404)
5 |
6 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
The page you were looking for doesn't exist.
62 |
You may have mistyped the address or the page may have moved.
63 |
64 |
If you are the application owner check the logs for more information.
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/spec/test_app/public/422.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | The change you wanted was rejected (422)
5 |
6 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
The change you wanted was rejected.
62 |
Maybe you tried to change something you didn't have access to.
63 |
64 |
If you are the application owner check the logs for more information.
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/spec/test_app/public/500.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | We're sorry, but something went wrong (500)
5 |
6 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
We're sorry, but something went wrong.
62 |
63 |
If you are the application owner check the logs for more information.
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/spec/test_app/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ruby-hyperloop/hyper-mesh/d29f00b16efbdd25419f07c7590505de4d3ba42a/spec/test_app/public/favicon.ico
--------------------------------------------------------------------------------
/spec/test_components.rb:
--------------------------------------------------------------------------------
1 | RSpec.configure do |config|
2 | config.before(:all) do
3 | on_client do
4 | class TestComponent < React::Component::Base
5 | param scope: :all
6 | render(:div) do
7 | div { "#{TestModel.send(params.scope).count} items" }
8 | ul { TestModel.send(params.scope).each { |model| li { model.test_attribute }}}
9 | end
10 | end
11 | class TestComponent2 < React::Component::Base
12 | render do
13 | "hello"
14 | end
15 | end
16 | end
17 | end
18 | end
19 |
--------------------------------------------------------------------------------
/terminal.md:
--------------------------------------------------------------------------------
1 | console helpfulness
2 |
3 | need to make things work with rails console, and async mode.
4 |
5 | Okay...
6 |
7 | detect where we are... am I running the server, or the console... if console then I do a post to server
8 |
9 | after-commit/change-or-destroy/model/id
10 |
11 |
12 |
13 | execute `rubycon` to bring up console in a new window
14 |
15 | uses
16 |
17 |
18 |
19 | the rubycon function will bring up a new browser window running the terminal emulator.
20 |
21 | the rubycon function will intercept all console.log/warn/error function calls and send them to the server, the server will push these out
22 |
23 | As each line is typed it is passed to the opal compiler
24 |
25 | once the compiler can parse the input it will be compiled to JS, and sent the server
26 |
27 | the server will then push using a dedicated debug channel out to the browser window.
28 |
29 | any result will be be sent back to the console.
30 |
31 | get new-console/clientid
32 |
33 | post client-to-console/clientid log-message
34 | post console-to-client/clientid js-code-string
35 | post console-to-server/clientid js-code-string (only accepts in dev mode)
36 |
37 | channel ClientToConsole-clientid log-message
38 | channel ConsoleToClient-clientid js-code-string
39 |
40 | 1) `rubycon()` will generate a GUID
41 | 2) then open a popup window with url syncromesh-root/new-console/GUID
42 | 3) then open channel connection ConsoleToClient-GUID
43 | 4) then patch window.log/warn/error
44 | 5) the `new-console` page will bring up a terminal window
45 | 6) then open channel connection ClientToConsole-GUID
46 |
47 | when either channel terminates, that will get resent to the other channel
48 | 7) the client window will disconnect the log/warn/error window, and disconnect the channel
49 | 8) the console window will close
50 |
51 | 1-4 + 7 are in a class RubyCon
52 | 5-6 + 8 use hyper-react-express in SAP
53 |
54 |
55 |
56 |
57 | nothing works whilst in debugger
58 |
59 | sooo... one place to hook in is render: if in debug mode when hitting render we render out a message, then wait for an event, then message the debugger with the component... debugger can poke around in the component, see what the state is, change state, whatever.. in otherwords we want to execute some opal code as if self were that component... fine! debugger can set state and finally leave.
60 |
61 | so render method is wrapped like this:
62 |
63 | if React::State(Debugger, :debugging_in_session)
64 | "DEBUGGING IN SESSSION"
65 | else
66 | ...normal code...
67 |
--------------------------------------------------------------------------------
/work-in-progress-drinking.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ruby-hyperloop/hyper-mesh/d29f00b16efbdd25419f07c7590505de4d3ba42a/work-in-progress-drinking.png
--------------------------------------------------------------------------------