hyper-model 0.6.0 → 0.99.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (140) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +35 -41
  3. data/.rspec +2 -0
  4. data/.travis.yml +33 -0
  5. data/CHANGELOG.md +34 -0
  6. data/DOCS.md +735 -0
  7. data/Gemfile +7 -0
  8. data/Gemfile.lock +298 -224
  9. data/{LICENSE → LICENSE.txt} +6 -6
  10. data/README.md +51 -2
  11. data/Rakefile +18 -0
  12. data/bin/console +14 -0
  13. data/bin/setup +7 -0
  14. data/codeship.database.yml +18 -0
  15. data/hyper-model.gemspec +62 -36
  16. data/lib/active_model_client_stubs.rb +16 -0
  17. data/lib/active_record_base.rb +331 -0
  18. data/{examples/chat-app/app/assets/images/.keep → lib/acts_as_string.rb} +0 -0
  19. data/lib/enumerable/pluck.rb +6 -0
  20. data/lib/hyper-model.rb +59 -8
  21. data/lib/hyper_model/version.rb +3 -0
  22. data/lib/hyper_react/input_tags.rb +47 -0
  23. data/lib/hyperloop/model/load.rb +1 -1
  24. data/lib/kernel/itself.rb +5 -0
  25. data/lib/object/tap.rb +7 -0
  26. data/lib/opal/equality_patches.rb +15 -0
  27. data/lib/opal/parse_patch.rb +14 -0
  28. data/lib/opal/set_patches.rb +8 -0
  29. data/lib/reactive_record/active_record/aggregations.rb +69 -0
  30. data/lib/reactive_record/active_record/associations.rb +118 -0
  31. data/lib/reactive_record/active_record/base.rb +10 -0
  32. data/lib/reactive_record/active_record/class_methods.rb +406 -0
  33. data/lib/reactive_record/active_record/error.rb +31 -0
  34. data/lib/reactive_record/active_record/errors.rb +374 -0
  35. data/lib/reactive_record/active_record/instance_methods.rb +187 -0
  36. data/lib/reactive_record/active_record/public_columns_hash.rb +44 -0
  37. data/lib/reactive_record/active_record/reactive_record/backing_record_inspector.rb +36 -0
  38. data/lib/reactive_record/active_record/reactive_record/base.rb +416 -0
  39. data/lib/reactive_record/active_record/reactive_record/collection.rb +558 -0
  40. data/lib/reactive_record/active_record/reactive_record/column_types.rb +75 -0
  41. data/lib/reactive_record/active_record/reactive_record/dummy_value.rb +236 -0
  42. data/lib/reactive_record/active_record/reactive_record/getters.rb +133 -0
  43. data/lib/reactive_record/active_record/reactive_record/isomorphic_base.rb +576 -0
  44. data/lib/reactive_record/active_record/reactive_record/lookup_tables.rb +54 -0
  45. data/lib/reactive_record/active_record/reactive_record/operations.rb +107 -0
  46. data/lib/reactive_record/active_record/reactive_record/scoped_collection.rb +62 -0
  47. data/lib/reactive_record/active_record/reactive_record/setters.rb +194 -0
  48. data/lib/reactive_record/active_record/reactive_record/unscoped_collection.rb +16 -0
  49. data/lib/reactive_record/active_record/reactive_record/while_loading.rb +343 -0
  50. data/lib/reactive_record/active_record_error.rb +4 -0
  51. data/lib/reactive_record/broadcast.rb +223 -0
  52. data/lib/reactive_record/engine.rb +11 -0
  53. data/lib/reactive_record/interval.rb +190 -0
  54. data/lib/reactive_record/permissions.rb +117 -0
  55. data/lib/reactive_record/pry.rb +13 -0
  56. data/lib/reactive_record/reactive_scope.rb +18 -0
  57. data/lib/reactive_record/scope_description.rb +121 -0
  58. data/lib/reactive_record/serializers.rb +7 -0
  59. data/lib/reactive_record/server_data_cache.rb +478 -0
  60. data/path_release_steps.md +9 -0
  61. metadata +399 -109
  62. data/CODE_OF_CONDUCT.md +0 -49
  63. data/examples/chat-app/.gitignore +0 -21
  64. data/examples/chat-app/Gemfile +0 -62
  65. data/examples/chat-app/Gemfile.lock +0 -309
  66. data/examples/chat-app/README.md +0 -3
  67. data/examples/chat-app/Rakefile +0 -6
  68. data/examples/chat-app/app/assets/config/manifest.js +0 -3
  69. data/examples/chat-app/app/assets/javascripts/application.js +0 -3
  70. data/examples/chat-app/app/assets/stylesheets/application.scss +0 -33
  71. data/examples/chat-app/app/controllers/application_controller.rb +0 -3
  72. data/examples/chat-app/app/controllers/home_controller.rb +0 -5
  73. data/examples/chat-app/app/hyperloop/components/app.rb +0 -12
  74. data/examples/chat-app/app/hyperloop/components/formatted_div.rb +0 -15
  75. data/examples/chat-app/app/hyperloop/components/input_box.rb +0 -26
  76. data/examples/chat-app/app/hyperloop/components/message.rb +0 -29
  77. data/examples/chat-app/app/hyperloop/components/messages.rb +0 -8
  78. data/examples/chat-app/app/hyperloop/components/nav.rb +0 -30
  79. data/examples/chat-app/app/hyperloop/models/application_record.rb +0 -3
  80. data/examples/chat-app/app/hyperloop/models/message.rb +0 -6
  81. data/examples/chat-app/app/hyperloop/operations/operations.rb +0 -13
  82. data/examples/chat-app/app/hyperloop/stores/message_store.rb +0 -17
  83. data/examples/chat-app/app/policies/application_policy.rb +0 -9
  84. data/examples/chat-app/app/views/layouts/application.html.erb +0 -12
  85. data/examples/chat-app/bin/bundle +0 -3
  86. data/examples/chat-app/bin/rails +0 -9
  87. data/examples/chat-app/bin/rake +0 -9
  88. data/examples/chat-app/bin/setup +0 -34
  89. data/examples/chat-app/bin/spring +0 -17
  90. data/examples/chat-app/bin/update +0 -29
  91. data/examples/chat-app/config.ru +0 -5
  92. data/examples/chat-app/config/application.rb +0 -12
  93. data/examples/chat-app/config/boot.rb +0 -3
  94. data/examples/chat-app/config/cable.yml +0 -9
  95. data/examples/chat-app/config/database.yml +0 -25
  96. data/examples/chat-app/config/environment.rb +0 -5
  97. data/examples/chat-app/config/environments/development.rb +0 -56
  98. data/examples/chat-app/config/environments/production.rb +0 -86
  99. data/examples/chat-app/config/environments/test.rb +0 -42
  100. data/examples/chat-app/config/initializers/application_controller_renderer.rb +0 -6
  101. data/examples/chat-app/config/initializers/assets.rb +0 -11
  102. data/examples/chat-app/config/initializers/backtrace_silencers.rb +0 -7
  103. data/examples/chat-app/config/initializers/cookies_serializer.rb +0 -5
  104. data/examples/chat-app/config/initializers/filter_parameter_logging.rb +0 -4
  105. data/examples/chat-app/config/initializers/hyperloop.rb +0 -6
  106. data/examples/chat-app/config/initializers/inflections.rb +0 -16
  107. data/examples/chat-app/config/initializers/mime_types.rb +0 -4
  108. data/examples/chat-app/config/initializers/new_framework_defaults.rb +0 -24
  109. data/examples/chat-app/config/initializers/session_store.rb +0 -3
  110. data/examples/chat-app/config/initializers/wrap_parameters.rb +0 -14
  111. data/examples/chat-app/config/locales/en.yml +0 -23
  112. data/examples/chat-app/config/puma.rb +0 -47
  113. data/examples/chat-app/config/routes.rb +0 -5
  114. data/examples/chat-app/config/secrets.yml +0 -22
  115. data/examples/chat-app/config/spring.rb +0 -6
  116. data/examples/chat-app/db/migrate/20170319194429_create_message.rb +0 -9
  117. data/examples/chat-app/db/schema.rb +0 -48
  118. data/examples/chat-app/db/seeds.rb +0 -7
  119. data/examples/chat-app/lib/assets/.keep +0 -0
  120. data/examples/chat-app/lib/tasks/.keep +0 -0
  121. data/examples/chat-app/log/.keep +0 -0
  122. data/examples/chat-app/public/404.html +0 -67
  123. data/examples/chat-app/public/422.html +0 -67
  124. data/examples/chat-app/public/500.html +0 -66
  125. data/examples/chat-app/public/apple-touch-icon-precomposed.png +0 -0
  126. data/examples/chat-app/public/apple-touch-icon.png +0 -0
  127. data/examples/chat-app/public/favicon.ico +0 -0
  128. data/examples/chat-app/public/robots.txt +0 -5
  129. data/examples/chat-app/test/controllers/.keep +0 -0
  130. data/examples/chat-app/test/fixtures/.keep +0 -0
  131. data/examples/chat-app/test/fixtures/files/.keep +0 -0
  132. data/examples/chat-app/test/helpers/.keep +0 -0
  133. data/examples/chat-app/test/integration/.keep +0 -0
  134. data/examples/chat-app/test/mailers/.keep +0 -0
  135. data/examples/chat-app/test/models/.keep +0 -0
  136. data/examples/chat-app/test/test_helper.rb +0 -10
  137. data/examples/chat-app/tmp/.keep +0 -0
  138. data/examples/chat-app/vendor/assets/javascripts/.keep +0 -0
  139. data/examples/chat-app/vendor/assets/stylesheets/.keep +0 -0
  140. data/lib/hyperloop/model/version.rb +0 -5
@@ -0,0 +1,6 @@
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
@@ -1,14 +1,65 @@
1
- require "hyperloop/model/version"
1
+ require 'set'
2
2
  require 'hyperloop-config'
3
-
4
- Hyperloop.import 'hyper-component'
5
- Hyperloop.import 'hyper-model'
6
-
3
+ require 'hyper-component'
7
4
  if RUBY_ENGINE == 'opal'
8
- require 'hyper-mesh'
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 'active_model_client_stubs'
12
+ require "reactive_record/active_record_error"
13
+ require "reactive_record/active_record/errors"
14
+ require "reactive_record/active_record/error"
15
+ require "reactive_record/server_data_cache"
16
+ require "reactive_record/active_record/reactive_record/while_loading"
17
+ require "reactive_record/active_record/reactive_record/operations"
18
+ require 'reactive_record/broadcast'
19
+ require "reactive_record/active_record/reactive_record/isomorphic_base"
20
+ require 'reactive_record/active_record/reactive_record/dummy_value'
21
+ require 'reactive_record/active_record/reactive_record/column_types'
22
+ require "reactive_record/active_record/aggregations"
23
+ require "reactive_record/active_record/associations"
24
+ require "reactive_record/active_record/reactive_record/backing_record_inspector"
25
+ require "reactive_record/active_record/reactive_record/getters"
26
+ require "reactive_record/active_record/reactive_record/setters"
27
+ require "reactive_record/active_record/reactive_record/lookup_tables"
28
+ require "reactive_record/active_record/reactive_record/base"
29
+ require "reactive_record/active_record/reactive_record/collection"
30
+ require "reactive_record/active_record/reactive_record/scoped_collection"
31
+ require "reactive_record/active_record/reactive_record/unscoped_collection"
32
+ require "reactive_record/interval"
33
+ require_relative 'active_record_base'
34
+ require_relative 'reactive_record/scope_description'
35
+ require "reactive_record/active_record/class_methods"
36
+ require "reactive_record/active_record/instance_methods"
37
+ require "reactive_record/active_record/base"
38
+ require 'hyper_react/input_tags'
9
39
  require 'hyperloop/model/load'
40
+ require_relative 'hyper_model/version'
41
+ require_relative 'opal/parse_patch'
42
+ require_relative 'opal/set_patches'
43
+ require_relative 'opal/equality_patches'
44
+ React::IsomorphicHelpers.log(
45
+ "The gem 'hyper-mesh' is deprecated. Use gem 'hyper-model' instead.", :warning
46
+ ) unless defined? Hyperloop::Model
10
47
  else
11
48
  require 'opal'
12
- require 'hyper-mesh'
13
- Opal.append_path(File.expand_path('../', __FILE__).untaint)
49
+ require 'hyper-operation'
50
+ require "reactive_record/permissions"
51
+ require "reactive_record/server_data_cache"
52
+ require "reactive_record/active_record/reactive_record/operations"
53
+ require 'reactive_record/broadcast'
54
+ require "reactive_record/active_record/reactive_record/isomorphic_base"
55
+ require "reactive_record/active_record/public_columns_hash"
56
+ require "reactive_record/serializers"
57
+ require "reactive_record/pry"
58
+ require_relative 'active_record_base'
59
+ require 'hyper_model/version'
60
+
61
+ Opal.append_path File.expand_path('../sources/', __FILE__).untaint
62
+ Opal.append_path File.expand_path('../', __FILE__).untaint
63
+ Opal.append_path File.expand_path('../../vendor', __FILE__).untaint
14
64
  end
65
+ require 'enumerable/pluck'
@@ -0,0 +1,3 @@
1
+ module HyperModel
2
+ VERSION = '0.99.0'
3
+ end
@@ -0,0 +1,47 @@
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
@@ -1,7 +1,7 @@
1
1
  module Hyperloop
2
2
  module Model
3
3
  def self.load(&block)
4
- ReactiveRecord.load &block
4
+ ReactiveRecord.load(&block)
5
5
  end
6
6
  end
7
7
  end
@@ -0,0 +1,5 @@
1
+ module Kernel
2
+ def itself
3
+ self
4
+ end
5
+ end
@@ -0,0 +1,7 @@
1
+ class Object
2
+ def tap
3
+ val = `self.$$is_boolean` ? self==true : self
4
+ yield val
5
+ val
6
+ end
7
+ end
@@ -0,0 +1,15 @@
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
@@ -0,0 +1,14 @@
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
@@ -0,0 +1,8 @@
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
@@ -0,0 +1,69 @@
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
@@ -0,0 +1,118 @@
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
@@ -0,0 +1,10 @@
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
@@ -0,0 +1,406 @@
1
+ module ActiveRecord
2
+
3
+ module ClassMethods
4
+
5
+ alias _new_without_sti_type_cast new
6
+
7
+ def new(*args, &block)
8
+ _new_without_sti_type_cast(*args, &block).cast_to_current_sti_type
9
+ end
10
+
11
+ def base_class
12
+ unless self < Base
13
+ raise ActiveRecordError, "#{name} doesn't descend from ActiveRecord"
14
+ end
15
+
16
+ if superclass == Base || superclass.abstract_class?
17
+ self
18
+ else
19
+ superclass.base_class
20
+ end
21
+ end
22
+
23
+ def abstract_class?
24
+ defined?(@abstract_class) && @abstract_class == true
25
+ end
26
+
27
+ def primary_key
28
+ @primary_key_value ||= (self == base_class) ? :id : base_class.primary_key
29
+ end
30
+
31
+ def primary_key=(val)
32
+ @primary_key_value = val.to_s
33
+ end
34
+
35
+ def inheritance_column
36
+ return nil if @no_inheritance_column
37
+ @inheritance_column_value ||=
38
+ if self == base_class
39
+ @inheritance_column_value || 'type'
40
+ else
41
+ superclass.inheritance_column.tap { |v| @no_inheritance_column = !v }
42
+ end
43
+ end
44
+
45
+ def inheritance_column=(name)
46
+ @no_inheritance_column = !name
47
+ @inheritance_column_value = name
48
+ end
49
+
50
+ def model_name
51
+ @model_name ||= ActiveModel::Name.new(self)
52
+ end
53
+
54
+ def find(id)
55
+ ReactiveRecord::Base.find(self, primary_key => id)
56
+ end
57
+
58
+ def find_by(opts = {})
59
+ dealiased_opts = {}
60
+ opts.each { |attr, value| dealiased_opts[_dealias_attribute(attr)] = value }
61
+ ReactiveRecord::Base.find(self, dealiased_opts)
62
+ end
63
+
64
+ def enum(*args)
65
+ # when we implement schema validation we should also implement value checking
66
+ end
67
+
68
+ def serialize(attr, *args)
69
+ ReactiveRecord::Base.serialized?[self][attr] = true
70
+ end
71
+
72
+ def _dealias_attribute(new)
73
+ if self == base_class
74
+ _attribute_aliases[new] || new
75
+ else
76
+ _attribute_aliases[new] ||= superclass._dealias_attribute(new)
77
+ end
78
+ end
79
+
80
+ def _attribute_aliases
81
+ @_attribute_aliases ||= {}
82
+ end
83
+
84
+ def alias_attribute(new_name, old_name)
85
+ ['', '=', '_changed?'].each do |variant|
86
+ define_method("#{new_name}#{variant}") { |*args, &block| send("#{old_name}#{variant}", *args, &block) }
87
+ end
88
+ _attribute_aliases[new_name] = old_name
89
+ end
90
+
91
+ # ignore any of these methods if they get called on the client. This list should be trimmed down to include only
92
+ # methods to be called as "macros" such as :after_create, etc...
93
+ SERVER_METHODS = [
94
+ :regulate_relationship, :regulate_scope,
95
+ :attribute_type_decorations, :defined_enums, :_validators, :timestamped_migrations, :lock_optimistically, :lock_optimistically=,
96
+ :local_stored_attributes=, :lock_optimistically?, :attribute_aliases?, :attribute_method_matchers?, :defined_enums?,
97
+ :has_many_without_reactive_record_add_changed_method, :has_many_with_reactive_record_add_changed_method,
98
+ :belongs_to_without_reactive_record_add_changed_method, :belongs_to_with_reactive_record_add_changed_method,
99
+ :cache_timestamp_format, :composed_of_with_reactive_record_add_changed_method, :schema_format, :schema_format=,
100
+ :error_on_ignored_order_or_limit, :error_on_ignored_order_or_limit=, :timestamped_migrations=, :dump_schema_after_migration,
101
+ :dump_schema_after_migration=, :dump_schemas, :dump_schemas=, :warn_on_records_fetched_greater_than=,
102
+ :belongs_to_required_by_default, :default_connection_handler, :connection_handler=, :default_connection_handler=,
103
+ :skip_time_zone_conversion_for_attributes, :skip_time_zone_conversion_for_attributes=, :time_zone_aware_types,
104
+ :time_zone_aware_types=, :protected_environments, :skip_time_zone_conversion_for_attributes?, :time_zone_aware_types?,
105
+ :partial_writes, :partial_writes=, :composed_of_without_reactive_record_add_changed_method, :logger, :partial_writes?,
106
+ :after_initialize, :record_timestamps, :record_timestamps=, :after_find, :after_touch, :before_save, :around_save,
107
+ :belongs_to_required_by_default=, :default_connection_handler?, :before_create, :around_create, :before_update, :around_update,
108
+ :after_save, :before_destroy, :around_destroy, :after_create, :after_destroy, :after_update, :_validation_callbacks,
109
+ :_validation_callbacks?, :_validation_callbacks=, :_initialize_callbacks, :_initialize_callbacks?, :_initialize_callbacks=,
110
+ :_find_callbacks, :_find_callbacks?, :_find_callbacks=, :_touch_callbacks, :_touch_callbacks?, :_touch_callbacks=, :_save_callbacks,
111
+ :_save_callbacks?, :_save_callbacks=, :_create_callbacks, :_create_callbacks?, :_create_callbacks=, :_update_callbacks,
112
+ :_update_callbacks?, :_update_callbacks=, :_destroy_callbacks, :_destroy_callbacks?, :_destroy_callbacks=, :record_timestamps?,
113
+ :pre_synchromesh_scope, :pre_synchromesh_default_scope, :do_not_synchronize, :do_not_synchronize?,
114
+ :logger=, :maintain_test_schema, :maintain_test_schema=, :scope, :time_zone_aware_attributes, :time_zone_aware_attributes=,
115
+ :default_timezone, :default_timezone=, :_attr_readonly, :warn_on_records_fetched_greater_than, :configurations, :configurations=,
116
+ :_attr_readonly?, :table_name_prefix=, :table_name_suffix=, :schema_migrations_table_name=, :internal_metadata_table_name,
117
+ :internal_metadata_table_name=, :primary_key_prefix_type, :_attr_readonly=, :pluralize_table_names=, :protected_environments=,
118
+ :ignored_columns=, :ignored_columns, :index_nested_attribute_errors, :index_nested_attribute_errors=, :primary_key_prefix_type=,
119
+ :table_name_prefix?, :table_name_suffix?, :schema_migrations_table_name?, :internal_metadata_table_name?, :protected_environments?,
120
+ :pluralize_table_names?, :ignored_columns?, :store_full_sti_class, :store_full_sti_class=, :nested_attributes_options,
121
+ :nested_attributes_options=, :store_full_sti_class?, :default_scopes, :default_scope_override, :default_scopes=, :default_scope_override=,
122
+ :nested_attributes_options?, :cache_timestamp_format=, :cache_timestamp_format?, :reactive_record_association_keys, :_validators=,
123
+ :has_many, :belongs_to, :composed_of, :belongs_to_without_reactive_record_add_is_method, :_rollback_callbacks, :_commit_callbacks,
124
+ :_before_commit_callbacks, :attribute_type_decorations=, :_commit_callbacks=, :_commit_callbacks?, :_before_commit_callbacks?,
125
+ :_before_commit_callbacks=, :_rollback_callbacks=, :_before_commit_without_transaction_enrollment_callbacks?,
126
+ :_before_commit_without_transaction_enrollment_callbacks=, :_commit_without_transaction_enrollment_callbacks,
127
+ :_commit_without_transaction_enrollment_callbacks?, :_commit_without_transaction_enrollment_callbacks=, :_rollback_callbacks?,
128
+ :_rollback_without_transaction_enrollment_callbacks?, :_rollback_without_transaction_enrollment_callbacks=,
129
+ :_rollback_without_transaction_enrollment_callbacks, :_before_commit_without_transaction_enrollment_callbacks, :aggregate_reflections,
130
+ :_reflections=, :aggregate_reflections=, :pluralize_table_names, :public_columns_hash, :attributes_to_define_after_schema_loads,
131
+ :attributes_to_define_after_schema_loads=, :table_name_suffix, :schema_migrations_table_name, :attribute_aliases,
132
+ :attribute_method_matchers, :connection_handler, :attribute_aliases=, :attribute_method_matchers=, :_validate_callbacks,
133
+ :_validate_callbacks?, :_validate_callbacks=, :_validators?, :_reflections?, :aggregate_reflections?, :include_root_in_json,
134
+ :_reflections, :include_root_in_json=, :include_root_in_json?, :local_stored_attributes, :default_scope, :table_name_prefix,
135
+ :attributes_to_define_after_schema_loads?, :attribute_type_decorations?, :defined_enums=, :suppress, :has_secure_token,
136
+ :generate_unique_secure_token, :store, :store_accessor, :_store_accessors_module, :stored_attributes, :reflect_on_aggregation,
137
+ :reflect_on_all_aggregations, :_reflect_on_association, :reflect_on_all_associations, :clear_reflections_cache, :reflections,
138
+ :reflect_on_association, :reflect_on_all_autosave_associations, :no_touching, :transaction, :after_commit, :after_rollback, :before_commit,
139
+ :before_commit_without_transaction_enrollment, :after_create_commit, :after_update_commit, :after_destroy_commit,
140
+ :after_commit_without_transaction_enrollment, :after_rollback_without_transaction_enrollment, :raise_in_transactional_callbacks,
141
+ :raise_in_transactional_callbacks=, :accepts_nested_attributes_for, :has_secure_password, :has_one, :has_and_belongs_to_many,
142
+ :before_validation, :after_validation, :serialize, :primary_key, :dangerous_attribute_method?, :get_primary_key, :quoted_primary_key,
143
+ :define_method_attribute, :reset_primary_key, :primary_key=, :define_method_attribute=, :attribute_names, :initialize_generated_modules,
144
+ :column_for_attribute, :define_attribute_methods, :undefine_attribute_methods, :instance_method_already_implemented?, :method_defined_within?,
145
+ :dangerous_class_method?, :class_method_defined_within?, :attribute_method?, :has_attribute?, :generated_attribute_methods,
146
+ :attribute_method_prefix, :attribute_method_suffix, :attribute_method_affix, :attribute_alias?, :attribute_alias, :define_attribute_method,
147
+ :update_counters, :locking_enabled?, :locking_column, :locking_column=, :reset_locking_column, :decorate_attribute_type,
148
+ :decorate_matching_attribute_types, :attribute, :define_attribute, :reset_counters, :increment_counter, :decrement_counter,
149
+ :validates_absence_of, :validates_length_of, :validates_size_of, :validates_presence_of, :validates_associated, :validates_uniqueness_of,
150
+ :validates_acceptance_of, :validates_confirmation_of, :validates_exclusion_of, :validates_format_of, :validates_inclusion_of,
151
+ :validates_numericality_of, :define_callbacks, :normalize_callback_params, :__update_callbacks, :get_callbacks, :set_callback,
152
+ :set_callbacks, :skip_callback, :reset_callbacks, :deprecated_false_terminator, :define_model_callbacks, :validate, :validators,
153
+ :validates_each, :validates_with, :clear_validators!, :validators_on, :validates, :_validates_default_keys, :_parse_validates_options,
154
+ :validates!, :_to_partial_path, :sanitize, :sanitize_sql, :sanitize_conditions, :quote_value, :sanitize_sql_for_conditions, :sanitize_sql_array,
155
+ :sanitize_sql_for_assignment, :sanitize_sql_hash_for_assignment, :sanitize_sql_for_order, :expand_hash_conditions_for_aggregates, :sanitize_sql_like,
156
+ :replace_named_bind_variables, :replace_bind_variables, :raise_if_bind_arity_mismatch, :replace_bind_variable, :quote_bound_value, :all,
157
+ :default_scoped, :valid_scope_name?, :scope_attributes?, :before_remove_const, :ignore_default_scope?, :unscoped, :build_default_scope,
158
+ :evaluate_default_scope, :ignore_default_scope=, :current_scope, :current_scope=, :scope_attributes, :base_class, :abstract_class?,
159
+ :finder_needs_type_condition?, :sti_name, :descends_from_active_record?, :abstract_class, :compute_type, :abstract_class=, :table_name, :columns,
160
+ :table_exists?, :columns_hash, :column_names, :attribute_types, :prefetch_primary_key?, :sequence_name, :quoted_table_name, :_default_attributes,
161
+ :type_for_attribute, :inheritance_column, :attributes_builder, :inheritance_column=, :reset_table_name, :table_name=, :reset_column_information,
162
+ :full_table_name_prefix, :full_table_name_suffix, :reset_sequence_name, :sequence_name=, :next_sequence_value, :column_defaults, :content_columns,
163
+ :readonly_attributes, :attr_readonly, :create, :create!, :instantiate, :find, :type_caster, :arel_table, :find_by, :find_by!, :initialize_find_by_cache,
164
+ :generated_association_methods, :arel_engine, :arel_attribute, :predicate_builder, :collection_cache_key, :relation_delegate_class,
165
+ :initialize_relation_delegate_cache, :enum, :collecting_queries_for_explain, :exec_explain, :i18n_scope, :lookup_ancestors, :human_attribute_name,
166
+ :references, :uniq, :maximum, :none, :exists?, :second, :limit, :order, :eager_load, :update, :delete_all, :destroy, :ids, :many?, :pluck, :third,
167
+ :delete, :fourth, :fifth, :forty_two, :second_to_last, :third_to_last, :preload, :sum, :take!, :first!, :last!, :second!, :offset, :select, :fourth!,
168
+ :third!, :third_to_last!, :fifth!, :where, :first_or_create, :second_to_last!, :forty_two!, :first, :having, :any?, :one?, :none?, :find_or_create_by,
169
+ :from, :first_or_create!, :first_or_initialize, :except, :find_or_create_by!, :find_or_initialize_by, :includes, :destroy_all, :update_all, :or,
170
+ :find_in_batches, :take, :joins, :find_each, :last, :in_batches, :reorder, :group, :left_joins, :left_outer_joins, :rewhere, :readonly, :create_with,
171
+ :distinct, :unscope, :calculate, :average, :count_by_sql, :minimum, :lock, :find_by_sql, :count, :cache, :uncached, :connection, :connection_pool,
172
+ :establish_connection, :connected?, :clear_cache!, :clear_reloadable_connections!, :connection_id, :connection_config, :clear_all_connections!,
173
+ :remove_connection, :connection_specification_name, :connection_specification_name=, :retrieve_connection, :connection_id=, :clear_active_connections!,
174
+ :sqlite3_connection, :direct_descendants, :benchmark, :model_name, :with_options, :attr_protected, :attr_accessible
175
+ ]
176
+
177
+ def method_missing(name, *args, &block)
178
+ if args.count == 1 && name.start_with?("find_by_") && !block
179
+ find_by(_dealias_attribute(name.sub(/^find_by_/, "")) => args[0])
180
+ elsif [].respond_to?(name)
181
+ all.send(name, *args, &block)
182
+ elsif name.end_with?('!')
183
+ send(name.chop, *args, &block).send(:reload_from_db) rescue nil
184
+ elsif !SERVER_METHODS.include?(name)
185
+ raise "#{self.name}.#{name}(#{args}) (called class method missing)"
186
+ end
187
+ end
188
+
189
+ # client side AR
190
+
191
+ # Any method that can be applied to an array will be applied to the result
192
+ # of all instead.
193
+ # Any method ending with ! just means apply the method after forcing a reload
194
+ # from the DB.
195
+
196
+ # alias pre_synchromesh_method_missing method_missing
197
+ #
198
+ # def method_missing(name, *args, &block)
199
+ # return all.send(name, *args, &block) if [].respond_to?(name)
200
+ # if name.end_with?('!')
201
+ # return send(name.chop, *args, &block).send(:reload_from_db) rescue nil
202
+ # end
203
+ # pre_synchromesh_method_missing(name, *args, &block)
204
+ # end
205
+
206
+ def create(*args, &block)
207
+ new(*args).save(&block)
208
+ end
209
+
210
+ def scope(name, *args)
211
+ opts = _synchromesh_scope_args_check(args)
212
+ scope_description = ReactiveRecord::ScopeDescription.new(self, name, opts)
213
+ singleton_class.send(:define_method, name) do |*vargs|
214
+ all.build_child_scope(scope_description, *name, *vargs)
215
+ end
216
+ # singleton_class.send(:define_method, "#{name}=") do |_collection|
217
+ # raise 'NO LONGER IMPLEMENTED - DOESNT PLAY WELL WITH SYNCHROMESH'
218
+ # end
219
+ end
220
+
221
+ def default_scope(*args, &block)
222
+ opts = _synchromesh_scope_args_check([*block, *args])
223
+ @_default_scopes ||= []
224
+ @_default_scopes << opts
225
+ end
226
+
227
+ def all
228
+ ReactiveRecord::Base.default_scope[self] ||=
229
+ begin
230
+ root = ReactiveRecord::Collection
231
+ .new(self, nil, nil, self, 'all')
232
+ .extend(ReactiveRecord::UnscopedCollection)
233
+ (@_default_scopes || [{ client: _all_filter }]).inject(root) do |scope, opts|
234
+ scope.build_child_scope(ReactiveRecord::ScopeDescription.new(self, :all, opts))
235
+ end
236
+ end
237
+ end
238
+
239
+ def _all_filter
240
+ # provides a filter for the all scopes taking into account STI subclasses
241
+ # note: within the lambda `self` will be the model instance
242
+ defining_class_is_base_class = base_class == self
243
+ defining_model_name = model_name.to_s
244
+ lambda do
245
+ # have to delay computation of inheritance column since it might
246
+ # not be defined when class is first defined
247
+ ic = self.class.inheritance_column
248
+ defining_class_is_base_class || !ic || self[ic] == defining_model_name
249
+ end
250
+ end
251
+
252
+ # def all=(_collection)
253
+ # raise "NO LONGER IMPLEMENTED DOESNT PLAY WELL WITH SYNCHROMESH"
254
+ # end
255
+
256
+ def unscoped
257
+ ReactiveRecord::Base.unscoped[self] ||=
258
+ ReactiveRecord::Collection
259
+ .new(self, nil, nil, self, 'unscoped')
260
+ .extend(ReactiveRecord::UnscopedCollection)
261
+ end
262
+
263
+ def finder_method(name)
264
+ ReactiveRecord::ScopeDescription.new(self, "_#{name}", {})
265
+ [name, "#{name}!"].each do |method|
266
+ singleton_class.send(:define_method, method) do |*vargs|
267
+ all.apply_scope("_#{method}", *vargs).first
268
+ end
269
+ end
270
+ end
271
+
272
+ def abstract_class=(val)
273
+ @abstract_class = val
274
+ end
275
+
276
+ # def scope(name, body)
277
+ # singleton_class.send(:define_method, name) do | *args |
278
+ # args = (args.count == 0) ? name : [name, *args]
279
+ # ReactiveRecord::Base.class_scopes(self)[args] ||= ReactiveRecord::Collection.new(self, nil, nil, self, args)
280
+ # end
281
+ # singleton_class.send(:define_method, "#{name}=") do |collection|
282
+ # ReactiveRecord::Base.class_scopes(self)[name] = collection
283
+ # end
284
+ # end
285
+
286
+ # def all
287
+ # ReactiveRecord::Base.class_scopes(self)[:all] ||= ReactiveRecord::Collection.new(self, nil, nil, self, "all")
288
+ # end
289
+ #
290
+ # def all=(collection)
291
+ # ReactiveRecord::Base.class_scopes(self)[:all] = collection
292
+ # end
293
+
294
+ [:belongs_to, :has_many, :has_one].each do |macro|
295
+ define_method(macro) do |*args| # is this a bug in opal? saying name, scope=nil, opts={} does not work!
296
+ name = args.first
297
+ opts = (args.count > 1 and args.last.is_a? Hash) ? args.last : {}
298
+ assoc = Associations::AssociationReflection.new(self, macro, name, opts)
299
+ if macro == :has_many
300
+ define_method(name) { @backing_record.get_has_many(assoc, nil) }
301
+ define_method("#{name}=") { |val| @backing_record.set_has_many(assoc, val) }
302
+ else
303
+ define_method(name) { @backing_record.get_belongs_to(assoc, nil) }
304
+ define_method("#{name}=") { |val| @backing_record.set_belongs_to(assoc, val) }
305
+ end
306
+ assoc
307
+ end
308
+ end
309
+
310
+ def composed_of(name, opts = {})
311
+ reflection = Aggregations::AggregationReflection.new(base_class, :composed_of, name, opts)
312
+ if reflection.klass < ActiveRecord::Base
313
+ define_method(name) { @backing_record.get_ar_aggregate(reflection, nil) }
314
+ define_method("#{name}=") { |val| @backing_record.set_ar_aggregate(reflection, val) }
315
+ else
316
+ define_method(name) { @backing_record.get_non_ar_aggregate(name, nil) }
317
+ define_method("#{name}=") { |val| @backing_record.set_non_ar_aggregate(reflection, val) }
318
+ end
319
+ end
320
+
321
+ def column_names
322
+ ReactiveRecord::Base.public_columns_hash.keys
323
+ end
324
+
325
+ def columns_hash
326
+ ReactiveRecord::Base.public_columns_hash[name] || {}
327
+ end
328
+
329
+ def server_methods
330
+ @server_methods ||= {}
331
+ end
332
+
333
+ def server_method(name, default: nil)
334
+ server_methods[name] = { default: default }
335
+ define_method(name) do |*args|
336
+ vector = args.count.zero? ? name : [[name] + args]
337
+ @backing_record.get_server_method(vector, nil)
338
+ end
339
+ define_method("#{name}!") do |*args|
340
+ vector = args.count.zero? ? name : [[name] + args]
341
+ @backing_record.get_server_method(vector, true)
342
+ end
343
+ end
344
+
345
+ def define_attribute_methods
346
+ columns_hash.each do |name, column_hash|
347
+ next if name == primary_key
348
+ define_method(name) { @backing_record.get_attr_value(name, nil) }
349
+ define_method("#{name}!") { @backing_record.get_attr_value(name, true) }
350
+ define_method("#{name}=") { |val| @backing_record.set_attr_value(name, val) }
351
+ define_method("#{name}_changed?") { @backing_record.changed?(name) }
352
+ define_method("#{name}?") { @backing_record.get_attr_value(name, nil).present? }
353
+ end
354
+ self.inheritance_column = nil if inheritance_column && !columns_hash.key?(inheritance_column)
355
+ end
356
+
357
+ def _react_param_conversion(param, opt = nil)
358
+ param = Native(param)
359
+ param = JSON.from_object(param.to_n) if param.is_a? Native::Object
360
+ result =
361
+ if param.is_a? self
362
+ param
363
+ elsif param.is_a? Hash
364
+ if opt == :validate_only
365
+ klass = ReactiveRecord::Base.infer_type_from_hash(self, param)
366
+ klass == self || klass < self
367
+ else
368
+ # TODO: investigate saving .changes here and then replacing the
369
+ # TODO: changes after the load is complete. In other words preserve the
370
+ # TODO: changed values as changes while just updating the synced values.
371
+ target =
372
+ if param[primary_key]
373
+ find(param[primary_key])
374
+ else
375
+ new
376
+ end
377
+
378
+ associations = reflect_on_all_associations
379
+
380
+ param = param.collect do |key, value|
381
+ assoc = associations.detect do |association|
382
+ association.association_foreign_key == key
383
+ end
384
+
385
+ if assoc
386
+ if value
387
+ [assoc.attribute, { id: [value] }]
388
+ else
389
+ [assoc.attribute, [nil]]
390
+ end
391
+ else
392
+ [key, [value]]
393
+ end
394
+ end
395
+ # TODO: verify wrapping with load_data was added so broadcasting works in 1.0.0.lap28
396
+ ReactiveRecord::Base.load_data do
397
+ ReactiveRecord::ServerDataCache.load_from_json(Hash[param], target)
398
+ end
399
+ target.cast_to_current_sti_type
400
+ end
401
+ end
402
+
403
+ result
404
+ end
405
+ end
406
+ end