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/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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_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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/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/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/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/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/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/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/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/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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/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/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/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/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/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/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/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 |
--------------------------------------------------------------------------------
/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/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/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 |
--------------------------------------------------------------------------------
/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/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/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/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/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/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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |